aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-block37
-rw-r--r--Documentation/DocBook/kernel-hacking.tmpl4
-rw-r--r--Documentation/arm/memory.txt2
-rw-r--r--Documentation/feature-removal-schedule.txt24
-rw-r--r--Documentation/filesystems/9p.txt3
-rw-r--r--Documentation/filesystems/afs.txt26
-rw-r--r--Documentation/filesystems/proc.txt15
-rw-r--r--Documentation/ioctl/ioctl-number.txt1
-rw-r--r--Documentation/kernel-parameters.txt4
-rw-r--r--Documentation/laptops/thinkpad-acpi.txt127
-rw-r--r--Documentation/lockdep-design.txt6
-rw-r--r--Documentation/sound/alsa/ALSA-Configuration.txt30
-rw-r--r--Documentation/sound/alsa/HD-Audio-Models.txt33
-rw-r--r--Documentation/sound/alsa/HD-Audio.txt64
-rw-r--r--Documentation/video4linux/CARDLIST.em28xx2
-rw-r--r--Documentation/video4linux/CARDLIST.saa71344
-rw-r--r--MAINTAINERS21
-rw-r--r--Makefile2
-rw-r--r--REPORTING-BUGS5
-rw-r--r--arch/arm/boot/compressed/misc.c1
-rw-r--r--arch/arm/common/clkdev.c1
-rw-r--r--arch/arm/configs/kirkwood_defconfig2
-rw-r--r--arch/arm/configs/mx27_defconfig270
-rw-r--r--arch/arm/configs/mx3_defconfig151
-rw-r--r--arch/arm/configs/omap3_evm_defconfig2
-rw-r--r--arch/arm/configs/rx51_defconfig7
-rw-r--r--arch/arm/include/asm/atomic.h2
-rw-r--r--arch/arm/include/asm/setup.h3
-rw-r--r--arch/arm/kernel/entry-common.S2
-rw-r--r--arch/arm/kernel/signal.c4
-rw-r--r--arch/arm/mach-davinci/board-dm355-evm.c1
-rw-r--r--arch/arm/mach-davinci/board-dm355-leopard.c1
-rw-r--r--arch/arm/mach-davinci/board-dm644x-evm.c1
-rw-r--r--arch/arm/mach-davinci/board-dm646x-evm.c1
-rw-r--r--arch/arm/mach-davinci/board-sffsdr.c1
-rw-r--r--arch/arm/mach-ep93xx/include/mach/ts72xx.h3
-rw-r--r--arch/arm/mach-ep93xx/ts72xx.c18
-rw-r--r--arch/arm/mach-ixp4xx/include/mach/io.h2
-rw-r--r--arch/arm/mach-kirkwood/ts219-setup.c9
-rw-r--r--arch/arm/mach-ks8695/include/mach/hardware.h5
-rw-r--r--arch/arm/mach-ks8695/include/mach/timex.h5
-rw-r--r--arch/arm/mach-ks8695/pci.c3
-rw-r--r--arch/arm/mach-mx3/mx31moboard-devboard.c2
-rw-r--r--arch/arm/mach-mx3/mx31moboard-marxbot.c2
-rw-r--r--arch/arm/mach-mx3/mx31moboard.c2
-rw-r--r--arch/arm/mach-mx3/pcm037_eet.c9
-rw-r--r--arch/arm/mach-omap1/mcbsp.c1
-rw-r--r--arch/arm/mach-omap2/board-2430sdp.c2
-rw-r--r--arch/arm/mach-omap2/board-3430sdp.c2
-rw-r--r--arch/arm/mach-omap2/board-4430sdp.c2
-rw-r--r--arch/arm/mach-omap2/board-apollon.c2
-rw-r--r--arch/arm/mach-omap2/board-generic.c2
-rw-r--r--arch/arm/mach-omap2/board-h4.c2
-rw-r--r--arch/arm/mach-omap2/board-ldp.c2
-rw-r--r--arch/arm/mach-omap2/board-omap3beagle.c7
-rw-r--r--arch/arm/mach-omap2/board-omap3evm.c7
-rw-r--r--arch/arm/mach-omap2/board-omap3pandora.c8
-rw-r--r--arch/arm/mach-omap2/board-overo.c11
-rw-r--r--arch/arm/mach-omap2/board-rx51-peripherals.c5
-rw-r--r--arch/arm/mach-omap2/board-rx51.c6
-rw-r--r--arch/arm/mach-omap2/board-zoom2.c2
-rw-r--r--arch/arm/mach-omap2/clock.c156
-rw-r--r--arch/arm/mach-omap2/clock.h6
-rw-r--r--arch/arm/mach-omap2/clock24xx.c37
-rw-r--r--arch/arm/mach-omap2/clock24xx.h4
-rw-r--r--arch/arm/mach-omap2/clock34xx.c155
-rw-r--r--arch/arm/mach-omap2/clock34xx.h85
-rw-r--r--arch/arm/mach-omap2/cm.h6
-rw-r--r--arch/arm/mach-omap2/io.c5
-rw-r--r--arch/arm/mach-omap2/mcbsp.c6
-rw-r--r--arch/arm/mach-omap2/mmc-twl4030.c6
-rw-r--r--arch/arm/mach-omap2/mux.c6
-rw-r--r--arch/arm/mach-omap2/pm.h3
-rw-r--r--arch/arm/mach-omap2/pm24xx.c2
-rw-r--r--arch/arm/mach-omap2/pm34xx.c51
-rw-r--r--arch/arm/mach-omap2/prcm.c43
-rw-r--r--arch/arm/mach-omap2/sdrc.c68
-rw-r--r--arch/arm/mach-omap2/serial.c197
-rw-r--r--arch/arm/mach-omap2/sram34xx.S143
-rw-r--r--arch/arm/mach-omap2/usb-musb.c21
-rw-r--r--arch/arm/mach-pxa/em-x270.c4
-rw-r--r--arch/arm/mach-pxa/include/mach/audio.h3
-rw-r--r--arch/arm/mach-pxa/palmld.c4
-rw-r--r--arch/arm/mach-pxa/palmt5.c4
-rw-r--r--arch/arm/mach-pxa/palmtx.c4
-rw-r--r--arch/arm/mach-pxa/treo680.c2
-rw-r--r--arch/arm/mach-pxa/zylonite_pxa300.c2
-rw-r--r--arch/arm/mach-pxa/zylonite_pxa320.c2
-rw-r--r--arch/arm/mach-s3c2410/include/mach/gpio-core.h2
-rw-r--r--arch/arm/mach-u300/core.c2
-rw-r--r--arch/arm/mm/init.c118
-rw-r--r--arch/arm/mm/mmu.c9
-rw-r--r--arch/arm/plat-omap/cpu-omap.c8
-rw-r--r--arch/arm/plat-omap/dma.c14
-rw-r--r--arch/arm/plat-omap/gpio.c127
-rw-r--r--arch/arm/plat-omap/include/mach/clock.h2
-rw-r--r--arch/arm/plat-omap/include/mach/cpu.h5
-rw-r--r--arch/arm/plat-omap/include/mach/io.h3
-rw-r--r--arch/arm/plat-omap/include/mach/mcbsp.h51
-rw-r--r--arch/arm/plat-omap/include/mach/mux.h4
-rw-r--r--arch/arm/plat-omap/include/mach/prcm.h1
-rw-r--r--arch/arm/plat-omap/include/mach/sdrc.h11
-rw-r--r--arch/arm/plat-omap/include/mach/serial.h1
-rw-r--r--arch/arm/plat-omap/include/mach/sram.h23
-rw-r--r--arch/arm/plat-omap/mcbsp.c401
-rw-r--r--arch/arm/plat-omap/sram.c34
-rw-r--r--arch/arm/plat-orion/include/plat/gpio.h2
-rw-r--r--arch/arm/plat-s3c/include/plat/audio-simtec.h37
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h5
-rw-r--r--arch/arm/plat-s3c24xx/clock-dclk.c2
-rw-r--r--arch/arm/plat-s3c24xx/pwm.c4
-rw-r--r--arch/arm/plat-s3c64xx/pm.c2
-rw-r--r--arch/arm/plat-s3c64xx/s3c6400-clock.c4
-rw-r--r--arch/arm/plat-stmp3xxx/pinmux.c1
-rw-r--r--arch/avr32/boards/favr-32/setup.c4
-rw-r--r--arch/avr32/lib/memcpy.S16
-rw-r--r--arch/ia64/Makefile5
-rw-r--r--arch/ia64/include/asm/bitops.h2
-rw-r--r--arch/ia64/include/asm/pgtable.h1
-rw-r--r--arch/ia64/kernel/dma-mapping.c4
-rw-r--r--arch/ia64/kernel/ia64_ksyms.c4
-rw-r--r--arch/ia64/kernel/iosapic.c4
-rw-r--r--arch/ia64/kernel/pci-dma.c5
-rw-r--r--arch/ia64/kernel/topology.c6
-rw-r--r--arch/ia64/kvm/mmio.c6
-rw-r--r--arch/ia64/kvm/vcpu.c6
-rw-r--r--arch/ia64/kvm/vcpu.h13
-rw-r--r--arch/ia64/lib/ip_fast_csum.S8
-rw-r--r--arch/m68k/amiga/config.c6
-rw-r--r--arch/m68k/include/asm/motorola_pgalloc.h10
-rw-r--r--arch/m68k/include/asm/pgtable_mm.h3
-rw-r--r--arch/m68k/include/asm/unistd.h4
-rw-r--r--arch/m68k/kernel/entry.S2
-rw-r--r--arch/m68knommu/kernel/syscalltable.S2
-rw-r--r--arch/microblaze/configs/mmu_defconfig70
-rw-r--r--arch/microblaze/configs/nommu_defconfig91
-rw-r--r--arch/microblaze/include/asm/hardirq.h2
-rw-r--r--arch/microblaze/kernel/intc.c2
-rw-r--r--arch/microblaze/kernel/irq.c9
-rw-r--r--arch/microblaze/kernel/syscall_table.S2
-rw-r--r--arch/microblaze/kernel/timer.c2
-rw-r--r--arch/microblaze/mm/init.c6
-rw-r--r--arch/mips/alchemy/mtx-1/platform.c14
-rw-r--r--arch/mips/ar7/Makefile1
-rw-r--r--arch/mips/ar7/clock.c13
-rw-r--r--arch/mips/ar7/memory.c2
-rw-r--r--arch/mips/ar7/platform.c10
-rw-r--r--arch/mips/ar7/prom.c2
-rw-r--r--arch/mips/ar7/setup.c1
-rw-r--r--arch/mips/cavium-octeon/smp.c4
-rw-r--r--arch/mips/dec/ecc-berr.c2
-rw-r--r--arch/mips/dec/int-handler.S2
-rw-r--r--arch/mips/dec/ioasic-irq.c2
-rw-r--r--arch/mips/dec/kn01-berr.c2
-rw-r--r--arch/mips/dec/kn02-irq.c2
-rw-r--r--arch/mips/dec/kn02xa-berr.c2
-rw-r--r--arch/mips/dec/prom/call_o32.S2
-rw-r--r--arch/mips/dec/prom/console.c2
-rw-r--r--arch/mips/dec/time.c2
-rw-r--r--arch/mips/emma/common/Makefile3
-rw-r--r--arch/mips/emma/common/prom.c3
-rw-r--r--arch/mips/emma/markeins/Makefile3
-rw-r--r--arch/mips/emma/markeins/irq.c3
-rw-r--r--arch/mips/emma/markeins/led.c3
-rw-r--r--arch/mips/emma/markeins/platform.c3
-rw-r--r--arch/mips/emma/markeins/setup.c3
-rw-r--r--arch/mips/fw/lib/call_o32.S2
-rw-r--r--arch/mips/include/asm/emma/emma2rh.h3
-rw-r--r--arch/mips/include/asm/emma/markeins.h3
-rw-r--r--arch/mips/include/asm/gic.h2
-rw-r--r--arch/mips/include/asm/page.h2
-rw-r--r--arch/mips/include/asm/pmc-sierra/msp71xx/war.h2
-rw-r--r--arch/mips/include/asm/processor.h5
-rw-r--r--arch/mips/include/asm/unistd.h15
-rw-r--r--arch/mips/jazz/jazzdma.c2
-rw-r--r--arch/mips/kernel/head.S3
-rw-r--r--arch/mips/kernel/irq_txx9.c2
-rw-r--r--arch/mips/kernel/module.c12
-rw-r--r--arch/mips/kernel/proc.c2
-rw-r--r--arch/mips/kernel/process.c4
-rw-r--r--arch/mips/kernel/scall32-o32.S1
-rw-r--r--arch/mips/kernel/scall64-64.S1
-rw-r--r--arch/mips/kernel/scall64-n32.S1
-rw-r--r--arch/mips/kernel/scall64-o32.S1
-rw-r--r--arch/mips/kernel/smtc.c13
-rw-r--r--arch/mips/kernel/stacktrace.c2
-rw-r--r--arch/mips/kernel/vpe.c49
-rw-r--r--arch/mips/mipssim/sim_time.c12
-rw-r--r--arch/mips/mm/c-octeon.c4
-rw-r--r--arch/mips/mm/extable.c6
-rw-r--r--arch/mips/mm/fault.c1
-rw-r--r--arch/mips/mti-malta/malta-int.c2
-rw-r--r--arch/mips/nxp/pnx8550/common/time.c2
-rw-r--r--arch/mips/pci/fixup-emma2rh.c3
-rw-r--r--arch/mips/pci/fixup-sb1250.c2
-rw-r--r--arch/mips/pci/ops-emma2rh.c3
-rw-r--r--arch/mips/pci/pci-emma2rh.c3
-rw-r--r--arch/mips/pci/pci-tx4927.c2
-rw-r--r--arch/mips/pci/pci-tx4938.c2
-rw-r--r--arch/mips/pci/pci-tx4939.c2
-rw-r--r--arch/mips/pci/pcie-octeon.c31
-rw-r--r--arch/mips/pmc-sierra/msp71xx/gpio.c2
-rw-r--r--arch/mips/pmc-sierra/msp71xx/gpio_extended.c2
-rw-r--r--arch/mips/pmc-sierra/msp71xx/msp_irq_slp.c12
-rw-r--r--arch/mips/pmc-sierra/yosemite/atmel_read_eeprom.c2
-rw-r--r--arch/mips/sibyte/swarm/swarm-i2c.c2
-rw-r--r--arch/mips/txx9/generic/mem_tx4927.c2
-rw-r--r--arch/mips/txx9/generic/setup.c2
-rw-r--r--arch/mips/txx9/rbtx4939/setup.c2
-rw-r--r--arch/mn10300/include/asm/pci.h1
-rw-r--r--arch/parisc/kernel/entry.S2
-rw-r--r--arch/parisc/kernel/module.c50
-rw-r--r--arch/parisc/kernel/traps.c2
-rw-r--r--arch/powerpc/configs/ps3_defconfig211
-rw-r--r--arch/powerpc/include/asm/kvm_host.h2
-rw-r--r--arch/powerpc/kernel/dma.c6
-rw-r--r--arch/powerpc/kernel/mpc7450-pmu.c3
-rw-r--r--arch/powerpc/kernel/perf_counter.c8
-rw-r--r--arch/powerpc/kernel/power4-pmu.c3
-rw-r--r--arch/powerpc/kernel/power5+-pmu.c5
-rw-r--r--arch/powerpc/kernel/power5-pmu.c3
-rw-r--r--arch/powerpc/kernel/power6-pmu.c3
-rw-r--r--arch/powerpc/kernel/power7-pmu.c9
-rw-r--r--arch/powerpc/kernel/ppc970-pmu.c5
-rw-r--r--arch/powerpc/platforms/ps3/time.c4
-rw-r--r--arch/powerpc/sysdev/xilinx_intc.c1
-rw-r--r--arch/s390/kernel/early.c3
-rw-r--r--arch/s390/kernel/setup.c25
-rw-r--r--arch/s390/kvm/interrupt.c2
-rw-r--r--arch/s390/kvm/sigp.c7
-rw-r--r--arch/sh/boards/board-ap325rxa.c2
-rw-r--r--arch/sh/boards/mach-migor/setup.c2
-rw-r--r--arch/sh/boards/mach-se/7724/setup.c9
-rw-r--r--arch/sh/kernel/cpu/sh2/setup-sh7619.c2
-rw-r--r--arch/sh/kernel/cpu/sh2a/setup-mxg.c2
-rw-r--r--arch/sh/kernel/cpu/sh2a/setup-sh7201.c2
-rw-r--r--arch/sh/kernel/cpu/sh2a/setup-sh7203.c2
-rw-r--r--arch/sh/kernel/cpu/sh2a/setup-sh7206.c2
-rw-r--r--arch/sh/kernel/cpu/sh3/setup-sh7705.c2
-rw-r--r--arch/sh/kernel/cpu/sh3/setup-sh770x.c2
-rw-r--r--arch/sh/kernel/cpu/sh3/setup-sh7710.c2
-rw-r--r--arch/sh/kernel/cpu/sh3/setup-sh7720.c2
-rw-r--r--arch/sh/kernel/cpu/sh4/setup-sh4-202.c2
-rw-r--r--arch/sh/kernel/cpu/sh4/setup-sh7750.c2
-rw-r--r--arch/sh/kernel/cpu/sh4/setup-sh7760.c2
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7343.c2
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7366.c2
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7722.c2
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7723.c2
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7724.c2
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7763.c2
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7770.c2
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7780.c2
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7785.c2
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7786.c2
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-shx3.c2
-rw-r--r--arch/sh/kernel/cpu/sh5/setup-sh5.c2
-rw-r--r--arch/sh/kernel/cpu/shmobile/sleep.S70
-rw-r--r--arch/sparc/configs/sparc32_defconfig74
-rw-r--r--arch/sparc/configs/sparc64_defconfig59
-rw-r--r--arch/sparc/include/asm/pgtable_64.h12
-rw-r--r--arch/sparc/kernel/irq_64.c2
-rw-r--r--arch/sparc/kernel/ktlb.S42
-rw-r--r--arch/sparc/kernel/nmi.c2
-rw-r--r--arch/sparc/kernel/smp_64.c4
-rw-r--r--arch/sparc/kernel/sun4d_smp.c22
-rw-r--r--arch/sparc/kernel/sun4m_smp.c26
-rw-r--r--arch/sparc/kernel/sys32.S4
-rw-r--r--arch/sparc/kernel/systbls_64.S4
-rw-r--r--arch/sparc/mm/fault_32.c7
-rw-r--r--arch/sparc/mm/fault_64.c7
-rw-r--r--arch/sparc/mm/init_64.c43
-rw-r--r--arch/sparc/mm/init_64.h7
-rw-r--r--arch/sparc/prom/misc_64.c2
-rw-r--r--arch/sparc/prom/printf.c7
-rw-r--r--arch/x86/Kconfig2
-rw-r--r--arch/x86/boot/compressed/Makefile2
-rw-r--r--arch/x86/include/asm/efi.h5
-rw-r--r--arch/x86/include/asm/irqflags.h8
-rw-r--r--arch/x86/include/asm/pgtable.h12
-rw-r--r--arch/x86/include/asm/uv/uv_bau.h2
-rw-r--r--arch/x86/include/asm/uv/uv_hub.h9
-rw-r--r--arch/x86/kernel/apic/io_apic.c3
-rw-r--r--arch/x86/kernel/apic/ipi.c3
-rw-r--r--arch/x86/kernel/apic/probe_64.c10
-rw-r--r--arch/x86/kernel/apic/x2apic_cluster.c10
-rw-r--r--arch/x86/kernel/apic/x2apic_phys.c10
-rw-r--r--arch/x86/kernel/apic/x2apic_uv_x.c42
-rw-r--r--arch/x86/kernel/apm_32.c2
-rw-r--r--arch/x86/kernel/cpu/Makefile4
-rw-r--r--arch/x86/kernel/cpu/amd.c7
-rw-r--r--arch/x86/kernel/cpu/common.c48
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce.c19
-rw-r--r--arch/x86/kernel/cpu/mcheck/therm_throt.c23
-rw-r--r--arch/x86/kernel/cpu/perf_counter.c40
-rw-r--r--arch/x86/kernel/efi.c4
-rw-r--r--arch/x86/kernel/efi_64.c6
-rw-r--r--arch/x86/kernel/head_32.S6
-rw-r--r--arch/x86/kernel/process.c6
-rw-r--r--arch/x86/kernel/reboot.c42
-rw-r--r--arch/x86/kernel/setup_percpu.c14
-rw-r--r--arch/x86/kernel/tlb_uv.c1
-rw-r--r--arch/x86/kernel/tsc.c29
-rw-r--r--arch/x86/kernel/vmi_32.c2
-rw-r--r--arch/x86/kernel/vmlinux.lds.S142
-rw-r--r--arch/x86/kvm/i8254.c3
-rw-r--r--arch/x86/kvm/mmu.c48
-rw-r--r--arch/x86/kvm/svm.c6
-rw-r--r--arch/x86/kvm/vmx.c6
-rw-r--r--arch/x86/kvm/x86.c44
-rw-r--r--arch/x86/lib/msr.c26
-rw-r--r--arch/x86/mm/init_64.c2
-rw-r--r--arch/x86/mm/pageattr.c39
-rw-r--r--arch/x86/mm/pat.c3
-rw-r--r--arch/x86/mm/pgtable.c1
-rw-r--r--arch/x86/mm/tlb.c21
-rw-r--r--arch/x86/xen/Makefile4
-rw-r--r--arch/x86/xen/enlighten.c24
-rw-r--r--block/Kconfig11
-rw-r--r--block/blk-settings.c77
-rw-r--r--block/blk-sysfs.c2
-rw-r--r--crypto/algapi.c11
-rw-r--r--drivers/acpi/acpi_memhotplug.c34
-rw-r--r--drivers/acpi/acpica/acobject.h1
-rw-r--r--drivers/acpi/acpica/dsopcode.c24
-rw-r--r--drivers/acpi/acpica/exfldio.c6
-rw-r--r--drivers/acpi/acpica/exstorob.c12
-rw-r--r--drivers/acpi/osl.c25
-rw-r--r--drivers/acpi/processor_core.c6
-rw-r--r--drivers/acpi/processor_idle.c6
-rw-r--r--drivers/acpi/processor_thermal.c6
-rw-r--r--drivers/acpi/processor_throttling.c30
-rw-r--r--drivers/acpi/system.c2
-rw-r--r--drivers/acpi/video.c7
-rw-r--r--drivers/ata/ahci.c79
-rw-r--r--drivers/ata/ata_piix.c14
-rw-r--r--drivers/ata/libata-core.c3
-rw-r--r--drivers/ata/pata_at91.c17
-rw-r--r--drivers/ata/pata_atiixp.c19
-rw-r--r--drivers/ata/sata_nv.c8
-rw-r--r--drivers/base/platform.c3
-rw-r--r--drivers/block/aoe/aoe.h2
-rw-r--r--drivers/block/aoe/aoeblk.c12
-rw-r--r--drivers/block/aoe/aoedev.c1
-rw-r--r--drivers/char/agp/intel-agp.c8
-rw-r--r--drivers/char/agp/parisc-agp.c2
-rw-r--r--drivers/char/n_tty.c3
-rw-r--r--drivers/char/pty.c12
-rw-r--r--drivers/char/tty_ldisc.c162
-rw-r--r--drivers/clocksource/sh_cmt.c28
-rw-r--r--drivers/cpufreq/cpufreq.c92
-rw-r--r--drivers/cpufreq/cpufreq_conservative.c6
-rw-r--r--drivers/edac/amd64_edac.c7
-rw-r--r--drivers/firewire/core-iso.c4
-rw-r--r--drivers/firewire/ohci.c14
-rw-r--r--drivers/firewire/sbp2.c8
-rw-r--r--drivers/gpu/drm/drm_crtc.c42
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c6
-rw-r--r--drivers/gpu/drm/drm_edid.c72
-rw-r--r--drivers/gpu/drm/drm_irq.c2
-rw-r--r--drivers/gpu/drm/drm_modes.c2
-rw-r--r--drivers/gpu/drm/drm_sysfs.c51
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c15
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h11
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c103
-rw-r--r--drivers/gpu/drm/i915/i915_gem_debugfs.c2
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c239
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h45
-rw-r--r--drivers/gpu/drm/i915/i915_suspend.c2
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c91
-rw-r--r--drivers/gpu/drm/i915/intel_bios.h45
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c23
-rw-r--r--drivers/gpu/drm/i915/intel_display.c836
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c230
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h24
-rw-r--r--drivers/gpu/drm/i915/intel_dvo.c6
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c82
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c14
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c265
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c25
-rw-r--r--drivers/gpu/drm/radeon/r100.c99
-rw-r--r--drivers/gpu/drm/radeon/r300.c44
-rw-r--r--drivers/gpu/drm/radeon/r420.c13
-rw-r--r--drivers/gpu/drm/radeon/r500_reg.h16
-rw-r--r--drivers/gpu/drm/radeon/r520.c1
-rw-r--r--drivers/gpu/drm/radeon/r600_cp.c22
-rw-r--r--drivers/gpu/drm/radeon/radeon.h55
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.h28
-rw-r--r--drivers/gpu/drm/radeon/radeon_combios.c48
-rw-r--r--drivers/gpu/drm/radeon/radeon_cp.c9
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c17
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c17
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.h6
-rw-r--r--drivers/gpu/drm/radeon/radeon_fb.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_gem.c30
-rw-r--r--drivers/gpu/drm/radeon/radeon_irq_kms.c54
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c39
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_crtc.c7
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_encoders.c1
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c21
-rw-r--r--drivers/gpu/drm/radeon/radeon_reg.h16
-rw-r--r--drivers/gpu/drm/radeon/radeon_state.c3
-rw-r--r--drivers/gpu/drm/radeon/rs600.c147
-rw-r--r--drivers/gpu/drm/radeon/rs690.c1
-rw-r--r--drivers/gpu/drm/radeon/rv515.c22
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c7
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_util.c8
-rw-r--r--drivers/i2c/busses/i2c-omap.c14
-rw-r--r--drivers/i2c/busses/i2c-stu300.c157
-rw-r--r--drivers/ide/ide-cs.c1
-rw-r--r--drivers/input/joydev.c68
-rw-r--r--drivers/input/joystick/iforce/iforce-main.c1
-rw-r--r--drivers/input/joystick/iforce/iforce-usb.c1
-rw-r--r--drivers/input/keyboard/atkbd.c35
-rw-r--r--drivers/input/keyboard/matrix_keypad.c18
-rw-r--r--drivers/input/misc/wistron_btns.c16
-rw-r--r--drivers/input/serio/hp_sdc_mlc.c2
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h16
-rw-r--r--drivers/input/tablet/wacom_sys.c43
-rw-r--r--drivers/input/touchscreen/ucb1400_ts.c17
-rw-r--r--drivers/isdn/mISDN/l1oip_core.c2
-rw-r--r--drivers/leds/ledtrig-gpio.c24
-rw-r--r--drivers/macintosh/via-maciisi.c2
-rw-r--r--drivers/md/dm-exception-store.c13
-rw-r--r--drivers/md/dm-exception-store.h4
-rw-r--r--drivers/md/dm-log-userspace-base.c39
-rw-r--r--drivers/md/dm-log-userspace-transfer.c8
-rw-r--r--drivers/md/dm-log-userspace-transfer.h2
-rw-r--r--drivers/md/dm-raid1.c8
-rw-r--r--drivers/md/dm-snap-persistent.c88
-rw-r--r--drivers/md/dm-snap.c23
-rw-r--r--drivers/md/dm-stripe.c13
-rw-r--r--drivers/md/dm-table.c51
-rw-r--r--drivers/md/dm.c15
-rw-r--r--drivers/md/linear.c2
-rw-r--r--drivers/md/md.c177
-rw-r--r--drivers/md/md.h12
-rw-r--r--drivers/md/multipath.c5
-rw-r--r--drivers/md/raid0.c1
-rw-r--r--drivers/md/raid1.c7
-rw-r--r--drivers/md/raid10.c4
-rw-r--r--drivers/md/raid5.c85
-rw-r--r--drivers/media/common/tuners/qt1010.c12
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.c4
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.h1
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.c2
-rw-r--r--drivers/media/dvb/frontends/cx22700.c2
-rw-r--r--drivers/media/dvb/frontends/cx22702.c2
-rw-r--r--drivers/media/dvb/frontends/cx24110.c2
-rw-r--r--drivers/media/dvb/frontends/dvb_dummy_fe.c6
-rw-r--r--drivers/media/dvb/frontends/l64781.c2
-rw-r--r--drivers/media/dvb/frontends/lgs8gl5.c2
-rw-r--r--drivers/media/dvb/frontends/mt312.c2
-rw-r--r--drivers/media/dvb/frontends/nxt6000.c2
-rw-r--r--drivers/media/dvb/frontends/or51132.c2
-rw-r--r--drivers/media/dvb/frontends/or51211.c2
-rw-r--r--drivers/media/dvb/frontends/s5h1409.c2
-rw-r--r--drivers/media/dvb/frontends/s5h1411.c2
-rw-r--r--drivers/media/dvb/frontends/si21xx.c2
-rw-r--r--drivers/media/dvb/frontends/sp8870.c2
-rw-r--r--drivers/media/dvb/frontends/sp887x.c2
-rw-r--r--drivers/media/dvb/frontends/stv0288.c2
-rw-r--r--drivers/media/dvb/frontends/stv0297.c2
-rw-r--r--drivers/media/dvb/frontends/stv0299.c2
-rw-r--r--drivers/media/dvb/frontends/tda10021.c2
-rw-r--r--drivers/media/dvb/frontends/tda10048.c2
-rw-r--r--drivers/media/dvb/frontends/tda1004x.c4
-rw-r--r--drivers/media/dvb/frontends/tda10086.c2
-rw-r--r--drivers/media/dvb/frontends/tda8083.c2
-rw-r--r--drivers/media/dvb/frontends/ves1820.c2
-rw-r--r--drivers/media/dvb/frontends/ves1x93.c2
-rw-r--r--drivers/media/dvb/frontends/zl10353.c12
-rw-r--r--drivers/media/dvb/siano/Kconfig40
-rw-r--r--drivers/media/dvb/siano/Makefile9
-rw-r--r--drivers/media/dvb/siano/sms-cards.c102
-rw-r--r--drivers/media/dvb/siano/smscoreapi.c2
-rw-r--r--drivers/media/dvb/siano/smsdvb.c44
-rw-r--r--drivers/media/dvb/siano/smssdio.c54
-rw-r--r--drivers/media/video/Kconfig2
-rw-r--r--drivers/media/video/bw-qcam.c2
-rw-r--r--drivers/media/video/cx18/cx18-controls.c3
-rw-r--r--drivers/media/video/cx23885/cx23885-417.c2
-rw-r--r--drivers/media/video/cx88/cx88-cards.c8
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c1
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c4
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c219
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c8
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c2
-rw-r--r--drivers/media/video/em28xx/em28xx-reg.h3
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c77
-rw-r--r--drivers/media/video/em28xx/em28xx.h4
-rw-r--r--drivers/media/video/gspca/Kconfig2
-rw-r--r--drivers/media/video/hdpvr/hdpvr-video.c2
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.c3
-rw-r--r--drivers/media/video/mt9v011.c156
-rw-r--r--drivers/media/video/mt9v011.h3
-rw-r--r--drivers/media/video/mx1_camera.c6
-rw-r--r--drivers/media/video/mx3_camera.c19
-rw-r--r--drivers/media/video/pxa_camera.c8
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c30
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c4
-rw-r--r--drivers/media/video/saa7134/saa7134.h4
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c5
-rw-r--r--drivers/media/video/stk-webcam.c4
-rw-r--r--drivers/media/video/uvc/uvc_driver.c24
-rw-r--r--drivers/media/video/uvc/uvc_status.c4
-rw-r--r--drivers/media/video/v4l2-ioctl.c15
-rw-r--r--drivers/media/video/zr364xx.c2
-rw-r--r--drivers/mfd/twl4030-irq.c55
-rw-r--r--drivers/mmc/host/sdhci-of.c2
-rw-r--r--drivers/mtd/devices/m25p80.c2
-rw-r--r--drivers/mtd/maps/Kconfig7
-rw-r--r--drivers/mtd/maps/Makefile1
-rw-r--r--drivers/mtd/maps/sbc8240.c250
-rw-r--r--drivers/mtd/mtd_blkdevs.c6
-rw-r--r--drivers/mtd/mtdblock.c16
-rw-r--r--drivers/mtd/mtdcore.c7
-rw-r--r--drivers/mtd/nand/orion_nand.c2
-rw-r--r--drivers/mtd/nftlcore.c15
-rw-r--r--drivers/mtd/onenand/omap2.c3
-rw-r--r--drivers/mtd/ubi/eba.c1
-rw-r--r--drivers/mtd/ubi/scan.c13
-rw-r--r--drivers/net/3c515.c4
-rw-r--r--drivers/net/3c59x.c14
-rw-r--r--drivers/net/8139cp.c5
-rw-r--r--drivers/net/Kconfig4
-rw-r--r--drivers/net/arm/w90p910_ether.c4
-rw-r--r--drivers/net/atl1c/atl1c_ethtool.c8
-rw-r--r--drivers/net/atlx/atl1.c8
-rw-r--r--drivers/net/b44.c5
-rw-r--r--drivers/net/bnx2.c17
-rw-r--r--drivers/net/bnx2.h1
-rw-r--r--drivers/net/can/dev.c7
-rw-r--r--drivers/net/cnic.c143
-rw-r--r--drivers/net/cnic.h1
-rw-r--r--drivers/net/cnic_if.h1
-rw-r--r--drivers/net/e100.c2
-rw-r--r--drivers/net/e1000e/ich8lan.c94
-rw-r--r--drivers/net/e1000e/netdev.c22
-rw-r--r--drivers/net/eexpress.c6
-rw-r--r--drivers/net/ehea/ehea.h2
-rw-r--r--drivers/net/ehea/ehea_main.c3
-rw-r--r--drivers/net/fec.c9
-rw-r--r--drivers/net/fec_mpc52xx.c5
-rw-r--r--drivers/net/gianfar.c13
-rw-r--r--drivers/net/gianfar_ethtool.c10
-rw-r--r--drivers/net/ibm_newemac/core.c2
-rw-r--r--drivers/net/igbvf/vf.c4
-rw-r--r--drivers/net/irda/au1k_ir.c4
-rw-r--r--drivers/net/irda/pxaficp_ir.c4
-rw-r--r--drivers/net/irda/sa1100_ir.c4
-rw-r--r--drivers/net/irda/w83977af_ir.c2
-rw-r--r--drivers/net/ixgbe/ixgbe.h4
-rw-r--r--drivers/net/ixgbe/ixgbe_82598.c67
-rw-r--r--drivers/net/ixgbe/ixgbe_ethtool.c38
-rw-r--r--drivers/net/ixgbe/ixgbe_fcoe.c2
-rw-r--r--drivers/net/ixgbe/ixgbe_main.c115
-rw-r--r--drivers/net/ixgbe/ixgbe_type.h8
-rw-r--r--drivers/net/ixp2000/ixpdev.c5
-rw-r--r--drivers/net/macb.c7
-rw-r--r--drivers/net/mlx4/en_rx.c5
-rw-r--r--drivers/net/mlx4/en_tx.c6
-rw-r--r--drivers/net/netxen/netxen_nic.h2
-rw-r--r--drivers/net/netxen/netxen_nic_init.c7
-rw-r--r--drivers/net/netxen/netxen_nic_main.c142
-rw-r--r--drivers/net/pcnet32.c32
-rw-r--r--drivers/net/ppp_generic.c34
-rw-r--r--drivers/net/pppoe.c1
-rw-r--r--drivers/net/pppol2tp.c1
-rw-r--r--drivers/net/s6gmac.c2
-rw-r--r--drivers/net/sky2.c14
-rw-r--r--drivers/net/sky2.h1
-rw-r--r--drivers/net/smc91x.c40
-rw-r--r--drivers/net/tulip/de4x5.c6
-rw-r--r--drivers/net/tulip/tulip_core.c5
-rw-r--r--drivers/net/tun.c50
-rw-r--r--drivers/net/ucc_geth.c5
-rw-r--r--drivers/net/usb/pegasus.h2
-rw-r--r--drivers/net/via-rhine.c5
-rw-r--r--drivers/net/via-velocity.c2
-rw-r--r--drivers/net/virtio_net.c61
-rw-r--r--drivers/net/wireless/airo.c13
-rw-r--r--drivers/net/wireless/ath/ar9170/main.c5
-rw-r--r--drivers/net/wireless/ath/ar9170/usb.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom.c4
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2200.c122
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debugfs.c12
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h6
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-sta.c12
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-tx.c14
-rw-r--r--drivers/net/wireless/iwlwifi/iwl3945-base.c7
-rw-r--r--drivers/net/wireless/iwmc3200wifi/commands.c1
-rw-r--r--drivers/net/wireless/iwmc3200wifi/netdev.c6
-rw-r--r--drivers/net/wireless/libertas/11d.c2
-rw-r--r--drivers/net/wireless/libertas/hostcmd.h4
-rw-r--r--drivers/net/wireless/libertas/scan.c3
-rw-r--r--drivers/net/wireless/mwl8k.c31
-rw-r--r--drivers/net/wireless/orinoco/hw.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00.h6
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187_dev.c14
-rw-r--r--drivers/net/wireless/zd1211rw/zd_mac.c2
-rw-r--r--drivers/net/yellowfin.c28
-rw-r--r--drivers/net/zorro8390.c3
-rw-r--r--drivers/parisc/ccio-dma.c1
-rw-r--r--drivers/parisc/dino.c2
-rw-r--r--drivers/parisc/eisa_eeprom.c2
-rw-r--r--drivers/parisc/hppb.c9
-rw-r--r--drivers/parisc/lba_pci.c2
-rw-r--r--drivers/parisc/pdc_stable.c2
-rw-r--r--drivers/pci/hotplug/sgi_hotplug.c7
-rw-r--r--drivers/pci/intel-iommu.c25
-rw-r--r--drivers/pci/iov.c23
-rw-r--r--drivers/pci/pci-driver.c2
-rw-r--r--drivers/pci/pci.c2
-rw-r--r--drivers/pci/pci.h13
-rw-r--r--drivers/pci/setup-bus.c4
-rw-r--r--drivers/pci/setup-res.c12
-rw-r--r--drivers/platform/x86/Kconfig25
-rw-r--r--drivers/platform/x86/eeepc-laptop.c9
-rw-r--r--drivers/platform/x86/hp-wmi.c12
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c390
-rw-r--r--drivers/platform/x86/toshiba_acpi.c1
-rw-r--r--drivers/platform/x86/wmi.c8
-rw-r--r--drivers/pps/pps.c2
-rw-r--r--drivers/s390/block/dasd.c2
-rw-r--r--drivers/s390/cio/device.c4
-rw-r--r--drivers/s390/scsi/zfcp_erp.c68
-rw-r--r--drivers/s390/scsi/zfcp_fc.c8
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c56
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c25
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c7
-rw-r--r--drivers/sbus/char/bbc_envctrl.c11
-rw-r--r--drivers/scsi/libfc/fc_exch.c23
-rw-r--r--drivers/scsi/libiscsi.c4
-rw-r--r--drivers/scsi/libsas/sas_expander.c147
-rw-r--r--drivers/scsi/libsas/sas_port.c19
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c98
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.h6
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_config.c91
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c124
-rw-r--r--drivers/scsi/qla4xxx/ql4_dbg.c15
-rw-r--r--drivers/scsi/qla4xxx/ql4_def.h9
-rw-r--r--drivers/scsi/qla4xxx/ql4_fw.h7
-rw-r--r--drivers/scsi/qla4xxx/ql4_iocb.c133
-rw-r--r--drivers/scsi/qla4xxx/ql4_isr.c145
-rw-r--r--drivers/scsi/qla4xxx/ql4_mbx.c10
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c40
-rw-r--r--drivers/scsi/qla4xxx/ql4_version.h2
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c4
-rw-r--r--drivers/scsi/sd.c20
-rw-r--r--drivers/serial/Kconfig2
-rw-r--r--drivers/serial/s3c2400.c8
-rw-r--r--drivers/serial/s3c2410.c8
-rw-r--r--drivers/serial/s3c2412.c8
-rw-r--r--drivers/serial/s3c2440.c8
-rw-r--r--drivers/serial/s3c24a0.c8
-rw-r--r--drivers/serial/s3c6400.c8
-rw-r--r--drivers/serial/serial_ks8695.c2
-rw-r--r--drivers/spi/spi_s3c24xx.c23
-rw-r--r--drivers/staging/b3dfg/Kconfig1
-rw-r--r--drivers/staging/heci/Kconfig1
-rw-r--r--drivers/staging/rspiusb/rspiusb.c2
-rw-r--r--drivers/staging/rt2860/rt_linux.h13
-rw-r--r--drivers/staging/rt2870/2870_main_dev.c67
-rw-r--r--drivers/staging/rt2870/common/2870_rtmp_init.c33
-rw-r--r--drivers/staging/rt2870/common/rtusb_io.c3
-rw-r--r--drivers/staging/rt2870/rt2870.h9
-rw-r--r--drivers/staging/rtl8192su/ieee80211.h2
-rw-r--r--drivers/staging/rtl8192su/ieee80211/ieee80211.h2
-rw-r--r--drivers/staging/rtl8192su/ieee80211/ieee80211_tx.c3
-rw-r--r--drivers/staging/rtl8192su/r8192U_core.c2
-rw-r--r--drivers/thermal/thermal_sys.c9
-rw-r--r--drivers/usb/class/cdc-acm.c10
-rw-r--r--drivers/usb/class/cdc-acm.h2
-rw-r--r--drivers/usb/core/devio.c10
-rw-r--r--drivers/usb/host/ehci-hcd.c3
-rw-r--r--drivers/usb/host/ehci-q.c15
-rw-r--r--drivers/usb/host/ehci-sched.c1
-rw-r--r--drivers/usb/musb/Kconfig1
-rw-r--r--drivers/usb/serial/ftdi_sio.c3
-rw-r--r--drivers/usb/serial/ftdi_sio.h14
-rw-r--r--drivers/usb/serial/pl2303.c1
-rw-r--r--drivers/usb/serial/pl2303.h4
-rw-r--r--drivers/usb/storage/unusual_devs.h7
-rw-r--r--drivers/video/console/fbcon.c6
-rw-r--r--drivers/video/console/fbcon_rotate.h2
-rw-r--r--drivers/video/console/sticore.c9
-rw-r--r--drivers/video/mx3fb.c86
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c5
-rw-r--r--drivers/video/via/hw.c4
-rw-r--r--drivers/video/via/lcd.c15
-rw-r--r--drivers/video/via/viafbdev.c101
-rw-r--r--drivers/video/via/viafbdev.h3
-rw-r--r--drivers/video/xen-fbfront.c8
-rw-r--r--drivers/w1/masters/omap_hdq.c1
-rw-r--r--drivers/watchdog/ar7_wdt.c2
-rw-r--r--drivers/watchdog/coh901327_wdt.c11
-rw-r--r--drivers/watchdog/ks8695_wdt.c4
-rw-r--r--fs/9p/v9fs.c21
-rw-r--r--fs/9p/v9fs.h1
-rw-r--r--fs/9p/vfs_inode.c126
-rw-r--r--fs/9p/vfs_super.c39
-rw-r--r--fs/afs/file.c18
-rw-r--r--fs/autofs4/expire.c2
-rw-r--r--fs/binfmt_flat.c17
-rw-r--r--fs/btrfs/extent-tree.c21
-rw-r--r--fs/btrfs/free-space-cache.c73
-rw-r--r--fs/btrfs/inode.c24
-rw-r--r--fs/btrfs/relocation.c9
-rw-r--r--fs/btrfs/zlib.c6
-rw-r--r--fs/buffer.c7
-rw-r--r--fs/char_dev.c39
-rw-r--r--fs/cifs/CHANGES7
-rw-r--r--fs/cifs/README25
-rw-r--r--fs/cifs/cifs_dfs_ref.c12
-rw-r--r--fs/cifs/cifs_unicode.c2
-rw-r--r--fs/cifs/cifsfs.c4
-rw-r--r--fs/cifs/connect.c55
-rw-r--r--fs/compat.c17
-rw-r--r--fs/compat_ioctl.c1
-rw-r--r--fs/exec.c67
-rw-r--r--fs/ext2/namei.c4
-rw-r--r--fs/ext3/Kconfig32
-rw-r--r--fs/ext3/super.c40
-rw-r--r--fs/gfs2/sys.c20
-rw-r--r--fs/hugetlbfs/inode.c20
-rw-r--r--fs/inode.c40
-rw-r--r--fs/jffs2/file.c2
-rw-r--r--fs/jffs2/wbuf.c10
-rw-r--r--fs/libfs.c2
-rw-r--r--fs/namei.c22
-rw-r--r--fs/namespace.c3
-rw-r--r--fs/nfs/direct.c20
-rw-r--r--fs/nfs/nfs4state.c4
-rw-r--r--fs/nfs/read.c6
-rw-r--r--fs/nfs/write.c6
-rw-r--r--fs/nilfs2/btnode.c2
-rw-r--r--fs/nilfs2/mdt.c4
-rw-r--r--fs/nilfs2/segment.c16
-rw-r--r--fs/nilfs2/super.c2
-rw-r--r--fs/nilfs2/the_nilfs.h2
-rw-r--r--fs/notify/inotify/inotify_fsnotify.c46
-rw-r--r--fs/notify/inotify/inotify_user.c254
-rw-r--r--fs/notify/notification.c11
-rw-r--r--fs/ocfs2/alloc.c49
-rw-r--r--fs/ocfs2/aops.c69
-rw-r--r--fs/ocfs2/dcache.c46
-rw-r--r--fs/ocfs2/dcache.h3
-rw-r--r--fs/ocfs2/dlm/dlmast.c1
-rw-r--r--fs/ocfs2/dlm/dlmrecovery.c2
-rw-r--r--fs/ocfs2/dlm/dlmunlock.c4
-rw-r--r--fs/ocfs2/file.c5
-rw-r--r--fs/ocfs2/journal.c8
-rw-r--r--fs/ocfs2/journal.h19
-rw-r--r--fs/ocfs2/ocfs2.h22
-rw-r--r--fs/ocfs2/ocfs2_lockid.h1
-rw-r--r--fs/ocfs2/quota.h1
-rw-r--r--fs/ocfs2/quota_global.c144
-rw-r--r--fs/ocfs2/quota_local.c110
-rw-r--r--fs/ocfs2/stack_o2cb.c3
-rw-r--r--fs/ocfs2/super.c34
-rw-r--r--fs/ocfs2/xattr.c3
-rw-r--r--fs/proc/base.c46
-rw-r--r--fs/proc/task_mmu.c1
-rw-r--r--fs/proc/task_nommu.c1
-rw-r--r--fs/select.c1
-rw-r--r--fs/xfs/linux-2.6/xfs_buf.c2
-rw-r--r--fs/xfs/linux-2.6/xfs_ioctl32.c2
-rw-r--r--fs/xfs/linux-2.6/xfs_sync.c13
-rw-r--r--fs/xfs/linux-2.6/xfs_sync.h1
-rw-r--r--fs/xfs/xfs_attr.c8
-rw-r--r--fs/xfs/xfs_bmap.c2
-rw-r--r--fs/xfs/xfs_btree.c4
-rw-r--r--fs/xfs/xfs_da_btree.c6
-rw-r--r--fs/xfs/xfs_dir2.c2
-rw-r--r--fs/xfs/xfs_fsops.c20
-rw-r--r--fs/xfs/xfs_iget.c253
-rw-r--r--fs/xfs/xfs_inode.c10
-rw-r--r--fs/xfs/xfs_inode.h17
-rw-r--r--fs/xfs/xfs_log.c2
-rw-r--r--fs/xfs/xfs_vnodeops.c4
-rw-r--r--include/acpi/acpiosxf.h4
-rw-r--r--include/acpi/processor.h5
-rw-r--r--include/crypto/algapi.h1
-rw-r--r--include/crypto/internal/skcipher.h4
-rw-r--r--include/drm/drm_pciids.h10
-rw-r--r--include/drm/radeon_drm.h6
-rw-r--r--include/linux/binfmts.h1
-rw-r--r--include/linux/bitmap.h18
-rw-r--r--include/linux/blkdev.h1
-rw-r--r--include/linux/cpumask.h20
-rw-r--r--include/linux/decompress/generic.h32
-rw-r--r--include/linux/device-mapper.h4
-rw-r--r--include/linux/dm-log-userspace.h13
-rw-r--r--include/linux/flex_array.h12
-rw-r--r--include/linux/fs.h24
-rw-r--r--include/linux/ftrace_event.h12
-rw-r--r--include/linux/gen_stats.h5
-rw-r--r--include/linux/hugetlb.h6
-rw-r--r--include/linux/inetdevice.h2
-rw-r--r--include/linux/input/matrix_keypad.h13
-rw-r--r--include/linux/kvm_host.h1
-rw-r--r--include/linux/lmb.h2
-rw-r--r--include/linux/mm.h15
-rw-r--r--include/linux/mm_types.h2
-rw-r--r--include/linux/mtd/mtd.h2
-rw-r--r--include/linux/mtd/partitions.h2
-rw-r--r--include/linux/nfs_fs.h5
-rw-r--r--include/linux/nodemask.h28
-rw-r--r--include/linux/perf_counter.h73
-rw-r--r--include/linux/sched.h1
-rw-r--r--include/linux/security.h24
-rw-r--r--include/linux/tty.h4
-rw-r--r--include/linux/tty_ldisc.h2
-rw-r--r--include/linux/ucb1400.h4
-rw-r--r--include/linux/wait.h9
-rw-r--r--include/linux/workqueue.h15
-rw-r--r--include/net/act_api.h2
-rw-r--r--include/net/bluetooth/rfcomm.h12
-rw-r--r--include/net/cfg80211.h5
-rw-r--r--include/net/gen_stats.h10
-rw-r--r--include/net/netfilter/xt_rateest.h2
-rw-r--r--include/net/sch_generic.h2
-rw-r--r--include/sound/ac97_codec.h9
-rw-r--r--include/sound/asound.h2
-rw-r--r--include/sound/core.h51
-rw-r--r--include/sound/info.h4
-rw-r--r--include/sound/memalloc.h6
-rw-r--r--include/sound/pcm.h25
-rw-r--r--include/sound/sh_fsi.h83
-rw-r--r--include/sound/soc-dai.h40
-rw-r--r--include/sound/soc-dapm.h10
-rw-r--r--include/sound/soc.h49
-rw-r--r--include/sound/tlv.h14
-rw-r--r--include/sound/uda1380.h22
-rw-r--r--include/sound/version.h2
-rw-r--r--include/sound/wm8993.h44
-rw-r--r--include/trace/ftrace.h183
-rw-r--r--init/Kconfig11
-rw-r--r--init/main.c9
-rw-r--r--ipc/shm.c8
-rw-r--r--kernel/fork.c47
-rw-r--r--kernel/futex.c28
-rw-r--r--kernel/futex_compat.c6
-rw-r--r--kernel/irq/manage.c27
-rw-r--r--kernel/irq/numa_migrate.c4
-rw-r--r--kernel/lockdep_proc.c3
-rw-r--r--kernel/module.c10
-rw-r--r--kernel/panic.c1
-rw-r--r--kernel/perf_counter.c680
-rw-r--r--kernel/posix-cpu-timers.c7
-rw-r--r--kernel/posix-timers.c7
-rw-r--r--kernel/rtmutex.c4
-rw-r--r--kernel/sched_cpupri.c15
-rw-r--r--kernel/sched_fair.c32
-rw-r--r--kernel/signal.c25
-rw-r--r--kernel/smp.c2
-rw-r--r--kernel/sysctl.c7
-rw-r--r--kernel/time/clockevents.c16
-rw-r--r--kernel/time/tick-broadcast.c7
-rw-r--r--kernel/time/timer_list.c2
-rw-r--r--kernel/trace/blktrace.c12
-rw-r--r--kernel/trace/ftrace.c21
-rw-r--r--kernel/trace/ring_buffer.c15
-rw-r--r--kernel/trace/trace.c25
-rw-r--r--kernel/trace/trace.h4
-rw-r--r--kernel/trace/trace_event_profile.c2
-rw-r--r--kernel/trace/trace_events.c4
-rw-r--r--kernel/trace/trace_events_filter.c20
-rw-r--r--kernel/trace/trace_functions_graph.c11
-rw-r--r--kernel/trace/trace_printk.c2
-rw-r--r--kernel/wait.c5
-rw-r--r--lib/bitmap.c12
-rw-r--r--lib/decompress_bunzip2.c24
-rw-r--r--lib/decompress_inflate.c10
-rw-r--r--lib/decompress_unlzma.c23
-rw-r--r--lib/dma-debug.c28
-rw-r--r--lib/flex_array.c43
-rw-r--r--lib/lmb.c2
-rw-r--r--mm/Kconfig6
-rw-r--r--mm/mempolicy.c84
-rw-r--r--mm/mempool.c4
-rw-r--r--mm/mmap.c3
-rw-r--r--mm/nommu.c10
-rw-r--r--mm/oom_kill.c64
-rw-r--r--mm/page_alloc.c10
-rw-r--r--mm/percpu.c50
-rw-r--r--mm/rmap.c1
-rw-r--r--mm/slub.c4
-rw-r--r--mm/vmscan.c9
-rw-r--r--net/9p/client.c21
-rw-r--r--net/9p/error.c2
-rw-r--r--net/9p/trans_fd.c8
-rw-r--r--net/9p/trans_rdma.c9
-rw-r--r--net/9p/trans_virtio.c11
-rw-r--r--net/appletalk/ddp.c1
-rw-r--r--net/bluetooth/rfcomm/core.c27
-rw-r--r--net/bluetooth/rfcomm/sock.c2
-rw-r--r--net/can/raw.c1
-rw-r--r--net/core/dev.c23
-rw-r--r--net/core/gen_estimator.c12
-rw-r--r--net/core/gen_stats.c11
-rw-r--r--net/core/net_namespace.c2
-rw-r--r--net/core/netpoll.c5
-rw-r--r--net/core/sock.c2
-rw-r--r--net/dccp/proto.c1
-rw-r--r--net/econet/af_econet.c1
-rw-r--r--net/ieee802154/af_ieee802154.c8
-rw-r--r--net/ieee802154/dgram.c14
-rw-r--r--net/ieee802154/raw.c14
-rw-r--r--net/ipv4/arp.c4
-rw-r--r--net/ipv4/ip_gre.c2
-rw-r--r--net/ipv4/ip_output.c2
-rw-r--r--net/ipv6/af_inet6.c4
-rw-r--r--net/irda/af_irda.c1
-rw-r--r--net/llc/af_llc.c1
-rw-r--r--net/mac80211/agg-tx.c8
-rw-r--r--net/mac80211/key.c28
-rw-r--r--net/mac80211/mlme.c2
-rw-r--r--net/mac80211/pm.c24
-rw-r--r--net/mac80211/rx.c12
-rw-r--r--net/netfilter/xt_RATEEST.c2
-rw-r--r--net/netfilter/xt_quota.c2
-rw-r--r--net/netlabel/netlabel_kapi.c2
-rw-r--r--net/netrom/af_netrom.c1
-rw-r--r--net/netrom/nr_route.c21
-rw-r--r--net/phonet/pn_dev.c2
-rw-r--r--net/rose/af_rose.c1
-rw-r--r--net/sched/sch_api.c2
-rw-r--r--net/sched/sch_atm.c2
-rw-r--r--net/sched/sch_cbq.c2
-rw-r--r--net/sched/sch_drr.c2
-rw-r--r--net/sched/sch_hfsc.c2
-rw-r--r--net/sched/sch_htb.c2
-rw-r--r--net/sctp/protocol.c1
-rw-r--r--net/socket.c2
-rw-r--r--net/sunrpc/clnt.c1
-rw-r--r--net/wireless/reg.c9
-rw-r--r--net/wireless/reg.h3
-rw-r--r--net/wireless/scan.c4
-rw-r--r--net/xfrm/xfrm_hash.h2
-rwxr-xr-xscripts/recordmcount.pl12
-rw-r--r--security/Kconfig16
-rw-r--r--security/Makefile2
-rw-r--r--security/capability.c9
-rw-r--r--security/commoncap.c30
-rw-r--r--security/integrity/ima/ima_crypto.c6
-rw-r--r--security/integrity/ima/ima_main.c10
-rw-r--r--security/min_addr.c49
-rw-r--r--security/selinux/hooks.c17
-rw-r--r--sound/Kconfig28
-rw-r--r--sound/arm/pxa2xx-ac97.c10
-rw-r--r--sound/arm/pxa2xx-pcm-lib.c3
-rw-r--r--sound/core/Kconfig4
-rw-r--r--sound/core/Makefile2
-rw-r--r--sound/core/control.c34
-rw-r--r--sound/core/info.c8
-rw-r--r--sound/core/init.c8
-rw-r--r--sound/core/memalloc.c4
-rw-r--r--sound/core/misc.c75
-rw-r--r--sound/core/oss/mixer_oss.c3
-rw-r--r--sound/core/oss/pcm_oss.c12
-rw-r--r--sound/core/pcm.c26
-rw-r--r--sound/core/pcm_lib.c51
-rw-r--r--sound/core/pcm_memory.c2
-rw-r--r--sound/core/pcm_native.c64
-rw-r--r--sound/core/rawmidi.c2
-rw-r--r--sound/core/seq/oss/seq_oss_midi.c14
-rw-r--r--sound/core/seq/seq_midi.c7
-rw-r--r--sound/core/vmaster.c8
-rw-r--r--sound/drivers/dummy.c700
-rw-r--r--sound/isa/cmi8330.c86
-rw-r--r--sound/oss/midibuf.c7
-rw-r--r--sound/oss/vwsnd.c6
-rw-r--r--sound/pci/Kconfig4
-rw-r--r--sound/pci/ali5451/ali5451.c83
-rw-r--r--sound/pci/azt3328.c1116
-rw-r--r--sound/pci/azt3328.h103
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.h2
-rw-r--r--sound/pci/ctxfi/ct20k2reg.h9
-rw-r--r--sound/pci/ctxfi/ctamixer.c20
-rw-r--r--sound/pci/ctxfi/ctatc.c77
-rw-r--r--sound/pci/ctxfi/ctdaio.c30
-rw-r--r--sound/pci/ctxfi/cthw20k1.c22
-rw-r--r--sound/pci/ctxfi/cthw20k2.c73
-rw-r--r--sound/pci/ctxfi/ctmixer.c8
-rw-r--r--sound/pci/ctxfi/ctpcm.c6
-rw-r--r--sound/pci/ctxfi/ctresource.c4
-rw-r--r--sound/pci/ctxfi/ctsrc.c10
-rw-r--r--sound/pci/ctxfi/ctvmem.c6
-rw-r--r--sound/pci/hda/Kconfig27
-rw-r--r--sound/pci/hda/Makefile4
-rw-r--r--sound/pci/hda/hda_beep.c4
-rw-r--r--sound/pci/hda/hda_codec.c70
-rw-r--r--sound/pci/hda/hda_codec.h12
-rw-r--r--sound/pci/hda/hda_generic.c18
-rw-r--r--sound/pci/hda/hda_hwdep.c236
-rw-r--r--sound/pci/hda/hda_intel.c177
-rw-r--r--sound/pci/hda/hda_local.h14
-rw-r--r--sound/pci/hda/hda_proc.c7
-rw-r--r--sound/pci/hda/patch_analog.c137
-rw-r--r--sound/pci/hda/patch_atihdmi.c3
-rw-r--r--sound/pci/hda/patch_ca0110.c3
-rw-r--r--sound/pci/hda/patch_cirrus.c1194
-rw-r--r--sound/pci/hda/patch_cmedia.c3
-rw-r--r--sound/pci/hda/patch_conexant.c479
-rw-r--r--sound/pci/hda/patch_intelhdmi.c104
-rw-r--r--sound/pci/hda/patch_nvhdmi.c2
-rw-r--r--sound/pci/hda/patch_realtek.c4209
-rw-r--r--sound/pci/hda/patch_sigmatel.c1221
-rw-r--r--sound/pci/hda/patch_via.c4
-rw-r--r--sound/pci/ice1712/ice1712.h9
-rw-r--r--sound/pci/ice1712/ice1724.c112
-rw-r--r--sound/pci/ice1712/prodigy_hifi.c46
-rw-r--r--sound/pci/oxygen/oxygen_io.c11
-rw-r--r--sound/pci/oxygen/oxygen_lib.c3
-rw-r--r--sound/pci/oxygen/oxygen_pcm.c2
-rw-r--r--sound/pci/rme9652/hdsp.c39
-rw-r--r--sound/pci/vx222/vx222_ops.c4
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile3
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c138
-rw-r--r--sound/soc/au1x/psc-ac97.c129
-rw-r--r--sound/soc/au1x/psc.h1
-rw-r--r--sound/soc/blackfin/Kconfig31
-rw-r--r--sound/soc/blackfin/Makefile8
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c10
-rw-r--r--sound/soc/blackfin/bf5xx-ad1836.c128
-rw-r--r--sound/soc/blackfin/bf5xx-ad1938.c142
-rw-r--r--sound/soc/blackfin/bf5xx-ad73311.c16
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c8
-rw-r--r--sound/soc/blackfin/bf5xx-ssm2602.c16
-rw-r--r--sound/soc/blackfin/bf5xx-tdm-pcm.c330
-rw-r--r--sound/soc/blackfin/bf5xx-tdm-pcm.h21
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.c343
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.h14
-rw-r--r--sound/soc/codecs/Kconfig44
-rw-r--r--sound/soc/codecs/Makefile26
-rw-r--r--sound/soc/codecs/ad1836.c446
-rw-r--r--sound/soc/codecs/ad1836.h64
-rw-r--r--sound/soc/codecs/ad1938.c682
-rw-r--r--sound/soc/codecs/ad1938.h100
-rw-r--r--sound/soc/codecs/ak4535.c16
-rw-r--r--sound/soc/codecs/ak4642.c502
-rw-r--r--sound/soc/codecs/ak4642.h20
-rw-r--r--sound/soc/codecs/cs4270.c27
-rw-r--r--sound/soc/codecs/cx20442.c501
-rw-r--r--sound/soc/codecs/cx20442.h20
-rw-r--r--sound/soc/codecs/max9877.c308
-rw-r--r--sound/soc/codecs/max9877.h37
-rw-r--r--sound/soc/codecs/spdif_transciever.c3
-rw-r--r--sound/soc/codecs/stac9766.c4
-rw-r--r--sound/soc/codecs/tlv320aic3x.c233
-rw-r--r--sound/soc/codecs/tlv320aic3x.h2
-rw-r--r--sound/soc/codecs/twl4030.c260
-rw-r--r--sound/soc/codecs/twl4030.h2
-rw-r--r--sound/soc/codecs/uda134x.c2
-rw-r--r--sound/soc/codecs/uda1380.c313
-rw-r--r--sound/soc/codecs/uda1380.h8
-rw-r--r--sound/soc/codecs/wm8350.c51
-rw-r--r--sound/soc/codecs/wm8400.c26
-rw-r--r--sound/soc/codecs/wm8510.c175
-rw-r--r--sound/soc/codecs/wm8523.c699
-rw-r--r--sound/soc/codecs/wm8523.h160
-rw-r--r--sound/soc/codecs/wm8580.c211
-rw-r--r--sound/soc/codecs/wm8728.c111
-rw-r--r--sound/soc/codecs/wm8731.c218
-rw-r--r--sound/soc/codecs/wm8750.c154
-rw-r--r--sound/soc/codecs/wm8753.c35
-rw-r--r--sound/soc/codecs/wm8776.c744
-rw-r--r--sound/soc/codecs/wm8776.h51
-rw-r--r--sound/soc/codecs/wm8900.c345
-rw-r--r--sound/soc/codecs/wm8903.c267
-rw-r--r--sound/soc/codecs/wm8940.c160
-rw-r--r--sound/soc/codecs/wm8960.c233
-rw-r--r--sound/soc/codecs/wm8961.c1265
-rw-r--r--sound/soc/codecs/wm8961.h866
-rw-r--r--sound/soc/codecs/wm8971.c127
-rw-r--r--sound/soc/codecs/wm8974.c808
-rw-r--r--sound/soc/codecs/wm8974.h99
-rw-r--r--sound/soc/codecs/wm8988.c180
-rw-r--r--sound/soc/codecs/wm8990.c194
-rw-r--r--sound/soc/codecs/wm8993.c1675
-rw-r--r--sound/soc/codecs/wm8993.h2132
-rw-r--r--sound/soc/codecs/wm9081.c317
-rw-r--r--sound/soc/codecs/wm9705.c2
-rw-r--r--sound/soc/codecs/wm_hubs.c743
-rw-r--r--sound/soc/codecs/wm_hubs.h24
-rw-r--r--sound/soc/davinci/Kconfig33
-rw-r--r--sound/soc/davinci/Makefile5
-rw-r--r--sound/soc/davinci/davinci-evm.c140
-rw-r--r--sound/soc/davinci/davinci-i2s.c340
-rw-r--r--sound/soc/davinci/davinci-mcasp.c973
-rw-r--r--sound/soc/davinci/davinci-mcasp.h60
-rw-r--r--sound/soc/davinci/davinci-pcm.c10
-rw-r--r--sound/soc/davinci/davinci-pcm.h19
-rw-r--r--sound/soc/fsl/efika-audio-fabric.c2
-rw-r--r--sound/soc/fsl/mpc5200_dma.c17
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.c3
-rw-r--r--sound/soc/fsl/pcm030-audio-fabric.c2
-rw-r--r--sound/soc/imx/Kconfig21
-rw-r--r--sound/soc/imx/Makefile10
-rw-r--r--sound/soc/imx/mx1_mx2-pcm.c488
-rw-r--r--sound/soc/imx/mx1_mx2-pcm.h26
-rw-r--r--sound/soc/imx/mx27vis_wm8974.c317
-rw-r--r--sound/soc/imx/mxc-ssi.c868
-rw-r--r--sound/soc/imx/mxc-ssi.h238
-rw-r--r--sound/soc/omap/Kconfig15
-rw-r--r--sound/soc/omap/Makefile4
-rw-r--r--sound/soc/omap/ams-delta.c646
-rw-r--r--sound/soc/omap/n810.c12
-rw-r--r--sound/soc/omap/omap-mcbsp.c123
-rw-r--r--sound/soc/omap/omap-mcbsp.h4
-rw-r--r--sound/soc/omap/omap-pcm.c53
-rw-r--r--sound/soc/omap/omap-pcm.h2
-rw-r--r--sound/soc/omap/sdp3430.c18
-rw-r--r--sound/soc/omap/zoom2.c314
-rw-r--r--sound/soc/pxa/magician.c56
-rw-r--r--sound/soc/pxa/palm27x.c204
-rw-r--r--sound/soc/pxa/pxa-ssp.c77
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c12
-rw-r--r--sound/soc/s3c24xx/Kconfig35
-rw-r--r--sound/soc/s3c24xx/Makefile9
-rw-r--r--sound/soc/s3c24xx/neo1973_gta02_wm8753.c498
-rw-r--r--sound/soc/s3c24xx/s3c-i2s-v2.c17
-rw-r--r--sound/soc/s3c24xx/s3c2443-ac97.c20
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.c5
-rw-r--r--sound/soc/s3c24xx/s3c24xx-pcm.c2
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec.c394
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec.h22
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec_hermes.c153
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c137
-rw-r--r--sound/soc/s6000/s6105-ipcam.c12
-rw-r--r--sound/soc/sh/Kconfig15
-rw-r--r--sound/soc/sh/Makefile4
-rw-r--r--sound/soc/sh/fsi-ak4642.c107
-rw-r--r--sound/soc/sh/fsi.c1004
-rw-r--r--sound/soc/soc-cache.c218
-rw-r--r--sound/soc/soc-core.c148
-rw-r--r--sound/soc/soc-dapm.c498
-rw-r--r--sound/soc/soc-jack.c24
-rw-r--r--sound/soc/txx9/txx9aclc.c10
-rw-r--r--sound/sound_core.c100
-rw-r--r--sound/usb/usbaudio.c6
-rw-r--r--sound/usb/usbmidi.c290
-rw-r--r--sound/usb/usbmixer.c73
-rw-r--r--tools/perf/Documentation/Makefile2
-rw-r--r--tools/perf/Documentation/examples.txt225
-rw-r--r--tools/perf/Documentation/perf-record.txt60
-rw-r--r--tools/perf/Documentation/perf-stat.txt2
-rw-r--r--tools/perf/Documentation/perf-top.txt112
-rw-r--r--tools/perf/Makefile40
-rw-r--r--tools/perf/builtin-annotate.c14
-rw-r--r--tools/perf/builtin-list.c3
-rw-r--r--tools/perf/builtin-record.c108
-rw-r--r--tools/perf/builtin-report.c146
-rw-r--r--tools/perf/builtin-stat.c2
-rw-r--r--tools/perf/builtin-top.c553
-rw-r--r--tools/perf/util/callchain.c32
-rw-r--r--tools/perf/util/callchain.h8
-rw-r--r--tools/perf/util/header.c5
-rw-r--r--tools/perf/util/parse-events.c36
-rw-r--r--tools/perf/util/parse-events.h1
-rw-r--r--tools/perf/util/quote.c2
-rw-r--r--tools/perf/util/symbol.c133
-rw-r--r--tools/perf/util/symbol.h26
-rw-r--r--virt/kvm/ioapic.c10
-rw-r--r--virt/kvm/irq_comm.c4
1170 files changed, 43722 insertions, 14273 deletions
diff --git a/Documentation/ABI/testing/sysfs-block b/Documentation/ABI/testing/sysfs-block
index cbbd3e06994..5f3bedaf8e3 100644
--- a/Documentation/ABI/testing/sysfs-block
+++ b/Documentation/ABI/testing/sysfs-block
@@ -94,28 +94,37 @@ What: /sys/block/<disk>/queue/physical_block_size
Date: May 2009
Contact: Martin K. Petersen <martin.petersen@oracle.com>
Description:
- This is the smallest unit the storage device can write
- without resorting to read-modify-write operation. It is
- usually the same as the logical block size but may be
- bigger. One example is SATA drives with 4KB sectors
- that expose a 512-byte logical block size to the
- operating system.
+ This is the smallest unit a physical storage device can
+ write atomically. It is usually the same as the logical
+ block size but may be bigger. One example is SATA
+ drives with 4KB sectors that expose a 512-byte logical
+ block size to the operating system. For stacked block
+ devices the physical_block_size variable contains the
+ maximum physical_block_size of the component devices.
What: /sys/block/<disk>/queue/minimum_io_size
Date: April 2009
Contact: Martin K. Petersen <martin.petersen@oracle.com>
Description:
- Storage devices may report a preferred minimum I/O size,
- which is the smallest request the device can perform
- without incurring a read-modify-write penalty. For disk
- drives this is often the physical block size. For RAID
- arrays it is often the stripe chunk size.
+ Storage devices may report a granularity or preferred
+ minimum I/O size which is the smallest request the
+ device can perform without incurring a performance
+ penalty. For disk drives this is often the physical
+ block size. For RAID arrays it is often the stripe
+ chunk size. A properly aligned multiple of
+ minimum_io_size is the preferred request size for
+ workloads where a high number of I/O operations is
+ desired.
What: /sys/block/<disk>/queue/optimal_io_size
Date: April 2009
Contact: Martin K. Petersen <martin.petersen@oracle.com>
Description:
Storage devices may report an optimal I/O size, which is
- the device's preferred unit of receiving I/O. This is
- rarely reported for disk drives. For RAID devices it is
- usually the stripe width or the internal block size.
+ the device's preferred unit for sustained I/O. This is
+ rarely reported for disk drives. For RAID arrays it is
+ usually the stripe width or the internal track size. A
+ properly aligned multiple of optimal_io_size is the
+ preferred request size for workloads where sustained
+ throughput is desired. If no optimal I/O size is
+ reported this file contains 0.
diff --git a/Documentation/DocBook/kernel-hacking.tmpl b/Documentation/DocBook/kernel-hacking.tmpl
index a50d6cd5857..992e67e6be7 100644
--- a/Documentation/DocBook/kernel-hacking.tmpl
+++ b/Documentation/DocBook/kernel-hacking.tmpl
@@ -449,8 +449,8 @@ printk(KERN_INFO "i = %u\n", i);
</para>
<programlisting>
-__u32 ipaddress;
-printk(KERN_INFO "my ip: %d.%d.%d.%d\n", NIPQUAD(ipaddress));
+__be32 ipaddress;
+printk(KERN_INFO "my ip: %pI4\n", &amp;ipaddress);
</programlisting>
<para>
diff --git a/Documentation/arm/memory.txt b/Documentation/arm/memory.txt
index 43cb1004d35..9d58c7c5edd 100644
--- a/Documentation/arm/memory.txt
+++ b/Documentation/arm/memory.txt
@@ -21,6 +21,8 @@ ffff8000 ffffffff copy_user_page / clear_user_page use.
For SA11xx and Xscale, this is used to
setup a minicache mapping.
+ffff4000 ffffffff cache aliasing on ARMv6 and later CPUs.
+
ffff1000 ffff7fff Reserved.
Platforms must not use this address range.
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index 09e031c5588..f0690bbbd73 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -468,3 +468,27 @@ Why: cpu_policy_rwsem has a new cleaner definition making it local to
cpufreq core and contained inside cpufreq.c. Other dependent
drivers should not use it in order to safely avoid lockdep issues.
Who: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
+
+----------------------------
+
+What: sound-slot/service-* module aliases and related clutters in
+ sound/sound_core.c
+When: August 2010
+Why: OSS sound_core grabs all legacy minors (0-255) of SOUND_MAJOR
+ (14) and requests modules using custom sound-slot/service-*
+ module aliases. The only benefit of doing this is allowing
+ use of custom module aliases which might as well be considered
+ a bug at this point. This preemptive claiming prevents
+ alternative OSS implementations.
+
+ Till the feature is removed, the kernel will be requesting
+ both sound-slot/service-* and the standard char-major-* module
+ aliases and allow turning off the pre-claiming selectively via
+ CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss
+ kernel parameter.
+
+ After the transition phase is complete, both the custom module
+ aliases and switches to disable it will go away. This removal
+ will also allow making ALSA OSS emulation independent of
+ sound_core. The dependency will be broken then too.
+Who: Tejun Heo <tj@kernel.org>
diff --git a/Documentation/filesystems/9p.txt b/Documentation/filesystems/9p.txt
index bf8080640eb..6208f55c44c 100644
--- a/Documentation/filesystems/9p.txt
+++ b/Documentation/filesystems/9p.txt
@@ -123,6 +123,9 @@ available from the same CVS repository.
There are user and developer mailing lists available through the v9fs project
on sourceforge (http://sourceforge.net/projects/v9fs).
+A stand-alone version of the module (which should build for any 2.6 kernel)
+is available via (http://github.com/ericvh/9p-sac/tree/master)
+
News and other information is maintained on SWiK (http://swik.net/v9fs).
Bug reports may be issued through the kernel.org bugzilla
diff --git a/Documentation/filesystems/afs.txt b/Documentation/filesystems/afs.txt
index 12ad6c7f4e5..ffef91c4e0d 100644
--- a/Documentation/filesystems/afs.txt
+++ b/Documentation/filesystems/afs.txt
@@ -23,15 +23,13 @@ it does support include:
(*) Security (currently only AFS kaserver and KerberosIV tickets).
- (*) File reading.
+ (*) File reading and writing.
(*) Automounting.
-It does not yet support the following AFS features:
-
- (*) Write support.
+ (*) Local caching (via fscache).
- (*) Local caching.
+It does not yet support the following AFS features:
(*) pioctl() system call.
@@ -56,7 +54,7 @@ They permit the debugging messages to be turned on dynamically by manipulating
the masks in the following files:
/sys/module/af_rxrpc/parameters/debug
- /sys/module/afs/parameters/debug
+ /sys/module/kafs/parameters/debug
=====
@@ -66,9 +64,9 @@ USAGE
When inserting the driver modules the root cell must be specified along with a
list of volume location server IP addresses:
- insmod af_rxrpc.o
- insmod rxkad.o
- insmod kafs.o rootcell=cambridge.redhat.com:172.16.18.73:172.16.18.91
+ modprobe af_rxrpc
+ modprobe rxkad
+ modprobe kafs rootcell=cambridge.redhat.com:172.16.18.73:172.16.18.91
The first module is the AF_RXRPC network protocol driver. This provides the
RxRPC remote operation protocol and may also be accessed from userspace. See:
@@ -81,7 +79,7 @@ is the actual filesystem driver for the AFS filesystem.
Once the module has been loaded, more modules can be added by the following
procedure:
- echo add grand.central.org 18.7.14.88:128.2.191.224 >/proc/fs/afs/cells
+ echo add grand.central.org 18.9.48.14:128.2.203.61:130.237.48.87 >/proc/fs/afs/cells
Where the parameters to the "add" command are the name of a cell and a list of
volume location servers within that cell, with the latter separated by colons.
@@ -101,7 +99,7 @@ The name of the volume can be suffixes with ".backup" or ".readonly" to
specify connection to only volumes of those types.
The name of the cell is optional, and if not given during a mount, then the
-named volume will be looked up in the cell specified during insmod.
+named volume will be looked up in the cell specified during modprobe.
Additional cells can be added through /proc (see later section).
@@ -163,14 +161,14 @@ THE CELL DATABASE
The filesystem maintains an internal database of all the cells it knows and the
IP addresses of the volume location servers for those cells. The cell to which
-the system belongs is added to the database when insmod is performed by the
+the system belongs is added to the database when modprobe is performed by the
"rootcell=" argument or, if compiled in, using a "kafs.rootcell=" argument on
the kernel command line.
Further cells can be added by commands similar to the following:
echo add CELLNAME VLADDR[:VLADDR][:VLADDR]... >/proc/fs/afs/cells
- echo add grand.central.org 18.7.14.88:128.2.191.224 >/proc/fs/afs/cells
+ echo add grand.central.org 18.9.48.14:128.2.203.61:130.237.48.87 >/proc/fs/afs/cells
No other cell database operations are available at this time.
@@ -233,7 +231,7 @@ insmod /tmp/kafs.o rootcell=cambridge.redhat.com:172.16.18.91
mount -t afs \%root.afs. /afs
mount -t afs \%cambridge.redhat.com:root.cell. /afs/cambridge.redhat.com/
-echo add grand.central.org 18.7.14.88:128.2.191.224 > /proc/fs/afs/cells
+echo add grand.central.org 18.9.48.14:128.2.203.61:130.237.48.87 > /proc/fs/afs/cells
mount -t afs "#grand.central.org:root.cell." /afs/grand.central.org/
mount -t afs "#grand.central.org:root.archive." /afs/grand.central.org/archive
mount -t afs "#grand.central.org:root.contrib." /afs/grand.central.org/contrib
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt
index fad18f9456e..ffead13f944 100644
--- a/Documentation/filesystems/proc.txt
+++ b/Documentation/filesystems/proc.txt
@@ -1167,13 +1167,11 @@ CHAPTER 3: PER-PROCESS PARAMETERS
3.1 /proc/<pid>/oom_adj - Adjust the oom-killer score
------------------------------------------------------
-This file can be used to adjust the score used to select which processes should
-be killed in an out-of-memory situation. The oom_adj value is a characteristic
-of the task's mm, so all threads that share an mm with pid will have the same
-oom_adj value. A high value will increase the likelihood of this process being
-killed by the oom-killer. Valid values are in the range -16 to +15 as
-explained below and a special value of -17, which disables oom-killing
-altogether for threads sharing pid's mm.
+This file can be used to adjust the score used to select which processes
+should be killed in an out-of-memory situation. Giving it a high score will
+increase the likelihood of this process being killed by the oom-killer. Valid
+values are in the range -16 to +15, plus the special value -17, which disables
+oom-killing altogether for this process.
The process to be killed in an out-of-memory situation is selected among all others
based on its badness score. This value equals the original memory size of the process
@@ -1187,9 +1185,6 @@ the parent's score if they do not share the same memory. Thus forking servers
are the prime candidates to be killed. Having only one 'hungry' child will make
parent less preferable than the child.
-/proc/<pid>/oom_adj cannot be changed for kthreads since they are immune from
-oom-killing already.
-
/proc/<pid>/oom_score shows process' current badness score.
The following heuristics are then applied:
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 7bb0d934b6d..dbea4f95fc8 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -139,6 +139,7 @@ Code Seq# Include File Comments
'm' all linux/synclink.h conflict!
'm' 00-1F net/irda/irmod.h conflict!
'n' 00-7F linux/ncp_fs.h
+'n' 80-8F linux/nilfs2_fs.h NILFS2
'n' E0-FF video/matrox.h matroxfb
'o' 00-1F fs/ocfs2/ocfs2_fs.h OCFS2
'o' 00-03 include/mtd/ubi-user.h conflict! (OCFS2 and UBI overlaps)
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index dd1a6d4bb74..7936b801fe6 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1115,6 +1115,10 @@ and is between 256 and 4096 characters. It is defined in the file
libata.dma=4 Compact Flash DMA only
Combinations also work, so libata.dma=3 enables DMA
for disks and CDROMs, but not CFs.
+
+ libata.ignore_hpa= [LIBATA] Ignore HPA limit
+ libata.ignore_hpa=0 keep BIOS limits (default)
+ libata.ignore_hpa=1 ignore limits, using full disk
libata.noacpi [LIBATA] Disables use of ACPI in libata suspend/resume
when set.
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
index f2296ecedb8..e2ddcdeb61b 100644
--- a/Documentation/laptops/thinkpad-acpi.txt
+++ b/Documentation/laptops/thinkpad-acpi.txt
@@ -36,8 +36,6 @@ detailed description):
- Bluetooth enable and disable
- video output switching, expansion control
- ThinkLight on and off
- - limited docking and undocking
- - UltraBay eject
- CMOS/UCMS control
- LED control
- ACPI sounds
@@ -729,131 +727,6 @@ cannot be read or if it is unknown, thinkpad-acpi will report it as "off".
It is impossible to know if the status returned through sysfs is valid.
-Docking / undocking -- /proc/acpi/ibm/dock
-------------------------------------------
-
-Docking and undocking (e.g. with the X4 UltraBase) requires some
-actions to be taken by the operating system to safely make or break
-the electrical connections with the dock.
-
-The docking feature of this driver generates the following ACPI events:
-
- ibm/dock GDCK 00000003 00000001 -- eject request
- ibm/dock GDCK 00000003 00000002 -- undocked
- ibm/dock GDCK 00000000 00000003 -- docked
-
-NOTE: These events will only be generated if the laptop was docked
-when originally booted. This is due to the current lack of support for
-hot plugging of devices in the Linux ACPI framework. If the laptop was
-booted while not in the dock, the following message is shown in the
-logs:
-
- Mar 17 01:42:34 aero kernel: thinkpad_acpi: dock device not present
-
-In this case, no dock-related events are generated but the dock and
-undock commands described below still work. They can be executed
-manually or triggered by Fn key combinations (see the example acpid
-configuration files included in the driver tarball package available
-on the web site).
-
-When the eject request button on the dock is pressed, the first event
-above is generated. The handler for this event should issue the
-following command:
-
- echo undock > /proc/acpi/ibm/dock
-
-After the LED on the dock goes off, it is safe to eject the laptop.
-Note: if you pressed this key by mistake, go ahead and eject the
-laptop, then dock it back in. Otherwise, the dock may not function as
-expected.
-
-When the laptop is docked, the third event above is generated. The
-handler for this event should issue the following command to fully
-enable the dock:
-
- echo dock > /proc/acpi/ibm/dock
-
-The contents of the /proc/acpi/ibm/dock file shows the current status
-of the dock, as provided by the ACPI framework.
-
-The docking support in this driver does not take care of enabling or
-disabling any other devices you may have attached to the dock. For
-example, a CD drive plugged into the UltraBase needs to be disabled or
-enabled separately. See the provided example acpid configuration files
-for how this can be accomplished.
-
-There is no support yet for PCI devices that may be attached to a
-docking station, e.g. in the ThinkPad Dock II. The driver currently
-does not recognize, enable or disable such devices. This means that
-the only docking stations currently supported are the X-series
-UltraBase docks and "dumb" port replicators like the Mini Dock (the
-latter don't need any ACPI support, actually).
-
-
-UltraBay eject -- /proc/acpi/ibm/bay
-------------------------------------
-
-Inserting or ejecting an UltraBay device requires some actions to be
-taken by the operating system to safely make or break the electrical
-connections with the device.
-
-This feature generates the following ACPI events:
-
- ibm/bay MSTR 00000003 00000000 -- eject request
- ibm/bay MSTR 00000001 00000000 -- eject lever inserted
-
-NOTE: These events will only be generated if the UltraBay was present
-when the laptop was originally booted (on the X series, the UltraBay
-is in the dock, so it may not be present if the laptop was undocked).
-This is due to the current lack of support for hot plugging of devices
-in the Linux ACPI framework. If the laptop was booted without the
-UltraBay, the following message is shown in the logs:
-
- Mar 17 01:42:34 aero kernel: thinkpad_acpi: bay device not present
-
-In this case, no bay-related events are generated but the eject
-command described below still works. It can be executed manually or
-triggered by a hot key combination.
-
-Sliding the eject lever generates the first event shown above. The
-handler for this event should take whatever actions are necessary to
-shut down the device in the UltraBay (e.g. call idectl), then issue
-the following command:
-
- echo eject > /proc/acpi/ibm/bay
-
-After the LED on the UltraBay goes off, it is safe to pull out the
-device.
-
-When the eject lever is inserted, the second event above is
-generated. The handler for this event should take whatever actions are
-necessary to enable the UltraBay device (e.g. call idectl).
-
-The contents of the /proc/acpi/ibm/bay file shows the current status
-of the UltraBay, as provided by the ACPI framework.
-
-EXPERIMENTAL warm eject support on the 600e/x, A22p and A3x (To use
-this feature, you need to supply the experimental=1 parameter when
-loading the module):
-
-These models do not have a button near the UltraBay device to request
-a hot eject but rather require the laptop to be put to sleep
-(suspend-to-ram) before the bay device is ejected or inserted).
-The sequence of steps to eject the device is as follows:
-
- echo eject > /proc/acpi/ibm/bay
- put the ThinkPad to sleep
- remove the drive
- resume from sleep
- cat /proc/acpi/ibm/bay should show that the drive was removed
-
-On the A3x, both the UltraBay 2000 and UltraBay Plus devices are
-supported. Use "eject2" instead of "eject" for the second bay.
-
-Note: the UltraBay eject support on the 600e/x, A22p and A3x is
-EXPERIMENTAL and may not work as expected. USE WITH CAUTION!
-
-
CMOS/UCMS control
-----------------
diff --git a/Documentation/lockdep-design.txt b/Documentation/lockdep-design.txt
index e20d913d591..abf768c681e 100644
--- a/Documentation/lockdep-design.txt
+++ b/Documentation/lockdep-design.txt
@@ -30,9 +30,9 @@ State
The validator tracks lock-class usage history into 4n + 1 separate state bits:
- 'ever held in STATE context'
-- 'ever head as readlock in STATE context'
-- 'ever head with STATE enabled'
-- 'ever head as readlock with STATE enabled'
+- 'ever held as readlock in STATE context'
+- 'ever held with STATE enabled'
+- 'ever held as readlock with STATE enabled'
Where STATE can be either one of (kernel/lockdep_states.h)
- hardirq
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index 4252697a95d..1c8eb4518ce 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -60,6 +60,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
slots - Reserve the slot index for the given driver.
This option takes multiple strings.
See "Module Autoloading Support" section for details.
+ debug - Specifies the debug message level
+ (0 = disable debug prints, 1 = normal debug messages,
+ 2 = verbose debug messages)
+ This option appears only when CONFIG_SND_DEBUG=y.
+ This option can be dynamically changed via sysfs
+ /sys/modules/snd/parameters/debug file.
Module snd-pcm-oss
------------------
@@ -513,6 +519,26 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
or input, but you may use this module for any application which
requires a sound card (like RealPlayer).
+ pcm_devs - Number of PCM devices assigned to each card
+ (default = 1, up to 4)
+ pcm_substreams - Number of PCM substreams assigned to each PCM
+ (default = 8, up to 16)
+ hrtimer - Use hrtimer (=1, default) or system timer (=0)
+ fake_buffer - Fake buffer allocations (default = 1)
+
+ When multiple PCM devices are created, snd-dummy gives different
+ behavior to each PCM device:
+ 0 = interleaved with mmap support
+ 1 = non-interleaved with mmap support
+ 2 = interleaved without mmap
+ 3 = non-interleaved without mmap
+
+ As default, snd-dummy drivers doesn't allocate the real buffers
+ but either ignores read/write or mmap a single dummy page to all
+ buffer pages, in order to save the resouces. If your apps need
+ the read/ written buffer data to be consistent, pass fake_buffer=0
+ option.
+
The power-management is supported.
Module snd-echo3g
@@ -768,6 +794,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
bdl_pos_adj - Specifies the DMA IRQ timing delay in samples.
Passing -1 will make the driver to choose the appropriate
value based on the controller chip.
+ patch - Specifies the early "patch" files to modify the HD-audio
+ setup before initializing the codecs. This option is
+ available only when CONFIG_SND_HDA_PATCH_LOADER=y is set.
+ See HD-Audio.txt for details.
[Single (global) options]
single_cmd - Use single immediate commands to communicate with
diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt
index 939a3dd5814..97eebd63bed 100644
--- a/Documentation/sound/alsa/HD-Audio-Models.txt
+++ b/Documentation/sound/alsa/HD-Audio-Models.txt
@@ -114,8 +114,8 @@ ALC662/663/272
samsung-nc10 Samsung NC10 mini notebook
auto auto-config reading BIOS (default)
-ALC882/885
-==========
+ALC882/883/885/888/889
+======================
3stack-dig 3-jack with SPDIF I/O
6stack-dig 6-jack digital with SPDIF I/O
arima Arima W820Di1
@@ -127,12 +127,8 @@ ALC882/885
mbp3 Macbook Pro rev3
imac24 iMac 24'' with jack detection
w2jc ASUS W2JC
- auto auto-config reading BIOS (default)
-
-ALC883/888
-==========
- 3stack-dig 3-jack with SPDIF I/O
- 6stack-dig 6-jack digital with SPDIF I/O
+ 3stack-2ch-dig 3-jack with SPDIF I/O (ALC883)
+ alc883-6stack-dig 6-jack digital with SPDIF I/O (ALC883)
3stack-6ch 3-jack 6-channel
3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
6stack-dig-demo 6-jack digital for Intel demo board
@@ -140,6 +136,7 @@ ALC883/888
acer-aspire Acer Aspire 9810
acer-aspire-4930g Acer Aspire 4930G
acer-aspire-6530g Acer Aspire 6530G
+ acer-aspire-7730g Acer Aspire 7730G
acer-aspire-8930g Acer Aspire 8930G
medion Medion Laptops
medion-md2 Medion MD2
@@ -155,10 +152,13 @@ ALC883/888
3stack-hp HP machines with 3stack (Lucknow, Samba boards)
6stack-dell Dell machines with 6stack (Inspiron 530)
mitac Mitac 8252D
+ clevo-m540r Clevo M540R (6ch + digital)
clevo-m720 Clevo M720 laptop series
fujitsu-pi2515 Fujitsu AMILO Pi2515
fujitsu-xa3530 Fujitsu AMILO XA3530
3stack-6ch-intel Intel DG33* boards
+ intel-alc889a Intel IbexPeak with ALC889A
+ intel-x58 Intel DX58 with ALC889
asus-p5q ASUS P5Q-EM boards
mb31 MacBook 3,1
sony-vaio-tt Sony VAIO TT
@@ -229,7 +229,7 @@ AD1984
======
basic default configuration
thinkpad Lenovo Thinkpad T61/X61
- dell Dell T3400
+ dell_desktop Dell T3400
AD1986A
=======
@@ -258,6 +258,7 @@ Conexant 5045
laptop-micsense Laptop with Mic sense (old model fujitsu)
laptop-hpmicsense Laptop with HP and Mic senses
benq Benq R55E
+ laptop-hp530 HP 530 laptop
test for testing/debugging purpose, almost all controls
can be adjusted. Appearing only when compiled with
$CONFIG_SND_DEBUG=y
@@ -278,9 +279,16 @@ Conexant 5051
hp-dv6736 HP dv6736
lenovo-x200 Lenovo X200 laptop
+Conexant 5066
+=============
+ laptop Basic Laptop config (default)
+ dell-laptop Dell laptops
+ olpc-xo-1_5 OLPC XO 1.5
+
STAC9200
========
ref Reference board
+ oqo OQO Model 2
dell-d21 Dell (unknown)
dell-d22 Dell (unknown)
dell-d23 Dell (unknown)
@@ -368,10 +376,12 @@ STAC92HD73*
===========
ref Reference board
no-jd BIOS setup but without jack-detection
+ intel Intel DG45* mobos
dell-m6-amic Dell desktops/laptops with analog mics
dell-m6-dmic Dell desktops/laptops with digital mics
dell-m6 Dell desktops/laptops with both type of mics
dell-eq Dell desktops/laptops
+ alienware Alienware M17x
auto BIOS setup (default)
STAC92HD83*
@@ -385,3 +395,8 @@ STAC9872
========
vaio VAIO laptop without SPDIF
auto BIOS setup (default)
+
+Cirrus Logic CS4206/4207
+========================
+ mbp55 MacBook Pro 5,5
+ auto BIOS setup (default)
diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt
index 71ac995b191..7b8a5f947d1 100644
--- a/Documentation/sound/alsa/HD-Audio.txt
+++ b/Documentation/sound/alsa/HD-Audio.txt
@@ -139,6 +139,10 @@ The driver checks PCI SSID and looks through the static configuration
table until any matching entry is found. If you have a new machine,
you may see a message like below:
------------------------------------------------------------------------
+ hda_codec: ALC880: BIOS auto-probing.
+------------------------------------------------------------------------
+Meanwhile, in the earlier versions, you would see a message like:
+------------------------------------------------------------------------
hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...
------------------------------------------------------------------------
Even if you see such a message, DON'T PANIC. Take a deep breath and
@@ -403,6 +407,66 @@ re-configure based on that state, run like below:
------------------------------------------------------------------------
+Early Patching
+~~~~~~~~~~~~~~
+When CONFIG_SND_HDA_PATCH_LOADER=y is set, you can pass a "patch" as a
+firmware file for modifying the HD-audio setup before initializing the
+codec. This can work basically like the reconfiguration via sysfs in
+the above, but it does it before the first codec configuration.
+
+A patch file is a plain text file which looks like below:
+
+------------------------------------------------------------------------
+ [codec]
+ 0x12345678 0xabcd1234 2
+
+ [model]
+ auto
+
+ [pincfg]
+ 0x12 0x411111f0
+
+ [verb]
+ 0x20 0x500 0x03
+ 0x20 0x400 0xff
+
+ [hint]
+ hp_detect = yes
+------------------------------------------------------------------------
+
+The file needs to have a line `[codec]`. The next line should contain
+three numbers indicating the codec vendor-id (0x12345678 in the
+example), the codec subsystem-id (0xabcd1234) and the address (2) of
+the codec. The rest patch entries are applied to this specified codec
+until another codec entry is given.
+
+The `[model]` line allows to change the model name of the each codec.
+In the example above, it will be changed to model=auto.
+Note that this overrides the module option.
+
+After the `[pincfg]` line, the contents are parsed as the initial
+default pin-configurations just like `user_pin_configs` sysfs above.
+The values can be shown in user_pin_configs sysfs file, too.
+
+Similarly, the lines after `[verb]` are parsed as `init_verbs`
+sysfs entries, and the lines after `[hint]` are parsed as `hints`
+sysfs entries, respectively.
+
+The hd-audio driver reads the file via request_firmware(). Thus,
+a patch file has to be located on the appropriate firmware path,
+typically, /lib/firmware. For example, when you pass the option
+`patch=hda-init.fw`, the file /lib/firmware/hda-init-fw must be
+present.
+
+The patch module option is specific to each card instance, and you
+need to give one file name for each instance, separated by commas.
+For example, if you have two cards, one for an on-board analog and one
+for an HDMI video board, you may pass patch option like below:
+------------------------------------------------------------------------
+ options snd-hda-intel patch=on-board-patch,hdmi-patch
+------------------------------------------------------------------------
+
+
Power-Saving
~~~~~~~~~~~~
The power-saving is a kind of auto-suspend of the device. When the
diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx
index 68c236c0184..e352d754875 100644
--- a/Documentation/video4linux/CARDLIST.em28xx
+++ b/Documentation/video4linux/CARDLIST.em28xx
@@ -1,5 +1,5 @@
0 -> Unknown EM2800 video grabber (em2800) [eb1a:2800]
- 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883]
+ 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2710,eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883]
2 -> Terratec Cinergy 250 USB (em2820/em2840) [0ccd:0036]
3 -> Pinnacle PCTV USB 2 (em2820/em2840) [2304:0208]
4 -> Hauppauge WinTV USB 2 (em2820/em2840) [2040:4200,2040:4201]
diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134
index 15562427e8a..c913e561419 100644
--- a/Documentation/video4linux/CARDLIST.saa7134
+++ b/Documentation/video4linux/CARDLIST.saa7134
@@ -153,8 +153,8 @@
152 -> Asus Tiger Rev:1.00 [1043:4857]
153 -> Kworld Plus TV Analog Lite PCI [17de:7128]
154 -> Avermedia AVerTV GO 007 FM Plus [1461:f31d]
-155 -> Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid [0070:6706,0070:6708]
-156 -> Hauppauge WinTV-HVR1110r3 DVB-T/Hybrid [0070:6707,0070:6709,0070:670a]
+155 -> Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid [0070:6706,0070:6708]
+156 -> Hauppauge WinTV-HVR1120 DVB-T/Hybrid [0070:6707,0070:6709,0070:670a]
157 -> Avermedia AVerTV Studio 507UA [1461:a11b]
158 -> AVerMedia Cardbus TV/Radio (E501R) [1461:b7e9]
159 -> Beholder BeholdTV 505 RDS [0000:505B]
diff --git a/MAINTAINERS b/MAINTAINERS
index d6befb2c470..8dca9d89c6c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -904,7 +904,7 @@ F: drivers/input/misc/ati_remote2.c
ATLX ETHERNET DRIVERS
M: Jay Cliburn <jcliburn@gmail.com>
-M: Chris Snook <csnook@redhat.com>
+M: Chris Snook <chris.snook@gmail.com>
M: Jie Yang <jie.yang@atheros.com>
L: atl1-devel@lists.sourceforge.net
W: http://sourceforge.net/projects/atl1
@@ -2238,6 +2238,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6.git
S: Maintained
F: drivers/media/video/gspca/pac207.c
+GSPCA SN9C20X SUBDRIVER
+M: Brian Johnson <brijohn@gmail.com>
+L: linux-media@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6.git
+S: Maintained
+F: drivers/media/video/gspca/sn9c20x.c
+
GSPCA T613 SUBDRIVER
M: Leandro Costantino <lcostantino@gmail.com>
L: linux-media@vger.kernel.org
@@ -3421,6 +3428,7 @@ F: drivers/mfd/
MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
S: Orphan
+L: linux-mmc@vger.kernel.org
F: drivers/mmc/
F: include/linux/mmc/
@@ -3555,6 +3563,9 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6.git
S: Maintained
F: net/
F: include/net/
+F: include/linux/in.h
+F: include/linux/net.h
+F: include/linux/netdevice.h
NETWORKING [IPv4/IPv6]
M: "David S. Miller" <davem@davemloft.net>
@@ -3590,6 +3601,8 @@ W: http://www.linuxfoundation.org/en/Net
T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6.git
S: Odd Fixes
F: drivers/net/
+F: include/linux/if_*
+F: include/linux/*device.h
NETXEN (1/10) GbE SUPPORT
M: Dhananjay Phadke <dhananjay@netxen.com>
@@ -3796,7 +3809,7 @@ W: http://open-osd.org
T: git git://git.open-osd.org/open-osd.git
S: Maintained
F: drivers/scsi/osd/
-F: drivers/include/scsi/osd_*
+F: include/scsi/osd_*
F: fs/exofs/
P54 WIRELESS DRIVER
@@ -4995,7 +5008,9 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial.git
S: Maintained
TTY LAYER
-S: Orphan
+M: Greg Kroah-Hartman <gregkh@suse.de>
+S: Maintained
+T: quilt kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/
F: drivers/char/tty_*
F: drivers/serial/serial_core.c
F: include/linux/serial_core.h
diff --git a/Makefile b/Makefile
index 0d46615bffe..60de4ef3125 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 31
-EXTRAVERSION = -rc5
+EXTRAVERSION =
NAME = Man-Eating Seals of Antiquity
# *DOCUMENTATION*
diff --git a/REPORTING-BUGS b/REPORTING-BUGS
index ab0c56630a8..55a6074ccbb 100644
--- a/REPORTING-BUGS
+++ b/REPORTING-BUGS
@@ -15,7 +15,10 @@ worry too much about getting the wrong person. If you are unsure send it
to the person responsible for the code relevant to what you were doing.
If it occurs repeatably try and describe how to recreate it. That is
worth even more than the oops itself. The list of maintainers and
-mailing lists is in the MAINTAINERS file in this directory.
+mailing lists is in the MAINTAINERS file in this directory. If you
+know the file name that causes the problem you can use the following
+command in this directory to find some of the maintainers of that file:
+ perl scripts/get_maintainer.pl -f <filename>
If it is a security bug, please copy the Security Contact listed
in the MAINTAINERS file. They can help coordinate bugfix and disclosure.
diff --git a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c
index 9e6e512f011..17153b54613 100644
--- a/arch/arm/boot/compressed/misc.c
+++ b/arch/arm/boot/compressed/misc.c
@@ -29,7 +29,6 @@ unsigned int __machine_arch_type;
static void putstr(const char *ptr);
-#include <linux/compiler.h>
#include <mach/uncompress.h>
#ifdef CONFIG_DEBUG_ICEDCC
diff --git a/arch/arm/common/clkdev.c b/arch/arm/common/clkdev.c
index f37afd9422f..aae5bc01acc 100644
--- a/arch/arm/common/clkdev.c
+++ b/arch/arm/common/clkdev.c
@@ -17,6 +17,7 @@
#include <linux/err.h>
#include <linux/string.h>
#include <linux/mutex.h>
+#include <linux/clk.h>
#include <asm/clkdev.h>
#include <mach/clkdev.h>
diff --git a/arch/arm/configs/kirkwood_defconfig b/arch/arm/configs/kirkwood_defconfig
index 0a1abb978d7..af74cc2de8b 100644
--- a/arch/arm/configs/kirkwood_defconfig
+++ b/arch/arm/configs/kirkwood_defconfig
@@ -629,7 +629,7 @@ CONFIG_SCSI_LOWLEVEL=y
CONFIG_ATA=y
# CONFIG_ATA_NONSTANDARD is not set
CONFIG_SATA_PMP=y
-# CONFIG_SATA_AHCI is not set
+CONFIG_SATA_AHCI=y
# CONFIG_SATA_SIL24 is not set
CONFIG_ATA_SFF=y
# CONFIG_SATA_SVW is not set
diff --git a/arch/arm/configs/mx27_defconfig b/arch/arm/configs/mx27_defconfig
index 083516cd0d7..75263a83741 100644
--- a/arch/arm/configs/mx27_defconfig
+++ b/arch/arm/configs/mx27_defconfig
@@ -1,15 +1,15 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.30-rc1
-# Wed Apr 8 10:18:06 2009
+# Linux kernel version: 2.6.31-rc4
+# Fri Jul 24 16:08:06 2009
#
CONFIG_ARM=y
+CONFIG_HAVE_PWM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
CONFIG_GENERIC_GPIO=y
CONFIG_GENERIC_TIME=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_MMU=y
-# CONFIG_NO_IOPORT is not set
CONFIG_GENERIC_HARDIRQS=y
CONFIG_STACKTRACE_SUPPORT=y
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
@@ -18,14 +18,13 @@ CONFIG_TRACE_IRQFLAGS_SUPPORT=y
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y
-# CONFIG_ARCH_HAS_ILOG2_U32 is not set
-# CONFIG_ARCH_HAS_ILOG2_U64 is not set
CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_ARCH_MTD_XIP=y
CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
CONFIG_VECTORS_BASE=0xffff0000
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
#
# General setup
@@ -85,7 +84,12 @@ CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
CONFIG_AIO=y
+
+#
+# Performance Counters
+#
CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_STRIP_ASM_SYMS is not set
# CONFIG_COMPAT_BRK is not set
CONFIG_SLAB=y
# CONFIG_SLUB is not set
@@ -99,6 +103,12 @@ CONFIG_KPROBES=y
CONFIG_KRETPROBES=y
CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_CLK=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
# CONFIG_SLOW_WORK is not set
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_SLABINFO=y
@@ -111,7 +121,7 @@ CONFIG_MODULE_UNLOAD=y
# CONFIG_MODVERSIONS is not set
# CONFIG_MODULE_SRCVERSION_ALL is not set
CONFIG_BLOCK=y
-# CONFIG_LBD is not set
+CONFIG_LBDAF=y
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_BLK_DEV_INTEGRITY is not set
@@ -138,13 +148,14 @@ CONFIG_FREEZER=y
# CONFIG_ARCH_VERSATILE is not set
# CONFIG_ARCH_AT91 is not set
# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_EP93XX is not set
-# CONFIG_ARCH_GEMINI is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
+CONFIG_ARCH_MXC=y
+# CONFIG_ARCH_STMP3XXX is not set
# CONFIG_ARCH_NETX is not set
# CONFIG_ARCH_H720X is not set
-# CONFIG_ARCH_IMX is not set
# CONFIG_ARCH_IOP13XX is not set
# CONFIG_ARCH_IOP32X is not set
# CONFIG_ARCH_IOP33X is not set
@@ -153,25 +164,25 @@ CONFIG_FREEZER=y
# CONFIG_ARCH_IXP4XX is not set
# CONFIG_ARCH_L7200 is not set
# CONFIG_ARCH_KIRKWOOD is not set
-# CONFIG_ARCH_KS8695 is not set
-# CONFIG_ARCH_NS9XXX is not set
# CONFIG_ARCH_LOKI is not set
# CONFIG_ARCH_MV78XX0 is not set
-CONFIG_ARCH_MXC=y
# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_W90X900 is not set
# CONFIG_ARCH_PNX4008 is not set
# CONFIG_ARCH_PXA is not set
-# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_MSM is not set
# CONFIG_ARCH_RPC is not set
# CONFIG_ARCH_SA1100 is not set
# CONFIG_ARCH_S3C2410 is not set
# CONFIG_ARCH_S3C64XX is not set
# CONFIG_ARCH_SHARK is not set
# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_U300 is not set
# CONFIG_ARCH_DAVINCI is not set
# CONFIG_ARCH_OMAP is not set
-# CONFIG_ARCH_MSM is not set
-# CONFIG_ARCH_W90X900 is not set
#
# Freescale MXC Implementations
@@ -188,6 +199,8 @@ CONFIG_MACH_MX27=y
CONFIG_MACH_MX27ADS=y
CONFIG_MACH_PCM038=y
CONFIG_MACH_PCM970_BASEBOARD=y
+CONFIG_MACH_MX27_3DS=y
+CONFIG_MACH_MX27LITE=y
CONFIG_MXC_IRQ_PRIOR=y
CONFIG_MXC_PWM=y
@@ -213,7 +226,6 @@ CONFIG_ARM_THUMB=y
# CONFIG_CPU_DCACHE_DISABLE is not set
# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
-# CONFIG_OUTER_CACHE is not set
CONFIG_COMMON_CLKDEV=y
#
@@ -238,7 +250,6 @@ CONFIG_PREEMPT=y
CONFIG_HZ=100
CONFIG_AEABI=y
CONFIG_OABI_COMPAT=y
-CONFIG_ARCH_FLATMEM_HAS_HOLES=y
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
# CONFIG_HIGHMEM is not set
@@ -253,10 +264,11 @@ CONFIG_SPLIT_PTLOCK_CPUS=4096
# CONFIG_PHYS_ADDR_T_64BIT is not set
CONFIG_ZONE_DMA_FLAG=0
CONFIG_VIRT_TO_BUS=y
-CONFIG_UNEVICTABLE_LRU=y
CONFIG_HAVE_MLOCK=y
CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
#
# Boot options
@@ -361,6 +373,7 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
# CONFIG_NET_SCHED is not set
# CONFIG_DCB is not set
@@ -474,7 +487,16 @@ CONFIG_MTD_PHYSMAP=y
# CONFIG_MTD_DOC2000 is not set
# CONFIG_MTD_DOC2001 is not set
# CONFIG_MTD_DOC2001PLUS is not set
-# CONFIG_MTD_NAND is not set
+CONFIG_MTD_NAND=y
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_GPIO is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+CONFIG_MTD_NAND_MXC=y
# CONFIG_MTD_ONENAND is not set
#
@@ -485,7 +507,15 @@ CONFIG_MTD_PHYSMAP=y
#
# UBI - Unsorted block images
#
-# CONFIG_MTD_UBI is not set
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MTD_UBI_BEB_RESERVE=1
+# CONFIG_MTD_UBI_GLUEBI is not set
+
+#
+# UBI debugging options
+#
+# CONFIG_MTD_UBI_DEBUG is not set
# CONFIG_PARPORT is not set
CONFIG_BLK_DEV=y
# CONFIG_BLK_DEV_COW_COMMON is not set
@@ -494,7 +524,21 @@ CONFIG_BLK_DEV=y
# CONFIG_BLK_DEV_RAM is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
-# CONFIG_MISC_DEVICES is not set
+# CONFIG_MG_DISK is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_ICS932S401 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_ISL29003 is not set
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+CONFIG_EEPROM_AT24=y
+# CONFIG_EEPROM_AT25 is not set
+# CONFIG_EEPROM_LEGACY is not set
+# CONFIG_EEPROM_MAX6875 is not set
+# CONFIG_EEPROM_93CX6 is not set
CONFIG_HAVE_IDE=y
# CONFIG_IDE is not set
@@ -508,7 +552,6 @@ CONFIG_HAVE_IDE=y
# CONFIG_ATA is not set
# CONFIG_MD is not set
CONFIG_NETDEVICES=y
-CONFIG_COMPAT_NET_DEV_OPS=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
@@ -534,6 +577,8 @@ CONFIG_NET_ETHERNET=y
# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
# CONFIG_B44 is not set
+# CONFIG_KS8842 is not set
+# CONFIG_KS8851 is not set
CONFIG_FEC=y
# CONFIG_FEC2 is not set
# CONFIG_NETDEV_1000 is not set
@@ -580,6 +625,11 @@ CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_TABLET is not set
CONFIG_INPUT_TOUCHSCREEN=y
# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_AD7877 is not set
+# CONFIG_TOUCHSCREEN_AD7879_I2C is not set
+# CONFIG_TOUCHSCREEN_AD7879_SPI is not set
+# CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_EETI is not set
# CONFIG_TOUCHSCREEN_FUJITSU is not set
# CONFIG_TOUCHSCREEN_GUNZE is not set
# CONFIG_TOUCHSCREEN_ELO is not set
@@ -592,6 +642,7 @@ CONFIG_INPUT_TOUCHSCREEN=y
# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_TOUCHSCREEN_W90X900 is not set
# CONFIG_INPUT_MISC is not set
#
@@ -644,6 +695,7 @@ CONFIG_I2C_HELPER_AUTO=y
#
# I2C system bus drivers (mostly embedded / system-on-chip)
#
+# CONFIG_I2C_DESIGNWARE is not set
# CONFIG_I2C_GPIO is not set
CONFIG_I2C_IMX=y
# CONFIG_I2C_OCORES is not set
@@ -668,7 +720,6 @@ CONFIG_I2C_IMX=y
# CONFIG_SENSORS_PCF8574 is not set
# CONFIG_PCF8575 is not set
# CONFIG_SENSORS_PCA9539 is not set
-# CONFIG_SENSORS_MAX6875 is not set
# CONFIG_SENSORS_TSL2550 is not set
# CONFIG_I2C_DEBUG_CORE is not set
# CONFIG_I2C_DEBUG_ALGO is not set
@@ -719,6 +770,7 @@ CONFIG_W1=y
#
# CONFIG_W1_MASTER_DS2482 is not set
CONFIG_W1_MASTER_MXC=y
+# CONFIG_W1_MASTER_DS1WM is not set
# CONFIG_W1_MASTER_GPIO is not set
#
@@ -753,54 +805,16 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_TPS65010 is not set
# CONFIG_TWL4030_CORE is not set
# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
# CONFIG_MFD_TC6393XB is not set
# CONFIG_PMIC_DA903X is not set
# CONFIG_MFD_WM8400 is not set
# CONFIG_MFD_WM8350_I2C is not set
# CONFIG_MFD_PCF50633 is not set
-
-#
-# Multimedia devices
-#
-
-#
-# Multimedia core support
-#
-CONFIG_VIDEO_DEV=y
-CONFIG_VIDEO_V4L2_COMMON=y
-CONFIG_VIDEO_ALLOW_V4L1=y
-CONFIG_VIDEO_V4L1_COMPAT=y
-# CONFIG_DVB_CORE is not set
-CONFIG_VIDEO_MEDIA=y
-
-#
-# Multimedia drivers
-#
-# CONFIG_MEDIA_ATTACH is not set
-CONFIG_MEDIA_TUNER=y
-# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
-CONFIG_MEDIA_TUNER_SIMPLE=y
-CONFIG_MEDIA_TUNER_TDA8290=y
-CONFIG_MEDIA_TUNER_TDA9887=y
-CONFIG_MEDIA_TUNER_TEA5761=y
-CONFIG_MEDIA_TUNER_TEA5767=y
-CONFIG_MEDIA_TUNER_MT20XX=y
-CONFIG_MEDIA_TUNER_XC2028=y
-CONFIG_MEDIA_TUNER_XC5000=y
-CONFIG_MEDIA_TUNER_MC44S803=y
-CONFIG_VIDEO_V4L2=y
-CONFIG_VIDEO_V4L1=y
-CONFIG_VIDEO_CAPTURE_DRIVERS=y
-# CONFIG_VIDEO_ADV_DEBUG is not set
-# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
-CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
-# CONFIG_VIDEO_VIVI is not set
-# CONFIG_VIDEO_CPIA is not set
-# CONFIG_VIDEO_SAA5246A is not set
-# CONFIG_VIDEO_SAA5249 is not set
-# CONFIG_SOC_CAMERA is not set
-# CONFIG_RADIO_ADAPTERS is not set
-# CONFIG_DAB is not set
+# CONFIG_AB3100_CORE is not set
+# CONFIG_EZX_PCAP is not set
+# CONFIG_MEDIA_SUPPORT is not set
#
# Graphics support
@@ -917,6 +931,7 @@ CONFIG_RTC_DRV_PCF8563=y
# CONFIG_RTC_DRV_S35390A is not set
# CONFIG_RTC_DRV_FM3130 is not set
# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
#
# SPI RTC drivers
@@ -962,12 +977,15 @@ CONFIG_RTC_DRV_PCF8563=y
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_FS_POSIX_ACL is not set
-CONFIG_FILE_LOCKING=y
# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_BTRFS_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
# CONFIG_DNOTIFY is not set
# CONFIG_INOTIFY is not set
+CONFIG_INOTIFY_USER=y
# CONFIG_QUOTA is not set
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
@@ -1021,6 +1039,12 @@ CONFIG_JFFS2_ZLIB=y
# CONFIG_JFFS2_LZO is not set
CONFIG_JFFS2_RTIME=y
# CONFIG_JFFS2_RUBIN is not set
+CONFIG_UBIFS_FS=y
+# CONFIG_UBIFS_FS_XATTR is not set
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_ZLIB=y
+# CONFIG_UBIFS_FS_DEBUG is not set
# CONFIG_CRAMFS is not set
# CONFIG_SQUASHFS is not set
# CONFIG_VXFS_FS is not set
@@ -1119,25 +1143,11 @@ CONFIG_SYSCTL_SYSCALL_CHECK=y
CONFIG_NOP_TRACER=y
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_RING_BUFFER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
CONFIG_TRACING=y
CONFIG_TRACING_SUPPORT=y
-
-#
-# Tracers
-#
-# CONFIG_FUNCTION_TRACER is not set
-# CONFIG_IRQSOFF_TRACER is not set
-# CONFIG_PREEMPT_TRACER is not set
-# CONFIG_SCHED_TRACER is not set
-# CONFIG_CONTEXT_SWITCH_TRACER is not set
-# CONFIG_EVENT_TRACER is not set
-# CONFIG_BOOT_TRACER is not set
-# CONFIG_TRACE_BRANCH_PROFILING is not set
-# CONFIG_STACK_TRACER is not set
-# CONFIG_KMEMTRACE is not set
-# CONFIG_WORKQUEUE_TRACER is not set
-# CONFIG_BLK_DEV_IO_TRACE is not set
-# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_FTRACE is not set
# CONFIG_DYNAMIC_DEBUG is not set
# CONFIG_SAMPLES is not set
CONFIG_HAVE_ARCH_KGDB=y
@@ -1151,16 +1161,104 @@ CONFIG_ARM_UNWIND=y
# CONFIG_SECURITY is not set
# CONFIG_SECURITYFS is not set
# CONFIG_SECURITY_FILE_CAPABILITIES is not set
-# CONFIG_CRYPTO is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_MANAGER2 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_ZLIB is not set
+CONFIG_CRYPTO_LZO=y
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
CONFIG_BINARY_PRINTF=y
#
# Library routines
#
CONFIG_BITREVERSE=y
+CONFIG_RATIONAL=y
CONFIG_GENERIC_FIND_LAST_BIT=y
# CONFIG_CRC_CCITT is not set
-# CONFIG_CRC16 is not set
+CONFIG_CRC16=y
# CONFIG_CRC_T10DIF is not set
# CONFIG_CRC_ITU_T is not set
CONFIG_CRC32=y
@@ -1168,6 +1266,8 @@ CONFIG_CRC32=y
# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/mx3_defconfig b/arch/arm/configs/mx3_defconfig
index 20ada526f6d..a4f9a2a8149 100644
--- a/arch/arm/configs/mx3_defconfig
+++ b/arch/arm/configs/mx3_defconfig
@@ -1,15 +1,15 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.30-rc1
-# Wed Apr 8 11:06:37 2009
+# Linux kernel version: 2.6.31-rc4
+# Tue Jul 28 14:11:34 2009
#
CONFIG_ARM=y
+CONFIG_HAVE_PWM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
CONFIG_GENERIC_GPIO=y
CONFIG_GENERIC_TIME=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_MMU=y
-# CONFIG_NO_IOPORT is not set
CONFIG_GENERIC_HARDIRQS=y
CONFIG_STACKTRACE_SUPPORT=y
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
@@ -18,14 +18,13 @@ CONFIG_TRACE_IRQFLAGS_SUPPORT=y
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y
-# CONFIG_ARCH_HAS_ILOG2_U32 is not set
-# CONFIG_ARCH_HAS_ILOG2_U64 is not set
CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_ARCH_MTD_XIP=y
CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
CONFIG_VECTORS_BASE=0xffff0000
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
#
# General setup
@@ -86,7 +85,12 @@ CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
CONFIG_AIO=y
+
+#
+# Performance Counters
+#
CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_STRIP_ASM_SYMS is not set
CONFIG_COMPAT_BRK=y
CONFIG_SLAB=y
# CONFIG_SLUB is not set
@@ -97,6 +101,11 @@ CONFIG_HAVE_OPROFILE=y
# CONFIG_KPROBES is not set
CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_CLK=y
+
+#
+# GCOV-based kernel profiling
+#
# CONFIG_SLOW_WORK is not set
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_SLABINFO=y
@@ -109,7 +118,7 @@ CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y
# CONFIG_MODULE_SRCVERSION_ALL is not set
CONFIG_BLOCK=y
-# CONFIG_LBD is not set
+CONFIG_LBDAF=y
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_BLK_DEV_INTEGRITY is not set
@@ -136,13 +145,14 @@ CONFIG_FREEZER=y
# CONFIG_ARCH_VERSATILE is not set
# CONFIG_ARCH_AT91 is not set
# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_EP93XX is not set
-# CONFIG_ARCH_GEMINI is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
+CONFIG_ARCH_MXC=y
+# CONFIG_ARCH_STMP3XXX is not set
# CONFIG_ARCH_NETX is not set
# CONFIG_ARCH_H720X is not set
-# CONFIG_ARCH_IMX is not set
# CONFIG_ARCH_IOP13XX is not set
# CONFIG_ARCH_IOP32X is not set
# CONFIG_ARCH_IOP33X is not set
@@ -151,25 +161,25 @@ CONFIG_FREEZER=y
# CONFIG_ARCH_IXP4XX is not set
# CONFIG_ARCH_L7200 is not set
# CONFIG_ARCH_KIRKWOOD is not set
-# CONFIG_ARCH_KS8695 is not set
-# CONFIG_ARCH_NS9XXX is not set
# CONFIG_ARCH_LOKI is not set
# CONFIG_ARCH_MV78XX0 is not set
-CONFIG_ARCH_MXC=y
# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_W90X900 is not set
# CONFIG_ARCH_PNX4008 is not set
# CONFIG_ARCH_PXA is not set
-# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_MSM is not set
# CONFIG_ARCH_RPC is not set
# CONFIG_ARCH_SA1100 is not set
# CONFIG_ARCH_S3C2410 is not set
# CONFIG_ARCH_S3C64XX is not set
# CONFIG_ARCH_SHARK is not set
# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_U300 is not set
# CONFIG_ARCH_DAVINCI is not set
# CONFIG_ARCH_OMAP is not set
-# CONFIG_ARCH_MSM is not set
-# CONFIG_ARCH_W90X900 is not set
#
# Freescale MXC Implementations
@@ -178,6 +188,7 @@ CONFIG_ARCH_MXC=y
# CONFIG_ARCH_MX2 is not set
CONFIG_ARCH_MX3=y
CONFIG_ARCH_MX31=y
+CONFIG_ARCH_MX35=y
#
# MX3 platforms:
@@ -185,12 +196,19 @@ CONFIG_ARCH_MX31=y
CONFIG_MACH_MX31ADS=y
CONFIG_MACH_MX31ADS_WM1133_EV1=y
CONFIG_MACH_PCM037=y
+CONFIG_MACH_PCM037_EET=y
CONFIG_MACH_MX31LITE=y
CONFIG_MACH_MX31_3DS=y
CONFIG_MACH_MX31MOBOARD=y
+CONFIG_MACH_MX31LILLY=y
CONFIG_MACH_QONG=y
+CONFIG_MACH_PCM043=y
+CONFIG_MACH_ARMADILLO5X0=y
+CONFIG_MACH_MX35_3DS=y
CONFIG_MXC_IRQ_PRIOR=y
CONFIG_MXC_PWM=y
+CONFIG_ARCH_HAS_RNGA=y
+CONFIG_ARCH_MXC_IOMUX_V3=y
#
# Processor Type
@@ -218,6 +236,7 @@ CONFIG_ARM_THUMB=y
# CONFIG_CPU_BPREDICT_DISABLE is not set
CONFIG_OUTER_CACHE=y
CONFIG_CACHE_L2X0=y
+# CONFIG_ARM_ERRATA_411920 is not set
CONFIG_COMMON_CLKDEV=y
#
@@ -242,7 +261,6 @@ CONFIG_PREEMPT=y
CONFIG_HZ=100
CONFIG_AEABI=y
CONFIG_OABI_COMPAT=y
-CONFIG_ARCH_FLATMEM_HAS_HOLES=y
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
# CONFIG_HIGHMEM is not set
@@ -257,10 +275,11 @@ CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_PHYS_ADDR_T_64BIT is not set
CONFIG_ZONE_DMA_FLAG=0
CONFIG_VIRT_TO_BUS=y
-CONFIG_UNEVICTABLE_LRU=y
CONFIG_HAVE_MLOCK=y
CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
#
# Boot options
@@ -362,6 +381,7 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
# CONFIG_NET_SCHED is not set
# CONFIG_DCB is not set
@@ -465,7 +485,16 @@ CONFIG_MTD_PHYSMAP=y
# CONFIG_MTD_DOC2000 is not set
# CONFIG_MTD_DOC2001 is not set
# CONFIG_MTD_DOC2001PLUS is not set
-# CONFIG_MTD_NAND is not set
+CONFIG_MTD_NAND=y
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_GPIO is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+CONFIG_MTD_NAND_MXC=y
# CONFIG_MTD_ONENAND is not set
#
@@ -476,10 +505,30 @@ CONFIG_MTD_PHYSMAP=y
#
# UBI - Unsorted block images
#
-# CONFIG_MTD_UBI is not set
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MTD_UBI_BEB_RESERVE=1
+# CONFIG_MTD_UBI_GLUEBI is not set
+
+#
+# UBI debugging options
+#
+# CONFIG_MTD_UBI_DEBUG is not set
# CONFIG_PARPORT is not set
# CONFIG_BLK_DEV is not set
-# CONFIG_MISC_DEVICES is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_ICS932S401 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_ISL29003 is not set
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+CONFIG_EEPROM_AT24=y
+# CONFIG_EEPROM_LEGACY is not set
+# CONFIG_EEPROM_MAX6875 is not set
+# CONFIG_EEPROM_93CX6 is not set
CONFIG_HAVE_IDE=y
# CONFIG_IDE is not set
@@ -493,7 +542,6 @@ CONFIG_HAVE_IDE=y
# CONFIG_ATA is not set
# CONFIG_MD is not set
CONFIG_NETDEVICES=y
-CONFIG_COMPAT_NET_DEV_OPS=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
@@ -528,7 +576,7 @@ CONFIG_MII=y
# CONFIG_ETHOC is not set
# CONFIG_SMC911X is not set
CONFIG_SMSC911X=y
-# CONFIG_DNET is not set
+CONFIG_DNET=y
# CONFIG_IBM_NEW_EMAC_ZMII is not set
# CONFIG_IBM_NEW_EMAC_RGMII is not set
# CONFIG_IBM_NEW_EMAC_TAH is not set
@@ -537,8 +585,10 @@ CONFIG_SMSC911X=y
# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
# CONFIG_B44 is not set
-CONFIG_CS89x0=y
-CONFIG_CS89x0_NONISA_IRQ=y
+# CONFIG_CS89x0 is not set
+# CONFIG_KS8842 is not set
+CONFIG_FEC=y
+# CONFIG_FEC2 is not set
# CONFIG_NETDEV_1000 is not set
# CONFIG_NETDEV_10000 is not set
@@ -609,6 +659,7 @@ CONFIG_I2C_HELPER_AUTO=y
#
# I2C system bus drivers (mostly embedded / system-on-chip)
#
+# CONFIG_I2C_DESIGNWARE is not set
# CONFIG_I2C_GPIO is not set
CONFIG_I2C_IMX=y
# CONFIG_I2C_OCORES is not set
@@ -633,7 +684,6 @@ CONFIG_I2C_IMX=y
# CONFIG_SENSORS_PCF8574 is not set
# CONFIG_PCF8575 is not set
# CONFIG_SENSORS_PCA9539 is not set
-# CONFIG_SENSORS_MAX6875 is not set
# CONFIG_SENSORS_TSL2550 is not set
# CONFIG_I2C_DEBUG_CORE is not set
# CONFIG_I2C_DEBUG_ALGO is not set
@@ -669,6 +719,7 @@ CONFIG_W1=y
#
# CONFIG_W1_MASTER_DS2482 is not set
CONFIG_W1_MASTER_MXC=y
+# CONFIG_W1_MASTER_DS1WM is not set
# CONFIG_W1_MASTER_GPIO is not set
#
@@ -703,6 +754,8 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_TPS65010 is not set
# CONFIG_TWL4030_CORE is not set
# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
# CONFIG_MFD_TC6393XB is not set
# CONFIG_PMIC_DA903X is not set
# CONFIG_MFD_WM8400 is not set
@@ -711,10 +764,8 @@ CONFIG_MFD_WM8350_CONFIG_MODE_0=y
CONFIG_MFD_WM8352_CONFIG_MODE_0=y
CONFIG_MFD_WM8350_I2C=y
# CONFIG_MFD_PCF50633 is not set
-
-#
-# Multimedia devices
-#
+# CONFIG_AB3100_CORE is not set
+CONFIG_MEDIA_SUPPORT=y
#
# Multimedia core support
@@ -758,8 +809,10 @@ CONFIG_SOC_CAMERA_MT9T031=y
CONFIG_SOC_CAMERA_MT9V022=y
CONFIG_SOC_CAMERA_TW9910=y
# CONFIG_SOC_CAMERA_PLATFORM is not set
-# CONFIG_SOC_CAMERA_OV772X is not set
+CONFIG_SOC_CAMERA_OV772X=y
+CONFIG_MX3_VIDEO=y
CONFIG_VIDEO_MX3=y
+# CONFIG_VIDEO_SH_MOBILE_CEU is not set
# CONFIG_RADIO_ADAPTERS is not set
# CONFIG_DAB is not set
@@ -847,8 +900,11 @@ CONFIG_REGULATOR=y
# CONFIG_REGULATOR_DEBUG is not set
# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
# CONFIG_REGULATOR_BQ24022 is not set
+# CONFIG_REGULATOR_MAX1586 is not set
CONFIG_REGULATOR_WM8350=y
+# CONFIG_REGULATOR_LP3971 is not set
# CONFIG_UIO is not set
# CONFIG_STAGING is not set
@@ -861,10 +917,12 @@ CONFIG_REGULATOR_WM8350=y
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_FS_POSIX_ACL is not set
-CONFIG_FILE_LOCKING=y
# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_BTRFS_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
# CONFIG_DNOTIFY is not set
CONFIG_INOTIFY=y
CONFIG_INOTIFY_USER=y
@@ -921,6 +979,12 @@ CONFIG_JFFS2_ZLIB=y
# CONFIG_JFFS2_LZO is not set
CONFIG_JFFS2_RTIME=y
# CONFIG_JFFS2_RUBIN is not set
+CONFIG_UBIFS_FS=y
+# CONFIG_UBIFS_FS_XATTR is not set
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_ZLIB=y
+# CONFIG_UBIFS_FS_DEBUG is not set
# CONFIG_CRAMFS is not set
# CONFIG_SQUASHFS is not set
# CONFIG_VXFS_FS is not set
@@ -937,6 +1001,7 @@ CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
# CONFIG_NFS_V3_ACL is not set
CONFIG_NFS_V4=y
+# CONFIG_NFS_V4_1 is not set
CONFIG_ROOT_NFS=y
# CONFIG_NFSD is not set
CONFIG_LOCKD=y
@@ -979,22 +1044,7 @@ CONFIG_FRAME_WARN=1024
CONFIG_SYSCTL_SYSCALL_CHECK=y
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_TRACING_SUPPORT=y
-
-#
-# Tracers
-#
-# CONFIG_FUNCTION_TRACER is not set
-# CONFIG_IRQSOFF_TRACER is not set
-# CONFIG_PREEMPT_TRACER is not set
-# CONFIG_SCHED_TRACER is not set
-# CONFIG_CONTEXT_SWITCH_TRACER is not set
-# CONFIG_EVENT_TRACER is not set
-# CONFIG_BOOT_TRACER is not set
-# CONFIG_TRACE_BRANCH_PROFILING is not set
-# CONFIG_STACK_TRACER is not set
-# CONFIG_KMEMTRACE is not set
-# CONFIG_WORKQUEUE_TRACER is not set
-# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_FTRACE is not set
# CONFIG_SAMPLES is not set
CONFIG_HAVE_ARCH_KGDB=y
CONFIG_ARM_UNWIND=y
@@ -1094,9 +1144,9 @@ CONFIG_CRYPTO_DES=y
#
# Compression
#
-# CONFIG_CRYPTO_DEFLATE is not set
+CONFIG_CRYPTO_DEFLATE=y
# CONFIG_CRYPTO_ZLIB is not set
-# CONFIG_CRYPTO_LZO is not set
+CONFIG_CRYPTO_LZO=y
#
# Random Number Generation
@@ -1109,9 +1159,10 @@ CONFIG_CRYPTO_HW=y
# Library routines
#
CONFIG_BITREVERSE=y
+CONFIG_RATIONAL=y
CONFIG_GENERIC_FIND_LAST_BIT=y
# CONFIG_CRC_CCITT is not set
-# CONFIG_CRC16 is not set
+CONFIG_CRC16=y
# CONFIG_CRC_T10DIF is not set
# CONFIG_CRC_ITU_T is not set
CONFIG_CRC32=y
@@ -1119,6 +1170,8 @@ CONFIG_CRC32=y
# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/omap3_evm_defconfig b/arch/arm/configs/omap3_evm_defconfig
index 28be17fbc15..d5ff4776cd0 100644
--- a/arch/arm/configs/omap3_evm_defconfig
+++ b/arch/arm/configs/omap3_evm_defconfig
@@ -1107,7 +1107,7 @@ CONFIG_USB_ZERO=m
CONFIG_USB_OTG_UTILS=y
# CONFIG_USB_GPIO_VBUS is not set
# CONFIG_ISP1301_OMAP is not set
-CONFIG_TWL4030_USB=y
+# CONFIG_TWL4030_USB is not set
# CONFIG_NOP_USB_XCEIV is not set
CONFIG_MMC=y
# CONFIG_MMC_DEBUG is not set
diff --git a/arch/arm/configs/rx51_defconfig b/arch/arm/configs/rx51_defconfig
index eb2cb31825c..f238df66efd 100644
--- a/arch/arm/configs/rx51_defconfig
+++ b/arch/arm/configs/rx51_defconfig
@@ -282,7 +282,7 @@ CONFIG_ALIGNMENT_TRAP=y
#
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_CMDLINE="init=/sbin/preinit ubi.mtd=rootfs root=ubi0:rootfs rootfstype=ubifs rootflags=bulk_read,no_chk_data_crc rw console=ttyMTD,log console=tty0"
+CONFIG_CMDLINE="init=/sbin/preinit ubi.mtd=rootfs root=ubi0:rootfs rootfstype=ubifs rootflags=bulk_read,no_chk_data_crc rw console=ttyMTD,log console=tty0 console=ttyS2,115200n8"
# CONFIG_XIP_KERNEL is not set
# CONFIG_KEXEC is not set
@@ -1354,7 +1354,7 @@ CONFIG_USB_OTG_UTILS=y
# CONFIG_USB_GPIO_VBUS is not set
# CONFIG_ISP1301_OMAP is not set
CONFIG_TWL4030_USB=y
-CONFIG_MMC=m
+CONFIG_MMC=y
# CONFIG_MMC_DEBUG is not set
# CONFIG_MMC_UNSAFE_RESUME is not set
@@ -1449,7 +1449,8 @@ CONFIG_RTC_DRV_TWL4030=m
# on-CPU RTC drivers
#
# CONFIG_DMADEVICES is not set
-# CONFIG_REGULATOR is not set
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_TWL4030=y
# CONFIG_UIO is not set
# CONFIG_STAGING is not set
diff --git a/arch/arm/include/asm/atomic.h b/arch/arm/include/asm/atomic.h
index 9e07fe50702..9ed2377fe8e 100644
--- a/arch/arm/include/asm/atomic.h
+++ b/arch/arm/include/asm/atomic.h
@@ -159,8 +159,6 @@ static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
#else /* ARM_ARCH_6 */
-#include <asm/system.h>
-
#ifdef CONFIG_SMP
#error SMP not supported on pre-ARMv6 CPUs
#endif
diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h
index ee1304f22f9..5ccce0a9b03 100644
--- a/arch/arm/include/asm/setup.h
+++ b/arch/arm/include/asm/setup.h
@@ -201,7 +201,8 @@ static struct tagtable __tagtable_##fn __tag = { tag, fn }
struct membank {
unsigned long start;
unsigned long size;
- int node;
+ unsigned short node;
+ unsigned short highmem;
};
struct meminfo {
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 366e5097a41..8c3de1a350b 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -148,7 +148,7 @@ trace:
sub r0, r0, #MCOUNT_INSN_SIZE
mov lr, pc
mov pc, r2
- mov lr, r1 @ restore lr
+ ldr lr, [fp, #-4] @ restore lr
ldmia sp!, {r0-r3, pc}
#endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
index 93bb4247b7e..f6bc5d44278 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -133,7 +133,7 @@ sys_sigaction(int sig, const struct old_sigaction __user *act,
}
#ifdef CONFIG_CRUNCH
-static int preserve_crunch_context(struct crunch_sigframe *frame)
+static int preserve_crunch_context(struct crunch_sigframe __user *frame)
{
char kbuf[sizeof(*frame) + 8];
struct crunch_sigframe *kframe;
@@ -146,7 +146,7 @@ static int preserve_crunch_context(struct crunch_sigframe *frame)
return __copy_to_user(frame, kframe, sizeof(*frame));
}
-static int restore_crunch_context(struct crunch_sigframe *frame)
+static int restore_crunch_context(struct crunch_sigframe __user *frame)
{
char kbuf[sizeof(*frame) + 8];
struct crunch_sigframe *kframe;
diff --git a/arch/arm/mach-davinci/board-dm355-evm.c b/arch/arm/mach-davinci/board-dm355-evm.c
index 5ac2f565d86..d6ab64ccd49 100644
--- a/arch/arm/mach-davinci/board-dm355-evm.c
+++ b/arch/arm/mach-davinci/board-dm355-evm.c
@@ -37,7 +37,6 @@
#include <mach/serial.h>
#include <mach/nand.h>
#include <mach/mmc.h>
-#include <mach/common.h>
#define DAVINCI_ASYNC_EMIF_CONTROL_BASE 0x01e10000
#define DAVINCI_ASYNC_EMIF_DATA_CE0_BASE 0x02000000
diff --git a/arch/arm/mach-davinci/board-dm355-leopard.c b/arch/arm/mach-davinci/board-dm355-leopard.c
index 28c9008df4f..84ad5d161a8 100644
--- a/arch/arm/mach-davinci/board-dm355-leopard.c
+++ b/arch/arm/mach-davinci/board-dm355-leopard.c
@@ -36,7 +36,6 @@
#include <mach/serial.h>
#include <mach/nand.h>
#include <mach/mmc.h>
-#include <mach/common.h>
#define DAVINCI_ASYNC_EMIF_CONTROL_BASE 0x01e10000
#define DAVINCI_ASYNC_EMIF_DATA_CE0_BASE 0x02000000
diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c
index d9d40450bdc..56c8cd01de9 100644
--- a/arch/arm/mach-davinci/board-dm644x-evm.c
+++ b/arch/arm/mach-davinci/board-dm644x-evm.c
@@ -45,7 +45,6 @@
#include <mach/nand.h>
#include <mach/mmc.h>
#include <mach/emac.h>
-#include <mach/common.h>
#define DM644X_EVM_PHY_MASK (0x2)
#define DM644X_EVM_MDIO_FREQUENCY (2200000) /* PHY bus frequency */
diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c
index e17de635262..8657e72debc 100644
--- a/arch/arm/mach-davinci/board-dm646x-evm.c
+++ b/arch/arm/mach-davinci/board-dm646x-evm.c
@@ -47,7 +47,6 @@
#include <mach/i2c.h>
#include <mach/mmc.h>
#include <mach/emac.h>
-#include <mach/common.h>
#define DM646X_EVM_PHY_MASK (0x2)
#define DM646X_EVM_MDIO_FREQUENCY (2200000) /* PHY bus frequency */
diff --git a/arch/arm/mach-davinci/board-sffsdr.c b/arch/arm/mach-davinci/board-sffsdr.c
index 748a8e48541..7acdfd8ac07 100644
--- a/arch/arm/mach-davinci/board-sffsdr.c
+++ b/arch/arm/mach-davinci/board-sffsdr.c
@@ -52,7 +52,6 @@
#include <mach/serial.h>
#include <mach/psc.h>
#include <mach/mux.h>
-#include <mach/common.h>
#define SFFSDR_PHY_MASK (0x2)
#define SFFSDR_MDIO_FREQUENCY (2200000) /* PHY bus frequency */
diff --git a/arch/arm/mach-ep93xx/include/mach/ts72xx.h b/arch/arm/mach-ep93xx/include/mach/ts72xx.h
index 34ddec081c4..411734422c1 100644
--- a/arch/arm/mach-ep93xx/include/mach/ts72xx.h
+++ b/arch/arm/mach-ep93xx/include/mach/ts72xx.h
@@ -41,9 +41,6 @@
#define TS72XX_OPTIONS2_TS9420_BOOT 0x02
-#define TS72XX_NOR_PHYS_BASE 0x60000000
-#define TS72XX_NOR2_PHYS_BASE 0x62000000
-
#define TS72XX_NAND1_DATA_PHYS_BASE 0x60000000
#define TS72XX_NAND2_DATA_PHYS_BASE 0x70000000
#define TS72XX_NAND_DATA_VIRT_BASE 0xfebfc000
diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c
index 7ee024d3482..aaf1371412a 100644
--- a/arch/arm/mach-ep93xx/ts72xx.c
+++ b/arch/arm/mach-ep93xx/ts72xx.c
@@ -112,13 +112,16 @@ static void __init ts72xx_map_io(void)
}
}
+/*************************************************************************
+ * NOR flash (TS-7200 only)
+ *************************************************************************/
static struct physmap_flash_data ts72xx_flash_data = {
- .width = 1,
+ .width = 2,
};
static struct resource ts72xx_flash_resource = {
- .start = TS72XX_NOR_PHYS_BASE,
- .end = TS72XX_NOR_PHYS_BASE + SZ_16M - 1,
+ .start = EP93XX_CS6_PHYS_BASE,
+ .end = EP93XX_CS6_PHYS_BASE + SZ_16M - 1,
.flags = IORESOURCE_MEM,
};
@@ -132,6 +135,12 @@ static struct platform_device ts72xx_flash = {
.resource = &ts72xx_flash_resource,
};
+static void __init ts72xx_register_flash(void)
+{
+ if (board_is_ts7200())
+ platform_device_register(&ts72xx_flash);
+}
+
static unsigned char ts72xx_rtc_readbyte(unsigned long addr)
{
__raw_writeb(addr, TS72XX_RTC_INDEX_VIRT_BASE);
@@ -165,8 +174,7 @@ static struct ep93xx_eth_data ts72xx_eth_data = {
static void __init ts72xx_init_machine(void)
{
ep93xx_init_devices();
- if (board_is_ts7200())
- platform_device_register(&ts72xx_flash);
+ ts72xx_register_flash();
platform_device_register(&ts72xx_rtc_device);
ep93xx_register_eth(&ts72xx_eth_data, 1);
diff --git a/arch/arm/mach-ixp4xx/include/mach/io.h b/arch/arm/mach-ixp4xx/include/mach/io.h
index ce63048d45e..8a947d42a6f 100644
--- a/arch/arm/mach-ixp4xx/include/mach/io.h
+++ b/arch/arm/mach-ixp4xx/include/mach/io.h
@@ -17,7 +17,7 @@
#include <mach/hardware.h>
-#define IO_SPACE_LIMIT 0xffff0000
+#define IO_SPACE_LIMIT 0x0000ffff
extern int (*ixp4xx_pci_read)(u32 addr, u32 cmd, u32* data);
extern int ixp4xx_pci_write(u32 addr, u32 cmd, u32 data);
diff --git a/arch/arm/mach-kirkwood/ts219-setup.c b/arch/arm/mach-kirkwood/ts219-setup.c
index 01aa213c0a6..ec1a64f263d 100644
--- a/arch/arm/mach-kirkwood/ts219-setup.c
+++ b/arch/arm/mach-kirkwood/ts219-setup.c
@@ -206,6 +206,15 @@ static void __init qnap_ts219_init(void)
}
+static int __init ts219_pci_init(void)
+{
+ if (machine_is_ts219())
+ kirkwood_pcie_init();
+
+ return 0;
+}
+subsys_initcall(ts219_pci_init);
+
MACHINE_START(TS219, "QNAP TS-119/TS-219")
/* Maintainer: Martin Michlmayr <tbm@cyrius.com> */
.phys_io = KIRKWOOD_REGS_PHYS_BASE,
diff --git a/arch/arm/mach-ks8695/include/mach/hardware.h b/arch/arm/mach-ks8695/include/mach/hardware.h
index 1d640d075b7..e0f911d9e02 100644
--- a/arch/arm/mach-ks8695/include/mach/hardware.h
+++ b/arch/arm/mach-ks8695/include/mach/hardware.h
@@ -17,6 +17,11 @@
#include <asm/sizes.h>
/*
+ * Clocks are derived from MCLK, which is 25Mhz
+ */
+#define KS8695_CLOCK_RATE 25000000
+
+/*
* Physical RAM address.
*/
#define KS8695_SDRAM_PA 0x00000000
diff --git a/arch/arm/mach-ks8695/include/mach/timex.h b/arch/arm/mach-ks8695/include/mach/timex.h
index 4682e350369..10f716371bd 100644
--- a/arch/arm/mach-ks8695/include/mach/timex.h
+++ b/arch/arm/mach-ks8695/include/mach/timex.h
@@ -14,7 +14,8 @@
#ifndef __ASM_ARCH_TIMEX_H
#define __ASM_ARCH_TIMEX_H
-/* timers are derived from MCLK, which is 25MHz */
-#define CLOCK_TICK_RATE 25000000
+#include <mach/hardware.h>
+
+#define CLOCK_TICK_RATE KS8695_CLOCK_RATE
#endif
diff --git a/arch/arm/mach-ks8695/pci.c b/arch/arm/mach-ks8695/pci.c
index f5ebcc0fcab..78499667eb7 100644
--- a/arch/arm/mach-ks8695/pci.c
+++ b/arch/arm/mach-ks8695/pci.c
@@ -245,6 +245,9 @@ static int ks8695_pci_fault(unsigned long addr, unsigned int fsr, struct pt_regs
static void __init ks8695_pci_preinit(void)
{
+ /* make software reset to avoid freeze if PCI bus was messed up */
+ __raw_writel(0x80000000, KS8695_PCI_VA + KS8695_PBCS);
+
/* stage 1 initialization, subid, subdevice = 0x0001 */
__raw_writel(0x00010001, KS8695_PCI_VA + KS8695_CRCSID);
diff --git a/arch/arm/mach-mx3/mx31moboard-devboard.c b/arch/arm/mach-mx3/mx31moboard-devboard.c
index 4704405165a..b48581e7ded 100644
--- a/arch/arm/mach-mx3/mx31moboard-devboard.c
+++ b/arch/arm/mach-mx3/mx31moboard-devboard.c
@@ -63,7 +63,7 @@ static struct imxuart_platform_data uart_pdata = {
static int devboard_sdhc2_get_ro(struct device *dev)
{
- return gpio_get_value(SDHC2_WP);
+ return !gpio_get_value(SDHC2_WP);
}
static int devboard_sdhc2_init(struct device *dev, irq_handler_t detect_irq,
diff --git a/arch/arm/mach-mx3/mx31moboard-marxbot.c b/arch/arm/mach-mx3/mx31moboard-marxbot.c
index 641c3d6153a..901fb0166c0 100644
--- a/arch/arm/mach-mx3/mx31moboard-marxbot.c
+++ b/arch/arm/mach-mx3/mx31moboard-marxbot.c
@@ -67,7 +67,7 @@ static unsigned int marxbot_pins[] = {
static int marxbot_sdhc2_get_ro(struct device *dev)
{
- return gpio_get_value(SDHC2_WP);
+ return !gpio_get_value(SDHC2_WP);
}
static int marxbot_sdhc2_init(struct device *dev, irq_handler_t detect_irq,
diff --git a/arch/arm/mach-mx3/mx31moboard.c b/arch/arm/mach-mx3/mx31moboard.c
index a17f2e41160..2a2da4739ec 100644
--- a/arch/arm/mach-mx3/mx31moboard.c
+++ b/arch/arm/mach-mx3/mx31moboard.c
@@ -94,7 +94,7 @@ static struct imxi2c_platform_data moboard_i2c1_pdata = {
static int moboard_sdhc1_get_ro(struct device *dev)
{
- return gpio_get_value(SDHC1_WP);
+ return !gpio_get_value(SDHC1_WP);
}
static int moboard_sdhc1_init(struct device *dev, irq_handler_t detect_irq,
diff --git a/arch/arm/mach-mx3/pcm037_eet.c b/arch/arm/mach-mx3/pcm037_eet.c
index fe52fb1bb8b..8d386000fc4 100644
--- a/arch/arm/mach-mx3/pcm037_eet.c
+++ b/arch/arm/mach-mx3/pcm037_eet.c
@@ -24,15 +24,6 @@
#include "devices.h"
static unsigned int pcm037_eet_pins[] = {
- /* SPI #1 */
- MX31_PIN_CSPI1_MISO__MISO,
- MX31_PIN_CSPI1_MOSI__MOSI,
- MX31_PIN_CSPI1_SCLK__SCLK,
- MX31_PIN_CSPI1_SPI_RDY__SPI_RDY,
- MX31_PIN_CSPI1_SS0__SS0,
- MX31_PIN_CSPI1_SS1__SS1,
- MX31_PIN_CSPI1_SS2__SS2,
-
/* Reserve and hardwire GPIO 57 high - S6E63D6 chipselect */
IOMUX_MODE(MX31_PIN_KEY_COL7, IOMUX_CONFIG_GPIO),
/* GPIO keys */
diff --git a/arch/arm/mach-omap1/mcbsp.c b/arch/arm/mach-omap1/mcbsp.c
index a2d7814896b..505d98cfe50 100644
--- a/arch/arm/mach-omap1/mcbsp.c
+++ b/arch/arm/mach-omap1/mcbsp.c
@@ -19,7 +19,6 @@
#include <mach/irqs.h>
#include <mach/dma.h>
-#include <mach/irqs.h>
#include <mach/mux.h>
#include <mach/cpu.h>
#include <mach/mcbsp.h>
diff --git a/arch/arm/mach-omap2/board-2430sdp.c b/arch/arm/mach-omap2/board-2430sdp.c
index 9c3fdcdf76c..8ec2a132904 100644
--- a/arch/arm/mach-omap2/board-2430sdp.c
+++ b/arch/arm/mach-omap2/board-2430sdp.c
@@ -141,7 +141,7 @@ static inline void board_smc91x_init(void)
static void __init omap_2430sdp_init_irq(void)
{
- omap2_init_common_hw(NULL);
+ omap2_init_common_hw(NULL, NULL);
omap_init_irq();
omap_gpio_init();
}
diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c
index 496a90e4ea7..ac262cd7450 100644
--- a/arch/arm/mach-omap2/board-3430sdp.c
+++ b/arch/arm/mach-omap2/board-3430sdp.c
@@ -169,7 +169,7 @@ static struct platform_device *sdp3430_devices[] __initdata = {
static void __init omap_3430sdp_init_irq(void)
{
- omap2_init_common_hw(hyb18m512160af6_sdrc_params);
+ omap2_init_common_hw(hyb18m512160af6_sdrc_params, NULL);
omap_init_irq();
omap_gpio_init();
}
diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c
index 57e477bd89c..b0c7402248f 100644
--- a/arch/arm/mach-omap2/board-4430sdp.c
+++ b/arch/arm/mach-omap2/board-4430sdp.c
@@ -59,7 +59,7 @@ static void __init gic_init_irq(void)
static void __init omap_4430sdp_init_irq(void)
{
- omap2_init_common_hw(NULL);
+ omap2_init_common_hw(NULL, NULL);
#ifdef CONFIG_OMAP_32K_TIMER
omap2_gp_clockevent_set_gptimer(1);
#endif
diff --git a/arch/arm/mach-omap2/board-apollon.c b/arch/arm/mach-omap2/board-apollon.c
index 06dfba888b0..dcfc20d0389 100644
--- a/arch/arm/mach-omap2/board-apollon.c
+++ b/arch/arm/mach-omap2/board-apollon.c
@@ -250,7 +250,7 @@ out:
static void __init omap_apollon_init_irq(void)
{
- omap2_init_common_hw(NULL);
+ omap2_init_common_hw(NULL, NULL);
omap_init_irq();
omap_gpio_init();
apollon_init_smc91x();
diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index 3492162a65c..fd00aa03690 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -33,7 +33,7 @@
static void __init omap_generic_init_irq(void)
{
- omap2_init_common_hw(NULL);
+ omap2_init_common_hw(NULL, NULL);
omap_init_irq();
}
diff --git a/arch/arm/mach-omap2/board-h4.c b/arch/arm/mach-omap2/board-h4.c
index e7d017cdc43..7b1d61d5bb2 100644
--- a/arch/arm/mach-omap2/board-h4.c
+++ b/arch/arm/mach-omap2/board-h4.c
@@ -270,7 +270,7 @@ static void __init h4_init_flash(void)
static void __init omap_h4_init_irq(void)
{
- omap2_init_common_hw(NULL);
+ omap2_init_common_hw(NULL, NULL);
omap_init_irq();
omap_gpio_init();
h4_init_flash();
diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c
index d8bc0a7dcb8..ea383f88cb1 100644
--- a/arch/arm/mach-omap2/board-ldp.c
+++ b/arch/arm/mach-omap2/board-ldp.c
@@ -270,7 +270,7 @@ static inline void __init ldp_init_smsc911x(void)
static void __init omap_ldp_init_irq(void)
{
- omap2_init_common_hw(NULL);
+ omap2_init_common_hw(NULL, NULL);
omap_init_irq();
omap_gpio_init();
ldp_init_smsc911x();
diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c
index 991ac9c3803..e00ba128cec 100644
--- a/arch/arm/mach-omap2/board-omap3beagle.c
+++ b/arch/arm/mach-omap2/board-omap3beagle.c
@@ -282,7 +282,8 @@ static int __init omap3_beagle_i2c_init(void)
static void __init omap3_beagle_init_irq(void)
{
- omap2_init_common_hw(mt46h32m32lf6_sdrc_params);
+ omap2_init_common_hw(mt46h32m32lf6_sdrc_params,
+ mt46h32m32lf6_sdrc_params);
omap_init_irq();
#ifdef CONFIG_OMAP_32K_TIMER
omap2_gp_clockevent_set_gptimer(12);
@@ -408,6 +409,10 @@ static void __init omap3_beagle_init(void)
usb_musb_init();
omap3beagle_flash_init();
+
+ /* Ensure SDRC pins are mux'd for self-refresh */
+ omap_cfg_reg(H16_34XX_SDRC_CKE0);
+ omap_cfg_reg(H17_34XX_SDRC_CKE1);
}
static void __init omap3_beagle_map_io(void)
diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c
index d3cc145814d..c4b144647dc 100644
--- a/arch/arm/mach-omap2/board-omap3evm.c
+++ b/arch/arm/mach-omap2/board-omap3evm.c
@@ -25,6 +25,7 @@
#include <linux/spi/spi.h>
#include <linux/spi/ads7846.h>
#include <linux/i2c/twl4030.h>
+#include <linux/usb/otg.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
@@ -279,7 +280,7 @@ struct spi_board_info omap3evm_spi_board_info[] = {
static void __init omap3_evm_init_irq(void)
{
- omap2_init_common_hw(mt46h32m32lf6_sdrc_params);
+ omap2_init_common_hw(mt46h32m32lf6_sdrc_params, NULL);
omap_init_irq();
omap_gpio_init();
omap3evm_init_smc911x();
@@ -307,6 +308,10 @@ static void __init omap3_evm_init(void)
ARRAY_SIZE(omap3evm_spi_board_info));
omap_serial_init();
+#ifdef CONFIG_NOP_USB_XCEIV
+ /* OMAP3EVM uses ISP1504 phy and so register nop transceiver */
+ usb_nop_xceiv_register();
+#endif
usb_musb_init();
ads7846_dev_init();
}
diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c
index e32aa23ce96..864ee3d021f 100644
--- a/arch/arm/mach-omap2/board-omap3pandora.c
+++ b/arch/arm/mach-omap2/board-omap3pandora.c
@@ -40,6 +40,7 @@
#include <mach/mcspi.h>
#include <mach/usb.h>
#include <mach/keypad.h>
+#include <mach/mux.h>
#include "sdram-micron-mt46h32m32lf-6.h"
#include "mmc-twl4030.h"
@@ -310,7 +311,8 @@ static int __init omap3pandora_i2c_init(void)
static void __init omap3pandora_init_irq(void)
{
- omap2_init_common_hw(mt46h32m32lf6_sdrc_params);
+ omap2_init_common_hw(mt46h32m32lf6_sdrc_params,
+ mt46h32m32lf6_sdrc_params);
omap_init_irq();
omap_gpio_init();
}
@@ -397,6 +399,10 @@ static void __init omap3pandora_init(void)
omap3pandora_ads7846_init();
pandora_keys_gpio_init();
usb_musb_init();
+
+ /* Ensure SDRC pins are mux'd for self-refresh */
+ omap_cfg_reg(H16_34XX_SDRC_CKE0);
+ omap_cfg_reg(H17_34XX_SDRC_CKE1);
}
static void __init omap3pandora_map_io(void)
diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c
index dff5528fbfb..6bce23004aa 100644
--- a/arch/arm/mach-omap2/board-overo.c
+++ b/arch/arm/mach-omap2/board-overo.c
@@ -44,6 +44,7 @@
#include <mach/gpmc.h>
#include <mach/hardware.h>
#include <mach/nand.h>
+#include <mach/mux.h>
#include <mach/usb.h>
#include "sdram-micron-mt46h32m32lf-6.h"
@@ -51,6 +52,7 @@
#define OVERO_GPIO_BT_XGATE 15
#define OVERO_GPIO_W2W_NRESET 16
+#define OVERO_GPIO_PENDOWN 114
#define OVERO_GPIO_BT_NRESET 164
#define OVERO_GPIO_USBH_CPEN 168
#define OVERO_GPIO_USBH_NRESET 183
@@ -146,7 +148,7 @@ static struct platform_device overo_smsc911x_device = {
.name = "smsc911x",
.id = -1,
.num_resources = ARRAY_SIZE(overo_smsc911x_resources),
- .resource = &overo_smsc911x_resources,
+ .resource = overo_smsc911x_resources,
.dev = {
.platform_data = &overo_smsc911x_config,
},
@@ -360,7 +362,8 @@ static int __init overo_i2c_init(void)
static void __init overo_init_irq(void)
{
- omap2_init_common_hw(mt46h32m32lf6_sdrc_params);
+ omap2_init_common_hw(mt46h32m32lf6_sdrc_params,
+ mt46h32m32lf6_sdrc_params);
omap_init_irq();
omap_gpio_init();
}
@@ -395,6 +398,10 @@ static void __init overo_init(void)
overo_ads7846_init();
overo_init_smsc911x();
+ /* Ensure SDRC pins are mux'd for self-refresh */
+ omap_cfg_reg(H16_34XX_SDRC_CKE0);
+ omap_cfg_reg(H17_34XX_SDRC_CKE1);
+
if ((gpio_request(OVERO_GPIO_W2W_NRESET,
"OVERO_GPIO_W2W_NRESET") == 0) &&
(gpio_direction_output(OVERO_GPIO_W2W_NRESET, 1) == 0)) {
diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
index 9a0bf6744a0..56d931a425f 100644
--- a/arch/arm/mach-omap2/board-rx51-peripherals.c
+++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -278,6 +278,10 @@ static struct twl4030_gpio_platform_data rx51_gpio_data = {
.setup = rx51_twlgpio_setup,
};
+static struct twl4030_usb_data rx51_usb_data = {
+ .usb_mode = T2_USB_MODE_ULPI,
+};
+
static struct twl4030_platform_data rx51_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@@ -286,6 +290,7 @@ static struct twl4030_platform_data rx51_twldata = {
.gpio = &rx51_gpio_data,
.keypad = &rx51_kp_data,
.madc = &rx51_madc_data,
+ .usb = &rx51_usb_data,
.vaux1 = &rx51_vaux1,
.vaux2 = &rx51_vaux2,
diff --git a/arch/arm/mach-omap2/board-rx51.c b/arch/arm/mach-omap2/board-rx51.c
index 374ff63c3eb..1c9e07fe826 100644
--- a/arch/arm/mach-omap2/board-rx51.c
+++ b/arch/arm/mach-omap2/board-rx51.c
@@ -61,7 +61,7 @@ static struct omap_board_config_kernel rx51_config[] = {
static void __init rx51_init_irq(void)
{
- omap2_init_common_hw(NULL);
+ omap2_init_common_hw(NULL, NULL);
omap_init_irq();
omap_gpio_init();
}
@@ -75,6 +75,10 @@ static void __init rx51_init(void)
omap_serial_init();
usb_musb_init();
rx51_peripherals_init();
+
+ /* Ensure SDRC pins are mux'd for self-refresh */
+ omap_cfg_reg(H16_34XX_SDRC_CKE0);
+ omap_cfg_reg(H17_34XX_SDRC_CKE1);
}
static void __init rx51_map_io(void)
diff --git a/arch/arm/mach-omap2/board-zoom2.c b/arch/arm/mach-omap2/board-zoom2.c
index bcc0f7632de..427b7b8b123 100644
--- a/arch/arm/mach-omap2/board-zoom2.c
+++ b/arch/arm/mach-omap2/board-zoom2.c
@@ -25,7 +25,7 @@
static void __init omap_zoom2_init_irq(void)
{
- omap2_init_common_hw(NULL);
+ omap2_init_common_hw(NULL, NULL);
omap_init_irq();
omap_gpio_init();
}
diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index b0665f161c0..456e2ad5f62 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -27,6 +27,7 @@
#include <mach/clock.h>
#include <mach/clockdomain.h>
#include <mach/cpu.h>
+#include <mach/prcm.h>
#include <asm/div64.h>
#include <mach/sdrc.h>
@@ -38,8 +39,6 @@
#include "cm-regbits-24xx.h"
#include "cm-regbits-34xx.h"
-#define MAX_CLOCK_ENABLE_WAIT 100000
-
/* DPLL rate rounding: minimum DPLL multiplier, divider values */
#define DPLL_MIN_MULTIPLIER 1
#define DPLL_MIN_DIVIDER 1
@@ -274,83 +273,97 @@ unsigned long omap2_fixed_divisor_recalc(struct clk *clk)
}
/**
- * omap2_wait_clock_ready - wait for clock to enable
- * @reg: physical address of clock IDLEST register
- * @mask: value to mask against to determine if the clock is active
- * @name: name of the clock (for printk)
+ * omap2_clk_dflt_find_companion - find companion clock to @clk
+ * @clk: struct clk * to find the companion clock of
+ * @other_reg: void __iomem ** to return the companion clock CM_*CLKEN va in
+ * @other_bit: u8 ** to return the companion clock bit shift in
+ *
+ * Note: We don't need special code here for INVERT_ENABLE for the
+ * time being since INVERT_ENABLE only applies to clocks enabled by
+ * CM_CLKEN_PLL
*
- * Returns 1 if the clock enabled in time, or 0 if it failed to enable
- * in roughly MAX_CLOCK_ENABLE_WAIT microseconds.
+ * Convert CM_ICLKEN* <-> CM_FCLKEN*. This conversion assumes it's
+ * just a matter of XORing the bits.
+ *
+ * Some clocks don't have companion clocks. For example, modules with
+ * only an interface clock (such as MAILBOXES) don't have a companion
+ * clock. Right now, this code relies on the hardware exporting a bit
+ * in the correct companion register that indicates that the
+ * nonexistent 'companion clock' is active. Future patches will
+ * associate this type of code with per-module data structures to
+ * avoid this issue, and remove the casts. No return value.
*/
-int omap2_wait_clock_ready(void __iomem *reg, u32 mask, const char *name)
+void omap2_clk_dflt_find_companion(struct clk *clk, void __iomem **other_reg,
+ u8 *other_bit)
{
- int i = 0;
- int ena = 0;
+ u32 r;
/*
- * 24xx uses 0 to indicate not ready, and 1 to indicate ready.
- * 34xx reverses this, just to keep us on our toes
+ * Convert CM_ICLKEN* <-> CM_FCLKEN*. This conversion assumes
+ * it's just a matter of XORing the bits.
*/
- if (cpu_mask & (RATE_IN_242X | RATE_IN_243X))
- ena = mask;
- else if (cpu_mask & RATE_IN_343X)
- ena = 0;
-
- /* Wait for lock */
- while (((__raw_readl(reg) & mask) != ena) &&
- (i++ < MAX_CLOCK_ENABLE_WAIT)) {
- udelay(1);
- }
-
- if (i <= MAX_CLOCK_ENABLE_WAIT)
- pr_debug("Clock %s stable after %d loops\n", name, i);
- else
- printk(KERN_ERR "Clock %s didn't enable in %d tries\n",
- name, MAX_CLOCK_ENABLE_WAIT);
-
-
- return (i < MAX_CLOCK_ENABLE_WAIT) ? 1 : 0;
-};
+ r = ((__force u32)clk->enable_reg ^ (CM_FCLKEN ^ CM_ICLKEN));
+ *other_reg = (__force void __iomem *)r;
+ *other_bit = clk->enable_bit;
+}
-/*
- * Note: We don't need special code here for INVERT_ENABLE
- * for the time being since INVERT_ENABLE only applies to clocks enabled by
- * CM_CLKEN_PLL
+/**
+ * omap2_clk_dflt_find_idlest - find CM_IDLEST reg va, bit shift for @clk
+ * @clk: struct clk * to find IDLEST info for
+ * @idlest_reg: void __iomem ** to return the CM_IDLEST va in
+ * @idlest_bit: u8 ** to return the CM_IDLEST bit shift in
+ *
+ * Return the CM_IDLEST register address and bit shift corresponding
+ * to the module that "owns" this clock. This default code assumes
+ * that the CM_IDLEST bit shift is the CM_*CLKEN bit shift, and that
+ * the IDLEST register address ID corresponds to the CM_*CLKEN
+ * register address ID (e.g., that CM_FCLKEN2 corresponds to
+ * CM_IDLEST2). This is not true for all modules. No return value.
*/
-static void omap2_clk_wait_ready(struct clk *clk)
+void omap2_clk_dflt_find_idlest(struct clk *clk, void __iomem **idlest_reg,
+ u8 *idlest_bit)
{
- void __iomem *reg, *other_reg, *st_reg;
- u32 bit;
+ u32 r;
- /*
- * REVISIT: This code is pretty ugly. It would be nice to generalize
- * it and pull it into struct clk itself somehow.
- */
- reg = clk->enable_reg;
+ r = (((__force u32)clk->enable_reg & ~0xf0) | 0x20);
+ *idlest_reg = (__force void __iomem *)r;
+ *idlest_bit = clk->enable_bit;
+}
- /*
- * Convert CM_ICLKEN* <-> CM_FCLKEN*. This conversion assumes
- * it's just a matter of XORing the bits.
- */
- other_reg = (void __iomem *)((u32)reg ^ (CM_FCLKEN ^ CM_ICLKEN));
+/**
+ * omap2_module_wait_ready - wait for an OMAP module to leave IDLE
+ * @clk: struct clk * belonging to the module
+ *
+ * If the necessary clocks for the OMAP hardware IP block that
+ * corresponds to clock @clk are enabled, then wait for the module to
+ * indicate readiness (i.e., to leave IDLE). This code does not
+ * belong in the clock code and will be moved in the medium term to
+ * module-dependent code. No return value.
+ */
+static void omap2_module_wait_ready(struct clk *clk)
+{
+ void __iomem *companion_reg, *idlest_reg;
+ u8 other_bit, idlest_bit;
+
+ /* Not all modules have multiple clocks that their IDLEST depends on */
+ if (clk->ops->find_companion) {
+ clk->ops->find_companion(clk, &companion_reg, &other_bit);
+ if (!(__raw_readl(companion_reg) & (1 << other_bit)))
+ return;
+ }
- /* Check if both functional and interface clocks
- * are running. */
- bit = 1 << clk->enable_bit;
- if (!(__raw_readl(other_reg) & bit))
- return;
- st_reg = (void __iomem *)(((u32)other_reg & ~0xf0) | 0x20); /* CM_IDLEST* */
+ clk->ops->find_idlest(clk, &idlest_reg, &idlest_bit);
- omap2_wait_clock_ready(st_reg, bit, clk->name);
+ omap2_cm_wait_idlest(idlest_reg, (1 << idlest_bit), clk->name);
}
-static int omap2_dflt_clk_enable(struct clk *clk)
+int omap2_dflt_clk_enable(struct clk *clk)
{
u32 v;
if (unlikely(clk->enable_reg == NULL)) {
- printk(KERN_ERR "clock.c: Enable for %s without enable code\n",
+ pr_err("clock.c: Enable for %s without enable code\n",
clk->name);
return 0; /* REVISIT: -EINVAL */
}
@@ -363,26 +376,13 @@ static int omap2_dflt_clk_enable(struct clk *clk)
__raw_writel(v, clk->enable_reg);
v = __raw_readl(clk->enable_reg); /* OCP barrier */
- return 0;
-}
+ if (clk->ops->find_idlest)
+ omap2_module_wait_ready(clk);
-static int omap2_dflt_clk_enable_wait(struct clk *clk)
-{
- int ret;
-
- if (!clk->enable_reg) {
- printk(KERN_ERR "clock.c: Enable for %s without enable code\n",
- clk->name);
- return 0; /* REVISIT: -EINVAL */
- }
-
- ret = omap2_dflt_clk_enable(clk);
- if (ret == 0)
- omap2_clk_wait_ready(clk);
- return ret;
+ return 0;
}
-static void omap2_dflt_clk_disable(struct clk *clk)
+void omap2_dflt_clk_disable(struct clk *clk)
{
u32 v;
@@ -406,8 +406,10 @@ static void omap2_dflt_clk_disable(struct clk *clk)
}
const struct clkops clkops_omap2_dflt_wait = {
- .enable = omap2_dflt_clk_enable_wait,
+ .enable = omap2_dflt_clk_enable,
.disable = omap2_dflt_clk_disable,
+ .find_companion = omap2_clk_dflt_find_companion,
+ .find_idlest = omap2_clk_dflt_find_idlest,
};
const struct clkops clkops_omap2_dflt = {
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h
index 2679ddfa642..9ae7540f8af 100644
--- a/arch/arm/mach-omap2/clock.h
+++ b/arch/arm/mach-omap2/clock.h
@@ -65,6 +65,12 @@ int omap2_clksel_set_rate(struct clk *clk, unsigned long rate);
u32 omap2_get_dpll_rate(struct clk *clk);
int omap2_wait_clock_ready(void __iomem *reg, u32 cval, const char *name);
void omap2_clk_prepare_for_reboot(void);
+int omap2_dflt_clk_enable(struct clk *clk);
+void omap2_dflt_clk_disable(struct clk *clk);
+void omap2_clk_dflt_find_companion(struct clk *clk, void __iomem **other_reg,
+ u8 *other_bit);
+void omap2_clk_dflt_find_idlest(struct clk *clk, void __iomem **idlest_reg,
+ u8 *idlest_bit);
extern const struct clkops clkops_omap2_dflt_wait;
extern const struct clkops clkops_omap2_dflt;
diff --git a/arch/arm/mach-omap2/clock24xx.c b/arch/arm/mach-omap2/clock24xx.c
index 44de0271fc2..bc5d3ac6661 100644
--- a/arch/arm/mach-omap2/clock24xx.c
+++ b/arch/arm/mach-omap2/clock24xx.c
@@ -30,6 +30,7 @@
#include <mach/clock.h>
#include <mach/sram.h>
+#include <mach/prcm.h>
#include <asm/div64.h>
#include <asm/clkdev.h>
@@ -43,6 +44,18 @@
static const struct clkops clkops_oscck;
static const struct clkops clkops_fixed;
+static void omap2430_clk_i2chs_find_idlest(struct clk *clk,
+ void __iomem **idlest_reg,
+ u8 *idlest_bit);
+
+/* 2430 I2CHS has non-standard IDLEST register */
+static const struct clkops clkops_omap2430_i2chs_wait = {
+ .enable = omap2_dflt_clk_enable,
+ .disable = omap2_dflt_clk_disable,
+ .find_idlest = omap2430_clk_i2chs_find_idlest,
+ .find_companion = omap2_clk_dflt_find_companion,
+};
+
#include "clock24xx.h"
struct omap_clk {
@@ -240,6 +253,26 @@ static void __iomem *prcm_clksrc_ctrl;
*-------------------------------------------------------------------------*/
/**
+ * omap2430_clk_i2chs_find_idlest - return CM_IDLEST info for 2430 I2CHS
+ * @clk: struct clk * being enabled
+ * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into
+ * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into
+ *
+ * OMAP2430 I2CHS CM_IDLEST bits are in CM_IDLEST1_CORE, but the
+ * CM_*CLKEN bits are in CM_{I,F}CLKEN2_CORE. This custom function
+ * passes back the correct CM_IDLEST register address for I2CHS
+ * modules. No return value.
+ */
+static void omap2430_clk_i2chs_find_idlest(struct clk *clk,
+ void __iomem **idlest_reg,
+ u8 *idlest_bit)
+{
+ *idlest_reg = OMAP_CM_REGADDR(CORE_MOD, CM_IDLEST);
+ *idlest_bit = clk->enable_bit;
+}
+
+
+/**
* omap2xxx_clk_get_core_rate - return the CORE_CLK rate
* @clk: pointer to the combined dpll_ck + core_ck (currently "dpll_ck")
*
@@ -325,8 +358,8 @@ static int omap2_clk_fixed_enable(struct clk *clk)
else if (clk == &apll54_ck)
cval = OMAP24XX_ST_54M_APLL;
- omap2_wait_clock_ready(OMAP_CM_REGADDR(PLL_MOD, CM_IDLEST), cval,
- clk->name);
+ omap2_cm_wait_idlest(OMAP_CM_REGADDR(PLL_MOD, CM_IDLEST), cval,
+ clk->name);
/*
* REVISIT: Should we return an error code if omap2_wait_clock_ready()
diff --git a/arch/arm/mach-omap2/clock24xx.h b/arch/arm/mach-omap2/clock24xx.h
index 458f00cdcbe..d19cf7a7d8d 100644
--- a/arch/arm/mach-omap2/clock24xx.h
+++ b/arch/arm/mach-omap2/clock24xx.h
@@ -2337,7 +2337,7 @@ static struct clk i2c2_fck = {
static struct clk i2chs2_fck = {
.name = "i2c_fck",
- .ops = &clkops_omap2_dflt_wait,
+ .ops = &clkops_omap2430_i2chs_wait,
.id = 2,
.parent = &func_96m_ck,
.clkdm_name = "core_l4_clkdm",
@@ -2370,7 +2370,7 @@ static struct clk i2c1_fck = {
static struct clk i2chs1_fck = {
.name = "i2c_fck",
- .ops = &clkops_omap2_dflt_wait,
+ .ops = &clkops_omap2430_i2chs_wait,
.id = 1,
.parent = &func_96m_ck,
.clkdm_name = "core_l4_clkdm",
diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
index 045da923e75..cd7819cc0c9 100644
--- a/arch/arm/mach-omap2/clock34xx.c
+++ b/arch/arm/mach-omap2/clock34xx.c
@@ -2,7 +2,7 @@
* OMAP3-specific clock framework functions
*
* Copyright (C) 2007-2008 Texas Instruments, Inc.
- * Copyright (C) 2007-2008 Nokia Corporation
+ * Copyright (C) 2007-2009 Nokia Corporation
*
* Written by Paul Walmsley
* Testing and integration fixes by Jouni Högander
@@ -41,6 +41,37 @@
static const struct clkops clkops_noncore_dpll_ops;
+static void omap3430es2_clk_ssi_find_idlest(struct clk *clk,
+ void __iomem **idlest_reg,
+ u8 *idlest_bit);
+static void omap3430es2_clk_hsotgusb_find_idlest(struct clk *clk,
+ void __iomem **idlest_reg,
+ u8 *idlest_bit);
+static void omap3430es2_clk_dss_usbhost_find_idlest(struct clk *clk,
+ void __iomem **idlest_reg,
+ u8 *idlest_bit);
+
+static const struct clkops clkops_omap3430es2_ssi_wait = {
+ .enable = omap2_dflt_clk_enable,
+ .disable = omap2_dflt_clk_disable,
+ .find_idlest = omap3430es2_clk_ssi_find_idlest,
+ .find_companion = omap2_clk_dflt_find_companion,
+};
+
+static const struct clkops clkops_omap3430es2_hsotgusb_wait = {
+ .enable = omap2_dflt_clk_enable,
+ .disable = omap2_dflt_clk_disable,
+ .find_idlest = omap3430es2_clk_hsotgusb_find_idlest,
+ .find_companion = omap2_clk_dflt_find_companion,
+};
+
+static const struct clkops clkops_omap3430es2_dss_usbhost_wait = {
+ .enable = omap2_dflt_clk_enable,
+ .disable = omap2_dflt_clk_disable,
+ .find_idlest = omap3430es2_clk_dss_usbhost_find_idlest,
+ .find_companion = omap2_clk_dflt_find_companion,
+};
+
#include "clock34xx.h"
struct omap_clk {
@@ -157,10 +188,13 @@ static struct omap_clk omap34xx_clks[] = {
CLK(NULL, "fshostusb_fck", &fshostusb_fck, CK_3430ES1),
CLK(NULL, "core_12m_fck", &core_12m_fck, CK_343X),
CLK("omap_hdq.0", "fck", &hdq_fck, CK_343X),
- CLK(NULL, "ssi_ssr_fck", &ssi_ssr_fck, CK_343X),
- CLK(NULL, "ssi_sst_fck", &ssi_sst_fck, CK_343X),
+ CLK(NULL, "ssi_ssr_fck", &ssi_ssr_fck_3430es1, CK_3430ES1),
+ CLK(NULL, "ssi_ssr_fck", &ssi_ssr_fck_3430es2, CK_3430ES2),
+ CLK(NULL, "ssi_sst_fck", &ssi_sst_fck_3430es1, CK_3430ES1),
+ CLK(NULL, "ssi_sst_fck", &ssi_sst_fck_3430es2, CK_3430ES2),
CLK(NULL, "core_l3_ick", &core_l3_ick, CK_343X),
- CLK("musb_hdrc", "ick", &hsotgusb_ick, CK_343X),
+ CLK("musb_hdrc", "ick", &hsotgusb_ick_3430es1, CK_3430ES1),
+ CLK("musb_hdrc", "ick", &hsotgusb_ick_3430es2, CK_3430ES2),
CLK(NULL, "sdrc_ick", &sdrc_ick, CK_343X),
CLK(NULL, "gpmc_fck", &gpmc_fck, CK_343X),
CLK(NULL, "security_l3_ick", &security_l3_ick, CK_343X),
@@ -193,18 +227,21 @@ static struct omap_clk omap34xx_clks[] = {
CLK(NULL, "mailboxes_ick", &mailboxes_ick, CK_343X),
CLK(NULL, "omapctrl_ick", &omapctrl_ick, CK_343X),
CLK(NULL, "ssi_l4_ick", &ssi_l4_ick, CK_343X),
- CLK(NULL, "ssi_ick", &ssi_ick, CK_343X),
+ CLK(NULL, "ssi_ick", &ssi_ick_3430es1, CK_3430ES1),
+ CLK(NULL, "ssi_ick", &ssi_ick_3430es2, CK_3430ES2),
CLK(NULL, "usb_l4_ick", &usb_l4_ick, CK_3430ES1),
CLK(NULL, "security_l4_ick2", &security_l4_ick2, CK_343X),
CLK(NULL, "aes1_ick", &aes1_ick, CK_343X),
CLK("omap_rng", "ick", &rng_ick, CK_343X),
CLK(NULL, "sha11_ick", &sha11_ick, CK_343X),
CLK(NULL, "des1_ick", &des1_ick, CK_343X),
- CLK("omapfb", "dss1_fck", &dss1_alwon_fck, CK_343X),
+ CLK("omapfb", "dss1_fck", &dss1_alwon_fck_3430es1, CK_3430ES1),
+ CLK("omapfb", "dss1_fck", &dss1_alwon_fck_3430es2, CK_3430ES2),
CLK("omapfb", "tv_fck", &dss_tv_fck, CK_343X),
CLK("omapfb", "video_fck", &dss_96m_fck, CK_343X),
CLK("omapfb", "dss2_fck", &dss2_alwon_fck, CK_343X),
- CLK("omapfb", "ick", &dss_ick, CK_343X),
+ CLK("omapfb", "ick", &dss_ick_3430es1, CK_3430ES1),
+ CLK("omapfb", "ick", &dss_ick_3430es2, CK_3430ES2),
CLK(NULL, "cam_mclk", &cam_mclk, CK_343X),
CLK(NULL, "cam_ick", &cam_ick, CK_343X),
CLK(NULL, "csi2_96m_fck", &csi2_96m_fck, CK_343X),
@@ -301,6 +338,73 @@ static struct omap_clk omap34xx_clks[] = {
#define SDRC_MPURATE_LOOPS 96
/**
+ * omap3430es2_clk_ssi_find_idlest - return CM_IDLEST info for SSI
+ * @clk: struct clk * being enabled
+ * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into
+ * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into
+ *
+ * The OMAP3430ES2 SSI target CM_IDLEST bit is at a different shift
+ * from the CM_{I,F}CLKEN bit. Pass back the correct info via
+ * @idlest_reg and @idlest_bit. No return value.
+ */
+static void omap3430es2_clk_ssi_find_idlest(struct clk *clk,
+ void __iomem **idlest_reg,
+ u8 *idlest_bit)
+{
+ u32 r;
+
+ r = (((__force u32)clk->enable_reg & ~0xf0) | 0x20);
+ *idlest_reg = (__force void __iomem *)r;
+ *idlest_bit = OMAP3430ES2_ST_SSI_IDLE_SHIFT;
+}
+
+/**
+ * omap3430es2_clk_dss_usbhost_find_idlest - CM_IDLEST info for DSS, USBHOST
+ * @clk: struct clk * being enabled
+ * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into
+ * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into
+ *
+ * Some OMAP modules on OMAP3 ES2+ chips have both initiator and
+ * target IDLEST bits. For our purposes, we are concerned with the
+ * target IDLEST bits, which exist at a different bit position than
+ * the *CLKEN bit position for these modules (DSS and USBHOST) (The
+ * default find_idlest code assumes that they are at the same
+ * position.) No return value.
+ */
+static void omap3430es2_clk_dss_usbhost_find_idlest(struct clk *clk,
+ void __iomem **idlest_reg,
+ u8 *idlest_bit)
+{
+ u32 r;
+
+ r = (((__force u32)clk->enable_reg & ~0xf0) | 0x20);
+ *idlest_reg = (__force void __iomem *)r;
+ /* USBHOST_IDLE has same shift */
+ *idlest_bit = OMAP3430ES2_ST_DSS_IDLE_SHIFT;
+}
+
+/**
+ * omap3430es2_clk_hsotgusb_find_idlest - return CM_IDLEST info for HSOTGUSB
+ * @clk: struct clk * being enabled
+ * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into
+ * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into
+ *
+ * The OMAP3430ES2 HSOTGUSB target CM_IDLEST bit is at a different
+ * shift from the CM_{I,F}CLKEN bit. Pass back the correct info via
+ * @idlest_reg and @idlest_bit. No return value.
+ */
+static void omap3430es2_clk_hsotgusb_find_idlest(struct clk *clk,
+ void __iomem **idlest_reg,
+ u8 *idlest_bit)
+{
+ u32 r;
+
+ r = (((__force u32)clk->enable_reg & ~0xf0) | 0x20);
+ *idlest_reg = (__force void __iomem *)r;
+ *idlest_bit = OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT;
+}
+
+/**
* omap3_dpll_recalc - recalculate DPLL rate
* @clk: DPLL struct clk
*
@@ -725,7 +829,9 @@ static int omap3_core_dpll_m2_set_rate(struct clk *clk, unsigned long rate)
u32 unlock_dll = 0;
u32 c;
unsigned long validrate, sdrcrate, mpurate;
- struct omap_sdrc_params *sp;
+ struct omap_sdrc_params *sdrc_cs0;
+ struct omap_sdrc_params *sdrc_cs1;
+ int ret;
if (!clk || !rate)
return -EINVAL;
@@ -743,8 +849,8 @@ static int omap3_core_dpll_m2_set_rate(struct clk *clk, unsigned long rate)
else
sdrcrate >>= ((clk->rate / rate) >> 1);
- sp = omap2_sdrc_get_params(sdrcrate);
- if (!sp)
+ ret = omap2_sdrc_get_params(sdrcrate, &sdrc_cs0, &sdrc_cs1);
+ if (ret)
return -EINVAL;
if (sdrcrate < MIN_SDRC_DLL_LOCK_FREQ) {
@@ -765,12 +871,29 @@ static int omap3_core_dpll_m2_set_rate(struct clk *clk, unsigned long rate)
pr_debug("clock: changing CORE DPLL rate from %lu to %lu\n", clk->rate,
validrate);
- pr_debug("clock: SDRC timing params used: %08x %08x %08x\n",
- sp->rfr_ctrl, sp->actim_ctrla, sp->actim_ctrlb);
-
- omap3_configure_core_dpll(sp->rfr_ctrl, sp->actim_ctrla,
- sp->actim_ctrlb, new_div, unlock_dll, c,
- sp->mr, rate > clk->rate);
+ pr_debug("clock: SDRC CS0 timing params used:"
+ " RFR %08x CTRLA %08x CTRLB %08x MR %08x\n",
+ sdrc_cs0->rfr_ctrl, sdrc_cs0->actim_ctrla,
+ sdrc_cs0->actim_ctrlb, sdrc_cs0->mr);
+ if (sdrc_cs1)
+ pr_debug("clock: SDRC CS1 timing params used: "
+ " RFR %08x CTRLA %08x CTRLB %08x MR %08x\n",
+ sdrc_cs1->rfr_ctrl, sdrc_cs1->actim_ctrla,
+ sdrc_cs1->actim_ctrlb, sdrc_cs1->mr);
+
+ if (sdrc_cs1)
+ omap3_configure_core_dpll(
+ new_div, unlock_dll, c, rate > clk->rate,
+ sdrc_cs0->rfr_ctrl, sdrc_cs0->actim_ctrla,
+ sdrc_cs0->actim_ctrlb, sdrc_cs0->mr,
+ sdrc_cs1->rfr_ctrl, sdrc_cs1->actim_ctrla,
+ sdrc_cs1->actim_ctrlb, sdrc_cs1->mr);
+ else
+ omap3_configure_core_dpll(
+ new_div, unlock_dll, c, rate > clk->rate,
+ sdrc_cs0->rfr_ctrl, sdrc_cs0->actim_ctrla,
+ sdrc_cs0->actim_ctrlb, sdrc_cs0->mr,
+ 0, 0, 0, 0);
return 0;
}
diff --git a/arch/arm/mach-omap2/clock34xx.h b/arch/arm/mach-omap2/clock34xx.h
index e433aec4efd..57cc2725b92 100644
--- a/arch/arm/mach-omap2/clock34xx.h
+++ b/arch/arm/mach-omap2/clock34xx.h
@@ -1568,7 +1568,7 @@ static const struct clksel ssi_ssr_clksel[] = {
{ .parent = NULL }
};
-static struct clk ssi_ssr_fck = {
+static struct clk ssi_ssr_fck_3430es1 = {
.name = "ssi_ssr_fck",
.ops = &clkops_omap2_dflt,
.init = &omap2_init_clksel_parent,
@@ -1581,10 +1581,31 @@ static struct clk ssi_ssr_fck = {
.recalc = &omap2_clksel_recalc,
};
-static struct clk ssi_sst_fck = {
+static struct clk ssi_ssr_fck_3430es2 = {
+ .name = "ssi_ssr_fck",
+ .ops = &clkops_omap3430es2_ssi_wait,
+ .init = &omap2_init_clksel_parent,
+ .enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_FCLKEN1),
+ .enable_bit = OMAP3430_EN_SSI_SHIFT,
+ .clksel_reg = OMAP_CM_REGADDR(CORE_MOD, CM_CLKSEL),
+ .clksel_mask = OMAP3430_CLKSEL_SSI_MASK,
+ .clksel = ssi_ssr_clksel,
+ .clkdm_name = "core_l4_clkdm",
+ .recalc = &omap2_clksel_recalc,
+};
+
+static struct clk ssi_sst_fck_3430es1 = {
.name = "ssi_sst_fck",
.ops = &clkops_null,
- .parent = &ssi_ssr_fck,
+ .parent = &ssi_ssr_fck_3430es1,
+ .fixed_div = 2,
+ .recalc = &omap2_fixed_divisor_recalc,
+};
+
+static struct clk ssi_sst_fck_3430es2 = {
+ .name = "ssi_sst_fck",
+ .ops = &clkops_null,
+ .parent = &ssi_ssr_fck_3430es2,
.fixed_div = 2,
.recalc = &omap2_fixed_divisor_recalc,
};
@@ -1606,9 +1627,19 @@ static struct clk core_l3_ick = {
.recalc = &followparent_recalc,
};
-static struct clk hsotgusb_ick = {
+static struct clk hsotgusb_ick_3430es1 = {
.name = "hsotgusb_ick",
- .ops = &clkops_omap2_dflt_wait,
+ .ops = &clkops_omap2_dflt,
+ .parent = &core_l3_ick,
+ .enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_ICLKEN1),
+ .enable_bit = OMAP3430_EN_HSOTGUSB_SHIFT,
+ .clkdm_name = "core_l3_clkdm",
+ .recalc = &followparent_recalc,
+};
+
+static struct clk hsotgusb_ick_3430es2 = {
+ .name = "hsotgusb_ick",
+ .ops = &clkops_omap3430es2_hsotgusb_wait,
.parent = &core_l3_ick,
.enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_ICLKEN1),
.enable_bit = OMAP3430_EN_HSOTGUSB_SHIFT,
@@ -1947,7 +1978,7 @@ static struct clk ssi_l4_ick = {
.recalc = &followparent_recalc,
};
-static struct clk ssi_ick = {
+static struct clk ssi_ick_3430es1 = {
.name = "ssi_ick",
.ops = &clkops_omap2_dflt,
.parent = &ssi_l4_ick,
@@ -1957,6 +1988,16 @@ static struct clk ssi_ick = {
.recalc = &followparent_recalc,
};
+static struct clk ssi_ick_3430es2 = {
+ .name = "ssi_ick",
+ .ops = &clkops_omap3430es2_ssi_wait,
+ .parent = &ssi_l4_ick,
+ .enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_ICLKEN1),
+ .enable_bit = OMAP3430_EN_SSI_SHIFT,
+ .clkdm_name = "core_l4_clkdm",
+ .recalc = &followparent_recalc,
+};
+
/* REVISIT: Technically the TRM claims that this is CORE_CLK based,
* but l4_ick makes more sense to me */
@@ -2024,7 +2065,7 @@ static struct clk des1_ick = {
};
/* DSS */
-static struct clk dss1_alwon_fck = {
+static struct clk dss1_alwon_fck_3430es1 = {
.name = "dss1_alwon_fck",
.ops = &clkops_omap2_dflt,
.parent = &dpll4_m4x2_ck,
@@ -2034,6 +2075,16 @@ static struct clk dss1_alwon_fck = {
.recalc = &followparent_recalc,
};
+static struct clk dss1_alwon_fck_3430es2 = {
+ .name = "dss1_alwon_fck",
+ .ops = &clkops_omap3430es2_dss_usbhost_wait,
+ .parent = &dpll4_m4x2_ck,
+ .enable_reg = OMAP_CM_REGADDR(OMAP3430_DSS_MOD, CM_FCLKEN),
+ .enable_bit = OMAP3430_EN_DSS1_SHIFT,
+ .clkdm_name = "dss_clkdm",
+ .recalc = &followparent_recalc,
+};
+
static struct clk dss_tv_fck = {
.name = "dss_tv_fck",
.ops = &clkops_omap2_dflt,
@@ -2067,7 +2118,7 @@ static struct clk dss2_alwon_fck = {
.recalc = &followparent_recalc,
};
-static struct clk dss_ick = {
+static struct clk dss_ick_3430es1 = {
/* Handles both L3 and L4 clocks */
.name = "dss_ick",
.ops = &clkops_omap2_dflt,
@@ -2079,6 +2130,18 @@ static struct clk dss_ick = {
.recalc = &followparent_recalc,
};
+static struct clk dss_ick_3430es2 = {
+ /* Handles both L3 and L4 clocks */
+ .name = "dss_ick",
+ .ops = &clkops_omap3430es2_dss_usbhost_wait,
+ .parent = &l4_ick,
+ .init = &omap2_init_clk_clkdm,
+ .enable_reg = OMAP_CM_REGADDR(OMAP3430_DSS_MOD, CM_ICLKEN),
+ .enable_bit = OMAP3430_CM_ICLKEN_DSS_EN_DSS_SHIFT,
+ .clkdm_name = "dss_clkdm",
+ .recalc = &followparent_recalc,
+};
+
/* CAM */
static struct clk cam_mclk = {
@@ -2118,7 +2181,7 @@ static struct clk csi2_96m_fck = {
static struct clk usbhost_120m_fck = {
.name = "usbhost_120m_fck",
- .ops = &clkops_omap2_dflt_wait,
+ .ops = &clkops_omap2_dflt,
.parent = &dpll5_m2_ck,
.init = &omap2_init_clk_clkdm,
.enable_reg = OMAP_CM_REGADDR(OMAP3430ES2_USBHOST_MOD, CM_FCLKEN),
@@ -2129,7 +2192,7 @@ static struct clk usbhost_120m_fck = {
static struct clk usbhost_48m_fck = {
.name = "usbhost_48m_fck",
- .ops = &clkops_omap2_dflt_wait,
+ .ops = &clkops_omap3430es2_dss_usbhost_wait,
.parent = &omap_48m_fck,
.init = &omap2_init_clk_clkdm,
.enable_reg = OMAP_CM_REGADDR(OMAP3430ES2_USBHOST_MOD, CM_FCLKEN),
@@ -2141,7 +2204,7 @@ static struct clk usbhost_48m_fck = {
static struct clk usbhost_ick = {
/* Handles both L3 and L4 clocks */
.name = "usbhost_ick",
- .ops = &clkops_omap2_dflt_wait,
+ .ops = &clkops_omap3430es2_dss_usbhost_wait,
.parent = &l4_ick,
.init = &omap2_init_clk_clkdm,
.enable_reg = OMAP_CM_REGADDR(OMAP3430ES2_USBHOST_MOD, CM_ICLKEN),
diff --git a/arch/arm/mach-omap2/cm.h b/arch/arm/mach-omap2/cm.h
index 1d3c93bf86d..f3c91a1ca39 100644
--- a/arch/arm/mach-omap2/cm.h
+++ b/arch/arm/mach-omap2/cm.h
@@ -29,9 +29,9 @@
* These registers appear once per CM module.
*/
-#define OMAP3430_CM_REVISION OMAP_CM_REGADDR(OCP_MOD, 0x0000)
-#define OMAP3430_CM_SYSCONFIG OMAP_CM_REGADDR(OCP_MOD, 0x0010)
-#define OMAP3430_CM_POLCTRL OMAP_CM_REGADDR(OCP_MOD, 0x009c)
+#define OMAP3430_CM_REVISION OMAP34XX_CM_REGADDR(OCP_MOD, 0x0000)
+#define OMAP3430_CM_SYSCONFIG OMAP34XX_CM_REGADDR(OCP_MOD, 0x0010)
+#define OMAP3430_CM_POLCTRL OMAP34XX_CM_REGADDR(OCP_MOD, 0x009c)
#define OMAP3_CM_CLKOUT_CTRL_OFFSET 0x0070
#define OMAP3430_CM_CLKOUT_CTRL OMAP_CM_REGADDR(OMAP3430_CCR_MOD, 0x0070)
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index 3a86b0f6603..e9b9bcb19b4 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -276,14 +276,15 @@ static int __init _omap2_init_reprogram_sdrc(void)
return v;
}
-void __init omap2_init_common_hw(struct omap_sdrc_params *sp)
+void __init omap2_init_common_hw(struct omap_sdrc_params *sdrc_cs0,
+ struct omap_sdrc_params *sdrc_cs1)
{
omap2_mux_init();
#ifndef CONFIG_ARCH_OMAP4 /* FIXME: Remove this once the clkdev is ready */
pwrdm_init(powerdomains_omap);
clkdm_init(clockdomains_omap, clkdm_pwrdm_autodeps);
omap2_clk_init();
- omap2_sdrc_init(sp);
+ omap2_sdrc_init(sdrc_cs0, sdrc_cs1);
_omap2_init_reprogram_sdrc();
#endif
gpmc_init();
diff --git a/arch/arm/mach-omap2/mcbsp.c b/arch/arm/mach-omap2/mcbsp.c
index a5c0f0435cd..0447d26d454 100644
--- a/arch/arm/mach-omap2/mcbsp.c
+++ b/arch/arm/mach-omap2/mcbsp.c
@@ -19,7 +19,6 @@
#include <mach/irqs.h>
#include <mach/dma.h>
-#include <mach/irqs.h>
#include <mach/mux.h>
#include <mach/cpu.h>
#include <mach/mcbsp.h>
@@ -129,6 +128,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP1_IRQ_RX,
.tx_irq = INT_24XX_MCBSP1_IRQ_TX,
.ops = &omap2_mcbsp_ops,
+ .buffer_size = 0x6F,
},
{
.phys_base = OMAP34XX_MCBSP2_BASE,
@@ -137,6 +137,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP2_IRQ_RX,
.tx_irq = INT_24XX_MCBSP2_IRQ_TX,
.ops = &omap2_mcbsp_ops,
+ .buffer_size = 0x3FF,
},
{
.phys_base = OMAP34XX_MCBSP3_BASE,
@@ -145,6 +146,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP3_IRQ_RX,
.tx_irq = INT_24XX_MCBSP3_IRQ_TX,
.ops = &omap2_mcbsp_ops,
+ .buffer_size = 0x6F,
},
{
.phys_base = OMAP34XX_MCBSP4_BASE,
@@ -153,6 +155,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP4_IRQ_RX,
.tx_irq = INT_24XX_MCBSP4_IRQ_TX,
.ops = &omap2_mcbsp_ops,
+ .buffer_size = 0x6F,
},
{
.phys_base = OMAP34XX_MCBSP5_BASE,
@@ -161,6 +164,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP5_IRQ_RX,
.tx_irq = INT_24XX_MCBSP5_IRQ_TX,
.ops = &omap2_mcbsp_ops,
+ .buffer_size = 0x6F,
},
};
#define OMAP34XX_MCBSP_PDATA_SZ ARRAY_SIZE(omap34xx_mcbsp_pdata)
diff --git a/arch/arm/mach-omap2/mmc-twl4030.c b/arch/arm/mach-omap2/mmc-twl4030.c
index 1541fd4c8d0..3c04c2f1b23 100644
--- a/arch/arm/mach-omap2/mmc-twl4030.c
+++ b/arch/arm/mach-omap2/mmc-twl4030.c
@@ -119,6 +119,7 @@ static int twl_mmc_late_init(struct device *dev)
if (i != 0)
break;
ret = PTR_ERR(reg);
+ hsmmc[i].vcc = NULL;
goto err;
}
hsmmc[i].vcc = reg;
@@ -165,8 +166,13 @@ done:
static void twl_mmc_cleanup(struct device *dev)
{
struct omap_mmc_platform_data *mmc = dev->platform_data;
+ int i;
gpio_free(mmc->slots[0].switch_pin);
+ for(i = 0; i < ARRAY_SIZE(hsmmc); i++) {
+ regulator_put(hsmmc[i].vcc);
+ regulator_put(hsmmc[i].vcc_aux);
+ }
}
#ifdef CONFIG_PM
diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c
index 026c4fc883a..43d6b92b65f 100644
--- a/arch/arm/mach-omap2/mux.c
+++ b/arch/arm/mach-omap2/mux.c
@@ -486,6 +486,12 @@ MUX_CFG_34XX("H19_34XX_GPIO164_OUT", 0x19c,
OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_OUTPUT)
MUX_CFG_34XX("J25_34XX_GPIO170", 0x1c6,
OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT)
+
+/* OMAP3 SDRC CKE signals to SDR/DDR ram chips */
+MUX_CFG_34XX("H16_34XX_SDRC_CKE0", 0x262,
+ OMAP34XX_MUX_MODE0 | OMAP34XX_PIN_OUTPUT)
+MUX_CFG_34XX("H17_34XX_SDRC_CKE1", 0x264,
+ OMAP34XX_MUX_MODE0 | OMAP34XX_PIN_OUTPUT)
};
#define OMAP34XX_PINS_SZ ARRAY_SIZE(omap34xx_pins)
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index f7b3baf7667..21201cd4117 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -11,9 +11,6 @@
#ifndef __ARCH_ARM_MACH_OMAP2_PM_H
#define __ARCH_ARM_MACH_OMAP2_PM_H
-extern int omap2_pm_init(void);
-extern int omap3_pm_init(void);
-
#ifdef CONFIG_PM_DEBUG
extern void omap2_pm_dump(int mode, int resume, unsigned int us);
extern int omap2_pm_debug;
diff --git a/arch/arm/mach-omap2/pm24xx.c b/arch/arm/mach-omap2/pm24xx.c
index db1025562fb..528dbdc26e2 100644
--- a/arch/arm/mach-omap2/pm24xx.c
+++ b/arch/arm/mach-omap2/pm24xx.c
@@ -470,7 +470,7 @@ static void __init prcm_setup_regs(void)
WKUP_MOD, PM_WKEN);
}
-int __init omap2_pm_init(void)
+static int __init omap2_pm_init(void)
{
u32 l;
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index 841d4c5ed8b..488d595d8e4 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -39,7 +39,9 @@
struct power_state {
struct powerdomain *pwrdm;
u32 next_state;
+#ifdef CONFIG_SUSPEND
u32 saved_state;
+#endif
struct list_head node;
};
@@ -293,6 +295,9 @@ out:
local_irq_enable();
}
+#ifdef CONFIG_SUSPEND
+static suspend_state_t suspend_state;
+
static int omap3_pm_prepare(void)
{
disable_hlt();
@@ -321,7 +326,6 @@ static int omap3_pm_suspend(void)
restore:
/* Restore next_pwrsts */
list_for_each_entry(pwrst, &pwrst_list, node) {
- set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
state = pwrdm_read_prev_pwrst(pwrst->pwrdm);
if (state > pwrst->next_state) {
printk(KERN_INFO "Powerdomain (%s) didn't enter "
@@ -329,6 +333,7 @@ restore:
pwrst->pwrdm->name, pwrst->next_state);
ret = -1;
}
+ set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
}
if (ret)
printk(KERN_ERR "Could not enter target state in pm_suspend\n");
@@ -339,11 +344,11 @@ restore:
return ret;
}
-static int omap3_pm_enter(suspend_state_t state)
+static int omap3_pm_enter(suspend_state_t unused)
{
int ret = 0;
- switch (state) {
+ switch (suspend_state) {
case PM_SUSPEND_STANDBY:
case PM_SUSPEND_MEM:
ret = omap3_pm_suspend();
@@ -360,12 +365,30 @@ static void omap3_pm_finish(void)
enable_hlt();
}
+/* Hooks to enable / disable UART interrupts during suspend */
+static int omap3_pm_begin(suspend_state_t state)
+{
+ suspend_state = state;
+ omap_uart_enable_irqs(0);
+ return 0;
+}
+
+static void omap3_pm_end(void)
+{
+ suspend_state = PM_SUSPEND_ON;
+ omap_uart_enable_irqs(1);
+ return;
+}
+
static struct platform_suspend_ops omap_pm_ops = {
+ .begin = omap3_pm_begin,
+ .end = omap3_pm_end,
.prepare = omap3_pm_prepare,
.enter = omap3_pm_enter,
.finish = omap3_pm_finish,
.valid = suspend_valid_only_mem,
};
+#endif /* CONFIG_SUSPEND */
/**
@@ -613,6 +636,24 @@ static void __init prcm_setup_regs(void)
/* Clear any pending PRCM interrupts */
prm_write_mod_reg(0, OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+ /* Don't attach IVA interrupts */
+ prm_write_mod_reg(0, WKUP_MOD, OMAP3430_PM_IVAGRPSEL);
+ prm_write_mod_reg(0, CORE_MOD, OMAP3430_PM_IVAGRPSEL1);
+ prm_write_mod_reg(0, CORE_MOD, OMAP3430ES2_PM_IVAGRPSEL3);
+ prm_write_mod_reg(0, OMAP3430_PER_MOD, OMAP3430_PM_IVAGRPSEL);
+
+ /* Clear any pending 'reset' flags */
+ prm_write_mod_reg(0xffffffff, MPU_MOD, RM_RSTST);
+ prm_write_mod_reg(0xffffffff, CORE_MOD, RM_RSTST);
+ prm_write_mod_reg(0xffffffff, OMAP3430_PER_MOD, RM_RSTST);
+ prm_write_mod_reg(0xffffffff, OMAP3430_EMU_MOD, RM_RSTST);
+ prm_write_mod_reg(0xffffffff, OMAP3430_NEON_MOD, RM_RSTST);
+ prm_write_mod_reg(0xffffffff, OMAP3430_DSS_MOD, RM_RSTST);
+ prm_write_mod_reg(0xffffffff, OMAP3430ES2_USBHOST_MOD, RM_RSTST);
+
+ /* Clear any pending PRCM interrupts */
+ prm_write_mod_reg(0, OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+
omap3_iva_idle();
omap3_d2d_idle();
}
@@ -652,7 +693,7 @@ static int __init clkdms_setup(struct clockdomain *clkdm)
return 0;
}
-int __init omap3_pm_init(void)
+static int __init omap3_pm_init(void)
{
struct power_state *pwrst, *tmp;
int ret;
@@ -692,7 +733,9 @@ int __init omap3_pm_init(void)
_omap_sram_idle = omap_sram_push(omap34xx_cpu_suspend,
omap34xx_cpu_suspend_sz);
+#ifdef CONFIG_SUSPEND
suspend_set_ops(&omap_pm_ops);
+#endif /* CONFIG_SUSPEND */
pm_idle = omap3_pm_idle;
diff --git a/arch/arm/mach-omap2/prcm.c b/arch/arm/mach-omap2/prcm.c
index f945156d558..ced555a4cd1 100644
--- a/arch/arm/mach-omap2/prcm.c
+++ b/arch/arm/mach-omap2/prcm.c
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/delay.h>
#include <mach/common.h>
#include <mach/prcm.h>
@@ -28,6 +29,8 @@
static void __iomem *prm_base;
static void __iomem *cm_base;
+#define MAX_MODULE_ENABLE_WAIT 100000
+
u32 omap_prcm_get_reset_sources(void)
{
/* XXX This presumably needs modification for 34XX */
@@ -120,6 +123,46 @@ u32 cm_rmw_mod_reg_bits(u32 mask, u32 bits, s16 module, s16 idx)
}
EXPORT_SYMBOL(cm_rmw_mod_reg_bits);
+/**
+ * omap2_cm_wait_idlest - wait for IDLEST bit to indicate module readiness
+ * @reg: physical address of module IDLEST register
+ * @mask: value to mask against to determine if the module is active
+ * @name: name of the clock (for printk)
+ *
+ * Returns 1 if the module indicated readiness in time, or 0 if it
+ * failed to enable in roughly MAX_MODULE_ENABLE_WAIT microseconds.
+ */
+int omap2_cm_wait_idlest(void __iomem *reg, u32 mask, const char *name)
+{
+ int i = 0;
+ int ena = 0;
+
+ /*
+ * 24xx uses 0 to indicate not ready, and 1 to indicate ready.
+ * 34xx reverses this, just to keep us on our toes
+ */
+ if (cpu_is_omap24xx())
+ ena = mask;
+ else if (cpu_is_omap34xx())
+ ena = 0;
+ else
+ BUG();
+
+ /* Wait for lock */
+ while (((__raw_readl(reg) & mask) != ena) &&
+ (i++ < MAX_MODULE_ENABLE_WAIT))
+ udelay(1);
+
+ if (i < MAX_MODULE_ENABLE_WAIT)
+ pr_debug("cm: Module associated with clock %s ready after %d "
+ "loops\n", name, i);
+ else
+ pr_err("cm: Module associated with clock %s didn't enable in "
+ "%d tries\n", name, MAX_MODULE_ENABLE_WAIT);
+
+ return (i < MAX_MODULE_ENABLE_WAIT) ? 1 : 0;
+};
+
void __init omap2_set_globals_prcm(struct omap_globals *omap2_globals)
{
prm_base = omap2_globals->prm;
diff --git a/arch/arm/mach-omap2/sdrc.c b/arch/arm/mach-omap2/sdrc.c
index 2045441e838..9e3bd4fa781 100644
--- a/arch/arm/mach-omap2/sdrc.c
+++ b/arch/arm/mach-omap2/sdrc.c
@@ -32,7 +32,7 @@
#include <mach/sdrc.h>
#include "sdrc.h"
-static struct omap_sdrc_params *sdrc_init_params;
+static struct omap_sdrc_params *sdrc_init_params_cs0, *sdrc_init_params_cs1;
void __iomem *omap2_sdrc_base;
void __iomem *omap2_sms_base;
@@ -45,33 +45,49 @@ void __iomem *omap2_sms_base;
/**
* omap2_sdrc_get_params - return SDRC register values for a given clock rate
* @r: SDRC clock rate (in Hz)
+ * @sdrc_cs0: chip select 0 ram timings **
+ * @sdrc_cs1: chip select 1 ram timings **
*
* Return pre-calculated values for the SDRC_ACTIM_CTRLA,
- * SDRC_ACTIM_CTRLB, SDRC_RFR_CTRL, and SDRC_MR registers, for a given
- * SDRC clock rate 'r'. These parameters control various timing
- * delays in the SDRAM controller that are expressed in terms of the
- * number of SDRC clock cycles to wait; hence the clock rate
- * dependency. Note that sdrc_init_params must be sorted rate
- * descending. Also assumes that both chip-selects use the same
- * timing parameters. Returns a struct omap_sdrc_params * upon
- * success, or NULL upon failure.
+ * SDRC_ACTIM_CTRLB, SDRC_RFR_CTRL and SDRC_MR registers in sdrc_cs[01]
+ * structs,for a given SDRC clock rate 'r'.
+ * These parameters control various timing delays in the SDRAM controller
+ * that are expressed in terms of the number of SDRC clock cycles to
+ * wait; hence the clock rate dependency.
+ *
+ * Supports 2 different timing parameters for both chip selects.
+ *
+ * Note 1: the sdrc_init_params_cs[01] must be sorted rate descending.
+ * Note 2: If sdrc_init_params_cs_1 is not NULL it must be of same size
+ * as sdrc_init_params_cs_0.
+ *
+ * Fills in the struct omap_sdrc_params * for each chip select.
+ * Returns 0 upon success or -1 upon failure.
*/
-struct omap_sdrc_params *omap2_sdrc_get_params(unsigned long r)
+int omap2_sdrc_get_params(unsigned long r,
+ struct omap_sdrc_params **sdrc_cs0,
+ struct omap_sdrc_params **sdrc_cs1)
{
- struct omap_sdrc_params *sp;
+ struct omap_sdrc_params *sp0, *sp1;
- if (!sdrc_init_params)
- return NULL;
+ if (!sdrc_init_params_cs0)
+ return -1;
- sp = sdrc_init_params;
+ sp0 = sdrc_init_params_cs0;
+ sp1 = sdrc_init_params_cs1;
- while (sp->rate && sp->rate != r)
- sp++;
+ while (sp0->rate && sp0->rate != r) {
+ sp0++;
+ if (sdrc_init_params_cs1)
+ sp1++;
+ }
- if (!sp->rate)
- return NULL;
+ if (!sp0->rate)
+ return -1;
- return sp;
+ *sdrc_cs0 = sp0;
+ *sdrc_cs1 = sp1;
+ return 0;
}
@@ -83,13 +99,15 @@ void __init omap2_set_globals_sdrc(struct omap_globals *omap2_globals)
/**
* omap2_sdrc_init - initialize SMS, SDRC devices on boot
- * @sp: pointer to a null-terminated list of struct omap_sdrc_params
+ * @sdrc_cs[01]: pointers to a null-terminated list of struct omap_sdrc_params
+ * Support for 2 chip selects timings
*
* Turn on smart idle modes for SDRAM scheduler and controller.
* Program a known-good configuration for the SDRC to deal with buggy
* bootloaders.
*/
-void __init omap2_sdrc_init(struct omap_sdrc_params *sp)
+void __init omap2_sdrc_init(struct omap_sdrc_params *sdrc_cs0,
+ struct omap_sdrc_params *sdrc_cs1)
{
u32 l;
@@ -103,11 +121,15 @@ void __init omap2_sdrc_init(struct omap_sdrc_params *sp)
l |= (0x2 << 3);
sdrc_write_reg(l, SDRC_SYSCONFIG);
- sdrc_init_params = sp;
+ sdrc_init_params_cs0 = sdrc_cs0;
+ sdrc_init_params_cs1 = sdrc_cs1;
/* XXX Enable SRFRONIDLEREQ here also? */
+ /*
+ * PWDENA should not be set due to 34xx erratum 1.150 - PWDENA
+ * can cause random memory corruption
+ */
l = (1 << SDRC_POWER_EXTCLKDIS_SHIFT) |
- (1 << SDRC_POWER_PWDENA_SHIFT) |
(1 << SDRC_POWER_PAGEPOLICY_SHIFT);
sdrc_write_reg(l, SDRC_POWER);
}
diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
index b094c15bfe4..a7421a50410 100644
--- a/arch/arm/mach-omap2/serial.c
+++ b/arch/arm/mach-omap2/serial.c
@@ -54,6 +54,7 @@ struct omap_uart_state {
struct plat_serial8250_port *p;
struct list_head node;
+ struct platform_device pdev;
#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM)
int context_valid;
@@ -68,10 +69,9 @@ struct omap_uart_state {
#endif
};
-static struct omap_uart_state omap_uart[OMAP_MAX_NR_PORTS];
static LIST_HEAD(uart_list);
-static struct plat_serial8250_port serial_platform_data[] = {
+static struct plat_serial8250_port serial_platform_data0[] = {
{
.membase = IO_ADDRESS(OMAP_UART1_BASE),
.mapbase = OMAP_UART1_BASE,
@@ -81,6 +81,12 @@ static struct plat_serial8250_port serial_platform_data[] = {
.regshift = 2,
.uartclk = OMAP24XX_BASE_BAUD * 16,
}, {
+ .flags = 0
+ }
+};
+
+static struct plat_serial8250_port serial_platform_data1[] = {
+ {
.membase = IO_ADDRESS(OMAP_UART2_BASE),
.mapbase = OMAP_UART2_BASE,
.irq = 73,
@@ -89,6 +95,12 @@ static struct plat_serial8250_port serial_platform_data[] = {
.regshift = 2,
.uartclk = OMAP24XX_BASE_BAUD * 16,
}, {
+ .flags = 0
+ }
+};
+
+static struct plat_serial8250_port serial_platform_data2[] = {
+ {
.membase = IO_ADDRESS(OMAP_UART3_BASE),
.mapbase = OMAP_UART3_BASE,
.irq = 74,
@@ -217,6 +229,40 @@ static inline void omap_uart_disable_clocks(struct omap_uart_state *uart)
clk_disable(uart->fck);
}
+static void omap_uart_enable_wakeup(struct omap_uart_state *uart)
+{
+ /* Set wake-enable bit */
+ if (uart->wk_en && uart->wk_mask) {
+ u32 v = __raw_readl(uart->wk_en);
+ v |= uart->wk_mask;
+ __raw_writel(v, uart->wk_en);
+ }
+
+ /* Ensure IOPAD wake-enables are set */
+ if (cpu_is_omap34xx() && uart->padconf) {
+ u16 v = omap_ctrl_readw(uart->padconf);
+ v |= OMAP3_PADCONF_WAKEUPENABLE0;
+ omap_ctrl_writew(v, uart->padconf);
+ }
+}
+
+static void omap_uart_disable_wakeup(struct omap_uart_state *uart)
+{
+ /* Clear wake-enable bit */
+ if (uart->wk_en && uart->wk_mask) {
+ u32 v = __raw_readl(uart->wk_en);
+ v &= ~uart->wk_mask;
+ __raw_writel(v, uart->wk_en);
+ }
+
+ /* Ensure IOPAD wake-enables are cleared */
+ if (cpu_is_omap34xx() && uart->padconf) {
+ u16 v = omap_ctrl_readw(uart->padconf);
+ v &= ~OMAP3_PADCONF_WAKEUPENABLE0;
+ omap_ctrl_writew(v, uart->padconf);
+ }
+}
+
static void omap_uart_smart_idle_enable(struct omap_uart_state *uart,
int enable)
{
@@ -246,6 +292,11 @@ static void omap_uart_block_sleep(struct omap_uart_state *uart)
static void omap_uart_allow_sleep(struct omap_uart_state *uart)
{
+ if (device_may_wakeup(&uart->pdev.dev))
+ omap_uart_enable_wakeup(uart);
+ else
+ omap_uart_disable_wakeup(uart);
+
if (!uart->clocked)
return;
@@ -292,7 +343,6 @@ void omap_uart_resume_idle(int num)
/* Check for normal UART wakeup */
if (__raw_readl(uart->wk_st) & uart->wk_mask)
omap_uart_block_sleep(uart);
-
return;
}
}
@@ -346,16 +396,13 @@ static irqreturn_t omap_uart_interrupt(int irq, void *dev_id)
return IRQ_NONE;
}
-static u32 sleep_timeout = DEFAULT_TIMEOUT;
-
static void omap_uart_idle_init(struct omap_uart_state *uart)
{
- u32 v;
struct plat_serial8250_port *p = uart->p;
int ret;
uart->can_sleep = 0;
- uart->timeout = sleep_timeout;
+ uart->timeout = DEFAULT_TIMEOUT;
setup_timer(&uart->timer, omap_uart_idle_timer,
(unsigned long) uart);
mod_timer(&uart->timer, jiffies + uart->timeout);
@@ -413,76 +460,101 @@ static void omap_uart_idle_init(struct omap_uart_state *uart)
uart->padconf = 0;
}
- /* Set wake-enable bit */
- if (uart->wk_en && uart->wk_mask) {
- v = __raw_readl(uart->wk_en);
- v |= uart->wk_mask;
- __raw_writel(v, uart->wk_en);
- }
-
- /* Ensure IOPAD wake-enables are set */
- if (cpu_is_omap34xx() && uart->padconf) {
- u16 v;
-
- v = omap_ctrl_readw(uart->padconf);
- v |= OMAP3_PADCONF_WAKEUPENABLE0;
- omap_ctrl_writew(v, uart->padconf);
- }
-
p->flags |= UPF_SHARE_IRQ;
ret = request_irq(p->irq, omap_uart_interrupt, IRQF_SHARED,
"serial idle", (void *)uart);
WARN_ON(ret);
}
-static ssize_t sleep_timeout_show(struct kobject *kobj,
- struct kobj_attribute *attr,
+void omap_uart_enable_irqs(int enable)
+{
+ int ret;
+ struct omap_uart_state *uart;
+
+ list_for_each_entry(uart, &uart_list, node) {
+ if (enable)
+ ret = request_irq(uart->p->irq, omap_uart_interrupt,
+ IRQF_SHARED, "serial idle", (void *)uart);
+ else
+ free_irq(uart->p->irq, (void *)uart);
+ }
+}
+
+static ssize_t sleep_timeout_show(struct device *dev,
+ struct device_attribute *attr,
char *buf)
{
- return sprintf(buf, "%u\n", sleep_timeout / HZ);
+ struct platform_device *pdev = container_of(dev,
+ struct platform_device, dev);
+ struct omap_uart_state *uart = container_of(pdev,
+ struct omap_uart_state, pdev);
+
+ return sprintf(buf, "%u\n", uart->timeout / HZ);
}
-static ssize_t sleep_timeout_store(struct kobject *kobj,
- struct kobj_attribute *attr,
+static ssize_t sleep_timeout_store(struct device *dev,
+ struct device_attribute *attr,
const char *buf, size_t n)
{
- struct omap_uart_state *uart;
+ struct platform_device *pdev = container_of(dev,
+ struct platform_device, dev);
+ struct omap_uart_state *uart = container_of(pdev,
+ struct omap_uart_state, pdev);
unsigned int value;
if (sscanf(buf, "%u", &value) != 1) {
printk(KERN_ERR "sleep_timeout_store: Invalid value\n");
return -EINVAL;
}
- sleep_timeout = value * HZ;
- list_for_each_entry(uart, &uart_list, node) {
- uart->timeout = sleep_timeout;
- if (uart->timeout)
- mod_timer(&uart->timer, jiffies + uart->timeout);
- else
- /* A zero value means disable timeout feature */
- omap_uart_block_sleep(uart);
- }
+
+ uart->timeout = value * HZ;
+ if (uart->timeout)
+ mod_timer(&uart->timer, jiffies + uart->timeout);
+ else
+ /* A zero value means disable timeout feature */
+ omap_uart_block_sleep(uart);
+
return n;
}
-static struct kobj_attribute sleep_timeout_attr =
- __ATTR(sleep_timeout, 0644, sleep_timeout_show, sleep_timeout_store);
-
+DEVICE_ATTR(sleep_timeout, 0644, sleep_timeout_show, sleep_timeout_store);
+#define DEV_CREATE_FILE(dev, attr) WARN_ON(device_create_file(dev, attr))
#else
static inline void omap_uart_idle_init(struct omap_uart_state *uart) {}
+#define DEV_CREATE_FILE(dev, attr)
#endif /* CONFIG_PM */
-static struct platform_device serial_device = {
- .name = "serial8250",
- .id = PLAT8250_DEV_PLATFORM,
- .dev = {
- .platform_data = serial_platform_data,
+static struct omap_uart_state omap_uart[OMAP_MAX_NR_PORTS] = {
+ {
+ .pdev = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = serial_platform_data0,
+ },
+ },
+ }, {
+ .pdev = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM1,
+ .dev = {
+ .platform_data = serial_platform_data1,
+ },
+ },
+ }, {
+ .pdev = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM2,
+ .dev = {
+ .platform_data = serial_platform_data2,
+ },
+ },
},
};
void __init omap_serial_init(void)
{
- int i, err;
+ int i;
const struct omap_uart_config *info;
char name[16];
@@ -496,14 +568,12 @@ void __init omap_serial_init(void)
if (info == NULL)
return;
- if (cpu_is_omap44xx()) {
- for (i = 0; i < OMAP_MAX_NR_PORTS; i++)
- serial_platform_data[i].irq += 32;
- }
for (i = 0; i < OMAP_MAX_NR_PORTS; i++) {
- struct plat_serial8250_port *p = serial_platform_data + i;
struct omap_uart_state *uart = &omap_uart[i];
+ struct platform_device *pdev = &uart->pdev;
+ struct device *dev = &pdev->dev;
+ struct plat_serial8250_port *p = dev->platform_data;
if (!(info->enabled_uarts & (1 << i))) {
p->membase = NULL;
@@ -531,20 +601,21 @@ void __init omap_serial_init(void)
uart->num = i;
p->private_data = uart;
uart->p = p;
- list_add(&uart->node, &uart_list);
+ list_add_tail(&uart->node, &uart_list);
+
+ if (cpu_is_omap44xx())
+ p->irq += 32;
omap_uart_enable_clocks(uart);
omap_uart_reset(uart);
omap_uart_idle_init(uart);
- }
-
- err = platform_device_register(&serial_device);
-
-#ifdef CONFIG_PM
- if (!err)
- err = sysfs_create_file(&serial_device.dev.kobj,
- &sleep_timeout_attr.attr);
-#endif
+ if (WARN_ON(platform_device_register(pdev)))
+ continue;
+ if ((cpu_is_omap34xx() && uart->padconf) ||
+ (uart->wk_en && uart->wk_mask)) {
+ device_init_wakeup(dev, true);
+ DEV_CREATE_FILE(dev, &dev_attr_sleep_timeout);
+ }
+ }
}
-
diff --git a/arch/arm/mach-omap2/sram34xx.S b/arch/arm/mach-omap2/sram34xx.S
index f41f8d96ddb..82aa4a3d160 100644
--- a/arch/arm/mach-omap2/sram34xx.S
+++ b/arch/arm/mach-omap2/sram34xx.S
@@ -36,7 +36,7 @@
.text
-/* r4 parameters */
+/* r1 parameters */
#define SDRC_NO_UNLOCK_DLL 0x0
#define SDRC_UNLOCK_DLL 0x1
@@ -58,7 +58,6 @@
/* SDRC_POWER bit settings */
#define SRFRONIDLEREQ_MASK 0x40
-#define PWDENA_MASK 0x4
/* CM_IDLEST1_CORE bit settings */
#define ST_SDRC_MASK 0x2
@@ -71,41 +70,72 @@
/*
* omap3_sram_configure_core_dpll - change DPLL3 M2 divider
- * r0 = new SDRC_RFR_CTRL register contents
- * r1 = new SDRC_ACTIM_CTRLA register contents
- * r2 = new SDRC_ACTIM_CTRLB register contents
- * r3 = new M2 divider setting (only 1 and 2 supported right now)
- * r4 = unlock SDRC DLL? (1 = yes, 0 = no). Only unlock DLL for
+ *
+ * Params passed in registers:
+ * r0 = new M2 divider setting (only 1 and 2 supported right now)
+ * r1 = unlock SDRC DLL? (1 = yes, 0 = no). Only unlock DLL for
* SDRC rates < 83MHz
- * r5 = number of MPU cycles to wait for SDRC to stabilize after
+ * r2 = number of MPU cycles to wait for SDRC to stabilize after
* reprogramming the SDRC when switching to a slower MPU speed
- * r6 = new SDRC_MR_0 register value
- * r7 = increasing SDRC rate? (1 = yes, 0 = no)
+ * r3 = increasing SDRC rate? (1 = yes, 0 = no)
+ *
+ * Params passed via the stack. The needed params will be copied in SRAM
+ * before use by the code in SRAM (SDRAM is not accessible during SDRC
+ * reconfiguration):
+ * new SDRC_RFR_CTRL_0 register contents
+ * new SDRC_ACTIM_CTRL_A_0 register contents
+ * new SDRC_ACTIM_CTRL_B_0 register contents
+ * new SDRC_MR_0 register value
+ * new SDRC_RFR_CTRL_1 register contents
+ * new SDRC_ACTIM_CTRL_A_1 register contents
+ * new SDRC_ACTIM_CTRL_B_1 register contents
+ * new SDRC_MR_1 register value
*
+ * If the param SDRC_RFR_CTRL_1 is 0, the parameters
+ * are not programmed into the SDRC CS1 registers
*/
ENTRY(omap3_sram_configure_core_dpll)
stmfd sp!, {r1-r12, lr} @ store regs to stack
- ldr r4, [sp, #52] @ pull extra args off the stack
- ldr r5, [sp, #56] @ load extra args from the stack
- ldr r6, [sp, #60] @ load extra args from the stack
- ldr r7, [sp, #64] @ load extra args from the stack
+
+ @ pull the extra args off the stack
+ @ and store them in SRAM
+ ldr r4, [sp, #52]
+ str r4, omap_sdrc_rfr_ctrl_0_val
+ ldr r4, [sp, #56]
+ str r4, omap_sdrc_actim_ctrl_a_0_val
+ ldr r4, [sp, #60]
+ str r4, omap_sdrc_actim_ctrl_b_0_val
+ ldr r4, [sp, #64]
+ str r4, omap_sdrc_mr_0_val
+ ldr r4, [sp, #68]
+ str r4, omap_sdrc_rfr_ctrl_1_val
+ cmp r4, #0 @ if SDRC_RFR_CTRL_1 is 0,
+ beq skip_cs1_params @ do not use cs1 params
+ ldr r4, [sp, #72]
+ str r4, omap_sdrc_actim_ctrl_a_1_val
+ ldr r4, [sp, #76]
+ str r4, omap_sdrc_actim_ctrl_b_1_val
+ ldr r4, [sp, #80]
+ str r4, omap_sdrc_mr_1_val
+skip_cs1_params:
dsb @ flush buffered writes to interconnect
- cmp r7, #1 @ if increasing SDRC clk rate,
+
+ cmp r3, #1 @ if increasing SDRC clk rate,
bleq configure_sdrc @ program the SDRC regs early (for RFR)
- cmp r4, #SDRC_UNLOCK_DLL @ set the intended DLL state
+ cmp r1, #SDRC_UNLOCK_DLL @ set the intended DLL state
bleq unlock_dll
blne lock_dll
bl sdram_in_selfrefresh @ put SDRAM in self refresh, idle SDRC
bl configure_core_dpll @ change the DPLL3 M2 divider
+ mov r12, r2
+ bl wait_clk_stable @ wait for SDRC to stabilize
bl enable_sdrc @ take SDRC out of idle
- cmp r4, #SDRC_UNLOCK_DLL @ wait for DLL status to change
+ cmp r1, #SDRC_UNLOCK_DLL @ wait for DLL status to change
bleq wait_dll_unlock
blne wait_dll_lock
- cmp r7, #1 @ if increasing SDRC clk rate,
+ cmp r3, #1 @ if increasing SDRC clk rate,
beq return_to_sdram @ return to SDRAM code, otherwise,
bl configure_sdrc @ reprogram SDRC regs now
- mov r12, r5
- bl wait_clk_stable @ wait for SDRC to stabilize
return_to_sdram:
isb @ prevent speculative exec past here
mov r0, #0 @ return value
@@ -113,7 +143,7 @@ return_to_sdram:
unlock_dll:
ldr r11, omap3_sdrc_dlla_ctrl
ldr r12, [r11]
- and r12, r12, #FIXEDDELAY_MASK
+ bic r12, r12, #FIXEDDELAY_MASK
orr r12, r12, #FIXEDDELAY_DEFAULT
orr r12, r12, #DLLIDLE_MASK
str r12, [r11] @ (no OCP barrier needed)
@@ -129,7 +159,6 @@ sdram_in_selfrefresh:
ldr r12, [r11] @ read the contents of SDRC_POWER
mov r9, r12 @ keep a copy of SDRC_POWER bits
orr r12, r12, #SRFRONIDLEREQ_MASK @ enable self refresh on idle
- bic r12, r12, #PWDENA_MASK @ clear PWDENA
str r12, [r11] @ write back to SDRC_POWER register
ldr r12, [r11] @ posted-write barrier for SDRC
idle_sdrc:
@@ -149,7 +178,7 @@ configure_core_dpll:
ldr r12, [r11]
ldr r10, core_m2_mask_val @ modify m2 for core dpll
and r12, r12, r10
- orr r12, r12, r3, lsl #CORE_DPLL_CLKOUT_DIV_SHIFT
+ orr r12, r12, r0, lsl #CORE_DPLL_CLKOUT_DIV_SHIFT
str r12, [r11]
ldr r12, [r11] @ posted-write barrier for CM
bx lr
@@ -187,15 +216,34 @@ wait_dll_unlock:
bne wait_dll_unlock
bx lr
configure_sdrc:
- ldr r11, omap3_sdrc_rfr_ctrl
- str r0, [r11]
- ldr r11, omap3_sdrc_actim_ctrla
- str r1, [r11]
- ldr r11, omap3_sdrc_actim_ctrlb
- str r2, [r11]
+ ldr r12, omap_sdrc_rfr_ctrl_0_val @ fetch value from SRAM
+ ldr r11, omap3_sdrc_rfr_ctrl_0 @ fetch addr from SRAM
+ str r12, [r11] @ store
+ ldr r12, omap_sdrc_actim_ctrl_a_0_val
+ ldr r11, omap3_sdrc_actim_ctrl_a_0
+ str r12, [r11]
+ ldr r12, omap_sdrc_actim_ctrl_b_0_val
+ ldr r11, omap3_sdrc_actim_ctrl_b_0
+ str r12, [r11]
+ ldr r12, omap_sdrc_mr_0_val
ldr r11, omap3_sdrc_mr_0
- str r6, [r11]
- ldr r6, [r11] @ posted-write barrier for SDRC
+ str r12, [r11]
+ ldr r12, omap_sdrc_rfr_ctrl_1_val
+ cmp r12, #0 @ if SDRC_RFR_CTRL_1 is 0,
+ beq skip_cs1_prog @ do not program cs1 params
+ ldr r11, omap3_sdrc_rfr_ctrl_1
+ str r12, [r11]
+ ldr r12, omap_sdrc_actim_ctrl_a_1_val
+ ldr r11, omap3_sdrc_actim_ctrl_a_1
+ str r12, [r11]
+ ldr r12, omap_sdrc_actim_ctrl_b_1_val
+ ldr r11, omap3_sdrc_actim_ctrl_b_1
+ str r12, [r11]
+ ldr r12, omap_sdrc_mr_1_val
+ ldr r11, omap3_sdrc_mr_1
+ str r12, [r11]
+skip_cs1_prog:
+ ldr r12, [r11] @ posted-write barrier for SDRC
bx lr
omap3_sdrc_power:
@@ -206,14 +254,40 @@ omap3_cm_idlest1_core:
.word OMAP34XX_CM_REGADDR(CORE_MOD, CM_IDLEST)
omap3_cm_iclken1_core:
.word OMAP34XX_CM_REGADDR(CORE_MOD, CM_ICLKEN1)
-omap3_sdrc_rfr_ctrl:
+
+omap3_sdrc_rfr_ctrl_0:
.word OMAP34XX_SDRC_REGADDR(SDRC_RFR_CTRL_0)
-omap3_sdrc_actim_ctrla:
+omap3_sdrc_rfr_ctrl_1:
+ .word OMAP34XX_SDRC_REGADDR(SDRC_RFR_CTRL_1)
+omap3_sdrc_actim_ctrl_a_0:
.word OMAP34XX_SDRC_REGADDR(SDRC_ACTIM_CTRL_A_0)
-omap3_sdrc_actim_ctrlb:
+omap3_sdrc_actim_ctrl_a_1:
+ .word OMAP34XX_SDRC_REGADDR(SDRC_ACTIM_CTRL_A_1)
+omap3_sdrc_actim_ctrl_b_0:
.word OMAP34XX_SDRC_REGADDR(SDRC_ACTIM_CTRL_B_0)
+omap3_sdrc_actim_ctrl_b_1:
+ .word OMAP34XX_SDRC_REGADDR(SDRC_ACTIM_CTRL_B_1)
omap3_sdrc_mr_0:
.word OMAP34XX_SDRC_REGADDR(SDRC_MR_0)
+omap3_sdrc_mr_1:
+ .word OMAP34XX_SDRC_REGADDR(SDRC_MR_1)
+omap_sdrc_rfr_ctrl_0_val:
+ .word 0xDEADBEEF
+omap_sdrc_rfr_ctrl_1_val:
+ .word 0xDEADBEEF
+omap_sdrc_actim_ctrl_a_0_val:
+ .word 0xDEADBEEF
+omap_sdrc_actim_ctrl_a_1_val:
+ .word 0xDEADBEEF
+omap_sdrc_actim_ctrl_b_0_val:
+ .word 0xDEADBEEF
+omap_sdrc_actim_ctrl_b_1_val:
+ .word 0xDEADBEEF
+omap_sdrc_mr_0_val:
+ .word 0xDEADBEEF
+omap_sdrc_mr_1_val:
+ .word 0xDEADBEEF
+
omap3_sdrc_dlla_status:
.word OMAP34XX_SDRC_REGADDR(SDRC_DLLA_STATUS)
omap3_sdrc_dlla_ctrl:
@@ -223,3 +297,4 @@ core_m2_mask_val:
ENTRY(omap3_sram_configure_core_dpll_sz)
.word . - omap3_sram_configure_core_dpll
+
diff --git a/arch/arm/mach-omap2/usb-musb.c b/arch/arm/mach-omap2/usb-musb.c
index d85296dc896..739e59e8025 100644
--- a/arch/arm/mach-omap2/usb-musb.c
+++ b/arch/arm/mach-omap2/usb-musb.c
@@ -155,20 +155,6 @@ static struct platform_device musb_device = {
.resource = musb_resources,
};
-#ifdef CONFIG_NOP_USB_XCEIV
-static u64 nop_xceiv_dmamask = DMA_BIT_MASK(32);
-
-static struct platform_device nop_xceiv_device = {
- .name = "nop_usb_xceiv",
- .id = -1,
- .dev = {
- .dma_mask = &nop_xceiv_dmamask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- .platform_data = NULL,
- },
-};
-#endif
-
void __init usb_musb_init(void)
{
if (cpu_is_omap243x())
@@ -183,13 +169,6 @@ void __init usb_musb_init(void)
*/
musb_plat.clock = "ick";
-#ifdef CONFIG_NOP_USB_XCEIV
- if (platform_device_register(&nop_xceiv_device) < 0) {
- printk(KERN_ERR "Unable to register NOP-XCEIV device\n");
- return;
- }
-#endif
-
if (platform_device_register(&musb_device) < 0) {
printk(KERN_ERR "Unable to register HS-USB (MUSB) device\n");
return;
diff --git a/arch/arm/mach-pxa/em-x270.c b/arch/arm/mach-pxa/em-x270.c
index 63b10d9bb1d..9cd09465a0e 100644
--- a/arch/arm/mach-pxa/em-x270.c
+++ b/arch/arm/mach-pxa/em-x270.c
@@ -1141,12 +1141,16 @@ struct power_supply_info em_x270_psy_info = {
static void em_x270_battery_low(void)
{
+#if defined(CONFIG_APM_EMULATION)
apm_queue_event(APM_LOW_BATTERY);
+#endif
}
static void em_x270_battery_critical(void)
{
+#if defined(CONFIG_APM_EMULATION)
apm_queue_event(APM_CRITICAL_SUSPEND);
+#endif
}
struct da9030_battery_info em_x270_batterty_info = {
diff --git a/arch/arm/mach-pxa/include/mach/audio.h b/arch/arm/mach-pxa/include/mach/audio.h
index 16eb02552d5..a3449e35a6f 100644
--- a/arch/arm/mach-pxa/include/mach/audio.h
+++ b/arch/arm/mach-pxa/include/mach/audio.h
@@ -3,10 +3,12 @@
#include <sound/core.h>
#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
/*
* @reset_gpio: AC97 reset gpio (normally gpio113 or gpio95)
* a -1 value means no gpio will be used for reset
+ * @codec_pdata: AC97 codec platform_data
* reset_gpio should only be specified for pxa27x CPUs where a silicon
* bug prevents correct operation of the reset line. If not specified,
@@ -20,6 +22,7 @@ typedef struct {
void (*resume)(void *);
void *priv;
int reset_gpio;
+ void *codec_pdata[AC97_BUS_MAX_DEVICES];
} pxa2xx_audio_ops_t;
extern void pxa_set_ac97_info(pxa2xx_audio_ops_t *ops);
diff --git a/arch/arm/mach-pxa/palmld.c b/arch/arm/mach-pxa/palmld.c
index ed70f281dd0..169fcc18154 100644
--- a/arch/arm/mach-pxa/palmld.c
+++ b/arch/arm/mach-pxa/palmld.c
@@ -128,6 +128,10 @@ static unsigned long palmld_pin_config[] __initdata = {
GPIO38_GPIO, /* wifi ready */
GPIO81_GPIO, /* wifi reset */
+ /* FFUART */
+ GPIO34_FFUART_RXD,
+ GPIO39_FFUART_TXD,
+
/* HDD */
GPIO98_GPIO, /* HDD reset */
GPIO115_GPIO, /* HDD power */
diff --git a/arch/arm/mach-pxa/palmt5.c b/arch/arm/mach-pxa/palmt5.c
index aae64a12a73..33f726ff55e 100644
--- a/arch/arm/mach-pxa/palmt5.c
+++ b/arch/arm/mach-pxa/palmt5.c
@@ -111,6 +111,10 @@ static unsigned long palmt5_pin_config[] __initdata = {
/* PWM */
GPIO16_PWM0_OUT,
+ /* FFUART */
+ GPIO34_FFUART_RXD,
+ GPIO39_FFUART_TXD,
+
/* MISC */
GPIO10_GPIO, /* hotsync button */
GPIO90_GPIO, /* power detect */
diff --git a/arch/arm/mach-pxa/palmtx.c b/arch/arm/mach-pxa/palmtx.c
index 6c15d84bde5..83d02087958 100644
--- a/arch/arm/mach-pxa/palmtx.c
+++ b/arch/arm/mach-pxa/palmtx.c
@@ -127,6 +127,10 @@ static unsigned long palmtx_pin_config[] __initdata = {
GPIO76_LCD_PCLK,
GPIO77_LCD_BIAS,
+ /* FFUART */
+ GPIO34_FFUART_RXD,
+ GPIO39_FFUART_TXD,
+
/* MISC. */
GPIO10_GPIO, /* hotsync button */
GPIO12_GPIO, /* power detect */
diff --git a/arch/arm/mach-pxa/treo680.c b/arch/arm/mach-pxa/treo680.c
index a06f19edebb..753ec4df17b 100644
--- a/arch/arm/mach-pxa/treo680.c
+++ b/arch/arm/mach-pxa/treo680.c
@@ -409,7 +409,7 @@ err1:
static void treo680_irda_shutdown(struct device *dev)
{
- gpio_free(GPIO_NR_TREO680_AMP_EN);
+ gpio_free(GPIO_NR_TREO680_IR_EN);
}
static struct pxaficp_platform_data treo680_ficp_info = {
diff --git a/arch/arm/mach-pxa/zylonite_pxa300.c b/arch/arm/mach-pxa/zylonite_pxa300.c
index cefd1c0a854..84095440a87 100644
--- a/arch/arm/mach-pxa/zylonite_pxa300.c
+++ b/arch/arm/mach-pxa/zylonite_pxa300.c
@@ -197,10 +197,12 @@ static void __init zylonite_detect_lcd_panel(void)
for (i = 0; i < NUM_LCD_DETECT_PINS; i++) {
id = id << 1;
gpio = mfp_to_gpio(lcd_detect_pins[i]);
+ gpio_request(gpio, "LCD_ID_PINS");
gpio_direction_input(gpio);
if (gpio_get_value(gpio))
id = id | 0x1;
+ gpio_free(gpio);
}
/* lcd id, flush out bit 1 */
diff --git a/arch/arm/mach-pxa/zylonite_pxa320.c b/arch/arm/mach-pxa/zylonite_pxa320.c
index cc5a2283360..60d08f23f5e 100644
--- a/arch/arm/mach-pxa/zylonite_pxa320.c
+++ b/arch/arm/mach-pxa/zylonite_pxa320.c
@@ -176,10 +176,12 @@ static void __init zylonite_detect_lcd_panel(void)
for (i = 0; i < NUM_LCD_DETECT_PINS; i++) {
id = id << 1;
gpio = mfp_to_gpio(lcd_detect_pins[i]);
+ gpio_request(gpio, "LCD_ID_PINS");
gpio_direction_input(gpio);
if (gpio_get_value(gpio))
id = id | 0x1;
+ gpio_free(gpio);
}
/* lcd id, flush out bit 1 */
diff --git a/arch/arm/mach-s3c2410/include/mach/gpio-core.h b/arch/arm/mach-s3c2410/include/mach/gpio-core.h
index 8fe192081d3..f8b879a7973 100644
--- a/arch/arm/mach-s3c2410/include/mach/gpio-core.h
+++ b/arch/arm/mach-s3c2410/include/mach/gpio-core.h
@@ -28,7 +28,7 @@ static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int pin)
return NULL;
chip = &s3c24xx_gpios[pin/32];
- return (S3C2410_GPIO_OFFSET(pin) > chip->chip.ngpio) ? chip : NULL;
+ return (S3C2410_GPIO_OFFSET(pin) < chip->chip.ngpio) ? chip : NULL;
}
#endif /* __ASM_ARCH_GPIO_CORE_H */
diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c
index 7936085dd75..2e9b8ccd8ec 100644
--- a/arch/arm/mach-u300/core.c
+++ b/arch/arm/mach-u300/core.c
@@ -510,7 +510,7 @@ static struct db_chip db_chips[] __initdata = {
}
};
-static void u300_init_check_chip(void)
+static void __init u300_init_check_chip(void)
{
u16 val;
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 8277802ec85..3a7279c1ce5 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -120,6 +120,32 @@ void show_mem(void)
printk("%d pages swap cached\n", cached);
}
+static void __init find_node_limits(int node, struct meminfo *mi,
+ unsigned long *min, unsigned long *max_low, unsigned long *max_high)
+{
+ int i;
+
+ *min = -1UL;
+ *max_low = *max_high = 0;
+
+ for_each_nodebank(i, mi, node) {
+ struct membank *bank = &mi->bank[i];
+ unsigned long start, end;
+
+ start = bank_pfn_start(bank);
+ end = bank_pfn_end(bank);
+
+ if (*min > start)
+ *min = start;
+ if (*max_high < end)
+ *max_high = end;
+ if (bank->highmem)
+ continue;
+ if (*max_low < end)
+ *max_low = end;
+ }
+}
+
/*
* FIXME: We really want to avoid allocating the bootmap bitmap
* over the top of the initrd. Hopefully, this is located towards
@@ -210,41 +236,25 @@ static inline void map_memory_bank(struct membank *bank)
#endif
}
-static unsigned long __init bootmem_init_node(int node, struct meminfo *mi)
+static void __init bootmem_init_node(int node, struct meminfo *mi,
+ unsigned long start_pfn, unsigned long end_pfn)
{
- unsigned long start_pfn, end_pfn, boot_pfn;
+ unsigned long boot_pfn;
unsigned int boot_pages;
pg_data_t *pgdat;
int i;
- start_pfn = -1UL;
- end_pfn = 0;
-
/*
- * Calculate the pfn range, and map the memory banks for this node.
+ * Map the memory banks for this node.
*/
for_each_nodebank(i, mi, node) {
struct membank *bank = &mi->bank[i];
- unsigned long start, end;
- start = bank_pfn_start(bank);
- end = bank_pfn_end(bank);
-
- if (start_pfn > start)
- start_pfn = start;
- if (end_pfn < end)
- end_pfn = end;
-
- map_memory_bank(bank);
+ if (!bank->highmem)
+ map_memory_bank(bank);
}
/*
- * If there is no memory in this node, ignore it.
- */
- if (end_pfn == 0)
- return end_pfn;
-
- /*
* Allocate the bootmem bitmap page.
*/
boot_pages = bootmem_bootmap_pages(end_pfn - start_pfn);
@@ -260,7 +270,8 @@ static unsigned long __init bootmem_init_node(int node, struct meminfo *mi)
for_each_nodebank(i, mi, node) {
struct membank *bank = &mi->bank[i];
- free_bootmem_node(pgdat, bank_phys_start(bank), bank_phys_size(bank));
+ if (!bank->highmem)
+ free_bootmem_node(pgdat, bank_phys_start(bank), bank_phys_size(bank));
memory_present(node, bank_pfn_start(bank), bank_pfn_end(bank));
}
@@ -269,8 +280,6 @@ static unsigned long __init bootmem_init_node(int node, struct meminfo *mi)
*/
reserve_bootmem_node(pgdat, boot_pfn << PAGE_SHIFT,
boot_pages << PAGE_SHIFT, BOOTMEM_DEFAULT);
-
- return end_pfn;
}
static void __init bootmem_reserve_initrd(int node)
@@ -297,33 +306,39 @@ static void __init bootmem_reserve_initrd(int node)
static void __init bootmem_free_node(int node, struct meminfo *mi)
{
unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
- unsigned long start_pfn, end_pfn;
- pg_data_t *pgdat = NODE_DATA(node);
+ unsigned long min, max_low, max_high;
int i;
- start_pfn = pgdat->bdata->node_min_pfn;
- end_pfn = pgdat->bdata->node_low_pfn;
+ find_node_limits(node, mi, &min, &max_low, &max_high);
/*
* initialise the zones within this node.
*/
memset(zone_size, 0, sizeof(zone_size));
- memset(zhole_size, 0, sizeof(zhole_size));
/*
* The size of this node has already been determined. If we need
* to do anything fancy with the allocation of this memory to the
* zones, now is the time to do it.
*/
- zone_size[0] = end_pfn - start_pfn;
+ zone_size[0] = max_low - min;
+#ifdef CONFIG_HIGHMEM
+ zone_size[ZONE_HIGHMEM] = max_high - max_low;
+#endif
/*
* For each bank in this node, calculate the size of the holes.
* holes = node_size - sum(bank_sizes_in_node)
*/
- zhole_size[0] = zone_size[0];
- for_each_nodebank(i, mi, node)
- zhole_size[0] -= bank_pfn_size(&mi->bank[i]);
+ memcpy(zhole_size, zone_size, sizeof(zhole_size));
+ for_each_nodebank(i, mi, node) {
+ int idx = 0;
+#ifdef CONFIG_HIGHMEM
+ if (mi->bank[i].highmem)
+ idx = ZONE_HIGHMEM;
+#endif
+ zhole_size[idx] -= bank_pfn_size(&mi->bank[i]);
+ }
/*
* Adjust the sizes according to any special requirements for
@@ -331,13 +346,13 @@ static void __init bootmem_free_node(int node, struct meminfo *mi)
*/
arch_adjust_zones(node, zone_size, zhole_size);
- free_area_init_node(node, zone_size, start_pfn, zhole_size);
+ free_area_init_node(node, zone_size, min, zhole_size);
}
void __init bootmem_init(void)
{
struct meminfo *mi = &meminfo;
- unsigned long memend_pfn = 0;
+ unsigned long min, max_low, max_high;
int node, initrd_node;
/*
@@ -345,11 +360,29 @@ void __init bootmem_init(void)
*/
initrd_node = check_initrd(mi);
+ max_low = max_high = 0;
+
/*
* Run through each node initialising the bootmem allocator.
*/
for_each_node(node) {
- unsigned long end_pfn = bootmem_init_node(node, mi);
+ unsigned long node_low, node_high;
+
+ find_node_limits(node, mi, &min, &node_low, &node_high);
+
+ if (node_low > max_low)
+ max_low = node_low;
+ if (node_high > max_high)
+ max_high = node_high;
+
+ /*
+ * If there is no memory in this node, ignore it.
+ * (We can't have nodes which have no lowmem)
+ */
+ if (node_low == 0)
+ continue;
+
+ bootmem_init_node(node, mi, min, node_low);
/*
* Reserve any special node zero regions.
@@ -362,12 +395,6 @@ void __init bootmem_init(void)
*/
if (node == initrd_node)
bootmem_reserve_initrd(node);
-
- /*
- * Remember the highest memory PFN.
- */
- if (end_pfn > memend_pfn)
- memend_pfn = end_pfn;
}
/*
@@ -383,7 +410,7 @@ void __init bootmem_init(void)
for_each_node(node)
bootmem_free_node(node, mi);
- high_memory = __va((memend_pfn << PAGE_SHIFT) - 1) + 1;
+ high_memory = __va((max_low << PAGE_SHIFT) - 1) + 1;
/*
* This doesn't seem to be used by the Linux memory manager any
@@ -393,7 +420,8 @@ void __init bootmem_init(void)
* Note: max_low_pfn and max_pfn reflect the number of _pages_ in
* the system, not the maximum PFN.
*/
- max_pfn = max_low_pfn = memend_pfn - PHYS_PFN_OFFSET;
+ max_low_pfn = max_low - PHYS_PFN_OFFSET;
+ max_pfn = max_high - PHYS_PFN_OFFSET;
}
static inline int free_area(unsigned long pfn, unsigned long end, char *s)
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 4722582b17b..4426ee67cec 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -687,13 +687,19 @@ __early_param("vmalloc=", early_vmalloc);
static void __init sanity_check_meminfo(void)
{
- int i, j;
+ int i, j, highmem = 0;
for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
struct membank *bank = &meminfo.bank[j];
*bank = meminfo.bank[i];
#ifdef CONFIG_HIGHMEM
+ if (__va(bank->start) > VMALLOC_MIN ||
+ __va(bank->start) < (void *)PAGE_OFFSET)
+ highmem = 1;
+
+ bank->highmem = highmem;
+
/*
* Split those memory banks which are partially overlapping
* the vmalloc area greatly simplifying things later.
@@ -714,6 +720,7 @@ static void __init sanity_check_meminfo(void)
i++;
bank[1].size -= VMALLOC_MIN - __va(bank->start);
bank[1].start = __pa(VMALLOC_MIN - 1) + 1;
+ bank[1].highmem = highmem = 1;
j++;
}
bank->size = VMALLOC_MIN - __va(bank->start);
diff --git a/arch/arm/plat-omap/cpu-omap.c b/arch/arm/plat-omap/cpu-omap.c
index 843e8af6406..1868c0d8f9b 100644
--- a/arch/arm/plat-omap/cpu-omap.c
+++ b/arch/arm/plat-omap/cpu-omap.c
@@ -78,10 +78,10 @@ static int omap_target(struct cpufreq_policy *policy,
/* Ensure desired rate is within allowed range. Some govenors
* (ondemand) will just pass target_freq=0 to get the minimum. */
- if (target_freq < policy->cpuinfo.min_freq)
- target_freq = policy->cpuinfo.min_freq;
- if (target_freq > policy->cpuinfo.max_freq)
- target_freq = policy->cpuinfo.max_freq;
+ if (target_freq < policy->min)
+ target_freq = policy->min;
+ if (target_freq > policy->max)
+ target_freq = policy->max;
freqs.old = omap_getspeed(0);
freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000;
diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c
index 7677a4a1cef..9b00f4cbc90 100644
--- a/arch/arm/plat-omap/dma.c
+++ b/arch/arm/plat-omap/dma.c
@@ -946,7 +946,9 @@ void omap_start_dma(int lch)
cur_lch = next_lch;
} while (next_lch != -1);
- } else if (cpu_class_is_omap2()) {
+ } else if (cpu_is_omap242x() ||
+ (cpu_is_omap243x() && omap_type() <= OMAP2430_REV_ES1_0)) {
+
/* Errata: Need to write lch even if not using chaining */
dma_write(lch, CLNK_CTRL(lch));
}
@@ -1125,6 +1127,11 @@ int omap_dma_running(void)
void omap_dma_link_lch(int lch_head, int lch_queue)
{
if (omap_dma_in_1510_mode()) {
+ if (lch_head == lch_queue) {
+ dma_write(dma_read(CCR(lch_head)) | (3 << 8),
+ CCR(lch_head));
+ return;
+ }
printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
BUG();
return;
@@ -1147,6 +1154,11 @@ EXPORT_SYMBOL(omap_dma_link_lch);
void omap_dma_unlink_lch(int lch_head, int lch_queue)
{
if (omap_dma_in_1510_mode()) {
+ if (lch_head == lch_queue) {
+ dma_write(dma_read(CCR(lch_head)) & ~(3 << 8),
+ CCR(lch_head));
+ return;
+ }
printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
BUG();
return;
diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c
index 26b387c1242..9298bc0ab17 100644
--- a/arch/arm/plat-omap/gpio.c
+++ b/arch/arm/plat-omap/gpio.c
@@ -476,14 +476,12 @@ static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable)
__raw_writel(l, reg);
}
-static int __omap_get_gpio_datain(int gpio)
+static int _get_gpio_datain(struct gpio_bank *bank, int gpio)
{
- struct gpio_bank *bank;
void __iomem *reg;
if (check_gpio(gpio) < 0)
return -EINVAL;
- bank = get_gpio_bank(gpio);
reg = bank->base;
switch (bank->method) {
#ifdef CONFIG_ARCH_OMAP1
@@ -524,6 +522,53 @@ static int __omap_get_gpio_datain(int gpio)
& (1 << get_gpio_index(gpio))) != 0;
}
+static int _get_gpio_dataout(struct gpio_bank *bank, int gpio)
+{
+ void __iomem *reg;
+
+ if (check_gpio(gpio) < 0)
+ return -EINVAL;
+ reg = bank->base;
+
+ switch (bank->method) {
+#ifdef CONFIG_ARCH_OMAP1
+ case METHOD_MPUIO:
+ reg += OMAP_MPUIO_OUTPUT;
+ break;
+#endif
+#ifdef CONFIG_ARCH_OMAP15XX
+ case METHOD_GPIO_1510:
+ reg += OMAP1510_GPIO_DATA_OUTPUT;
+ break;
+#endif
+#ifdef CONFIG_ARCH_OMAP16XX
+ case METHOD_GPIO_1610:
+ reg += OMAP1610_GPIO_DATAOUT;
+ break;
+#endif
+#ifdef CONFIG_ARCH_OMAP730
+ case METHOD_GPIO_730:
+ reg += OMAP730_GPIO_DATA_OUTPUT;
+ break;
+#endif
+#ifdef CONFIG_ARCH_OMAP850
+ case METHOD_GPIO_850:
+ reg += OMAP850_GPIO_DATA_OUTPUT;
+ break;
+#endif
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
+ defined(CONFIG_ARCH_OMAP4)
+ case METHOD_GPIO_24XX:
+ reg += OMAP24XX_GPIO_DATAOUT;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ return (__raw_readl(reg) & (1 << get_gpio_index(gpio))) != 0;
+}
+
#define MOD_REG_BIT(reg, bit_mask, set) \
do { \
int l = __raw_readl(base + reg); \
@@ -1189,6 +1234,7 @@ static void gpio_mask_irq(unsigned int irq)
struct gpio_bank *bank = get_irq_chip_data(irq);
_set_gpio_irqenable(bank, gpio, 0);
+ _set_gpio_triggering(bank, get_gpio_index(gpio), IRQ_TYPE_NONE);
}
static void gpio_unmask_irq(unsigned int irq)
@@ -1196,6 +1242,11 @@ static void gpio_unmask_irq(unsigned int irq)
unsigned int gpio = irq - IH_GPIO_BASE;
struct gpio_bank *bank = get_irq_chip_data(irq);
unsigned int irq_mask = 1 << get_gpio_index(gpio);
+ struct irq_desc *desc = irq_to_desc(irq);
+ u32 trigger = desc->status & IRQ_TYPE_SENSE_MASK;
+
+ if (trigger)
+ _set_gpio_triggering(bank, get_gpio_index(gpio), trigger);
/* For level-triggered GPIOs, the clearing must be done after
* the HW source is cleared, thus after the handler has run */
@@ -1350,9 +1401,49 @@ static int gpio_input(struct gpio_chip *chip, unsigned offset)
return 0;
}
+static int gpio_is_input(struct gpio_bank *bank, int mask)
+{
+ void __iomem *reg = bank->base;
+
+ switch (bank->method) {
+ case METHOD_MPUIO:
+ reg += OMAP_MPUIO_IO_CNTL;
+ break;
+ case METHOD_GPIO_1510:
+ reg += OMAP1510_GPIO_DIR_CONTROL;
+ break;
+ case METHOD_GPIO_1610:
+ reg += OMAP1610_GPIO_DIRECTION;
+ break;
+ case METHOD_GPIO_730:
+ reg += OMAP730_GPIO_DIR_CONTROL;
+ break;
+ case METHOD_GPIO_850:
+ reg += OMAP850_GPIO_DIR_CONTROL;
+ break;
+ case METHOD_GPIO_24XX:
+ reg += OMAP24XX_GPIO_OE;
+ break;
+ }
+ return __raw_readl(reg) & mask;
+}
+
static int gpio_get(struct gpio_chip *chip, unsigned offset)
{
- return __omap_get_gpio_datain(chip->base + offset);
+ struct gpio_bank *bank;
+ void __iomem *reg;
+ int gpio;
+ u32 mask;
+
+ gpio = chip->base + offset;
+ bank = get_gpio_bank(gpio);
+ reg = bank->base;
+ mask = 1 << get_gpio_index(gpio);
+
+ if (gpio_is_input(bank, mask))
+ return _get_gpio_datain(bank, gpio);
+ else
+ return _get_gpio_dataout(bank, gpio);
}
static int gpio_output(struct gpio_chip *chip, unsigned offset, int value)
@@ -1886,34 +1977,6 @@ arch_initcall(omap_gpio_sysinit);
#include <linux/debugfs.h>
#include <linux/seq_file.h>
-static int gpio_is_input(struct gpio_bank *bank, int mask)
-{
- void __iomem *reg = bank->base;
-
- switch (bank->method) {
- case METHOD_MPUIO:
- reg += OMAP_MPUIO_IO_CNTL;
- break;
- case METHOD_GPIO_1510:
- reg += OMAP1510_GPIO_DIR_CONTROL;
- break;
- case METHOD_GPIO_1610:
- reg += OMAP1610_GPIO_DIRECTION;
- break;
- case METHOD_GPIO_730:
- reg += OMAP730_GPIO_DIR_CONTROL;
- break;
- case METHOD_GPIO_850:
- reg += OMAP850_GPIO_DIR_CONTROL;
- break;
- case METHOD_GPIO_24XX:
- reg += OMAP24XX_GPIO_OE;
- break;
- }
- return __raw_readl(reg) & mask;
-}
-
-
static int dbg_gpio_show(struct seq_file *s, void *unused)
{
unsigned i, j, gpio;
diff --git a/arch/arm/plat-omap/include/mach/clock.h b/arch/arm/plat-omap/include/mach/clock.h
index f9f65e1ba3f..4b8b0d65cbf 100644
--- a/arch/arm/plat-omap/include/mach/clock.h
+++ b/arch/arm/plat-omap/include/mach/clock.h
@@ -20,6 +20,8 @@ struct clockdomain;
struct clkops {
int (*enable)(struct clk *);
void (*disable)(struct clk *);
+ void (*find_idlest)(struct clk *, void __iomem **, u8 *);
+ void (*find_companion)(struct clk *, void __iomem **, u8 *);
};
#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) || \
diff --git a/arch/arm/plat-omap/include/mach/cpu.h b/arch/arm/plat-omap/include/mach/cpu.h
index 285eaa3a827..11e73d9e892 100644
--- a/arch/arm/plat-omap/include/mach/cpu.h
+++ b/arch/arm/plat-omap/include/mach/cpu.h
@@ -378,9 +378,6 @@ IS_OMAP_TYPE(3430, 0x3430)
#define cpu_class_is_omap2() (cpu_is_omap24xx() || cpu_is_omap34xx() || \
cpu_is_omap44xx())
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) || \
- defined(CONFIG_ARCH_OMAP4)
-
/* Various silicon revisions for omap2 */
#define OMAP242X_CLASS 0x24200024
#define OMAP2420_REV_ES1_0 0x24200024
@@ -436,5 +433,3 @@ IS_OMAP_TYPE(3430, 0x3430)
int omap_chip_is(struct omap_chip_id oci);
void omap2_check_revision(void);
-
-#endif /* defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) */
diff --git a/arch/arm/plat-omap/include/mach/io.h b/arch/arm/plat-omap/include/mach/io.h
index 73f483d56ca..21fb0efdda8 100644
--- a/arch/arm/plat-omap/include/mach/io.h
+++ b/arch/arm/plat-omap/include/mach/io.h
@@ -228,7 +228,8 @@ extern void omap1_map_common_io(void);
extern void omap1_init_common_hw(void);
extern void omap2_map_common_io(void);
-extern void omap2_init_common_hw(struct omap_sdrc_params *sp);
+extern void omap2_init_common_hw(struct omap_sdrc_params *sdrc_cs0,
+ struct omap_sdrc_params *sdrc_cs1);
#define __arch_ioremap(p,s,t) omap_ioremap(p,s,t)
#define __arch_iounmap(v) omap_iounmap(v)
diff --git a/arch/arm/plat-omap/include/mach/mcbsp.h b/arch/arm/plat-omap/include/mach/mcbsp.h
index bb154ea7676..63a3f254af7 100644
--- a/arch/arm/plat-omap/include/mach/mcbsp.h
+++ b/arch/arm/plat-omap/include/mach/mcbsp.h
@@ -134,6 +134,11 @@
#define OMAP_MCBSP_REG_XCERG 0x74
#define OMAP_MCBSP_REG_XCERH 0x78
#define OMAP_MCBSP_REG_SYSCON 0x8C
+#define OMAP_MCBSP_REG_THRSH2 0x90
+#define OMAP_MCBSP_REG_THRSH1 0x94
+#define OMAP_MCBSP_REG_IRQST 0xA0
+#define OMAP_MCBSP_REG_IRQEN 0xA4
+#define OMAP_MCBSP_REG_WAKEUPEN 0xA8
#define OMAP_MCBSP_REG_XCCR 0xAC
#define OMAP_MCBSP_REG_RCCR 0xB0
@@ -249,8 +254,27 @@
#define RDISABLE 0x0001
/********************** McBSP SYSCONFIG bit definitions ********************/
+#define CLOCKACTIVITY(value) ((value)<<8)
+#define SIDLEMODE(value) ((value)<<3)
+#define ENAWAKEUP 0x0004
#define SOFTRST 0x0002
+/********************** McBSP DMA operating modes **************************/
+#define MCBSP_DMA_MODE_ELEMENT 0
+#define MCBSP_DMA_MODE_THRESHOLD 1
+#define MCBSP_DMA_MODE_FRAME 2
+
+/********************** McBSP WAKEUPEN bit definitions *********************/
+#define XEMPTYEOFEN 0x4000
+#define XRDYEN 0x0400
+#define XEOFEN 0x0200
+#define XFSXEN 0x0100
+#define XSYNCERREN 0x0080
+#define RRDYEN 0x0008
+#define REOFEN 0x0004
+#define RFSREN 0x0002
+#define RSYNCERREN 0x0001
+
/* we don't do multichannel for now */
struct omap_mcbsp_reg_cfg {
u16 spcr2;
@@ -344,6 +368,9 @@ struct omap_mcbsp_platform_data {
u8 dma_rx_sync, dma_tx_sync;
u16 rx_irq, tx_irq;
struct omap_mcbsp_ops *ops;
+#ifdef CONFIG_ARCH_OMAP34XX
+ u16 buffer_size;
+#endif
};
struct omap_mcbsp {
@@ -377,6 +404,11 @@ struct omap_mcbsp {
struct omap_mcbsp_platform_data *pdata;
struct clk *iclk;
struct clk *fclk;
+#ifdef CONFIG_ARCH_OMAP34XX
+ int dma_op_mode;
+ u16 max_tx_thres;
+ u16 max_rx_thres;
+#endif
};
extern struct omap_mcbsp **mcbsp_ptr;
extern int omap_mcbsp_count;
@@ -385,10 +417,25 @@ int omap_mcbsp_init(void);
void omap_mcbsp_register_board_cfg(struct omap_mcbsp_platform_data *config,
int size);
void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config);
+#ifdef CONFIG_ARCH_OMAP34XX
+void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold);
+void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold);
+u16 omap_mcbsp_get_max_tx_threshold(unsigned int id);
+u16 omap_mcbsp_get_max_rx_threshold(unsigned int id);
+int omap_mcbsp_get_dma_op_mode(unsigned int id);
+#else
+static inline void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
+{ }
+static inline void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
+{ }
+static inline u16 omap_mcbsp_get_max_tx_threshold(unsigned int id) { return 0; }
+static inline u16 omap_mcbsp_get_max_rx_threshold(unsigned int id) { return 0; }
+static inline int omap_mcbsp_get_dma_op_mode(unsigned int id) { return 0; }
+#endif
int omap_mcbsp_request(unsigned int id);
void omap_mcbsp_free(unsigned int id);
-void omap_mcbsp_start(unsigned int id);
-void omap_mcbsp_stop(unsigned int id);
+void omap_mcbsp_start(unsigned int id, int tx, int rx);
+void omap_mcbsp_stop(unsigned int id, int tx, int rx);
void omap_mcbsp_xmit_word(unsigned int id, u32 word);
u32 omap_mcbsp_recv_word(unsigned int id);
diff --git a/arch/arm/plat-omap/include/mach/mux.h b/arch/arm/plat-omap/include/mach/mux.h
index 85a62170576..80281c458ba 100644
--- a/arch/arm/plat-omap/include/mach/mux.h
+++ b/arch/arm/plat-omap/include/mach/mux.h
@@ -853,6 +853,10 @@ enum omap34xx_index {
AE5_34XX_GPIO143,
H19_34XX_GPIO164_OUT,
J25_34XX_GPIO170,
+
+ /* OMAP3 SDRC CKE signals to SDR/DDR ram chips */
+ H16_34XX_SDRC_CKE0,
+ H17_34XX_SDRC_CKE1,
};
struct omap_mux_cfg {
diff --git a/arch/arm/plat-omap/include/mach/prcm.h b/arch/arm/plat-omap/include/mach/prcm.h
index 24ac3c71591..cda2a70397b 100644
--- a/arch/arm/plat-omap/include/mach/prcm.h
+++ b/arch/arm/plat-omap/include/mach/prcm.h
@@ -25,6 +25,7 @@
u32 omap_prcm_get_reset_sources(void);
void omap_prcm_arch_reset(char mode);
+int omap2_cm_wait_idlest(void __iomem *reg, u32 mask, const char *name);
#endif
diff --git a/arch/arm/plat-omap/include/mach/sdrc.h b/arch/arm/plat-omap/include/mach/sdrc.h
index adc73522491..0be18e4ff18 100644
--- a/arch/arm/plat-omap/include/mach/sdrc.h
+++ b/arch/arm/plat-omap/include/mach/sdrc.h
@@ -30,6 +30,10 @@
#define SDRC_ACTIM_CTRL_A_0 0x09c
#define SDRC_ACTIM_CTRL_B_0 0x0a0
#define SDRC_RFR_CTRL_0 0x0a4
+#define SDRC_MR_1 0x0B4
+#define SDRC_ACTIM_CTRL_A_1 0x0C4
+#define SDRC_ACTIM_CTRL_B_1 0x0C8
+#define SDRC_RFR_CTRL_1 0x0D4
/*
* These values represent the number of memory clock cycles between
@@ -102,8 +106,11 @@ struct omap_sdrc_params {
u32 mr;
};
-void __init omap2_sdrc_init(struct omap_sdrc_params *sp);
-struct omap_sdrc_params *omap2_sdrc_get_params(unsigned long r);
+void __init omap2_sdrc_init(struct omap_sdrc_params *sdrc_cs0,
+ struct omap_sdrc_params *sdrc_cs1);
+int omap2_sdrc_get_params(unsigned long r,
+ struct omap_sdrc_params **sdrc_cs0,
+ struct omap_sdrc_params **sdrc_cs1);
#ifdef CONFIG_ARCH_OMAP2
diff --git a/arch/arm/plat-omap/include/mach/serial.h b/arch/arm/plat-omap/include/mach/serial.h
index 13abd02d152..def0529c75e 100644
--- a/arch/arm/plat-omap/include/mach/serial.h
+++ b/arch/arm/plat-omap/include/mach/serial.h
@@ -59,6 +59,7 @@ extern void omap_uart_check_wakeup(void);
extern void omap_uart_prepare_suspend(void);
extern void omap_uart_prepare_idle(int num);
extern void omap_uart_resume_idle(int num);
+extern void omap_uart_enable_irqs(int enable);
#endif
#endif
diff --git a/arch/arm/plat-omap/include/mach/sram.h b/arch/arm/plat-omap/include/mach/sram.h
index 4d53cc59d7a..8974e3fc269 100644
--- a/arch/arm/plat-omap/include/mach/sram.h
+++ b/arch/arm/plat-omap/include/mach/sram.h
@@ -21,11 +21,12 @@ extern void omap2_sram_reprogram_sdrc(u32 perf_level, u32 dll_val,
u32 mem_type);
extern u32 omap2_set_prcm(u32 dpll_ctrl_val, u32 sdrc_rfr_val, int bypass);
-extern u32 omap3_configure_core_dpll(u32 sdrc_rfr_ctrl,
- u32 sdrc_actim_ctrla,
- u32 sdrc_actim_ctrlb, u32 m2,
- u32 unlock_dll, u32 f, u32 sdrc_mr,
- u32 inc);
+extern u32 omap3_configure_core_dpll(
+ u32 m2, u32 unlock_dll, u32 f, u32 inc,
+ u32 sdrc_rfr_ctrl_0, u32 sdrc_actim_ctrl_a_0,
+ u32 sdrc_actim_ctrl_b_0, u32 sdrc_mr_0,
+ u32 sdrc_rfr_ctrl_1, u32 sdrc_actim_ctrl_a_1,
+ u32 sdrc_actim_ctrl_b_1, u32 sdrc_mr_1);
/* Do not use these */
extern void omap1_sram_reprogram_clock(u32 ckctl, u32 dpllctl);
@@ -59,12 +60,12 @@ extern void omap243x_sram_reprogram_sdrc(u32 perf_level, u32 dll_val,
u32 mem_type);
extern unsigned long omap243x_sram_reprogram_sdrc_sz;
-
-extern u32 omap3_sram_configure_core_dpll(u32 sdrc_rfr_ctrl,
- u32 sdrc_actim_ctrla,
- u32 sdrc_actim_ctrlb, u32 m2,
- u32 unlock_dll, u32 f, u32 sdrc_mr,
- u32 inc);
+extern u32 omap3_sram_configure_core_dpll(
+ u32 m2, u32 unlock_dll, u32 f, u32 inc,
+ u32 sdrc_rfr_ctrl_0, u32 sdrc_actim_ctrl_a_0,
+ u32 sdrc_actim_ctrl_b_0, u32 sdrc_mr_0,
+ u32 sdrc_rfr_ctrl_1, u32 sdrc_actim_ctrl_a_1,
+ u32 sdrc_actim_ctrl_b_1, u32 sdrc_mr_1);
extern unsigned long omap3_sram_configure_core_dpll_sz;
#endif
diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c
index efa0e0111f3..8dc7927906f 100644
--- a/arch/arm/plat-omap/mcbsp.c
+++ b/arch/arm/plat-omap/mcbsp.c
@@ -198,6 +198,170 @@ void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
}
EXPORT_SYMBOL(omap_mcbsp_config);
+#ifdef CONFIG_ARCH_OMAP34XX
+/*
+ * omap_mcbsp_set_tx_threshold configures how to deal
+ * with transmit threshold. the threshold value and handler can be
+ * configure in here.
+ */
+void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
+{
+ struct omap_mcbsp *mcbsp;
+ void __iomem *io_base;
+
+ if (!cpu_is_omap34xx())
+ return;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+ io_base = mcbsp->io_base;
+
+ OMAP_MCBSP_WRITE(io_base, THRSH2, threshold);
+}
+EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
+
+/*
+ * omap_mcbsp_set_rx_threshold configures how to deal
+ * with receive threshold. the threshold value and handler can be
+ * configure in here.
+ */
+void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
+{
+ struct omap_mcbsp *mcbsp;
+ void __iomem *io_base;
+
+ if (!cpu_is_omap34xx())
+ return;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+ io_base = mcbsp->io_base;
+
+ OMAP_MCBSP_WRITE(io_base, THRSH1, threshold);
+}
+EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
+
+/*
+ * omap_mcbsp_get_max_tx_thres just return the current configured
+ * maximum threshold for transmission
+ */
+u16 omap_mcbsp_get_max_tx_threshold(unsigned int id)
+{
+ struct omap_mcbsp *mcbsp;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+
+ return mcbsp->max_tx_thres;
+}
+EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold);
+
+/*
+ * omap_mcbsp_get_max_rx_thres just return the current configured
+ * maximum threshold for reception
+ */
+u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
+{
+ struct omap_mcbsp *mcbsp;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+
+ return mcbsp->max_rx_thres;
+}
+EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
+
+/*
+ * omap_mcbsp_get_dma_op_mode just return the current configured
+ * operating mode for the mcbsp channel
+ */
+int omap_mcbsp_get_dma_op_mode(unsigned int id)
+{
+ struct omap_mcbsp *mcbsp;
+ int dma_op_mode;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+
+ spin_lock_irq(&mcbsp->lock);
+ dma_op_mode = mcbsp->dma_op_mode;
+ spin_unlock_irq(&mcbsp->lock);
+
+ return dma_op_mode;
+}
+EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode);
+
+static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp)
+{
+ /*
+ * Enable wakup behavior, smart idle and all wakeups
+ * REVISIT: some wakeups may be unnecessary
+ */
+ if (cpu_is_omap34xx()) {
+ u16 syscon;
+
+ syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
+ syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
+
+ spin_lock_irq(&mcbsp->lock);
+ if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
+ syscon |= (ENAWAKEUP | SIDLEMODE(0x02) |
+ CLOCKACTIVITY(0x02));
+ OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN,
+ XRDYEN | RRDYEN);
+ } else {
+ syscon |= SIDLEMODE(0x01);
+ }
+ spin_unlock_irq(&mcbsp->lock);
+
+ OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
+ }
+}
+
+static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
+{
+ /*
+ * Disable wakup behavior, smart idle and all wakeups
+ */
+ if (cpu_is_omap34xx()) {
+ u16 syscon;
+
+ syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
+ syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
+ /*
+ * HW bug workaround - If no_idle mode is taken, we need to
+ * go to smart_idle before going to always_idle, or the
+ * device will not hit retention anymore.
+ */
+ syscon |= SIDLEMODE(0x02);
+ OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
+
+ syscon &= ~(SIDLEMODE(0x03));
+ OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
+
+ OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN, 0);
+ }
+}
+#else
+static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
+static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
+#endif
+
/*
* We can choose between IRQ based or polled IO.
* This needs to be called before omap_mcbsp_request().
@@ -257,6 +421,9 @@ int omap_mcbsp_request(unsigned int id)
clk_enable(mcbsp->iclk);
clk_enable(mcbsp->fclk);
+ /* Do procedure specific to omap34xx arch, if applicable */
+ omap34xx_mcbsp_request(mcbsp);
+
/*
* Make sure that transmitter, receiver and sample-rate generator are
* not running before activating IRQs.
@@ -305,6 +472,9 @@ void omap_mcbsp_free(unsigned int id)
if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
mcbsp->pdata->ops->free(id);
+ /* Do procedure specific to omap34xx arch, if applicable */
+ omap34xx_mcbsp_free(mcbsp);
+
clk_disable(mcbsp->fclk);
clk_disable(mcbsp->iclk);
@@ -328,14 +498,15 @@ void omap_mcbsp_free(unsigned int id)
EXPORT_SYMBOL(omap_mcbsp_free);
/*
- * Here we start the McBSP, by enabling the sample
- * generator, both transmitter and receivers,
- * and the frame sync.
+ * Here we start the McBSP, by enabling transmitter, receiver or both.
+ * If no transmitter or receiver is active prior calling, then sample-rate
+ * generator and frame sync are started.
*/
-void omap_mcbsp_start(unsigned int id)
+void omap_mcbsp_start(unsigned int id, int tx, int rx)
{
struct omap_mcbsp *mcbsp;
void __iomem *io_base;
+ int idle;
u16 w;
if (!omap_mcbsp_check_valid_id(id)) {
@@ -348,32 +519,58 @@ void omap_mcbsp_start(unsigned int id)
mcbsp->rx_word_length = (OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7;
mcbsp->tx_word_length = (OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7;
- /* Start the sample generator */
- w = OMAP_MCBSP_READ(io_base, SPCR2);
- OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6));
+ idle = !((OMAP_MCBSP_READ(io_base, SPCR2) |
+ OMAP_MCBSP_READ(io_base, SPCR1)) & 1);
+
+ if (idle) {
+ /* Start the sample generator */
+ w = OMAP_MCBSP_READ(io_base, SPCR2);
+ OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6));
+ }
/* Enable transmitter and receiver */
+ tx &= 1;
w = OMAP_MCBSP_READ(io_base, SPCR2);
- OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1);
+ OMAP_MCBSP_WRITE(io_base, SPCR2, w | tx);
+ rx &= 1;
w = OMAP_MCBSP_READ(io_base, SPCR1);
- OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1);
+ OMAP_MCBSP_WRITE(io_base, SPCR1, w | rx);
- udelay(100);
+ /*
+ * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
+ * REVISIT: 100us may give enough time for two CLKSRG, however
+ * due to some unknown PM related, clock gating etc. reason it
+ * is now at 500us.
+ */
+ udelay(500);
- /* Start frame sync */
- w = OMAP_MCBSP_READ(io_base, SPCR2);
- OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7));
+ if (idle) {
+ /* Start frame sync */
+ w = OMAP_MCBSP_READ(io_base, SPCR2);
+ OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7));
+ }
+
+ if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+ /* Release the transmitter and receiver */
+ w = OMAP_MCBSP_READ(io_base, XCCR);
+ w &= ~(tx ? XDISABLE : 0);
+ OMAP_MCBSP_WRITE(io_base, XCCR, w);
+ w = OMAP_MCBSP_READ(io_base, RCCR);
+ w &= ~(rx ? RDISABLE : 0);
+ OMAP_MCBSP_WRITE(io_base, RCCR, w);
+ }
/* Dump McBSP Regs */
omap_mcbsp_dump_reg(id);
}
EXPORT_SYMBOL(omap_mcbsp_start);
-void omap_mcbsp_stop(unsigned int id)
+void omap_mcbsp_stop(unsigned int id, int tx, int rx)
{
struct omap_mcbsp *mcbsp;
void __iomem *io_base;
+ int idle;
u16 w;
if (!omap_mcbsp_check_valid_id(id)) {
@@ -385,16 +582,33 @@ void omap_mcbsp_stop(unsigned int id)
io_base = mcbsp->io_base;
/* Reset transmitter */
+ tx &= 1;
+ if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+ w = OMAP_MCBSP_READ(io_base, XCCR);
+ w |= (tx ? XDISABLE : 0);
+ OMAP_MCBSP_WRITE(io_base, XCCR, w);
+ }
w = OMAP_MCBSP_READ(io_base, SPCR2);
- OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1));
+ OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~tx);
/* Reset receiver */
+ rx &= 1;
+ if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+ w = OMAP_MCBSP_READ(io_base, RCCR);
+ w |= (tx ? RDISABLE : 0);
+ OMAP_MCBSP_WRITE(io_base, RCCR, w);
+ }
w = OMAP_MCBSP_READ(io_base, SPCR1);
- OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1));
+ OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~rx);
- /* Reset the sample rate generator */
- w = OMAP_MCBSP_READ(io_base, SPCR2);
- OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6));
+ idle = !((OMAP_MCBSP_READ(io_base, SPCR2) |
+ OMAP_MCBSP_READ(io_base, SPCR1)) & 1);
+
+ if (idle) {
+ /* Reset the sample rate generator */
+ w = OMAP_MCBSP_READ(io_base, SPCR2);
+ OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6));
+ }
}
EXPORT_SYMBOL(omap_mcbsp_stop);
@@ -883,6 +1097,149 @@ void omap_mcbsp_set_spi_mode(unsigned int id,
}
EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
+#ifdef CONFIG_ARCH_OMAP34XX
+#define max_thres(m) (mcbsp->pdata->buffer_size)
+#define valid_threshold(m, val) ((val) <= max_thres(m))
+#define THRESHOLD_PROP_BUILDER(prop) \
+static ssize_t prop##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%u\n", mcbsp->prop); \
+} \
+ \
+static ssize_t prop##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t size) \
+{ \
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
+ unsigned long val; \
+ int status; \
+ \
+ status = strict_strtoul(buf, 0, &val); \
+ if (status) \
+ return status; \
+ \
+ if (!valid_threshold(mcbsp, val)) \
+ return -EDOM; \
+ \
+ mcbsp->prop = val; \
+ return size; \
+} \
+ \
+static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
+
+THRESHOLD_PROP_BUILDER(max_tx_thres);
+THRESHOLD_PROP_BUILDER(max_rx_thres);
+
+static const char *dma_op_modes[] = {
+ "element", "threshold", "frame",
+};
+
+static ssize_t dma_op_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+ int dma_op_mode, i = 0;
+ ssize_t len = 0;
+ const char * const *s;
+
+ spin_lock_irq(&mcbsp->lock);
+ dma_op_mode = mcbsp->dma_op_mode;
+ spin_unlock_irq(&mcbsp->lock);
+
+ for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
+ if (dma_op_mode == i)
+ len += sprintf(buf + len, "[%s] ", *s);
+ else
+ len += sprintf(buf + len, "%s ", *s);
+ }
+ len += sprintf(buf + len, "\n");
+
+ return len;
+}
+
+static ssize_t dma_op_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+ const char * const *s;
+ int i = 0;
+
+ for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
+ if (sysfs_streq(buf, *s))
+ break;
+
+ if (i == ARRAY_SIZE(dma_op_modes))
+ return -EINVAL;
+
+ spin_lock_irq(&mcbsp->lock);
+ if (!mcbsp->free) {
+ size = -EBUSY;
+ goto unlock;
+ }
+ mcbsp->dma_op_mode = i;
+
+unlock:
+ spin_unlock_irq(&mcbsp->lock);
+
+ return size;
+}
+
+static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
+
+static const struct attribute *additional_attrs[] = {
+ &dev_attr_max_tx_thres.attr,
+ &dev_attr_max_rx_thres.attr,
+ &dev_attr_dma_op_mode.attr,
+ NULL,
+};
+
+static const struct attribute_group additional_attr_group = {
+ .attrs = (struct attribute **)additional_attrs,
+};
+
+static inline int __devinit omap_additional_add(struct device *dev)
+{
+ return sysfs_create_group(&dev->kobj, &additional_attr_group);
+}
+
+static inline void __devexit omap_additional_remove(struct device *dev)
+{
+ sysfs_remove_group(&dev->kobj, &additional_attr_group);
+}
+
+static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
+{
+ mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
+ if (cpu_is_omap34xx()) {
+ mcbsp->max_tx_thres = max_thres(mcbsp);
+ mcbsp->max_rx_thres = max_thres(mcbsp);
+ /*
+ * REVISIT: Set dmap_op_mode to THRESHOLD as default
+ * for mcbsp2 instances.
+ */
+ if (omap_additional_add(mcbsp->dev))
+ dev_warn(mcbsp->dev,
+ "Unable to create additional controls\n");
+ } else {
+ mcbsp->max_tx_thres = -EINVAL;
+ mcbsp->max_rx_thres = -EINVAL;
+ }
+}
+
+static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
+{
+ if (cpu_is_omap34xx())
+ omap_additional_remove(mcbsp->dev);
+}
+#else
+static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}
+static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) {}
+#endif /* CONFIG_ARCH_OMAP34XX */
+
/*
* McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
* 730 has only 2 McBSP, and both of them are MPU peripherals.
@@ -953,6 +1310,10 @@ static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
mcbsp->dev = &pdev->dev;
mcbsp_ptr[id] = mcbsp;
platform_set_drvdata(pdev, mcbsp);
+
+ /* Initialize mcbsp properties for OMAP34XX if needed / applicable */
+ omap34xx_device_init(mcbsp);
+
return 0;
err_fclk:
@@ -976,6 +1337,8 @@ static int __devexit omap_mcbsp_remove(struct platform_device *pdev)
mcbsp->pdata->ops->free)
mcbsp->pdata->ops->free(mcbsp->id);
+ omap34xx_device_exit(mcbsp);
+
clk_disable(mcbsp->fclk);
clk_disable(mcbsp->iclk);
clk_put(mcbsp->fclk);
diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c
index 4ea73804d21..5eae7876979 100644
--- a/arch/arm/plat-omap/sram.c
+++ b/arch/arm/plat-omap/sram.c
@@ -44,9 +44,9 @@
#define OMAP2_SRAM_VA 0xe3000000
#define OMAP2_SRAM_PUB_VA (OMAP2_SRAM_VA + 0x800)
#define OMAP3_SRAM_PA 0x40200000
-#define OMAP3_SRAM_VA 0xd7000000
+#define OMAP3_SRAM_VA 0xe3000000
#define OMAP3_SRAM_PUB_PA 0x40208000
-#define OMAP3_SRAM_PUB_VA 0xd7008000
+#define OMAP3_SRAM_PUB_VA (OMAP3_SRAM_VA + 0x8000)
#define OMAP4_SRAM_PA 0x40200000 /*0x402f0000*/
#define OMAP4_SRAM_VA 0xd7000000 /*0xd70f0000*/
@@ -373,20 +373,26 @@ static inline int omap243x_sram_init(void)
#ifdef CONFIG_ARCH_OMAP3
-static u32 (*_omap3_sram_configure_core_dpll)(u32 sdrc_rfr_ctrl,
- u32 sdrc_actim_ctrla,
- u32 sdrc_actim_ctrlb,
- u32 m2, u32 unlock_dll,
- u32 f, u32 sdrc_mr, u32 inc);
-u32 omap3_configure_core_dpll(u32 sdrc_rfr_ctrl, u32 sdrc_actim_ctrla,
- u32 sdrc_actim_ctrlb, u32 m2, u32 unlock_dll,
- u32 f, u32 sdrc_mr, u32 inc)
+static u32 (*_omap3_sram_configure_core_dpll)(
+ u32 m2, u32 unlock_dll, u32 f, u32 inc,
+ u32 sdrc_rfr_ctrl_0, u32 sdrc_actim_ctrl_a_0,
+ u32 sdrc_actim_ctrl_b_0, u32 sdrc_mr_0,
+ u32 sdrc_rfr_ctrl_1, u32 sdrc_actim_ctrl_a_1,
+ u32 sdrc_actim_ctrl_b_1, u32 sdrc_mr_1);
+
+u32 omap3_configure_core_dpll(u32 m2, u32 unlock_dll, u32 f, u32 inc,
+ u32 sdrc_rfr_ctrl_0, u32 sdrc_actim_ctrl_a_0,
+ u32 sdrc_actim_ctrl_b_0, u32 sdrc_mr_0,
+ u32 sdrc_rfr_ctrl_1, u32 sdrc_actim_ctrl_a_1,
+ u32 sdrc_actim_ctrl_b_1, u32 sdrc_mr_1)
{
BUG_ON(!_omap3_sram_configure_core_dpll);
- return _omap3_sram_configure_core_dpll(sdrc_rfr_ctrl,
- sdrc_actim_ctrla,
- sdrc_actim_ctrlb, m2,
- unlock_dll, f, sdrc_mr, inc);
+ return _omap3_sram_configure_core_dpll(
+ m2, unlock_dll, f, inc,
+ sdrc_rfr_ctrl_0, sdrc_actim_ctrl_a_0,
+ sdrc_actim_ctrl_b_0, sdrc_mr_0,
+ sdrc_rfr_ctrl_1, sdrc_actim_ctrl_a_1,
+ sdrc_actim_ctrl_b_1, sdrc_mr_1);
}
/* REVISIT: Should this be same as omap34xx_sram_init() after off-idle? */
diff --git a/arch/arm/plat-orion/include/plat/gpio.h b/arch/arm/plat-orion/include/plat/gpio.h
index 9646a94ed3d..07c430fdc9e 100644
--- a/arch/arm/plat-orion/include/plat/gpio.h
+++ b/arch/arm/plat-orion/include/plat/gpio.h
@@ -11,6 +11,8 @@
#ifndef __PLAT_GPIO_H
#define __PLAT_GPIO_H
+#include <linux/init.h>
+
/*
* GENERIC_GPIO primitives.
*/
diff --git a/arch/arm/plat-s3c/include/plat/audio-simtec.h b/arch/arm/plat-s3c/include/plat/audio-simtec.h
new file mode 100644
index 00000000000..0f440b9168d
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/audio-simtec.h
@@ -0,0 +1,37 @@
+/* arch/arm/plat-s3c/include/plat/audio-simtec.h
+ *
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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.
+ *
+ * Simtec Audio support.
+*/
+
+/**
+ * struct s3c24xx_audio_simtec_pdata - platform data for simtec audio
+ * @use_mpllin: Select codec clock from MPLLin
+ * @output_cdclk: Need to output CDCLK to the codec
+ * @have_mic: Set if we have a MIC socket
+ * @have_lout: Set if we have a LineOut socket
+ * @amp_gpio: GPIO pin to enable the AMP
+ * @amp_gain: Option GPIO to control AMP gain
+ */
+struct s3c24xx_audio_simtec_pdata {
+ unsigned int use_mpllin:1;
+ unsigned int output_cdclk:1;
+
+ unsigned int have_mic:1;
+ unsigned int have_lout:1;
+
+ int amp_gpio;
+ int amp_gain[2];
+
+ void (*startup)(void);
+};
+
+extern int simtec_audio_add(const char *codec_name,
+ struct s3c24xx_audio_simtec_pdata *pdata);
diff --git a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
index 0fad7571030..07659dad174 100644
--- a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
+++ b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
@@ -33,6 +33,11 @@
#define S3C2412_IISCON_RXDMA_ACTIVE (1 << 1)
#define S3C2412_IISCON_IIS_ACTIVE (1 << 0)
+#define S3C64XX_IISMOD_BLC_16BIT (0 << 13)
+#define S3C64XX_IISMOD_BLC_8BIT (1 << 13)
+#define S3C64XX_IISMOD_BLC_24BIT (2 << 13)
+#define S3C64XX_IISMOD_BLC_MASK (3 << 13)
+
#define S3C64XX_IISMOD_IMS_PCLK (0 << 10)
#define S3C64XX_IISMOD_IMS_SYSMUX (1 << 10)
diff --git a/arch/arm/plat-s3c24xx/clock-dclk.c b/arch/arm/plat-s3c24xx/clock-dclk.c
index 5b75a797b5a..0afb217a775 100644
--- a/arch/arm/plat-s3c24xx/clock-dclk.c
+++ b/arch/arm/plat-s3c24xx/clock-dclk.c
@@ -129,7 +129,7 @@ static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent)
/* calculate the MISCCR setting for the clock */
- if (parent == &clk_xtal)
+ if (parent == &clk_mpll)
source = S3C2410_MISCCR_CLK0_MPLL;
else if (parent == &clk_upll)
source = S3C2410_MISCCR_CLK0_UPLL;
diff --git a/arch/arm/plat-s3c24xx/pwm.c b/arch/arm/plat-s3c24xx/pwm.c
index 0120b760315..82a6d4de02a 100644
--- a/arch/arm/plat-s3c24xx/pwm.c
+++ b/arch/arm/plat-s3c24xx/pwm.c
@@ -246,6 +246,10 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
tcmp = duty_ns / tin_ns;
tcmp = tcnt - tcmp;
+ /* the pwm hw only checks the compare register after a decrement,
+ so the pin never toggles if tcmp = tcnt */
+ if (tcmp == tcnt)
+ tcmp--;
pwm_dbg(pwm, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt);
diff --git a/arch/arm/plat-s3c64xx/pm.c b/arch/arm/plat-s3c64xx/pm.c
index 07a6516a4f3..47632fc7eb6 100644
--- a/arch/arm/plat-s3c64xx/pm.c
+++ b/arch/arm/plat-s3c64xx/pm.c
@@ -117,8 +117,6 @@ void s3c_pm_save_core(void)
* this.
*/
-#include <plat/regs-gpio.h>
-
static void s3c64xx_cpu_suspend(void)
{
unsigned long tmp;
diff --git a/arch/arm/plat-s3c64xx/s3c6400-clock.c b/arch/arm/plat-s3c64xx/s3c6400-clock.c
index 1debc1f9f98..febac1950d8 100644
--- a/arch/arm/plat-s3c64xx/s3c6400-clock.c
+++ b/arch/arm/plat-s3c64xx/s3c6400-clock.c
@@ -153,7 +153,7 @@ static unsigned long s3c64xx_clk_arm_round_rate(struct clk *clk,
u32 div;
if (parent < rate)
- return rate;
+ return parent;
div = (parent / rate) - 1;
if (div > armclk_mask)
@@ -175,7 +175,7 @@ static int s3c64xx_clk_arm_set_rate(struct clk *clk, unsigned long rate)
div = clk_get_rate(clk->parent) / rate;
val = __raw_readl(S3C_CLK_DIV0);
- val &= armclk_mask;
+ val &= ~armclk_mask;
val |= (div - 1);
__raw_writel(val, S3C_CLK_DIV0);
diff --git a/arch/arm/plat-stmp3xxx/pinmux.c b/arch/arm/plat-stmp3xxx/pinmux.c
index d4120038220..6d6b1a468ed 100644
--- a/arch/arm/plat-stmp3xxx/pinmux.c
+++ b/arch/arm/plat-stmp3xxx/pinmux.c
@@ -22,7 +22,6 @@
#include <linux/sysdev.h>
#include <linux/string.h>
#include <linux/bitops.h>
-#include <linux/sysdev.h>
#include <linux/irq.h>
#include <mach/hardware.h>
diff --git a/arch/avr32/boards/favr-32/setup.c b/arch/avr32/boards/favr-32/setup.c
index 46c9b0a224c..75f19f47fb2 100644
--- a/arch/avr32/boards/favr-32/setup.c
+++ b/arch/avr32/boards/favr-32/setup.c
@@ -72,6 +72,10 @@ static struct ads7846_platform_data ads7843_data = {
.debounce_max = 20,
.debounce_rep = 4,
.debounce_tol = 5,
+
+ .keep_vref_on = true,
+ .settle_delay_usecs = 500,
+ .penirq_recheck_delay_usecs = 100,
};
static struct spi_board_info __initdata spi1_board_info[] = {
diff --git a/arch/avr32/lib/memcpy.S b/arch/avr32/lib/memcpy.S
index 0abb26142b6..c2ca49d705a 100644
--- a/arch/avr32/lib/memcpy.S
+++ b/arch/avr32/lib/memcpy.S
@@ -24,8 +24,8 @@ memcpy:
brne 1f
/* At this point, "from" is word-aligned */
-2: sub r10, 4
- mov r9, r12
+2: mov r9, r12
+5: sub r10, 4
brlt 4f
3: ld.w r8, r11++
@@ -49,6 +49,7 @@ memcpy:
/* Handle unaligned "from" pointer */
1: sub r10, 4
+ movlt r9, r12
brlt 4b
add r10, r9
lsl r9, 2
@@ -59,4 +60,13 @@ memcpy:
st.b r12++, r8
ld.ub r8, r11++
st.b r12++, r8
- rjmp 2b
+ mov r8, r12
+ add pc, pc, r9
+ sub r8, 1
+ nop
+ sub r8, 1
+ nop
+ sub r8, 1
+ nop
+ mov r9, r8
+ rjmp 5b
diff --git a/arch/ia64/Makefile b/arch/ia64/Makefile
index 58a7e46affd..e7cbaa02cd0 100644
--- a/arch/ia64/Makefile
+++ b/arch/ia64/Makefile
@@ -41,11 +41,6 @@ $(error Sorry, you need a newer version of the assember, one that is built from
ftp://ftp.hpl.hp.com/pub/linux-ia64/gas-030124.tar.gz)
endif
-ifeq ($(call cc-version),0304)
- cflags-$(CONFIG_ITANIUM) += -mtune=merced
- cflags-$(CONFIG_MCKINLEY) += -mtune=mckinley
-endif
-
KBUILD_CFLAGS += $(cflags-y)
head-y := arch/ia64/kernel/head.o arch/ia64/kernel/init_task.o
diff --git a/arch/ia64/include/asm/bitops.h b/arch/ia64/include/asm/bitops.h
index e2ca8003733..57a2787bc9f 100644
--- a/arch/ia64/include/asm/bitops.h
+++ b/arch/ia64/include/asm/bitops.h
@@ -286,7 +286,7 @@ __test_and_clear_bit(int nr, volatile void * addr)
{
__u32 *p = (__u32 *) addr + (nr >> 5);
__u32 m = 1 << (nr & 31);
- int oldbitset = *p & m;
+ int oldbitset = (*p & m) != 0;
*p &= ~m;
return oldbitset;
diff --git a/arch/ia64/include/asm/pgtable.h b/arch/ia64/include/asm/pgtable.h
index 0a9cc73d35c..8840a690d1e 100644
--- a/arch/ia64/include/asm/pgtable.h
+++ b/arch/ia64/include/asm/pgtable.h
@@ -155,7 +155,6 @@
#include <linux/bitops.h>
#include <asm/cacheflush.h>
#include <asm/mmu_context.h>
-#include <asm/processor.h>
/*
* Next come the mappings that determine how mmap() protection bits
diff --git a/arch/ia64/kernel/dma-mapping.c b/arch/ia64/kernel/dma-mapping.c
index 39a3cd0a417..f2c1600da09 100644
--- a/arch/ia64/kernel/dma-mapping.c
+++ b/arch/ia64/kernel/dma-mapping.c
@@ -10,7 +10,9 @@ EXPORT_SYMBOL(dma_ops);
static int __init dma_init(void)
{
- dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES);
+ dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES);
+
+ return 0;
}
fs_initcall(dma_init);
diff --git a/arch/ia64/kernel/ia64_ksyms.c b/arch/ia64/kernel/ia64_ksyms.c
index 2d311864e35..8ebccb589e1 100644
--- a/arch/ia64/kernel/ia64_ksyms.c
+++ b/arch/ia64/kernel/ia64_ksyms.c
@@ -21,6 +21,7 @@ EXPORT_SYMBOL(csum_ipv6_magic);
#include <asm/page.h>
EXPORT_SYMBOL(clear_page);
+EXPORT_SYMBOL(copy_page);
#ifdef CONFIG_VIRTUAL_MEM_MAP
#include <linux/bootmem.h>
@@ -60,9 +61,6 @@ EXPORT_SYMBOL(__udivdi3);
EXPORT_SYMBOL(__moddi3);
EXPORT_SYMBOL(__umoddi3);
-#include <asm/page.h>
-EXPORT_SYMBOL(copy_page);
-
#if defined(CONFIG_MD_RAID456) || defined(CONFIG_MD_RAID456_MODULE)
extern void xor_ia64_2(void);
extern void xor_ia64_3(void);
diff --git a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c
index c48b03f2b61..dab4d393908 100644
--- a/arch/ia64/kernel/iosapic.c
+++ b/arch/ia64/kernel/iosapic.c
@@ -1072,6 +1072,10 @@ iosapic_init (unsigned long phys_addr, unsigned int gsi_base)
}
addr = ioremap(phys_addr, 0);
+ if (addr == NULL) {
+ spin_unlock_irqrestore(&iosapic_lock, flags);
+ return -ENOMEM;
+ }
ver = iosapic_version(addr);
if ((err = iosapic_check_gsi_range(gsi_base, ver))) {
iounmap(addr);
diff --git a/arch/ia64/kernel/pci-dma.c b/arch/ia64/kernel/pci-dma.c
index 05695962fe4..f6b1ff0aea7 100644
--- a/arch/ia64/kernel/pci-dma.c
+++ b/arch/ia64/kernel/pci-dma.c
@@ -69,11 +69,6 @@ iommu_dma_init(void)
int iommu_dma_supported(struct device *dev, u64 mask)
{
- struct dma_map_ops *ops = platform_dma_get_ops(dev);
-
- if (ops->dma_supported)
- return ops->dma_supported(dev, mask);
-
/* Copied from i386. Doesn't make much sense, because it will
only work for pci_alloc_coherent.
The caller just has to use GFP_DMA in this case. */
diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c
index bc80dff1df7..8f060352e12 100644
--- a/arch/ia64/kernel/topology.c
+++ b/arch/ia64/kernel/topology.c
@@ -372,6 +372,10 @@ static int __cpuinit cache_add_dev(struct sys_device * sys_dev)
retval = kobject_init_and_add(&all_cpu_cache_info[cpu].kobj,
&cache_ktype_percpu_entry, &sys_dev->kobj,
"%s", "cache");
+ if (unlikely(retval < 0)) {
+ cpu_cache_sysfs_exit(cpu);
+ return retval;
+ }
for (i = 0; i < all_cpu_cache_info[cpu].num_cache_leaves; i++) {
this_object = LEAF_KOBJECT_PTR(cpu,i);
@@ -385,7 +389,7 @@ static int __cpuinit cache_add_dev(struct sys_device * sys_dev)
}
kobject_put(&all_cpu_cache_info[cpu].kobj);
cpu_cache_sysfs_exit(cpu);
- break;
+ return retval;
}
kobject_uevent(&(this_object->kobj), KOBJ_ADD);
}
diff --git a/arch/ia64/kvm/mmio.c b/arch/ia64/kvm/mmio.c
index 21f63fffc37..9bf55afd08d 100644
--- a/arch/ia64/kvm/mmio.c
+++ b/arch/ia64/kvm/mmio.c
@@ -247,7 +247,8 @@ void emulate_io_inst(struct kvm_vcpu *vcpu, u64 padr, u64 ma)
vcpu_get_fpreg(vcpu, inst.M9.f2, &v);
/* Write high word. FIXME: this is a kludge! */
v.u.bits[1] &= 0x3ffff;
- mmio_access(vcpu, padr + 8, &v.u.bits[1], 8, ma, IOREQ_WRITE);
+ mmio_access(vcpu, padr + 8, (u64 *)&v.u.bits[1], 8,
+ ma, IOREQ_WRITE);
data = v.u.bits[0];
size = 3;
} else if (inst.M10.major == 7 && inst.M10.x6 == 0x3B) {
@@ -265,7 +266,8 @@ void emulate_io_inst(struct kvm_vcpu *vcpu, u64 padr, u64 ma)
/* Write high word.FIXME: this is a kludge! */
v.u.bits[1] &= 0x3ffff;
- mmio_access(vcpu, padr + 8, &v.u.bits[1], 8, ma, IOREQ_WRITE);
+ mmio_access(vcpu, padr + 8, (u64 *)&v.u.bits[1],
+ 8, ma, IOREQ_WRITE);
data = v.u.bits[0];
size = 3;
} else if (inst.M10.major == 7 && inst.M10.x6 == 0x31) {
diff --git a/arch/ia64/kvm/vcpu.c b/arch/ia64/kvm/vcpu.c
index 46b02cbcc87..cc406d064a0 100644
--- a/arch/ia64/kvm/vcpu.c
+++ b/arch/ia64/kvm/vcpu.c
@@ -461,7 +461,7 @@ void setreg(unsigned long regnum, unsigned long val,
u64 vcpu_get_gr(struct kvm_vcpu *vcpu, unsigned long reg)
{
struct kvm_pt_regs *regs = vcpu_regs(vcpu);
- u64 val;
+ unsigned long val;
if (!reg)
return 0;
@@ -469,7 +469,7 @@ u64 vcpu_get_gr(struct kvm_vcpu *vcpu, unsigned long reg)
return val;
}
-void vcpu_set_gr(struct kvm_vcpu *vcpu, u64 reg, u64 value, int nat)
+void vcpu_set_gr(struct kvm_vcpu *vcpu, unsigned long reg, u64 value, int nat)
{
struct kvm_pt_regs *regs = vcpu_regs(vcpu);
long sof = (regs->cr_ifs) & 0x7f;
@@ -1072,7 +1072,7 @@ void kvm_ttag(struct kvm_vcpu *vcpu, INST64 inst)
vcpu_set_gr(vcpu, inst.M46.r1, tag, 0);
}
-int vcpu_tpa(struct kvm_vcpu *vcpu, u64 vadr, u64 *padr)
+int vcpu_tpa(struct kvm_vcpu *vcpu, u64 vadr, unsigned long *padr)
{
struct thash_data *data;
union ia64_isr visr, pt_isr;
diff --git a/arch/ia64/kvm/vcpu.h b/arch/ia64/kvm/vcpu.h
index 042af92ced8..360724d3ae6 100644
--- a/arch/ia64/kvm/vcpu.h
+++ b/arch/ia64/kvm/vcpu.h
@@ -686,14 +686,15 @@ static inline int highest_inservice_irq(struct kvm_vcpu *vcpu)
return highest_bits((int *)&(VMX(vcpu, insvc[0])));
}
-extern void vcpu_get_fpreg(struct kvm_vcpu *vcpu, u64 reg,
+extern void vcpu_get_fpreg(struct kvm_vcpu *vcpu, unsigned long reg,
struct ia64_fpreg *val);
-extern void vcpu_set_fpreg(struct kvm_vcpu *vcpu, u64 reg,
+extern void vcpu_set_fpreg(struct kvm_vcpu *vcpu, unsigned long reg,
struct ia64_fpreg *val);
-extern u64 vcpu_get_gr(struct kvm_vcpu *vcpu, u64 reg);
-extern void vcpu_set_gr(struct kvm_vcpu *vcpu, u64 reg, u64 val, int nat);
-extern u64 vcpu_get_psr(struct kvm_vcpu *vcpu);
-extern void vcpu_set_psr(struct kvm_vcpu *vcpu, u64 val);
+extern u64 vcpu_get_gr(struct kvm_vcpu *vcpu, unsigned long reg);
+extern void vcpu_set_gr(struct kvm_vcpu *vcpu, unsigned long reg,
+ u64 val, int nat);
+extern unsigned long vcpu_get_psr(struct kvm_vcpu *vcpu);
+extern void vcpu_set_psr(struct kvm_vcpu *vcpu, unsigned long val);
extern u64 vcpu_thash(struct kvm_vcpu *vcpu, u64 vadr);
extern void vcpu_bsw0(struct kvm_vcpu *vcpu);
extern void thash_vhpt_insert(struct kvm_vcpu *v, u64 pte,
diff --git a/arch/ia64/lib/ip_fast_csum.S b/arch/ia64/lib/ip_fast_csum.S
index 1f86aeb2c94..620d9dc5220 100644
--- a/arch/ia64/lib/ip_fast_csum.S
+++ b/arch/ia64/lib/ip_fast_csum.S
@@ -96,20 +96,22 @@ END(ip_fast_csum)
GLOBAL_ENTRY(csum_ipv6_magic)
ld4 r20=[in0],4
ld4 r21=[in1],4
- dep r15=in3,in2,32,16
+ zxt4 in2=in2
;;
ld4 r22=[in0],4
ld4 r23=[in1],4
- mux1 r15=r15,@rev
+ dep r15=in3,in2,32,16
;;
ld4 r24=[in0],4
ld4 r25=[in1],4
- shr.u r15=r15,16
+ mux1 r15=r15,@rev
add r16=r20,r21
add r17=r22,r23
+ zxt4 in4=in4
;;
ld4 r26=[in0],4
ld4 r27=[in1],4
+ shr.u r15=r15,16
add r18=r24,r25
add r8=r16,r17
;;
diff --git a/arch/m68k/amiga/config.c b/arch/m68k/amiga/config.c
index 6e562751ad5..6c74751c7b8 100644
--- a/arch/m68k/amiga/config.c
+++ b/arch/m68k/amiga/config.c
@@ -574,10 +574,11 @@ static int a2000_hwclk(int op, struct rtc_time *t)
tod_2000.cntrl1 = TOD2000_CNTRL1_HOLD;
- while ((tod_2000.cntrl1 & TOD2000_CNTRL1_BUSY) && cnt--) {
+ while ((tod_2000.cntrl1 & TOD2000_CNTRL1_BUSY) && cnt) {
tod_2000.cntrl1 &= ~TOD2000_CNTRL1_HOLD;
udelay(70);
tod_2000.cntrl1 |= TOD2000_CNTRL1_HOLD;
+ --cnt;
}
if (!cnt)
@@ -649,10 +650,11 @@ static int amiga_set_clock_mmss(unsigned long nowtime)
tod_2000.cntrl1 |= TOD2000_CNTRL1_HOLD;
- while ((tod_2000.cntrl1 & TOD2000_CNTRL1_BUSY) && cnt--) {
+ while ((tod_2000.cntrl1 & TOD2000_CNTRL1_BUSY) && cnt) {
tod_2000.cntrl1 &= ~TOD2000_CNTRL1_HOLD;
udelay(70);
tod_2000.cntrl1 |= TOD2000_CNTRL1_HOLD;
+ --cnt;
}
if (!cnt)
diff --git a/arch/m68k/include/asm/motorola_pgalloc.h b/arch/m68k/include/asm/motorola_pgalloc.h
index 15ee4c74a9f..2f02f264e69 100644
--- a/arch/m68k/include/asm/motorola_pgalloc.h
+++ b/arch/m68k/include/asm/motorola_pgalloc.h
@@ -36,12 +36,10 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long addres
return NULL;
pte = kmap(page);
- if (pte) {
- __flush_page_to_ram(pte);
- flush_tlb_kernel_page(pte);
- nocache_page(pte);
- }
- kunmap(pte);
+ __flush_page_to_ram(pte);
+ flush_tlb_kernel_page(pte);
+ nocache_page(pte);
+ kunmap(page);
pgtable_page_ctor(page);
return page;
}
diff --git a/arch/m68k/include/asm/pgtable_mm.h b/arch/m68k/include/asm/pgtable_mm.h
index 0b604f0f192..fe60e1abaee 100644
--- a/arch/m68k/include/asm/pgtable_mm.h
+++ b/arch/m68k/include/asm/pgtable_mm.h
@@ -135,8 +135,6 @@ static inline void update_mmu_cache(struct vm_area_struct *vma,
#endif
#ifndef __ASSEMBLY__
-#include <asm-generic/pgtable.h>
-
/*
* Macro to mark a page protection value as "uncacheable".
*/
@@ -154,6 +152,7 @@ static inline void update_mmu_cache(struct vm_area_struct *vma,
? (__pgprot((pgprot_val(prot) & _CACHEMASK040) | _PAGE_NOCACHE_S)) \
: (prot)))
+#include <asm-generic/pgtable.h>
#endif /* !__ASSEMBLY__ */
/*
diff --git a/arch/m68k/include/asm/unistd.h b/arch/m68k/include/asm/unistd.h
index aa29a8640f7..946d8691f2b 100644
--- a/arch/m68k/include/asm/unistd.h
+++ b/arch/m68k/include/asm/unistd.h
@@ -334,10 +334,12 @@
#define __NR_inotify_init1 328
#define __NR_preadv 329
#define __NR_pwritev 330
+#define __NR_rt_tgsigqueueinfo 331
+#define __NR_perf_counter_open 332
#ifdef __KERNEL__
-#define NR_syscalls 331
+#define NR_syscalls 333
#define __ARCH_WANT_IPC_PARSE_VERSION
#define __ARCH_WANT_OLD_READDIR
diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S
index 8744f60c07a..c3735cd6207 100644
--- a/arch/m68k/kernel/entry.S
+++ b/arch/m68k/kernel/entry.S
@@ -755,4 +755,6 @@ sys_call_table:
.long sys_inotify_init1
.long sys_preadv
.long sys_pwritev /* 330 */
+ .long sys_rt_tgsigqueueinfo
+ .long sys_perf_counter_open
diff --git a/arch/m68knommu/kernel/syscalltable.S b/arch/m68knommu/kernel/syscalltable.S
index c0b8782832f..0ae123e0898 100644
--- a/arch/m68knommu/kernel/syscalltable.S
+++ b/arch/m68knommu/kernel/syscalltable.S
@@ -349,6 +349,8 @@ ENTRY(sys_call_table)
.long sys_inotify_init1
.long sys_preadv
.long sys_pwritev /* 330 */
+ .long sys_rt_tgsigqueueinfo
+ .long sys_perf_counter_open
.rept NR_syscalls-(.-sys_call_table)/4
.long sys_ni_syscall
diff --git a/arch/microblaze/configs/mmu_defconfig b/arch/microblaze/configs/mmu_defconfig
index bd0b85ec38f..09c32962b66 100644
--- a/arch/microblaze/configs/mmu_defconfig
+++ b/arch/microblaze/configs/mmu_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.30-rc6
-# Fri May 22 10:02:33 2009
+# Linux kernel version: 2.6.31-rc6
+# Tue Aug 18 11:00:02 2009
#
CONFIG_MICROBLAZE=y
# CONFIG_SWAP is not set
@@ -18,7 +18,11 @@ CONFIG_GENERIC_TIME=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_CSUM=y
+# CONFIG_PCI is not set
+CONFIG_NO_DMA=y
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
#
# General setup
@@ -59,8 +63,8 @@ CONFIG_INITRAMFS_ROOT_GID=0
CONFIG_RD_GZIP=y
# CONFIG_RD_BZIP2 is not set
# CONFIG_RD_LZMA is not set
-CONFIG_INITRAMFS_COMPRESSION_NONE=y
-# CONFIG_INITRAMFS_COMPRESSION_GZIP is not set
+# CONFIG_INITRAMFS_COMPRESSION_NONE is not set
+CONFIG_INITRAMFS_COMPRESSION_GZIP=y
# CONFIG_INITRAMFS_COMPRESSION_BZIP2 is not set
# CONFIG_INITRAMFS_COMPRESSION_LZMA is not set
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
@@ -71,7 +75,6 @@ CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_KALLSYMS_EXTRA_PASS=y
-# CONFIG_STRIP_ASM_SYMS is not set
# CONFIG_HOTPLUG is not set
CONFIG_PRINTK=y
CONFIG_BUG=y
@@ -84,13 +87,22 @@ CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
# CONFIG_SHMEM is not set
CONFIG_AIO=y
+
+#
+# Performance Counters
+#
CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_STRIP_ASM_SYMS is not set
CONFIG_COMPAT_BRK=y
CONFIG_SLAB=y
# CONFIG_SLUB is not set
# CONFIG_SLOB is not set
# CONFIG_PROFILING is not set
# CONFIG_MARKERS is not set
+
+#
+# GCOV-based kernel profiling
+#
# CONFIG_SLOW_WORK is not set
# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
CONFIG_SLABINFO=y
@@ -102,7 +114,7 @@ CONFIG_MODULE_UNLOAD=y
# CONFIG_MODVERSIONS is not set
# CONFIG_MODULE_SRCVERSION_ALL is not set
CONFIG_BLOCK=y
-# CONFIG_LBD is not set
+CONFIG_LBDAF=y
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_BLK_DEV_INTEGRITY is not set
@@ -194,9 +206,9 @@ CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_PHYS_ADDR_T_64BIT is not set
CONFIG_ZONE_DMA_FLAG=0
CONFIG_VIRT_TO_BUS=y
-CONFIG_UNEVICTABLE_LRU=y
CONFIG_HAVE_MLOCK=y
CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
#
# Exectuable file formats
@@ -262,6 +274,7 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
# CONFIG_NET_SCHED is not set
# CONFIG_DCB is not set
@@ -325,7 +338,6 @@ CONFIG_MISC_DEVICES=y
# CONFIG_ATA is not set
# CONFIG_MD is not set
CONFIG_NETDEVICES=y
-CONFIG_COMPAT_NET_DEV_OPS=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
@@ -344,7 +356,7 @@ CONFIG_NET_ETHERNET=y
# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
-# CONFIG_B44 is not set
+# CONFIG_KS8842 is not set
CONFIG_NETDEV_1000=y
CONFIG_NETDEV_10000=y
@@ -410,6 +422,11 @@ CONFIG_LEGACY_PTY_COUNT=256
# CONFIG_TCG_TPM is not set
# CONFIG_I2C is not set
# CONFIG_SPI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
# CONFIG_GPIOLIB is not set
# CONFIG_W1 is not set
@@ -418,12 +435,6 @@ CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
# CONFIG_THERMAL is not set
# CONFIG_THERMAL_HWMON is not set
# CONFIG_WATCHDOG is not set
-CONFIG_SSB_POSSIBLE=y
-
-#
-# Sonics Silicon Backplane
-#
-# CONFIG_SSB is not set
#
# Multifunction device drivers
@@ -433,22 +444,7 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_HTC_PASIC3 is not set
# CONFIG_MFD_TMIO is not set
# CONFIG_REGULATOR is not set
-
-#
-# Multimedia devices
-#
-
-#
-# Multimedia core support
-#
-# CONFIG_VIDEO_DEV is not set
-# CONFIG_DVB_CORE is not set
-# CONFIG_VIDEO_MEDIA is not set
-
-#
-# Multimedia drivers
-#
-# CONFIG_DAB is not set
+# CONFIG_MEDIA_SUPPORT is not set
#
# Graphics support
@@ -469,9 +465,12 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_NEW_LEDS is not set
# CONFIG_ACCESSIBILITY is not set
# CONFIG_RTC_CLASS is not set
-# CONFIG_DMADEVICES is not set
# CONFIG_AUXDISPLAY is not set
# CONFIG_UIO is not set
+
+#
+# TI VLYNQ
+#
# CONFIG_STAGING is not set
#
@@ -485,12 +484,15 @@ CONFIG_EXT2_FS=y
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_FS_POSIX_ACL is not set
-CONFIG_FILE_LOCKING=y
# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_BTRFS_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
# CONFIG_DNOTIFY is not set
# CONFIG_INOTIFY is not set
+CONFIG_INOTIFY_USER=y
# CONFIG_QUOTA is not set
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
@@ -678,6 +680,7 @@ CONFIG_DEBUG_INFO=y
# CONFIG_SYSCTL_SYSCALL_CHECK is not set
# CONFIG_PAGE_POISONING is not set
# CONFIG_SAMPLES is not set
+# CONFIG_KMEMCHECK is not set
CONFIG_EARLY_PRINTK=y
CONFIG_HEART_BEAT=y
CONFIG_DEBUG_BOOTMEM=y
@@ -793,6 +796,5 @@ CONFIG_ZLIB_INFLATE=y
CONFIG_DECOMPRESS_GZIP=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
-CONFIG_HAS_DMA=y
CONFIG_HAVE_LMB=y
CONFIG_NLATTR=y
diff --git a/arch/microblaze/configs/nommu_defconfig b/arch/microblaze/configs/nommu_defconfig
index 4ef6af0a8f3..8b638615a97 100644
--- a/arch/microblaze/configs/nommu_defconfig
+++ b/arch/microblaze/configs/nommu_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.30-rc5
-# Mon May 11 09:01:02 2009
+# Linux kernel version: 2.6.31-rc6
+# Tue Aug 18 10:35:30 2009
#
CONFIG_MICROBLAZE=y
# CONFIG_SWAP is not set
@@ -17,9 +17,12 @@ CONFIG_GENERIC_TIME=y
# CONFIG_GENERIC_TIME_VSYSCALL is not set
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_CSUM=y
# CONFIG_PCI is not set
-# CONFIG_NO_DMA is not set
+CONFIG_NO_DMA=y
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
#
# General setup
@@ -64,7 +67,6 @@ CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_KALLSYMS_EXTRA_PASS=y
-# CONFIG_STRIP_ASM_SYMS is not set
# CONFIG_HOTPLUG is not set
CONFIG_PRINTK=y
CONFIG_BUG=y
@@ -76,13 +78,23 @@ CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_AIO=y
+
+#
+# Performance Counters
+#
CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_STRIP_ASM_SYMS is not set
CONFIG_COMPAT_BRK=y
CONFIG_SLAB=y
# CONFIG_SLUB is not set
# CONFIG_SLOB is not set
# CONFIG_PROFILING is not set
# CONFIG_MARKERS is not set
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
# CONFIG_SLOW_WORK is not set
# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
CONFIG_SLABINFO=y
@@ -95,7 +107,7 @@ CONFIG_MODULE_UNLOAD=y
# CONFIG_MODVERSIONS is not set
# CONFIG_MODULE_SRCVERSION_ALL is not set
CONFIG_BLOCK=y
-# CONFIG_LBD is not set
+CONFIG_LBDAF=y
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_BLK_DEV_INTEGRITY is not set
@@ -156,8 +168,16 @@ CONFIG_CMDLINE_BOOL=y
CONFIG_CMDLINE="console=ttyUL0,115200"
# CONFIG_CMDLINE_FORCE is not set
CONFIG_OF=y
-CONFIG_OF_DEVICE=y
CONFIG_PROC_DEVICETREE=y
+
+#
+# Advanced setup
+#
+
+#
+# Default settings for advanced configuration options are used
+#
+CONFIG_KERNEL_START=0x90000000
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
# CONFIG_DISCONTIGMEM_MANUAL is not set
@@ -169,7 +189,7 @@ CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_PHYS_ADDR_T_64BIT is not set
CONFIG_ZONE_DMA_FLAG=0
CONFIG_VIRT_TO_BUS=y
-CONFIG_UNEVICTABLE_LRU=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
CONFIG_NOMMU_INITIAL_TRIM_EXCESS=1
#
@@ -237,6 +257,7 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
# CONFIG_NET_SCHED is not set
# CONFIG_DCB is not set
@@ -254,7 +275,11 @@ CONFIG_WIRELESS=y
CONFIG_WIRELESS_OLD_REGULATORY=y
# CONFIG_WIRELESS_EXT is not set
# CONFIG_LIB80211 is not set
-# CONFIG_MAC80211 is not set
+
+#
+# CFG80211 needs to be enabled for MAC80211
+#
+CONFIG_MAC80211_DEFAULT_PS_VALUE=0
# CONFIG_WIMAX is not set
# CONFIG_RFKILL is not set
# CONFIG_NET_9P is not set
@@ -353,6 +378,7 @@ CONFIG_MTD_UCLINUX=y
# UBI - Unsorted block images
#
# CONFIG_MTD_UBI is not set
+CONFIG_OF_DEVICE=y
# CONFIG_PARPORT is not set
CONFIG_BLK_DEV=y
# CONFIG_BLK_DEV_COW_COMMON is not set
@@ -364,6 +390,7 @@ CONFIG_BLK_DEV_RAM_SIZE=4096
# CONFIG_BLK_DEV_XIP is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_XILINX_SYSACE is not set
CONFIG_MISC_DEVICES=y
# CONFIG_ENCLOSURE_SERVICES is not set
# CONFIG_C2PORT is not set
@@ -383,7 +410,6 @@ CONFIG_MISC_DEVICES=y
# CONFIG_ATA is not set
# CONFIG_MD is not set
CONFIG_NETDEVICES=y
-CONFIG_COMPAT_NET_DEV_OPS=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
@@ -402,7 +428,7 @@ CONFIG_NET_ETHERNET=y
# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
-# CONFIG_B44 is not set
+# CONFIG_KS8842 is not set
CONFIG_NETDEV_1000=y
CONFIG_NETDEV_10000=y
@@ -463,23 +489,25 @@ CONFIG_HW_RANDOM=y
# CONFIG_HW_RANDOM_TIMERIOMEM is not set
# CONFIG_RTC is not set
# CONFIG_GEN_RTC is not set
+# CONFIG_XILINX_HWICAP is not set
# CONFIG_R3964 is not set
# CONFIG_RAW_DRIVER is not set
# CONFIG_TCG_TPM is not set
# CONFIG_I2C is not set
# CONFIG_SPI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+# CONFIG_GPIOLIB is not set
# CONFIG_W1 is not set
# CONFIG_POWER_SUPPLY is not set
# CONFIG_HWMON is not set
# CONFIG_THERMAL is not set
# CONFIG_THERMAL_HWMON is not set
# CONFIG_WATCHDOG is not set
-CONFIG_SSB_POSSIBLE=y
-
-#
-# Sonics Silicon Backplane
-#
-# CONFIG_SSB is not set
#
# Multifunction device drivers
@@ -489,22 +517,7 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_HTC_PASIC3 is not set
# CONFIG_MFD_TMIO is not set
# CONFIG_REGULATOR is not set
-
-#
-# Multimedia devices
-#
-
-#
-# Multimedia core support
-#
-# CONFIG_VIDEO_DEV is not set
-# CONFIG_DVB_CORE is not set
-# CONFIG_VIDEO_MEDIA is not set
-
-#
-# Multimedia drivers
-#
-CONFIG_DAB=y
+# CONFIG_MEDIA_SUPPORT is not set
#
# Graphics support
@@ -520,9 +533,10 @@ CONFIG_VIDEO_OUTPUT_CONTROL=y
# CONFIG_DISPLAY_SUPPORT is not set
# CONFIG_SOUND is not set
CONFIG_USB_SUPPORT=y
-# CONFIG_USB_ARCH_HAS_HCD is not set
+CONFIG_USB_ARCH_HAS_HCD=y
# CONFIG_USB_ARCH_HAS_OHCI is not set
# CONFIG_USB_ARCH_HAS_EHCI is not set
+# CONFIG_USB is not set
# CONFIG_USB_OTG_WHITELIST is not set
# CONFIG_USB_OTG_BLACKLIST_HUB is not set
@@ -543,9 +557,12 @@ CONFIG_USB_SUPPORT=y
# CONFIG_NEW_LEDS is not set
# CONFIG_ACCESSIBILITY is not set
# CONFIG_RTC_CLASS is not set
-# CONFIG_DMADEVICES is not set
# CONFIG_AUXDISPLAY is not set
# CONFIG_UIO is not set
+
+#
+# TI VLYNQ
+#
# CONFIG_STAGING is not set
#
@@ -558,12 +575,15 @@ CONFIG_EXT2_FS=y
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
CONFIG_FS_POSIX_ACL=y
-CONFIG_FILE_LOCKING=y
# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_BTRFS_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
# CONFIG_DNOTIFY is not set
# CONFIG_INOTIFY is not set
+CONFIG_INOTIFY_USER=y
# CONFIG_QUOTA is not set
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
@@ -813,6 +833,5 @@ CONFIG_GENERIC_FIND_LAST_BIT=y
CONFIG_ZLIB_INFLATE=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
-CONFIG_HAS_DMA=y
CONFIG_HAVE_LMB=y
CONFIG_NLATTR=y
diff --git a/arch/microblaze/include/asm/hardirq.h b/arch/microblaze/include/asm/hardirq.h
index 41e1e1aa36a..cd1ac9aad56 100644
--- a/arch/microblaze/include/asm/hardirq.h
+++ b/arch/microblaze/include/asm/hardirq.h
@@ -12,8 +12,6 @@
/* should be defined in each interrupt controller driver */
extern unsigned int get_irq(struct pt_regs *regs);
-#define ack_bad_irq ack_bad_irq
-void ack_bad_irq(unsigned int irq);
#include <asm-generic/hardirq.h>
#endif /* _ASM_MICROBLAZE_HARDIRQ_H */
diff --git a/arch/microblaze/kernel/intc.c b/arch/microblaze/kernel/intc.c
index b15605299a5..6eea6f92b84 100644
--- a/arch/microblaze/kernel/intc.c
+++ b/arch/microblaze/kernel/intc.c
@@ -12,6 +12,7 @@
#include <linux/irq.h>
#include <asm/page.h>
#include <linux/io.h>
+#include <linux/bug.h>
#include <asm/prom.h>
#include <asm/irq.h>
@@ -130,6 +131,7 @@ void __init init_IRQ(void)
if (intc)
break;
}
+ BUG_ON(!intc);
intc_baseaddr = *(int *) of_get_property(intc, "reg", NULL);
intc_baseaddr = (unsigned long) ioremap(intc_baseaddr, PAGE_SIZE);
diff --git a/arch/microblaze/kernel/irq.c b/arch/microblaze/kernel/irq.c
index f688ee93e3b..7d5ddd62d4d 100644
--- a/arch/microblaze/kernel/irq.c
+++ b/arch/microblaze/kernel/irq.c
@@ -30,15 +30,6 @@ unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
}
EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
-/*
- * 'what should we do if we get a hw irq event on an illegal vector'.
- * each architecture has to answer this themselves.
- */
-void ack_bad_irq(unsigned int irq)
-{
- printk(KERN_WARNING "unexpected IRQ trap at vector %02x\n", irq);
-}
-
static u32 concurrent_irq;
void do_IRQ(struct pt_regs *regs)
diff --git a/arch/microblaze/kernel/syscall_table.S b/arch/microblaze/kernel/syscall_table.S
index 216db817beb..457216097df 100644
--- a/arch/microblaze/kernel/syscall_table.S
+++ b/arch/microblaze/kernel/syscall_table.S
@@ -313,7 +313,7 @@ ENTRY(sys_call_table)
.long sys_fchmodat
.long sys_faccessat
.long sys_ni_syscall /* pselect6 */
- .long sys_ni_syscall /* sys_ppoll */
+ .long sys_ppoll
.long sys_unshare /* 310 */
.long sys_set_robust_list
.long sys_get_robust_list
diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c
index bdfa2f9f0c8..5499deae7fa 100644
--- a/arch/microblaze/kernel/timer.c
+++ b/arch/microblaze/kernel/timer.c
@@ -22,6 +22,7 @@
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/io.h>
+#include <linux/bug.h>
#include <asm/cpuinfo.h>
#include <asm/setup.h>
#include <asm/prom.h>
@@ -234,6 +235,7 @@ void __init time_init(void)
if (timer)
break;
}
+ BUG_ON(!timer);
timer_baseaddr = *(int *) of_get_property(timer, "reg", NULL);
timer_baseaddr = (unsigned long) ioremap(timer_baseaddr, PAGE_SIZE);
diff --git a/arch/microblaze/mm/init.c b/arch/microblaze/mm/init.c
index 8d92c4efe9a..f207f1a94db 100644
--- a/arch/microblaze/mm/init.c
+++ b/arch/microblaze/mm/init.c
@@ -130,13 +130,13 @@ void __init setup_memory(void)
* (in case the address isn't page-aligned).
*/
#ifndef CONFIG_MMU
- map_size = init_bootmem_node(NODE_DATA(0), PFN_UP(TOPHYS((u32)_end)),
+ map_size = init_bootmem_node(NODE_DATA(0), PFN_UP(TOPHYS((u32)klimit)),
min_low_pfn, max_low_pfn);
#else
map_size = init_bootmem_node(&contig_page_data,
- PFN_UP(TOPHYS((u32)_end)), min_low_pfn, max_low_pfn);
+ PFN_UP(TOPHYS((u32)klimit)), min_low_pfn, max_low_pfn);
#endif
- lmb_reserve(PFN_UP(TOPHYS((u32)_end)) << PAGE_SHIFT, map_size);
+ lmb_reserve(PFN_UP(TOPHYS((u32)klimit)) << PAGE_SHIFT, map_size);
/* free bootmem is whole main memory */
free_bootmem(memory_start, memory_size);
diff --git a/arch/mips/alchemy/mtx-1/platform.c b/arch/mips/alchemy/mtx-1/platform.c
index 8b5914d1241..e30e42add69 100644
--- a/arch/mips/alchemy/mtx-1/platform.c
+++ b/arch/mips/alchemy/mtx-1/platform.c
@@ -1,7 +1,7 @@
/*
* MTX-1 platform devices registration
*
- * Copyright (C) 2007, Florian Fainelli <florian@openwrt.org>
+ * Copyright (C) 2007-2009, Florian Fainelli <florian@openwrt.org>
*
* 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
@@ -142,7 +142,17 @@ static struct __initdata platform_device * mtx1_devs[] = {
static int __init mtx1_register_devices(void)
{
- gpio_direction_input(207);
+ int rc;
+
+ rc = gpio_request(mtx1_gpio_button[0].gpio,
+ mtx1_gpio_button[0].desc);
+ if (rc < 0) {
+ printk(KERN_INFO "mtx1: failed to request %d\n",
+ mtx1_gpio_button[0].gpio);
+ goto out;
+ }
+ gpio_direction_input(mtx1_gpio_button[0].gpio);
+out:
return platform_add_devices(mtx1_devs, ARRAY_SIZE(mtx1_devs));
}
diff --git a/arch/mips/ar7/Makefile b/arch/mips/ar7/Makefile
index 7435e44b396..26bc5da1899 100644
--- a/arch/mips/ar7/Makefile
+++ b/arch/mips/ar7/Makefile
@@ -8,3 +8,4 @@ obj-y := \
platform.o \
gpio.o \
clock.o
+EXTRA_CFLAGS += -Werror
diff --git a/arch/mips/ar7/clock.c b/arch/mips/ar7/clock.c
index 27dc6663f2f..cc65c8eb391 100644
--- a/arch/mips/ar7/clock.c
+++ b/arch/mips/ar7/clock.c
@@ -264,19 +264,6 @@ static void __init tnetd7300_init_clocks(void)
iounmap(bootcr);
}
-static int tnetd7200_get_clock(int base, struct tnetd7200_clock *clock,
- u32 *bootcr, u32 bus_clock)
-{
- int divisor = ((readl(&clock->prediv) & 0x1f) + 1) *
- ((readl(&clock->postdiv) & 0x1f) + 1);
-
- if (*bootcr & BOOT_PLL_BYPASS)
- return base / divisor;
-
- return base * ((readl(&clock->mul) & 0xf) + 1) / divisor;
-}
-
-
static void tnetd7200_set_clock(int base, struct tnetd7200_clock *clock,
int prediv, int postdiv, int postdiv2, int mul, u32 frequency)
{
diff --git a/arch/mips/ar7/memory.c b/arch/mips/ar7/memory.c
index 46fed44825a..696c723dc6d 100644
--- a/arch/mips/ar7/memory.c
+++ b/arch/mips/ar7/memory.c
@@ -52,7 +52,7 @@ static int __init memsize(void)
size <<= 1;
} while (size < (64 << 20));
- writel(tmpaddr, &addr);
+ writel((u32)tmpaddr, &addr);
return size;
}
diff --git a/arch/mips/ar7/platform.c b/arch/mips/ar7/platform.c
index 54224496178..2ecab615593 100644
--- a/arch/mips/ar7/platform.c
+++ b/arch/mips/ar7/platform.c
@@ -28,7 +28,6 @@
#include <linux/serial_8250.h>
#include <linux/ioport.h>
#include <linux/io.h>
-#include <linux/version.h>
#include <linux/vlynq.h>
#include <linux/leds.h>
#include <linux/string.h>
@@ -243,13 +242,13 @@ static struct platform_device physmap_flash = {
.num_resources = 1,
};
-static u64 cpmac_dma_mask = DMA_32BIT_MASK;
+static u64 cpmac_dma_mask = DMA_BIT_MASK(32);
static struct platform_device cpmac_low = {
.id = 0,
.name = "cpmac",
.dev = {
.dma_mask = &cpmac_dma_mask,
- .coherent_dma_mask = DMA_32BIT_MASK,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &cpmac_low_data,
},
.resource = cpmac_low_res,
@@ -261,7 +260,7 @@ static struct platform_device cpmac_high = {
.name = "cpmac",
.dev = {
.dma_mask = &cpmac_dma_mask,
- .coherent_dma_mask = DMA_32BIT_MASK,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &cpmac_high_data,
},
.resource = cpmac_high_res,
@@ -481,6 +480,7 @@ static void __init detect_leds(void)
static int __init ar7_register_devices(void)
{
int res;
+#ifdef CONFIG_SERIAL_8250
static struct uart_port uart_port[2];
memset(uart_port, 0, sizeof(struct uart_port) * 2);
@@ -512,7 +512,7 @@ static int __init ar7_register_devices(void)
if (res)
return res;
}
-
+#endif /* CONFIG_SERIAL_8250 */
res = platform_device_register(&physmap_flash);
if (res)
return res;
diff --git a/arch/mips/ar7/prom.c b/arch/mips/ar7/prom.c
index a320bceb2f9..5ad6f1db656 100644
--- a/arch/mips/ar7/prom.c
+++ b/arch/mips/ar7/prom.c
@@ -144,7 +144,7 @@ static char * __init lookup_psp_var_map(u8 num)
{
int i;
- for (i = 0; i < sizeof(psp_var_map); i++)
+ for (i = 0; i < ARRAY_SIZE(psp_var_map); i++)
if (psp_var_map[i].num == num)
return psp_var_map[i].value;
diff --git a/arch/mips/ar7/setup.c b/arch/mips/ar7/setup.c
index 6ebb5f16d96..39f6b5b9646 100644
--- a/arch/mips/ar7/setup.c
+++ b/arch/mips/ar7/setup.c
@@ -15,7 +15,6 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/pm.h>
diff --git a/arch/mips/cavium-octeon/smp.c b/arch/mips/cavium-octeon/smp.c
index 0b891a9c625..32d51a31dc4 100644
--- a/arch/mips/cavium-octeon/smp.c
+++ b/arch/mips/cavium-octeon/smp.c
@@ -194,11 +194,11 @@ static void octeon_init_secondary(void)
void octeon_prepare_cpus(unsigned int max_cpus)
{
cvmx_write_csr(CVMX_CIU_MBOX_CLRX(cvmx_get_core_num()), 0xffffffff);
- if (request_irq(OCTEON_IRQ_MBOX0, mailbox_interrupt, IRQF_SHARED,
+ if (request_irq(OCTEON_IRQ_MBOX0, mailbox_interrupt, IRQF_DISABLED,
"mailbox0", mailbox_interrupt)) {
panic("Cannot request_irq(OCTEON_IRQ_MBOX0)\n");
}
- if (request_irq(OCTEON_IRQ_MBOX1, mailbox_interrupt, IRQF_SHARED,
+ if (request_irq(OCTEON_IRQ_MBOX1, mailbox_interrupt, IRQF_DISABLED,
"mailbox1", mailbox_interrupt)) {
panic("Cannot request_irq(OCTEON_IRQ_MBOX1)\n");
}
diff --git a/arch/mips/dec/ecc-berr.c b/arch/mips/dec/ecc-berr.c
index 6a17c9b508e..7abce661b90 100644
--- a/arch/mips/dec/ecc-berr.c
+++ b/arch/mips/dec/ecc-berr.c
@@ -1,6 +1,4 @@
/*
- * linux/arch/mips/dec/ecc-berr.c
- *
* Bus error event handling code for systems equipped with ECC
* handling logic, i.e. DECstation/DECsystem 5000/200 (KN02),
* 5000/240 (KN03), 5000/260 (KN05) and DECsystem 5900 (KN03),
diff --git a/arch/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S
index 00cecdcc75f..82c85281878 100644
--- a/arch/mips/dec/int-handler.S
+++ b/arch/mips/dec/int-handler.S
@@ -1,6 +1,4 @@
/*
- * arch/mips/dec/int-handler.S
- *
* Copyright (C) 1995, 1996, 1997 Paul M. Antoine and Harald Koerfgen
* Copyright (C) 2000, 2001, 2002, 2003, 2005 Maciej W. Rozycki
*
diff --git a/arch/mips/dec/ioasic-irq.c b/arch/mips/dec/ioasic-irq.c
index 3acb133668d..cb41954fc32 100644
--- a/arch/mips/dec/ioasic-irq.c
+++ b/arch/mips/dec/ioasic-irq.c
@@ -1,6 +1,4 @@
/*
- * linux/arch/mips/dec/ioasic-irq.c
- *
* DEC I/O ASIC interrupts.
*
* Copyright (c) 2002, 2003 Maciej W. Rozycki
diff --git a/arch/mips/dec/kn01-berr.c b/arch/mips/dec/kn01-berr.c
index d3b8002bf1e..b0dc6d53edd 100644
--- a/arch/mips/dec/kn01-berr.c
+++ b/arch/mips/dec/kn01-berr.c
@@ -1,6 +1,4 @@
/*
- * linux/arch/mips/dec/kn01-berr.c
- *
* Bus error event handling code for DECstation/DECsystem 3100
* and 2100 (KN01) systems equipped with parity error detection
* logic.
diff --git a/arch/mips/dec/kn02-irq.c b/arch/mips/dec/kn02-irq.c
index 02439dc0ba8..ed90a8deabc 100644
--- a/arch/mips/dec/kn02-irq.c
+++ b/arch/mips/dec/kn02-irq.c
@@ -1,6 +1,4 @@
/*
- * linux/arch/mips/dec/kn02-irq.c
- *
* DECstation 5000/200 (KN02) Control and Status Register
* interrupts.
*
diff --git a/arch/mips/dec/kn02xa-berr.c b/arch/mips/dec/kn02xa-berr.c
index 5f04545c360..07ca5405d48 100644
--- a/arch/mips/dec/kn02xa-berr.c
+++ b/arch/mips/dec/kn02xa-berr.c
@@ -1,6 +1,4 @@
/*
- * linux/arch/mips/dec/kn02xa-berr.c
- *
* Bus error event handling code for 5000-series systems equipped
* with parity error detection logic, i.e. DECstation/DECsystem
* 5000/120, /125, /133 (KN02-BA), 5000/150 (KN04-BA) and Personal
diff --git a/arch/mips/dec/prom/call_o32.S b/arch/mips/dec/prom/call_o32.S
index e523454bda3..8c8498159e4 100644
--- a/arch/mips/dec/prom/call_o32.S
+++ b/arch/mips/dec/prom/call_o32.S
@@ -1,6 +1,4 @@
/*
- * arch/mips/dec/prom/call_o32.S
- *
* O32 interface for the 64 (or N32) ABI.
*
* Copyright (C) 2002 Maciej W. Rozycki
diff --git a/arch/mips/dec/prom/console.c b/arch/mips/dec/prom/console.c
index 078e1a12421..caa6e047caf 100644
--- a/arch/mips/dec/prom/console.c
+++ b/arch/mips/dec/prom/console.c
@@ -1,6 +1,4 @@
/*
- * arch/mips/dec/prom/console.c
- *
* DECstation PROM-based early console support.
*
* Copyright (C) 2004, 2007 Maciej W. Rozycki
diff --git a/arch/mips/dec/time.c b/arch/mips/dec/time.c
index 1359c03ded5..463136e6685 100644
--- a/arch/mips/dec/time.c
+++ b/arch/mips/dec/time.c
@@ -1,6 +1,4 @@
/*
- * linux/arch/mips/dec/time.c
- *
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
* Copyright (C) 2000, 2003 Maciej W. Rozycki
*
diff --git a/arch/mips/emma/common/Makefile b/arch/mips/emma/common/Makefile
index c392d28c1ef..f27d84d1904 100644
--- a/arch/mips/emma/common/Makefile
+++ b/arch/mips/emma/common/Makefile
@@ -1,7 +1,4 @@
#
-# arch/mips/emma2rh/common/Makefile
-# Makefile for the common code of NEC EMMA2RH based board.
-#
# Copyright (C) NEC Electronics Corporation 2005-2006
#
# This program is free software; you can redistribute it and/or modify
diff --git a/arch/mips/emma/common/prom.c b/arch/mips/emma/common/prom.c
index 120f53fbdb4..708f0876140 100644
--- a/arch/mips/emma/common/prom.c
+++ b/arch/mips/emma/common/prom.c
@@ -1,7 +1,4 @@
/*
- * arch/mips/emma2rh/common/prom.c
- * This file is prom file.
- *
* Copyright (C) NEC Electronics Corporation 2004-2006
*
* This file is based on the arch/mips/ddb5xxx/common/prom.c
diff --git a/arch/mips/emma/markeins/Makefile b/arch/mips/emma/markeins/Makefile
index 16e0017ba91..f8ba2508fa2 100644
--- a/arch/mips/emma/markeins/Makefile
+++ b/arch/mips/emma/markeins/Makefile
@@ -1,7 +1,4 @@
#
-# arch/mips/emma2rh/markeins/Makefile
-# Makefile for the common code of NEC EMMA2RH based board.
-#
# Copyright (C) NEC Electronics Corporation 2005-2006
#
# This program is free software; you can redistribute it and/or modify
diff --git a/arch/mips/emma/markeins/irq.c b/arch/mips/emma/markeins/irq.c
index 43828ae796e..9504b7ee0b7 100644
--- a/arch/mips/emma/markeins/irq.c
+++ b/arch/mips/emma/markeins/irq.c
@@ -1,7 +1,4 @@
/*
- * arch/mips/emma2rh/markeins/irq.c
- * This file defines the irq handler for EMMA2RH.
- *
* Copyright (C) NEC Electronics Corporation 2004-2006
*
* This file is based on the arch/mips/ddb5xxx/ddb5477/irq.c
diff --git a/arch/mips/emma/markeins/led.c b/arch/mips/emma/markeins/led.c
index 377a181b656..49755896857 100644
--- a/arch/mips/emma/markeins/led.c
+++ b/arch/mips/emma/markeins/led.c
@@ -1,7 +1,4 @@
/*
- * arch/mips/emma2rh/markeins/led.c
- * This file defines the led display for Mark-eins.
- *
* Copyright (C) NEC Electronics Corporation 2004-2006
*
* This program is free software; you can redistribute it and/or modify
diff --git a/arch/mips/emma/markeins/platform.c b/arch/mips/emma/markeins/platform.c
index 80ae12ef87d..b05b08b92a3 100644
--- a/arch/mips/emma/markeins/platform.c
+++ b/arch/mips/emma/markeins/platform.c
@@ -1,7 +1,4 @@
/*
- * arch/mips/emma2rh/markeins/platofrm.c
- * This file sets up platform devices for EMMA2RH Mark-eins.
- *
* Copyright(C) MontaVista Software Inc, 2006
*
* Author: dmitry pervushin <dpervushin@ru.mvista.com>
diff --git a/arch/mips/emma/markeins/setup.c b/arch/mips/emma/markeins/setup.c
index 67f45650008..335dc8c1a1b 100644
--- a/arch/mips/emma/markeins/setup.c
+++ b/arch/mips/emma/markeins/setup.c
@@ -1,7 +1,4 @@
/*
- * arch/mips/emma2rh/markeins/setup.c
- * This file is setup for EMMA2RH Mark-eins.
- *
* Copyright (C) NEC Electronics Corporation 2004-2006
*
* This file is based on the arch/mips/ddb5xxx/ddb5477/setup.c.
diff --git a/arch/mips/fw/lib/call_o32.S b/arch/mips/fw/lib/call_o32.S
index bdf7d1d4081..e0a68713b3c 100644
--- a/arch/mips/fw/lib/call_o32.S
+++ b/arch/mips/fw/lib/call_o32.S
@@ -1,6 +1,4 @@
/*
- * arch/mips/dec/prom/call_o32.S
- *
* O32 interface for the 64 (or N32) ABI.
*
* Copyright (C) 2002 Maciej W. Rozycki
diff --git a/arch/mips/include/asm/emma/emma2rh.h b/arch/mips/include/asm/emma/emma2rh.h
index 30aea91de62..2afb2fe11b3 100644
--- a/arch/mips/include/asm/emma/emma2rh.h
+++ b/arch/mips/include/asm/emma/emma2rh.h
@@ -1,7 +1,4 @@
/*
- * arch/mips/include/asm/emma/emma2rh.h
- * This file is EMMA2RH common header.
- *
* Copyright (C) NEC Electronics Corporation 2005-2006
*
* This file based on include/asm-mips/ddb5xxx/ddb5xxx.h
diff --git a/arch/mips/include/asm/emma/markeins.h b/arch/mips/include/asm/emma/markeins.h
index 973b0628490..2618bf23024 100644
--- a/arch/mips/include/asm/emma/markeins.h
+++ b/arch/mips/include/asm/emma/markeins.h
@@ -1,7 +1,4 @@
/*
- * include/asm-mips/emma2rh/markeins.h
- * This file is EMMA2RH board depended header.
- *
* Copyright (C) NEC Electronics Corporation 2005-2006
*
* This file based on include/asm-mips/ddb5xxx/ddb5xxx.h
diff --git a/arch/mips/include/asm/gic.h b/arch/mips/include/asm/gic.h
index 10292e37c1f..a8f57341f12 100644
--- a/arch/mips/include/asm/gic.h
+++ b/arch/mips/include/asm/gic.h
@@ -20,7 +20,7 @@
#define GIC_TRIG_EDGE 1
#define GIC_TRIG_LEVEL 0
-#if CONFIG_SMP
+#ifdef CONFIG_SMP
#define GIC_NUM_INTRS (24 + NR_CPUS * 2)
#else
#define GIC_NUM_INTRS 32
diff --git a/arch/mips/include/asm/page.h b/arch/mips/include/asm/page.h
index 96a14a426a7..4320239cf4e 100644
--- a/arch/mips/include/asm/page.h
+++ b/arch/mips/include/asm/page.h
@@ -32,10 +32,12 @@
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define PAGE_MASK (~((1 << PAGE_SHIFT) - 1))
+#ifdef CONFIG_HUGETLB_PAGE
#define HPAGE_SHIFT (PAGE_SHIFT + PAGE_SHIFT - 3)
#define HPAGE_SIZE ((1UL) << HPAGE_SHIFT)
#define HPAGE_MASK (~(HPAGE_SIZE - 1))
#define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT)
+#endif /* CONFIG_HUGETLB_PAGE */
#ifndef __ASSEMBLY__
diff --git a/arch/mips/include/asm/pmc-sierra/msp71xx/war.h b/arch/mips/include/asm/pmc-sierra/msp71xx/war.h
index 0bf48fc1892..9e2ee429c52 100644
--- a/arch/mips/include/asm/pmc-sierra/msp71xx/war.h
+++ b/arch/mips/include/asm/pmc-sierra/msp71xx/war.h
@@ -23,6 +23,8 @@
#if defined(CONFIG_PMC_MSP7120_EVAL) || defined(CONFIG_PMC_MSP7120_GW) || \
defined(CONFIG_PMC_MSP7120_FPGA)
#define MIPS34K_MISSED_ITLB_WAR 1
+#else
+#define MIPS34K_MISSED_ITLB_WAR 0
#endif
#endif /* __ASM_MIPS_PMC_SIERRA_WAR_H */
diff --git a/arch/mips/include/asm/processor.h b/arch/mips/include/asm/processor.h
index 0f926aa0cb4..087a8884ef0 100644
--- a/arch/mips/include/asm/processor.h
+++ b/arch/mips/include/asm/processor.h
@@ -311,8 +311,9 @@ extern void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long
unsigned long get_wchan(struct task_struct *p);
-#define __KSTK_TOS(tsk) ((unsigned long)task_stack_page(tsk) + THREAD_SIZE - 32)
-#define task_pt_regs(tsk) ((struct pt_regs *)__KSTK_TOS(tsk) - 1)
+#define __KSTK_TOS(tsk) ((unsigned long)task_stack_page(tsk) + \
+ THREAD_SIZE - 32 - sizeof(struct pt_regs))
+#define task_pt_regs(tsk) ((struct pt_regs *)__KSTK_TOS(tsk))
#define KSTK_EIP(tsk) (task_pt_regs(tsk)->cp0_epc)
#define KSTK_ESP(tsk) (task_pt_regs(tsk)->regs[29])
#define KSTK_STATUS(tsk) (task_pt_regs(tsk)->cp0_status)
diff --git a/arch/mips/include/asm/unistd.h b/arch/mips/include/asm/unistd.h
index b70c49fdda2..e753a777949 100644
--- a/arch/mips/include/asm/unistd.h
+++ b/arch/mips/include/asm/unistd.h
@@ -354,16 +354,17 @@
#define __NR_pwritev (__NR_Linux + 331)
#define __NR_rt_tgsigqueueinfo (__NR_Linux + 332)
#define __NR_perf_counter_open (__NR_Linux + 333)
+#define __NR_accept4 (__NR_Linux + 334)
/*
* Offset of the last Linux o32 flavoured syscall
*/
-#define __NR_Linux_syscalls 333
+#define __NR_Linux_syscalls 334
#endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
#define __NR_O32_Linux 4000
-#define __NR_O32_Linux_syscalls 333
+#define __NR_O32_Linux_syscalls 334
#if _MIPS_SIM == _MIPS_SIM_ABI64
@@ -664,16 +665,17 @@
#define __NR_pwritev (__NR_Linux + 290)
#define __NR_rt_tgsigqueueinfo (__NR_Linux + 291)
#define __NR_perf_counter_open (__NR_Linux + 292)
+#define __NR_accept4 (__NR_Linux + 293)
/*
* Offset of the last Linux 64-bit flavoured syscall
*/
-#define __NR_Linux_syscalls 292
+#define __NR_Linux_syscalls 293
#endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */
#define __NR_64_Linux 5000
-#define __NR_64_Linux_syscalls 292
+#define __NR_64_Linux_syscalls 293
#if _MIPS_SIM == _MIPS_SIM_NABI32
@@ -978,16 +980,17 @@
#define __NR_pwritev (__NR_Linux + 294)
#define __NR_rt_tgsigqueueinfo (__NR_Linux + 295)
#define __NR_perf_counter_open (__NR_Linux + 296)
+#define __NR_accept4 (__NR_Linux + 297)
/*
* Offset of the last N32 flavoured syscall
*/
-#define __NR_Linux_syscalls 296
+#define __NR_Linux_syscalls 297
#endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */
#define __NR_N32_Linux 6000
-#define __NR_N32_Linux_syscalls 296
+#define __NR_N32_Linux_syscalls 297
#ifdef __KERNEL__
diff --git a/arch/mips/jazz/jazzdma.c b/arch/mips/jazz/jazzdma.c
index f0fd636723b..0d64d0f4641 100644
--- a/arch/mips/jazz/jazzdma.c
+++ b/arch/mips/jazz/jazzdma.c
@@ -190,7 +190,7 @@ int vdma_free(unsigned long laddr)
return -1;
}
- while (pgtbl[i].owner == laddr && i < VDMA_PGTBL_ENTRIES) {
+ while (i < VDMA_PGTBL_ENTRIES && pgtbl[i].owner == laddr) {
pgtbl[i].owner = VDMA_PAGE_EMPTY;
i++;
}
diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S
index 492a0a8d70f..531ce7b1612 100644
--- a/arch/mips/kernel/head.S
+++ b/arch/mips/kernel/head.S
@@ -188,7 +188,8 @@ NESTED(kernel_entry, 16, sp) # kernel entry point
MTC0 zero, CP0_CONTEXT # clear context register
PTR_LA $28, init_thread_union
- PTR_LI sp, _THREAD_SIZE - 32
+ /* Set the SP after an empty pt_regs. */
+ PTR_LI sp, _THREAD_SIZE - 32 - PT_SIZE
PTR_ADDU sp, $28
set_saved_sp sp, t0, t1
PTR_SUBU sp, 4 * SZREG # init stack pointer
diff --git a/arch/mips/kernel/irq_txx9.c b/arch/mips/kernel/irq_txx9.c
index a4d1462c27f..9b78029bea7 100644
--- a/arch/mips/kernel/irq_txx9.c
+++ b/arch/mips/kernel/irq_txx9.c
@@ -1,6 +1,4 @@
/*
- * linux/arch/mips/kernel/irq_txx9.c
- *
* Based on linux/arch/mips/jmr3927/rbhma3100/irq.c,
* linux/arch/mips/tx4927/common/tx4927_irq.c,
* linux/arch/mips/tx4938/common/irq.c
diff --git a/arch/mips/kernel/module.c b/arch/mips/kernel/module.c
index 3e9100dcc12..6f51dda87fc 100644
--- a/arch/mips/kernel/module.c
+++ b/arch/mips/kernel/module.c
@@ -98,7 +98,8 @@ static int apply_r_mips_32_rela(struct module *me, u32 *location, Elf_Addr v)
static int apply_r_mips_26_rel(struct module *me, u32 *location, Elf_Addr v)
{
if (v % 4) {
- printk(KERN_ERR "module %s: dangerous relocation\n", me->name);
+ pr_err("module %s: dangerous R_MIPS_26 REL relocation\n",
+ me->name);
return -ENOEXEC;
}
@@ -118,7 +119,8 @@ static int apply_r_mips_26_rel(struct module *me, u32 *location, Elf_Addr v)
static int apply_r_mips_26_rela(struct module *me, u32 *location, Elf_Addr v)
{
if (v % 4) {
- printk(KERN_ERR "module %s: dangerous relocation\n", me->name);
+ pr_err("module %s: dangerous R_MIPS_26 RELArelocation\n",
+ me->name);
return -ENOEXEC;
}
@@ -222,7 +224,7 @@ static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v)
return 0;
out_danger:
- printk(KERN_ERR "module %s: dangerous " "relocation\n", me->name);
+ pr_err("module %s: dangerous R_MIPS_LO16 REL relocation\n", me->name);
return -ENOEXEC;
}
@@ -301,7 +303,7 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab,
/* This is the symbol it is referring to */
sym = (Elf_Sym *)sechdrs[symindex].sh_addr
+ ELF_MIPS_R_SYM(rel[i]);
- if (!sym->st_value) {
+ if (IS_ERR_VALUE(sym->st_value)) {
/* Ignore unresolved weak symbol */
if (ELF_ST_BIND(sym->st_info) == STB_WEAK)
continue;
@@ -341,7 +343,7 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
/* This is the symbol it is referring to */
sym = (Elf_Sym *)sechdrs[symindex].sh_addr
+ ELF_MIPS_R_SYM(rel[i]);
- if (!sym->st_value) {
+ if (IS_ERR_VALUE(sym->st_value)) {
/* Ignore unresolved weak symbol */
if (ELF_ST_BIND(sym->st_info) == STB_WEAK)
continue;
diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c
index e0a4ac18fa0..26109c4d517 100644
--- a/arch/mips/kernel/proc.c
+++ b/arch/mips/kernel/proc.c
@@ -1,6 +1,4 @@
/*
- * linux/arch/mips/kernel/proc.c
- *
* Copyright (C) 1995, 1996, 2001 Ralf Baechle
* Copyright (C) 2001, 2004 MIPS Technologies, Inc.
* Copyright (C) 2004 Maciej W. Rozycki
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index c09d681b718..f3d73e1831c 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -115,7 +115,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
{
struct thread_info *ti = task_thread_info(p);
struct pt_regs *childregs;
- long childksp;
+ unsigned long childksp;
p->set_child_tid = p->clear_child_tid = NULL;
childksp = (unsigned long)task_stack_page(p) + THREAD_SIZE - 32;
@@ -132,6 +132,8 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
/* set up new TSS. */
childregs = (struct pt_regs *) childksp - 1;
+ /* Put the stack after the struct pt_regs. */
+ childksp = (unsigned long) childregs;
*childregs = *regs;
childregs->regs[7] = 0; /* Clear error flag */
diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S
index 20a86e08fd5..b5708212353 100644
--- a/arch/mips/kernel/scall32-o32.S
+++ b/arch/mips/kernel/scall32-o32.S
@@ -654,6 +654,7 @@ einval: li v0, -ENOSYS
sys sys_pwritev 6
sys sys_rt_tgsigqueueinfo 4
sys sys_perf_counter_open 5
+ sys sys_accept4 4
.endm
/* We pre-compute the number of _instruction_ bytes needed to
diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S
index b046130d4c5..3d866f24e06 100644
--- a/arch/mips/kernel/scall64-64.S
+++ b/arch/mips/kernel/scall64-64.S
@@ -491,4 +491,5 @@ sys_call_table:
PTR sys_pwritev /* 5390 */
PTR sys_rt_tgsigqueueinfo
PTR sys_perf_counter_open
+ PTR sys_accept4
.size sys_call_table,.-sys_call_table
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S
index 15874f9812c..e855b118a07 100644
--- a/arch/mips/kernel/scall64-n32.S
+++ b/arch/mips/kernel/scall64-n32.S
@@ -417,4 +417,5 @@ EXPORT(sysn32_call_table)
PTR sys_pwritev
PTR compat_sys_rt_tgsigqueueinfo /* 5295 */
PTR sys_perf_counter_open
+ PTR sys_accept4
.size sysn32_call_table,.-sysn32_call_table
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index 781e0f1e953..0c49f1a660b 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -537,4 +537,5 @@ sys_call_table:
PTR compat_sys_pwritev
PTR compat_sys_rt_tgsigqueueinfo
PTR sys_perf_counter_open
+ PTR sys_accept4
.size sys_call_table,.-sys_call_table
diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c
index 8a0626cbb10..c16bb6d6c25 100644
--- a/arch/mips/kernel/smtc.c
+++ b/arch/mips/kernel/smtc.c
@@ -465,11 +465,8 @@ void smtc_prepare_cpus(int cpus)
smtc_configure_tlb();
for (tc = 0, vpe = 0 ; (vpe < nvpe) && (tc < ntc) ; vpe++) {
- /*
- * Set the MVP bits.
- */
- settc(tc);
- write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_MVP);
+ if (tcpervpe[vpe] == 0)
+ continue;
if (vpe != 0)
printk(", ");
printk("VPE %d: TC", vpe);
@@ -488,6 +485,12 @@ void smtc_prepare_cpus(int cpus)
}
if (vpe != 0) {
/*
+ * Allow this VPE to control others.
+ */
+ write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() |
+ VPECONF0_MVP);
+
+ /*
* Clear any stale software interrupts from VPE's Cause
*/
write_vpe_c0_cause(0);
diff --git a/arch/mips/kernel/stacktrace.c b/arch/mips/kernel/stacktrace.c
index 58f5cd76c8c..d52ff77baf3 100644
--- a/arch/mips/kernel/stacktrace.c
+++ b/arch/mips/kernel/stacktrace.c
@@ -1,6 +1,4 @@
/*
- * arch/mips/kernel/stacktrace.c
- *
* Stack trace management functions
*
* Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
diff --git a/arch/mips/kernel/vpe.c b/arch/mips/kernel/vpe.c
index 07b9ec2c6e3..9a1ab7e87fd 100644
--- a/arch/mips/kernel/vpe.c
+++ b/arch/mips/kernel/vpe.c
@@ -73,7 +73,7 @@ static int major;
static const int minor = 1; /* fixed for now */
#ifdef CONFIG_MIPS_APSP_KSPD
- static struct kspd_notifications kspd_events;
+static struct kspd_notifications kspd_events;
static int kspd_events_reqd = 0;
#endif
@@ -155,10 +155,9 @@ struct {
};
static void release_progmem(void *ptr);
-extern void save_gp_address(unsigned int secbase, unsigned int rel);
/* get the vpe associated with this minor */
-struct vpe *get_vpe(int minor)
+static struct vpe *get_vpe(int minor)
{
struct vpe *v;
@@ -174,7 +173,7 @@ struct vpe *get_vpe(int minor)
}
/* get the vpe associated with this minor */
-struct tc *get_tc(int index)
+static struct tc *get_tc(int index)
{
struct tc *t;
@@ -186,20 +185,8 @@ struct tc *get_tc(int index)
return NULL;
}
-struct tc *get_tc_unused(void)
-{
- struct tc *t;
-
- list_for_each_entry(t, &vpecontrol.tc_list, list) {
- if (t->state == TC_STATE_UNUSED)
- return t;
- }
-
- return NULL;
-}
-
/* allocate a vpe and associate it with this minor (or index) */
-struct vpe *alloc_vpe(int minor)
+static struct vpe *alloc_vpe(int minor)
{
struct vpe *v;
@@ -216,7 +203,7 @@ struct vpe *alloc_vpe(int minor)
}
/* allocate a tc. At startup only tc0 is running, all other can be halted. */
-struct tc *alloc_tc(int index)
+static struct tc *alloc_tc(int index)
{
struct tc *tc;
@@ -232,7 +219,7 @@ out:
}
/* clean up and free everything */
-void release_vpe(struct vpe *v)
+static void release_vpe(struct vpe *v)
{
list_del(&v->list);
if (v->load_addr)
@@ -240,7 +227,7 @@ void release_vpe(struct vpe *v)
kfree(v);
}
-void dump_mtregs(void)
+static void dump_mtregs(void)
{
unsigned long val;
@@ -327,7 +314,8 @@ static void layout_sections(struct module *mod, const Elf_Ehdr * hdr,
|| (s->sh_flags & masks[m][1])
|| s->sh_entsize != ~0UL)
continue;
- s->sh_entsize = get_offset(&mod->core_size, s);
+ s->sh_entsize =
+ get_offset((unsigned long *)&mod->core_size, s);
}
if (m == 0)
@@ -461,16 +449,15 @@ static int apply_r_mips_lo16(struct module *me, uint32_t *location,
{
unsigned long insnlo = *location;
Elf32_Addr val, vallo;
+ struct mips_hi16 *l, *next;
/* Sign extend the addend we extract from the lo insn. */
vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
if (mips_hi16_list != NULL) {
- struct mips_hi16 *l;
l = mips_hi16_list;
while (l != NULL) {
- struct mips_hi16 *next;
unsigned long insn;
/*
@@ -480,7 +467,7 @@ static int apply_r_mips_lo16(struct module *me, uint32_t *location,
printk(KERN_DEBUG "VPE loader: "
"apply_r_mips_lo16/hi16: \t"
"inconsistent value information\n");
- return -ENOEXEC;
+ goto out_free;
}
/*
@@ -518,6 +505,16 @@ static int apply_r_mips_lo16(struct module *me, uint32_t *location,
*location = insnlo;
return 0;
+
+out_free:
+ while (l != NULL) {
+ next = l->next;
+ kfree(l);
+ l = next;
+ }
+ mips_hi16_list = NULL;
+
+ return -ENOEXEC;
}
static int (*reloc_handlers[]) (struct module *me, uint32_t *location,
@@ -541,7 +538,7 @@ static char *rstrs[] = {
[R_MIPS_PC16] = "MIPS_PC16"
};
-int apply_relocations(Elf32_Shdr *sechdrs,
+static int apply_relocations(Elf32_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
@@ -586,7 +583,7 @@ int apply_relocations(Elf32_Shdr *sechdrs,
return 0;
}
-void save_gp_address(unsigned int secbase, unsigned int rel)
+static inline void save_gp_address(unsigned int secbase, unsigned int rel)
{
gp_addr = secbase + rel;
gp_offs = gp_addr - (secbase & 0xffff0000);
diff --git a/arch/mips/mipssim/sim_time.c b/arch/mips/mipssim/sim_time.c
index 0cea932f124..5492c42f765 100644
--- a/arch/mips/mipssim/sim_time.c
+++ b/arch/mips/mipssim/sim_time.c
@@ -89,13 +89,13 @@ unsigned __cpuinit get_c0_compare_int(void)
if (cpu_has_veic) {
set_vi_handler(MSC01E_INT_CPUCTR, mips_timer_dispatch);
mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR;
- } else {
-#endif
- {
- if (cpu_has_vint)
- set_vi_handler(cp0_compare_irq, mips_timer_dispatch);
- mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
+
+ return mips_cpu_timer_irq;
}
+#endif
+ if (cpu_has_vint)
+ set_vi_handler(cp0_compare_irq, mips_timer_dispatch);
+ mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
return mips_cpu_timer_irq;
}
diff --git a/arch/mips/mm/c-octeon.c b/arch/mips/mm/c-octeon.c
index b165cdcb281..10ab69f7183 100644
--- a/arch/mips/mm/c-octeon.c
+++ b/arch/mips/mm/c-octeon.c
@@ -289,7 +289,7 @@ static void cache_parity_error_octeon(int non_recoverable)
}
/**
- * Called when the the exception is not recoverable
+ * Called when the the exception is recoverable
*/
asmlinkage void cache_parity_error_octeon_recoverable(void)
@@ -298,7 +298,7 @@ asmlinkage void cache_parity_error_octeon_recoverable(void)
}
/**
- * Called when the the exception is recoverable
+ * Called when the the exception is not recoverable
*/
asmlinkage void cache_parity_error_octeon_non_recoverable(void)
diff --git a/arch/mips/mm/extable.c b/arch/mips/mm/extable.c
index 297fb9f390d..9d25d2ba4b9 100644
--- a/arch/mips/mm/extable.c
+++ b/arch/mips/mm/extable.c
@@ -1,5 +1,9 @@
/*
- * linux/arch/mips/mm/extable.c
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1997, 99, 2001 - 2004 Ralf Baechle <ralf@linux-mips.org>
*/
#include <linux/module.h>
#include <linux/spinlock.h>
diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c
index 6751ce9ede9..f956ecbb813 100644
--- a/arch/mips/mm/fault.c
+++ b/arch/mips/mm/fault.c
@@ -171,6 +171,7 @@ out_of_memory:
* We ran out of memory, call the OOM killer, and return the userspace
* (which will retry the fault, or kill us if we got oom-killed).
*/
+ up_read(&mm->mmap_sem);
pagefault_out_of_memory();
return;
diff --git a/arch/mips/mti-malta/malta-int.c b/arch/mips/mti-malta/malta-int.c
index a8756f82c31..3e0a9b35ba5 100644
--- a/arch/mips/mti-malta/malta-int.c
+++ b/arch/mips/mti-malta/malta-int.c
@@ -331,6 +331,7 @@ static struct irqaction irq_call = {
.flags = IRQF_DISABLED|IRQF_PERCPU,
.name = "IPI_call"
};
+#endif /* CONFIG_MIPS_MT_SMP */
static int gic_resched_int_base;
static int gic_call_int_base;
@@ -346,7 +347,6 @@ unsigned int plat_ipi_resched_int_xlate(unsigned int cpu)
{
return GIC_RESCHED_INT(cpu);
}
-#endif /* CONFIG_MIPS_MT_SMP */
static struct irqaction i8259irq = {
.handler = no_action,
diff --git a/arch/mips/nxp/pnx8550/common/time.c b/arch/mips/nxp/pnx8550/common/time.c
index 8df43e9e4d9..18b19278487 100644
--- a/arch/mips/nxp/pnx8550/common/time.c
+++ b/arch/mips/nxp/pnx8550/common/time.c
@@ -138,7 +138,7 @@ __init void plat_time_init(void)
* HZ timer interrupts per second.
*/
mips_hpt_frequency = 27UL * ((1000000UL * n)/(m * pow2p));
- cpj = (mips_hpt_frequency + HZ / 2) / HZ;
+ cpj = DIV_ROUND_CLOSEST(mips_hpt_frequency, HZ);
write_c0_count(0);
timer_ack();
diff --git a/arch/mips/pci/fixup-emma2rh.c b/arch/mips/pci/fixup-emma2rh.c
index fba5aad00d5..0d9ccf4dfc5 100644
--- a/arch/mips/pci/fixup-emma2rh.c
+++ b/arch/mips/pci/fixup-emma2rh.c
@@ -1,7 +1,4 @@
/*
- * arch/mips/pci/fixup-emma2rh.c
- * This file defines the PCI configration.
- *
* Copyright (C) NEC Electronics Corporation 2004-2006
*
* This file is based on the arch/mips/ddb5xxx/ddb5477/pci.c
diff --git a/arch/mips/pci/fixup-sb1250.c b/arch/mips/pci/fixup-sb1250.c
index 0ad39e53f7b..f0bb9146e6c 100644
--- a/arch/mips/pci/fixup-sb1250.c
+++ b/arch/mips/pci/fixup-sb1250.c
@@ -1,6 +1,4 @@
/*
- * arch/mips/pci/fixup-sb1250.c
- *
* Copyright (C) 2004, 2006 MIPS Technologies, Inc. All rights reserved.
* Author: Maciej W. Rozycki <macro@mips.com>
*
diff --git a/arch/mips/pci/ops-emma2rh.c b/arch/mips/pci/ops-emma2rh.c
index 5947a70b0b7..710aef5c070 100644
--- a/arch/mips/pci/ops-emma2rh.c
+++ b/arch/mips/pci/ops-emma2rh.c
@@ -1,7 +1,4 @@
/*
- * arch/mips/pci/ops-emma2rh.c
- * This file defines the PCI operation for EMMA2RH.
- *
* Copyright (C) NEC Electronics Corporation 2004-2006
*
* This file is based on the arch/mips/pci/ops-vr41xx.c
diff --git a/arch/mips/pci/pci-emma2rh.c b/arch/mips/pci/pci-emma2rh.c
index 2df4190232c..773e34ff4d1 100644
--- a/arch/mips/pci/pci-emma2rh.c
+++ b/arch/mips/pci/pci-emma2rh.c
@@ -1,7 +1,4 @@
/*
- * arch/mips/pci/pci-emma2rh.c
- * This file defines the PCI configration.
- *
* Copyright (C) NEC Electronics Corporation 2004-2006
*
* This file is based on the arch/mips/ddb5xxx/ddb5477/pci.c
diff --git a/arch/mips/pci/pci-tx4927.c b/arch/mips/pci/pci-tx4927.c
index aaa90059679..a5807406a7f 100644
--- a/arch/mips/pci/pci-tx4927.c
+++ b/arch/mips/pci/pci-tx4927.c
@@ -1,6 +1,4 @@
/*
- * linux/arch/mips/pci/pci-tx4927.c
- *
* Based on linux/arch/mips/txx9/rbtx4938/setup.c,
* and RBTX49xx patch from CELF patch archive.
*
diff --git a/arch/mips/pci/pci-tx4938.c b/arch/mips/pci/pci-tx4938.c
index 1ea257bc3b8..20e45f30b2e 100644
--- a/arch/mips/pci/pci-tx4938.c
+++ b/arch/mips/pci/pci-tx4938.c
@@ -1,6 +1,4 @@
/*
- * linux/arch/mips/pci/pci-tx4938.c
- *
* Based on linux/arch/mips/txx9/rbtx4938/setup.c,
* and RBTX49xx patch from CELF patch archive.
*
diff --git a/arch/mips/pci/pci-tx4939.c b/arch/mips/pci/pci-tx4939.c
index 5fecf1cdc32..9ef840693ba 100644
--- a/arch/mips/pci/pci-tx4939.c
+++ b/arch/mips/pci/pci-tx4939.c
@@ -1,6 +1,4 @@
/*
- * linux/arch/mips/pci/pci-tx4939.c
- *
* Based on linux/arch/mips/txx9/rbtx4939/setup.c,
* and RBTX49xx patch from CELF patch archive.
*
diff --git a/arch/mips/pci/pcie-octeon.c b/arch/mips/pci/pcie-octeon.c
index 75262247f3e..6aa5c542d52 100644
--- a/arch/mips/pci/pcie-octeon.c
+++ b/arch/mips/pci/pcie-octeon.c
@@ -1040,19 +1040,29 @@ static inline int octeon_pcie_read_config(int pcie_port, struct pci_bus *bus,
int bus_number = bus->number;
/*
- * We need to force the bus number to be zero on the root
- * bus. Linux numbers the 2nd root bus to start after all
- * buses on root 0.
+ * For the top level bus make sure our hardware bus number
+ * matches the software one.
*/
- if (bus->parent == NULL)
- bus_number = 0;
+ if (bus->parent == NULL) {
+ union cvmx_pciercx_cfg006 pciercx_cfg006;
+ pciercx_cfg006.u32 = cvmx_pcie_cfgx_read(pcie_port,
+ CVMX_PCIERCX_CFG006(pcie_port));
+ if (pciercx_cfg006.s.pbnum != bus_number) {
+ pciercx_cfg006.s.pbnum = bus_number;
+ pciercx_cfg006.s.sbnum = bus_number;
+ pciercx_cfg006.s.subbnum = bus_number;
+ cvmx_pcie_cfgx_write(pcie_port,
+ CVMX_PCIERCX_CFG006(pcie_port),
+ pciercx_cfg006.u32);
+ }
+ }
/*
* PCIe only has a single device connected to Octeon. It is
* always device ID 0. Don't bother doing reads for other
* device IDs on the first segment.
*/
- if ((bus_number == 0) && (devfn >> 3 != 0))
+ if ((bus->parent == NULL) && (devfn >> 3 != 0))
return PCIBIOS_FUNC_NOT_SUPPORTED;
/*
@@ -1070,7 +1080,7 @@ static inline int octeon_pcie_read_config(int pcie_port, struct pci_bus *bus,
* bridge only respondes to device ID 0, function
* 0-1
*/
- if ((bus_number == 0) && (devfn >= 2))
+ if ((bus->parent == NULL) && (devfn >= 2))
return PCIBIOS_FUNC_NOT_SUPPORTED;
/*
* The PCI-X slots are device ID 2,3. Choose one of
@@ -1167,13 +1177,6 @@ static inline int octeon_pcie_write_config(int pcie_port, struct pci_bus *bus,
int size, u32 val)
{
int bus_number = bus->number;
- /*
- * We need to force the bus number to be zero on the root
- * bus. Linux numbers the 2nd root bus to start after all
- * busses on root 0.
- */
- if (bus->parent == NULL)
- bus_number = 0;
switch (size) {
case 4:
diff --git a/arch/mips/pmc-sierra/msp71xx/gpio.c b/arch/mips/pmc-sierra/msp71xx/gpio.c
index 69848c5813e..aaccbe52438 100644
--- a/arch/mips/pmc-sierra/msp71xx/gpio.c
+++ b/arch/mips/pmc-sierra/msp71xx/gpio.c
@@ -1,6 +1,4 @@
/*
- * @file /arch/mips/pmc-sierra/msp71xx/gpio.c
- *
* Generic PMC MSP71xx GPIO handling. These base gpio are controlled by two
* types of registers. The data register sets the output level when in output
* mode and when in input mode will contain the value at the input. The config
diff --git a/arch/mips/pmc-sierra/msp71xx/gpio_extended.c b/arch/mips/pmc-sierra/msp71xx/gpio_extended.c
index fc6dbc6cf1c..2a99f360fae 100644
--- a/arch/mips/pmc-sierra/msp71xx/gpio_extended.c
+++ b/arch/mips/pmc-sierra/msp71xx/gpio_extended.c
@@ -1,6 +1,4 @@
/*
- * @file /arch/mips/pmc-sierra/msp71xx/gpio_extended.c
- *
* Generic PMC MSP71xx EXTENDED (EXD) GPIO handling. The extended gpio is
* a set of hardware registers that have no need for explicit locking as
* it is handled by unique method of writing individual set/clr bits.
diff --git a/arch/mips/pmc-sierra/msp71xx/msp_irq_slp.c b/arch/mips/pmc-sierra/msp71xx/msp_irq_slp.c
index f5f1b8d2bb9..61f39023234 100644
--- a/arch/mips/pmc-sierra/msp71xx/msp_irq_slp.c
+++ b/arch/mips/pmc-sierra/msp71xx/msp_irq_slp.c
@@ -45,13 +45,6 @@ static inline void mask_msp_slp_irq(unsigned int irq)
*/
static inline void ack_msp_slp_irq(unsigned int irq)
{
- mask_slp_irq(irq);
-
- /*
- * only really necessary for 18, 16-14 and sometimes 3:0 (since
- * these can be edge sensitive) but it doesn't hurt for the others.
- */
-
/* check for PER interrupt range */
if (irq < MSP_PER_INTBASE)
*SLP_INT_STS_REG = (1 << (irq - MSP_SLP_INTBASE));
@@ -62,8 +55,7 @@ static inline void ack_msp_slp_irq(unsigned int irq)
static struct irq_chip msp_slp_irq_controller = {
.name = "MSP_SLP",
.ack = ack_msp_slp_irq,
- .mask = ack_msp_slp_irq,
- .mask_ack = ack_msp_slp_irq,
+ .mask = mask_msp_slp_irq,
.unmask = unmask_msp_slp_irq,
};
@@ -79,7 +71,7 @@ void __init msp_slp_irq_init(void)
/* initialize all the IRQ descriptors */
for (i = MSP_SLP_INTBASE; i < MSP_PER_INTBASE + 32; i++)
- set_irq_chip_and_handler(i, &msp_slp_irq_controller
+ set_irq_chip_and_handler(i, &msp_slp_irq_controller,
handle_level_irq);
}
diff --git a/arch/mips/pmc-sierra/yosemite/atmel_read_eeprom.c b/arch/mips/pmc-sierra/yosemite/atmel_read_eeprom.c
index caf5e9a0acc..fc990cb3194 100644
--- a/arch/mips/pmc-sierra/yosemite/atmel_read_eeprom.c
+++ b/arch/mips/pmc-sierra/yosemite/atmel_read_eeprom.c
@@ -1,6 +1,4 @@
/*
- * arch/mips/pmc-sierra/yosemite/atmel_read_eeprom.c
- *
* Copyright (C) 2003 PMC-Sierra Inc.
* Author: Manish Lachwani (lachwani@pmc-sierra.com)
*
diff --git a/arch/mips/sibyte/swarm/swarm-i2c.c b/arch/mips/sibyte/swarm/swarm-i2c.c
index 4282ac9d01d..062505054d4 100644
--- a/arch/mips/sibyte/swarm/swarm-i2c.c
+++ b/arch/mips/sibyte/swarm/swarm-i2c.c
@@ -1,6 +1,4 @@
/*
- * arch/mips/sibyte/swarm/swarm-i2c.c
- *
* Broadcom BCM91250A (SWARM), etc. I2C platform setup.
*
* Copyright (c) 2008 Maciej W. Rozycki
diff --git a/arch/mips/txx9/generic/mem_tx4927.c b/arch/mips/txx9/generic/mem_tx4927.c
index ef6ea6e9787..70f9626f822 100644
--- a/arch/mips/txx9/generic/mem_tx4927.c
+++ b/arch/mips/txx9/generic/mem_tx4927.c
@@ -1,6 +1,4 @@
/*
- * linux/arch/mips/txx9/generic/mem_tx4927.c
- *
* common tx4927 memory interface
*
* Author: MontaVista Software, Inc.
diff --git a/arch/mips/txx9/generic/setup.c b/arch/mips/txx9/generic/setup.c
index 3b7d77d61ce..a205e2ba8e7 100644
--- a/arch/mips/txx9/generic/setup.c
+++ b/arch/mips/txx9/generic/setup.c
@@ -1,6 +1,4 @@
/*
- * linux/arch/mips/txx9/generic/setup.c
- *
* Based on linux/arch/mips/txx9/rbtx4938/setup.c,
* and RBTX49xx patch from CELF patch archive.
*
diff --git a/arch/mips/txx9/rbtx4939/setup.c b/arch/mips/txx9/rbtx4939/setup.c
index c033ffe71cd..b0c241ecf60 100644
--- a/arch/mips/txx9/rbtx4939/setup.c
+++ b/arch/mips/txx9/rbtx4939/setup.c
@@ -512,10 +512,10 @@ static void __init rbtx4939_setup(void)
rbtx4939_ebusc_setup();
/* always enable ATA0 */
txx9_set64(&tx4939_ccfgptr->pcfg, TX4939_PCFG_ATA0MODE);
- rbtx4939_update_ioc_pen();
if (txx9_master_clock == 0)
txx9_master_clock = 20000000;
tx4939_setup();
+ rbtx4939_update_ioc_pen();
#ifdef HAVE_RBTX4939_IOSWAB
ioswabw = rbtx4939_ioswabw;
__mem_ioswabw = rbtx4939_mem_ioswabw;
diff --git a/arch/mn10300/include/asm/pci.h b/arch/mn10300/include/asm/pci.h
index 35d2ed6396f..19aecc90f7a 100644
--- a/arch/mn10300/include/asm/pci.h
+++ b/arch/mn10300/include/asm/pci.h
@@ -59,7 +59,6 @@ void pcibios_penalize_isa_irq(int irq);
#include <linux/slab.h>
#include <asm/scatterlist.h>
#include <linux/string.h>
-#include <linux/mm.h>
#include <asm/io.h>
struct pci_dev;
diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S
index ae3e70cd1e1..e552e547cb9 100644
--- a/arch/parisc/kernel/entry.S
+++ b/arch/parisc/kernel/entry.S
@@ -553,7 +553,7 @@
* on most of those machines only handles cache transactions.
*/
extrd,u,*= \pte,_PAGE_NO_CACHE_BIT+32,1,%r0
- depi 1,12,1,\prot
+ depdi 1,12,1,\prot
/* Drop prot bits and convert to page addr for iitlbt and idtlbt */
convert_for_tlb_insert20 \pte
diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c
index ef5caf2e6ed..61ee0eec4e6 100644
--- a/arch/parisc/kernel/module.c
+++ b/arch/parisc/kernel/module.c
@@ -86,8 +86,12 @@
* the bottom of the table, which has a maximum signed displacement of
* 0x3fff; however, since we're only going forward, this becomes
* 0x1fff, and thus, since each GOT entry is 8 bytes long we can have
- * at most 1023 entries */
-#define MAX_GOTS 1023
+ * at most 1023 entries.
+ * To overcome this 14bit displacement with some kernel modules, we'll
+ * use instead the unusal 16bit displacement method (see reassemble_16a)
+ * which gives us a maximum positive displacement of 0x7fff, and as such
+ * allows us to allocate up to 4095 GOT entries. */
+#define MAX_GOTS 4095
/* three functions to determine where in the module core
* or init pieces the location is */
@@ -145,12 +149,40 @@ struct stub_entry {
/* The reassemble_* functions prepare an immediate value for
insertion into an opcode. pa-risc uses all sorts of weird bitfields
in the instruction to hold the value. */
+static inline int sign_unext(int x, int len)
+{
+ int len_ones;
+
+ len_ones = (1 << len) - 1;
+ return x & len_ones;
+}
+
+static inline int low_sign_unext(int x, int len)
+{
+ int sign, temp;
+
+ sign = (x >> (len-1)) & 1;
+ temp = sign_unext(x, len-1);
+ return (temp << 1) | sign;
+}
+
static inline int reassemble_14(int as14)
{
return (((as14 & 0x1fff) << 1) |
((as14 & 0x2000) >> 13));
}
+static inline int reassemble_16a(int as16)
+{
+ int s, t;
+
+ /* Unusual 16-bit encoding, for wide mode only. */
+ t = (as16 << 1) & 0xffff;
+ s = (as16 & 0x8000);
+ return (t ^ s ^ (s >> 1)) | (s >> 15);
+}
+
+
static inline int reassemble_17(int as17)
{
return (((as17 & 0x10000) >> 16) |
@@ -407,6 +439,7 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
enum elf_stub_type stub_type, Elf_Addr loc0, unsigned int targetsec)
{
struct stub_entry *stub;
+ int __maybe_unused d;
/* initialize stub_offset to point in front of the section */
if (!me->arch.section[targetsec].stub_offset) {
@@ -460,12 +493,19 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
*/
switch (stub_type) {
case ELF_STUB_GOT:
- stub->insns[0] = 0x537b0000; /* ldd 0(%dp),%dp */
+ d = get_got(me, value, addend);
+ if (d <= 15) {
+ /* Format 5 */
+ stub->insns[0] = 0x0f6010db; /* ldd 0(%dp),%dp */
+ stub->insns[0] |= low_sign_unext(d, 5) << 16;
+ } else {
+ /* Format 3 */
+ stub->insns[0] = 0x537b0000; /* ldd 0(%dp),%dp */
+ stub->insns[0] |= reassemble_16a(d);
+ }
stub->insns[1] = 0x53610020; /* ldd 10(%dp),%r1 */
stub->insns[2] = 0xe820d000; /* bve (%r1) */
stub->insns[3] = 0x537b0030; /* ldd 18(%dp),%dp */
-
- stub->insns[0] |= reassemble_14(get_got(me, value, addend) & 0x3fff);
break;
case ELF_STUB_MILLI:
stub->insns[0] = 0x20200000; /* ldil 0,%r1 */
diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c
index 528f0ff9b27..8b58bf0b7d5 100644
--- a/arch/parisc/kernel/traps.c
+++ b/arch/parisc/kernel/traps.c
@@ -532,7 +532,7 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
/* Kill the user process later */
regs->iaoq[0] = 0 | 3;
regs->iaoq[1] = regs->iaoq[0] + 4;
- regs->iasq[0] = regs->iasq[0] = regs->sr[7];
+ regs->iasq[0] = regs->iasq[1] = regs->sr[7];
regs->gr[0] &= ~PSW_B;
return;
}
diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig
index e28e65e7a0e..7de127e4cee 100644
--- a/arch/powerpc/configs/ps3_defconfig
+++ b/arch/powerpc/configs/ps3_defconfig
@@ -1,13 +1,14 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.30-rc5
-# Fri May 15 10:37:00 2009
+# Linux kernel version: 2.6.31-rc7
+# Mon Aug 24 17:38:50 2009
#
CONFIG_PPC64=y
#
# Processor support
#
+CONFIG_PPC_BOOK3S_64=y
CONFIG_PPC_BOOK3S=y
# CONFIG_POWER4_ONLY is not set
CONFIG_POWER3=y
@@ -20,6 +21,7 @@ CONFIG_PPC_STD_MMU=y
CONFIG_PPC_STD_MMU_64=y
CONFIG_PPC_MM_SLICES=y
CONFIG_VIRT_CPU_ACCOUNTING=y
+CONFIG_PPC_HAVE_PMU_SUPPORT=y
CONFIG_SMP=y
CONFIG_NR_CPUS=2
CONFIG_64BIT=y
@@ -31,6 +33,7 @@ CONFIG_GENERIC_TIME=y
CONFIG_GENERIC_TIME_VSYSCALL=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
CONFIG_HAVE_SETUP_PER_CPU_AREA=y
CONFIG_IRQ_PER_CPU=y
CONFIG_STACKTRACE_SUPPORT=y
@@ -41,7 +44,6 @@ CONFIG_RWSEM_XCHGADD_ALGORITHM=y
CONFIG_ARCH_HAS_ILOG2_U32=y
CONFIG_ARCH_HAS_ILOG2_U64=y
CONFIG_GENERIC_HWEIGHT=y
-CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_GENERIC_FIND_NEXT_BIT=y
CONFIG_ARCH_NO_VIRT_TO_BUS=y
CONFIG_PPC=y
@@ -62,6 +64,7 @@ CONFIG_DTC=y
# CONFIG_PPC_DCR_MMIO is not set
CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
#
# General setup
@@ -113,7 +116,6 @@ CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_KALLSYMS_EXTRA_PASS=y
-# CONFIG_STRIP_ASM_SYMS is not set
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
@@ -126,7 +128,14 @@ CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
CONFIG_AIO=y
+CONFIG_HAVE_PERF_COUNTERS=y
+
+#
+# Performance Counters
+#
+# CONFIG_PERF_COUNTERS is not set
CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_STRIP_ASM_SYMS is not set
# CONFIG_COMPAT_BRK is not set
CONFIG_SLAB=y
# CONFIG_SLUB is not set
@@ -145,6 +154,11 @@ CONFIG_HAVE_KRETPROBES=y
CONFIG_HAVE_ARCH_TRACEHOOK=y
CONFIG_HAVE_DMA_ATTRS=y
CONFIG_USE_GENERIC_SMP_HELPERS=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
# CONFIG_SLOW_WORK is not set
# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
CONFIG_SLABINFO=y
@@ -210,7 +224,7 @@ CONFIG_PPC_CELL=y
#
# Cell Broadband Engine options
#
-CONFIG_SPU_FS=y
+CONFIG_SPU_FS=m
CONFIG_SPU_FS_64K_LS=y
# CONFIG_SPU_TRACE is not set
CONFIG_SPU_BASE=y
@@ -255,6 +269,7 @@ CONFIG_BINFMT_MISC=y
CONFIG_HUGETLB_PAGE_SIZE_VARIABLE=y
# CONFIG_IOMMU_VMERGE is not set
CONFIG_IOMMU_HELPER=y
+# CONFIG_SWIOTLB is not set
CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
CONFIG_ARCH_HAS_WALK_MEMORY=y
CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y
@@ -285,9 +300,9 @@ CONFIG_MIGRATION=y
CONFIG_PHYS_ADDR_T_64BIT=y
CONFIG_ZONE_DMA_FLAG=1
CONFIG_BOUNCE=y
-CONFIG_UNEVICTABLE_LRU=y
CONFIG_HAVE_MLOCK=y
CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
CONFIG_ARCH_MEMORY_PROBE=y
CONFIG_PPC_HAS_HASH_64K=y
CONFIG_PPC_4K_PAGES=y
@@ -399,6 +414,7 @@ CONFIG_IPV6_NDISC_NODETYPE=y
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
# CONFIG_NET_SCHED is not set
# CONFIG_DCB is not set
@@ -433,11 +449,14 @@ CONFIG_BT_HCIBTUSB=m
CONFIG_WIRELESS=y
CONFIG_CFG80211=m
# CONFIG_CFG80211_REG_DEBUG is not set
+# CONFIG_CFG80211_DEBUGFS is not set
# CONFIG_WIRELESS_OLD_REGULATORY is not set
CONFIG_WIRELESS_EXT=y
# CONFIG_WIRELESS_EXT_SYSFS is not set
# CONFIG_LIB80211 is not set
CONFIG_MAC80211=m
+CONFIG_MAC80211_DEFAULT_PS=y
+CONFIG_MAC80211_DEFAULT_PS_VALUE=1
#
# Rate control algorithm selection
@@ -447,7 +466,6 @@ CONFIG_MAC80211_RC_PID=y
CONFIG_MAC80211_RC_DEFAULT_PID=y
# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set
CONFIG_MAC80211_RC_DEFAULT="pid"
-# CONFIG_MAC80211_MESH is not set
# CONFIG_MAC80211_LEDS is not set
# CONFIG_MAC80211_DEBUGFS is not set
# CONFIG_MAC80211_DEBUG_MENU is not set
@@ -472,77 +490,7 @@ CONFIG_EXTRA_FIRMWARE=""
# CONFIG_DEBUG_DEVRES is not set
# CONFIG_SYS_HYPERVISOR is not set
# CONFIG_CONNECTOR is not set
-CONFIG_MTD=y
-CONFIG_MTD_DEBUG=y
-CONFIG_MTD_DEBUG_VERBOSE=0
-# CONFIG_MTD_CONCAT is not set
-# CONFIG_MTD_PARTITIONS is not set
-# CONFIG_MTD_TESTS is not set
-
-#
-# User Modules And Translation Layers
-#
-# CONFIG_MTD_CHAR is not set
-CONFIG_MTD_BLKDEVS=y
-CONFIG_MTD_BLOCK=y
-# CONFIG_FTL is not set
-# CONFIG_NFTL is not set
-# CONFIG_INFTL is not set
-# CONFIG_RFD_FTL is not set
-# CONFIG_SSFDC is not set
-# CONFIG_MTD_OOPS is not set
-
-#
-# RAM/ROM/Flash chip drivers
-#
-# CONFIG_MTD_CFI is not set
-# CONFIG_MTD_JEDECPROBE is not set
-CONFIG_MTD_MAP_BANK_WIDTH_1=y
-CONFIG_MTD_MAP_BANK_WIDTH_2=y
-CONFIG_MTD_MAP_BANK_WIDTH_4=y
-# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
-# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
-# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
-CONFIG_MTD_CFI_I1=y
-CONFIG_MTD_CFI_I2=y
-# CONFIG_MTD_CFI_I4 is not set
-# CONFIG_MTD_CFI_I8 is not set
-# CONFIG_MTD_RAM is not set
-# CONFIG_MTD_ROM is not set
-# CONFIG_MTD_ABSENT is not set
-
-#
-# Mapping drivers for chip access
-#
-# CONFIG_MTD_COMPLEX_MAPPINGS is not set
-# CONFIG_MTD_PLATRAM is not set
-
-#
-# Self-contained MTD device drivers
-#
-# CONFIG_MTD_SLRAM is not set
-# CONFIG_MTD_PHRAM is not set
-# CONFIG_MTD_MTDRAM is not set
-# CONFIG_MTD_BLOCK2MTD is not set
-
-#
-# Disk-On-Chip Device Drivers
-#
-# CONFIG_MTD_DOC2000 is not set
-# CONFIG_MTD_DOC2001 is not set
-# CONFIG_MTD_DOC2001PLUS is not set
-# CONFIG_MTD_NAND is not set
-# CONFIG_MTD_ONENAND is not set
-
-#
-# LPDDR flash memory drivers
-#
-# CONFIG_MTD_LPDDR is not set
-
-#
-# UBI - Unsorted block images
-#
-# CONFIG_MTD_UBI is not set
+# CONFIG_MTD is not set
CONFIG_OF_DEVICE=y
# CONFIG_PARPORT is not set
CONFIG_BLK_DEV=y
@@ -590,10 +538,6 @@ CONFIG_BLK_DEV_SR=y
# CONFIG_BLK_DEV_SR_VENDOR is not set
CONFIG_CHR_DEV_SG=m
# CONFIG_CHR_DEV_SCH is not set
-
-#
-# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
-#
CONFIG_SCSI_MULTI_LUN=y
# CONFIG_SCSI_CONSTANTS is not set
# CONFIG_SCSI_LOGGING is not set
@@ -626,7 +570,6 @@ CONFIG_BLK_DEV_DM=m
# CONFIG_DM_UEVENT is not set
# CONFIG_MACINTOSH_DRIVERS is not set
CONFIG_NETDEVICES=y
-CONFIG_COMPAT_NET_DEV_OPS=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
@@ -646,10 +589,11 @@ CONFIG_MII=m
# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
# CONFIG_B44 is not set
+# CONFIG_KS8842 is not set
CONFIG_NETDEV_1000=y
CONFIG_GELIC_NET=y
CONFIG_GELIC_WIRELESS=y
-CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE=y
+# CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE is not set
# CONFIG_NETDEV_10000 is not set
#
@@ -669,8 +613,7 @@ CONFIG_WLAN_80211=y
# CONFIG_HOSTAP is not set
# CONFIG_B43 is not set
# CONFIG_B43LEGACY is not set
-CONFIG_ZD1211RW=m
-# CONFIG_ZD1211RW_DEBUG is not set
+# CONFIG_ZD1211RW is not set
# CONFIG_RT2X00 is not set
#
@@ -682,7 +625,7 @@ CONFIG_ZD1211RW=m
#
# CONFIG_USB_CATC is not set
# CONFIG_USB_KAWETH is not set
-CONFIG_USB_PEGASUS=m
+# CONFIG_USB_PEGASUS is not set
# CONFIG_USB_RTL8150 is not set
CONFIG_USB_USBNET=m
CONFIG_USB_NET_AX8817X=m
@@ -693,10 +636,11 @@ CONFIG_USB_NET_AX8817X=m
# CONFIG_USB_NET_GL620A is not set
# CONFIG_USB_NET_NET1080 is not set
# CONFIG_USB_NET_PLUSB is not set
-CONFIG_USB_NET_MCS7830=m
+# CONFIG_USB_NET_MCS7830 is not set
# CONFIG_USB_NET_RNDIS_HOST is not set
# CONFIG_USB_NET_CDC_SUBSET is not set
# CONFIG_USB_NET_ZAURUS is not set
+# CONFIG_USB_NET_INT51X1 is not set
# CONFIG_WAN is not set
CONFIG_PPP=m
CONFIG_PPP_MULTILINK=y
@@ -771,8 +715,7 @@ CONFIG_DEVKMEM=y
#
CONFIG_UNIX98_PTYS=y
# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
-CONFIG_LEGACY_PTYS=y
-CONFIG_LEGACY_PTY_COUNT=16
+# CONFIG_LEGACY_PTYS is not set
# CONFIG_HVC_UDBG is not set
# CONFIG_IPMI_HANDLER is not set
# CONFIG_HW_RANDOM is not set
@@ -782,6 +725,11 @@ CONFIG_LEGACY_PTY_COUNT=16
# CONFIG_TCG_TPM is not set
# CONFIG_I2C is not set
# CONFIG_SPI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
# CONFIG_GPIOLIB is not set
# CONFIG_W1 is not set
@@ -805,22 +753,7 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_HTC_PASIC3 is not set
# CONFIG_MFD_TMIO is not set
# CONFIG_REGULATOR is not set
-
-#
-# Multimedia devices
-#
-
-#
-# Multimedia core support
-#
-# CONFIG_VIDEO_DEV is not set
-# CONFIG_DVB_CORE is not set
-# CONFIG_VIDEO_MEDIA is not set
-
-#
-# Multimedia drivers
-#
-# CONFIG_DAB is not set
+# CONFIG_MEDIA_SUPPORT is not set
#
# Graphics support
@@ -898,6 +831,11 @@ CONFIG_SND_SUPPORT_OLD_API=y
CONFIG_SND_VERBOSE_PROCFS=y
# CONFIG_SND_VERBOSE_PRINTK is not set
# CONFIG_SND_DEBUG is not set
+# CONFIG_SND_RAWMIDI_SEQ is not set
+# CONFIG_SND_OPL3_LIB_SEQ is not set
+# CONFIG_SND_OPL4_LIB_SEQ is not set
+# CONFIG_SND_SBAWE_SEQ is not set
+# CONFIG_SND_EMU10K1_SEQ is not set
# CONFIG_SND_DRIVERS is not set
CONFIG_SND_PPC=y
CONFIG_SND_PS3=m
@@ -930,29 +868,34 @@ CONFIG_USB_HIDDEV=y
# Special HID drivers
#
# CONFIG_HID_A4TECH is not set
-# CONFIG_HID_APPLE is not set
-# CONFIG_HID_BELKIN is not set
-# CONFIG_HID_CHERRY is not set
+CONFIG_HID_APPLE=m
+CONFIG_HID_BELKIN=m
+CONFIG_HID_CHERRY=m
# CONFIG_HID_CHICONY is not set
# CONFIG_HID_CYPRESS is not set
-# CONFIG_DRAGONRISE_FF is not set
-# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_DRAGONRISE is not set
+CONFIG_HID_EZKEY=m
# CONFIG_HID_KYE is not set
# CONFIG_HID_GYRATION is not set
# CONFIG_HID_KENSINGTON is not set
-# CONFIG_HID_LOGITECH is not set
-# CONFIG_HID_MICROSOFT is not set
+CONFIG_HID_LOGITECH=m
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_HID_MICROSOFT=m
# CONFIG_HID_MONTEREY is not set
# CONFIG_HID_NTRIG is not set
# CONFIG_HID_PANTHERLORD is not set
# CONFIG_HID_PETALYNX is not set
# CONFIG_HID_SAMSUNG is not set
CONFIG_HID_SONY=m
-# CONFIG_HID_SUNPLUS is not set
-# CONFIG_GREENASIA_FF is not set
+CONFIG_HID_SUNPLUS=m
+# CONFIG_HID_GREENASIA is not set
+CONFIG_HID_SMARTJOYPLUS=m
+# CONFIG_SMARTJOYPLUS_FF is not set
# CONFIG_HID_TOPSEED is not set
-# CONFIG_THRUSTMASTER_FF is not set
-# CONFIG_ZEROPLUS_FF is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_WACOM is not set
+# CONFIG_HID_ZEROPLUS is not set
CONFIG_USB_SUPPORT=y
CONFIG_USB_ARCH_HAS_HCD=y
CONFIG_USB_ARCH_HAS_OHCI=y
@@ -988,6 +931,8 @@ CONFIG_USB_EHCI_BIG_ENDIAN_MMIO=y
# CONFIG_USB_ISP116X_HCD is not set
# CONFIG_USB_ISP1760_HCD is not set
CONFIG_USB_OHCI_HCD=m
+# CONFIG_USB_OHCI_HCD_PPC_OF_BE is not set
+# CONFIG_USB_OHCI_HCD_PPC_OF_LE is not set
# CONFIG_USB_OHCI_HCD_PPC_OF is not set
# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y
@@ -1115,6 +1060,10 @@ CONFIG_RTC_DRV_PS3=m
# CONFIG_DMADEVICES is not set
# CONFIG_AUXDISPLAY is not set
# CONFIG_UIO is not set
+
+#
+# TI VLYNQ
+#
# CONFIG_STAGING is not set
#
@@ -1141,11 +1090,12 @@ CONFIG_FS_MBCACHE=y
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_FS_POSIX_ACL is not set
-CONFIG_FILE_LOCKING=y
# CONFIG_XFS_FS is not set
# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_BTRFS_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
CONFIG_DNOTIFY=y
CONFIG_INOTIFY=y
CONFIG_INOTIFY_USER=y
@@ -1205,7 +1155,6 @@ CONFIG_MISC_FILESYSTEMS=y
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
-# CONFIG_JFFS2_FS is not set
# CONFIG_CRAMFS is not set
# CONFIG_SQUASHFS is not set
# CONFIG_VXFS_FS is not set
@@ -1222,6 +1171,7 @@ CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
# CONFIG_NFS_V3_ACL is not set
CONFIG_NFS_V4=y
+# CONFIG_NFS_V4_1 is not set
CONFIG_ROOT_NFS=y
# CONFIG_NFSD is not set
CONFIG_LOCKD=y
@@ -1359,7 +1309,6 @@ CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_DEBUG_LIST=y
# CONFIG_DEBUG_SG is not set
# CONFIG_DEBUG_NOTIFIERS is not set
-# CONFIG_BOOT_PRINTK_DELAY is not set
# CONFIG_RCU_TORTURE_TEST is not set
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
# CONFIG_BACKTRACE_SELF_TEST is not set
@@ -1374,31 +1323,21 @@ CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
CONFIG_HAVE_DYNAMIC_FTRACE=y
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
CONFIG_RING_BUFFER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
CONFIG_TRACING=y
CONFIG_TRACING_SUPPORT=y
-
-#
-# Tracers
-#
-# CONFIG_FUNCTION_TRACER is not set
-# CONFIG_IRQSOFF_TRACER is not set
-# CONFIG_SCHED_TRACER is not set
-# CONFIG_CONTEXT_SWITCH_TRACER is not set
-# CONFIG_EVENT_TRACER is not set
-# CONFIG_BOOT_TRACER is not set
-# CONFIG_TRACE_BRANCH_PROFILING is not set
-# CONFIG_STACK_TRACER is not set
-# CONFIG_KMEMTRACE is not set
-# CONFIG_WORKQUEUE_TRACER is not set
-# CONFIG_BLK_DEV_IO_TRACE is not set
-# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_FTRACE is not set
# CONFIG_DYNAMIC_DEBUG is not set
# CONFIG_SAMPLES is not set
CONFIG_HAVE_ARCH_KGDB=y
# CONFIG_KGDB is not set
+# CONFIG_PPC_DISABLE_WERROR is not set
+CONFIG_PPC_WERROR=y
CONFIG_PRINT_STACK_DEPTH=64
CONFIG_DEBUG_STACKOVERFLOW=y
# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_PPC_EMULATED_STATS is not set
# CONFIG_CODE_PATCHING_SELFTEST is not set
# CONFIG_FTR_FIXUP_SELFTEST is not set
# CONFIG_MSI_BITMAP_SELFTEST is not set
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index dfdf13c9fef..fddc3ed715f 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -34,7 +34,7 @@
#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
/* We don't currently support large pages. */
-#define KVM_PAGES_PER_HPAGE (1<<31)
+#define KVM_PAGES_PER_HPAGE (1UL << 31)
struct kvm;
struct kvm_run;
diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c
index 20a60d661ba..ccf129d47d8 100644
--- a/arch/powerpc/kernel/dma.c
+++ b/arch/powerpc/kernel/dma.c
@@ -7,6 +7,7 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
+#include <linux/lmb.h>
#include <asm/bug.h>
#include <asm/abs_addr.h>
@@ -90,11 +91,10 @@ static void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sg,
static int dma_direct_dma_supported(struct device *dev, u64 mask)
{
#ifdef CONFIG_PPC64
- /* Could be improved to check for memory though it better be
- * done via some global so platforms can set the limit in case
+ /* Could be improved so platforms can set the limit in case
* they have limited DMA windows
*/
- return mask >= DMA_BIT_MASK(32);
+ return mask >= (lmb_end_of_DRAM() - 1);
#else
return 1;
#endif
diff --git a/arch/powerpc/kernel/mpc7450-pmu.c b/arch/powerpc/kernel/mpc7450-pmu.c
index c244133c67a..cc466d039af 100644
--- a/arch/powerpc/kernel/mpc7450-pmu.c
+++ b/arch/powerpc/kernel/mpc7450-pmu.c
@@ -407,7 +407,8 @@ struct power_pmu mpc7450_pmu = {
static int init_mpc7450_pmu(void)
{
- if (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc/7450"))
+ if (!cur_cpu_spec->oprofile_cpu_type ||
+ strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc/7450"))
return -ENODEV;
return register_power_pmu(&mpc7450_pmu);
diff --git a/arch/powerpc/kernel/perf_counter.c b/arch/powerpc/kernel/perf_counter.c
index 809fdf94b95..70e1f57f7dd 100644
--- a/arch/powerpc/kernel/perf_counter.c
+++ b/arch/powerpc/kernel/perf_counter.c
@@ -518,6 +518,8 @@ void hw_perf_disable(void)
struct cpu_hw_counters *cpuhw;
unsigned long flags;
+ if (!ppmu)
+ return;
local_irq_save(flags);
cpuhw = &__get_cpu_var(cpu_hw_counters);
@@ -572,6 +574,8 @@ void hw_perf_enable(void)
int n_lim;
int idx;
+ if (!ppmu)
+ return;
local_irq_save(flags);
cpuhw = &__get_cpu_var(cpu_hw_counters);
if (!cpuhw->disabled) {
@@ -737,6 +741,8 @@ int hw_perf_group_sched_in(struct perf_counter *group_leader,
long i, n, n0;
struct perf_counter *sub;
+ if (!ppmu)
+ return 0;
cpuhw = &__get_cpu_var(cpu_hw_counters);
n0 = cpuhw->n_counters;
n = collect_events(group_leader, ppmu->n_counter - n0,
@@ -1281,6 +1287,8 @@ void hw_perf_counter_setup(int cpu)
{
struct cpu_hw_counters *cpuhw = &per_cpu(cpu_hw_counters, cpu);
+ if (!ppmu)
+ return;
memset(cpuhw, 0, sizeof(*cpuhw));
cpuhw->mmcr[0] = MMCR0_FC;
}
diff --git a/arch/powerpc/kernel/power4-pmu.c b/arch/powerpc/kernel/power4-pmu.c
index db90b0c5c27..3c90a3d9173 100644
--- a/arch/powerpc/kernel/power4-pmu.c
+++ b/arch/powerpc/kernel/power4-pmu.c
@@ -606,7 +606,8 @@ static struct power_pmu power4_pmu = {
static int init_power4_pmu(void)
{
- if (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power4"))
+ if (!cur_cpu_spec->oprofile_cpu_type ||
+ strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power4"))
return -ENODEV;
return register_power_pmu(&power4_pmu);
diff --git a/arch/powerpc/kernel/power5+-pmu.c b/arch/powerpc/kernel/power5+-pmu.c
index f4adca8e98a..31918af3e35 100644
--- a/arch/powerpc/kernel/power5+-pmu.c
+++ b/arch/powerpc/kernel/power5+-pmu.c
@@ -678,8 +678,9 @@ static struct power_pmu power5p_pmu = {
static int init_power5p_pmu(void)
{
- if (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power5+")
- && strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power5++"))
+ if (!cur_cpu_spec->oprofile_cpu_type ||
+ (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power5+")
+ && strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power5++")))
return -ENODEV;
return register_power_pmu(&power5p_pmu);
diff --git a/arch/powerpc/kernel/power5-pmu.c b/arch/powerpc/kernel/power5-pmu.c
index 29b2c6c0e83..867f6f66396 100644
--- a/arch/powerpc/kernel/power5-pmu.c
+++ b/arch/powerpc/kernel/power5-pmu.c
@@ -618,7 +618,8 @@ static struct power_pmu power5_pmu = {
static int init_power5_pmu(void)
{
- if (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power5"))
+ if (!cur_cpu_spec->oprofile_cpu_type ||
+ strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power5"))
return -ENODEV;
return register_power_pmu(&power5_pmu);
diff --git a/arch/powerpc/kernel/power6-pmu.c b/arch/powerpc/kernel/power6-pmu.c
index 09ae5bf5bda..fa21890531d 100644
--- a/arch/powerpc/kernel/power6-pmu.c
+++ b/arch/powerpc/kernel/power6-pmu.c
@@ -537,7 +537,8 @@ static struct power_pmu power6_pmu = {
static int init_power6_pmu(void)
{
- if (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power6"))
+ if (!cur_cpu_spec->oprofile_cpu_type ||
+ strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power6"))
return -ENODEV;
return register_power_pmu(&power6_pmu);
diff --git a/arch/powerpc/kernel/power7-pmu.c b/arch/powerpc/kernel/power7-pmu.c
index 5a9f5cbd40a..018d094d92f 100644
--- a/arch/powerpc/kernel/power7-pmu.c
+++ b/arch/powerpc/kernel/power7-pmu.c
@@ -317,7 +317,7 @@ static int power7_generic_events[] = {
*/
static int power7_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
[C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */
- [C(OP_READ)] = { 0x400f0, 0xc880 },
+ [C(OP_READ)] = { 0xc880, 0x400f0 },
[C(OP_WRITE)] = { 0, 0x300f0 },
[C(OP_PREFETCH)] = { 0xd8b8, 0 },
},
@@ -327,8 +327,8 @@ static int power7_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
[C(OP_PREFETCH)] = { 0x408a, 0 },
},
[C(LL)] = { /* RESULT_ACCESS RESULT_MISS */
- [C(OP_READ)] = { 0x6080, 0x6084 },
- [C(OP_WRITE)] = { 0x6082, 0x6086 },
+ [C(OP_READ)] = { 0x16080, 0x26080 },
+ [C(OP_WRITE)] = { 0x16082, 0x26082 },
[C(OP_PREFETCH)] = { 0, 0 },
},
[C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */
@@ -366,7 +366,8 @@ static struct power_pmu power7_pmu = {
static int init_power7_pmu(void)
{
- if (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power7"))
+ if (!cur_cpu_spec->oprofile_cpu_type ||
+ strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power7"))
return -ENODEV;
return register_power_pmu(&power7_pmu);
diff --git a/arch/powerpc/kernel/ppc970-pmu.c b/arch/powerpc/kernel/ppc970-pmu.c
index 833097ac45d..75dccb71a04 100644
--- a/arch/powerpc/kernel/ppc970-pmu.c
+++ b/arch/powerpc/kernel/ppc970-pmu.c
@@ -488,8 +488,9 @@ static struct power_pmu ppc970_pmu = {
static int init_ppc970_pmu(void)
{
- if (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/970")
- && strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/970MP"))
+ if (!cur_cpu_spec->oprofile_cpu_type ||
+ (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/970")
+ && strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/970MP")))
return -ENODEV;
return register_power_pmu(&ppc970_pmu);
diff --git a/arch/powerpc/platforms/ps3/time.c b/arch/powerpc/platforms/ps3/time.c
index b178a1e66c9..40b5cb43300 100644
--- a/arch/powerpc/platforms/ps3/time.c
+++ b/arch/powerpc/platforms/ps3/time.c
@@ -21,6 +21,7 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
+#include <asm/firmware.h>
#include <asm/rtc.h>
#include <asm/lv1call.h>
#include <asm/ps3.h>
@@ -84,6 +85,9 @@ static int __init ps3_rtc_init(void)
{
struct platform_device *pdev;
+ if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+ return -ENODEV;
+
pdev = platform_device_register_simple("rtc-ps3", -1, NULL, 0);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
diff --git a/arch/powerpc/sysdev/xilinx_intc.c b/arch/powerpc/sysdev/xilinx_intc.c
index 3ee1fd37bbf..40edad52077 100644
--- a/arch/powerpc/sysdev/xilinx_intc.c
+++ b/arch/powerpc/sysdev/xilinx_intc.c
@@ -234,7 +234,6 @@ static void xilinx_i8259_cascade(unsigned int irq, struct irq_desc *desc)
generic_handle_irq(cascade_irq);
/* Let xilinx_intc end the interrupt */
- desc->chip->ack(irq);
desc->chip->unmask(irq);
}
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 8d15314381e..cae14c49951 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -208,6 +208,9 @@ static noinline __init void detect_machine_type(void)
machine_flags |= MACHINE_FLAG_KVM;
else
machine_flags |= MACHINE_FLAG_VM;
+
+ /* Store machine flags for setting up lowcore early */
+ S390_lowcore.machine_flags = machine_flags;
}
static __init void early_pgm_check_handler(void)
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 9717717c6fe..cbb897bc50b 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -154,6 +154,20 @@ static int __init condev_setup(char *str)
__setup("condev=", condev_setup);
+static void __init set_preferred_console(void)
+{
+ if (MACHINE_IS_KVM) {
+ add_preferred_console("hvc", 0, NULL);
+ s390_virtio_console_init();
+ return;
+ }
+
+ if (CONSOLE_IS_3215 || CONSOLE_IS_SCLP)
+ add_preferred_console("ttyS", 0, NULL);
+ if (CONSOLE_IS_3270)
+ add_preferred_console("tty3270", 0, NULL);
+}
+
static int __init conmode_setup(char *str)
{
#if defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE)
@@ -168,6 +182,7 @@ static int __init conmode_setup(char *str)
if (strncmp(str, "3270", 5) == 0)
SET_CONSOLE_3270;
#endif
+ set_preferred_console();
return 1;
}
@@ -780,9 +795,6 @@ static void __init setup_hwcaps(void)
void __init
setup_arch(char **cmdline_p)
{
- /* set up preferred console */
- add_preferred_console("ttyS", 0, NULL);
-
/*
* print what head.S has found out about the machine
*/
@@ -802,11 +814,9 @@ setup_arch(char **cmdline_p)
if (MACHINE_IS_VM)
pr_info("Linux is running as a z/VM "
"guest operating system in 64-bit mode\n");
- else if (MACHINE_IS_KVM) {
+ else if (MACHINE_IS_KVM)
pr_info("Linux is running under KVM in 64-bit mode\n");
- add_preferred_console("hvc", 0, NULL);
- s390_virtio_console_init();
- } else
+ else
pr_info("Linux is running natively in 64-bit mode\n");
#endif /* CONFIG_64BIT */
@@ -851,6 +861,7 @@ setup_arch(char **cmdline_p)
/* Setup default console */
conmode_default();
+ set_preferred_console();
/* Setup zfcpdump support */
setup_zfcpdump(console_devno);
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index f04f5301b1b..4d613415c43 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -386,7 +386,7 @@ no_timer:
}
__unset_cpu_idle(vcpu);
__set_current_state(TASK_RUNNING);
- remove_wait_queue(&vcpu->wq, &wait);
+ remove_wait_queue(&vcpu->arch.local_int.wq, &wait);
spin_unlock_bh(&vcpu->arch.local_int.lock);
spin_unlock(&vcpu->arch.local_int.float_int->lock);
hrtimer_try_to_cancel(&vcpu->arch.ckc_timer);
diff --git a/arch/s390/kvm/sigp.c b/arch/s390/kvm/sigp.c
index 36678835034..0ef81d6776e 100644
--- a/arch/s390/kvm/sigp.c
+++ b/arch/s390/kvm/sigp.c
@@ -169,7 +169,7 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
unsigned long *reg)
{
struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
- struct kvm_s390_local_interrupt *li;
+ struct kvm_s390_local_interrupt *li = NULL;
struct kvm_s390_interrupt_info *inti;
int rc;
u8 tmp;
@@ -189,9 +189,10 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
return 2; /* busy */
spin_lock(&fi->lock);
- li = fi->local_int[cpu_addr];
+ if (cpu_addr < KVM_MAX_VCPUS)
+ li = fi->local_int[cpu_addr];
- if ((cpu_addr >= KVM_MAX_VCPUS) || (li == NULL)) {
+ if (li == NULL) {
rc = 1; /* incorrect state */
*reg &= SIGP_STAT_INCORRECT_STATE;
kfree(inti);
diff --git a/arch/sh/boards/board-ap325rxa.c b/arch/sh/boards/board-ap325rxa.c
index 7ffd1b4315b..b9c88cc519e 100644
--- a/arch/sh/boards/board-ap325rxa.c
+++ b/arch/sh/boards/board-ap325rxa.c
@@ -547,7 +547,7 @@ static int __init ap325rxa_devices_setup(void)
return platform_add_devices(ap325rxa_devices,
ARRAY_SIZE(ap325rxa_devices));
}
-device_initcall(ap325rxa_devices_setup);
+arch_initcall(ap325rxa_devices_setup);
/* Return the board specific boot mode pin configuration */
static int ap325rxa_mode_pins(void)
diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c
index f70f4644deb..f9b2e4df35b 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -608,7 +608,7 @@ static int __init migor_devices_setup(void)
return platform_add_devices(migor_devices, ARRAY_SIZE(migor_devices));
}
-__initcall(migor_devices_setup);
+arch_initcall(migor_devices_setup);
/* Return the board specific boot mode pin configuration */
static int migor_mode_pins(void)
diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c
index 8fed45a2fb8..15456a0773b 100644
--- a/arch/sh/boards/mach-se/7724/setup.c
+++ b/arch/sh/boards/mach-se/7724/setup.c
@@ -238,7 +238,7 @@ static struct platform_device ceu1_device = {
},
};
-/* KEYSC */
+/* KEYSC in SoC (Needs SW33-2 set to ON) */
static struct sh_keysc_info keysc_info = {
.mode = SH_KEYSC_MODE_1,
.scan_timing = 10,
@@ -255,12 +255,13 @@ static struct sh_keysc_info keysc_info = {
static struct resource keysc_resources[] = {
[0] = {
- .start = 0x1a204000,
- .end = 0x1a20400f,
+ .name = "KEYSC",
+ .start = 0x044b0000,
+ .end = 0x044b000f,
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = IRQ0_KEY,
+ .start = 79,
.flags = IORESOURCE_IRQ,
},
};
diff --git a/arch/sh/kernel/cpu/sh2/setup-sh7619.c b/arch/sh/kernel/cpu/sh2/setup-sh7619.c
index 13798733f2d..8555c05e866 100644
--- a/arch/sh/kernel/cpu/sh2/setup-sh7619.c
+++ b/arch/sh/kernel/cpu/sh2/setup-sh7619.c
@@ -187,7 +187,7 @@ static int __init sh7619_devices_setup(void)
return platform_add_devices(sh7619_devices,
ARRAY_SIZE(sh7619_devices));
}
-__initcall(sh7619_devices_setup);
+arch_initcall(sh7619_devices_setup);
void __init plat_irq_setup(void)
{
diff --git a/arch/sh/kernel/cpu/sh2a/setup-mxg.c b/arch/sh/kernel/cpu/sh2a/setup-mxg.c
index 869c2da4820..b6737644531 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-mxg.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-mxg.c
@@ -238,7 +238,7 @@ static int __init mxg_devices_setup(void)
return platform_add_devices(mxg_devices,
ARRAY_SIZE(mxg_devices));
}
-__initcall(mxg_devices_setup);
+arch_initcall(mxg_devices_setup);
void __init plat_irq_setup(void)
{
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7201.c b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c
index d8febe12806..fbde5b75deb 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-sh7201.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c
@@ -357,7 +357,7 @@ static int __init sh7201_devices_setup(void)
return platform_add_devices(sh7201_devices,
ARRAY_SIZE(sh7201_devices));
}
-__initcall(sh7201_devices_setup);
+arch_initcall(sh7201_devices_setup);
void __init plat_irq_setup(void)
{
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7203.c b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c
index 62e3039d239..d3fd536c9a8 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-sh7203.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c
@@ -367,7 +367,7 @@ static int __init sh7203_devices_setup(void)
return platform_add_devices(sh7203_devices,
ARRAY_SIZE(sh7203_devices));
}
-__initcall(sh7203_devices_setup);
+arch_initcall(sh7203_devices_setup);
void __init plat_irq_setup(void)
{
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
index 3e6f3d7a58b..a9ccc5e8d9e 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
@@ -338,7 +338,7 @@ static int __init sh7206_devices_setup(void)
return platform_add_devices(sh7206_devices,
ARRAY_SIZE(sh7206_devices));
}
-__initcall(sh7206_devices_setup);
+arch_initcall(sh7206_devices_setup);
void __init plat_irq_setup(void)
{
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7705.c b/arch/sh/kernel/cpu/sh3/setup-sh7705.c
index 88f742fed9e..c2310598387 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh7705.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh7705.c
@@ -222,7 +222,7 @@ static int __init sh7705_devices_setup(void)
return platform_add_devices(sh7705_devices,
ARRAY_SIZE(sh7705_devices));
}
-__initcall(sh7705_devices_setup);
+arch_initcall(sh7705_devices_setup);
static struct platform_device *sh7705_early_devices[] __initdata = {
&tmu0_device,
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh770x.c b/arch/sh/kernel/cpu/sh3/setup-sh770x.c
index c5630679858..347ab35d069 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh770x.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh770x.c
@@ -250,7 +250,7 @@ static int __init sh770x_devices_setup(void)
return platform_add_devices(sh770x_devices,
ARRAY_SIZE(sh770x_devices));
}
-__initcall(sh770x_devices_setup);
+arch_initcall(sh770x_devices_setup);
static struct platform_device *sh770x_early_devices[] __initdata = {
&tmu0_device,
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7710.c b/arch/sh/kernel/cpu/sh3/setup-sh7710.c
index efa76c8148f..717e90ae109 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh7710.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh7710.c
@@ -226,7 +226,7 @@ static int __init sh7710_devices_setup(void)
return platform_add_devices(sh7710_devices,
ARRAY_SIZE(sh7710_devices));
}
-__initcall(sh7710_devices_setup);
+arch_initcall(sh7710_devices_setup);
static struct platform_device *sh7710_early_devices[] __initdata = {
&tmu0_device,
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7720.c b/arch/sh/kernel/cpu/sh3/setup-sh7720.c
index 5b2107798ed..74d8baaf8e9 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh7720.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh7720.c
@@ -388,7 +388,7 @@ static int __init sh7720_devices_setup(void)
return platform_add_devices(sh7720_devices,
ARRAY_SIZE(sh7720_devices));
}
-__initcall(sh7720_devices_setup);
+arch_initcall(sh7720_devices_setup);
static struct platform_device *sh7720_early_devices[] __initdata = {
&cmt0_device,
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh4-202.c b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c
index 6d088d12359..de4827df19a 100644
--- a/arch/sh/kernel/cpu/sh4/setup-sh4-202.c
+++ b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c
@@ -138,7 +138,7 @@ static int __init sh4202_devices_setup(void)
return platform_add_devices(sh4202_devices,
ARRAY_SIZE(sh4202_devices));
}
-__initcall(sh4202_devices_setup);
+arch_initcall(sh4202_devices_setup);
static struct platform_device *sh4202_early_devices[] __initdata = {
&tmu0_device,
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7750.c b/arch/sh/kernel/cpu/sh4/setup-sh7750.c
index 851672d15cf..1b8b122e8f3 100644
--- a/arch/sh/kernel/cpu/sh4/setup-sh7750.c
+++ b/arch/sh/kernel/cpu/sh4/setup-sh7750.c
@@ -239,7 +239,7 @@ static int __init sh7750_devices_setup(void)
return platform_add_devices(sh7750_devices,
ARRAY_SIZE(sh7750_devices));
}
-__initcall(sh7750_devices_setup);
+arch_initcall(sh7750_devices_setup);
static struct platform_device *sh7750_early_devices[] __initdata = {
&tmu0_device,
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7760.c b/arch/sh/kernel/cpu/sh4/setup-sh7760.c
index 5b822519bd9..7fbb7be9284 100644
--- a/arch/sh/kernel/cpu/sh4/setup-sh7760.c
+++ b/arch/sh/kernel/cpu/sh4/setup-sh7760.c
@@ -265,7 +265,7 @@ static int __init sh7760_devices_setup(void)
return platform_add_devices(sh7760_devices,
ARRAY_SIZE(sh7760_devices));
}
-__initcall(sh7760_devices_setup);
+arch_initcall(sh7760_devices_setup);
static struct platform_device *sh7760_early_devices[] __initdata = {
&tmu0_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
index 6307e087c86..ac4d5672ec1 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
@@ -325,7 +325,7 @@ static int __init sh7343_devices_setup(void)
return platform_add_devices(sh7343_devices,
ARRAY_SIZE(sh7343_devices));
}
-__initcall(sh7343_devices_setup);
+arch_initcall(sh7343_devices_setup);
static struct platform_device *sh7343_early_devices[] __initdata = {
&cmt_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
index c18f7d09281..1a956b1becc 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
@@ -318,7 +318,7 @@ static int __init sh7366_devices_setup(void)
return platform_add_devices(sh7366_devices,
ARRAY_SIZE(sh7366_devices));
}
-__initcall(sh7366_devices_setup);
+arch_initcall(sh7366_devices_setup);
static struct platform_device *sh7366_early_devices[] __initdata = {
&cmt_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
index ea524a2da3e..cda76ebf87c 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
@@ -359,7 +359,7 @@ static int __init sh7722_devices_setup(void)
return platform_add_devices(sh7722_devices,
ARRAY_SIZE(sh7722_devices));
}
-__initcall(sh7722_devices_setup);
+arch_initcall(sh7722_devices_setup);
static struct platform_device *sh7722_early_devices[] __initdata = {
&cmt_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
index e1bb80b2a27..b45dace9539 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
@@ -473,7 +473,7 @@ static int __init sh7723_devices_setup(void)
return platform_add_devices(sh7723_devices,
ARRAY_SIZE(sh7723_devices));
}
-__initcall(sh7723_devices_setup);
+arch_initcall(sh7723_devices_setup);
static struct platform_device *sh7723_early_devices[] __initdata = {
&cmt_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c
index e5ac9eb11c6..a04edaab9a2 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c
@@ -508,7 +508,7 @@ static int __init sh7724_devices_setup(void)
return platform_add_devices(sh7724_devices,
ARRAY_SIZE(sh7724_devices));
}
-device_initcall(sh7724_devices_setup);
+arch_initcall(sh7724_devices_setup);
static struct platform_device *sh7724_early_devices[] __initdata = {
&cmt_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
index f1e0c0d36da..4659fff6b84 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
@@ -314,7 +314,7 @@ static int __init sh7763_devices_setup(void)
return platform_add_devices(sh7763_devices,
ARRAY_SIZE(sh7763_devices));
}
-__initcall(sh7763_devices_setup);
+arch_initcall(sh7763_devices_setup);
static struct platform_device *sh7763_early_devices[] __initdata = {
&tmu0_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7770.c b/arch/sh/kernel/cpu/sh4a/setup-sh7770.c
index 1e86209db28..eead08d89d3 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7770.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7770.c
@@ -368,7 +368,7 @@ static int __init sh7770_devices_setup(void)
return platform_add_devices(sh7770_devices,
ARRAY_SIZE(sh7770_devices));
}
-__initcall(sh7770_devices_setup);
+arch_initcall(sh7770_devices_setup);
static struct platform_device *sh7770_early_devices[] __initdata = {
&tmu0_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c
index 715e05b431e..2c901f44695 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c
@@ -256,7 +256,7 @@ static int __init sh7780_devices_setup(void)
return platform_add_devices(sh7780_devices,
ARRAY_SIZE(sh7780_devices));
}
-__initcall(sh7780_devices_setup);
+arch_initcall(sh7780_devices_setup);
static struct platform_device *sh7780_early_devices[] __initdata = {
&tmu0_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c
index af561402570..7f6c718b6c3 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c
@@ -263,7 +263,7 @@ static int __init sh7785_devices_setup(void)
return platform_add_devices(sh7785_devices,
ARRAY_SIZE(sh7785_devices));
}
-__initcall(sh7785_devices_setup);
+arch_initcall(sh7785_devices_setup);
static struct platform_device *sh7785_early_devices[] __initdata = {
&tmu0_device,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
index b70049470a0..0104a8ec536 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
@@ -547,7 +547,7 @@ static int __init sh7786_devices_setup(void)
return platform_add_devices(sh7786_devices,
ARRAY_SIZE(sh7786_devices));
}
-device_initcall(sh7786_devices_setup);
+arch_initcall(sh7786_devices_setup);
void __init plat_early_device_setup(void)
{
diff --git a/arch/sh/kernel/cpu/sh4a/setup-shx3.c b/arch/sh/kernel/cpu/sh4a/setup-shx3.c
index 53c65fd9cce..07f078961c7 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-shx3.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-shx3.c
@@ -256,7 +256,7 @@ static int __init shx3_devices_setup(void)
return platform_add_devices(shx3_devices,
ARRAY_SIZE(shx3_devices));
}
-__initcall(shx3_devices_setup);
+arch_initcall(shx3_devices_setup);
void __init plat_early_device_setup(void)
{
diff --git a/arch/sh/kernel/cpu/sh5/setup-sh5.c b/arch/sh/kernel/cpu/sh5/setup-sh5.c
index f5ff1ac57fc..6a0f82f7003 100644
--- a/arch/sh/kernel/cpu/sh5/setup-sh5.c
+++ b/arch/sh/kernel/cpu/sh5/setup-sh5.c
@@ -186,7 +186,7 @@ static int __init sh5_devices_setup(void)
return platform_add_devices(sh5_devices,
ARRAY_SIZE(sh5_devices));
}
-__initcall(sh5_devices_setup);
+arch_initcall(sh5_devices_setup);
void __init plat_early_device_setup(void)
{
diff --git a/arch/sh/kernel/cpu/shmobile/sleep.S b/arch/sh/kernel/cpu/shmobile/sleep.S
index 5d888ef53d8..baf2d7d46b0 100644
--- a/arch/sh/kernel/cpu/shmobile/sleep.S
+++ b/arch/sh/kernel/cpu/shmobile/sleep.S
@@ -26,8 +26,30 @@ ENTRY(sh_mobile_standby)
tst #SUSP_SH_SF, r0
bt skip_set_sf
+#ifdef CONFIG_CPU_SUBTYPE_SH7724
+ /* DBSC: put memory in self-refresh mode */
- /* SDRAM: disable power down and put in self-refresh mode */
+ mov.l dben_reg, r4
+ mov.l dben_data0, r1
+ mov.l r1, @r4
+
+ mov.l dbrfpdn0_reg, r4
+ mov.l dbrfpdn0_data0, r1
+ mov.l r1, @r4
+
+ mov.l dbcmdcnt_reg, r4
+ mov.l dbcmdcnt_data0, r1
+ mov.l r1, @r4
+
+ mov.l dbcmdcnt_reg, r4
+ mov.l dbcmdcnt_data1, r1
+ mov.l r1, @r4
+
+ mov.l dbrfpdn0_reg, r4
+ mov.l dbrfpdn0_data1, r1
+ mov.l r1, @r4
+#else
+ /* SBSC: disable power down and put in self-refresh mode */
mov.l 1f, r4
mov.l 2f, r1
mov.l @r4, r2
@@ -35,6 +57,7 @@ ENTRY(sh_mobile_standby)
mov.l 3f, r3
and r3, r2
mov.l r2, @r4
+#endif
skip_set_sf:
tst #SUSP_SH_SLEEP, r0
@@ -84,7 +107,36 @@ done_sleep:
tst #SUSP_SH_SF, r0
bt skip_restore_sf
- /* SDRAM: set auto-refresh mode */
+#ifdef CONFIG_CPU_SUBTYPE_SH7724
+ /* DBSC: put memory in auto-refresh mode */
+
+ mov.l dbrfpdn0_reg, r4
+ mov.l dbrfpdn0_data0, r1
+ mov.l r1, @r4
+
+ /* sleep 140 ns */
+ nop
+ nop
+ nop
+ nop
+
+ mov.l dbcmdcnt_reg, r4
+ mov.l dbcmdcnt_data0, r1
+ mov.l r1, @r4
+
+ mov.l dbcmdcnt_reg, r4
+ mov.l dbcmdcnt_data1, r1
+ mov.l r1, @r4
+
+ mov.l dben_reg, r4
+ mov.l dben_data1, r1
+ mov.l r1, @r4
+
+ mov.l dbrfpdn0_reg, r4
+ mov.l dbrfpdn0_data2, r1
+ mov.l r1, @r4
+#else
+ /* SBSC: set auto-refresh mode */
mov.l 1f, r4
mov.l @r4, r2
mov.l 4f, r3
@@ -98,15 +150,29 @@ done_sleep:
add r4, r3
or r2, r3
mov.l r3, @r1
+#endif
skip_restore_sf:
rts
nop
.balign 4
+#ifdef CONFIG_CPU_SUBTYPE_SH7724
+dben_reg: .long 0xfd000010 /* DBEN */
+dben_data0: .long 0
+dben_data1: .long 1
+dbrfpdn0_reg: .long 0xfd000040 /* DBRFPDN0 */
+dbrfpdn0_data0: .long 0
+dbrfpdn0_data1: .long 1
+dbrfpdn0_data2: .long 0x00010000
+dbcmdcnt_reg: .long 0xfd000014 /* DBCMDCNT */
+dbcmdcnt_data0: .long 2
+dbcmdcnt_data1: .long 4
+#else
1: .long 0xfe400008 /* SDCR0 */
2: .long 0x00000400
3: .long 0xffff7fff
4: .long 0xfffffbff
+#endif
5: .long 0xa4150020 /* STBCR */
6: .long 0xfe40001c /* RTCOR */
7: .long 0xfe400018 /* RTCNT */
diff --git a/arch/sparc/configs/sparc32_defconfig b/arch/sparc/configs/sparc32_defconfig
index 8bcd27af724..a0f62a808ed 100644
--- a/arch/sparc/configs/sparc32_defconfig
+++ b/arch/sparc/configs/sparc32_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.30-rc2
-# Fri Apr 17 04:04:46 2009
+# Linux kernel version: 2.6.31-rc1
+# Tue Aug 18 23:45:52 2009
#
# CONFIG_64BIT is not set
CONFIG_SPARC=y
@@ -17,6 +17,7 @@ CONFIG_GENERIC_ISA_DMA=y
CONFIG_ARCH_NO_VIRT_TO_BUS=y
CONFIG_OF=y
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
#
# General setup
@@ -74,7 +75,6 @@ CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS=y
# CONFIG_KALLSYMS_ALL is not set
# CONFIG_KALLSYMS_EXTRA_PASS is not set
-# CONFIG_STRIP_ASM_SYMS is not set
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
@@ -87,8 +87,13 @@ CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
CONFIG_AIO=y
+
+#
+# Performance Counters
+#
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_PCI_QUIRKS=y
+# CONFIG_STRIP_ASM_SYMS is not set
CONFIG_COMPAT_BRK=y
CONFIG_SLAB=y
# CONFIG_SLUB is not set
@@ -97,6 +102,10 @@ CONFIG_SLAB=y
# CONFIG_MARKERS is not set
CONFIG_HAVE_OPROFILE=y
CONFIG_HAVE_ARCH_TRACEHOOK=y
+
+#
+# GCOV-based kernel profiling
+#
# CONFIG_SLOW_WORK is not set
# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
CONFIG_SLABINFO=y
@@ -109,7 +118,7 @@ CONFIG_MODULE_UNLOAD=y
# CONFIG_MODVERSIONS is not set
# CONFIG_MODULE_SRCVERSION_ALL is not set
CONFIG_BLOCK=y
-# CONFIG_LBD is not set
+CONFIG_LBDAF=y
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_BLK_DEV_INTEGRITY is not set
@@ -154,9 +163,9 @@ CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_PHYS_ADDR_T_64BIT is not set
CONFIG_ZONE_DMA_FLAG=1
CONFIG_BOUNCE=y
-CONFIG_UNEVICTABLE_LRU=y
CONFIG_HAVE_MLOCK=y
CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
CONFIG_SUN_PM=y
# CONFIG_SPARC_LED is not set
CONFIG_SERIAL_CONSOLE=y
@@ -264,6 +273,7 @@ CONFIG_IPV6_TUNNEL=m
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
# CONFIG_NET_SCHED is not set
# CONFIG_DCB is not set
@@ -281,7 +291,11 @@ CONFIG_WIRELESS=y
CONFIG_WIRELESS_OLD_REGULATORY=y
# CONFIG_WIRELESS_EXT is not set
# CONFIG_LIB80211 is not set
-# CONFIG_MAC80211 is not set
+
+#
+# CFG80211 needs to be enabled for MAC80211
+#
+CONFIG_MAC80211_DEFAULT_PS_VALUE=0
# CONFIG_WIMAX is not set
# CONFIG_RFKILL is not set
# CONFIG_NET_9P is not set
@@ -335,6 +349,7 @@ CONFIG_MISC_DEVICES=y
# EEPROM support
#
# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_CB710_CORE is not set
CONFIG_HAVE_IDE=y
# CONFIG_IDE is not set
@@ -358,10 +373,6 @@ CONFIG_BLK_DEV_SR=m
# CONFIG_BLK_DEV_SR_VENDOR is not set
CONFIG_CHR_DEV_SG=m
# CONFIG_CHR_DEV_SCH is not set
-
-#
-# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
-#
# CONFIG_SCSI_MULTI_LUN is not set
# CONFIG_SCSI_CONSTANTS is not set
# CONFIG_SCSI_LOGGING is not set
@@ -379,6 +390,7 @@ CONFIG_SCSI_SPI_ATTRS=y
CONFIG_SCSI_LOWLEVEL=y
# CONFIG_ISCSI_TCP is not set
# CONFIG_SCSI_CXGB3_ISCSI is not set
+# CONFIG_SCSI_BNX2_ISCSI is not set
# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
# CONFIG_SCSI_3W_9XXX is not set
# CONFIG_SCSI_ACARD is not set
@@ -387,6 +399,7 @@ CONFIG_SCSI_LOWLEVEL=y
# CONFIG_SCSI_AIC7XXX_OLD is not set
# CONFIG_SCSI_AIC79XX is not set
# CONFIG_SCSI_AIC94XX is not set
+# CONFIG_SCSI_MVSAS is not set
# CONFIG_SCSI_ARCMSR is not set
# CONFIG_MEGARAID_NEWGEN is not set
# CONFIG_MEGARAID_LEGACY is not set
@@ -401,7 +414,6 @@ CONFIG_SCSI_LOWLEVEL=y
# CONFIG_SCSI_IPS is not set
# CONFIG_SCSI_INITIO is not set
# CONFIG_SCSI_INIA100 is not set
-# CONFIG_SCSI_MVSAS is not set
# CONFIG_SCSI_STEX is not set
# CONFIG_SCSI_SYM53C8XX_2 is not set
# CONFIG_SCSI_QLOGIC_1280 is not set
@@ -426,13 +438,16 @@ CONFIG_SCSI_SUNESP=y
#
#
-# Enable only one of the two stacks, unless you know what you are doing
+# You can enable one or both FireWire driver stacks.
+#
+
+#
+# See the help texts for more information.
#
# CONFIG_FIREWIRE is not set
# CONFIG_IEEE1394 is not set
# CONFIG_I2O is not set
CONFIG_NETDEVICES=y
-CONFIG_COMPAT_NET_DEV_OPS=y
CONFIG_DUMMY=m
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
@@ -463,6 +478,7 @@ CONFIG_SUNQE=m
# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
# CONFIG_NET_PCI is not set
# CONFIG_B44 is not set
+# CONFIG_KS8842 is not set
# CONFIG_ATL2 is not set
CONFIG_NETDEV_1000=y
# CONFIG_ACENIC is not set
@@ -482,6 +498,7 @@ CONFIG_NETDEV_1000=y
# CONFIG_VIA_VELOCITY is not set
# CONFIG_TIGON3 is not set
# CONFIG_BNX2 is not set
+# CONFIG_CNIC is not set
# CONFIG_QLA3XXX is not set
# CONFIG_ATL1 is not set
# CONFIG_ATL1E is not set
@@ -629,6 +646,11 @@ CONFIG_HW_RANDOM=m
CONFIG_DEVPORT=y
# CONFIG_I2C is not set
# CONFIG_SPI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
# CONFIG_GPIOLIB is not set
# CONFIG_W1 is not set
@@ -668,22 +690,7 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_HTC_PASIC3 is not set
# CONFIG_MFD_TMIO is not set
# CONFIG_REGULATOR is not set
-
-#
-# Multimedia devices
-#
-
-#
-# Multimedia core support
-#
-# CONFIG_VIDEO_DEV is not set
-# CONFIG_DVB_CORE is not set
-# CONFIG_VIDEO_MEDIA is not set
-
-#
-# Multimedia drivers
-#
-# CONFIG_DAB is not set
+# CONFIG_MEDIA_SUPPORT is not set
#
# Graphics support
@@ -776,6 +783,10 @@ CONFIG_RTC_DRV_M48T59=y
# CONFIG_DMADEVICES is not set
# CONFIG_AUXDISPLAY is not set
# CONFIG_UIO is not set
+
+#
+# TI VLYNQ
+#
# CONFIG_STAGING is not set
#
@@ -799,10 +810,12 @@ CONFIG_FS_MBCACHE=y
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
CONFIG_FS_POSIX_ACL=y
-CONFIG_FILE_LOCKING=y
# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_BTRFS_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
CONFIG_DNOTIFY=y
CONFIG_INOTIFY=y
CONFIG_INOTIFY_USER=y
@@ -985,6 +998,7 @@ CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
CONFIG_KGDB_TESTS=y
# CONFIG_KGDB_TESTS_ON_BOOT is not set
+# CONFIG_KMEMCHECK is not set
# CONFIG_DEBUG_STACK_USAGE is not set
# CONFIG_STACK_DEBUG is not set
diff --git a/arch/sparc/configs/sparc64_defconfig b/arch/sparc/configs/sparc64_defconfig
index 0123a4c596c..fdddf7a6f72 100644
--- a/arch/sparc/configs/sparc64_defconfig
+++ b/arch/sparc/configs/sparc64_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.30
-# Tue Jun 16 04:59:36 2009
+# Linux kernel version: 2.6.31-rc1
+# Tue Aug 18 23:56:02 2009
#
CONFIG_64BIT=y
CONFIG_SPARC=y
@@ -26,6 +26,7 @@ CONFIG_ARCH_NO_VIRT_TO_BUS=y
CONFIG_OF=y
CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
#
# General setup
@@ -119,6 +120,11 @@ CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
CONFIG_HAVE_ARCH_TRACEHOOK=y
CONFIG_USE_GENERIC_SMP_HELPERS=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
# CONFIG_SLOW_WORK is not set
# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
CONFIG_SLABINFO=y
@@ -204,7 +210,6 @@ CONFIG_MIGRATION=y
CONFIG_PHYS_ADDR_T_64BIT=y
CONFIG_ZONE_DMA_FLAG=0
CONFIG_NR_QUICK=1
-CONFIG_UNEVICTABLE_LRU=y
CONFIG_HAVE_MLOCK=y
CONFIG_HAVE_MLOCKED_PAGE_BIT=y
CONFIG_DEFAULT_MMAP_MIN_ADDR=8192
@@ -410,6 +415,7 @@ CONFIG_MISC_DEVICES=y
#
# CONFIG_EEPROM_AT24 is not set
# CONFIG_EEPROM_LEGACY is not set
+# CONFIG_EEPROM_MAX6875 is not set
# CONFIG_EEPROM_93CX6 is not set
# CONFIG_CB710_CORE is not set
CONFIG_HAVE_IDE=y
@@ -562,6 +568,7 @@ CONFIG_BLK_DEV_DM=m
CONFIG_DM_CRYPT=m
CONFIG_DM_SNAPSHOT=m
CONFIG_DM_MIRROR=m
+# CONFIG_DM_LOG_USERSPACE is not set
CONFIG_DM_ZERO=m
# CONFIG_DM_MULTIPATH is not set
# CONFIG_DM_DELAY is not set
@@ -573,7 +580,11 @@ CONFIG_DM_ZERO=m
#
#
-# Enable only one of the two stacks, unless you know what you are doing
+# You can enable one or both FireWire driver stacks.
+#
+
+#
+# See the help texts for more information.
#
# CONFIG_FIREWIRE is not set
# CONFIG_IEEE1394 is not set
@@ -667,6 +678,7 @@ CONFIG_E1000E=m
# CONFIG_VIA_VELOCITY is not set
CONFIG_TIGON3=m
CONFIG_BNX2=m
+# CONFIG_CNIC is not set
# CONFIG_QLA3XXX is not set
# CONFIG_ATL1 is not set
# CONFIG_ATL1E is not set
@@ -773,6 +785,7 @@ CONFIG_MOUSE_SERIAL=y
# CONFIG_MOUSE_APPLETOUCH is not set
# CONFIG_MOUSE_BCM5974 is not set
# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TABLET is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
@@ -870,6 +883,7 @@ CONFIG_I2C_ALGOBIT=y
#
# I2C system bus drivers (mostly embedded / system-on-chip)
#
+# CONFIG_I2C_DESIGNWARE is not set
# CONFIG_I2C_OCORES is not set
# CONFIG_I2C_SIMTEC is not set
@@ -898,13 +912,17 @@ CONFIG_I2C_ALGOBIT=y
# CONFIG_SENSORS_PCF8574 is not set
# CONFIG_PCF8575 is not set
# CONFIG_SENSORS_PCA9539 is not set
-# CONFIG_SENSORS_MAX6875 is not set
# CONFIG_SENSORS_TSL2550 is not set
# CONFIG_I2C_DEBUG_CORE is not set
# CONFIG_I2C_DEBUG_ALGO is not set
# CONFIG_I2C_DEBUG_BUS is not set
# CONFIG_I2C_DEBUG_CHIP is not set
# CONFIG_SPI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
# CONFIG_GPIOLIB is not set
# CONFIG_W1 is not set
@@ -959,6 +977,7 @@ CONFIG_HWMON=y
# CONFIG_SENSORS_SMSC47B397 is not set
# CONFIG_SENSORS_ADS7828 is not set
# CONFIG_SENSORS_THMC50 is not set
+# CONFIG_SENSORS_TMP401 is not set
# CONFIG_SENSORS_VIA686A is not set
# CONFIG_SENSORS_VT1211 is not set
# CONFIG_SENSORS_VT8231 is not set
@@ -994,23 +1013,9 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_MFD_WM8400 is not set
# CONFIG_MFD_WM8350_I2C is not set
# CONFIG_MFD_PCF50633 is not set
+# CONFIG_AB3100_CORE is not set
# CONFIG_REGULATOR is not set
-
-#
-# Multimedia devices
-#
-
-#
-# Multimedia core support
-#
-# CONFIG_VIDEO_DEV is not set
-# CONFIG_DVB_CORE is not set
-# CONFIG_VIDEO_MEDIA is not set
-
-#
-# Multimedia drivers
-#
-# CONFIG_DAB is not set
+# CONFIG_MEDIA_SUPPORT is not set
#
# Graphics support
@@ -1284,7 +1289,6 @@ CONFIG_USB=y
#
# Miscellaneous USB options
#
-CONFIG_USB_DEVICEFS=y
# CONFIG_USB_DEVICE_CLASS is not set
# CONFIG_USB_DYNAMIC_MINORS is not set
# CONFIG_USB_OTG is not set
@@ -1296,6 +1300,7 @@ CONFIG_USB_DEVICEFS=y
# USB Host Controller Drivers
#
# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_XHCI_HCD is not set
CONFIG_USB_EHCI_HCD=m
# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
# CONFIG_USB_EHCI_TT_NEWSCHED is not set
@@ -1374,7 +1379,6 @@ CONFIG_USB_STORAGE=m
# CONFIG_USB_LD is not set
# CONFIG_USB_TRANCEVIBRATOR is not set
# CONFIG_USB_IOWARRIOR is not set
-# CONFIG_USB_TEST is not set
# CONFIG_USB_ISIGHTFW is not set
# CONFIG_USB_VST is not set
# CONFIG_USB_GADGET is not set
@@ -1420,6 +1424,7 @@ CONFIG_RTC_INTF_DEV=y
# CONFIG_RTC_DRV_S35390A is not set
# CONFIG_RTC_DRV_FM3130 is not set
# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
#
# SPI RTC drivers
@@ -1448,6 +1453,10 @@ CONFIG_RTC_DRV_STARFIRE=y
# CONFIG_DMADEVICES is not set
# CONFIG_AUXDISPLAY is not set
# CONFIG_UIO is not set
+
+#
+# TI VLYNQ
+#
# CONFIG_STAGING is not set
#
@@ -1480,11 +1489,11 @@ CONFIG_FS_MBCACHE=y
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
CONFIG_FS_POSIX_ACL=y
-CONFIG_FILE_LOCKING=y
# CONFIG_XFS_FS is not set
# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_BTRFS_FS is not set
+CONFIG_FILE_LOCKING=y
CONFIG_FSNOTIFY=y
CONFIG_DNOTIFY=y
CONFIG_INOTIFY=y
@@ -1560,7 +1569,7 @@ CONFIG_NETWORK_FILESYSTEMS=y
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y
CONFIG_SUN_PARTITION=y
-CONFIG_NLS=m
+CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
# CONFIG_NLS_CODEPAGE_437 is not set
# CONFIG_NLS_CODEPAGE_737 is not set
diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h
index b049abf9902..0ff92fa2206 100644
--- a/arch/sparc/include/asm/pgtable_64.h
+++ b/arch/sparc/include/asm/pgtable_64.h
@@ -726,11 +726,17 @@ extern unsigned long pte_file(pte_t);
extern pte_t pgoff_to_pte(unsigned long);
#define PTE_FILE_MAX_BITS (64UL - PAGE_SHIFT - 1UL)
-extern unsigned long *sparc64_valid_addr_bitmap;
+extern unsigned long sparc64_valid_addr_bitmap[];
/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
-#define kern_addr_valid(addr) \
- (test_bit(__pa((unsigned long)(addr))>>22, sparc64_valid_addr_bitmap))
+static inline bool kern_addr_valid(unsigned long addr)
+{
+ unsigned long paddr = __pa(addr);
+
+ if ((paddr >> 41UL) != 0UL)
+ return false;
+ return test_bit(paddr >> 22, sparc64_valid_addr_bitmap);
+}
extern int page_in_phys_avail(unsigned long paddr);
diff --git a/arch/sparc/kernel/irq_64.c b/arch/sparc/kernel/irq_64.c
index f0ee7905540..8daab33fc17 100644
--- a/arch/sparc/kernel/irq_64.c
+++ b/arch/sparc/kernel/irq_64.c
@@ -886,7 +886,7 @@ void notrace init_irqwork_curcpu(void)
* Therefore you cannot make any OBP calls, not even prom_printf,
* from these two routines.
*/
-static void __cpuinit register_one_mondo(unsigned long paddr, unsigned long type, unsigned long qmask)
+static void __cpuinit notrace register_one_mondo(unsigned long paddr, unsigned long type, unsigned long qmask)
{
unsigned long num_entries = (qmask + 1) / 64;
unsigned long status;
diff --git a/arch/sparc/kernel/ktlb.S b/arch/sparc/kernel/ktlb.S
index cef8defcd7a..3ea6e8cde8c 100644
--- a/arch/sparc/kernel/ktlb.S
+++ b/arch/sparc/kernel/ktlb.S
@@ -151,12 +151,46 @@ kvmap_dtlb_4v:
* Must preserve %g1 and %g6 (TAG).
*/
kvmap_dtlb_tsb4m_miss:
- sethi %hi(kpte_linear_bitmap), %g2
- or %g2, %lo(kpte_linear_bitmap), %g2
+ /* Clear the PAGE_OFFSET top virtual bits, shift
+ * down to get PFN, and make sure PFN is in range.
+ */
+ sllx %g4, 21, %g5
- /* Clear the PAGE_OFFSET top virtual bits, then shift
- * down to get a 256MB physical address index.
+ /* Check to see if we know about valid memory at the 4MB
+ * chunk this physical address will reside within.
*/
+ srlx %g5, 21 + 41, %g2
+ brnz,pn %g2, kvmap_dtlb_longpath
+ nop
+
+ /* This unconditional branch and delay-slot nop gets patched
+ * by the sethi sequence once the bitmap is properly setup.
+ */
+ .globl valid_addr_bitmap_insn
+valid_addr_bitmap_insn:
+ ba,pt %xcc, 2f
+ nop
+ .subsection 2
+ .globl valid_addr_bitmap_patch
+valid_addr_bitmap_patch:
+ sethi %hi(sparc64_valid_addr_bitmap), %g7
+ or %g7, %lo(sparc64_valid_addr_bitmap), %g7
+ .previous
+
+ srlx %g5, 21 + 22, %g2
+ srlx %g2, 6, %g5
+ and %g2, 63, %g2
+ sllx %g5, 3, %g5
+ ldx [%g7 + %g5], %g5
+ mov 1, %g7
+ sllx %g7, %g2, %g7
+ andcc %g5, %g7, %g0
+ be,pn %xcc, kvmap_dtlb_longpath
+
+2: sethi %hi(kpte_linear_bitmap), %g2
+ or %g2, %lo(kpte_linear_bitmap), %g2
+
+ /* Get the 256MB physical address index. */
sllx %g4, 21, %g5
mov 1, %g7
srlx %g5, 21 + 28, %g5
diff --git a/arch/sparc/kernel/nmi.c b/arch/sparc/kernel/nmi.c
index 2c0cc72d295..b75bf502cd4 100644
--- a/arch/sparc/kernel/nmi.c
+++ b/arch/sparc/kernel/nmi.c
@@ -103,7 +103,7 @@ notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs)
}
if (!touched && __get_cpu_var(last_irq_sum) == sum) {
local_inc(&__get_cpu_var(alert_counter));
- if (local_read(&__get_cpu_var(alert_counter)) == 5 * nmi_hz)
+ if (local_read(&__get_cpu_var(alert_counter)) == 30 * nmi_hz)
die_nmi("BUG: NMI Watchdog detected LOCKUP",
regs, panic_on_timeout);
} else {
diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c
index fa44eaf8d89..3691907a43b 100644
--- a/arch/sparc/kernel/smp_64.c
+++ b/arch/sparc/kernel/smp_64.c
@@ -1499,7 +1499,7 @@ void __init setup_per_cpu_areas(void)
dyn_size = pcpur_size - static_size - PERCPU_MODULE_RESERVE;
- ptrs_size = PFN_ALIGN(num_possible_cpus() * sizeof(pcpur_ptrs[0]));
+ ptrs_size = PFN_ALIGN(nr_cpu_ids * sizeof(pcpur_ptrs[0]));
pcpur_ptrs = alloc_bootmem(ptrs_size);
for_each_possible_cpu(cpu) {
@@ -1514,7 +1514,7 @@ void __init setup_per_cpu_areas(void)
/* allocate address and map */
vm.flags = VM_ALLOC;
- vm.size = num_possible_cpus() * PCPU_CHUNK_SIZE;
+ vm.size = nr_cpu_ids * PCPU_CHUNK_SIZE;
vm_area_register_early(&vm, PCPU_CHUNK_SIZE);
for_each_possible_cpu(cpu) {
diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c
index 54fb02468f0..68791cad7b7 100644
--- a/arch/sparc/kernel/sun4d_smp.c
+++ b/arch/sparc/kernel/sun4d_smp.c
@@ -162,9 +162,6 @@ extern void cpu_panic(void);
*/
extern struct linux_prom_registers smp_penguin_ctable;
-extern unsigned long trapbase_cpu1[];
-extern unsigned long trapbase_cpu2[];
-extern unsigned long trapbase_cpu3[];
void __init smp4d_boot_cpus(void)
{
@@ -235,25 +232,6 @@ void __init smp4d_smp_done(void)
*prev = first;
local_flush_cache_all();
- /* Free unneeded trap tables */
- ClearPageReserved(virt_to_page(trapbase_cpu1));
- init_page_count(virt_to_page(trapbase_cpu1));
- free_page((unsigned long)trapbase_cpu1);
- totalram_pages++;
- num_physpages++;
-
- ClearPageReserved(virt_to_page(trapbase_cpu2));
- init_page_count(virt_to_page(trapbase_cpu2));
- free_page((unsigned long)trapbase_cpu2);
- totalram_pages++;
- num_physpages++;
-
- ClearPageReserved(virt_to_page(trapbase_cpu3));
- init_page_count(virt_to_page(trapbase_cpu3));
- free_page((unsigned long)trapbase_cpu3);
- totalram_pages++;
- num_physpages++;
-
/* Ok, they are spinning and ready to go. */
smp_processors_ready = 1;
sun4d_distribute_irqs();
diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c
index 960b113d000..762d6eedd94 100644
--- a/arch/sparc/kernel/sun4m_smp.c
+++ b/arch/sparc/kernel/sun4m_smp.c
@@ -121,9 +121,6 @@ void __cpuinit smp4m_callin(void)
*/
extern struct linux_prom_registers smp_penguin_ctable;
-extern unsigned long trapbase_cpu1[];
-extern unsigned long trapbase_cpu2[];
-extern unsigned long trapbase_cpu3[];
void __init smp4m_boot_cpus(void)
{
@@ -193,29 +190,6 @@ void __init smp4m_smp_done(void)
*prev = first;
local_flush_cache_all();
- /* Free unneeded trap tables */
- if (!cpu_isset(1, cpu_present_map)) {
- ClearPageReserved(virt_to_page(trapbase_cpu1));
- init_page_count(virt_to_page(trapbase_cpu1));
- free_page((unsigned long)trapbase_cpu1);
- totalram_pages++;
- num_physpages++;
- }
- if (!cpu_isset(2, cpu_present_map)) {
- ClearPageReserved(virt_to_page(trapbase_cpu2));
- init_page_count(virt_to_page(trapbase_cpu2));
- free_page((unsigned long)trapbase_cpu2);
- totalram_pages++;
- num_physpages++;
- }
- if (!cpu_isset(3, cpu_present_map)) {
- ClearPageReserved(virt_to_page(trapbase_cpu3));
- init_page_count(virt_to_page(trapbase_cpu3));
- free_page((unsigned long)trapbase_cpu3);
- totalram_pages++;
- num_physpages++;
- }
-
/* Ok, they are spinning and ready to go. */
}
diff --git a/arch/sparc/kernel/sys32.S b/arch/sparc/kernel/sys32.S
index f061c4dda9e..aed94869ad6 100644
--- a/arch/sparc/kernel/sys32.S
+++ b/arch/sparc/kernel/sys32.S
@@ -134,10 +134,12 @@ SIGN1(sys32_getpeername, sys_getpeername, %o0)
SIGN1(sys32_getsockname, sys_getsockname, %o0)
SIGN2(sys32_ioprio_get, sys_ioprio_get, %o0, %o1)
SIGN3(sys32_ioprio_set, sys_ioprio_set, %o0, %o1, %o2)
-SIGN2(sys32_splice, sys_splice, %o0, %o1)
+SIGN2(sys32_splice, sys_splice, %o0, %o2)
SIGN2(sys32_sync_file_range, compat_sync_file_range, %o0, %o5)
SIGN2(sys32_tee, sys_tee, %o0, %o1)
SIGN1(sys32_vmsplice, compat_sys_vmsplice, %o0)
+SIGN1(sys32_truncate, sys_truncate, %o1)
+SIGN1(sys32_ftruncate, sys_ftruncate, %o1)
.globl sys32_mmap2
sys32_mmap2:
diff --git a/arch/sparc/kernel/systbls_64.S b/arch/sparc/kernel/systbls_64.S
index 6b3ee88e253..2ee7250ba7a 100644
--- a/arch/sparc/kernel/systbls_64.S
+++ b/arch/sparc/kernel/systbls_64.S
@@ -43,8 +43,8 @@ sys_call_table32:
/*110*/ .word sys_setresgid, sys_getresgid, sys_setregid, sys_nis_syscall, sys_nis_syscall
.word sys32_getgroups, compat_sys_gettimeofday, sys32_getrusage, sys_nis_syscall, sys_getcwd
/*120*/ .word compat_sys_readv, compat_sys_writev, compat_sys_settimeofday, sys_fchown16, sys_fchmod
- .word sys_nis_syscall, sys_setreuid16, sys_setregid16, sys_rename, sys_truncate
-/*130*/ .word sys_ftruncate, sys_flock, compat_sys_lstat64, sys_nis_syscall, sys_nis_syscall
+ .word sys_nis_syscall, sys_setreuid16, sys_setregid16, sys_rename, sys32_truncate
+/*130*/ .word sys32_ftruncate, sys_flock, compat_sys_lstat64, sys_nis_syscall, sys_nis_syscall
.word sys_nis_syscall, sys32_mkdir, sys_rmdir, compat_sys_utimes, compat_sys_stat64
/*140*/ .word sys32_sendfile64, sys_nis_syscall, sys32_futex, sys_gettid, compat_sys_getrlimit
.word compat_sys_setrlimit, sys_pivot_root, sys32_prctl, sys_pciconfig_read, sys_pciconfig_write
diff --git a/arch/sparc/mm/fault_32.c b/arch/sparc/mm/fault_32.c
index a5e30c642ee..b99f81c4906 100644
--- a/arch/sparc/mm/fault_32.c
+++ b/arch/sparc/mm/fault_32.c
@@ -319,9 +319,10 @@ no_context:
*/
out_of_memory:
up_read(&mm->mmap_sem);
- printk("VM: killing process %s\n", tsk->comm);
- if (from_user)
- do_group_exit(SIGKILL);
+ if (from_user) {
+ pagefault_out_of_memory();
+ return;
+ }
goto no_context;
do_sigbus:
diff --git a/arch/sparc/mm/fault_64.c b/arch/sparc/mm/fault_64.c
index e5620b27c8b..43b0da96a4f 100644
--- a/arch/sparc/mm/fault_64.c
+++ b/arch/sparc/mm/fault_64.c
@@ -447,9 +447,10 @@ handle_kernel_fault:
out_of_memory:
insn = get_fault_insn(regs, insn);
up_read(&mm->mmap_sem);
- printk("VM: killing process %s\n", current->comm);
- if (!(regs->tstate & TSTATE_PRIV))
- do_group_exit(SIGKILL);
+ if (!(regs->tstate & TSTATE_PRIV)) {
+ pagefault_out_of_memory();
+ return;
+ }
goto handle_kernel_fault;
intr_or_no_mm:
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index ed6be6ba2f4..a70a5e1904d 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -145,7 +145,8 @@ static void __init read_obp_memory(const char *property,
cmp_p64, NULL);
}
-unsigned long *sparc64_valid_addr_bitmap __read_mostly;
+unsigned long sparc64_valid_addr_bitmap[VALID_ADDR_BITMAP_BYTES /
+ sizeof(unsigned long)];
EXPORT_SYMBOL(sparc64_valid_addr_bitmap);
/* Kernel physical address base and size in bytes. */
@@ -1874,7 +1875,7 @@ static int pavail_rescan_ents __initdata;
* memory list again, and make sure it provides at least as much
* memory as 'pavail' does.
*/
-static void __init setup_valid_addr_bitmap_from_pavail(void)
+static void __init setup_valid_addr_bitmap_from_pavail(unsigned long *bitmap)
{
int i;
@@ -1897,8 +1898,7 @@ static void __init setup_valid_addr_bitmap_from_pavail(void)
if (new_start <= old_start &&
new_end >= (old_start + PAGE_SIZE)) {
- set_bit(old_start >> 22,
- sparc64_valid_addr_bitmap);
+ set_bit(old_start >> 22, bitmap);
goto do_next_page;
}
}
@@ -1919,20 +1919,21 @@ static void __init setup_valid_addr_bitmap_from_pavail(void)
}
}
+static void __init patch_tlb_miss_handler_bitmap(void)
+{
+ extern unsigned int valid_addr_bitmap_insn[];
+ extern unsigned int valid_addr_bitmap_patch[];
+
+ valid_addr_bitmap_insn[1] = valid_addr_bitmap_patch[1];
+ mb();
+ valid_addr_bitmap_insn[0] = valid_addr_bitmap_patch[0];
+ flushi(&valid_addr_bitmap_insn[0]);
+}
+
void __init mem_init(void)
{
unsigned long codepages, datapages, initpages;
unsigned long addr, last;
- int i;
-
- i = last_valid_pfn >> ((22 - PAGE_SHIFT) + 6);
- i += 1;
- sparc64_valid_addr_bitmap = (unsigned long *) alloc_bootmem(i << 3);
- if (sparc64_valid_addr_bitmap == NULL) {
- prom_printf("mem_init: Cannot alloc valid_addr_bitmap.\n");
- prom_halt();
- }
- memset(sparc64_valid_addr_bitmap, 0, i << 3);
addr = PAGE_OFFSET + kern_base;
last = PAGE_ALIGN(kern_size) + addr;
@@ -1941,15 +1942,19 @@ void __init mem_init(void)
addr += PAGE_SIZE;
}
- setup_valid_addr_bitmap_from_pavail();
+ setup_valid_addr_bitmap_from_pavail(sparc64_valid_addr_bitmap);
+ patch_tlb_miss_handler_bitmap();
high_memory = __va(last_valid_pfn << PAGE_SHIFT);
#ifdef CONFIG_NEED_MULTIPLE_NODES
- for_each_online_node(i) {
- if (NODE_DATA(i)->node_spanned_pages != 0) {
- totalram_pages +=
- free_all_bootmem_node(NODE_DATA(i));
+ {
+ int i;
+ for_each_online_node(i) {
+ if (NODE_DATA(i)->node_spanned_pages != 0) {
+ totalram_pages +=
+ free_all_bootmem_node(NODE_DATA(i));
+ }
}
}
#else
diff --git a/arch/sparc/mm/init_64.h b/arch/sparc/mm/init_64.h
index 16063870a48..c2f772dbd55 100644
--- a/arch/sparc/mm/init_64.h
+++ b/arch/sparc/mm/init_64.h
@@ -5,10 +5,13 @@
* marked non-static so that assembler code can get at them.
*/
-#define MAX_PHYS_ADDRESS (1UL << 42UL)
-#define KPTE_BITMAP_CHUNK_SZ (256UL * 1024UL * 1024UL)
+#define MAX_PHYS_ADDRESS (1UL << 41UL)
+#define KPTE_BITMAP_CHUNK_SZ (256UL * 1024UL * 1024UL)
#define KPTE_BITMAP_BYTES \
((MAX_PHYS_ADDRESS / KPTE_BITMAP_CHUNK_SZ) / 8)
+#define VALID_ADDR_BITMAP_CHUNK_SZ (4UL * 1024UL * 1024UL)
+#define VALID_ADDR_BITMAP_BYTES \
+ ((MAX_PHYS_ADDRESS / VALID_ADDR_BITMAP_CHUNK_SZ) / 8)
extern unsigned long kern_linear_pte_xor[2];
extern unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
diff --git a/arch/sparc/prom/misc_64.c b/arch/sparc/prom/misc_64.c
index eedffb4fec2..39fc6af21b7 100644
--- a/arch/sparc/prom/misc_64.c
+++ b/arch/sparc/prom/misc_64.c
@@ -88,7 +88,7 @@ void prom_cmdline(void)
/* Drop into the prom, but completely terminate the program.
* No chance of continuing.
*/
-void prom_halt(void)
+void notrace prom_halt(void)
{
#ifdef CONFIG_SUN_LDOMS
if (ldom_domaining_enabled)
diff --git a/arch/sparc/prom/printf.c b/arch/sparc/prom/printf.c
index 660943ee4c2..ca869266b9f 100644
--- a/arch/sparc/prom/printf.c
+++ b/arch/sparc/prom/printf.c
@@ -14,14 +14,14 @@
*/
#include <linux/kernel.h>
+#include <linux/compiler.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
static char ppbuf[1024];
-void
-prom_write(const char *buf, unsigned int n)
+void notrace prom_write(const char *buf, unsigned int n)
{
char ch;
@@ -33,8 +33,7 @@ prom_write(const char *buf, unsigned int n)
}
}
-void
-prom_printf(const char *fmt, ...)
+void notrace prom_printf(const char *fmt, ...)
{
va_list args;
int i;
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 738bdc6b0f8..13ffa5df37d 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -24,6 +24,7 @@ config X86
select HAVE_UNSTABLE_SCHED_CLOCK
select HAVE_IDE
select HAVE_OPROFILE
+ select HAVE_PERF_COUNTERS if (!M386 && !M486)
select HAVE_IOREMAP_PROT
select HAVE_KPROBES
select ARCH_WANT_OPTIONAL_GPIOLIB
@@ -742,7 +743,6 @@ config X86_UP_IOAPIC
config X86_LOCAL_APIC
def_bool y
depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC
- select HAVE_PERF_COUNTERS if (!M386 && !M486)
config X86_IO_APIC
def_bool y
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index e2ff504b4dd..f8ed0658404 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -4,7 +4,7 @@
# create a compressed vmlinux image from the original vmlinux
#
-targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma head_$(BITS).o misc.o piggy.o
+targets := vmlinux.lds vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma head_$(BITS).o misc.o piggy.o
KBUILD_CFLAGS := -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2
KBUILD_CFLAGS += -fno-strict-aliasing -fPIC
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index edc90f23e70..8406ed7f992 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -33,7 +33,7 @@ extern unsigned long asmlinkage efi_call_phys(void *, ...);
#define efi_call_virt6(f, a1, a2, a3, a4, a5, a6) \
efi_call_virt(f, a1, a2, a3, a4, a5, a6)
-#define efi_ioremap(addr, size) ioremap_cache(addr, size)
+#define efi_ioremap(addr, size, type) ioremap_cache(addr, size)
#else /* !CONFIG_X86_32 */
@@ -84,7 +84,8 @@ extern u64 efi_call6(void *fp, u64 arg1, u64 arg2, u64 arg3,
efi_call6((void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2), \
(u64)(a3), (u64)(a4), (u64)(a5), (u64)(a6))
-extern void __iomem *efi_ioremap(unsigned long addr, unsigned long size);
+extern void __iomem *efi_ioremap(unsigned long addr, unsigned long size,
+ u32 type);
#endif /* CONFIG_X86_32 */
diff --git a/arch/x86/include/asm/irqflags.h b/arch/x86/include/asm/irqflags.h
index 2bdab21f089..c6ccbe7e81a 100644
--- a/arch/x86/include/asm/irqflags.h
+++ b/arch/x86/include/asm/irqflags.h
@@ -12,9 +12,15 @@ static inline unsigned long native_save_fl(void)
{
unsigned long flags;
+ /*
+ * Note: this needs to be "=r" not "=rm", because we have the
+ * stack offset from what gcc expects at the time the "pop" is
+ * executed, and so a memory reference with respect to the stack
+ * would end up using the wrong address.
+ */
asm volatile("# __raw_save_flags\n\t"
"pushf ; pop %0"
- : "=g" (flags)
+ : "=r" (flags)
: /* no input */
: "memory");
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index 3cc06e3fceb..16748077559 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -2,6 +2,7 @@
#define _ASM_X86_PGTABLE_H
#include <asm/page.h>
+#include <asm/e820.h>
#include <asm/pgtable_types.h>
@@ -269,10 +270,17 @@ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
#define canon_pgprot(p) __pgprot(massage_pgprot(p))
-static inline int is_new_memtype_allowed(unsigned long flags,
- unsigned long new_flags)
+static inline int is_new_memtype_allowed(u64 paddr, unsigned long size,
+ unsigned long flags,
+ unsigned long new_flags)
{
/*
+ * PAT type is always WB for ISA. So no need to check.
+ */
+ if (is_ISA_range(paddr, paddr + size - 1))
+ return 1;
+
+ /*
* Certain new memtypes are not allowed with certain
* requested memtype:
* - request is uncached, return cannot be write-back
diff --git a/arch/x86/include/asm/uv/uv_bau.h b/arch/x86/include/asm/uv/uv_bau.h
index bddd44f2f0a..80e2984f521 100644
--- a/arch/x86/include/asm/uv/uv_bau.h
+++ b/arch/x86/include/asm/uv/uv_bau.h
@@ -133,7 +133,7 @@ struct bau_msg_payload {
* see table 4.2.3.0.1 in broacast_assist spec.
*/
struct bau_msg_header {
- unsigned int dest_subnodeid:6; /* must be zero */
+ unsigned int dest_subnodeid:6; /* must be 0x10, for the LB */
/* bits 5:0 */
unsigned int base_dest_nodeid:15; /* nasid>>1 (pnode) of */
/* bits 20:6 */ /* first bit in node_map */
diff --git a/arch/x86/include/asm/uv/uv_hub.h b/arch/x86/include/asm/uv/uv_hub.h
index 341070f7ad5..77a68505419 100644
--- a/arch/x86/include/asm/uv/uv_hub.h
+++ b/arch/x86/include/asm/uv/uv_hub.h
@@ -175,7 +175,7 @@ DECLARE_PER_CPU(struct uv_hub_info_s, __uv_hub_info);
#define UV_GLOBAL_MMR32_PNODE_BITS(p) ((p) << (UV_GLOBAL_MMR32_PNODE_SHIFT))
#define UV_GLOBAL_MMR64_PNODE_BITS(p) \
- ((unsigned long)(UV_PNODE_TO_GNODE(p)) << UV_GLOBAL_MMR64_PNODE_SHIFT)
+ (((unsigned long)(p)) << UV_GLOBAL_MMR64_PNODE_SHIFT)
#define UV_APIC_PNODE_SHIFT 6
@@ -327,6 +327,7 @@ struct uv_blade_info {
unsigned short nr_possible_cpus;
unsigned short nr_online_cpus;
unsigned short pnode;
+ short memory_nid;
};
extern struct uv_blade_info *uv_blade_info;
extern short *uv_node_to_blade;
@@ -363,6 +364,12 @@ static inline int uv_blade_to_pnode(int bid)
return uv_blade_info[bid].pnode;
}
+/* Nid of memory node on blade. -1 if no blade-local memory */
+static inline int uv_blade_to_memory_nid(int bid)
+{
+ return uv_blade_info[bid].memory_nid;
+}
+
/* Determine the number of possible cpus on a blade */
static inline int uv_blade_nr_possible_cpus(int bid)
{
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index 2284a4812b6..d2ed6c5ddc8 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -3793,6 +3793,9 @@ int arch_enable_uv_irq(char *irq_name, unsigned int irq, int cpu, int mmr_blade,
mmr_pnode = uv_blade_to_pnode(mmr_blade);
uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value);
+ if (cfg->move_in_progress)
+ send_cleanup_vector(cfg);
+
return irq;
}
diff --git a/arch/x86/kernel/apic/ipi.c b/arch/x86/kernel/apic/ipi.c
index dbf5445727a..6ef00ba4c88 100644
--- a/arch/x86/kernel/apic/ipi.c
+++ b/arch/x86/kernel/apic/ipi.c
@@ -106,6 +106,9 @@ void default_send_IPI_mask_logical(const struct cpumask *cpumask, int vector)
unsigned long mask = cpumask_bits(cpumask)[0];
unsigned long flags;
+ if (WARN_ONCE(!mask, "empty IPI mask"))
+ return;
+
local_irq_save(flags);
WARN_ON(mask & ~cpumask_bits(cpu_online_mask)[0]);
__default_send_IPI_dest_field(mask, vector, apic->dest_logical);
diff --git a/arch/x86/kernel/apic/probe_64.c b/arch/x86/kernel/apic/probe_64.c
index bc3e880f9b8..fcec2f1d34a 100644
--- a/arch/x86/kernel/apic/probe_64.c
+++ b/arch/x86/kernel/apic/probe_64.c
@@ -44,6 +44,11 @@ static struct apic *apic_probe[] __initdata = {
NULL,
};
+static int apicid_phys_pkg_id(int initial_apic_id, int index_msb)
+{
+ return hard_smp_processor_id() >> index_msb;
+}
+
/*
* Check the APIC IDs in bios_cpu_apicid and choose the APIC mode.
*/
@@ -69,6 +74,11 @@ void __init default_setup_apic_routing(void)
printk(KERN_INFO "Setting APIC routing to %s\n", apic->name);
}
+ if (is_vsmp_box()) {
+ /* need to update phys_pkg_id */
+ apic->phys_pkg_id = apicid_phys_pkg_id;
+ }
+
/*
* Now that apic routing model is selected, configure the
* fault handling for intr remapping.
diff --git a/arch/x86/kernel/apic/x2apic_cluster.c b/arch/x86/kernel/apic/x2apic_cluster.c
index 8e4cbb255c3..a5371ec3677 100644
--- a/arch/x86/kernel/apic/x2apic_cluster.c
+++ b/arch/x86/kernel/apic/x2apic_cluster.c
@@ -17,11 +17,13 @@ static int x2apic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
return x2apic_enabled();
}
-/* Start with all IRQs pointing to boot CPU. IRQ balancing will shift them. */
-
+/*
+ * need to use more than cpu 0, because we need more vectors when
+ * MSI-X are used.
+ */
static const struct cpumask *x2apic_target_cpus(void)
{
- return cpumask_of(0);
+ return cpu_online_mask;
}
/*
@@ -170,7 +172,7 @@ static unsigned long set_apic_id(unsigned int id)
static int x2apic_cluster_phys_pkg_id(int initial_apicid, int index_msb)
{
- return current_cpu_data.initial_apicid >> index_msb;
+ return initial_apicid >> index_msb;
}
static void x2apic_send_IPI_self(int vector)
diff --git a/arch/x86/kernel/apic/x2apic_phys.c b/arch/x86/kernel/apic/x2apic_phys.c
index a284359627e..a8989aadc99 100644
--- a/arch/x86/kernel/apic/x2apic_phys.c
+++ b/arch/x86/kernel/apic/x2apic_phys.c
@@ -27,11 +27,13 @@ static int x2apic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
return 0;
}
-/* Start with all IRQs pointing to boot CPU. IRQ balancing will shift them. */
-
+/*
+ * need to use more than cpu 0, because we need more vectors when
+ * MSI-X are used.
+ */
static const struct cpumask *x2apic_target_cpus(void)
{
- return cpumask_of(0);
+ return cpu_online_mask;
}
static void x2apic_vector_allocation_domain(int cpu, struct cpumask *retmask)
@@ -162,7 +164,7 @@ static unsigned long set_apic_id(unsigned int id)
static int x2apic_phys_pkg_id(int initial_apicid, int index_msb)
{
- return current_cpu_data.initial_apicid >> index_msb;
+ return initial_apicid >> index_msb;
}
static void x2apic_send_IPI_self(int vector)
diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c
index 096d19aea2f..601159374e8 100644
--- a/arch/x86/kernel/apic/x2apic_uv_x.c
+++ b/arch/x86/kernel/apic/x2apic_uv_x.c
@@ -46,7 +46,7 @@ static int early_get_nodeid(void)
return node_id.s.node_id;
}
-static int uv_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+static int __init uv_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
{
if (!strcmp(oem_id, "SGI")) {
if (!strcmp(oem_table_id, "UVL"))
@@ -253,7 +253,7 @@ static void uv_send_IPI_self(int vector)
apic_write(APIC_SELF_IPI, vector);
}
-struct apic apic_x2apic_uv_x = {
+struct apic __refdata apic_x2apic_uv_x = {
.name = "UV large system",
.probe = NULL,
@@ -261,7 +261,7 @@ struct apic apic_x2apic_uv_x = {
.apic_id_registered = uv_apic_id_registered,
.irq_delivery_mode = dest_Fixed,
- .irq_dest_mode = 1, /* logical */
+ .irq_dest_mode = 0, /* physical */
.target_cpus = uv_target_cpus,
.disable_esr = 0,
@@ -362,12 +362,6 @@ static __init void get_lowmem_redirect(unsigned long *base, unsigned long *size)
BUG();
}
-static __init void map_low_mmrs(void)
-{
- init_extra_mapping_uc(UV_GLOBAL_MMR32_BASE, UV_GLOBAL_MMR32_SIZE);
- init_extra_mapping_uc(UV_LOCAL_MMR_BASE, UV_LOCAL_MMR_SIZE);
-}
-
enum map_type {map_wb, map_uc};
static __init void map_high(char *id, unsigned long base, int shift,
@@ -395,26 +389,6 @@ static __init void map_gru_high(int max_pnode)
map_high("GRU", gru.s.base, shift, max_pnode, map_wb);
}
-static __init void map_config_high(int max_pnode)
-{
- union uvh_rh_gam_cfg_overlay_config_mmr_u cfg;
- int shift = UVH_RH_GAM_CFG_OVERLAY_CONFIG_MMR_BASE_SHFT;
-
- cfg.v = uv_read_local_mmr(UVH_RH_GAM_CFG_OVERLAY_CONFIG_MMR);
- if (cfg.s.enable)
- map_high("CONFIG", cfg.s.base, shift, max_pnode, map_uc);
-}
-
-static __init void map_mmr_high(int max_pnode)
-{
- union uvh_rh_gam_mmr_overlay_config_mmr_u mmr;
- int shift = UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR_BASE_SHFT;
-
- mmr.v = uv_read_local_mmr(UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR);
- if (mmr.s.enable)
- map_high("MMR", mmr.s.base, shift, max_pnode, map_uc);
-}
-
static __init void map_mmioh_high(int max_pnode)
{
union uvh_rh_gam_mmioh_overlay_config_mmr_u mmioh;
@@ -566,8 +540,6 @@ void __init uv_system_init(void)
unsigned long mmr_base, present, paddr;
unsigned short pnode_mask;
- map_low_mmrs();
-
m_n_config.v = uv_read_local_mmr(UVH_SI_ADDR_MAP_CONFIG);
m_val = m_n_config.s.m_skt;
n_val = m_n_config.s.n_skt;
@@ -591,6 +563,8 @@ void __init uv_system_init(void)
bytes = sizeof(struct uv_blade_info) * uv_num_possible_blades();
uv_blade_info = kmalloc(bytes, GFP_KERNEL);
BUG_ON(!uv_blade_info);
+ for (blade = 0; blade < uv_num_possible_blades(); blade++)
+ uv_blade_info[blade].memory_nid = -1;
get_lowmem_redirect(&lowmem_redir_base, &lowmem_redir_size);
@@ -629,6 +603,9 @@ void __init uv_system_init(void)
lcpu = uv_blade_info[blade].nr_possible_cpus;
uv_blade_info[blade].nr_possible_cpus++;
+ /* Any node on the blade, else will contain -1. */
+ uv_blade_info[blade].memory_nid = nid;
+
uv_cpu_hub_info(cpu)->lowmem_remap_base = lowmem_redir_base;
uv_cpu_hub_info(cpu)->lowmem_remap_top = lowmem_redir_size;
uv_cpu_hub_info(cpu)->m_val = m_val;
@@ -662,11 +639,10 @@ void __init uv_system_init(void)
pnode = (paddr >> m_val) & pnode_mask;
blade = boot_pnode_to_blade(pnode);
uv_node_to_blade[nid] = blade;
+ max_pnode = max(pnode, max_pnode);
}
map_gru_high(max_pnode);
- map_mmr_high(max_pnode);
- map_config_high(max_pnode);
map_mmioh_high(max_pnode);
uv_cpu_init();
diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c
index 79302e9a33a..442b5508893 100644
--- a/arch/x86/kernel/apm_32.c
+++ b/arch/x86/kernel/apm_32.c
@@ -811,7 +811,7 @@ static int apm_do_idle(void)
u8 ret = 0;
int idled = 0;
int polling;
- int err;
+ int err = 0;
polling = !!(current_thread_info()->status & TS_POLLING);
if (polling) {
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index 3efcb2b96a1..c1f253dac15 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -7,6 +7,10 @@ ifdef CONFIG_FUNCTION_TRACER
CFLAGS_REMOVE_common.o = -pg
endif
+# Make sure load_percpu_segment has no stackprotector
+nostackp := $(call cc-option, -fno-stack-protector)
+CFLAGS_common.o := $(nostackp)
+
obj-y := intel_cacheinfo.o addon_cpuid_features.o
obj-y += proc.o capflags.o powerflags.o common.o
obj-y += vmware.o hypervisor.o
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index e2485b03f1c..63fddcd082c 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -400,6 +400,13 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c)
level = cpuid_eax(1);
if((level >= 0x0f48 && level < 0x0f50) || level >= 0x0f58)
set_cpu_cap(c, X86_FEATURE_REP_GOOD);
+
+ /*
+ * Some BIOSes incorrectly force this feature, but only K8
+ * revision D (model = 0x14) and later actually support it.
+ */
+ if (c->x86_model < 0x14)
+ clear_cpu_cap(c, X86_FEATURE_LAHF_LM);
}
if (c->x86 == 0x10 || c->x86 == 0x11)
set_cpu_cap(c, X86_FEATURE_REP_GOOD);
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index f1961c07af9..5ce60a88027 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -59,7 +59,30 @@ void __init setup_cpu_local_masks(void)
alloc_bootmem_cpumask_var(&cpu_sibling_setup_mask);
}
-static const struct cpu_dev *this_cpu __cpuinitdata;
+static void __cpuinit default_init(struct cpuinfo_x86 *c)
+{
+#ifdef CONFIG_X86_64
+ display_cacheinfo(c);
+#else
+ /* Not much we can do here... */
+ /* Check if at least it has cpuid */
+ if (c->cpuid_level == -1) {
+ /* No cpuid. It must be an ancient CPU */
+ if (c->x86 == 4)
+ strcpy(c->x86_model_id, "486");
+ else if (c->x86 == 3)
+ strcpy(c->x86_model_id, "386");
+ }
+#endif
+}
+
+static const struct cpu_dev __cpuinitconst default_cpu = {
+ .c_init = default_init,
+ .c_vendor = "Unknown",
+ .c_x86_vendor = X86_VENDOR_UNKNOWN,
+};
+
+static const struct cpu_dev *this_cpu __cpuinitdata = &default_cpu;
DEFINE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page) = { .gdt = {
#ifdef CONFIG_X86_64
@@ -332,29 +355,6 @@ void switch_to_new_gdt(int cpu)
static const struct cpu_dev *__cpuinitdata cpu_devs[X86_VENDOR_NUM] = {};
-static void __cpuinit default_init(struct cpuinfo_x86 *c)
-{
-#ifdef CONFIG_X86_64
- display_cacheinfo(c);
-#else
- /* Not much we can do here... */
- /* Check if at least it has cpuid */
- if (c->cpuid_level == -1) {
- /* No cpuid. It must be an ancient CPU */
- if (c->x86 == 4)
- strcpy(c->x86_model_id, "486");
- else if (c->x86 == 3)
- strcpy(c->x86_model_id, "386");
- }
-#endif
-}
-
-static const struct cpu_dev __cpuinitconst default_cpu = {
- .c_init = default_init,
- .c_vendor = "Unknown",
- .c_x86_vendor = X86_VENDOR_UNKNOWN,
-};
-
static void __cpuinit get_model_name(struct cpuinfo_x86 *c)
{
unsigned int *v;
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index 1cfb623ce11..01213048f62 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -1226,8 +1226,13 @@ static void mce_init(void)
}
/* Add per CPU specific workarounds here */
-static void mce_cpu_quirks(struct cpuinfo_x86 *c)
+static int mce_cpu_quirks(struct cpuinfo_x86 *c)
{
+ if (c->x86_vendor == X86_VENDOR_UNKNOWN) {
+ pr_info("MCE: unknown CPU type - not enabling MCE support.\n");
+ return -EOPNOTSUPP;
+ }
+
/* This should be disabled by the BIOS, but isn't always */
if (c->x86_vendor == X86_VENDOR_AMD) {
if (c->x86 == 15 && banks > 4) {
@@ -1273,11 +1278,20 @@ static void mce_cpu_quirks(struct cpuinfo_x86 *c)
if ((c->x86 > 6 || (c->x86 == 6 && c->x86_model >= 0xe)) &&
monarch_timeout < 0)
monarch_timeout = USEC_PER_SEC;
+
+ /*
+ * There are also broken BIOSes on some Pentium M and
+ * earlier systems:
+ */
+ if (c->x86 == 6 && c->x86_model <= 13 && mce_bootlog < 0)
+ mce_bootlog = 0;
}
if (monarch_timeout < 0)
monarch_timeout = 0;
if (mce_bootlog != 0)
mce_panic_timeout = 30;
+
+ return 0;
}
static void __cpuinit mce_ancient_init(struct cpuinfo_x86 *c)
@@ -1338,11 +1352,10 @@ void __cpuinit mcheck_init(struct cpuinfo_x86 *c)
if (!mce_available(c))
return;
- if (mce_cap_init() < 0) {
+ if (mce_cap_init() < 0 || mce_cpu_quirks(c) < 0) {
mce_disabled = 1;
return;
}
- mce_cpu_quirks(c);
machine_check_vector = do_machine_check;
diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c
index bff8dd191dd..5957a93e517 100644
--- a/arch/x86/kernel/cpu/mcheck/therm_throt.c
+++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c
@@ -36,6 +36,7 @@
static DEFINE_PER_CPU(__u64, next_check) = INITIAL_JIFFIES;
static DEFINE_PER_CPU(unsigned long, thermal_throttle_count);
+static DEFINE_PER_CPU(bool, thermal_throttle_active);
static atomic_t therm_throt_en = ATOMIC_INIT(0);
@@ -96,27 +97,33 @@ static int therm_throt_process(int curr)
{
unsigned int cpu = smp_processor_id();
__u64 tmp_jiffs = get_jiffies_64();
+ bool was_throttled = __get_cpu_var(thermal_throttle_active);
+ bool is_throttled = __get_cpu_var(thermal_throttle_active) = curr;
- if (curr)
+ if (is_throttled)
__get_cpu_var(thermal_throttle_count)++;
- if (time_before64(tmp_jiffs, __get_cpu_var(next_check)))
+ if (!(was_throttled ^ is_throttled) &&
+ time_before64(tmp_jiffs, __get_cpu_var(next_check)))
return 0;
__get_cpu_var(next_check) = tmp_jiffs + CHECK_INTERVAL;
/* if we just entered the thermal event */
- if (curr) {
+ if (is_throttled) {
printk(KERN_CRIT "CPU%d: Temperature above threshold, "
- "cpu clock throttled (total events = %lu)\n", cpu,
- __get_cpu_var(thermal_throttle_count));
+ "cpu clock throttled (total events = %lu)\n",
+ cpu, __get_cpu_var(thermal_throttle_count));
add_taint(TAINT_MACHINE_CHECK);
- } else {
- printk(KERN_CRIT "CPU%d: Temperature/speed normal\n", cpu);
+ return 1;
+ }
+ if (was_throttled) {
+ printk(KERN_INFO "CPU%d: Temperature/speed normal\n", cpu);
+ return 1;
}
- return 1;
+ return 0;
}
#ifdef CONFIG_SYSFS
diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c
index a7aa8f90095..900332b800f 100644
--- a/arch/x86/kernel/cpu/perf_counter.c
+++ b/arch/x86/kernel/cpu/perf_counter.c
@@ -55,6 +55,7 @@ struct x86_pmu {
int num_counters_fixed;
int counter_bits;
u64 counter_mask;
+ int apic;
u64 max_period;
u64 intel_ctrl;
};
@@ -72,8 +73,8 @@ static const u64 p6_perfmon_event_map[] =
{
[PERF_COUNT_HW_CPU_CYCLES] = 0x0079,
[PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0,
- [PERF_COUNT_HW_CACHE_REFERENCES] = 0x0000,
- [PERF_COUNT_HW_CACHE_MISSES] = 0x0000,
+ [PERF_COUNT_HW_CACHE_REFERENCES] = 0x0f2e,
+ [PERF_COUNT_HW_CACHE_MISSES] = 0x012e,
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4,
[PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5,
[PERF_COUNT_HW_BUS_CYCLES] = 0x0062,
@@ -613,6 +614,7 @@ static DEFINE_MUTEX(pmc_reserve_mutex);
static bool reserve_pmc_hardware(void)
{
+#ifdef CONFIG_X86_LOCAL_APIC
int i;
if (nmi_watchdog == NMI_LOCAL_APIC)
@@ -627,9 +629,11 @@ static bool reserve_pmc_hardware(void)
if (!reserve_evntsel_nmi(x86_pmu.eventsel + i))
goto eventsel_fail;
}
+#endif
return true;
+#ifdef CONFIG_X86_LOCAL_APIC
eventsel_fail:
for (i--; i >= 0; i--)
release_evntsel_nmi(x86_pmu.eventsel + i);
@@ -644,10 +648,12 @@ perfctr_fail:
enable_lapic_nmi_watchdog();
return false;
+#endif
}
static void release_pmc_hardware(void)
{
+#ifdef CONFIG_X86_LOCAL_APIC
int i;
for (i = 0; i < x86_pmu.num_counters; i++) {
@@ -657,6 +663,7 @@ static void release_pmc_hardware(void)
if (nmi_watchdog == NMI_LOCAL_APIC)
enable_lapic_nmi_watchdog();
+#endif
}
static void hw_perf_counter_destroy(struct perf_counter *counter)
@@ -748,6 +755,15 @@ static int __hw_perf_counter_init(struct perf_counter *counter)
hwc->sample_period = x86_pmu.max_period;
hwc->last_period = hwc->sample_period;
atomic64_set(&hwc->period_left, hwc->sample_period);
+ } else {
+ /*
+ * If we have a PMU initialized but no APIC
+ * interrupts, we cannot sample hardware
+ * counters (user-space has to fall back and
+ * sample via a hrtimer based software counter):
+ */
+ if (!x86_pmu.apic)
+ return -EOPNOTSUPP;
}
counter->destroy = hw_perf_counter_destroy;
@@ -1449,18 +1465,22 @@ void smp_perf_pending_interrupt(struct pt_regs *regs)
void set_perf_counter_pending(void)
{
+#ifdef CONFIG_X86_LOCAL_APIC
apic->send_IPI_self(LOCAL_PENDING_VECTOR);
+#endif
}
void perf_counters_lapic_init(void)
{
- if (!x86_pmu_initialized())
+#ifdef CONFIG_X86_LOCAL_APIC
+ if (!x86_pmu.apic || !x86_pmu_initialized())
return;
/*
* Always use NMI for PMU
*/
apic_write(APIC_LVTPC, APIC_DM_NMI);
+#endif
}
static int __kprobes
@@ -1484,7 +1504,9 @@ perf_counter_nmi_handler(struct notifier_block *self,
regs = args->regs;
+#ifdef CONFIG_X86_LOCAL_APIC
apic_write(APIC_LVTPC, APIC_DM_NMI);
+#endif
/*
* Can't rely on the handled return value to say it was our NMI, two
* counters could trigger 'simultaneously' raising two back-to-back NMIs.
@@ -1515,6 +1537,7 @@ static struct x86_pmu p6_pmu = {
.event_map = p6_pmu_event_map,
.raw_event = p6_pmu_raw_event,
.max_events = ARRAY_SIZE(p6_perfmon_event_map),
+ .apic = 1,
.max_period = (1ULL << 31) - 1,
.version = 0,
.num_counters = 2,
@@ -1541,6 +1564,7 @@ static struct x86_pmu intel_pmu = {
.event_map = intel_pmu_event_map,
.raw_event = intel_pmu_raw_event,
.max_events = ARRAY_SIZE(intel_perfmon_event_map),
+ .apic = 1,
/*
* Intel PMCs cannot be accessed sanely above 32 bit width,
* so we install an artificial 1<<31 period regardless of
@@ -1564,6 +1588,7 @@ static struct x86_pmu amd_pmu = {
.num_counters = 4,
.counter_bits = 48,
.counter_mask = (1ULL << 48) - 1,
+ .apic = 1,
/* use highest bit to detect overflow */
.max_period = (1ULL << 47) - 1,
};
@@ -1589,13 +1614,14 @@ static int p6_pmu_init(void)
return -ENODEV;
}
+ x86_pmu = p6_pmu;
+
if (!cpu_has_apic) {
- pr_info("no Local APIC, try rebooting with lapic");
- return -ENODEV;
+ pr_info("no APIC, boot with the \"lapic\" boot parameter to force-enable it.\n");
+ pr_info("no hardware sampling interrupt available.\n");
+ x86_pmu.apic = 0;
}
- x86_pmu = p6_pmu;
-
return 0;
}
diff --git a/arch/x86/kernel/efi.c b/arch/x86/kernel/efi.c
index 96f7ac0bbf0..fe26ba3e345 100644
--- a/arch/x86/kernel/efi.c
+++ b/arch/x86/kernel/efi.c
@@ -354,7 +354,7 @@ void __init efi_init(void)
*/
c16 = tmp = early_ioremap(efi.systab->fw_vendor, 2);
if (c16) {
- for (i = 0; i < sizeof(vendor) && *c16; ++i)
+ for (i = 0; i < sizeof(vendor) - 1 && *c16; ++i)
vendor[i] = *c16++;
vendor[i] = '\0';
} else
@@ -512,7 +512,7 @@ void __init efi_enter_virtual_mode(void)
&& end_pfn <= max_pfn_mapped))
va = __va(md->phys_addr);
else
- va = efi_ioremap(md->phys_addr, size);
+ va = efi_ioremap(md->phys_addr, size, md->type);
md->virt_addr = (u64) (unsigned long) va;
diff --git a/arch/x86/kernel/efi_64.c b/arch/x86/kernel/efi_64.c
index 22c3b7828c5..ac0621a7ac3 100644
--- a/arch/x86/kernel/efi_64.c
+++ b/arch/x86/kernel/efi_64.c
@@ -98,10 +98,14 @@ void __init efi_call_phys_epilog(void)
early_runtime_code_mapping_set_exec(0);
}
-void __iomem *__init efi_ioremap(unsigned long phys_addr, unsigned long size)
+void __iomem *__init efi_ioremap(unsigned long phys_addr, unsigned long size,
+ u32 type)
{
unsigned long last_map_pfn;
+ if (type == EFI_MEMORY_MAPPED_IO)
+ return ioremap(phys_addr, size);
+
last_map_pfn = init_memory_mapping(phys_addr, phys_addr + size);
if ((last_map_pfn << PAGE_SHIFT) < phys_addr + size)
return NULL;
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S
index 8663afb5653..cc827ac9e8d 100644
--- a/arch/x86/kernel/head_32.S
+++ b/arch/x86/kernel/head_32.S
@@ -261,9 +261,7 @@ page_pde_offset = (__PAGE_OFFSET >> 20);
* which will be freed later
*/
-#ifndef CONFIG_HOTPLUG_CPU
-.section .init.text,"ax",@progbits
-#endif
+__CPUINIT
#ifdef CONFIG_SMP
ENTRY(startup_32_smp)
@@ -602,7 +600,7 @@ ignore_int:
#endif
iret
-.section .cpuinit.data,"wa"
+ __REFDATA
.align 4
ENTRY(initial_code)
.long i386_start_kernel
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 994dd6a4a2a..071166a4ba8 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -519,16 +519,12 @@ static void c1e_idle(void)
if (!cpumask_test_cpu(cpu, c1e_mask)) {
cpumask_set_cpu(cpu, c1e_mask);
/*
- * Force broadcast so ACPI can not interfere. Needs
- * to run with interrupts enabled as it uses
- * smp_function_call.
+ * Force broadcast so ACPI can not interfere.
*/
- local_irq_enable();
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_FORCE,
&cpu);
printk(KERN_INFO "Switch to broadcast mode on CPU%d\n",
cpu);
- local_irq_disable();
}
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu);
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index 508e982dd07..a06e8d10184 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -3,6 +3,7 @@
#include <linux/init.h>
#include <linux/pm.h>
#include <linux/efi.h>
+#include <linux/dmi.h>
#include <acpi/reboot.h>
#include <asm/io.h>
#include <asm/apic.h>
@@ -17,7 +18,6 @@
#include <asm/cpu.h>
#ifdef CONFIG_X86_32
-# include <linux/dmi.h>
# include <linux/ctype.h>
# include <linux/mc146818rtc.h>
#else
@@ -404,6 +404,46 @@ EXPORT_SYMBOL(machine_real_restart);
#endif /* CONFIG_X86_32 */
+/*
+ * Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot
+ */
+static int __init set_pci_reboot(const struct dmi_system_id *d)
+{
+ if (reboot_type != BOOT_CF9) {
+ reboot_type = BOOT_CF9;
+ printk(KERN_INFO "%s series board detected. "
+ "Selecting PCI-method for reboots.\n", d->ident);
+ }
+ return 0;
+}
+
+static struct dmi_system_id __initdata pci_reboot_dmi_table[] = {
+ { /* Handle problems with rebooting on Apple MacBook5 */
+ .callback = set_pci_reboot,
+ .ident = "Apple MacBook5",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5"),
+ },
+ },
+ { /* Handle problems with rebooting on Apple MacBookPro5 */
+ .callback = set_pci_reboot,
+ .ident = "Apple MacBookPro5",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5"),
+ },
+ },
+ { }
+};
+
+static int __init pci_reboot_init(void)
+{
+ dmi_check_system(pci_reboot_dmi_table);
+ return 0;
+}
+core_initcall(pci_reboot_init);
+
static inline void kb_wait(void)
{
int i;
diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c
index 29a3eef7cf4..07d81916f21 100644
--- a/arch/x86/kernel/setup_percpu.c
+++ b/arch/x86/kernel/setup_percpu.c
@@ -165,7 +165,7 @@ static ssize_t __init setup_pcpu_lpage(size_t static_size, bool chosen)
if (!chosen) {
size_t vm_size = VMALLOC_END - VMALLOC_START;
- size_t tot_size = num_possible_cpus() * PMD_SIZE;
+ size_t tot_size = nr_cpu_ids * PMD_SIZE;
/* on non-NUMA, embedding is better */
if (!pcpu_need_numa())
@@ -199,7 +199,7 @@ static ssize_t __init setup_pcpu_lpage(size_t static_size, bool chosen)
dyn_size = pcpul_size - static_size - PERCPU_FIRST_CHUNK_RESERVE;
/* allocate pointer array and alloc large pages */
- map_size = PFN_ALIGN(num_possible_cpus() * sizeof(pcpul_map[0]));
+ map_size = PFN_ALIGN(nr_cpu_ids * sizeof(pcpul_map[0]));
pcpul_map = alloc_bootmem(map_size);
for_each_possible_cpu(cpu) {
@@ -228,7 +228,7 @@ static ssize_t __init setup_pcpu_lpage(size_t static_size, bool chosen)
/* allocate address and map */
pcpul_vm.flags = VM_ALLOC;
- pcpul_vm.size = num_possible_cpus() * PMD_SIZE;
+ pcpul_vm.size = nr_cpu_ids * PMD_SIZE;
vm_area_register_early(&pcpul_vm, PMD_SIZE);
for_each_possible_cpu(cpu) {
@@ -250,8 +250,8 @@ static ssize_t __init setup_pcpu_lpage(size_t static_size, bool chosen)
PMD_SIZE, pcpul_vm.addr, NULL);
/* sort pcpul_map array for pcpu_lpage_remapped() */
- for (i = 0; i < num_possible_cpus() - 1; i++)
- for (j = i + 1; j < num_possible_cpus(); j++)
+ for (i = 0; i < nr_cpu_ids - 1; i++)
+ for (j = i + 1; j < nr_cpu_ids; j++)
if (pcpul_map[i].ptr > pcpul_map[j].ptr) {
struct pcpul_ent tmp = pcpul_map[i];
pcpul_map[i] = pcpul_map[j];
@@ -288,7 +288,7 @@ void *pcpu_lpage_remapped(void *kaddr)
{
void *pmd_addr = (void *)((unsigned long)kaddr & PMD_MASK);
unsigned long offset = (unsigned long)kaddr & ~PMD_MASK;
- int left = 0, right = num_possible_cpus() - 1;
+ int left = 0, right = nr_cpu_ids - 1;
int pos;
/* pcpul in use at all? */
@@ -377,7 +377,7 @@ static ssize_t __init setup_pcpu_4k(size_t static_size)
pcpu4k_nr_static_pages = PFN_UP(static_size);
/* unaligned allocations can't be freed, round up to page size */
- pages_size = PFN_ALIGN(pcpu4k_nr_static_pages * num_possible_cpus()
+ pages_size = PFN_ALIGN(pcpu4k_nr_static_pages * nr_cpu_ids
* sizeof(pcpu4k_pages[0]));
pcpu4k_pages = alloc_bootmem(pages_size);
diff --git a/arch/x86/kernel/tlb_uv.c b/arch/x86/kernel/tlb_uv.c
index 8ccabb8a2f6..77b9689f8ed 100644
--- a/arch/x86/kernel/tlb_uv.c
+++ b/arch/x86/kernel/tlb_uv.c
@@ -744,6 +744,7 @@ uv_activation_descriptor_init(int node, int pnode)
* note that base_dest_nodeid is actually a nasid.
*/
ad2->header.base_dest_nodeid = uv_partition_base_pnode << 1;
+ ad2->header.dest_subnodeid = 0x10; /* the LB */
ad2->header.command = UV_NET_ENDPOINT_INTD;
ad2->header.int_both = 1;
/*
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 6e1a368d21d..71f4368b357 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -275,15 +275,20 @@ static unsigned long pit_calibrate_tsc(u32 latch, unsigned long ms, int loopmin)
* use the TSC value at the transitions to calculate a pretty
* good value for the TSC frequencty.
*/
+static inline int pit_verify_msb(unsigned char val)
+{
+ /* Ignore LSB */
+ inb(0x42);
+ return inb(0x42) == val;
+}
+
static inline int pit_expect_msb(unsigned char val, u64 *tscp, unsigned long *deltap)
{
int count;
u64 tsc = 0;
for (count = 0; count < 50000; count++) {
- /* Ignore LSB */
- inb(0x42);
- if (inb(0x42) != val)
+ if (!pit_verify_msb(val))
break;
tsc = get_cycles();
}
@@ -336,8 +341,7 @@ static unsigned long quick_pit_calibrate(void)
* to do that is to just read back the 16-bit counter
* once from the PIT.
*/
- inb(0x42);
- inb(0x42);
+ pit_verify_msb(0);
if (pit_expect_msb(0xff, &tsc, &d1)) {
for (i = 1; i <= MAX_QUICK_PIT_ITERATIONS; i++) {
@@ -348,8 +352,19 @@ static unsigned long quick_pit_calibrate(void)
* Iterate until the error is less than 500 ppm
*/
delta -= tsc;
- if (d1+d2 < delta >> 11)
- goto success;
+ if (d1+d2 >= delta >> 11)
+ continue;
+
+ /*
+ * Check the PIT one more time to verify that
+ * all TSC reads were stable wrt the PIT.
+ *
+ * This also guarantees serialization of the
+ * last cycle read ('d2') in pit_expect_msb.
+ */
+ if (!pit_verify_msb(0xfe - i))
+ break;
+ goto success;
}
}
printk("Fast TSC calibration failed\n");
diff --git a/arch/x86/kernel/vmi_32.c b/arch/x86/kernel/vmi_32.c
index b263423fbe2..95a7289e4b0 100644
--- a/arch/x86/kernel/vmi_32.c
+++ b/arch/x86/kernel/vmi_32.c
@@ -441,7 +441,7 @@ vmi_startup_ipi_hook(int phys_apicid, unsigned long start_eip,
ap.ds = __USER_DS;
ap.es = __USER_DS;
ap.fs = __KERNEL_PERCPU;
- ap.gs = 0;
+ ap.gs = __KERNEL_STACK_CANARY;
ap.eflags = 0;
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index 59f31d2dd43..9fc178255c0 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -46,11 +46,10 @@ PHDRS {
data PT_LOAD FLAGS(7); /* RWE */
#ifdef CONFIG_X86_64
user PT_LOAD FLAGS(7); /* RWE */
- data.init PT_LOAD FLAGS(7); /* RWE */
#ifdef CONFIG_SMP
percpu PT_LOAD FLAGS(7); /* RWE */
#endif
- data.init2 PT_LOAD FLAGS(7); /* RWE */
+ init PT_LOAD FLAGS(7); /* RWE */
#endif
note PT_NOTE FLAGS(0); /* ___ */
}
@@ -103,65 +102,43 @@ SECTIONS
__stop___ex_table = .;
} :text = 0x9090
- RODATA
+ RO_DATA(PAGE_SIZE)
/* Data */
- . = ALIGN(PAGE_SIZE);
.data : AT(ADDR(.data) - LOAD_OFFSET) {
/* Start of data section */
_sdata = .;
- DATA_DATA
- CONSTRUCTORS
- } :data
+
+ /* init_task */
+ INIT_TASK_DATA(THREAD_SIZE)
#ifdef CONFIG_X86_32
- /* 32 bit has nosave before _edata */
- . = ALIGN(PAGE_SIZE);
- .data_nosave : AT(ADDR(.data_nosave) - LOAD_OFFSET) {
- __nosave_begin = .;
- *(.data.nosave)
- . = ALIGN(PAGE_SIZE);
- __nosave_end = .;
- }
+ /* 32 bit has nosave before _edata */
+ NOSAVE_DATA
#endif
- . = ALIGN(PAGE_SIZE);
- .data.page_aligned : AT(ADDR(.data.page_aligned) - LOAD_OFFSET) {
- *(.data.page_aligned)
+ PAGE_ALIGNED_DATA(PAGE_SIZE)
*(.data.idt)
- }
-#ifdef CONFIG_X86_32
- . = ALIGN(32);
-#else
- . = ALIGN(PAGE_SIZE);
- . = ALIGN(CONFIG_X86_L1_CACHE_BYTES);
-#endif
- .data.cacheline_aligned :
- AT(ADDR(.data.cacheline_aligned) - LOAD_OFFSET) {
- *(.data.cacheline_aligned)
- }
+ CACHELINE_ALIGNED_DATA(CONFIG_X86_L1_CACHE_BYTES)
- /* rarely changed data like cpu maps */
-#ifdef CONFIG_X86_32
- . = ALIGN(32);
-#else
- . = ALIGN(CONFIG_X86_INTERNODE_CACHE_BYTES);
-#endif
- .data.read_mostly : AT(ADDR(.data.read_mostly) - LOAD_OFFSET) {
- *(.data.read_mostly)
+ DATA_DATA
+ CONSTRUCTORS
+
+ /* rarely changed data like cpu maps */
+ READ_MOSTLY_DATA(CONFIG_X86_INTERNODE_CACHE_BYTES)
/* End of data section */
_edata = .;
- }
+ } :data
#ifdef CONFIG_X86_64
#define VSYSCALL_ADDR (-10*1024*1024)
-#define VSYSCALL_PHYS_ADDR ((LOADADDR(.data.read_mostly) + \
- SIZEOF(.data.read_mostly) + 4095) & ~(4095))
-#define VSYSCALL_VIRT_ADDR ((ADDR(.data.read_mostly) + \
- SIZEOF(.data.read_mostly) + 4095) & ~(4095))
+#define VSYSCALL_PHYS_ADDR ((LOADADDR(.data) + SIZEOF(.data) + \
+ PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
+#define VSYSCALL_VIRT_ADDR ((ADDR(.data) + SIZEOF(.data) + \
+ PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
#define VLOAD_OFFSET (VSYSCALL_ADDR - VSYSCALL_PHYS_ADDR)
#define VLOAD(x) (ADDR(x) - VLOAD_OFFSET)
@@ -227,35 +204,29 @@ SECTIONS
#endif /* CONFIG_X86_64 */
- /* init_task */
- . = ALIGN(THREAD_SIZE);
- .data.init_task : AT(ADDR(.data.init_task) - LOAD_OFFSET) {
- *(.data.init_task)
+ /* Init code and data - will be freed after init */
+ . = ALIGN(PAGE_SIZE);
+ .init.begin : AT(ADDR(.init.begin) - LOAD_OFFSET) {
+ __init_begin = .; /* paired with __init_end */
}
-#ifdef CONFIG_X86_64
- :data.init
-#endif
+#if defined(CONFIG_X86_64) && defined(CONFIG_SMP)
/*
- * smp_locks might be freed after init
- * start/end must be page aligned
+ * percpu offsets are zero-based on SMP. PERCPU_VADDR() changes the
+ * output PHDR, so the next output section - .init.text - should
+ * start another segment - init.
*/
- . = ALIGN(PAGE_SIZE);
- .smp_locks : AT(ADDR(.smp_locks) - LOAD_OFFSET) {
- __smp_locks = .;
- *(.smp_locks)
- __smp_locks_end = .;
- . = ALIGN(PAGE_SIZE);
- }
+ PERCPU_VADDR(0, :percpu)
+#endif
- /* Init code and data - will be freed after init */
- . = ALIGN(PAGE_SIZE);
.init.text : AT(ADDR(.init.text) - LOAD_OFFSET) {
- __init_begin = .; /* paired with __init_end */
_sinittext = .;
INIT_TEXT
_einittext = .;
}
+#ifdef CONFIG_X86_64
+ :init
+#endif
.init.data : AT(ADDR(.init.data) - LOAD_OFFSET) {
INIT_DATA
@@ -326,17 +297,7 @@ SECTIONS
}
#endif
-#if defined(CONFIG_X86_64) && defined(CONFIG_SMP)
- /*
- * percpu offsets are zero-based on SMP. PERCPU_VADDR() changes the
- * output PHDR, so the next output section - __data_nosave - should
- * start another section data.init2. Also, pda should be at the head of
- * percpu area. Preallocate it and define the percpu offset symbol
- * so that it can be accessed as a percpu variable.
- */
- . = ALIGN(PAGE_SIZE);
- PERCPU_VADDR(0, :percpu)
-#else
+#if !defined(CONFIG_X86_64) || !defined(CONFIG_SMP)
PERCPU(PAGE_SIZE)
#endif
@@ -347,15 +308,22 @@ SECTIONS
__init_end = .;
}
+ /*
+ * smp_locks might be freed after init
+ * start/end must be page aligned
+ */
+ . = ALIGN(PAGE_SIZE);
+ .smp_locks : AT(ADDR(.smp_locks) - LOAD_OFFSET) {
+ __smp_locks = .;
+ *(.smp_locks)
+ __smp_locks_end = .;
+ . = ALIGN(PAGE_SIZE);
+ }
+
#ifdef CONFIG_X86_64
.data_nosave : AT(ADDR(.data_nosave) - LOAD_OFFSET) {
- . = ALIGN(PAGE_SIZE);
- __nosave_begin = .;
- *(.data.nosave)
- . = ALIGN(PAGE_SIZE);
- __nosave_end = .;
- } :data.init2
- /* use another section data.init2, see PERCPU_VADDR() above */
+ NOSAVE_DATA
+ }
#endif
/* BSS */
@@ -393,8 +361,8 @@ SECTIONS
#ifdef CONFIG_X86_32
-ASSERT((_end - LOAD_OFFSET <= KERNEL_IMAGE_SIZE),
- "kernel image bigger than KERNEL_IMAGE_SIZE")
+. = ASSERT((_end - LOAD_OFFSET <= KERNEL_IMAGE_SIZE),
+ "kernel image bigger than KERNEL_IMAGE_SIZE");
#else
/*
* Per-cpu symbols which need to be offset from __per_cpu_load
@@ -407,12 +375,12 @@ INIT_PER_CPU(irq_stack_union);
/*
* Build-time check on the image size:
*/
-ASSERT((_end - _text <= KERNEL_IMAGE_SIZE),
- "kernel image bigger than KERNEL_IMAGE_SIZE")
+. = ASSERT((_end - _text <= KERNEL_IMAGE_SIZE),
+ "kernel image bigger than KERNEL_IMAGE_SIZE");
#ifdef CONFIG_SMP
-ASSERT((per_cpu__irq_stack_union == 0),
- "irq_stack_union is not at start of per-cpu area");
+. = ASSERT((per_cpu__irq_stack_union == 0),
+ "irq_stack_union is not at start of per-cpu area");
#endif
#endif /* CONFIG_X86_32 */
@@ -420,7 +388,7 @@ ASSERT((per_cpu__irq_stack_union == 0),
#ifdef CONFIG_KEXEC
#include <asm/kexec.h>
-ASSERT(kexec_control_code_size <= KEXEC_CONTROL_CODE_MAX_SIZE,
- "kexec control code size is too big")
+. = ASSERT(kexec_control_code_size <= KEXEC_CONTROL_CODE_MAX_SIZE,
+ "kexec control code size is too big");
#endif
diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c
index 4d6f0d293ee..21f68e00524 100644
--- a/arch/x86/kvm/i8254.c
+++ b/arch/x86/kvm/i8254.c
@@ -104,6 +104,9 @@ static s64 __kpit_elapsed(struct kvm *kvm)
ktime_t remaining;
struct kvm_kpit_state *ps = &kvm->arch.vpit->pit_state;
+ if (!ps->pit_timer.period)
+ return 0;
+
/*
* The Counter does not stop when it reaches zero. In
* Modes 0, 1, 4, and 5 the Counter ``wraps around'' to
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index 7030b5f911b..0ef5bb2b404 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -489,16 +489,20 @@ static unsigned long *gfn_to_rmap(struct kvm *kvm, gfn_t gfn, int lpage)
*
* If rmapp bit zero is one, (then rmap & ~1) points to a struct kvm_rmap_desc
* containing more mappings.
+ *
+ * Returns the number of rmap entries before the spte was added or zero if
+ * the spte was not added.
+ *
*/
-static void rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn, int lpage)
+static int rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn, int lpage)
{
struct kvm_mmu_page *sp;
struct kvm_rmap_desc *desc;
unsigned long *rmapp;
- int i;
+ int i, count = 0;
if (!is_rmap_pte(*spte))
- return;
+ return count;
gfn = unalias_gfn(vcpu->kvm, gfn);
sp = page_header(__pa(spte));
sp->gfns[spte - sp->spt] = gfn;
@@ -515,8 +519,10 @@ static void rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn, int lpage)
} else {
rmap_printk("rmap_add: %p %llx many->many\n", spte, *spte);
desc = (struct kvm_rmap_desc *)(*rmapp & ~1ul);
- while (desc->shadow_ptes[RMAP_EXT-1] && desc->more)
+ while (desc->shadow_ptes[RMAP_EXT-1] && desc->more) {
desc = desc->more;
+ count += RMAP_EXT;
+ }
if (desc->shadow_ptes[RMAP_EXT-1]) {
desc->more = mmu_alloc_rmap_desc(vcpu);
desc = desc->more;
@@ -525,6 +531,7 @@ static void rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn, int lpage)
;
desc->shadow_ptes[i] = spte;
}
+ return count;
}
static void rmap_desc_remove_entry(unsigned long *rmapp,
@@ -754,6 +761,19 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp)
return young;
}
+#define RMAP_RECYCLE_THRESHOLD 1000
+
+static void rmap_recycle(struct kvm_vcpu *vcpu, gfn_t gfn, int lpage)
+{
+ unsigned long *rmapp;
+
+ gfn = unalias_gfn(vcpu->kvm, gfn);
+ rmapp = gfn_to_rmap(vcpu->kvm, gfn, lpage);
+
+ kvm_unmap_rmapp(vcpu->kvm, rmapp);
+ kvm_flush_remote_tlbs(vcpu->kvm);
+}
+
int kvm_age_hva(struct kvm *kvm, unsigned long hva)
{
return kvm_handle_hva(kvm, hva, kvm_age_rmapp);
@@ -1407,24 +1427,25 @@ static int kvm_mmu_zap_page(struct kvm *kvm, struct kvm_mmu_page *sp)
*/
void kvm_mmu_change_mmu_pages(struct kvm *kvm, unsigned int kvm_nr_mmu_pages)
{
+ int used_pages;
+
+ used_pages = kvm->arch.n_alloc_mmu_pages - kvm->arch.n_free_mmu_pages;
+ used_pages = max(0, used_pages);
+
/*
* If we set the number of mmu pages to be smaller be than the
* number of actived pages , we must to free some mmu pages before we
* change the value
*/
- if ((kvm->arch.n_alloc_mmu_pages - kvm->arch.n_free_mmu_pages) >
- kvm_nr_mmu_pages) {
- int n_used_mmu_pages = kvm->arch.n_alloc_mmu_pages
- - kvm->arch.n_free_mmu_pages;
-
- while (n_used_mmu_pages > kvm_nr_mmu_pages) {
+ if (used_pages > kvm_nr_mmu_pages) {
+ while (used_pages > kvm_nr_mmu_pages) {
struct kvm_mmu_page *page;
page = container_of(kvm->arch.active_mmu_pages.prev,
struct kvm_mmu_page, link);
kvm_mmu_zap_page(kvm, page);
- n_used_mmu_pages--;
+ used_pages--;
}
kvm->arch.n_free_mmu_pages = 0;
}
@@ -1740,6 +1761,7 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
{
int was_rmapped = 0;
int was_writeble = is_writeble_pte(*shadow_pte);
+ int rmap_count;
pgprintk("%s: spte %llx access %x write_fault %d"
" user_fault %d gfn %lx\n",
@@ -1781,9 +1803,11 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
page_header_update_slot(vcpu->kvm, shadow_pte, gfn);
if (!was_rmapped) {
- rmap_add(vcpu, shadow_pte, gfn, largepage);
+ rmap_count = rmap_add(vcpu, shadow_pte, gfn, largepage);
if (!is_rmap_pte(*shadow_pte))
kvm_release_pfn_clean(pfn);
+ if (rmap_count > RMAP_RECYCLE_THRESHOLD)
+ rmap_recycle(vcpu, gfn, largepage);
} else {
if (was_writeble)
kvm_release_pfn_dirty(pfn);
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 71510e07e69..b1f658ad2f0 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -711,6 +711,7 @@ static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
svm->vmcb->control.tsc_offset += delta;
vcpu->cpu = cpu;
kvm_migrate_timers(vcpu);
+ svm->asid_generation = 0;
}
for (i = 0; i < NR_HOST_SAVE_USER_MSRS; i++)
@@ -1031,7 +1032,6 @@ static void new_asid(struct vcpu_svm *svm, struct svm_cpu_data *svm_data)
svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ALL_ASID;
}
- svm->vcpu.cpu = svm_data->cpu;
svm->asid_generation = svm_data->asid_generation;
svm->vmcb->control.asid = svm_data->next_asid++;
}
@@ -2300,8 +2300,8 @@ static void pre_svm_run(struct vcpu_svm *svm)
struct svm_cpu_data *svm_data = per_cpu(svm_data, cpu);
svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING;
- if (svm->vcpu.cpu != cpu ||
- svm->asid_generation != svm_data->asid_generation)
+ /* FIXME: handle wraparound of asid_generation */
+ if (svm->asid_generation != svm_data->asid_generation)
new_asid(svm, svm_data);
}
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 356a0ce85c6..29f912927a5 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -3157,8 +3157,8 @@ static void handle_invalid_guest_state(struct kvm_vcpu *vcpu,
struct vcpu_vmx *vmx = to_vmx(vcpu);
enum emulation_result err = EMULATE_DONE;
- preempt_enable();
local_irq_enable();
+ preempt_enable();
while (!guest_state_valid(vcpu)) {
err = emulate_instruction(vcpu, kvm_run, 0, 0, 0);
@@ -3168,7 +3168,7 @@ static void handle_invalid_guest_state(struct kvm_vcpu *vcpu,
if (err != EMULATE_DONE) {
kvm_report_emulation_failure(vcpu, "emulation failure");
- return;
+ break;
}
if (signal_pending(current))
@@ -3177,8 +3177,8 @@ static void handle_invalid_guest_state(struct kvm_vcpu *vcpu,
schedule();
}
- local_irq_disable();
preempt_disable();
+ local_irq_disable();
vmx->invalid_state_emulation_result = err;
}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index fe5474aec41..3d452901182 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -704,11 +704,48 @@ static bool msr_mtrr_valid(unsigned msr)
return false;
}
+static bool valid_pat_type(unsigned t)
+{
+ return t < 8 && (1 << t) & 0xf3; /* 0, 1, 4, 5, 6, 7 */
+}
+
+static bool valid_mtrr_type(unsigned t)
+{
+ return t < 8 && (1 << t) & 0x73; /* 0, 1, 4, 5, 6 */
+}
+
+static bool mtrr_valid(struct kvm_vcpu *vcpu, u32 msr, u64 data)
+{
+ int i;
+
+ if (!msr_mtrr_valid(msr))
+ return false;
+
+ if (msr == MSR_IA32_CR_PAT) {
+ for (i = 0; i < 8; i++)
+ if (!valid_pat_type((data >> (i * 8)) & 0xff))
+ return false;
+ return true;
+ } else if (msr == MSR_MTRRdefType) {
+ if (data & ~0xcff)
+ return false;
+ return valid_mtrr_type(data & 0xff);
+ } else if (msr >= MSR_MTRRfix64K_00000 && msr <= MSR_MTRRfix4K_F8000) {
+ for (i = 0; i < 8 ; i++)
+ if (!valid_mtrr_type((data >> (i * 8)) & 0xff))
+ return false;
+ return true;
+ }
+
+ /* variable MTRRs */
+ return valid_mtrr_type(data & 0xff);
+}
+
static int set_msr_mtrr(struct kvm_vcpu *vcpu, u32 msr, u64 data)
{
u64 *p = (u64 *)&vcpu->arch.mtrr_state.fixed_ranges;
- if (!msr_mtrr_valid(msr))
+ if (!mtrr_valid(vcpu, msr, data))
return 1;
if (msr == MSR_MTRRdefType) {
@@ -1079,14 +1116,13 @@ long kvm_arch_dev_ioctl(struct file *filp,
if (copy_to_user(user_msr_list, &msr_list, sizeof msr_list))
goto out;
r = -E2BIG;
- if (n < num_msrs_to_save)
+ if (n < msr_list.nmsrs)
goto out;
r = -EFAULT;
if (copy_to_user(user_msr_list->indices, &msrs_to_save,
num_msrs_to_save * sizeof(u32)))
goto out;
- if (copy_to_user(user_msr_list->indices
- + num_msrs_to_save * sizeof(u32),
+ if (copy_to_user(user_msr_list->indices + num_msrs_to_save,
&emulated_msrs,
ARRAY_SIZE(emulated_msrs) * sizeof(u32)))
goto out;
diff --git a/arch/x86/lib/msr.c b/arch/x86/lib/msr.c
index 1440b9c0547..caa24aca811 100644
--- a/arch/x86/lib/msr.c
+++ b/arch/x86/lib/msr.c
@@ -89,16 +89,13 @@ void rdmsr_on_cpus(const cpumask_t *mask, u32 msr_no, struct msr *msrs)
rv.msrs = msrs;
rv.msr_no = msr_no;
- preempt_disable();
- /*
- * FIXME: handle the CPU we're executing on separately for now until
- * smp_call_function_many has been fixed to not skip it.
- */
- this_cpu = raw_smp_processor_id();
- smp_call_function_single(this_cpu, __rdmsr_on_cpu, &rv, 1);
+ this_cpu = get_cpu();
+
+ if (cpumask_test_cpu(this_cpu, mask))
+ __rdmsr_on_cpu(&rv);
smp_call_function_many(mask, __rdmsr_on_cpu, &rv, 1);
- preempt_enable();
+ put_cpu();
}
EXPORT_SYMBOL(rdmsr_on_cpus);
@@ -121,16 +118,13 @@ void wrmsr_on_cpus(const cpumask_t *mask, u32 msr_no, struct msr *msrs)
rv.msrs = msrs;
rv.msr_no = msr_no;
- preempt_disable();
- /*
- * FIXME: handle the CPU we're executing on separately for now until
- * smp_call_function_many has been fixed to not skip it.
- */
- this_cpu = raw_smp_processor_id();
- smp_call_function_single(this_cpu, __wrmsr_on_cpu, &rv, 1);
+ this_cpu = get_cpu();
+
+ if (cpumask_test_cpu(this_cpu, mask))
+ __wrmsr_on_cpu(&rv);
smp_call_function_many(mask, __wrmsr_on_cpu, &rv, 1);
- preempt_enable();
+ put_cpu();
}
EXPORT_SYMBOL(wrmsr_on_cpus);
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 6176fe8f29e..ea56b8cbb6a 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -796,7 +796,7 @@ int __init reserve_bootmem_generic(unsigned long phys, unsigned long len,
return ret;
#else
- reserve_bootmem(phys, len, BOOTMEM_DEFAULT);
+ reserve_bootmem(phys, len, flags);
#endif
if (phys+len <= MAX_DMA_PFN*PAGE_SIZE) {
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index 1b734d7a896..7e600c1962d 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -591,9 +591,12 @@ static int __change_page_attr(struct cpa_data *cpa, int primary)
unsigned int level;
pte_t *kpte, old_pte;
- if (cpa->flags & CPA_PAGES_ARRAY)
- address = (unsigned long)page_address(cpa->pages[cpa->curpage]);
- else if (cpa->flags & CPA_ARRAY)
+ if (cpa->flags & CPA_PAGES_ARRAY) {
+ struct page *page = cpa->pages[cpa->curpage];
+ if (unlikely(PageHighMem(page)))
+ return 0;
+ address = (unsigned long)page_address(page);
+ } else if (cpa->flags & CPA_ARRAY)
address = cpa->vaddr[cpa->curpage];
else
address = *cpa->vaddr;
@@ -697,9 +700,12 @@ static int cpa_process_alias(struct cpa_data *cpa)
* No need to redo, when the primary call touched the direct
* mapping already:
*/
- if (cpa->flags & CPA_PAGES_ARRAY)
- vaddr = (unsigned long)page_address(cpa->pages[cpa->curpage]);
- else if (cpa->flags & CPA_ARRAY)
+ if (cpa->flags & CPA_PAGES_ARRAY) {
+ struct page *page = cpa->pages[cpa->curpage];
+ if (unlikely(PageHighMem(page)))
+ return 0;
+ vaddr = (unsigned long)page_address(page);
+ } else if (cpa->flags & CPA_ARRAY)
vaddr = cpa->vaddr[cpa->curpage];
else
vaddr = *cpa->vaddr;
@@ -997,12 +1003,15 @@ EXPORT_SYMBOL(set_memory_array_uc);
int _set_memory_wc(unsigned long addr, int numpages)
{
int ret;
+ unsigned long addr_copy = addr;
+
ret = change_page_attr_set(&addr, numpages,
__pgprot(_PAGE_CACHE_UC_MINUS), 0);
-
if (!ret) {
- ret = change_page_attr_set(&addr, numpages,
- __pgprot(_PAGE_CACHE_WC), 0);
+ ret = change_page_attr_set_clr(&addr_copy, numpages,
+ __pgprot(_PAGE_CACHE_WC),
+ __pgprot(_PAGE_CACHE_MASK),
+ 0, 0, NULL);
}
return ret;
}
@@ -1119,7 +1128,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray)
int free_idx;
for (i = 0; i < addrinarray; i++) {
- start = (unsigned long)page_address(pages[i]);
+ if (PageHighMem(pages[i]))
+ continue;
+ start = page_to_pfn(pages[i]) << PAGE_SHIFT;
end = start + PAGE_SIZE;
if (reserve_memtype(start, end, _PAGE_CACHE_UC_MINUS, NULL))
goto err_out;
@@ -1132,7 +1143,9 @@ int set_pages_array_uc(struct page **pages, int addrinarray)
err_out:
free_idx = i;
for (i = 0; i < free_idx; i++) {
- start = (unsigned long)page_address(pages[i]);
+ if (PageHighMem(pages[i]))
+ continue;
+ start = page_to_pfn(pages[i]) << PAGE_SHIFT;
end = start + PAGE_SIZE;
free_memtype(start, end);
}
@@ -1161,7 +1174,9 @@ int set_pages_array_wb(struct page **pages, int addrinarray)
return retval;
for (i = 0; i < addrinarray; i++) {
- start = (unsigned long)page_address(pages[i]);
+ if (PageHighMem(pages[i]))
+ continue;
+ start = page_to_pfn(pages[i]) << PAGE_SHIFT;
end = start + PAGE_SIZE;
free_memtype(start, end);
}
diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c
index e6718bb2806..352aa9e927e 100644
--- a/arch/x86/mm/pat.c
+++ b/arch/x86/mm/pat.c
@@ -623,7 +623,8 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot,
return ret;
if (flags != want_flags) {
- if (strict_prot || !is_new_memtype_allowed(want_flags, flags)) {
+ if (strict_prot ||
+ !is_new_memtype_allowed(paddr, size, want_flags, flags)) {
free_memtype(paddr, paddr + size);
printk(KERN_ERR "%s:%d map pfn expected mapping type %s"
" for %Lx-%Lx, got %s\n",
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
index af8f9650058..ed34f5e3599 100644
--- a/arch/x86/mm/pgtable.c
+++ b/arch/x86/mm/pgtable.c
@@ -329,7 +329,6 @@ void __init reserve_top_address(unsigned long reserve)
printk(KERN_INFO "Reserving virtual address space above 0x%08x\n",
(int)-reserve);
__FIXADDR_TOP = -reserve - PAGE_SIZE;
- __VMALLOC_RESERVE += reserve;
#endif
}
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 821e97017e9..c814e144a3f 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -183,18 +183,17 @@ static void flush_tlb_others_ipi(const struct cpumask *cpumask,
f->flush_mm = mm;
f->flush_va = va;
- cpumask_andnot(to_cpumask(f->flush_cpumask),
- cpumask, cpumask_of(smp_processor_id()));
-
- /*
- * We have to send the IPI only to
- * CPUs affected.
- */
- apic->send_IPI_mask(to_cpumask(f->flush_cpumask),
- INVALIDATE_TLB_VECTOR_START + sender);
+ if (cpumask_andnot(to_cpumask(f->flush_cpumask), cpumask, cpumask_of(smp_processor_id()))) {
+ /*
+ * We have to send the IPI only to
+ * CPUs affected.
+ */
+ apic->send_IPI_mask(to_cpumask(f->flush_cpumask),
+ INVALIDATE_TLB_VECTOR_START + sender);
- while (!cpumask_empty(to_cpumask(f->flush_cpumask)))
- cpu_relax();
+ while (!cpumask_empty(to_cpumask(f->flush_cpumask)))
+ cpu_relax();
+ }
f->flush_mm = NULL;
f->flush_va = 0;
diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile
index 172438f86a0..7410640db17 100644
--- a/arch/x86/xen/Makefile
+++ b/arch/x86/xen/Makefile
@@ -5,6 +5,10 @@ CFLAGS_REMOVE_time.o = -pg
CFLAGS_REMOVE_irq.o = -pg
endif
+# Make sure early boot has no stackprotector
+nostackp := $(call cc-option, -fno-stack-protector)
+CFLAGS_enlighten.o := $(nostackp)
+
obj-y := enlighten.o setup.o multicalls.o mmu.o irq.o \
time.o xen-asm.o xen-asm_$(BITS).o \
grant-table.o suspend.o
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index 0a1700a2be9..eb33aaa8415 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -215,6 +215,7 @@ static __init void xen_init_cpuid_mask(void)
(1 << X86_FEATURE_ACPI)); /* disable ACPI */
ax = 1;
+ cx = 0;
xen_cpuid(&ax, &bx, &cx, &dx);
/* cpuid claims we support xsave; try enabling it to see what happens */
@@ -974,10 +975,6 @@ asmlinkage void __init xen_start_kernel(void)
xen_domain_type = XEN_PV_DOMAIN;
- BUG_ON(memcmp(xen_start_info->magic, "xen-3", 5) != 0);
-
- xen_setup_features();
-
/* Install Xen paravirt ops */
pv_info = xen_info;
pv_init_ops = xen_init_ops;
@@ -986,8 +983,15 @@ asmlinkage void __init xen_start_kernel(void)
pv_apic_ops = xen_apic_ops;
pv_mmu_ops = xen_mmu_ops;
- xen_init_irq_ops();
+#ifdef CONFIG_X86_64
+ /*
+ * Setup percpu state. We only need to do this for 64-bit
+ * because 32-bit already has %fs set properly.
+ */
+ load_percpu_segment(0);
+#endif
+ xen_init_irq_ops();
xen_init_cpuid_mask();
#ifdef CONFIG_X86_LOCAL_APIC
@@ -997,6 +1001,8 @@ asmlinkage void __init xen_start_kernel(void)
set_xen_basic_apic_ops();
#endif
+ xen_setup_features();
+
if (xen_feature(XENFEAT_mmu_pt_update_preserve_ad)) {
pv_mmu_ops.ptep_modify_prot_start = xen_ptep_modify_prot_start;
pv_mmu_ops.ptep_modify_prot_commit = xen_ptep_modify_prot_commit;
@@ -1004,13 +1010,6 @@ asmlinkage void __init xen_start_kernel(void)
machine_ops = xen_machine_ops;
-#ifdef CONFIG_X86_64
- /*
- * Setup percpu state. We only need to do this for 64-bit
- * because 32-bit already has %fs set properly.
- */
- load_percpu_segment(0);
-#endif
/*
* The only reliable way to retain the initial address of the
* percpu gdt_page is to remember it here, so we can go and
@@ -1061,6 +1060,7 @@ asmlinkage void __init xen_start_kernel(void)
/* set up basic CPUID stuff */
cpu_detect(&new_cpu_data);
new_cpu_data.hard_math = 1;
+ new_cpu_data.wp_works_ok = 1;
new_cpu_data.x86_capability[0] = cpuid_edx(1);
#endif
diff --git a/block/Kconfig b/block/Kconfig
index 95a86adc33a..9be0b56eaee 100644
--- a/block/Kconfig
+++ b/block/Kconfig
@@ -48,9 +48,9 @@ config LBDAF
If unsure, say Y.
config BLK_DEV_BSG
- bool "Block layer SG support v4 (EXPERIMENTAL)"
- depends on EXPERIMENTAL
- ---help---
+ bool "Block layer SG support v4"
+ default y
+ help
Saying Y here will enable generic SG (SCSI generic) v4 support
for any block device.
@@ -60,7 +60,10 @@ config BLK_DEV_BSG
protocols (e.g. Task Management Functions and SMP in Serial
Attached SCSI).
- If unsure, say N.
+ This option is required by recent UDEV versions to properly
+ access device serial numbers, etc.
+
+ If unsure, say Y.
config BLK_DEV_INTEGRITY
bool "Block layer data integrity support"
diff --git a/block/blk-settings.c b/block/blk-settings.c
index 8a3ea3bba10..476d8706507 100644
--- a/block/blk-settings.c
+++ b/block/blk-settings.c
@@ -7,6 +7,7 @@
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/bootmem.h> /* for max_pfn/max_low_pfn */
+#include <linux/gcd.h>
#include "blk.h"
@@ -384,8 +385,8 @@ void blk_queue_alignment_offset(struct request_queue *q, unsigned int offset)
EXPORT_SYMBOL(blk_queue_alignment_offset);
/**
- * blk_queue_io_min - set minimum request size for the queue
- * @q: the request queue for the device
+ * blk_limits_io_min - set minimum request size for a device
+ * @limits: the queue limits
* @min: smallest I/O size in bytes
*
* Description:
@@ -394,15 +395,35 @@ EXPORT_SYMBOL(blk_queue_alignment_offset);
* smallest I/O the device can perform without incurring a performance
* penalty.
*/
-void blk_queue_io_min(struct request_queue *q, unsigned int min)
+void blk_limits_io_min(struct queue_limits *limits, unsigned int min)
{
- q->limits.io_min = min;
+ limits->io_min = min;
- if (q->limits.io_min < q->limits.logical_block_size)
- q->limits.io_min = q->limits.logical_block_size;
+ if (limits->io_min < limits->logical_block_size)
+ limits->io_min = limits->logical_block_size;
- if (q->limits.io_min < q->limits.physical_block_size)
- q->limits.io_min = q->limits.physical_block_size;
+ if (limits->io_min < limits->physical_block_size)
+ limits->io_min = limits->physical_block_size;
+}
+EXPORT_SYMBOL(blk_limits_io_min);
+
+/**
+ * blk_queue_io_min - set minimum request size for the queue
+ * @q: the request queue for the device
+ * @min: smallest I/O size in bytes
+ *
+ * Description:
+ * Storage devices may report a granularity or preferred minimum I/O
+ * size which is the smallest request the device can perform without
+ * incurring a performance penalty. For disk drives this is often the
+ * physical block size. For RAID arrays it is often the stripe chunk
+ * size. A properly aligned multiple of minimum_io_size is the
+ * preferred request size for workloads where a high number of I/O
+ * operations is desired.
+ */
+void blk_queue_io_min(struct request_queue *q, unsigned int min)
+{
+ blk_limits_io_min(&q->limits, min);
}
EXPORT_SYMBOL(blk_queue_io_min);
@@ -412,8 +433,12 @@ EXPORT_SYMBOL(blk_queue_io_min);
* @opt: optimal request size in bytes
*
* Description:
- * Drivers can call this function to set the preferred I/O request
- * size for devices that report such a value.
+ * Storage devices may report an optimal I/O size, which is the
+ * device's preferred unit for sustained I/O. This is rarely reported
+ * for disk drives. For RAID arrays it is usually the stripe width or
+ * the internal track size. A properly aligned multiple of
+ * optimal_io_size is the preferred request size for workloads where
+ * sustained throughput is desired.
*/
void blk_queue_io_opt(struct request_queue *q, unsigned int opt)
{
@@ -433,27 +458,7 @@ EXPORT_SYMBOL(blk_queue_io_opt);
**/
void blk_queue_stack_limits(struct request_queue *t, struct request_queue *b)
{
- /* zero is "infinity" */
- t->limits.max_sectors = min_not_zero(queue_max_sectors(t),
- queue_max_sectors(b));
-
- t->limits.max_hw_sectors = min_not_zero(queue_max_hw_sectors(t),
- queue_max_hw_sectors(b));
-
- t->limits.seg_boundary_mask = min_not_zero(queue_segment_boundary(t),
- queue_segment_boundary(b));
-
- t->limits.max_phys_segments = min_not_zero(queue_max_phys_segments(t),
- queue_max_phys_segments(b));
-
- t->limits.max_hw_segments = min_not_zero(queue_max_hw_segments(t),
- queue_max_hw_segments(b));
-
- t->limits.max_segment_size = min_not_zero(queue_max_segment_size(t),
- queue_max_segment_size(b));
-
- t->limits.logical_block_size = max(queue_logical_block_size(t),
- queue_logical_block_size(b));
+ blk_stack_limits(&t->limits, &b->limits, 0);
if (!t->queue_lock)
WARN_ON_ONCE(1);
@@ -523,6 +528,16 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
return -1;
}
+ /* Find lcm() of optimal I/O size */
+ if (t->io_opt && b->io_opt)
+ t->io_opt = (t->io_opt * b->io_opt) / gcd(t->io_opt, b->io_opt);
+ else if (b->io_opt)
+ t->io_opt = b->io_opt;
+
+ /* Verify that optimal I/O size is a multiple of io_min */
+ if (t->io_min && t->io_opt % t->io_min)
+ return -1;
+
return 0;
}
EXPORT_SYMBOL(blk_stack_limits);
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index 418d6361968..d3aa2aadb3e 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -133,7 +133,7 @@ queue_max_sectors_store(struct request_queue *q, const char *page, size_t count)
return -EINVAL;
spin_lock_irq(q->queue_lock);
- blk_queue_max_sectors(q, max_sectors_kb << 1);
+ q->limits.max_sectors = max_sectors_kb << 1;
spin_unlock_irq(q->queue_lock);
return ret;
diff --git a/crypto/algapi.c b/crypto/algapi.c
index 56c62e2858d..df0863d5699 100644
--- a/crypto/algapi.c
+++ b/crypto/algapi.c
@@ -692,7 +692,7 @@ out:
}
EXPORT_SYMBOL_GPL(crypto_enqueue_request);
-struct crypto_async_request *crypto_dequeue_request(struct crypto_queue *queue)
+void *__crypto_dequeue_request(struct crypto_queue *queue, unsigned int offset)
{
struct list_head *request;
@@ -707,7 +707,14 @@ struct crypto_async_request *crypto_dequeue_request(struct crypto_queue *queue)
request = queue->list.next;
list_del(request);
- return list_entry(request, struct crypto_async_request, list);
+ return (char *)list_entry(request, struct crypto_async_request, list) -
+ offset;
+}
+EXPORT_SYMBOL_GPL(__crypto_dequeue_request);
+
+struct crypto_async_request *crypto_dequeue_request(struct crypto_queue *queue)
+{
+ return __crypto_dequeue_request(queue, 0);
}
EXPORT_SYMBOL_GPL(crypto_dequeue_request);
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
index 7a0f4aa4fa1..9a62224cc27 100644
--- a/drivers/acpi/acpi_memhotplug.c
+++ b/drivers/acpi/acpi_memhotplug.c
@@ -38,6 +38,9 @@
#define _COMPONENT ACPI_MEMORY_DEVICE_COMPONENT
+#undef PREFIX
+#define PREFIX "ACPI:memory_hp:"
+
ACPI_MODULE_NAME("acpi_memhotplug");
MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>");
MODULE_DESCRIPTION("Hotplug Mem Driver");
@@ -153,6 +156,7 @@ acpi_memory_get_device(acpi_handle handle,
acpi_handle phandle;
struct acpi_device *device = NULL;
struct acpi_device *pdevice = NULL;
+ int result;
if (!acpi_bus_get_device(handle, &device) && device)
@@ -165,9 +169,9 @@ acpi_memory_get_device(acpi_handle handle,
}
/* Get the parent device */
- status = acpi_bus_get_device(phandle, &pdevice);
- if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status, "Cannot get acpi bus device"));
+ result = acpi_bus_get_device(phandle, &pdevice);
+ if (result) {
+ printk(KERN_WARNING PREFIX "Cannot get acpi bus device");
return -EINVAL;
}
@@ -175,9 +179,9 @@ acpi_memory_get_device(acpi_handle handle,
* Now add the notified device. This creates the acpi_device
* and invokes .add function
*/
- status = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE);
- if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status, "Cannot add acpi bus"));
+ result = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE);
+ if (result) {
+ printk(KERN_WARNING PREFIX "Cannot add acpi bus");
return -EINVAL;
}
@@ -238,7 +242,12 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
num_enabled++;
continue;
}
-
+ /*
+ * If the memory block size is zero, please ignore it.
+ * Don't try to do the following memory hotplug flowchart.
+ */
+ if (!info->length)
+ continue;
if (node < 0)
node = memory_add_physaddr_to_nid(info->start_addr);
@@ -253,8 +262,15 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
mem_device->state = MEMORY_INVALID_STATE;
return -EINVAL;
}
-
- return result;
+ /*
+ * Sometimes the memory device will contain several memory blocks.
+ * When one memory block is hot-added to the system memory, it will
+ * be regarded as a success.
+ * Otherwise if the last memory block can't be hot-added to the system
+ * memory, it will be failure and the memory device can't be bound with
+ * driver.
+ */
+ return 0;
}
static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device)
diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
index 544dcf83492..eb6f038b03d 100644
--- a/drivers/acpi/acpica/acobject.h
+++ b/drivers/acpi/acpica/acobject.h
@@ -97,6 +97,7 @@
#define AOPOBJ_OBJECT_INITIALIZED 0x08
#define AOPOBJ_SETUP_COMPLETE 0x10
#define AOPOBJ_SINGLE_DATUM 0x20
+#define AOPOBJ_INVALID 0x40 /* Used if host OS won't allow an op_region address */
/******************************************************************************
*
diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c
index 584d766e6f1..b79978f7bc7 100644
--- a/drivers/acpi/acpica/dsopcode.c
+++ b/drivers/acpi/acpica/dsopcode.c
@@ -397,6 +397,30 @@ acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *obj_desc)
status = acpi_ds_execute_arguments(node, acpi_ns_get_parent_node(node),
extra_desc->extra.aml_length,
extra_desc->extra.aml_start);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Validate the region address/length via the host OS */
+
+ status = acpi_os_validate_address(obj_desc->region.space_id,
+ obj_desc->region.address,
+ (acpi_size) obj_desc->region.length,
+ acpi_ut_get_node_name(node));
+
+ if (ACPI_FAILURE(status)) {
+ /*
+ * Invalid address/length. We will emit an error message and mark
+ * the region as invalid, so that it will cause an additional error if
+ * it is ever used. Then return AE_OK.
+ */
+ ACPI_EXCEPTION((AE_INFO, status,
+ "During address validation of OpRegion [%4.4s]",
+ node->name.ascii));
+ obj_desc->common.flags |= AOPOBJ_INVALID;
+ status = AE_OK;
+ }
+
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c
index d4075b82102..6687be167f5 100644
--- a/drivers/acpi/acpica/exfldio.c
+++ b/drivers/acpi/acpica/exfldio.c
@@ -113,6 +113,12 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc,
}
}
+ /* Exit if Address/Length have been disallowed by the host OS */
+
+ if (rgn_desc->common.flags & AOPOBJ_INVALID) {
+ return_ACPI_STATUS(AE_AML_ILLEGAL_ADDRESS);
+ }
+
/*
* Exit now for SMBus address space, it has a non-linear address space
* and the request cannot be directly validated
diff --git a/drivers/acpi/acpica/exstorob.c b/drivers/acpi/acpica/exstorob.c
index 67340cc7014..257706e7734 100644
--- a/drivers/acpi/acpica/exstorob.c
+++ b/drivers/acpi/acpica/exstorob.c
@@ -70,6 +70,12 @@ acpi_ex_store_buffer_to_buffer(union acpi_operand_object *source_desc,
ACPI_FUNCTION_TRACE_PTR(ex_store_buffer_to_buffer, source_desc);
+ /* If Source and Target are the same, just return */
+
+ if (source_desc == target_desc) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
/* We know that source_desc is a buffer by now */
buffer = ACPI_CAST_PTR(u8, source_desc->buffer.pointer);
@@ -161,6 +167,12 @@ acpi_ex_store_string_to_string(union acpi_operand_object *source_desc,
ACPI_FUNCTION_TRACE_PTR(ex_store_string_to_string, source_desc);
+ /* If Source and Target are the same, just return */
+
+ if (source_desc == target_desc) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
/* We know that source_desc is a string by now */
buffer = ACPI_CAST_PTR(u8, source_desc->string.pointer);
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 71670719d61..5691f165a95 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -189,11 +189,36 @@ acpi_status __init acpi_os_initialize(void)
return AE_OK;
}
+static void bind_to_cpu0(struct work_struct *work)
+{
+ set_cpus_allowed(current, cpumask_of_cpu(0));
+ kfree(work);
+}
+
+static void bind_workqueue(struct workqueue_struct *wq)
+{
+ struct work_struct *work;
+
+ work = kzalloc(sizeof(struct work_struct), GFP_KERNEL);
+ INIT_WORK(work, bind_to_cpu0);
+ queue_work(wq, work);
+}
+
acpi_status acpi_os_initialize1(void)
{
+ /*
+ * On some machines, a software-initiated SMI causes corruption unless
+ * the SMI runs on CPU 0. An SMI can be initiated by any AML, but
+ * typically it's done in GPE-related methods that are run via
+ * workqueues, so we can avoid the known corruption cases by binding
+ * the workqueues to CPU 0.
+ */
kacpid_wq = create_singlethread_workqueue("kacpid");
+ bind_workqueue(kacpid_wq);
kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify");
+ bind_workqueue(kacpi_notify_wq);
kacpi_hotplug_wq = create_singlethread_workqueue("kacpi_hotplug");
+ bind_workqueue(kacpi_hotplug_wq);
BUG_ON(!kacpid_wq);
BUG_ON(!kacpi_notify_wq);
BUG_ON(!kacpi_hotplug_wq);
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index 84e0f3c0744..2cc4b303387 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -1151,6 +1151,9 @@ static int __init acpi_processor_init(void)
{
int result = 0;
+ if (acpi_disabled)
+ return 0;
+
memset(&errata, 0, sizeof(errata));
#ifdef CONFIG_SMP
@@ -1197,6 +1200,9 @@ out_proc:
static void __exit acpi_processor_exit(void)
{
+ if (acpi_disabled)
+ return;
+
acpi_processor_ppc_exit();
acpi_thermal_cpufreq_exit();
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 0efa59e7e3a..66393d5c4c7 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -162,8 +162,9 @@ static void lapic_timer_check_state(int state, struct acpi_processor *pr,
pr->power.timer_broadcast_on_state = state;
}
-static void lapic_timer_propagate_broadcast(struct acpi_processor *pr)
+static void lapic_timer_propagate_broadcast(void *arg)
{
+ struct acpi_processor *pr = (struct acpi_processor *) arg;
unsigned long reason;
reason = pr->power.timer_broadcast_on_state < INT_MAX ?
@@ -635,7 +636,8 @@ static int acpi_processor_power_verify(struct acpi_processor *pr)
working++;
}
- lapic_timer_propagate_broadcast(pr);
+ smp_call_function_single(pr->id, lapic_timer_propagate_broadcast,
+ pr, 1);
return (working);
}
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c
index 39838c66603..31adda1099e 100644
--- a/drivers/acpi/processor_thermal.c
+++ b/drivers/acpi/processor_thermal.c
@@ -66,7 +66,7 @@ static int acpi_processor_apply_limit(struct acpi_processor *pr)
if (pr->limit.thermal.tx > tx)
tx = pr->limit.thermal.tx;
- result = acpi_processor_set_throttling(pr, tx);
+ result = acpi_processor_set_throttling(pr, tx, false);
if (result)
goto end;
}
@@ -421,12 +421,12 @@ processor_set_cur_state(struct thermal_cooling_device *cdev,
if (state <= max_pstate) {
if (pr->flags.throttling && pr->throttling.state)
- result = acpi_processor_set_throttling(pr, 0);
+ result = acpi_processor_set_throttling(pr, 0, false);
cpufreq_set_cur_state(pr->id, state);
} else {
cpufreq_set_cur_state(pr->id, max_pstate);
result = acpi_processor_set_throttling(pr,
- state - max_pstate);
+ state - max_pstate, false);
}
return result;
}
diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c
index 227543789ba..ae39797aab5 100644
--- a/drivers/acpi/processor_throttling.c
+++ b/drivers/acpi/processor_throttling.c
@@ -62,7 +62,8 @@ struct throttling_tstate {
#define THROTTLING_POSTCHANGE (2)
static int acpi_processor_get_throttling(struct acpi_processor *pr);
-int acpi_processor_set_throttling(struct acpi_processor *pr, int state);
+int acpi_processor_set_throttling(struct acpi_processor *pr,
+ int state, bool force);
static int acpi_processor_update_tsd_coord(void)
{
@@ -361,7 +362,7 @@ int acpi_processor_tstate_has_changed(struct acpi_processor *pr)
*/
target_state = throttling_limit;
}
- return acpi_processor_set_throttling(pr, target_state);
+ return acpi_processor_set_throttling(pr, target_state, false);
}
/*
@@ -839,10 +840,10 @@ static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr)
if (ret >= 0) {
state = acpi_get_throttling_state(pr, value);
if (state == -1) {
- ACPI_WARNING((AE_INFO,
- "Invalid throttling state, reset"));
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Invalid throttling state, reset\n"));
state = 0;
- ret = acpi_processor_set_throttling(pr, state);
+ ret = acpi_processor_set_throttling(pr, state, true);
if (ret)
return ret;
}
@@ -915,7 +916,7 @@ static int acpi_processor_get_fadt_info(struct acpi_processor *pr)
}
static int acpi_processor_set_throttling_fadt(struct acpi_processor *pr,
- int state)
+ int state, bool force)
{
u32 value = 0;
u32 duty_mask = 0;
@@ -930,7 +931,7 @@ static int acpi_processor_set_throttling_fadt(struct acpi_processor *pr,
if (!pr->flags.throttling)
return -ENODEV;
- if (state == pr->throttling.state)
+ if (!force && (state == pr->throttling.state))
return 0;
if (state < pr->throttling_platform_limit)
@@ -988,7 +989,7 @@ static int acpi_processor_set_throttling_fadt(struct acpi_processor *pr,
}
static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr,
- int state)
+ int state, bool force)
{
int ret;
acpi_integer value;
@@ -1002,7 +1003,7 @@ static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr,
if (!pr->flags.throttling)
return -ENODEV;
- if (state == pr->throttling.state)
+ if (!force && (state == pr->throttling.state))
return 0;
if (state < pr->throttling_platform_limit)
@@ -1018,7 +1019,8 @@ static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr,
return 0;
}
-int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
+int acpi_processor_set_throttling(struct acpi_processor *pr,
+ int state, bool force)
{
cpumask_var_t saved_mask;
int ret = 0;
@@ -1070,7 +1072,7 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
/* FIXME: use work_on_cpu() */
set_cpus_allowed_ptr(current, cpumask_of(pr->id));
ret = p_throttling->acpi_processor_set_throttling(pr,
- t_state.target_state);
+ t_state.target_state, force);
} else {
/*
* When the T-state coordination is SW_ALL or HW_ALL,
@@ -1103,7 +1105,7 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
set_cpus_allowed_ptr(current, cpumask_of(i));
ret = match_pr->throttling.
acpi_processor_set_throttling(
- match_pr, t_state.target_state);
+ match_pr, t_state.target_state, force);
}
}
/*
@@ -1201,7 +1203,7 @@ int acpi_processor_get_throttling_info(struct acpi_processor *pr)
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Disabling throttling (was T%d)\n",
pr->throttling.state));
- result = acpi_processor_set_throttling(pr, 0);
+ result = acpi_processor_set_throttling(pr, 0, false);
if (result)
goto end;
}
@@ -1307,7 +1309,7 @@ static ssize_t acpi_processor_write_throttling(struct file *file,
if (strcmp(tmpbuf, charp) != 0)
return -EINVAL;
- result = acpi_processor_set_throttling(pr, state_val);
+ result = acpi_processor_set_throttling(pr, state_val, false);
if (result)
return result;
diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c
index 0944daec064..9c61ab2177c 100644
--- a/drivers/acpi/system.c
+++ b/drivers/acpi/system.c
@@ -121,7 +121,7 @@ static void acpi_table_attr_init(struct acpi_table_attr *table_attr,
table_attr->attr.size = 0;
table_attr->attr.read = acpi_table_show;
table_attr->attr.attr.name = table_attr->name;
- table_attr->attr.attr.mode = 0444;
+ table_attr->attr.attr.mode = 0400;
return;
}
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index 8851315ce85..60ea984c84a 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -2004,8 +2004,11 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
status = acpi_remove_notify_handler(device->dev->handle,
ACPI_DEVICE_NOTIFY,
acpi_video_device_notify);
- sysfs_remove_link(&device->backlight->dev.kobj, "device");
- backlight_device_unregister(device->backlight);
+ if (device->backlight) {
+ sysfs_remove_link(&device->backlight->dev.kobj, "device");
+ backlight_device_unregister(device->backlight);
+ device->backlight = NULL;
+ }
if (device->cdev) {
sysfs_remove_link(&device->dev->dev.kobj,
"thermal_cooling");
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 958c1fa4190..fe3eba5d6b3 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -219,6 +219,8 @@ enum {
AHCI_HFLAG_SECT255 = (1 << 8), /* max 255 sectors */
AHCI_HFLAG_YES_NCQ = (1 << 9), /* force NCQ cap on */
AHCI_HFLAG_NO_SUSPEND = (1 << 10), /* don't suspend */
+ AHCI_HFLAG_SRST_TOUT_IS_OFFLINE = (1 << 11), /* treat SRST timeout as
+ link offline */
/* ap->flags bits */
@@ -1663,6 +1665,7 @@ static int ahci_do_softreset(struct ata_link *link, unsigned int *class,
int (*check_ready)(struct ata_link *link))
{
struct ata_port *ap = link->ap;
+ struct ahci_host_priv *hpriv = ap->host->private_data;
const char *reason = NULL;
unsigned long now, msecs;
struct ata_taskfile tf;
@@ -1701,12 +1704,21 @@ static int ahci_do_softreset(struct ata_link *link, unsigned int *class,
/* wait for link to become ready */
rc = ata_wait_after_reset(link, deadline, check_ready);
- /* link occupied, -ENODEV too is an error */
- if (rc) {
+ if (rc == -EBUSY && hpriv->flags & AHCI_HFLAG_SRST_TOUT_IS_OFFLINE) {
+ /*
+ * Workaround for cases where link online status can't
+ * be trusted. Treat device readiness timeout as link
+ * offline.
+ */
+ ata_link_printk(link, KERN_INFO,
+ "device not ready, treating as offline\n");
+ *class = ATA_DEV_NONE;
+ } else if (rc) {
+ /* link occupied, -ENODEV too is an error */
reason = "device not ready";
goto fail;
- }
- *class = ahci_dev_classify(ap);
+ } else
+ *class = ahci_dev_classify(ap);
DPRINTK("EXIT, class=%u\n", *class);
return 0;
@@ -1773,7 +1785,8 @@ static int ahci_sb600_softreset(struct ata_link *link, unsigned int *class,
irq_sts = readl(port_mmio + PORT_IRQ_STAT);
if (irq_sts & PORT_IRQ_BAD_PMP) {
ata_link_printk(link, KERN_WARNING,
- "failed due to HW bug, retry pmp=0\n");
+ "applying SB600 PMP SRST workaround "
+ "and retrying\n");
rc = ahci_do_softreset(link, class, 0, deadline,
ahci_check_ready);
}
@@ -2726,6 +2739,56 @@ static bool ahci_broken_suspend(struct pci_dev *pdev)
return !ver || strcmp(ver, dmi->driver_data) < 0;
}
+static bool ahci_broken_online(struct pci_dev *pdev)
+{
+#define ENCODE_BUSDEVFN(bus, slot, func) \
+ (void *)(unsigned long)(((bus) << 8) | PCI_DEVFN((slot), (func)))
+ static const struct dmi_system_id sysids[] = {
+ /*
+ * There are several gigabyte boards which use
+ * SIMG5723s configured as hardware RAID. Certain
+ * 5723 firmware revisions shipped there keep the link
+ * online but fail to answer properly to SRST or
+ * IDENTIFY when no device is attached downstream
+ * causing libata to retry quite a few times leading
+ * to excessive detection delay.
+ *
+ * As these firmwares respond to the second reset try
+ * with invalid device signature, considering unknown
+ * sig as offline works around the problem acceptably.
+ */
+ {
+ .ident = "EP45-DQ6",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR,
+ "Gigabyte Technology Co., Ltd."),
+ DMI_MATCH(DMI_BOARD_NAME, "EP45-DQ6"),
+ },
+ .driver_data = ENCODE_BUSDEVFN(0x0a, 0x00, 0),
+ },
+ {
+ .ident = "EP45-DS5",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR,
+ "Gigabyte Technology Co., Ltd."),
+ DMI_MATCH(DMI_BOARD_NAME, "EP45-DS5"),
+ },
+ .driver_data = ENCODE_BUSDEVFN(0x03, 0x00, 0),
+ },
+ { } /* terminate list */
+ };
+#undef ENCODE_BUSDEVFN
+ const struct dmi_system_id *dmi = dmi_first_match(sysids);
+ unsigned int val;
+
+ if (!dmi)
+ return false;
+
+ val = (unsigned long)dmi->driver_data;
+
+ return pdev->bus->number == (val >> 8) && pdev->devfn == (val & 0xff);
+}
+
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
@@ -2841,6 +2904,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
"BIOS update required for suspend/resume\n");
}
+ if (ahci_broken_online(pdev)) {
+ hpriv->flags |= AHCI_HFLAG_SRST_TOUT_IS_OFFLINE;
+ dev_info(&pdev->dev,
+ "online status unreliable, applying workaround\n");
+ }
+
/* CAP.NP sometimes indicate the index of the last enabled
* port, at other times, that of the last possible port, so
* determining the maximum port number requires looking at
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index 56b8a3ff128..9ac4e378992 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -664,6 +664,8 @@ static int piix_pata_prereset(struct ata_link *link, unsigned long deadline)
return ata_sff_prereset(link, deadline);
}
+static DEFINE_SPINLOCK(piix_lock);
+
/**
* piix_set_piomode - Initialize host controller PATA PIO timings
* @ap: Port whose timings we are configuring
@@ -677,8 +679,9 @@ static int piix_pata_prereset(struct ata_link *link, unsigned long deadline)
static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
{
- unsigned int pio = adev->pio_mode - XFER_PIO_0;
struct pci_dev *dev = to_pci_dev(ap->host->dev);
+ unsigned long flags;
+ unsigned int pio = adev->pio_mode - XFER_PIO_0;
unsigned int is_slave = (adev->devno != 0);
unsigned int master_port= ap->port_no ? 0x42 : 0x40;
unsigned int slave_port = 0x44;
@@ -708,6 +711,8 @@ static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
if (adev->class == ATA_DEV_ATA)
control |= 4; /* PPE enable */
+ spin_lock_irqsave(&piix_lock, flags);
+
/* PIO configuration clears DTE unconditionally. It will be
* programmed in set_dmamode which is guaranteed to be called
* after set_piomode if any DMA mode is available.
@@ -747,6 +752,8 @@ static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
udma_enable &= ~(1 << (2 * ap->port_no + adev->devno));
pci_write_config_byte(dev, 0x48, udma_enable);
}
+
+ spin_unlock_irqrestore(&piix_lock, flags);
}
/**
@@ -764,6 +771,7 @@ static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)
static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, int isich)
{
struct pci_dev *dev = to_pci_dev(ap->host->dev);
+ unsigned long flags;
u8 master_port = ap->port_no ? 0x42 : 0x40;
u16 master_data;
u8 speed = adev->dma_mode;
@@ -777,6 +785,8 @@ static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, in
{ 2, 1 },
{ 2, 3 }, };
+ spin_lock_irqsave(&piix_lock, flags);
+
pci_read_config_word(dev, master_port, &master_data);
if (ap->udma_mask)
pci_read_config_byte(dev, 0x48, &udma_enable);
@@ -867,6 +877,8 @@ static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, in
/* Don't scribble on 0x48 if the controller does not support UDMA */
if (ap->udma_mask)
pci_write_config_byte(dev, 0x48, udma_enable);
+
+ spin_unlock_irqrestore(&piix_lock, flags);
}
/**
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 8ac98ff16d7..072ba5ea138 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4302,6 +4302,9 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
{ "WDC WD2500JD-00HBB0", "WD-WMAL71490727", ATA_HORKAGE_BROKEN_HPA },
{ "MAXTOR 6L080L4", "A93.0500", ATA_HORKAGE_BROKEN_HPA },
+ /* this one allows HPA unlocking but fails IOs on the area */
+ { "OCZ-VERTEX", "1.30", ATA_HORKAGE_BROKEN_HPA },
+
/* Devices which report 1 sector over size HPA */
{ "ST340823A", NULL, ATA_HORKAGE_HPA_SIZE, },
{ "ST320413A", NULL, ATA_HORKAGE_HPA_SIZE, },
diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c
index 5702affcb32..41c94b1ae49 100644
--- a/drivers/ata/pata_at91.c
+++ b/drivers/ata/pata_at91.c
@@ -250,7 +250,7 @@ static int __devinit pata_at91_probe(struct platform_device *pdev)
ata_port_desc(ap, "no IRQ, using PIO polling");
}
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
if (!info) {
dev_err(dev, "failed to allocate memory for private data\n");
@@ -275,7 +275,7 @@ static int __devinit pata_at91_probe(struct platform_device *pdev)
if (!info->ide_addr) {
dev_err(dev, "failed to map IO base\n");
ret = -ENOMEM;
- goto err_ide_ioremap;
+ goto err_put;
}
info->alt_addr = devm_ioremap(dev,
@@ -284,7 +284,7 @@ static int __devinit pata_at91_probe(struct platform_device *pdev)
if (!info->alt_addr) {
dev_err(dev, "failed to map CTL base\n");
ret = -ENOMEM;
- goto err_alt_ioremap;
+ goto err_put;
}
ap->ioaddr.cmd_addr = info->ide_addr;
@@ -303,13 +303,8 @@ static int __devinit pata_at91_probe(struct platform_device *pdev)
irq ? ata_sff_interrupt : NULL,
irq_flags, &pata_at91_sht);
-err_alt_ioremap:
- devm_iounmap(dev, info->ide_addr);
-
-err_ide_ioremap:
+err_put:
clk_put(info->mck);
- kfree(info);
-
return ret;
}
@@ -317,7 +312,6 @@ static int __devexit pata_at91_remove(struct platform_device *pdev)
{
struct ata_host *host = dev_get_drvdata(&pdev->dev);
struct at91_ide_info *info;
- struct device *dev = &pdev->dev;
if (!host)
return 0;
@@ -328,11 +322,8 @@ static int __devexit pata_at91_remove(struct platform_device *pdev)
if (!info)
return 0;
- devm_iounmap(dev, info->ide_addr);
- devm_iounmap(dev, info->alt_addr);
clk_put(info->mck);
- kfree(info);
return 0;
}
diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c
index bec0b8ade66..45915566e4e 100644
--- a/drivers/ata/pata_atiixp.c
+++ b/drivers/ata/pata_atiixp.c
@@ -1,6 +1,7 @@
/*
* pata_atiixp.c - ATI PATA for new ATA layer
* (C) 2005 Red Hat Inc
+ * (C) 2009 Bartlomiej Zolnierkiewicz
*
* Based on
*
@@ -61,20 +62,19 @@ static void atiixp_set_pio_timing(struct ata_port *ap, struct ata_device *adev,
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
int dn = 2 * ap->port_no + adev->devno;
-
- /* Check this is correct - the order is odd in both drivers */
int timing_shift = (16 * ap->port_no) + 8 * (adev->devno ^ 1);
- u16 pio_mode_data, pio_timing_data;
+ u32 pio_timing_data;
+ u16 pio_mode_data;
pci_read_config_word(pdev, ATIIXP_IDE_PIO_MODE, &pio_mode_data);
pio_mode_data &= ~(0x7 << (4 * dn));
pio_mode_data |= pio << (4 * dn);
pci_write_config_word(pdev, ATIIXP_IDE_PIO_MODE, pio_mode_data);
- pci_read_config_word(pdev, ATIIXP_IDE_PIO_TIMING, &pio_timing_data);
+ pci_read_config_dword(pdev, ATIIXP_IDE_PIO_TIMING, &pio_timing_data);
pio_timing_data &= ~(0xFF << timing_shift);
pio_timing_data |= (pio_timings[pio] << timing_shift);
- pci_write_config_word(pdev, ATIIXP_IDE_PIO_TIMING, pio_timing_data);
+ pci_write_config_dword(pdev, ATIIXP_IDE_PIO_TIMING, pio_timing_data);
}
/**
@@ -119,16 +119,17 @@ static void atiixp_set_dmamode(struct ata_port *ap, struct ata_device *adev)
udma_mode_data |= dma << (4 * dn);
pci_write_config_word(pdev, ATIIXP_IDE_UDMA_MODE, udma_mode_data);
} else {
- u16 mwdma_timing_data;
- /* Check this is correct - the order is odd in both drivers */
int timing_shift = (16 * ap->port_no) + 8 * (adev->devno ^ 1);
+ u32 mwdma_timing_data;
dma -= XFER_MW_DMA_0;
- pci_read_config_word(pdev, ATIIXP_IDE_MWDMA_TIMING, &mwdma_timing_data);
+ pci_read_config_dword(pdev, ATIIXP_IDE_MWDMA_TIMING,
+ &mwdma_timing_data);
mwdma_timing_data &= ~(0xFF << timing_shift);
mwdma_timing_data |= (mwdma_timings[dma] << timing_shift);
- pci_write_config_word(pdev, ATIIXP_IDE_MWDMA_TIMING, mwdma_timing_data);
+ pci_write_config_dword(pdev, ATIIXP_IDE_MWDMA_TIMING,
+ mwdma_timing_data);
}
/*
* We must now look at the PIO mode situation. We may need to
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index b2d11f300c3..86a40582999 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -602,6 +602,7 @@ MODULE_VERSION(DRV_VERSION);
static int adma_enabled;
static int swncq_enabled = 1;
+static int msi_enabled;
static void nv_adma_register_mode(struct ata_port *ap)
{
@@ -2459,6 +2460,11 @@ static int nv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
} else if (type == SWNCQ)
nv_swncq_host_init(host);
+ if (msi_enabled) {
+ dev_printk(KERN_NOTICE, &pdev->dev, "Using MSI\n");
+ pci_enable_msi(pdev);
+ }
+
pci_set_master(pdev);
return ata_host_activate(host, pdev->irq, ipriv->irq_handler,
IRQF_SHARED, ipriv->sht);
@@ -2558,4 +2564,6 @@ module_param_named(adma, adma_enabled, bool, 0444);
MODULE_PARM_DESC(adma, "Enable use of ADMA (Default: false)");
module_param_named(swncq, swncq_enabled, bool, 0444);
MODULE_PARM_DESC(swncq, "Enable use of SWNCQ (Default: true)");
+module_param_named(msi, msi_enabled, bool, 0444);
+MODULE_PARM_DESC(msi, "Enable use of MSI (Default: false)");
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 81cb01bfc35..456594bd97b 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -483,9 +483,6 @@ int platform_driver_register(struct platform_driver *drv)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
- if (drv->suspend || drv->resume)
- pr_warning("Platform driver '%s' needs updating - please use "
- "dev_pm_ops\n", drv->driver.name);
return driver_register(&drv->driver);
}
diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h
index 5e41e6dd657..db195abad69 100644
--- a/drivers/block/aoe/aoe.h
+++ b/drivers/block/aoe/aoe.h
@@ -155,7 +155,7 @@ struct aoedev {
u16 fw_ver; /* version of blade's firmware */
struct work_struct work;/* disk create work struct */
struct gendisk *gd;
- struct request_queue blkq;
+ struct request_queue *blkq;
struct hd_geometry geo;
sector_t ssize;
struct timer_list timer;
diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c
index 2307a271bdc..1e15889c4b9 100644
--- a/drivers/block/aoe/aoeblk.c
+++ b/drivers/block/aoe/aoeblk.c
@@ -264,9 +264,12 @@ aoeblk_gdalloc(void *vp)
goto err_disk;
}
- blk_queue_make_request(&d->blkq, aoeblk_make_request);
- if (bdi_init(&d->blkq.backing_dev_info))
+ d->blkq = blk_alloc_queue(GFP_KERNEL);
+ if (!d->blkq)
goto err_mempool;
+ blk_queue_make_request(d->blkq, aoeblk_make_request);
+ if (bdi_init(&d->blkq->backing_dev_info))
+ goto err_blkq;
spin_lock_irqsave(&d->lock, flags);
gd->major = AOE_MAJOR;
gd->first_minor = d->sysminor * AOE_PARTITIONS;
@@ -276,7 +279,7 @@ aoeblk_gdalloc(void *vp)
snprintf(gd->disk_name, sizeof gd->disk_name, "etherd/e%ld.%d",
d->aoemajor, d->aoeminor);
- gd->queue = &d->blkq;
+ gd->queue = d->blkq;
d->gd = gd;
d->flags &= ~DEVFL_GDALLOC;
d->flags |= DEVFL_UP;
@@ -287,6 +290,9 @@ aoeblk_gdalloc(void *vp)
aoedisk_add_sysfs(d);
return;
+err_blkq:
+ blk_cleanup_queue(d->blkq);
+ d->blkq = NULL;
err_mempool:
mempool_destroy(d->bufpool);
err_disk:
diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c
index eeea477d960..fa67027789a 100644
--- a/drivers/block/aoe/aoedev.c
+++ b/drivers/block/aoe/aoedev.c
@@ -113,6 +113,7 @@ aoedev_freedev(struct aoedev *d)
if (d->bufpool)
mempool_destroy(d->bufpool);
skbpoolfree(d);
+ blk_cleanup_queue(d->blkq);
kfree(d);
}
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index 8c9d50db5c3..c5855779058 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -49,6 +49,7 @@
#define PCI_DEVICE_ID_INTEL_IGDNG_D_HB 0x0040
#define PCI_DEVICE_ID_INTEL_IGDNG_D_IG 0x0042
#define PCI_DEVICE_ID_INTEL_IGDNG_M_HB 0x0044
+#define PCI_DEVICE_ID_INTEL_IGDNG_MA_HB 0x0062
#define PCI_DEVICE_ID_INTEL_IGDNG_M_IG 0x0046
/* cover 915 and 945 variants */
@@ -81,7 +82,8 @@
agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \
agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB || \
agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_D_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_M_HB)
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_M_HB || \
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_MA_HB)
extern int agp_memory_reserved;
@@ -1216,6 +1218,7 @@ static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size)
case PCI_DEVICE_ID_INTEL_G41_HB:
case PCI_DEVICE_ID_INTEL_IGDNG_D_HB:
case PCI_DEVICE_ID_INTEL_IGDNG_M_HB:
+ case PCI_DEVICE_ID_INTEL_IGDNG_MA_HB:
*gtt_offset = *gtt_size = MB(2);
break;
default:
@@ -2195,6 +2198,8 @@ static const struct intel_driver_description {
"IGDNG/D", NULL, &intel_i965_driver },
{ PCI_DEVICE_ID_INTEL_IGDNG_M_HB, PCI_DEVICE_ID_INTEL_IGDNG_M_IG, 0,
"IGDNG/M", NULL, &intel_i965_driver },
+ { PCI_DEVICE_ID_INTEL_IGDNG_MA_HB, PCI_DEVICE_ID_INTEL_IGDNG_M_IG, 0,
+ "IGDNG/MA", NULL, &intel_i965_driver },
{ 0, 0, 0, NULL, NULL, NULL }
};
@@ -2398,6 +2403,7 @@ static struct pci_device_id agp_intel_pci_table[] = {
ID(PCI_DEVICE_ID_INTEL_G41_HB),
ID(PCI_DEVICE_ID_INTEL_IGDNG_D_HB),
ID(PCI_DEVICE_ID_INTEL_IGDNG_M_HB),
+ ID(PCI_DEVICE_ID_INTEL_IGDNG_MA_HB),
{ }
};
diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c
index f4bb43fb801..e077701ae3d 100644
--- a/drivers/char/agp/parisc-agp.c
+++ b/drivers/char/agp/parisc-agp.c
@@ -225,7 +225,7 @@ static const struct agp_bridge_driver parisc_agp_driver = {
.configure = parisc_agp_configure,
.fetch_size = parisc_agp_fetch_size,
.tlb_flush = parisc_agp_tlbflush,
- .mask_memory = parisc_agp_mask_memory,
+ .mask_memory = parisc_agp_page_mask_memory,
.masks = parisc_agp_masks,
.agp_enable = parisc_agp_enable,
.cache_flush = global_cache_flush,
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index 973be2f4419..4e28b35024e 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -300,8 +300,7 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space)
if (space < 2)
return -1;
tty->canon_column = tty->column = 0;
- tty_put_char(tty, '\r');
- tty_put_char(tty, c);
+ tty->ops->write(tty, "\r\n", 2);
return 2;
}
tty->canon_column = tty->column;
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 6e6942c45f5..b33d6688e91 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -109,21 +109,13 @@ static int pty_space(struct tty_struct *to)
* the other side of the pty/tty pair.
*/
-static int pty_write(struct tty_struct *tty, const unsigned char *buf,
- int count)
+static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c)
{
struct tty_struct *to = tty->link;
- int c;
if (tty->stopped)
return 0;
- /* This isn't locked but our 8K is quite sloppy so no
- big deal */
-
- c = pty_space(to);
- if (c > count)
- c = count;
if (c > 0) {
/* Stuff the data into the input queue of the other end */
c = tty_insert_flip_string(to, buf, c);
@@ -144,6 +136,8 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf,
static int pty_write_room(struct tty_struct *tty)
{
+ if (tty->stopped)
+ return 0;
return pty_space(tty->link);
}
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index acd76b767d4..e48af9f7921 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/tty_ldisc.c
@@ -48,6 +48,41 @@ static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
/* Line disc dispatch table */
static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
+static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld)
+{
+ if (ld)
+ atomic_inc(&ld->users);
+ return ld;
+}
+
+static void put_ldisc(struct tty_ldisc *ld)
+{
+ unsigned long flags;
+
+ if (WARN_ON_ONCE(!ld))
+ return;
+
+ /*
+ * If this is the last user, free the ldisc, and
+ * release the ldisc ops.
+ *
+ * We really want an "atomic_dec_and_lock_irqsave()",
+ * but we don't have it, so this does it by hand.
+ */
+ local_irq_save(flags);
+ if (atomic_dec_and_lock(&ld->users, &tty_ldisc_lock)) {
+ struct tty_ldisc_ops *ldo = ld->ops;
+
+ ldo->refcount--;
+ module_put(ldo->owner);
+ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+
+ kfree(ld);
+ return;
+ }
+ local_irq_restore(flags);
+}
+
/**
* tty_register_ldisc - install a line discipline
* @disc: ldisc number
@@ -142,7 +177,7 @@ static struct tty_ldisc *tty_ldisc_try_get(int disc)
/* lock it */
ldops->refcount++;
ld->ops = ldops;
- ld->refcount = 0;
+ atomic_set(&ld->users, 1);
err = 0;
}
}
@@ -181,35 +216,6 @@ static struct tty_ldisc *tty_ldisc_get(int disc)
return ld;
}
-/**
- * tty_ldisc_put - drop ldisc reference
- * @ld: ldisc
- *
- * Drop a reference to a line discipline. Manage refcounts and
- * module usage counts. Free the ldisc once the recount hits zero.
- *
- * Locking:
- * takes tty_ldisc_lock to guard against ldisc races
- */
-
-static void tty_ldisc_put(struct tty_ldisc *ld)
-{
- unsigned long flags;
- int disc = ld->ops->num;
- struct tty_ldisc_ops *ldo;
-
- BUG_ON(disc < N_TTY || disc >= NR_LDISCS);
-
- spin_lock_irqsave(&tty_ldisc_lock, flags);
- ldo = tty_ldiscs[disc];
- BUG_ON(ldo->refcount == 0);
- ldo->refcount--;
- module_put(ldo->owner);
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- WARN_ON(ld->refcount);
- kfree(ld);
-}
-
static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos)
{
return (*pos < NR_LDISCS) ? pos : NULL;
@@ -234,7 +240,7 @@ static int tty_ldiscs_seq_show(struct seq_file *m, void *v)
if (IS_ERR(ld))
return 0;
seq_printf(m, "%-10s %2d\n", ld->ops->name ? ld->ops->name : "???", i);
- tty_ldisc_put(ld);
+ put_ldisc(ld);
return 0;
}
@@ -288,20 +294,17 @@ static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
* Locking: takes tty_ldisc_lock
*/
-static int tty_ldisc_try(struct tty_struct *tty)
+static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty)
{
unsigned long flags;
struct tty_ldisc *ld;
- int ret = 0;
spin_lock_irqsave(&tty_ldisc_lock, flags);
- ld = tty->ldisc;
- if (test_bit(TTY_LDISC, &tty->flags)) {
- ld->refcount++;
- ret = 1;
- }
+ ld = NULL;
+ if (test_bit(TTY_LDISC, &tty->flags))
+ ld = get_ldisc(tty->ldisc);
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- return ret;
+ return ld;
}
/**
@@ -322,10 +325,11 @@ static int tty_ldisc_try(struct tty_struct *tty)
struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
{
+ struct tty_ldisc *ld;
+
/* wait_event is a macro */
- wait_event(tty_ldisc_wait, tty_ldisc_try(tty));
- WARN_ON(tty->ldisc->refcount == 0);
- return tty->ldisc;
+ wait_event(tty_ldisc_wait, (ld = tty_ldisc_try(tty)) != NULL);
+ return ld;
}
EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
@@ -342,9 +346,7 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
{
- if (tty_ldisc_try(tty))
- return tty->ldisc;
- return NULL;
+ return tty_ldisc_try(tty);
}
EXPORT_SYMBOL_GPL(tty_ldisc_ref);
@@ -360,21 +362,15 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref);
void tty_ldisc_deref(struct tty_ldisc *ld)
{
- unsigned long flags;
-
- BUG_ON(ld == NULL);
-
- spin_lock_irqsave(&tty_ldisc_lock, flags);
- if (ld->refcount == 0)
- printk(KERN_ERR "tty_ldisc_deref: no references.\n");
- else
- ld->refcount--;
- if (ld->refcount == 0)
- wake_up(&tty_ldisc_wait);
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ put_ldisc(ld);
}
EXPORT_SYMBOL_GPL(tty_ldisc_deref);
+static inline void tty_ldisc_put(struct tty_ldisc *ld)
+{
+ put_ldisc(ld);
+}
+
/**
* tty_ldisc_enable - allow ldisc use
* @tty: terminal to activate ldisc on
@@ -512,8 +508,9 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
* be obtained while the delayed work queue halt ensures that no more
* data is fed to the ldisc.
*
- * In order to wait for any existing references to complete see
- * tty_ldisc_wait_idle.
+ * You need to do a 'flush_scheduled_work()' (outside the ldisc_mutex)
+ * in order to make sure any currently executing ldisc work is also
+ * flushed.
*/
static int tty_ldisc_halt(struct tty_struct *tty)
@@ -523,31 +520,6 @@ static int tty_ldisc_halt(struct tty_struct *tty)
}
/**
- * tty_ldisc_wait_idle - wait for the ldisc to become idle
- * @tty: tty to wait for
- *
- * Wait for the line discipline to become idle. The discipline must
- * have been halted for this to guarantee it remains idle.
- *
- * tty_ldisc_lock protects the ref counts currently.
- */
-
-static int tty_ldisc_wait_idle(struct tty_struct *tty)
-{
- unsigned long flags;
- spin_lock_irqsave(&tty_ldisc_lock, flags);
- while (tty->ldisc->refcount) {
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- if (wait_event_timeout(tty_ldisc_wait,
- tty->ldisc->refcount == 0, 5 * HZ) == 0)
- return -EBUSY;
- spin_lock_irqsave(&tty_ldisc_lock, flags);
- }
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- return 0;
-}
-
-/**
* tty_set_ldisc - set line discipline
* @tty: the terminal to set
* @ldisc: the line discipline
@@ -642,14 +614,6 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
flush_scheduled_work();
- /* Let any existing reference holders finish */
- retval = tty_ldisc_wait_idle(tty);
- if (retval < 0) {
- clear_bit(TTY_LDISC_CHANGING, &tty->flags);
- tty_ldisc_put(new_ldisc);
- return retval;
- }
-
mutex_lock(&tty->ldisc_mutex);
if (test_bit(TTY_HUPPED, &tty->flags)) {
/* We were raced by the hangup method. It will have stomped
@@ -790,12 +754,14 @@ void tty_ldisc_hangup(struct tty_struct *tty)
* N_TTY.
*/
if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
+ /* Make sure the old ldisc is quiescent */
+ tty_ldisc_halt(tty);
+ flush_scheduled_work();
+
/* Avoid racing set_ldisc or tty_ldisc_release */
mutex_lock(&tty->ldisc_mutex);
if (tty->ldisc) { /* Not yet closed */
/* Switch back to N_TTY */
- tty_ldisc_halt(tty);
- tty_ldisc_wait_idle(tty);
tty_ldisc_reinit(tty);
/* At this point we have a closed ldisc and we want to
reopen it. We could defer this to the next open but
@@ -860,14 +826,6 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
tty_ldisc_halt(tty);
flush_scheduled_work();
- /*
- * Wait for any short term users (we know they are just driver
- * side waiters as the file is closing so user count on the file
- * side is zero.
- */
-
- tty_ldisc_wait_idle(tty);
-
mutex_lock(&tty->ldisc_mutex);
/*
* Now kill off the ldisc
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 2964f5f4a7e..6b3e0c2f33e 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -40,6 +40,7 @@ struct sh_cmt_priv {
struct platform_device *pdev;
unsigned long flags;
+ unsigned long flags_suspend;
unsigned long match_value;
unsigned long next_match_value;
unsigned long max_match_value;
@@ -667,11 +668,38 @@ static int __devexit sh_cmt_remove(struct platform_device *pdev)
return -EBUSY; /* cannot unregister clockevent and clocksource */
}
+static int sh_cmt_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_cmt_priv *p = platform_get_drvdata(pdev);
+
+ /* save flag state and stop CMT channel */
+ p->flags_suspend = p->flags;
+ sh_cmt_stop(p, p->flags);
+ return 0;
+}
+
+static int sh_cmt_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_cmt_priv *p = platform_get_drvdata(pdev);
+
+ /* start CMT channel from saved state */
+ sh_cmt_start(p, p->flags_suspend);
+ return 0;
+}
+
+static struct dev_pm_ops sh_cmt_dev_pm_ops = {
+ .suspend = sh_cmt_suspend,
+ .resume = sh_cmt_resume,
+};
+
static struct platform_driver sh_cmt_device_driver = {
.probe = sh_cmt_probe,
.remove = __devexit_p(sh_cmt_remove),
.driver = {
.name = "sh_cmt",
+ .pm = &sh_cmt_dev_pm_ops,
}
};
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index b90eda8b344..2968ed6a9c4 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -858,6 +858,8 @@ static int cpufreq_add_dev(struct sys_device *sys_dev)
/* Check for existing affected CPUs.
* They may not be aware of it due to CPU Hotplug.
+ * cpufreq_cpu_put is called when the device is removed
+ * in __cpufreq_remove_dev()
*/
managed_policy = cpufreq_cpu_get(j);
if (unlikely(managed_policy)) {
@@ -884,7 +886,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev)
ret = sysfs_create_link(&sys_dev->kobj,
&managed_policy->kobj,
"cpufreq");
- if (!ret)
+ if (ret)
cpufreq_cpu_put(managed_policy);
/*
* Success. We only needed to be added to the mask.
@@ -924,6 +926,8 @@ static int cpufreq_add_dev(struct sys_device *sys_dev)
spin_lock_irqsave(&cpufreq_driver_lock, flags);
for_each_cpu(j, policy->cpus) {
+ if (!cpu_online(j))
+ continue;
per_cpu(cpufreq_cpu_data, j) = policy;
per_cpu(policy_cpu, j) = policy->cpu;
}
@@ -1244,9 +1248,9 @@ EXPORT_SYMBOL(cpufreq_get);
static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg)
{
- int cpu = sysdev->id;
int ret = 0;
- unsigned int cur_freq = 0;
+
+ int cpu = sysdev->id;
struct cpufreq_policy *cpu_policy;
dprintk("suspending cpu %u\n", cpu);
@@ -1269,42 +1273,9 @@ static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg)
if (cpufreq_driver->suspend) {
ret = cpufreq_driver->suspend(cpu_policy, pmsg);
- if (ret) {
+ if (ret)
printk(KERN_ERR "cpufreq: suspend failed in ->suspend "
"step on CPU %u\n", cpu_policy->cpu);
- goto out;
- }
- }
-
- if (cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)
- goto out;
-
- if (cpufreq_driver->get)
- cur_freq = cpufreq_driver->get(cpu_policy->cpu);
-
- if (!cur_freq || !cpu_policy->cur) {
- printk(KERN_ERR "cpufreq: suspend failed to assert current "
- "frequency is what timing core thinks it is.\n");
- goto out;
- }
-
- if (unlikely(cur_freq != cpu_policy->cur)) {
- struct cpufreq_freqs freqs;
-
- if (!(cpufreq_driver->flags & CPUFREQ_PM_NO_WARN))
- dprintk("Warning: CPU frequency is %u, "
- "cpufreq assumed %u kHz.\n",
- cur_freq, cpu_policy->cur);
-
- freqs.cpu = cpu;
- freqs.old = cpu_policy->cur;
- freqs.new = cur_freq;
-
- srcu_notifier_call_chain(&cpufreq_transition_notifier_list,
- CPUFREQ_SUSPENDCHANGE, &freqs);
- adjust_jiffies(CPUFREQ_SUSPENDCHANGE, &freqs);
-
- cpu_policy->cur = cur_freq;
}
out:
@@ -1316,14 +1287,17 @@ out:
* cpufreq_resume - restore proper CPU frequency handling after resume
*
* 1.) resume CPUfreq hardware support (cpufreq_driver->resume())
- * 2.) if ->target and !CPUFREQ_CONST_LOOPS: verify we're in sync
- * 3.) schedule call cpufreq_update_policy() ASAP as interrupts are
- * restored.
+ * 2.) schedule call cpufreq_update_policy() ASAP as interrupts are
+ * restored. It will verify that the current freq is in sync with
+ * what we believe it to be. This is a bit later than when it
+ * should be, but nonethteless it's better than calling
+ * cpufreq_driver->get() here which might re-enable interrupts...
*/
static int cpufreq_resume(struct sys_device *sysdev)
{
- int cpu = sysdev->id;
int ret = 0;
+
+ int cpu = sysdev->id;
struct cpufreq_policy *cpu_policy;
dprintk("resuming cpu %u\n", cpu);
@@ -1353,42 +1327,8 @@ static int cpufreq_resume(struct sys_device *sysdev)
}
}
- if (!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) {
- unsigned int cur_freq = 0;
-
- if (cpufreq_driver->get)
- cur_freq = cpufreq_driver->get(cpu_policy->cpu);
-
- if (!cur_freq || !cpu_policy->cur) {
- printk(KERN_ERR "cpufreq: resume failed to assert "
- "current frequency is what timing core "
- "thinks it is.\n");
- goto out;
- }
-
- if (unlikely(cur_freq != cpu_policy->cur)) {
- struct cpufreq_freqs freqs;
-
- if (!(cpufreq_driver->flags & CPUFREQ_PM_NO_WARN))
- dprintk("Warning: CPU frequency "
- "is %u, cpufreq assumed %u kHz.\n",
- cur_freq, cpu_policy->cur);
-
- freqs.cpu = cpu;
- freqs.old = cpu_policy->cur;
- freqs.new = cur_freq;
-
- srcu_notifier_call_chain(
- &cpufreq_transition_notifier_list,
- CPUFREQ_RESUMECHANGE, &freqs);
- adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs);
-
- cpu_policy->cur = cur_freq;
- }
- }
-
-out:
schedule_work(&cpu_policy->update);
+
fail:
cpufreq_cpu_put(cpu_policy);
return ret;
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 57490502b21..bdea7e2f94b 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -63,6 +63,7 @@ struct cpu_dbs_info_s {
unsigned int down_skip;
unsigned int requested_freq;
int cpu;
+ unsigned int enable:1;
/*
* percpu mutex that serializes governor limit change with
* do_dbs_timer invocation. We do not want do_dbs_timer to run
@@ -141,6 +142,9 @@ dbs_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
struct cpufreq_policy *policy;
+ if (!this_dbs_info->enable)
+ return 0;
+
policy = this_dbs_info->cur_policy;
/*
@@ -497,6 +501,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
delay -= jiffies % delay;
+ dbs_info->enable = 1;
INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer);
queue_delayed_work_on(dbs_info->cpu, kconservative_wq, &dbs_info->work,
delay);
@@ -504,6 +509,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
{
+ dbs_info->enable = 0;
cancel_delayed_work_sync(&dbs_info->work);
}
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 24964c1d0af..e2a10bcba7a 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -868,6 +868,8 @@ static void amd64_read_dbam_reg(struct amd64_pvt *pvt)
goto err_reg;
}
+ return;
+
err_reg:
debugf0("Error reading F2x%03x.\n", reg);
}
@@ -2634,6 +2636,8 @@ static void amd64_read_mc_registers(struct amd64_pvt *pvt)
amd64_dump_misc_regs(pvt);
+ return;
+
err_reg:
debugf0("Reading an MC register failed\n");
@@ -2977,6 +2981,9 @@ static int amd64_check_ecc_enabled(struct amd64_pvt *pvt)
"ECC is enabled by BIOS, Proceeding "
"with EDAC module initialization\n");
+ /* Signal good ECC status */
+ ret = 0;
+
/* CLEAR the override, since BIOS controlled it */
ecc_enable_override = 0;
}
diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c
index 110e731f557..1c0b504a42f 100644
--- a/drivers/firewire/core-iso.c
+++ b/drivers/firewire/core-iso.c
@@ -196,7 +196,7 @@ static int manage_bandwidth(struct fw_card *card, int irm_id, int generation,
switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
irm_id, generation, SCODE_100,
CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE,
- data, sizeof(data))) {
+ data, 8)) {
case RCODE_GENERATION:
/* A generation change frees all bandwidth. */
return allocate ? -EAGAIN : bandwidth;
@@ -233,7 +233,7 @@ static int manage_channel(struct fw_card *card, int irm_id, int generation,
data[1] = old ^ c;
switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
irm_id, generation, SCODE_100,
- offset, data, sizeof(data))) {
+ offset, data, 8)) {
case RCODE_GENERATION:
/* A generation change frees all channels. */
return allocate ? -EAGAIN : i;
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index ecddd11b797..76b321bb73f 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -34,6 +34,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
+#include <linux/pci_ids.h>
#include <linux/spinlock.h>
#include <linux/string.h>
@@ -2372,6 +2373,9 @@ static void ohci_pmac_off(struct pci_dev *dev)
#define ohci_pmac_off(dev)
#endif /* CONFIG_PPC_PMAC */
+#define PCI_VENDOR_ID_AGERE PCI_VENDOR_ID_ATT
+#define PCI_DEVICE_ID_AGERE_FW643 0x5901
+
static int __devinit pci_probe(struct pci_dev *dev,
const struct pci_device_id *ent)
{
@@ -2422,6 +2426,16 @@ static int __devinit pci_probe(struct pci_dev *dev,
version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff;
ohci->use_dualbuffer = version >= OHCI_VERSION_1_1;
+ /* dual-buffer mode is broken if more than one IR context is active */
+ if (dev->vendor == PCI_VENDOR_ID_AGERE &&
+ dev->device == PCI_DEVICE_ID_AGERE_FW643)
+ ohci->use_dualbuffer = false;
+
+ /* dual-buffer mode is broken */
+ if (dev->vendor == PCI_VENDOR_ID_RICOH &&
+ dev->device == PCI_DEVICE_ID_RICOH_R5C832)
+ ohci->use_dualbuffer = false;
+
/* x86-32 currently doesn't use highmem for dma_alloc_coherent */
#if !defined(CONFIG_X86_32)
/* dual-buffer mode is broken with descriptor addresses above 2G */
diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c
index 8d51568ee14..e5df822a813 100644
--- a/drivers/firewire/sbp2.c
+++ b/drivers/firewire/sbp2.c
@@ -456,12 +456,12 @@ static void sbp2_status_write(struct fw_card *card, struct fw_request *request,
}
spin_unlock_irqrestore(&card->lock, flags);
- if (&orb->link != &lu->orb_list)
+ if (&orb->link != &lu->orb_list) {
orb->callback(orb, &status);
- else
+ kref_put(&orb->kref, free_orb);
+ } else {
fw_error("status write for unknown orb\n");
-
- kref_put(&orb->kref, free_orb);
+ }
fw_send_response(card, request, RCODE_COMPLETE);
}
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 8fab7890a36..2f631c75f70 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -258,31 +258,6 @@ void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type)
EXPORT_SYMBOL(drm_mode_object_find);
/**
- * drm_crtc_from_fb - find the CRTC structure associated with an fb
- * @dev: DRM device
- * @fb: framebuffer in question
- *
- * LOCKING:
- * Caller must hold mode_config lock.
- *
- * Find CRTC in the mode_config structure that matches @fb.
- *
- * RETURNS:
- * Pointer to the CRTC or NULL if it wasn't found.
- */
-struct drm_crtc *drm_crtc_from_fb(struct drm_device *dev,
- struct drm_framebuffer *fb)
-{
- struct drm_crtc *crtc;
-
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- if (crtc->fb == fb)
- return crtc;
- }
- return NULL;
-}
-
-/**
* drm_framebuffer_init - initialize a framebuffer
* @dev: DRM device
*
@@ -328,11 +303,20 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
{
struct drm_device *dev = fb->dev;
struct drm_crtc *crtc;
+ struct drm_mode_set set;
+ int ret;
/* remove from any CRTC */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- if (crtc->fb == fb)
- crtc->fb = NULL;
+ if (crtc->fb == fb) {
+ /* should turn off the crtc */
+ memset(&set, 0, sizeof(struct drm_mode_set));
+ set.crtc = crtc;
+ set.fb = NULL;
+ ret = crtc->funcs->set_config(&set);
+ if (ret)
+ DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
+ }
}
drm_mode_object_put(dev, &fb->base);
@@ -1461,7 +1445,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
goto out;
}
- if (crtc_req->count_connectors > 0 && !mode && !fb) {
+ if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
DRM_DEBUG("Count connectors is %d but no mode or fb set\n",
crtc_req->count_connectors);
ret = -EINVAL;
@@ -1511,7 +1495,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
set.mode = mode;
set.connectors = connector_set;
set.num_connectors = crtc_req->count_connectors;
- set.fb =fb;
+ set.fb = fb;
ret = crtc->funcs->set_config(&set);
out:
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 3da9cfa3184..6aaa2cb2336 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -706,8 +706,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
struct drm_encoder **save_encoders, *new_encoder;
struct drm_framebuffer *old_fb = NULL;
bool save_enabled;
- bool mode_changed = false;
- bool fb_changed = false;
+ bool mode_changed = false; /* if true do a full mode set */
+ bool fb_changed = false; /* if true and !mode_changed just do a flip */
struct drm_connector *connector;
int count = 0, ro, fail = 0;
struct drm_crtc_helper_funcs *crtc_funcs;
@@ -758,6 +758,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
if (set->crtc->fb == NULL) {
DRM_DEBUG("crtc has no fb, full mode set\n");
mode_changed = true;
+ } else if (set->fb == NULL) {
+ mode_changed = true;
} else if ((set->fb->bits_per_pixel !=
set->crtc->fb->bits_per_pixel) ||
set->fb->depth != set->crtc->fb->depth)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 80cc6d06d61..7f2728bbc16 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -502,12 +502,40 @@ static int add_detailed_info(struct drm_connector *connector,
struct detailed_non_pixel *data = &timing->data.other_data;
struct drm_display_mode *newmode;
- /* EDID up to and including 1.2 may put monitor info here */
- if (edid->version == 1 && edid->revision < 3)
- continue;
-
- /* Detailed mode timing */
- if (timing->pixel_clock) {
+ /* X server check is version 1.1 or higher */
+ if (edid->version == 1 && edid->revision >= 1 &&
+ !timing->pixel_clock) {
+ /* Other timing or info */
+ switch (data->type) {
+ case EDID_DETAIL_MONITOR_SERIAL:
+ break;
+ case EDID_DETAIL_MONITOR_STRING:
+ break;
+ case EDID_DETAIL_MONITOR_RANGE:
+ /* Get monitor range data */
+ break;
+ case EDID_DETAIL_MONITOR_NAME:
+ break;
+ case EDID_DETAIL_MONITOR_CPDATA:
+ break;
+ case EDID_DETAIL_STD_MODES:
+ /* Five modes per detailed section */
+ for (j = 0; j < 5; i++) {
+ struct std_timing *std;
+ struct drm_display_mode *newmode;
+
+ std = &data->data.timings[j];
+ newmode = drm_mode_std(dev, std);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
newmode = drm_mode_detailed(dev, edid, timing, quirks);
if (!newmode)
continue;
@@ -518,38 +546,6 @@ static int add_detailed_info(struct drm_connector *connector,
drm_mode_probed_add(connector, newmode);
modes++;
- continue;
- }
-
- /* Other timing or info */
- switch (data->type) {
- case EDID_DETAIL_MONITOR_SERIAL:
- break;
- case EDID_DETAIL_MONITOR_STRING:
- break;
- case EDID_DETAIL_MONITOR_RANGE:
- /* Get monitor range data */
- break;
- case EDID_DETAIL_MONITOR_NAME:
- break;
- case EDID_DETAIL_MONITOR_CPDATA:
- break;
- case EDID_DETAIL_STD_MODES:
- /* Five modes per detailed section */
- for (j = 0; j < 5; i++) {
- struct std_timing *std;
- struct drm_display_mode *newmode;
-
- std = &data->data.timings[j];
- newmode = drm_mode_std(dev, std);
- if (newmode) {
- drm_mode_probed_add(connector, newmode);
- modes++;
- }
- }
- break;
- default:
- break;
}
}
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index b4a3dbcebe9..f85aaf21e78 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -566,7 +566,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
ret = drm_vblank_get(dev, crtc);
if (ret) {
- DRM_ERROR("failed to acquire vblank counter, %d\n", ret);
+ DRM_DEBUG("failed to acquire vblank counter, %d\n", ret);
return ret;
}
seq = drm_vblank_count(dev, crtc);
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 54f492a488a..7914097b09c 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -566,6 +566,8 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
found_it = 1;
/* if equal delete the probed mode */
mode->status = pmode->status;
+ /* Merge type bits together */
+ mode->type |= pmode->type;
list_del(&pmode->head);
drm_mode_destroy(connector->dev, pmode);
break;
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 85ec31b3ff0..f7a615b80c7 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -22,44 +22,50 @@
#define to_drm_minor(d) container_of(d, struct drm_minor, kdev)
#define to_drm_connector(d) container_of(d, struct drm_connector, kdev)
+static struct device_type drm_sysfs_device_minor = {
+ .name = "drm_minor"
+};
+
/**
- * drm_sysfs_suspend - DRM class suspend hook
+ * drm_class_suspend - DRM class suspend hook
* @dev: Linux device to suspend
* @state: power state to enter
*
* Just figures out what the actual struct drm_device associated with
* @dev is and calls its suspend hook, if present.
*/
-static int drm_sysfs_suspend(struct device *dev, pm_message_t state)
+static int drm_class_suspend(struct device *dev, pm_message_t state)
{
- struct drm_minor *drm_minor = to_drm_minor(dev);
- struct drm_device *drm_dev = drm_minor->dev;
-
- if (drm_minor->type == DRM_MINOR_LEGACY &&
- !drm_core_check_feature(drm_dev, DRIVER_MODESET) &&
- drm_dev->driver->suspend)
- return drm_dev->driver->suspend(drm_dev, state);
-
+ if (dev->type == &drm_sysfs_device_minor) {
+ struct drm_minor *drm_minor = to_drm_minor(dev);
+ struct drm_device *drm_dev = drm_minor->dev;
+
+ if (drm_minor->type == DRM_MINOR_LEGACY &&
+ !drm_core_check_feature(drm_dev, DRIVER_MODESET) &&
+ drm_dev->driver->suspend)
+ return drm_dev->driver->suspend(drm_dev, state);
+ }
return 0;
}
/**
- * drm_sysfs_resume - DRM class resume hook
+ * drm_class_resume - DRM class resume hook
* @dev: Linux device to resume
*
* Just figures out what the actual struct drm_device associated with
* @dev is and calls its resume hook, if present.
*/
-static int drm_sysfs_resume(struct device *dev)
+static int drm_class_resume(struct device *dev)
{
- struct drm_minor *drm_minor = to_drm_minor(dev);
- struct drm_device *drm_dev = drm_minor->dev;
-
- if (drm_minor->type == DRM_MINOR_LEGACY &&
- !drm_core_check_feature(drm_dev, DRIVER_MODESET) &&
- drm_dev->driver->resume)
- return drm_dev->driver->resume(drm_dev);
-
+ if (dev->type == &drm_sysfs_device_minor) {
+ struct drm_minor *drm_minor = to_drm_minor(dev);
+ struct drm_device *drm_dev = drm_minor->dev;
+
+ if (drm_minor->type == DRM_MINOR_LEGACY &&
+ !drm_core_check_feature(drm_dev, DRIVER_MODESET) &&
+ drm_dev->driver->resume)
+ return drm_dev->driver->resume(drm_dev);
+ }
return 0;
}
@@ -99,8 +105,8 @@ struct class *drm_sysfs_create(struct module *owner, char *name)
goto err_out;
}
- class->suspend = drm_sysfs_suspend;
- class->resume = drm_sysfs_resume;
+ class->suspend = drm_class_suspend;
+ class->resume = drm_class_resume;
err = class_create_file(class, &class_attr_version);
if (err)
@@ -480,6 +486,7 @@ int drm_sysfs_device_add(struct drm_minor *minor)
minor->kdev.class = drm_class;
minor->kdev.release = drm_sysfs_device_release;
minor->kdev.devt = minor->device;
+ minor->kdev.type = &drm_sysfs_device_minor;
if (minor->type == DRM_MINOR_CONTROL)
minor_str = "controlD%d";
else if (minor->type == DRM_MINOR_RENDER)
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 8c4783180bf..50d1f782768 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1186,6 +1186,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (ret)
goto out_iomapfree;
+ dev_priv->wq = create_workqueue("i915");
+ if (dev_priv->wq == NULL) {
+ DRM_ERROR("Failed to create our workqueue.\n");
+ ret = -ENOMEM;
+ goto out_iomapfree;
+ }
+
/* enable GEM by default */
dev_priv->has_gem = 1;
@@ -1211,7 +1218,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (!I915_NEED_GFX_HWS(dev)) {
ret = i915_init_phys_hws(dev);
if (ret != 0)
- goto out_iomapfree;
+ goto out_workqueue_free;
}
i915_get_mem_freq(dev);
@@ -1245,7 +1252,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
ret = i915_load_modeset_init(dev, prealloc_size, agp_size);
if (ret < 0) {
DRM_ERROR("failed to init modeset\n");
- goto out_rmmap;
+ goto out_workqueue_free;
}
}
@@ -1256,6 +1263,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
return 0;
+out_workqueue_free:
+ destroy_workqueue(dev_priv->wq);
out_iomapfree:
io_mapping_free(dev_priv->mm.gtt_mapping);
out_rmmap:
@@ -1269,6 +1278,8 @@ int i915_driver_unload(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ destroy_workqueue(dev_priv->wq);
+
io_mapping_free(dev_priv->mm.gtt_mapping);
if (dev_priv->mm.gtt_mtrr >= 0) {
mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base,
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index d0875287588..5b4f87e5562 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -219,8 +219,10 @@ typedef struct drm_i915_private {
unsigned int lvds_vbt:1;
unsigned int int_crt_support:1;
unsigned int lvds_use_ssc:1;
+ unsigned int edp_support:1;
int lvds_ssc_freq;
+ int crt_ddc_bus; /* -1 = unknown, else GPIO to use for CRT DDC */
struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
int num_fence_regs; /* 8 on pre-965, 16 otherwise */
@@ -229,6 +231,8 @@ typedef struct drm_i915_private {
spinlock_t error_lock;
struct drm_i915_error_state *first_error;
+ struct work_struct error_work;
+ struct workqueue_struct *wq;
/* Register state */
u8 saveLBB;
@@ -381,6 +385,9 @@ typedef struct drm_i915_private {
*/
struct list_head inactive_list;
+ /** LRU list of objects with fence regs on them. */
+ struct list_head fence_list;
+
/**
* List of breadcrumbs associated with GPU requests currently
* outstanding.
@@ -448,6 +455,9 @@ struct drm_i915_gem_object {
/** This object's place on the active/flushing/inactive lists */
struct list_head list;
+ /** This object's place on the fenced object LRU */
+ struct list_head fence_list;
+
/**
* This is set if the object is on the active or flushing lists
* (has pending rendering), and is not set if it's on inactive (ready
@@ -888,6 +898,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
IS_I915GM(dev)))
#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IGDNG(dev))
#define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_IGDNG(dev))
+#define SUPPORTS_EDP(dev) (IS_IGDNG_M(dev))
#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
/* dsparb controlled by hw only */
#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev))
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 5bf420378b6..80e5ba490dc 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -978,6 +978,7 @@ int
i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_set_domain *args = data;
struct drm_gem_object *obj;
uint32_t read_domains = args->read_domains;
@@ -1010,8 +1011,18 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
obj, obj->size, read_domains, write_domain);
#endif
if (read_domains & I915_GEM_DOMAIN_GTT) {
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+
ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0);
+ /* Update the LRU on the fence for the CPU access that's
+ * about to occur.
+ */
+ if (obj_priv->fence_reg != I915_FENCE_REG_NONE) {
+ list_move_tail(&obj_priv->fence_list,
+ &dev_priv->mm.fence_list);
+ }
+
/* Silently promote "you're not bound, there was nothing to do"
* to success, since the client was just asking us to
* make sure everything was done.
@@ -1155,8 +1166,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
}
/* Need a new fence register? */
- if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
- obj_priv->tiling_mode != I915_TILING_NONE) {
+ if (obj_priv->tiling_mode != I915_TILING_NONE) {
ret = i915_gem_object_get_fence_reg(obj);
if (ret) {
mutex_unlock(&dev->struct_mutex);
@@ -1570,7 +1580,7 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
}
if (was_empty && !dev_priv->mm.suspended)
- schedule_delayed_work(&dev_priv->mm.retire_work, HZ);
+ queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
return seqno;
}
@@ -1719,7 +1729,7 @@ i915_gem_retire_work_handler(struct work_struct *work)
i915_gem_retire_requests(dev);
if (!dev_priv->mm.suspended &&
!list_empty(&dev_priv->mm.request_list))
- schedule_delayed_work(&dev_priv->mm.retire_work, HZ);
+ queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
mutex_unlock(&dev->struct_mutex);
}
@@ -2208,6 +2218,12 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj)
struct drm_i915_gem_object *old_obj_priv = NULL;
int i, ret, avail;
+ /* Just update our place in the LRU if our fence is getting used. */
+ if (obj_priv->fence_reg != I915_FENCE_REG_NONE) {
+ list_move_tail(&obj_priv->fence_list, &dev_priv->mm.fence_list);
+ return 0;
+ }
+
switch (obj_priv->tiling_mode) {
case I915_TILING_NONE:
WARN(1, "allocating a fence for non-tiled object?\n");
@@ -2229,7 +2245,6 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj)
}
/* First try to find a free reg */
-try_again:
avail = 0;
for (i = dev_priv->fence_reg_start; i < dev_priv->num_fence_regs; i++) {
reg = &dev_priv->fence_regs[i];
@@ -2243,63 +2258,62 @@ try_again:
/* None available, try to steal one or wait for a user to finish */
if (i == dev_priv->num_fence_regs) {
- uint32_t seqno = dev_priv->mm.next_gem_seqno;
+ struct drm_gem_object *old_obj = NULL;
if (avail == 0)
return -ENOSPC;
- for (i = dev_priv->fence_reg_start;
- i < dev_priv->num_fence_regs; i++) {
- uint32_t this_seqno;
-
- reg = &dev_priv->fence_regs[i];
- old_obj_priv = reg->obj->driver_private;
+ list_for_each_entry(old_obj_priv, &dev_priv->mm.fence_list,
+ fence_list) {
+ old_obj = old_obj_priv->obj;
if (old_obj_priv->pin_count)
continue;
+ /* Take a reference, as otherwise the wait_rendering
+ * below may cause the object to get freed out from
+ * under us.
+ */
+ drm_gem_object_reference(old_obj);
+
/* i915 uses fences for GPU access to tiled buffers */
if (IS_I965G(dev) || !old_obj_priv->active)
break;
- /* find the seqno of the first available fence */
- this_seqno = old_obj_priv->last_rendering_seqno;
- if (this_seqno != 0 &&
- reg->obj->write_domain == 0 &&
- i915_seqno_passed(seqno, this_seqno))
- seqno = this_seqno;
- }
-
- /*
- * Now things get ugly... we have to wait for one of the
- * objects to finish before trying again.
- */
- if (i == dev_priv->num_fence_regs) {
- if (seqno == dev_priv->mm.next_gem_seqno) {
- i915_gem_flush(dev,
- I915_GEM_GPU_DOMAINS,
- I915_GEM_GPU_DOMAINS);
- seqno = i915_add_request(dev, NULL,
- I915_GEM_GPU_DOMAINS);
- if (seqno == 0)
- return -ENOMEM;
+ /* This brings the object to the head of the LRU if it
+ * had been written to. The only way this should
+ * result in us waiting longer than the expected
+ * optimal amount of time is if there was a
+ * fence-using buffer later that was read-only.
+ */
+ i915_gem_object_flush_gpu_write_domain(old_obj);
+ ret = i915_gem_object_wait_rendering(old_obj);
+ if (ret != 0) {
+ drm_gem_object_unreference(old_obj);
+ return ret;
}
- ret = i915_wait_request(dev, seqno);
- if (ret)
- return ret;
- goto try_again;
+ break;
}
/*
* Zap this virtual mapping so we can set up a fence again
* for this object next time we need it.
*/
- i915_gem_release_mmap(reg->obj);
+ i915_gem_release_mmap(old_obj);
+
+ i = old_obj_priv->fence_reg;
+ reg = &dev_priv->fence_regs[i];
+
old_obj_priv->fence_reg = I915_FENCE_REG_NONE;
+ list_del_init(&old_obj_priv->fence_list);
+
+ drm_gem_object_unreference(old_obj);
}
obj_priv->fence_reg = i;
+ list_add_tail(&obj_priv->fence_list, &dev_priv->mm.fence_list);
+
reg->obj = obj;
if (IS_I965G(dev))
@@ -2342,6 +2356,7 @@ i915_gem_clear_fence_reg(struct drm_gem_object *obj)
dev_priv->fence_regs[obj_priv->fence_reg].obj = NULL;
obj_priv->fence_reg = I915_FENCE_REG_NONE;
+ list_del_init(&obj_priv->fence_list);
}
/**
@@ -3595,9 +3610,7 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment)
* Pre-965 chips need a fence register set up in order to
* properly handle tiled surfaces.
*/
- if (!IS_I965G(dev) &&
- obj_priv->fence_reg == I915_FENCE_REG_NONE &&
- obj_priv->tiling_mode != I915_TILING_NONE) {
+ if (!IS_I965G(dev) && obj_priv->tiling_mode != I915_TILING_NONE) {
ret = i915_gem_object_get_fence_reg(obj);
if (ret != 0) {
if (ret != -EBUSY && ret != -ERESTARTSYS)
@@ -3806,6 +3819,7 @@ int i915_gem_init_object(struct drm_gem_object *obj)
obj_priv->obj = obj;
obj_priv->fence_reg = I915_FENCE_REG_NONE;
INIT_LIST_HEAD(&obj_priv->list);
+ INIT_LIST_HEAD(&obj_priv->fence_list);
return 0;
}
@@ -4218,15 +4232,11 @@ int
i915_gem_leavevt_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- int ret;
-
if (drm_core_check_feature(dev, DRIVER_MODESET))
return 0;
- ret = i915_gem_idle(dev);
drm_irq_uninstall(dev);
-
- return ret;
+ return i915_gem_idle(dev);
}
void
@@ -4253,6 +4263,7 @@ i915_gem_load(struct drm_device *dev)
INIT_LIST_HEAD(&dev_priv->mm.flushing_list);
INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
INIT_LIST_HEAD(&dev_priv->mm.request_list);
+ INIT_LIST_HEAD(&dev_priv->mm.fence_list);
INIT_DELAYED_WORK(&dev_priv->mm.retire_work,
i915_gem_retire_work_handler);
dev_priv->mm.next_gem_seqno = 1;
diff --git a/drivers/gpu/drm/i915/i915_gem_debugfs.c b/drivers/gpu/drm/i915/i915_gem_debugfs.c
index 9a44bfcb813..cb3b97405fb 100644
--- a/drivers/gpu/drm/i915/i915_gem_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_gem_debugfs.c
@@ -343,6 +343,8 @@ static int i915_error_state(struct seq_file *m, void *unused)
error = dev_priv->first_error;
+ seq_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec,
+ error->time.tv_usec);
seq_printf(m, "EIR: 0x%08x\n", error->eir);
seq_printf(m, " PGTBL_ER: 0x%08x\n", error->pgtbl_er);
seq_printf(m, " INSTPM: 0x%08x\n", error->instpm);
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 7ba23a69a0c..7ebc84c2881 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -190,7 +190,7 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
if (!i915_pipe_enabled(dev, pipe)) {
- DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe);
+ DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);
return 0;
}
@@ -219,7 +219,7 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45;
if (!i915_pipe_enabled(dev, pipe)) {
- DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe);
+ DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);
return 0;
}
@@ -290,6 +290,35 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev)
return ret;
}
+/**
+ * i915_error_work_func - do process context error handling work
+ * @work: work struct
+ *
+ * Fire an error uevent so userspace can see that a hang or error
+ * was detected.
+ */
+static void i915_error_work_func(struct work_struct *work)
+{
+ drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
+ error_work);
+ struct drm_device *dev = dev_priv->dev;
+ char *event_string = "ERROR=1";
+ char *envp[] = { event_string, NULL };
+
+ DRM_DEBUG("generating error event\n");
+
+ kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp);
+}
+
+/**
+ * i915_capture_error_state - capture an error record for later analysis
+ * @dev: drm device
+ *
+ * Should be called when an error is detected (either a hang or an error
+ * interrupt) to capture error state from the time of the error. Fills
+ * out a structure which becomes available in debugfs for user level tools
+ * to pick up.
+ */
static void i915_capture_error_state(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -325,12 +354,137 @@ static void i915_capture_error_state(struct drm_device *dev)
error->acthd = I915_READ(ACTHD_I965);
}
+ do_gettimeofday(&error->time);
+
dev_priv->first_error = error;
out:
spin_unlock_irqrestore(&dev_priv->error_lock, flags);
}
+/**
+ * i915_handle_error - handle an error interrupt
+ * @dev: drm device
+ *
+ * Do some basic checking of regsiter state at error interrupt time and
+ * dump it to the syslog. Also call i915_capture_error_state() to make
+ * sure we get a record and make it available in debugfs. Fire a uevent
+ * so userspace knows something bad happened (should trigger collection
+ * of a ring dump etc.).
+ */
+static void i915_handle_error(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 eir = I915_READ(EIR);
+ u32 pipea_stats = I915_READ(PIPEASTAT);
+ u32 pipeb_stats = I915_READ(PIPEBSTAT);
+
+ i915_capture_error_state(dev);
+
+ printk(KERN_ERR "render error detected, EIR: 0x%08x\n",
+ eir);
+
+ if (IS_G4X(dev)) {
+ if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) {
+ u32 ipeir = I915_READ(IPEIR_I965);
+
+ printk(KERN_ERR " IPEIR: 0x%08x\n",
+ I915_READ(IPEIR_I965));
+ printk(KERN_ERR " IPEHR: 0x%08x\n",
+ I915_READ(IPEHR_I965));
+ printk(KERN_ERR " INSTDONE: 0x%08x\n",
+ I915_READ(INSTDONE_I965));
+ printk(KERN_ERR " INSTPS: 0x%08x\n",
+ I915_READ(INSTPS));
+ printk(KERN_ERR " INSTDONE1: 0x%08x\n",
+ I915_READ(INSTDONE1));
+ printk(KERN_ERR " ACTHD: 0x%08x\n",
+ I915_READ(ACTHD_I965));
+ I915_WRITE(IPEIR_I965, ipeir);
+ (void)I915_READ(IPEIR_I965);
+ }
+ if (eir & GM45_ERROR_PAGE_TABLE) {
+ u32 pgtbl_err = I915_READ(PGTBL_ER);
+ printk(KERN_ERR "page table error\n");
+ printk(KERN_ERR " PGTBL_ER: 0x%08x\n",
+ pgtbl_err);
+ I915_WRITE(PGTBL_ER, pgtbl_err);
+ (void)I915_READ(PGTBL_ER);
+ }
+ }
+
+ if (IS_I9XX(dev)) {
+ if (eir & I915_ERROR_PAGE_TABLE) {
+ u32 pgtbl_err = I915_READ(PGTBL_ER);
+ printk(KERN_ERR "page table error\n");
+ printk(KERN_ERR " PGTBL_ER: 0x%08x\n",
+ pgtbl_err);
+ I915_WRITE(PGTBL_ER, pgtbl_err);
+ (void)I915_READ(PGTBL_ER);
+ }
+ }
+
+ if (eir & I915_ERROR_MEMORY_REFRESH) {
+ printk(KERN_ERR "memory refresh error\n");
+ printk(KERN_ERR "PIPEASTAT: 0x%08x\n",
+ pipea_stats);
+ printk(KERN_ERR "PIPEBSTAT: 0x%08x\n",
+ pipeb_stats);
+ /* pipestat has already been acked */
+ }
+ if (eir & I915_ERROR_INSTRUCTION) {
+ printk(KERN_ERR "instruction error\n");
+ printk(KERN_ERR " INSTPM: 0x%08x\n",
+ I915_READ(INSTPM));
+ if (!IS_I965G(dev)) {
+ u32 ipeir = I915_READ(IPEIR);
+
+ printk(KERN_ERR " IPEIR: 0x%08x\n",
+ I915_READ(IPEIR));
+ printk(KERN_ERR " IPEHR: 0x%08x\n",
+ I915_READ(IPEHR));
+ printk(KERN_ERR " INSTDONE: 0x%08x\n",
+ I915_READ(INSTDONE));
+ printk(KERN_ERR " ACTHD: 0x%08x\n",
+ I915_READ(ACTHD));
+ I915_WRITE(IPEIR, ipeir);
+ (void)I915_READ(IPEIR);
+ } else {
+ u32 ipeir = I915_READ(IPEIR_I965);
+
+ printk(KERN_ERR " IPEIR: 0x%08x\n",
+ I915_READ(IPEIR_I965));
+ printk(KERN_ERR " IPEHR: 0x%08x\n",
+ I915_READ(IPEHR_I965));
+ printk(KERN_ERR " INSTDONE: 0x%08x\n",
+ I915_READ(INSTDONE_I965));
+ printk(KERN_ERR " INSTPS: 0x%08x\n",
+ I915_READ(INSTPS));
+ printk(KERN_ERR " INSTDONE1: 0x%08x\n",
+ I915_READ(INSTDONE1));
+ printk(KERN_ERR " ACTHD: 0x%08x\n",
+ I915_READ(ACTHD_I965));
+ I915_WRITE(IPEIR_I965, ipeir);
+ (void)I915_READ(IPEIR_I965);
+ }
+ }
+
+ I915_WRITE(EIR, eir);
+ (void)I915_READ(EIR);
+ eir = I915_READ(EIR);
+ if (eir) {
+ /*
+ * some errors might have become stuck,
+ * mask them.
+ */
+ DRM_ERROR("EIR stuck: 0x%08x, masking\n", eir);
+ I915_WRITE(EMR, I915_READ(EMR) | eir);
+ I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
+ }
+
+ queue_work(dev_priv->wq, &dev_priv->error_work);
+}
+
irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *) arg;
@@ -372,6 +526,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
pipea_stats = I915_READ(PIPEASTAT);
pipeb_stats = I915_READ(PIPEBSTAT);
+ if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
+ i915_handle_error(dev);
+
/*
* Clear the PIPE(A|B)STAT regs before the IIR
*/
@@ -403,86 +560,13 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
DRM_DEBUG("hotplug event received, stat 0x%08x\n",
hotplug_status);
if (hotplug_status & dev_priv->hotplug_supported_mask)
- schedule_work(&dev_priv->hotplug_work);
+ queue_work(dev_priv->wq,
+ &dev_priv->hotplug_work);
I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
I915_READ(PORT_HOTPLUG_STAT);
}
- if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) {
- u32 eir = I915_READ(EIR);
-
- i915_capture_error_state(dev);
-
- printk(KERN_ERR "render error detected, EIR: 0x%08x\n",
- eir);
- if (eir & I915_ERROR_PAGE_TABLE) {
- u32 pgtbl_err = I915_READ(PGTBL_ER);
- printk(KERN_ERR "page table error\n");
- printk(KERN_ERR " PGTBL_ER: 0x%08x\n",
- pgtbl_err);
- I915_WRITE(PGTBL_ER, pgtbl_err);
- (void)I915_READ(PGTBL_ER);
- }
- if (eir & I915_ERROR_MEMORY_REFRESH) {
- printk(KERN_ERR "memory refresh error\n");
- printk(KERN_ERR "PIPEASTAT: 0x%08x\n",
- pipea_stats);
- printk(KERN_ERR "PIPEBSTAT: 0x%08x\n",
- pipeb_stats);
- /* pipestat has already been acked */
- }
- if (eir & I915_ERROR_INSTRUCTION) {
- printk(KERN_ERR "instruction error\n");
- printk(KERN_ERR " INSTPM: 0x%08x\n",
- I915_READ(INSTPM));
- if (!IS_I965G(dev)) {
- u32 ipeir = I915_READ(IPEIR);
-
- printk(KERN_ERR " IPEIR: 0x%08x\n",
- I915_READ(IPEIR));
- printk(KERN_ERR " IPEHR: 0x%08x\n",
- I915_READ(IPEHR));
- printk(KERN_ERR " INSTDONE: 0x%08x\n",
- I915_READ(INSTDONE));
- printk(KERN_ERR " ACTHD: 0x%08x\n",
- I915_READ(ACTHD));
- I915_WRITE(IPEIR, ipeir);
- (void)I915_READ(IPEIR);
- } else {
- u32 ipeir = I915_READ(IPEIR_I965);
-
- printk(KERN_ERR " IPEIR: 0x%08x\n",
- I915_READ(IPEIR_I965));
- printk(KERN_ERR " IPEHR: 0x%08x\n",
- I915_READ(IPEHR_I965));
- printk(KERN_ERR " INSTDONE: 0x%08x\n",
- I915_READ(INSTDONE_I965));
- printk(KERN_ERR " INSTPS: 0x%08x\n",
- I915_READ(INSTPS));
- printk(KERN_ERR " INSTDONE1: 0x%08x\n",
- I915_READ(INSTDONE1));
- printk(KERN_ERR " ACTHD: 0x%08x\n",
- I915_READ(ACTHD_I965));
- I915_WRITE(IPEIR_I965, ipeir);
- (void)I915_READ(IPEIR_I965);
- }
- }
-
- I915_WRITE(EIR, eir);
- (void)I915_READ(EIR);
- eir = I915_READ(EIR);
- if (eir) {
- /*
- * some errors might have become stuck,
- * mask them.
- */
- DRM_ERROR("EIR stuck: 0x%08x, masking\n", eir);
- I915_WRITE(EMR, I915_READ(EMR) | eir);
- I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
- }
- }
-
I915_WRITE(IIR, iir);
new_iir = I915_READ(IIR); /* Flush posted writes */
@@ -830,6 +914,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
atomic_set(&dev_priv->irq_received, 0);
INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
+ INIT_WORK(&dev_priv->error_work, i915_error_work_func);
if (IS_IGDNG(dev)) {
igdng_irq_preinstall(dev);
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 6c085848409..2955083aa47 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1395,6 +1395,7 @@
#define TV_V_CHROMA_42 0x684a8
/* Display Port */
+#define DP_A 0x64000 /* eDP */
#define DP_B 0x64100
#define DP_C 0x64200
#define DP_D 0x64300
@@ -1437,13 +1438,22 @@
/* Mystic DPCD version 1.1 special mode */
#define DP_ENHANCED_FRAMING (1 << 18)
+/* eDP */
+#define DP_PLL_FREQ_270MHZ (0 << 16)
+#define DP_PLL_FREQ_160MHZ (1 << 16)
+#define DP_PLL_FREQ_MASK (3 << 16)
+
/** locked once port is enabled */
#define DP_PORT_REVERSAL (1 << 15)
+/* eDP */
+#define DP_PLL_ENABLE (1 << 14)
+
/** sends the clock on lane 15 of the PEG for debug */
#define DP_CLOCK_OUTPUT_ENABLE (1 << 13)
#define DP_SCRAMBLING_DISABLE (1 << 12)
+#define DP_SCRAMBLING_DISABLE_IGDNG (1 << 7)
/** limit RGB values to avoid confusing TVs */
#define DP_COLOR_RANGE_16_235 (1 << 8)
@@ -1463,6 +1473,13 @@
* is 20 bytes in each direction, hence the 5 fixed
* data registers
*/
+#define DPA_AUX_CH_CTL 0x64010
+#define DPA_AUX_CH_DATA1 0x64014
+#define DPA_AUX_CH_DATA2 0x64018
+#define DPA_AUX_CH_DATA3 0x6401c
+#define DPA_AUX_CH_DATA4 0x64020
+#define DPA_AUX_CH_DATA5 0x64024
+
#define DPB_AUX_CH_CTL 0x64110
#define DPB_AUX_CH_DATA1 0x64114
#define DPB_AUX_CH_DATA2 0x64118
@@ -1618,7 +1635,7 @@
#define I830_FIFO_LINE_SIZE 32
#define I945_FIFO_SIZE 127 /* 945 & 965 */
#define I915_FIFO_SIZE 95
-#define I855GM_FIFO_SIZE 255
+#define I855GM_FIFO_SIZE 127 /* In cachelines */
#define I830_FIFO_SIZE 95
#define I915_MAX_WM 0x3f
@@ -1848,6 +1865,8 @@
#define PFA_CTL_1 0x68080
#define PFB_CTL_1 0x68880
#define PF_ENABLE (1<<31)
+#define PFA_WIN_SZ 0x68074
+#define PFB_WIN_SZ 0x68874
/* legacy palette */
#define LGC_PALETTE_A 0x4a000
@@ -2208,4 +2227,28 @@
#define PCH_PP_OFF_DELAYS 0xc720c
#define PCH_PP_DIVISOR 0xc7210
+#define PCH_DP_B 0xe4100
+#define PCH_DPB_AUX_CH_CTL 0xe4110
+#define PCH_DPB_AUX_CH_DATA1 0xe4114
+#define PCH_DPB_AUX_CH_DATA2 0xe4118
+#define PCH_DPB_AUX_CH_DATA3 0xe411c
+#define PCH_DPB_AUX_CH_DATA4 0xe4120
+#define PCH_DPB_AUX_CH_DATA5 0xe4124
+
+#define PCH_DP_C 0xe4200
+#define PCH_DPC_AUX_CH_CTL 0xe4210
+#define PCH_DPC_AUX_CH_DATA1 0xe4214
+#define PCH_DPC_AUX_CH_DATA2 0xe4218
+#define PCH_DPC_AUX_CH_DATA3 0xe421c
+#define PCH_DPC_AUX_CH_DATA4 0xe4220
+#define PCH_DPC_AUX_CH_DATA5 0xe4224
+
+#define PCH_DP_D 0xe4300
+#define PCH_DPD_AUX_CH_CTL 0xe4310
+#define PCH_DPD_AUX_CH_DATA1 0xe4314
+#define PCH_DPD_AUX_CH_DATA2 0xe4318
+#define PCH_DPD_AUX_CH_DATA3 0xe431c
+#define PCH_DPD_AUX_CH_DATA4 0xe4320
+#define PCH_DPD_AUX_CH_DATA5 0xe4324
+
#endif /* _I915_REG_H_ */
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
index 9e1d16e5c3e..1d04e1904ac 100644
--- a/drivers/gpu/drm/i915/i915_suspend.c
+++ b/drivers/gpu/drm/i915/i915_suspend.c
@@ -598,7 +598,7 @@ int i915_restore_state(struct drm_device *dev)
for (i = 0; i < 16; i++) {
I915_WRITE(SWF00 + (i << 2), dev_priv->saveSWF0[i]);
- I915_WRITE(SWF10 + (i << 2), dev_priv->saveSWF1[i+7]);
+ I915_WRITE(SWF10 + (i << 2), dev_priv->saveSWF1[i]);
}
for (i = 0; i < 3; i++)
I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]);
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 7cc44719102..f806fcc54e0 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -59,6 +59,16 @@ find_section(struct bdb_header *bdb, int section_id)
return NULL;
}
+static u16
+get_blocksize(void *p)
+{
+ u16 *block_ptr, block_size;
+
+ block_ptr = (u16 *)((char *)p - 2);
+ block_size = *block_ptr;
+ return block_size;
+}
+
static void
fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode,
struct lvds_dvo_timing *dvo_timing)
@@ -97,14 +107,13 @@ static void
parse_lfp_panel_data(struct drm_i915_private *dev_priv,
struct bdb_header *bdb)
{
- struct drm_device *dev = dev_priv->dev;
struct bdb_lvds_options *lvds_options;
struct bdb_lvds_lfp_data *lvds_lfp_data;
struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs;
struct bdb_lvds_lfp_data_entry *entry;
struct lvds_dvo_timing *dvo_timing;
struct drm_display_mode *panel_fixed_mode;
- int lfp_data_size;
+ int lfp_data_size, dvo_timing_offset;
/* Defaults if we can't find VBT info */
dev_priv->lvds_dither = 0;
@@ -133,14 +142,16 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
entry = (struct bdb_lvds_lfp_data_entry *)
((uint8_t *)lvds_lfp_data->data + (lfp_data_size *
lvds_options->panel_type));
+ dvo_timing_offset = lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset -
+ lvds_lfp_data_ptrs->ptr[0].fp_timing_offset;
- /* On IGDNG mobile, LVDS data block removes panel fitting registers.
- So dec 2 dword from dvo_timing offset */
- if (IS_IGDNG(dev))
- dvo_timing = (struct lvds_dvo_timing *)
- ((u8 *)&entry->dvo_timing - 8);
- else
- dvo_timing = &entry->dvo_timing;
+ /*
+ * the size of fp_timing varies on the different platform.
+ * So calculate the DVO timing relative offset in LVDS data
+ * entry to get the DVO timing entry
+ */
+ dvo_timing = (struct lvds_dvo_timing *)
+ ((unsigned char *)entry + dvo_timing_offset);
panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
@@ -214,6 +225,41 @@ parse_general_features(struct drm_i915_private *dev_priv,
}
static void
+parse_general_definitions(struct drm_i915_private *dev_priv,
+ struct bdb_header *bdb)
+{
+ struct bdb_general_definitions *general;
+ const int crt_bus_map_table[] = {
+ GPIOB,
+ GPIOA,
+ GPIOC,
+ GPIOD,
+ GPIOE,
+ GPIOF,
+ };
+
+ /* Set sensible defaults in case we can't find the general block
+ or it is the wrong chipset */
+ dev_priv->crt_ddc_bus = -1;
+
+ general = find_section(bdb, BDB_GENERAL_DEFINITIONS);
+ if (general) {
+ u16 block_size = get_blocksize(general);
+ if (block_size >= sizeof(*general)) {
+ int bus_pin = general->crt_ddc_gmbus_pin;
+ DRM_DEBUG("crt_ddc_bus_pin: %d\n", bus_pin);
+ if ((bus_pin >= 1) && (bus_pin <= 6)) {
+ dev_priv->crt_ddc_bus =
+ crt_bus_map_table[bus_pin-1];
+ }
+ } else {
+ DRM_DEBUG("BDB_GD too small (%d). Invalid.\n",
+ block_size);
+ }
+ }
+}
+
+static void
parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
struct bdb_header *bdb)
{
@@ -221,7 +267,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
struct bdb_general_definitions *p_defs;
struct child_device_config *p_child;
int i, child_device_num, count;
- u16 block_size, *block_ptr;
+ u16 block_size;
p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
if (!p_defs) {
@@ -239,8 +285,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
return;
}
/* get the block size of general definitions */
- block_ptr = (u16 *)((char *)p_defs - 2);
- block_size = *block_ptr;
+ block_size = get_blocksize(p_defs);
/* get the number of child device */
child_device_num = (block_size - sizeof(*p_defs)) /
sizeof(*p_child);
@@ -295,6 +340,25 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
}
return;
}
+
+static void
+parse_driver_features(struct drm_i915_private *dev_priv,
+ struct bdb_header *bdb)
+{
+ struct drm_device *dev = dev_priv->dev;
+ struct bdb_driver_features *driver;
+
+ /* set default for chips without eDP */
+ if (!SUPPORTS_EDP(dev)) {
+ dev_priv->edp_support = 0;
+ return;
+ }
+
+ driver = find_section(bdb, BDB_DRIVER_FEATURES);
+ if (driver && driver->lvds_config == BDB_DRIVER_FEATURE_EDP)
+ dev_priv->edp_support = 1;
+}
+
/**
* intel_init_bios - initialize VBIOS settings & find VBT
* @dev: DRM device
@@ -342,9 +406,12 @@ intel_init_bios(struct drm_device *dev)
/* Grab useful general definitions */
parse_general_features(dev_priv, bdb);
+ parse_general_definitions(dev_priv, bdb);
parse_lfp_panel_data(dev_priv, bdb);
parse_sdvo_panel_data(dev_priv, bdb);
parse_sdvo_device_mapping(dev_priv, bdb);
+ parse_driver_features(dev_priv, bdb);
+
pci_unmap_rom(pdev, bios);
return 0;
diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
index fe72e1c225d..0f8e5f69ac7 100644
--- a/drivers/gpu/drm/i915/intel_bios.h
+++ b/drivers/gpu/drm/i915/intel_bios.h
@@ -381,6 +381,51 @@ struct bdb_sdvo_lvds_options {
} __attribute__((packed));
+#define BDB_DRIVER_FEATURE_NO_LVDS 0
+#define BDB_DRIVER_FEATURE_INT_LVDS 1
+#define BDB_DRIVER_FEATURE_SDVO_LVDS 2
+#define BDB_DRIVER_FEATURE_EDP 3
+
+struct bdb_driver_features {
+ u8 boot_dev_algorithm:1;
+ u8 block_display_switch:1;
+ u8 allow_display_switch:1;
+ u8 hotplug_dvo:1;
+ u8 dual_view_zoom:1;
+ u8 int15h_hook:1;
+ u8 sprite_in_clone:1;
+ u8 primary_lfp_id:1;
+
+ u16 boot_mode_x;
+ u16 boot_mode_y;
+ u8 boot_mode_bpp;
+ u8 boot_mode_refresh;
+
+ u16 enable_lfp_primary:1;
+ u16 selective_mode_pruning:1;
+ u16 dual_frequency:1;
+ u16 render_clock_freq:1; /* 0: high freq; 1: low freq */
+ u16 nt_clone_support:1;
+ u16 power_scheme_ui:1; /* 0: CUI; 1: 3rd party */
+ u16 sprite_display_assign:1; /* 0: secondary; 1: primary */
+ u16 cui_aspect_scaling:1;
+ u16 preserve_aspect_ratio:1;
+ u16 sdvo_device_power_down:1;
+ u16 crt_hotplug:1;
+ u16 lvds_config:2;
+ u16 tv_hotplug:1;
+ u16 hdmi_config:2;
+
+ u8 static_display:1;
+ u8 reserved2:7;
+ u16 legacy_crt_max_x;
+ u16 legacy_crt_max_y;
+ u8 legacy_crt_max_refresh;
+
+ u8 hdmi_termination;
+ u8 custom_vbt_version;
+} __attribute__((packed));
+
bool intel_init_bios(struct drm_device *dev);
/*
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index d6a1a6e5539..590f81c8f59 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -156,6 +156,9 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
temp = adpa = I915_READ(PCH_ADPA);
+ adpa &= ~ADPA_DAC_ENABLE;
+ I915_WRITE(PCH_ADPA, adpa);
+
adpa &= ~ADPA_CRT_HOTPLUG_MASK;
adpa |= (ADPA_CRT_HOTPLUG_PERIOD_128 |
@@ -169,13 +172,14 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
DRM_DEBUG("pch crt adpa 0x%x", adpa);
I915_WRITE(PCH_ADPA, adpa);
- /* This might not be needed as not specified in spec...*/
- udelay(1000);
+ while ((I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) != 0)
+ ;
/* Check the status to see if both blue and green are on now */
adpa = I915_READ(PCH_ADPA);
- if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) ==
- ADPA_CRT_HOTPLUG_MONITOR_COLOR)
+ adpa &= ADPA_CRT_HOTPLUG_MONITOR_MASK;
+ if ((adpa == ADPA_CRT_HOTPLUG_MONITOR_COLOR) ||
+ (adpa == ADPA_CRT_HOTPLUG_MONITOR_MONO))
ret = true;
else
ret = false;
@@ -504,6 +508,7 @@ void intel_crt_init(struct drm_device *dev)
{
struct drm_connector *connector;
struct intel_output *intel_output;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 i2c_reg;
intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
@@ -523,8 +528,12 @@ void intel_crt_init(struct drm_device *dev)
/* Set up the DDC bus. */
if (IS_IGDNG(dev))
i2c_reg = PCH_GPIOA;
- else
+ else {
i2c_reg = GPIOA;
+ /* Use VBT information for CRT DDC if available */
+ if (dev_priv->crt_ddc_bus != -1)
+ i2c_reg = dev_priv->crt_ddc_bus;
+ }
intel_output->ddc_bus = intel_i2c_create(dev, i2c_reg, "CRTDDC_A");
if (!intel_output->ddc_bus) {
dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
@@ -533,6 +542,10 @@ void intel_crt_init(struct drm_device *dev)
}
intel_output->type = INTEL_OUTPUT_ANALOG;
+ intel_output->clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
+ (1 << INTEL_ANALOG_CLONE_BIT) |
+ (1 << INTEL_SDVO_LVDS_CLONE_BIT);
+ intel_output->crtc_mask = (1 << 0) | (1 << 1);
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 508838ee31e..748ed50c55c 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -34,6 +34,8 @@
#include "drm_crtc_helper.h"
+#define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))
+
bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
static void intel_update_watermarks(struct drm_device *dev);
@@ -88,7 +90,7 @@ struct intel_limit {
#define I8XX_P2_SLOW 4
#define I8XX_P2_FAST 2
#define I8XX_P2_LVDS_SLOW 14
-#define I8XX_P2_LVDS_FAST 14 /* No fast option */
+#define I8XX_P2_LVDS_FAST 7
#define I8XX_P2_SLOW_LIMIT 165000
#define I9XX_DOT_MIN 20000
@@ -268,6 +270,9 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
static bool
intel_find_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc,
int target, int refclk, intel_clock_t *best_clock);
+static bool
+intel_find_pll_igdng_dp(const intel_limit_t *, struct drm_crtc *crtc,
+ int target, int refclk, intel_clock_t *best_clock);
static const intel_limit_t intel_limits_i8xx_dvo = {
.dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
@@ -598,6 +603,23 @@ bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
return false;
}
+struct drm_connector *
+intel_pipe_get_output (struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct drm_connector *l_entry, *ret = NULL;
+
+ list_for_each_entry(l_entry, &mode_config->connector_list, head) {
+ if (l_entry->encoder &&
+ l_entry->encoder->crtc == crtc) {
+ ret = l_entry;
+ break;
+ }
+ }
+ return ret;
+}
+
#define INTELPllInvalid(s) do { /* DRM_DEBUG(s); */ return false; } while (0)
/**
* Returns whether the given set of divisors are valid for a given refclk with
@@ -644,8 +666,8 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
intel_clock_t clock;
int err = target;
- if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
- (I915_READ(LVDS) & LVDS_PORT_EN) != 0) {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
+ (I915_READ(LVDS)) != 0) {
/*
* For LVDS, if the panel is on, just rely on its current
* settings for dual-channel. We haven't figured out how to
@@ -752,6 +774,30 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
}
static bool
+intel_find_pll_igdng_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
+ int target, int refclk, intel_clock_t *best_clock)
+{
+ struct drm_device *dev = crtc->dev;
+ intel_clock_t clock;
+ if (target < 200000) {
+ clock.n = 1;
+ clock.p1 = 2;
+ clock.p2 = 10;
+ clock.m1 = 12;
+ clock.m2 = 9;
+ } else {
+ clock.n = 2;
+ clock.p1 = 1;
+ clock.p2 = 10;
+ clock.m1 = 14;
+ clock.m2 = 8;
+ }
+ intel_clock(dev, refclk, &clock);
+ memcpy(best_clock, &clock, sizeof(intel_clock_t));
+ return true;
+}
+
+static bool
intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
int target, int refclk, intel_clock_t *best_clock)
{
@@ -763,6 +809,14 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
int err_most = 47;
found = false;
+ /* eDP has only 2 clock choice, no n/m/p setting */
+ if (HAS_eDP)
+ return true;
+
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT))
+ return intel_find_pll_igdng_dp(limit, crtc, target,
+ refclk, best_clock);
+
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
LVDS_CLKB_POWER_UP)
@@ -998,6 +1052,90 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
return 0;
}
+/* Disable the VGA plane that we never use */
+static void i915_disable_vga (struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u8 sr1;
+ u32 vga_reg;
+
+ if (IS_IGDNG(dev))
+ vga_reg = CPU_VGACNTRL;
+ else
+ vga_reg = VGACNTRL;
+
+ if (I915_READ(vga_reg) & VGA_DISP_DISABLE)
+ return;
+
+ I915_WRITE8(VGA_SR_INDEX, 1);
+ sr1 = I915_READ8(VGA_SR_DATA);
+ I915_WRITE8(VGA_SR_DATA, sr1 | (1 << 5));
+ udelay(100);
+
+ I915_WRITE(vga_reg, VGA_DISP_DISABLE);
+}
+
+static void igdng_disable_pll_edp (struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 dpa_ctl;
+
+ DRM_DEBUG("\n");
+ dpa_ctl = I915_READ(DP_A);
+ dpa_ctl &= ~DP_PLL_ENABLE;
+ I915_WRITE(DP_A, dpa_ctl);
+}
+
+static void igdng_enable_pll_edp (struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 dpa_ctl;
+
+ dpa_ctl = I915_READ(DP_A);
+ dpa_ctl |= DP_PLL_ENABLE;
+ I915_WRITE(DP_A, dpa_ctl);
+ udelay(200);
+}
+
+
+static void igdng_set_pll_edp (struct drm_crtc *crtc, int clock)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 dpa_ctl;
+
+ DRM_DEBUG("eDP PLL enable for clock %d\n", clock);
+ dpa_ctl = I915_READ(DP_A);
+ dpa_ctl &= ~DP_PLL_FREQ_MASK;
+
+ if (clock < 200000) {
+ u32 temp;
+ dpa_ctl |= DP_PLL_FREQ_160MHZ;
+ /* workaround for 160Mhz:
+ 1) program 0x4600c bits 15:0 = 0x8124
+ 2) program 0x46010 bit 0 = 1
+ 3) program 0x46034 bit 24 = 1
+ 4) program 0x64000 bit 14 = 1
+ */
+ temp = I915_READ(0x4600c);
+ temp &= 0xffff0000;
+ I915_WRITE(0x4600c, temp | 0x8124);
+
+ temp = I915_READ(0x46010);
+ I915_WRITE(0x46010, temp | 1);
+
+ temp = I915_READ(0x46034);
+ I915_WRITE(0x46034, temp | (1 << 24));
+ } else {
+ dpa_ctl |= DP_PLL_FREQ_270MHZ;
+ }
+ I915_WRITE(DP_A, dpa_ctl);
+
+ udelay(500);
+}
+
static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct drm_device *dev = crtc->dev;
@@ -1015,6 +1153,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR;
int transconf_reg = (pipe == 0) ? TRANSACONF : TRANSBCONF;
int pf_ctl_reg = (pipe == 0) ? PFA_CTL_1 : PFB_CTL_1;
+ int pf_win_size = (pipe == 0) ? PFA_WIN_SZ : PFB_WIN_SZ;
int cpu_htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
int cpu_hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
int cpu_hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
@@ -1028,7 +1167,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
int trans_vblank_reg = (pipe == 0) ? TRANS_VBLANK_A : TRANS_VBLANK_B;
int trans_vsync_reg = (pipe == 0) ? TRANS_VSYNC_A : TRANS_VSYNC_B;
u32 temp;
- int tries = 5, j;
+ int tries = 5, j, n;
/* XXX: When our outputs are all unaware of DPMS modes other than off
* and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
@@ -1038,27 +1177,32 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
DRM_DEBUG("crtc %d dpms on\n", pipe);
- /* enable PCH DPLL */
- temp = I915_READ(pch_dpll_reg);
- if ((temp & DPLL_VCO_ENABLE) == 0) {
- I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE);
- I915_READ(pch_dpll_reg);
- }
-
- /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
- temp = I915_READ(fdi_rx_reg);
- I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE |
- FDI_SEL_PCDCLK |
- FDI_DP_PORT_WIDTH_X4); /* default 4 lanes */
- I915_READ(fdi_rx_reg);
- udelay(200);
+ if (HAS_eDP) {
+ /* enable eDP PLL */
+ igdng_enable_pll_edp(crtc);
+ } else {
+ /* enable PCH DPLL */
+ temp = I915_READ(pch_dpll_reg);
+ if ((temp & DPLL_VCO_ENABLE) == 0) {
+ I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE);
+ I915_READ(pch_dpll_reg);
+ }
- /* Enable CPU FDI TX PLL, always on for IGDNG */
- temp = I915_READ(fdi_tx_reg);
- if ((temp & FDI_TX_PLL_ENABLE) == 0) {
- I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
- I915_READ(fdi_tx_reg);
- udelay(100);
+ /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
+ temp = I915_READ(fdi_rx_reg);
+ I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE |
+ FDI_SEL_PCDCLK |
+ FDI_DP_PORT_WIDTH_X4); /* default 4 lanes */
+ I915_READ(fdi_rx_reg);
+ udelay(200);
+
+ /* Enable CPU FDI TX PLL, always on for IGDNG */
+ temp = I915_READ(fdi_tx_reg);
+ if ((temp & FDI_TX_PLL_ENABLE) == 0) {
+ I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
+ I915_READ(fdi_tx_reg);
+ udelay(100);
+ }
}
/* Enable CPU pipe */
@@ -1077,122 +1221,126 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
}
- /* enable CPU FDI TX and PCH FDI RX */
- temp = I915_READ(fdi_tx_reg);
- temp |= FDI_TX_ENABLE;
- temp |= FDI_DP_PORT_WIDTH_X4; /* default */
- temp &= ~FDI_LINK_TRAIN_NONE;
- temp |= FDI_LINK_TRAIN_PATTERN_1;
- I915_WRITE(fdi_tx_reg, temp);
- I915_READ(fdi_tx_reg);
+ if (!HAS_eDP) {
+ /* enable CPU FDI TX and PCH FDI RX */
+ temp = I915_READ(fdi_tx_reg);
+ temp |= FDI_TX_ENABLE;
+ temp |= FDI_DP_PORT_WIDTH_X4; /* default */
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_PATTERN_1;
+ I915_WRITE(fdi_tx_reg, temp);
+ I915_READ(fdi_tx_reg);
- temp = I915_READ(fdi_rx_reg);
- temp &= ~FDI_LINK_TRAIN_NONE;
- temp |= FDI_LINK_TRAIN_PATTERN_1;
- I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE);
- I915_READ(fdi_rx_reg);
+ temp = I915_READ(fdi_rx_reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_PATTERN_1;
+ I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE);
+ I915_READ(fdi_rx_reg);
- udelay(150);
+ udelay(150);
- /* Train FDI. */
- /* umask FDI RX Interrupt symbol_lock and bit_lock bit
- for train result */
- temp = I915_READ(fdi_rx_imr_reg);
- temp &= ~FDI_RX_SYMBOL_LOCK;
- temp &= ~FDI_RX_BIT_LOCK;
- I915_WRITE(fdi_rx_imr_reg, temp);
- I915_READ(fdi_rx_imr_reg);
- udelay(150);
+ /* Train FDI. */
+ /* umask FDI RX Interrupt symbol_lock and bit_lock bit
+ for train result */
+ temp = I915_READ(fdi_rx_imr_reg);
+ temp &= ~FDI_RX_SYMBOL_LOCK;
+ temp &= ~FDI_RX_BIT_LOCK;
+ I915_WRITE(fdi_rx_imr_reg, temp);
+ I915_READ(fdi_rx_imr_reg);
+ udelay(150);
- temp = I915_READ(fdi_rx_iir_reg);
- DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+ temp = I915_READ(fdi_rx_iir_reg);
+ DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
- if ((temp & FDI_RX_BIT_LOCK) == 0) {
- for (j = 0; j < tries; j++) {
- temp = I915_READ(fdi_rx_iir_reg);
- DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
- if (temp & FDI_RX_BIT_LOCK)
- break;
- udelay(200);
- }
- if (j != tries)
+ if ((temp & FDI_RX_BIT_LOCK) == 0) {
+ for (j = 0; j < tries; j++) {
+ temp = I915_READ(fdi_rx_iir_reg);
+ DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+ if (temp & FDI_RX_BIT_LOCK)
+ break;
+ udelay(200);
+ }
+ if (j != tries)
+ I915_WRITE(fdi_rx_iir_reg,
+ temp | FDI_RX_BIT_LOCK);
+ else
+ DRM_DEBUG("train 1 fail\n");
+ } else {
I915_WRITE(fdi_rx_iir_reg,
temp | FDI_RX_BIT_LOCK);
- else
- DRM_DEBUG("train 1 fail\n");
- } else {
- I915_WRITE(fdi_rx_iir_reg,
- temp | FDI_RX_BIT_LOCK);
- DRM_DEBUG("train 1 ok 2!\n");
- }
- temp = I915_READ(fdi_tx_reg);
- temp &= ~FDI_LINK_TRAIN_NONE;
- temp |= FDI_LINK_TRAIN_PATTERN_2;
- I915_WRITE(fdi_tx_reg, temp);
-
- temp = I915_READ(fdi_rx_reg);
- temp &= ~FDI_LINK_TRAIN_NONE;
- temp |= FDI_LINK_TRAIN_PATTERN_2;
- I915_WRITE(fdi_rx_reg, temp);
+ DRM_DEBUG("train 1 ok 2!\n");
+ }
+ temp = I915_READ(fdi_tx_reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_PATTERN_2;
+ I915_WRITE(fdi_tx_reg, temp);
+
+ temp = I915_READ(fdi_rx_reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_PATTERN_2;
+ I915_WRITE(fdi_rx_reg, temp);
- udelay(150);
+ udelay(150);
- temp = I915_READ(fdi_rx_iir_reg);
- DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+ temp = I915_READ(fdi_rx_iir_reg);
+ DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
- if ((temp & FDI_RX_SYMBOL_LOCK) == 0) {
- for (j = 0; j < tries; j++) {
- temp = I915_READ(fdi_rx_iir_reg);
- DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
- if (temp & FDI_RX_SYMBOL_LOCK)
- break;
- udelay(200);
- }
- if (j != tries) {
+ if ((temp & FDI_RX_SYMBOL_LOCK) == 0) {
+ for (j = 0; j < tries; j++) {
+ temp = I915_READ(fdi_rx_iir_reg);
+ DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+ if (temp & FDI_RX_SYMBOL_LOCK)
+ break;
+ udelay(200);
+ }
+ if (j != tries) {
+ I915_WRITE(fdi_rx_iir_reg,
+ temp | FDI_RX_SYMBOL_LOCK);
+ DRM_DEBUG("train 2 ok 1!\n");
+ } else
+ DRM_DEBUG("train 2 fail\n");
+ } else {
I915_WRITE(fdi_rx_iir_reg,
temp | FDI_RX_SYMBOL_LOCK);
- DRM_DEBUG("train 2 ok 1!\n");
- } else
- DRM_DEBUG("train 2 fail\n");
- } else {
- I915_WRITE(fdi_rx_iir_reg, temp | FDI_RX_SYMBOL_LOCK);
- DRM_DEBUG("train 2 ok 2!\n");
- }
- DRM_DEBUG("train done\n");
+ DRM_DEBUG("train 2 ok 2!\n");
+ }
+ DRM_DEBUG("train done\n");
- /* set transcoder timing */
- I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg));
- I915_WRITE(trans_hblank_reg, I915_READ(cpu_hblank_reg));
- I915_WRITE(trans_hsync_reg, I915_READ(cpu_hsync_reg));
+ /* set transcoder timing */
+ I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg));
+ I915_WRITE(trans_hblank_reg, I915_READ(cpu_hblank_reg));
+ I915_WRITE(trans_hsync_reg, I915_READ(cpu_hsync_reg));
- I915_WRITE(trans_vtot_reg, I915_READ(cpu_vtot_reg));
- I915_WRITE(trans_vblank_reg, I915_READ(cpu_vblank_reg));
- I915_WRITE(trans_vsync_reg, I915_READ(cpu_vsync_reg));
+ I915_WRITE(trans_vtot_reg, I915_READ(cpu_vtot_reg));
+ I915_WRITE(trans_vblank_reg, I915_READ(cpu_vblank_reg));
+ I915_WRITE(trans_vsync_reg, I915_READ(cpu_vsync_reg));
- /* enable PCH transcoder */
- temp = I915_READ(transconf_reg);
- I915_WRITE(transconf_reg, temp | TRANS_ENABLE);
- I915_READ(transconf_reg);
+ /* enable PCH transcoder */
+ temp = I915_READ(transconf_reg);
+ I915_WRITE(transconf_reg, temp | TRANS_ENABLE);
+ I915_READ(transconf_reg);
- while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0)
- ;
+ while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0)
+ ;
- /* enable normal */
+ /* enable normal */
- temp = I915_READ(fdi_tx_reg);
- temp &= ~FDI_LINK_TRAIN_NONE;
- I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE |
- FDI_TX_ENHANCE_FRAME_ENABLE);
- I915_READ(fdi_tx_reg);
+ temp = I915_READ(fdi_tx_reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE |
+ FDI_TX_ENHANCE_FRAME_ENABLE);
+ I915_READ(fdi_tx_reg);
- temp = I915_READ(fdi_rx_reg);
- temp &= ~FDI_LINK_TRAIN_NONE;
- I915_WRITE(fdi_rx_reg, temp | FDI_LINK_TRAIN_NONE |
- FDI_RX_ENHANCE_FRAME_ENABLE);
- I915_READ(fdi_rx_reg);
+ temp = I915_READ(fdi_rx_reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ I915_WRITE(fdi_rx_reg, temp | FDI_LINK_TRAIN_NONE |
+ FDI_RX_ENHANCE_FRAME_ENABLE);
+ I915_READ(fdi_rx_reg);
- /* wait one idle pattern time */
- udelay(100);
+ /* wait one idle pattern time */
+ udelay(100);
+
+ }
intel_crtc_load_lut(crtc);
@@ -1200,8 +1348,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
case DRM_MODE_DPMS_OFF:
DRM_DEBUG("crtc %d dpms off\n", pipe);
- /* Disable the VGA plane that we never use */
- I915_WRITE(CPU_VGACNTRL, VGA_DISP_DISABLE);
+ i915_disable_vga(dev);
/* Disable display plane */
temp = I915_READ(dspcntr_reg);
@@ -1217,17 +1364,23 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
if ((temp & PIPEACONF_ENABLE) != 0) {
I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
I915_READ(pipeconf_reg);
+ n = 0;
/* wait for cpu pipe off, pipe state */
- while ((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) != 0)
- ;
+ while ((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) != 0) {
+ n++;
+ if (n < 60) {
+ udelay(500);
+ continue;
+ } else {
+ DRM_DEBUG("pipe %d off delay\n", pipe);
+ break;
+ }
+ }
} else
DRM_DEBUG("crtc %d is disabled\n", pipe);
- /* IGDNG-A : disable cpu panel fitter ? */
- temp = I915_READ(pf_ctl_reg);
- if ((temp & PF_ENABLE) != 0) {
- I915_WRITE(pf_ctl_reg, temp & ~PF_ENABLE);
- I915_READ(pf_ctl_reg);
+ if (HAS_eDP) {
+ igdng_disable_pll_edp(crtc);
}
/* disable CPU FDI tx and PCH FDI rx */
@@ -1239,6 +1392,8 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
I915_WRITE(fdi_rx_reg, temp & ~FDI_RX_ENABLE);
I915_READ(fdi_rx_reg);
+ udelay(100);
+
/* still set train pattern 1 */
temp = I915_READ(fdi_tx_reg);
temp &= ~FDI_LINK_TRAIN_NONE;
@@ -1250,14 +1405,25 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
temp |= FDI_LINK_TRAIN_PATTERN_1;
I915_WRITE(fdi_rx_reg, temp);
+ udelay(100);
+
/* disable PCH transcoder */
temp = I915_READ(transconf_reg);
if ((temp & TRANS_ENABLE) != 0) {
I915_WRITE(transconf_reg, temp & ~TRANS_ENABLE);
I915_READ(transconf_reg);
+ n = 0;
/* wait for PCH transcoder off, transcoder state */
- while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) != 0)
- ;
+ while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) != 0) {
+ n++;
+ if (n < 60) {
+ udelay(500);
+ continue;
+ } else {
+ DRM_DEBUG("transcoder %d off delay\n", pipe);
+ break;
+ }
+ }
}
/* disable PCH DPLL */
@@ -1275,6 +1441,22 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
I915_READ(fdi_rx_reg);
}
+ /* Disable CPU FDI TX PLL */
+ temp = I915_READ(fdi_tx_reg);
+ if ((temp & FDI_TX_PLL_ENABLE) != 0) {
+ I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_PLL_ENABLE);
+ I915_READ(fdi_tx_reg);
+ udelay(100);
+ }
+
+ /* Disable PF */
+ temp = I915_READ(pf_ctl_reg);
+ if ((temp & PF_ENABLE) != 0) {
+ I915_WRITE(pf_ctl_reg, temp & ~PF_ENABLE);
+ I915_READ(pf_ctl_reg);
+ }
+ I915_WRITE(pf_win_size, 0);
+
/* Wait for the clocks to turn off. */
udelay(150);
break;
@@ -1342,7 +1524,7 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
//intel_crtc_dpms_video(crtc, FALSE); TODO
/* Disable the VGA plane that we never use */
- I915_WRITE(VGACNTRL, VGA_DISP_DISABLE);
+ i915_disable_vga(dev);
/* Disable display plane */
temp = I915_READ(dspcntr_reg);
@@ -1623,48 +1805,72 @@ static struct intel_watermark_params igd_cursor_hplloff_wm = {
IGD_FIFO_LINE_SIZE
};
static struct intel_watermark_params i945_wm_info = {
- I915_FIFO_LINE_SIZE,
+ I945_FIFO_SIZE,
I915_MAX_WM,
1,
- 0,
- IGD_FIFO_LINE_SIZE
+ 2,
+ I915_FIFO_LINE_SIZE
};
static struct intel_watermark_params i915_wm_info = {
- I945_FIFO_SIZE,
+ I915_FIFO_SIZE,
I915_MAX_WM,
1,
- 0,
+ 2,
I915_FIFO_LINE_SIZE
};
static struct intel_watermark_params i855_wm_info = {
I855GM_FIFO_SIZE,
I915_MAX_WM,
1,
- 0,
+ 2,
I830_FIFO_LINE_SIZE
};
static struct intel_watermark_params i830_wm_info = {
I830_FIFO_SIZE,
I915_MAX_WM,
1,
- 0,
+ 2,
I830_FIFO_LINE_SIZE
};
+/**
+ * intel_calculate_wm - calculate watermark level
+ * @clock_in_khz: pixel clock
+ * @wm: chip FIFO params
+ * @pixel_size: display pixel size
+ * @latency_ns: memory latency for the platform
+ *
+ * Calculate the watermark level (the level at which the display plane will
+ * start fetching from memory again). Each chip has a different display
+ * FIFO size and allocation, so the caller needs to figure that out and pass
+ * in the correct intel_watermark_params structure.
+ *
+ * As the pixel clock runs, the FIFO will be drained at a rate that depends
+ * on the pixel size. When it reaches the watermark level, it'll start
+ * fetching FIFO line sized based chunks from memory until the FIFO fills
+ * past the watermark point. If the FIFO drains completely, a FIFO underrun
+ * will occur, and a display engine hang could result.
+ */
static unsigned long intel_calculate_wm(unsigned long clock_in_khz,
struct intel_watermark_params *wm,
int pixel_size,
unsigned long latency_ns)
{
- unsigned long bytes_required, wm_size;
+ long entries_required, wm_size;
+
+ entries_required = (clock_in_khz * pixel_size * latency_ns) / 1000000;
+ entries_required /= wm->cacheline_size;
+
+ DRM_DEBUG("FIFO entries required for mode: %d\n", entries_required);
- bytes_required = (clock_in_khz * pixel_size * latency_ns) / 1000000;
- bytes_required /= wm->cacheline_size;
- wm_size = wm->fifo_size - bytes_required - wm->guard_size;
+ wm_size = wm->fifo_size - (entries_required + wm->guard_size);
- if (wm_size > wm->max_wm)
+ DRM_DEBUG("FIFO watermark level: %d\n", wm_size);
+
+ /* Don't promote wm_size to unsigned... */
+ if (wm_size > (long)wm->max_wm)
wm_size = wm->max_wm;
- if (wm_size == 0)
+ if (wm_size <= 0)
wm_size = wm->default_wm;
return wm_size;
}
@@ -1799,8 +2005,54 @@ static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
return;
}
-const static int latency_ns = 5000; /* default for non-igd platforms */
+/*
+ * Latency for FIFO fetches is dependent on several factors:
+ * - memory configuration (speed, channels)
+ * - chipset
+ * - current MCH state
+ * It can be fairly high in some situations, so here we assume a fairly
+ * pessimal value. It's a tradeoff between extra memory fetches (if we
+ * set this value too high, the FIFO will fetch frequently to stay full)
+ * and power consumption (set it too low to save power and we might see
+ * FIFO underruns and display "flicker").
+ *
+ * A value of 5us seems to be a good balance; safe for very low end
+ * platforms but not overly aggressive on lower latency configs.
+ */
+const static int latency_ns = 5000;
+static int intel_get_fifo_size(struct drm_device *dev, int plane)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ uint32_t dsparb = I915_READ(DSPARB);
+ int size;
+
+ if (IS_I9XX(dev)) {
+ if (plane == 0)
+ size = dsparb & 0x7f;
+ else
+ size = ((dsparb >> DSPARB_CSTART_SHIFT) & 0x7f) -
+ (dsparb & 0x7f);
+ } else if (IS_I85X(dev)) {
+ if (plane == 0)
+ size = dsparb & 0x1ff;
+ else
+ size = ((dsparb >> DSPARB_BEND_SHIFT) & 0x1ff) -
+ (dsparb & 0x1ff);
+ size >>= 1; /* Convert to cachelines */
+ } else if (IS_845G(dev)) {
+ size = dsparb & 0x7f;
+ size >>= 2; /* Convert to cachelines */
+ } else {
+ size = dsparb & 0x7f;
+ size >>= 1; /* Convert to cachelines */
+ }
+
+ DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A",
+ size);
+
+ return size;
+}
static void i965_update_wm(struct drm_device *dev)
{
@@ -1817,101 +2069,89 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
int planeb_clock, int sr_hdisplay, int pixel_size)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- uint32_t fwater_lo = I915_READ(FW_BLC) & MM_FIFO_WATERMARK;
- uint32_t fwater_hi = I915_READ(FW_BLC2) & LM_FIFO_WATERMARK;
- int bsize, asize, cwm, bwm = 1, awm = 1, srwm = 1;
- uint32_t dsparb = I915_READ(DSPARB);
- int planea_entries, planeb_entries;
- struct intel_watermark_params *wm_params;
+ uint32_t fwater_lo;
+ uint32_t fwater_hi;
+ int total_size, cacheline_size, cwm, srwm = 1;
+ int planea_wm, planeb_wm;
+ struct intel_watermark_params planea_params, planeb_params;
unsigned long line_time_us;
int sr_clock, sr_entries = 0;
+ /* Create copies of the base settings for each pipe */
if (IS_I965GM(dev) || IS_I945GM(dev))
- wm_params = &i945_wm_info;
+ planea_params = planeb_params = i945_wm_info;
else if (IS_I9XX(dev))
- wm_params = &i915_wm_info;
+ planea_params = planeb_params = i915_wm_info;
else
- wm_params = &i855_wm_info;
+ planea_params = planeb_params = i855_wm_info;
- planea_entries = intel_calculate_wm(planea_clock, wm_params,
- pixel_size, latency_ns);
- planeb_entries = intel_calculate_wm(planeb_clock, wm_params,
- pixel_size, latency_ns);
+ /* Grab a couple of global values before we overwrite them */
+ total_size = planea_params.fifo_size;
+ cacheline_size = planea_params.cacheline_size;
- DRM_DEBUG("FIFO entries - A: %d, B: %d\n", planea_entries,
- planeb_entries);
+ /* Update per-plane FIFO sizes */
+ planea_params.fifo_size = intel_get_fifo_size(dev, 0);
+ planeb_params.fifo_size = intel_get_fifo_size(dev, 1);
- if (IS_I9XX(dev)) {
- asize = dsparb & 0x7f;
- bsize = (dsparb >> DSPARB_CSTART_SHIFT) & 0x7f;
- } else {
- asize = dsparb & 0x1ff;
- bsize = (dsparb >> DSPARB_BEND_SHIFT) & 0x1ff;
- }
- DRM_DEBUG("FIFO size - A: %d, B: %d\n", asize, bsize);
-
- /* Two extra entries for padding */
- awm = asize - (planea_entries + 2);
- bwm = bsize - (planeb_entries + 2);
-
- /* Sanity check against potentially bad FIFO allocations */
- if (awm <= 0) {
- /* pipe is on but has too few FIFO entries */
- if (planea_entries != 0)
- DRM_DEBUG("plane A needs more FIFO entries\n");
- awm = 1;
- }
- if (bwm <= 0) {
- if (planeb_entries != 0)
- DRM_DEBUG("plane B needs more FIFO entries\n");
- bwm = 1;
- }
+ planea_wm = intel_calculate_wm(planea_clock, &planea_params,
+ pixel_size, latency_ns);
+ planeb_wm = intel_calculate_wm(planeb_clock, &planeb_params,
+ pixel_size, latency_ns);
+ DRM_DEBUG("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm);
/*
* Overlay gets an aggressive default since video jitter is bad.
*/
cwm = 2;
- /* Calc sr entries for one pipe configs */
- if (!planea_clock || !planeb_clock) {
+ /* Calc sr entries for one plane configs */
+ if (sr_hdisplay && (!planea_clock || !planeb_clock)) {
+ /* self-refresh has much higher latency */
+ const static int sr_latency_ns = 6000;
+
sr_clock = planea_clock ? planea_clock : planeb_clock;
- line_time_us = (sr_hdisplay * 1000) / sr_clock;
- sr_entries = (((latency_ns / line_time_us) + 1) * pixel_size *
- sr_hdisplay) / 1000;
- sr_entries = roundup(sr_entries / wm_params->cacheline_size, 1);
- if (sr_entries < wm_params->fifo_size)
- srwm = wm_params->fifo_size - sr_entries;
+ line_time_us = ((sr_hdisplay * 1000) / sr_clock);
+
+ /* Use ns/us then divide to preserve precision */
+ sr_entries = (((sr_latency_ns / line_time_us) + 1) *
+ pixel_size * sr_hdisplay) / 1000;
+ sr_entries = roundup(sr_entries / cacheline_size, 1);
+ DRM_DEBUG("self-refresh entries: %d\n", sr_entries);
+ srwm = total_size - sr_entries;
+ if (srwm < 0)
+ srwm = 1;
+ if (IS_I9XX(dev))
+ I915_WRITE(FW_BLC_SELF, (srwm & 0x3f));
}
DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
- awm, bwm, cwm, srwm);
+ planea_wm, planeb_wm, cwm, srwm);
+
+ fwater_lo = ((planeb_wm & 0x3f) << 16) | (planea_wm & 0x3f);
+ fwater_hi = (cwm & 0x1f);
- fwater_lo = fwater_lo | ((bwm & 0x3f) << 16) | (awm & 0x3f);
- fwater_hi = fwater_hi | (cwm & 0x1f);
+ /* Set request length to 8 cachelines per fetch */
+ fwater_lo = fwater_lo | (1 << 24) | (1 << 8);
+ fwater_hi = fwater_hi | (1 << 8);
I915_WRITE(FW_BLC, fwater_lo);
I915_WRITE(FW_BLC2, fwater_hi);
- if (IS_I9XX(dev))
- I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN | (srwm & 0x3f));
}
static void i830_update_wm(struct drm_device *dev, int planea_clock,
int pixel_size)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- uint32_t dsparb = I915_READ(DSPARB);
- uint32_t fwater_lo = I915_READ(FW_BLC) & MM_FIFO_WATERMARK;
- unsigned int asize, awm;
- int planea_entries;
+ uint32_t fwater_lo = I915_READ(FW_BLC) & ~0xfff;
+ int planea_wm;
- planea_entries = intel_calculate_wm(planea_clock, &i830_wm_info,
- pixel_size, latency_ns);
+ i830_wm_info.fifo_size = intel_get_fifo_size(dev, 0);
- asize = dsparb & 0x7f;
+ planea_wm = intel_calculate_wm(planea_clock, &i830_wm_info,
+ pixel_size, latency_ns);
+ fwater_lo |= (3<<8) | planea_wm;
- awm = asize - planea_entries;
-
- fwater_lo = fwater_lo | awm;
+ DRM_DEBUG("Setting FIFO watermarks - A: %d\n", planea_wm);
I915_WRITE(FW_BLC, fwater_lo);
}
@@ -1984,7 +2224,7 @@ static void intel_update_watermarks(struct drm_device *dev)
if (enabled <= 0)
return;
- /* Single pipe configs can enable self refresh */
+ /* Single plane configs can enable self refresh */
if (enabled == 1 && IS_IGD(dev))
igd_enable_cxsr(dev, sr_clock, pixel_size);
else if (IS_IGD(dev))
@@ -2028,6 +2268,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
u32 dpll = 0, fp = 0, dspcntr, pipeconf;
bool ok, is_sdvo = false, is_dvo = false;
bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false;
+ bool is_edp = false;
struct drm_mode_config *mode_config = &dev->mode_config;
struct drm_connector *connector;
const intel_limit_t *limit;
@@ -2043,6 +2284,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
int lvds_reg = LVDS;
u32 temp;
int sdvo_pixel_multiply;
+ int target_clock;
drm_vblank_pre_modeset(dev, pipe);
@@ -2074,6 +2316,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
case INTEL_OUTPUT_DISPLAYPORT:
is_dp = true;
break;
+ case INTEL_OUTPUT_EDP:
+ is_edp = true;
+ break;
}
num_outputs++;
@@ -2125,11 +2370,29 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
}
/* FDI link */
- if (IS_IGDNG(dev))
- igdng_compute_m_n(3, 4, /* lane num 4 */
- adjusted_mode->clock,
- 270000, /* lane clock */
- &m_n);
+ if (IS_IGDNG(dev)) {
+ int lane, link_bw;
+ /* eDP doesn't require FDI link, so just set DP M/N
+ according to current link config */
+ if (is_edp) {
+ struct drm_connector *edp;
+ target_clock = mode->clock;
+ edp = intel_pipe_get_output(crtc);
+ intel_edp_link_config(to_intel_output(edp),
+ &lane, &link_bw);
+ } else {
+ /* DP over FDI requires target mode clock
+ instead of link clock */
+ if (is_dp)
+ target_clock = mode->clock;
+ else
+ target_clock = adjusted_mode->clock;
+ lane = 4;
+ link_bw = 270000;
+ }
+ igdng_compute_m_n(3, lane, target_clock,
+ link_bw, &m_n);
+ }
if (IS_IGD(dev))
fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
@@ -2147,7 +2410,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
if (is_sdvo) {
dpll |= DPLL_DVO_HIGH_SPEED;
sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
- if (IS_I945G(dev) || IS_I945GM(dev))
+ if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
else if (IS_IGDNG(dev))
dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
@@ -2250,29 +2513,15 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
dpll_reg = pch_dpll_reg;
}
- if (dpll & DPLL_VCO_ENABLE) {
+ if (is_edp) {
+ igdng_disable_pll_edp(crtc);
+ } else if ((dpll & DPLL_VCO_ENABLE)) {
I915_WRITE(fp_reg, fp);
I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
I915_READ(dpll_reg);
udelay(150);
}
- if (IS_IGDNG(dev)) {
- /* enable PCH clock reference source */
- /* XXX need to change the setting for other outputs */
- u32 temp;
- temp = I915_READ(PCH_DREF_CONTROL);
- temp &= ~DREF_NONSPREAD_SOURCE_MASK;
- temp |= DREF_NONSPREAD_CK505_ENABLE;
- temp &= ~DREF_SSC_SOURCE_MASK;
- temp |= DREF_SSC_SOURCE_ENABLE;
- temp &= ~DREF_SSC1_ENABLE;
- /* if no eDP, disable source output to CPU */
- temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
- temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
- I915_WRITE(PCH_DREF_CONTROL, temp);
- }
-
/* The LVDS pin pair needs to be on before the DPLLs are enabled.
* This is an exception to the general rule that mode_set doesn't turn
* things on.
@@ -2304,23 +2553,25 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
if (is_dp)
intel_dp_set_m_n(crtc, mode, adjusted_mode);
- I915_WRITE(fp_reg, fp);
- I915_WRITE(dpll_reg, dpll);
- I915_READ(dpll_reg);
- /* Wait for the clocks to stabilize. */
- udelay(150);
-
- if (IS_I965G(dev) && !IS_IGDNG(dev)) {
- sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
- I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
- ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
- } else {
- /* write it again -- the BIOS does, after all */
+ if (!is_edp) {
+ I915_WRITE(fp_reg, fp);
I915_WRITE(dpll_reg, dpll);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+
+ if (IS_I965G(dev) && !IS_IGDNG(dev)) {
+ sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
+ ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
+ } else {
+ /* write it again -- the BIOS does, after all */
+ I915_WRITE(dpll_reg, dpll);
+ }
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
}
- I915_READ(dpll_reg);
- /* Wait for the clocks to stabilize. */
- udelay(150);
I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
((adjusted_mode->crtc_htotal - 1) << 16));
@@ -2350,10 +2601,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
I915_WRITE(link_m1_reg, m_n.link_m);
I915_WRITE(link_n1_reg, m_n.link_n);
- /* enable FDI RX PLL too */
- temp = I915_READ(fdi_rx_reg);
- I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
- udelay(200);
+ if (is_edp) {
+ igdng_set_pll_edp(crtc, adjusted_mode->clock);
+ } else {
+ /* enable FDI RX PLL too */
+ temp = I915_READ(fdi_rx_reg);
+ I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
+ udelay(200);
+ }
}
I915_WRITE(pipeconf_reg, pipeconf);
@@ -2929,7 +3184,7 @@ static int intel_connector_clones(struct drm_device *dev, int type_mask)
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct intel_output *intel_output = to_intel_output(connector);
- if (type_mask & (1 << intel_output->type))
+ if (type_mask & intel_output->clone_mask)
index_mask |= (1 << entry);
entry++;
}
@@ -2951,12 +3206,17 @@ static void intel_setup_outputs(struct drm_device *dev)
if (IS_IGDNG(dev)) {
int found;
+ if (IS_MOBILE(dev) && (I915_READ(DP_A) & DP_DETECTED))
+ intel_dp_init(dev, DP_A);
+
if (I915_READ(HDMIB) & PORT_DETECTED) {
/* check SDVOB */
/* found = intel_sdvo_init(dev, HDMIB); */
found = 0;
if (!found)
intel_hdmi_init(dev, HDMIB);
+ if (!found && (I915_READ(PCH_DP_B) & DP_DETECTED))
+ intel_dp_init(dev, PCH_DP_B);
}
if (I915_READ(HDMIC) & PORT_DETECTED)
@@ -2965,31 +3225,37 @@ static void intel_setup_outputs(struct drm_device *dev)
if (I915_READ(HDMID) & PORT_DETECTED)
intel_hdmi_init(dev, HDMID);
+ if (I915_READ(PCH_DP_C) & DP_DETECTED)
+ intel_dp_init(dev, PCH_DP_C);
+
+ if (I915_READ(PCH_DP_D) & DP_DETECTED)
+ intel_dp_init(dev, PCH_DP_D);
+
} else if (IS_I9XX(dev)) {
- int found;
- u32 reg;
+ bool found = false;
if (I915_READ(SDVOB) & SDVO_DETECTED) {
found = intel_sdvo_init(dev, SDVOB);
if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
intel_hdmi_init(dev, SDVOB);
+
if (!found && SUPPORTS_INTEGRATED_DP(dev))
intel_dp_init(dev, DP_B);
}
/* Before G4X SDVOC doesn't have its own detect register */
- if (IS_G4X(dev))
- reg = SDVOC;
- else
- reg = SDVOB;
- if (I915_READ(reg) & SDVO_DETECTED) {
+ if (I915_READ(SDVOB) & SDVO_DETECTED)
found = intel_sdvo_init(dev, SDVOC);
- if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
+
+ if (!found && (I915_READ(SDVOC) & SDVO_DETECTED)) {
+
+ if (SUPPORTS_INTEGRATED_HDMI(dev))
intel_hdmi_init(dev, SDVOC);
- if (!found && SUPPORTS_INTEGRATED_DP(dev))
+ if (SUPPORTS_INTEGRATED_DP(dev))
intel_dp_init(dev, DP_C);
}
+
if (SUPPORTS_INTEGRATED_DP(dev) && (I915_READ(DP_D) & DP_DETECTED))
intel_dp_init(dev, DP_D);
} else
@@ -3001,47 +3267,10 @@ static void intel_setup_outputs(struct drm_device *dev)
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct intel_output *intel_output = to_intel_output(connector);
struct drm_encoder *encoder = &intel_output->enc;
- int crtc_mask = 0, clone_mask = 0;
- /* valid crtcs */
- switch(intel_output->type) {
- case INTEL_OUTPUT_HDMI:
- crtc_mask = ((1 << 0)|
- (1 << 1));
- clone_mask = ((1 << INTEL_OUTPUT_HDMI));
- break;
- case INTEL_OUTPUT_DVO:
- case INTEL_OUTPUT_SDVO:
- crtc_mask = ((1 << 0)|
- (1 << 1));
- clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
- (1 << INTEL_OUTPUT_DVO) |
- (1 << INTEL_OUTPUT_SDVO));
- break;
- case INTEL_OUTPUT_ANALOG:
- crtc_mask = ((1 << 0)|
- (1 << 1));
- clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
- (1 << INTEL_OUTPUT_DVO) |
- (1 << INTEL_OUTPUT_SDVO));
- break;
- case INTEL_OUTPUT_LVDS:
- crtc_mask = (1 << 1);
- clone_mask = (1 << INTEL_OUTPUT_LVDS);
- break;
- case INTEL_OUTPUT_TVOUT:
- crtc_mask = ((1 << 0) |
- (1 << 1));
- clone_mask = (1 << INTEL_OUTPUT_TVOUT);
- break;
- case INTEL_OUTPUT_DISPLAYPORT:
- crtc_mask = ((1 << 0) |
- (1 << 1));
- clone_mask = (1 << INTEL_OUTPUT_DISPLAYPORT);
- break;
- }
- encoder->possible_crtcs = crtc_mask;
- encoder->possible_clones = intel_connector_clones(dev, clone_mask);
+ encoder->possible_crtcs = intel_output->crtc_mask;
+ encoder->possible_clones = intel_connector_clones(dev,
+ intel_output->clone_mask);
}
}
@@ -3148,6 +3377,9 @@ void intel_modeset_init(struct drm_device *dev)
if (IS_I965G(dev)) {
dev->mode_config.max_width = 8192;
dev->mode_config.max_height = 8192;
+ } else if (IS_I9XX(dev)) {
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
} else {
dev->mode_config.max_width = 2048;
dev->mode_config.max_height = 2048;
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 6770ae88370..2b914d73207 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -40,6 +40,8 @@
#define DP_LINK_CONFIGURATION_SIZE 9
+#define IS_eDP(i) ((i)->type == INTEL_OUTPUT_EDP)
+
struct intel_dp_priv {
uint32_t output_reg;
uint32_t DP;
@@ -63,6 +65,19 @@ intel_dp_link_train(struct intel_output *intel_output, uint32_t DP,
static void
intel_dp_link_down(struct intel_output *intel_output, uint32_t DP);
+void
+intel_edp_link_config (struct intel_output *intel_output,
+ int *lane_num, int *link_bw)
+{
+ struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+
+ *lane_num = dp_priv->lane_count;
+ if (dp_priv->link_bw == DP_LINK_BW_1_62)
+ *link_bw = 162000;
+ else if (dp_priv->link_bw == DP_LINK_BW_2_7)
+ *link_bw = 270000;
+}
+
static int
intel_dp_max_lane_count(struct intel_output *intel_output)
{
@@ -206,7 +221,13 @@ intel_dp_aux_ch(struct intel_output *intel_output,
* and would like to run at 2MHz. So, take the
* hrawclk value and divide by 2 and use that
*/
- aux_clock_divider = intel_hrawclk(dev) / 2;
+ if (IS_eDP(intel_output))
+ aux_clock_divider = 225; /* eDP input clock at 450Mhz */
+ else if (IS_IGDNG(dev))
+ aux_clock_divider = 62; /* IGDNG: input clock fixed at 125Mhz */
+ else
+ aux_clock_divider = intel_hrawclk(dev) / 2;
+
/* Must try at least 3 times according to DP spec */
for (try = 0; try < 5; try++) {
/* Load the send data into the aux channel data registers */
@@ -236,7 +257,7 @@ intel_dp_aux_ch(struct intel_output *intel_output,
}
/* Clear done status and any errors */
- I915_WRITE(ch_ctl, (ctl |
+ I915_WRITE(ch_ctl, (status |
DP_AUX_CH_CTL_DONE |
DP_AUX_CH_CTL_TIME_OUT_ERROR |
DP_AUX_CH_CTL_RECEIVE_ERROR));
@@ -295,7 +316,7 @@ intel_dp_aux_native_write(struct intel_output *intel_output,
return -1;
msg[0] = AUX_NATIVE_WRITE << 4;
msg[1] = address >> 8;
- msg[2] = address;
+ msg[2] = address & 0xff;
msg[3] = send_bytes - 1;
memcpy(&msg[4], send, send_bytes);
msg_bytes = send_bytes + 4;
@@ -387,8 +408,8 @@ intel_dp_i2c_init(struct intel_output *intel_output, const char *name)
memset(&dp_priv->adapter, '\0', sizeof (dp_priv->adapter));
dp_priv->adapter.owner = THIS_MODULE;
dp_priv->adapter.class = I2C_CLASS_DDC;
- strncpy (dp_priv->adapter.name, name, sizeof dp_priv->adapter.name - 1);
- dp_priv->adapter.name[sizeof dp_priv->adapter.name - 1] = '\0';
+ strncpy (dp_priv->adapter.name, name, sizeof(dp_priv->adapter.name) - 1);
+ dp_priv->adapter.name[sizeof(dp_priv->adapter.name) - 1] = '\0';
dp_priv->adapter.algo_data = &dp_priv->algo;
dp_priv->adapter.dev.parent = &intel_output->base.kdev;
@@ -493,22 +514,40 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
intel_dp_compute_m_n(3, lane_count,
mode->clock, adjusted_mode->clock, &m_n);
- if (intel_crtc->pipe == 0) {
- I915_WRITE(PIPEA_GMCH_DATA_M,
- ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
- m_n.gmch_m);
- I915_WRITE(PIPEA_GMCH_DATA_N,
- m_n.gmch_n);
- I915_WRITE(PIPEA_DP_LINK_M, m_n.link_m);
- I915_WRITE(PIPEA_DP_LINK_N, m_n.link_n);
+ if (IS_IGDNG(dev)) {
+ if (intel_crtc->pipe == 0) {
+ I915_WRITE(TRANSA_DATA_M1,
+ ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+ m_n.gmch_m);
+ I915_WRITE(TRANSA_DATA_N1, m_n.gmch_n);
+ I915_WRITE(TRANSA_DP_LINK_M1, m_n.link_m);
+ I915_WRITE(TRANSA_DP_LINK_N1, m_n.link_n);
+ } else {
+ I915_WRITE(TRANSB_DATA_M1,
+ ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+ m_n.gmch_m);
+ I915_WRITE(TRANSB_DATA_N1, m_n.gmch_n);
+ I915_WRITE(TRANSB_DP_LINK_M1, m_n.link_m);
+ I915_WRITE(TRANSB_DP_LINK_N1, m_n.link_n);
+ }
} else {
- I915_WRITE(PIPEB_GMCH_DATA_M,
- ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
- m_n.gmch_m);
- I915_WRITE(PIPEB_GMCH_DATA_N,
- m_n.gmch_n);
- I915_WRITE(PIPEB_DP_LINK_M, m_n.link_m);
- I915_WRITE(PIPEB_DP_LINK_N, m_n.link_n);
+ if (intel_crtc->pipe == 0) {
+ I915_WRITE(PIPEA_GMCH_DATA_M,
+ ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+ m_n.gmch_m);
+ I915_WRITE(PIPEA_GMCH_DATA_N,
+ m_n.gmch_n);
+ I915_WRITE(PIPEA_DP_LINK_M, m_n.link_m);
+ I915_WRITE(PIPEA_DP_LINK_N, m_n.link_n);
+ } else {
+ I915_WRITE(PIPEB_GMCH_DATA_M,
+ ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+ m_n.gmch_m);
+ I915_WRITE(PIPEB_GMCH_DATA_N,
+ m_n.gmch_n);
+ I915_WRITE(PIPEB_DP_LINK_M, m_n.link_m);
+ I915_WRITE(PIPEB_DP_LINK_N, m_n.link_n);
+ }
}
}
@@ -556,8 +595,38 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
if (intel_crtc->pipe == 1)
dp_priv->DP |= DP_PIPEB_SELECT;
+
+ if (IS_eDP(intel_output)) {
+ /* don't miss out required setting for eDP */
+ dp_priv->DP |= DP_PLL_ENABLE;
+ if (adjusted_mode->clock < 200000)
+ dp_priv->DP |= DP_PLL_FREQ_160MHZ;
+ else
+ dp_priv->DP |= DP_PLL_FREQ_270MHZ;
+ }
}
+static void igdng_edp_backlight_on (struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pp;
+
+ DRM_DEBUG("\n");
+ pp = I915_READ(PCH_PP_CONTROL);
+ pp |= EDP_BLC_ENABLE;
+ I915_WRITE(PCH_PP_CONTROL, pp);
+}
+
+static void igdng_edp_backlight_off (struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pp;
+
+ DRM_DEBUG("\n");
+ pp = I915_READ(PCH_PP_CONTROL);
+ pp &= ~EDP_BLC_ENABLE;
+ I915_WRITE(PCH_PP_CONTROL, pp);
+}
static void
intel_dp_dpms(struct drm_encoder *encoder, int mode)
@@ -569,11 +638,17 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
uint32_t dp_reg = I915_READ(dp_priv->output_reg);
if (mode != DRM_MODE_DPMS_ON) {
- if (dp_reg & DP_PORT_EN)
+ if (dp_reg & DP_PORT_EN) {
intel_dp_link_down(intel_output, dp_priv->DP);
+ if (IS_eDP(intel_output))
+ igdng_edp_backlight_off(dev);
+ }
} else {
- if (!(dp_reg & DP_PORT_EN))
+ if (!(dp_reg & DP_PORT_EN)) {
intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
+ if (IS_eDP(intel_output))
+ igdng_edp_backlight_on(dev);
+ }
}
dp_priv->dpms_mode = mode;
}
@@ -935,6 +1010,23 @@ intel_dp_link_down(struct intel_output *intel_output, uint32_t DP)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+ DRM_DEBUG("\n");
+
+ if (IS_eDP(intel_output)) {
+ DP &= ~DP_PLL_ENABLE;
+ I915_WRITE(dp_priv->output_reg, DP);
+ POSTING_READ(dp_priv->output_reg);
+ udelay(100);
+ }
+
+ DP &= ~DP_LINK_TRAIN_MASK;
+ I915_WRITE(dp_priv->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE);
+ POSTING_READ(dp_priv->output_reg);
+
+ udelay(17000);
+
+ if (IS_eDP(intel_output))
+ DP |= DP_LINK_TRAIN_OFF;
I915_WRITE(dp_priv->output_reg, DP & ~DP_PORT_EN);
POSTING_READ(dp_priv->output_reg);
}
@@ -978,6 +1070,24 @@ intel_dp_check_link_status(struct intel_output *intel_output)
intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
}
+static enum drm_connector_status
+igdng_dp_detect(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+ enum drm_connector_status status;
+
+ status = connector_status_disconnected;
+ if (intel_dp_aux_native_read(intel_output,
+ 0x000, dp_priv->dpcd,
+ sizeof (dp_priv->dpcd)) == sizeof (dp_priv->dpcd))
+ {
+ if (dp_priv->dpcd[0] != 0)
+ status = connector_status_connected;
+ }
+ return status;
+}
+
/**
* Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection.
*
@@ -996,6 +1106,9 @@ intel_dp_detect(struct drm_connector *connector)
dp_priv->has_audio = false;
+ if (IS_IGDNG(dev))
+ return igdng_dp_detect(connector);
+
temp = I915_READ(PORT_HOTPLUG_EN);
I915_WRITE(PORT_HOTPLUG_EN,
@@ -1039,11 +1152,27 @@ intel_dp_detect(struct drm_connector *connector)
static int intel_dp_get_modes(struct drm_connector *connector)
{
struct intel_output *intel_output = to_intel_output(connector);
+ struct drm_device *dev = intel_output->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
/* We should parse the EDID data and find out if it has an audio sink
*/
- return intel_ddc_get_modes(intel_output);
+ ret = intel_ddc_get_modes(intel_output);
+ if (ret)
+ return ret;
+
+ /* if eDP has no EDID, try to use fixed panel mode from VBT */
+ if (IS_eDP(intel_output)) {
+ if (dev_priv->panel_fixed_mode != NULL) {
+ struct drm_display_mode *mode;
+ mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
+ drm_mode_probed_add(connector, mode);
+ return 1;
+ }
+ }
+ return 0;
}
static void
@@ -1106,6 +1235,7 @@ intel_dp_init(struct drm_device *dev, int output_reg)
struct drm_connector *connector;
struct intel_output *intel_output;
struct intel_dp_priv *dp_priv;
+ const char *name = NULL;
intel_output = kcalloc(sizeof(struct intel_output) +
sizeof(struct intel_dp_priv), 1, GFP_KERNEL);
@@ -1119,8 +1249,23 @@ intel_dp_init(struct drm_device *dev, int output_reg)
DRM_MODE_CONNECTOR_DisplayPort);
drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs);
- intel_output->type = INTEL_OUTPUT_DISPLAYPORT;
-
+ if (output_reg == DP_A)
+ intel_output->type = INTEL_OUTPUT_EDP;
+ else
+ intel_output->type = INTEL_OUTPUT_DISPLAYPORT;
+
+ if (output_reg == DP_B)
+ intel_output->clone_mask = (1 << INTEL_DP_B_CLONE_BIT);
+ else if (output_reg == DP_C)
+ intel_output->clone_mask = (1 << INTEL_DP_C_CLONE_BIT);
+ else if (output_reg == DP_D)
+ intel_output->clone_mask = (1 << INTEL_DP_D_CLONE_BIT);
+
+ if (IS_eDP(intel_output)) {
+ intel_output->crtc_mask = (1 << 1);
+ intel_output->clone_mask = (1 << INTEL_EDP_CLONE_BIT);
+ } else
+ intel_output->crtc_mask = (1 << 0) | (1 << 1);
connector->interlace_allowed = true;
connector->doublescan_allowed = 0;
@@ -1139,12 +1284,41 @@ intel_dp_init(struct drm_device *dev, int output_reg)
drm_sysfs_connector_add(connector);
/* Set up the DDC bus. */
- intel_dp_i2c_init(intel_output,
- (output_reg == DP_B) ? "DPDDC-B" :
- (output_reg == DP_C) ? "DPDDC-C" : "DPDDC-D");
+ switch (output_reg) {
+ case DP_A:
+ name = "DPDDC-A";
+ break;
+ case DP_B:
+ case PCH_DP_B:
+ name = "DPDDC-B";
+ break;
+ case DP_C:
+ case PCH_DP_C:
+ name = "DPDDC-C";
+ break;
+ case DP_D:
+ case PCH_DP_D:
+ name = "DPDDC-D";
+ break;
+ }
+
+ intel_dp_i2c_init(intel_output, name);
+
intel_output->ddc_bus = &dp_priv->adapter;
intel_output->hot_plug = intel_dp_hot_plug;
+ if (output_reg == DP_A) {
+ /* initialize panel mode from VBT if available for eDP */
+ if (dev_priv->lfp_lvds_vbt_mode) {
+ dev_priv->panel_fixed_mode =
+ drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
+ if (dev_priv->panel_fixed_mode) {
+ dev_priv->panel_fixed_mode->type |=
+ DRM_MODE_TYPE_PREFERRED;
+ }
+ }
+ }
+
/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
* 0xd. Failure to do so will result in spurious interrupts being
* generated on the port when a cable is not attached.
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 004541c935a..26a6227c15f 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -55,6 +55,26 @@
#define INTEL_OUTPUT_TVOUT 5
#define INTEL_OUTPUT_HDMI 6
#define INTEL_OUTPUT_DISPLAYPORT 7
+#define INTEL_OUTPUT_EDP 8
+
+/* Intel Pipe Clone Bit */
+#define INTEL_HDMIB_CLONE_BIT 1
+#define INTEL_HDMIC_CLONE_BIT 2
+#define INTEL_HDMID_CLONE_BIT 3
+#define INTEL_HDMIE_CLONE_BIT 4
+#define INTEL_HDMIF_CLONE_BIT 5
+#define INTEL_SDVO_NON_TV_CLONE_BIT 6
+#define INTEL_SDVO_TV_CLONE_BIT 7
+#define INTEL_SDVO_LVDS_CLONE_BIT 8
+#define INTEL_ANALOG_CLONE_BIT 9
+#define INTEL_TV_CLONE_BIT 10
+#define INTEL_DP_B_CLONE_BIT 11
+#define INTEL_DP_C_CLONE_BIT 12
+#define INTEL_DP_D_CLONE_BIT 13
+#define INTEL_LVDS_CLONE_BIT 14
+#define INTEL_DVO_TMDS_CLONE_BIT 15
+#define INTEL_DVO_LVDS_CLONE_BIT 16
+#define INTEL_EDP_CLONE_BIT 17
#define INTEL_DVO_CHIP_NONE 0
#define INTEL_DVO_CHIP_LVDS 1
@@ -85,6 +105,8 @@ struct intel_output {
bool needs_tv_clock;
void *dev_priv;
void (*hot_plug)(struct intel_output *);
+ int crtc_mask;
+ int clone_mask;
};
struct intel_crtc {
@@ -121,6 +143,8 @@ extern void intel_dp_init(struct drm_device *dev, int dp_reg);
void
intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
+extern void intel_edp_link_config (struct intel_output *, int *, int *);
+
extern void intel_crtc_load_lut(struct drm_crtc *crtc);
extern void intel_encoder_prepare (struct drm_encoder *encoder);
diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
index 13bff20930e..a4d2606de77 100644
--- a/drivers/gpu/drm/i915/intel_dvo.c
+++ b/drivers/gpu/drm/i915/intel_dvo.c
@@ -435,14 +435,20 @@ void intel_dvo_init(struct drm_device *dev)
continue;
intel_output->type = INTEL_OUTPUT_DVO;
+ intel_output->crtc_mask = (1 << 0) | (1 << 1);
switch (dvo->type) {
case INTEL_DVO_CHIP_TMDS:
+ intel_output->clone_mask =
+ (1 << INTEL_DVO_TMDS_CLONE_BIT) |
+ (1 << INTEL_ANALOG_CLONE_BIT);
drm_connector_init(dev, connector,
&intel_dvo_connector_funcs,
DRM_MODE_CONNECTOR_DVII);
encoder_type = DRM_MODE_ENCODER_TMDS;
break;
case INTEL_DVO_CHIP_LVDS:
+ intel_output->clone_mask =
+ (1 << INTEL_DVO_LVDS_CLONE_BIT);
drm_connector_init(dev, connector,
&intel_dvo_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 9e30daae37d..fa304e13601 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -130,16 +130,17 @@ static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,
}
static enum drm_connector_status
-intel_hdmi_edid_detect(struct drm_connector *connector)
+intel_hdmi_detect(struct drm_connector *connector)
{
struct intel_output *intel_output = to_intel_output(connector);
struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
struct edid *edid = NULL;
enum drm_connector_status status = connector_status_disconnected;
+ hdmi_priv->has_hdmi_sink = false;
edid = drm_get_edid(&intel_output->base,
intel_output->ddc_bus);
- hdmi_priv->has_hdmi_sink = false;
+
if (edid) {
if (edid->input & DRM_EDID_INPUT_DIGITAL) {
status = connector_status_connected;
@@ -148,65 +149,8 @@ intel_hdmi_edid_detect(struct drm_connector *connector)
intel_output->base.display_info.raw_edid = NULL;
kfree(edid);
}
- return status;
-}
-
-static enum drm_connector_status
-igdng_hdmi_detect(struct drm_connector *connector)
-{
- struct intel_output *intel_output = to_intel_output(connector);
- struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
-
- /* FIXME hotplug detect */
- hdmi_priv->has_hdmi_sink = false;
- return intel_hdmi_edid_detect(connector);
-}
-
-static enum drm_connector_status
-intel_hdmi_detect(struct drm_connector *connector)
-{
- struct drm_device *dev = connector->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_output *intel_output = to_intel_output(connector);
- struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv;
- u32 temp, bit;
-
- if (IS_IGDNG(dev))
- return igdng_hdmi_detect(connector);
-
- temp = I915_READ(PORT_HOTPLUG_EN);
-
- switch (hdmi_priv->sdvox_reg) {
- case SDVOB:
- temp |= HDMIB_HOTPLUG_INT_EN;
- break;
- case SDVOC:
- temp |= HDMIC_HOTPLUG_INT_EN;
- break;
- default:
- return connector_status_unknown;
- }
-
- I915_WRITE(PORT_HOTPLUG_EN, temp);
-
- POSTING_READ(PORT_HOTPLUG_EN);
-
- switch (hdmi_priv->sdvox_reg) {
- case SDVOB:
- bit = HDMIB_HOTPLUG_INT_STATUS;
- break;
- case SDVOC:
- bit = HDMIC_HOTPLUG_INT_STATUS;
- break;
- default:
- return connector_status_unknown;
- }
-
- if ((I915_READ(PORT_HOTPLUG_STAT) & bit) != 0)
- return intel_hdmi_edid_detect(connector);
- else
- return connector_status_disconnected;
+ return status;
}
static int intel_hdmi_get_modes(struct drm_connector *connector)
@@ -286,22 +230,28 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
+ intel_output->crtc_mask = (1 << 0) | (1 << 1);
/* Set up the DDC bus. */
- if (sdvox_reg == SDVOB)
+ if (sdvox_reg == SDVOB) {
+ intel_output->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT);
intel_output->ddc_bus = intel_i2c_create(dev, GPIOE, "HDMIB");
- else if (sdvox_reg == SDVOC)
+ } else if (sdvox_reg == SDVOC) {
+ intel_output->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT);
intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "HDMIC");
- else if (sdvox_reg == HDMIB)
+ } else if (sdvox_reg == HDMIB) {
+ intel_output->clone_mask = (1 << INTEL_HDMID_CLONE_BIT);
intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOE,
"HDMIB");
- else if (sdvox_reg == HDMIC)
+ } else if (sdvox_reg == HDMIC) {
+ intel_output->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT);
intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOD,
"HDMIC");
- else if (sdvox_reg == HDMID)
+ } else if (sdvox_reg == HDMID) {
+ intel_output->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT);
intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOF,
"HDMID");
-
+ }
if (!intel_output->ddc_bus)
goto err_connector;
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 9ab38efffec..8df02ef8926 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -780,6 +780,14 @@ static const struct dmi_system_id intel_no_lvds[] = {
},
{
.callback = intel_no_lvds_dmi_callback,
+ .ident = "AOpen Mini PC MP915",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
+ DMI_MATCH(DMI_BOARD_NAME, "i915GMx-F"),
+ },
+ },
+ {
+ .callback = intel_no_lvds_dmi_callback,
.ident = "Aopen i945GTt-VFA",
.matches = {
DMI_MATCH(DMI_PRODUCT_VERSION, "AO00001JW"),
@@ -884,6 +892,10 @@ void intel_lvds_init(struct drm_device *dev)
if (IS_IGDNG(dev)) {
if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0)
return;
+ if (dev_priv->edp_support) {
+ DRM_DEBUG("disable LVDS for eDP support\n");
+ return;
+ }
gpio = PCH_GPIOC;
}
@@ -904,6 +916,8 @@ void intel_lvds_init(struct drm_device *dev)
drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
intel_output->type = INTEL_OUTPUT_LVDS;
+ intel_output->clone_mask = (1 << INTEL_LVDS_CLONE_BIT);
+ intel_output->crtc_mask = (1 << 1);
drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs);
drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs);
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 4f0c30948bc..d3b74ba62b4 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -31,6 +31,7 @@
#include "drm.h"
#include "drm_crtc.h"
#include "intel_drv.h"
+#include "drm_edid.h"
#include "i915_drm.h"
#include "i915_drv.h"
#include "intel_sdvo_regs.h"
@@ -55,6 +56,12 @@ struct intel_sdvo_priv {
/* Pixel clock limitations reported by the SDVO device, in kHz */
int pixel_clock_min, pixel_clock_max;
+ /*
+ * For multiple function SDVO device,
+ * this is for current attached outputs.
+ */
+ uint16_t attached_output;
+
/**
* This is set if we're going to treat the device as TV-out.
*
@@ -114,6 +121,9 @@ struct intel_sdvo_priv {
u32 save_SDVOX;
};
+static bool
+intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags);
+
/**
* Writes the SDVOB or SDVOC with the given value, but always writes both
* SDVOB and SDVOC to work around apparent hardware issues (according to
@@ -1435,41 +1445,96 @@ void intel_sdvo_set_hotplug(struct drm_connector *connector, int on)
intel_sdvo_read_response(intel_output, &response, 2);
}
-static void
-intel_sdvo_hdmi_sink_detect(struct drm_connector *connector)
+static bool
+intel_sdvo_multifunc_encoder(struct intel_output *intel_output)
+{
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int caps = 0;
+
+ if (sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1))
+ caps++;
+ if (sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1))
+ caps++;
+ if (sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_SVID0 | SDVO_OUTPUT_SVID1))
+ caps++;
+ if (sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_CVBS1))
+ caps++;
+ if (sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_YPRPB0 | SDVO_OUTPUT_YPRPB1))
+ caps++;
+
+ if (sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_SCART0 | SDVO_OUTPUT_SCART1))
+ caps++;
+
+ if (sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1))
+ caps++;
+
+ return (caps > 1);
+}
+
+enum drm_connector_status
+intel_sdvo_hdmi_sink_detect(struct drm_connector *connector, u16 response)
{
struct intel_output *intel_output = to_intel_output(connector);
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ enum drm_connector_status status = connector_status_connected;
struct edid *edid = NULL;
edid = drm_get_edid(&intel_output->base,
intel_output->ddc_bus);
if (edid != NULL) {
- sdvo_priv->is_hdmi = drm_detect_hdmi_monitor(edid);
+ /* Don't report the output as connected if it's a DVI-I
+ * connector with a non-digital EDID coming out.
+ */
+ if (response & (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) {
+ if (edid->input & DRM_EDID_INPUT_DIGITAL)
+ sdvo_priv->is_hdmi =
+ drm_detect_hdmi_monitor(edid);
+ else
+ status = connector_status_disconnected;
+ }
+
kfree(edid);
intel_output->base.display_info.raw_edid = NULL;
- }
+
+ } else if (response & (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1))
+ status = connector_status_disconnected;
+
+ return status;
}
static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connector)
{
- u8 response[2];
+ uint16_t response;
u8 status;
struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0);
status = intel_sdvo_read_response(intel_output, &response, 2);
- DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]);
+ DRM_DEBUG("SDVO response %d %d\n", response & 0xff, response >> 8);
if (status != SDVO_CMD_STATUS_SUCCESS)
return connector_status_unknown;
- if ((response[0] != 0) || (response[1] != 0)) {
- intel_sdvo_hdmi_sink_detect(connector);
- return connector_status_connected;
- } else
+ if (response == 0)
return connector_status_disconnected;
+
+ if (intel_sdvo_multifunc_encoder(intel_output) &&
+ sdvo_priv->attached_output != response) {
+ if (sdvo_priv->controlled_output != response &&
+ intel_sdvo_output_setup(intel_output, response) != true)
+ return connector_status_unknown;
+ sdvo_priv->attached_output = response;
+ }
+ return intel_sdvo_hdmi_sink_detect(connector, response);
}
static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
@@ -1866,16 +1931,112 @@ intel_sdvo_get_slave_addr(struct drm_device *dev, int output_device)
return 0x72;
}
+static bool
+intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags)
+{
+ struct drm_connector *connector = &intel_output->base;
+ struct drm_encoder *encoder = &intel_output->enc;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ bool ret = true, registered = false;
+
+ sdvo_priv->is_tv = false;
+ intel_output->needs_tv_clock = false;
+ sdvo_priv->is_lvds = false;
+
+ if (device_is_registered(&connector->kdev)) {
+ drm_sysfs_connector_remove(connector);
+ registered = true;
+ }
+
+ if (flags &
+ (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) {
+ if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0)
+ sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS0;
+ else
+ sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS1;
+
+ encoder->encoder_type = DRM_MODE_ENCODER_TMDS;
+ connector->connector_type = DRM_MODE_CONNECTOR_DVID;
+
+ if (intel_sdvo_get_supp_encode(intel_output,
+ &sdvo_priv->encode) &&
+ intel_sdvo_get_digital_encoding_mode(intel_output) &&
+ sdvo_priv->is_hdmi) {
+ /* enable hdmi encoding mode if supported */
+ intel_sdvo_set_encode(intel_output, SDVO_ENCODE_HDMI);
+ intel_sdvo_set_colorimetry(intel_output,
+ SDVO_COLORIMETRY_RGB256);
+ connector->connector_type = DRM_MODE_CONNECTOR_HDMIA;
+ intel_output->clone_mask =
+ (1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
+ (1 << INTEL_ANALOG_CLONE_BIT);
+ }
+ } else if (flags & SDVO_OUTPUT_SVID0) {
+
+ sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0;
+ encoder->encoder_type = DRM_MODE_ENCODER_TVDAC;
+ connector->connector_type = DRM_MODE_CONNECTOR_SVIDEO;
+ sdvo_priv->is_tv = true;
+ intel_output->needs_tv_clock = true;
+ intel_output->clone_mask = 1 << INTEL_SDVO_TV_CLONE_BIT;
+ } else if (flags & SDVO_OUTPUT_RGB0) {
+
+ sdvo_priv->controlled_output = SDVO_OUTPUT_RGB0;
+ encoder->encoder_type = DRM_MODE_ENCODER_DAC;
+ connector->connector_type = DRM_MODE_CONNECTOR_VGA;
+ intel_output->clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
+ (1 << INTEL_ANALOG_CLONE_BIT);
+ } else if (flags & SDVO_OUTPUT_RGB1) {
+
+ sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1;
+ encoder->encoder_type = DRM_MODE_ENCODER_DAC;
+ connector->connector_type = DRM_MODE_CONNECTOR_VGA;
+ } else if (flags & SDVO_OUTPUT_LVDS0) {
+
+ sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0;
+ encoder->encoder_type = DRM_MODE_ENCODER_LVDS;
+ connector->connector_type = DRM_MODE_CONNECTOR_LVDS;
+ sdvo_priv->is_lvds = true;
+ intel_output->clone_mask = (1 << INTEL_ANALOG_CLONE_BIT) |
+ (1 << INTEL_SDVO_LVDS_CLONE_BIT);
+ } else if (flags & SDVO_OUTPUT_LVDS1) {
+
+ sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS1;
+ encoder->encoder_type = DRM_MODE_ENCODER_LVDS;
+ connector->connector_type = DRM_MODE_CONNECTOR_LVDS;
+ sdvo_priv->is_lvds = true;
+ intel_output->clone_mask = (1 << INTEL_ANALOG_CLONE_BIT) |
+ (1 << INTEL_SDVO_LVDS_CLONE_BIT);
+ } else {
+
+ unsigned char bytes[2];
+
+ sdvo_priv->controlled_output = 0;
+ memcpy(bytes, &sdvo_priv->caps.output_flags, 2);
+ DRM_DEBUG_KMS(I915_SDVO,
+ "%s: Unknown SDVO output type (0x%02x%02x)\n",
+ SDVO_NAME(sdvo_priv),
+ bytes[0], bytes[1]);
+ ret = false;
+ }
+ intel_output->crtc_mask = (1 << 0) | (1 << 1);
+
+ if (ret && registered)
+ ret = drm_sysfs_connector_add(connector) == 0 ? true : false;
+
+
+ return ret;
+
+}
+
bool intel_sdvo_init(struct drm_device *dev, int output_device)
{
struct drm_connector *connector;
struct intel_output *intel_output;
struct intel_sdvo_priv *sdvo_priv;
- int connector_type;
u8 ch[0x40];
int i;
- int encoder_type;
intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL);
if (!intel_output) {
@@ -1925,88 +2086,28 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
intel_output->ddc_bus->algo = &intel_sdvo_i2c_bit_algo;
/* In defaut case sdvo lvds is false */
- sdvo_priv->is_lvds = false;
intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps);
- if (sdvo_priv->caps.output_flags &
- (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) {
- if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0)
- sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS0;
- else
- sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS1;
-
- encoder_type = DRM_MODE_ENCODER_TMDS;
- connector_type = DRM_MODE_CONNECTOR_DVID;
-
- if (intel_sdvo_get_supp_encode(intel_output,
- &sdvo_priv->encode) &&
- intel_sdvo_get_digital_encoding_mode(intel_output) &&
- sdvo_priv->is_hdmi) {
- /* enable hdmi encoding mode if supported */
- intel_sdvo_set_encode(intel_output, SDVO_ENCODE_HDMI);
- intel_sdvo_set_colorimetry(intel_output,
- SDVO_COLORIMETRY_RGB256);
- connector_type = DRM_MODE_CONNECTOR_HDMIA;
- }
- }
- else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_SVID0)
- {
- sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0;
- encoder_type = DRM_MODE_ENCODER_TVDAC;
- connector_type = DRM_MODE_CONNECTOR_SVIDEO;
- sdvo_priv->is_tv = true;
- intel_output->needs_tv_clock = true;
- }
- else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0)
- {
- sdvo_priv->controlled_output = SDVO_OUTPUT_RGB0;
- encoder_type = DRM_MODE_ENCODER_DAC;
- connector_type = DRM_MODE_CONNECTOR_VGA;
- }
- else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1)
- {
- sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1;
- encoder_type = DRM_MODE_ENCODER_DAC;
- connector_type = DRM_MODE_CONNECTOR_VGA;
- }
- else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS0)
- {
- sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0;
- encoder_type = DRM_MODE_ENCODER_LVDS;
- connector_type = DRM_MODE_CONNECTOR_LVDS;
- sdvo_priv->is_lvds = true;
- }
- else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS1)
- {
- sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS1;
- encoder_type = DRM_MODE_ENCODER_LVDS;
- connector_type = DRM_MODE_CONNECTOR_LVDS;
- sdvo_priv->is_lvds = true;
- }
- else
- {
- unsigned char bytes[2];
-
- sdvo_priv->controlled_output = 0;
- memcpy (bytes, &sdvo_priv->caps.output_flags, 2);
- DRM_DEBUG_KMS(I915_SDVO,
- "%s: Unknown SDVO output type (0x%02x%02x)\n",
- SDVO_NAME(sdvo_priv),
- bytes[0], bytes[1]);
- encoder_type = DRM_MODE_ENCODER_NONE;
- connector_type = DRM_MODE_CONNECTOR_Unknown;
+ if (intel_sdvo_output_setup(intel_output,
+ sdvo_priv->caps.output_flags) != true) {
+ DRM_DEBUG("SDVO output failed to setup on SDVO%c\n",
+ output_device == SDVOB ? 'B' : 'C');
goto err_i2c;
}
+
connector = &intel_output->base;
drm_connector_init(dev, connector, &intel_sdvo_connector_funcs,
- connector_type);
+ connector->connector_type);
+
drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs);
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
- drm_encoder_init(dev, &intel_output->enc, &intel_sdvo_enc_funcs, encoder_type);
+ drm_encoder_init(dev, &intel_output->enc,
+ &intel_sdvo_enc_funcs, intel_output->enc.encoder_type);
+
drm_encoder_helper_add(&intel_output->enc, &intel_sdvo_helper_funcs);
drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index a43c98e3f07..5b1c9e9fdba 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1490,6 +1490,27 @@ static struct input_res {
{"1920x1080", 1920, 1080},
};
+/*
+ * Chose preferred mode according to line number of TV format
+ */
+static void
+intel_tv_chose_preferred_modes(struct drm_connector *connector,
+ struct drm_display_mode *mode_ptr)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output);
+
+ if (tv_mode->nbr_end < 480 && mode_ptr->vdisplay == 480)
+ mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
+ else if (tv_mode->nbr_end > 480) {
+ if (tv_mode->progressive == true && tv_mode->nbr_end < 720) {
+ if (mode_ptr->vdisplay == 720)
+ mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
+ } else if (mode_ptr->vdisplay == 1080)
+ mode_ptr->type |= DRM_MODE_TYPE_PREFERRED;
+ }
+}
+
/**
* Stub get_modes function.
*
@@ -1544,6 +1565,7 @@ intel_tv_get_modes(struct drm_connector *connector)
mode_ptr->clock = (int) tmp;
mode_ptr->type = DRM_MODE_TYPE_DRIVER;
+ intel_tv_chose_preferred_modes(connector, mode_ptr);
drm_mode_probed_add(connector, mode_ptr);
count++;
}
@@ -1696,6 +1718,7 @@ intel_tv_init(struct drm_device *dev)
if (!intel_output) {
return;
}
+
connector = &intel_output->base;
drm_connector_init(dev, connector, &intel_tv_connector_funcs,
@@ -1707,6 +1730,8 @@ intel_tv_init(struct drm_device *dev)
drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
tv_priv = (struct intel_tv_priv *)(intel_output + 1);
intel_output->type = INTEL_OUTPUT_TVOUT;
+ intel_output->crtc_mask = (1 << 0) | (1 << 1);
+ intel_output->clone_mask = (1 << INTEL_TV_CLONE_BIT);
intel_output->enc.possible_crtcs = ((1 << 0) | (1 << 1));
intel_output->enc.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
intel_output->dev_priv = tv_priv;
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 05a44896dff..68e728e8be4 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -254,6 +254,72 @@ void r100_mc_fini(struct radeon_device *rdev)
/*
+ * Interrupts
+ */
+int r100_irq_set(struct radeon_device *rdev)
+{
+ uint32_t tmp = 0;
+
+ if (rdev->irq.sw_int) {
+ tmp |= RADEON_SW_INT_ENABLE;
+ }
+ if (rdev->irq.crtc_vblank_int[0]) {
+ tmp |= RADEON_CRTC_VBLANK_MASK;
+ }
+ if (rdev->irq.crtc_vblank_int[1]) {
+ tmp |= RADEON_CRTC2_VBLANK_MASK;
+ }
+ WREG32(RADEON_GEN_INT_CNTL, tmp);
+ return 0;
+}
+
+static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
+{
+ uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS);
+ uint32_t irq_mask = RADEON_SW_INT_TEST | RADEON_CRTC_VBLANK_STAT |
+ RADEON_CRTC2_VBLANK_STAT;
+
+ if (irqs) {
+ WREG32(RADEON_GEN_INT_STATUS, irqs);
+ }
+ return irqs & irq_mask;
+}
+
+int r100_irq_process(struct radeon_device *rdev)
+{
+ uint32_t status;
+
+ status = r100_irq_ack(rdev);
+ if (!status) {
+ return IRQ_NONE;
+ }
+ while (status) {
+ /* SW interrupt */
+ if (status & RADEON_SW_INT_TEST) {
+ radeon_fence_process(rdev);
+ }
+ /* Vertical blank interrupts */
+ if (status & RADEON_CRTC_VBLANK_STAT) {
+ drm_handle_vblank(rdev->ddev, 0);
+ }
+ if (status & RADEON_CRTC2_VBLANK_STAT) {
+ drm_handle_vblank(rdev->ddev, 1);
+ }
+ status = r100_irq_ack(rdev);
+ }
+ return IRQ_HANDLED;
+}
+
+u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc)
+{
+ if (crtc == 0)
+ return RREG32(RADEON_CRTC_CRNT_FRAME);
+ else
+ return RREG32(RADEON_CRTC2_CRNT_FRAME);
+}
+
+
+/*
* Fence emission
*/
void r100_fence_ring_emit(struct radeon_device *rdev,
@@ -722,13 +788,14 @@ int r100_cs_packet_parse(struct radeon_cs_parser *p,
unsigned idx)
{
struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx];
- uint32_t header = ib_chunk->kdata[idx];
+ uint32_t header;
if (idx >= ib_chunk->length_dw) {
DRM_ERROR("Can not parse packet at %d after CS end %d !\n",
idx, ib_chunk->length_dw);
return -EINVAL;
}
+ header = ib_chunk->kdata[idx];
pkt->idx = idx;
pkt->type = CP_PACKET_GET_TYPE(header);
pkt->count = CP_PACKET_GET_COUNT(header);
@@ -1024,6 +1091,16 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
tmp |= tile_flags;
ib[idx] = tmp;
break;
+ case RADEON_RB3D_ZPASS_ADDR:
+ r = r100_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("No reloc for ib[%d]=0x%04X\n",
+ idx, reg);
+ r100_cs_dump_packet(p, pkt);
+ return r;
+ }
+ ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset);
+ break;
default:
/* FIXME: we don't want to allow anyothers packet */
break;
@@ -1555,26 +1632,6 @@ void r100_pll_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
r100_pll_errata_after_data(rdev);
}
-uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg)
-{
- if (reg < 0x10000)
- return readl(((void __iomem *)rdev->rmmio) + reg);
- else {
- writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
- return readl(((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
- }
-}
-
-void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
-{
- if (reg < 0x10000)
- writel(v, ((void __iomem *)rdev->rmmio) + reg);
- else {
- writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
- writel(v, ((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
- }
-}
-
int r100_init(struct radeon_device *rdev)
{
return 0;
diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c
index 9c8d41534a5..051bca6e3a4 100644
--- a/drivers/gpu/drm/radeon/r300.c
+++ b/drivers/gpu/drm/radeon/r300.c
@@ -83,8 +83,8 @@ void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev)
WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp | RADEON_PCIE_TX_GART_INVALIDATE_TLB);
(void)RREG32_PCIE(RADEON_PCIE_TX_GART_CNTL);
WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp);
- mb();
}
+ mb();
}
int rv370_pcie_gart_enable(struct radeon_device *rdev)
@@ -448,6 +448,7 @@ void r300_gpu_init(struct radeon_device *rdev)
/* rv350,rv370,rv380 */
rdev->num_gb_pipes = 1;
}
+ rdev->num_z_pipes = 1;
gb_tile_config = (R300_ENABLE_TILING | R300_TILE_SIZE_16);
switch (rdev->num_gb_pipes) {
case 2:
@@ -486,7 +487,8 @@ void r300_gpu_init(struct radeon_device *rdev)
printk(KERN_WARNING "Failed to wait MC idle while "
"programming pipes. Bad things might happen.\n");
}
- DRM_INFO("radeon: %d pipes initialized.\n", rdev->num_gb_pipes);
+ DRM_INFO("radeon: %d quad pipes, %d Z pipes initialized.\n",
+ rdev->num_gb_pipes, rdev->num_z_pipes);
}
int r300_ga_reset(struct radeon_device *rdev)
@@ -593,27 +595,6 @@ void r300_vram_info(struct radeon_device *rdev)
/*
- * Indirect registers accessor
- */
-uint32_t rv370_pcie_rreg(struct radeon_device *rdev, uint32_t reg)
-{
- uint32_t r;
-
- WREG8(RADEON_PCIE_INDEX, ((reg) & 0xff));
- (void)RREG32(RADEON_PCIE_INDEX);
- r = RREG32(RADEON_PCIE_DATA);
- return r;
-}
-
-void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
-{
- WREG8(RADEON_PCIE_INDEX, ((reg) & 0xff));
- (void)RREG32(RADEON_PCIE_INDEX);
- WREG32(RADEON_PCIE_DATA, (v));
- (void)RREG32(RADEON_PCIE_DATA);
-}
-
-/*
* PCIE Lanes
*/
@@ -1014,7 +995,7 @@ static const unsigned r300_reg_safe_bm[159] = {
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0xFFFF0000, 0xFFFFFFFF, 0xFF80FFFF,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x0003FC01, 0xFFFFFFF8, 0xFE800B19,
+ 0x0003FC01, 0xFFFFFCF8, 0xFF800B19,
};
static int r300_packet0_check(struct radeon_cs_parser *p,
@@ -1403,6 +1384,21 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
tmp = (ib_chunk->kdata[idx] >> 22) & 0xF;
track->textures[i].txdepth = tmp;
break;
+ case R300_ZB_ZPASS_ADDR:
+ r = r100_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("No reloc for ib[%d]=0x%04X\n",
+ idx, reg);
+ r100_cs_dump_packet(p, pkt);
+ return r;
+ }
+ ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset);
+ break;
+ case 0x4be8:
+ /* valid register only on RV530 */
+ if (p->rdev->family == CHIP_RV530)
+ break;
+ /* fallthrough do not move */
default:
printk(KERN_ERR "Forbidden register 0x%04X in cs at %d\n",
reg, idx);
diff --git a/drivers/gpu/drm/radeon/r420.c b/drivers/gpu/drm/radeon/r420.c
index dea497a979f..97426a6f370 100644
--- a/drivers/gpu/drm/radeon/r420.c
+++ b/drivers/gpu/drm/radeon/r420.c
@@ -165,7 +165,18 @@ void r420_pipes_init(struct radeon_device *rdev)
printk(KERN_WARNING "Failed to wait GUI idle while "
"programming pipes. Bad things might happen.\n");
}
- DRM_INFO("radeon: %d pipes initialized.\n", rdev->num_gb_pipes);
+
+ if (rdev->family == CHIP_RV530) {
+ tmp = RREG32(RV530_GB_PIPE_SELECT2);
+ if ((tmp & 3) == 3)
+ rdev->num_z_pipes = 2;
+ else
+ rdev->num_z_pipes = 1;
+ } else
+ rdev->num_z_pipes = 1;
+
+ DRM_INFO("radeon: %d quad pipes, %d z pipes initialized.\n",
+ rdev->num_gb_pipes, rdev->num_z_pipes);
}
void r420_gpu_init(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/r500_reg.h b/drivers/gpu/drm/radeon/r500_reg.h
index 036691b38cb..e1d5e0331e1 100644
--- a/drivers/gpu/drm/radeon/r500_reg.h
+++ b/drivers/gpu/drm/radeon/r500_reg.h
@@ -350,6 +350,7 @@
#define AVIVO_D1CRTC_BLANK_CONTROL 0x6084
#define AVIVO_D1CRTC_INTERLACE_CONTROL 0x6088
#define AVIVO_D1CRTC_INTERLACE_STATUS 0x608c
+#define AVIVO_D1CRTC_FRAME_COUNT 0x60a4
#define AVIVO_D1CRTC_STEREO_CONTROL 0x60c4
/* master controls */
@@ -438,14 +439,15 @@
# define AVIVO_DC_LB_DISP1_END_ADR_SHIFT 4
# define AVIVO_DC_LB_DISP1_END_ADR_MASK 0x7ff
-#define R500_DxMODE_INT_MASK 0x6540
-#define R500_D1MODE_INT_MASK (1<<0)
-#define R500_D2MODE_INT_MASK (1<<8)
-
#define AVIVO_D1MODE_DATA_FORMAT 0x6528
# define AVIVO_D1MODE_INTERLEAVE_EN (1 << 0)
#define AVIVO_D1MODE_DESKTOP_HEIGHT 0x652C
+#define AVIVO_D1MODE_VBLANK_STATUS 0x6534
+# define AVIVO_VBLANK_ACK (1 << 4)
#define AVIVO_D1MODE_VLINE_START_END 0x6538
+#define AVIVO_DxMODE_INT_MASK 0x6540
+# define AVIVO_D1MODE_INT_MASK (1 << 0)
+# define AVIVO_D2MODE_INT_MASK (1 << 8)
#define AVIVO_D1MODE_VIEWPORT_START 0x6580
#define AVIVO_D1MODE_VIEWPORT_SIZE 0x6584
#define AVIVO_D1MODE_EXT_OVERSCAN_LEFT_RIGHT 0x6588
@@ -475,6 +477,7 @@
#define AVIVO_D2CRTC_BLANK_CONTROL 0x6884
#define AVIVO_D2CRTC_INTERLACE_CONTROL 0x6888
#define AVIVO_D2CRTC_INTERLACE_STATUS 0x688c
+#define AVIVO_D2CRTC_FRAME_COUNT 0x68a4
#define AVIVO_D2CRTC_STEREO_CONTROL 0x68c4
#define AVIVO_D2GRPH_ENABLE 0x6900
@@ -497,6 +500,7 @@
#define AVIVO_D2CUR_SIZE 0x6c10
#define AVIVO_D2CUR_POSITION 0x6c14
+#define AVIVO_D2MODE_VBLANK_STATUS 0x6d34
#define AVIVO_D2MODE_VLINE_START_END 0x6d38
#define AVIVO_D2MODE_VIEWPORT_START 0x6d80
#define AVIVO_D2MODE_VIEWPORT_SIZE 0x6d84
@@ -748,4 +752,8 @@
# define AVIVO_I2C_EN (1 << 0)
# define AVIVO_I2C_RESET (1 << 8)
+#define AVIVO_DISP_INTERRUPT_STATUS 0x7edc
+# define AVIVO_D1_VBLANK_INTERRUPT (1 << 4)
+# define AVIVO_D2_VBLANK_INTERRUPT (1 << 5)
+
#endif
diff --git a/drivers/gpu/drm/radeon/r520.c b/drivers/gpu/drm/radeon/r520.c
index 09fb0b6ec7d..ebd6b0f7bdf 100644
--- a/drivers/gpu/drm/radeon/r520.c
+++ b/drivers/gpu/drm/radeon/r520.c
@@ -177,7 +177,6 @@ void r520_gpu_init(struct radeon_device *rdev)
*/
/* workaround for RV530 */
if (rdev->family == CHIP_RV530) {
- WREG32(0x4124, 1);
WREG32(0x4128, 0xFF);
}
r420_pipes_init(rdev);
diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c
index 146f3570af8..20f17908b03 100644
--- a/drivers/gpu/drm/radeon/r600_cp.c
+++ b/drivers/gpu/drm/radeon/r600_cp.c
@@ -384,8 +384,9 @@ static void r600_cp_load_microcode(drm_radeon_private_t *dev_priv)
DRM_INFO("Loading RV670 PFP Microcode\n");
for (i = 0; i < PFP_UCODE_SIZE; i++)
RADEON_WRITE(R600_CP_PFP_UCODE_DATA, RV670_pfp_microcode[i]);
- } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780)) {
- DRM_INFO("Loading RS780 CP Microcode\n");
+ } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780) ||
+ ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS880)) {
+ DRM_INFO("Loading RS780/RS880 CP Microcode\n");
for (i = 0; i < PM4_UCODE_SIZE; i++) {
RADEON_WRITE(R600_CP_ME_RAM_DATA,
RS780_cp_microcode[i][0]);
@@ -396,7 +397,7 @@ static void r600_cp_load_microcode(drm_radeon_private_t *dev_priv)
}
RADEON_WRITE(R600_CP_PFP_UCODE_ADDR, 0);
- DRM_INFO("Loading RS780 PFP Microcode\n");
+ DRM_INFO("Loading RS780/RS880 PFP Microcode\n");
for (i = 0; i < PFP_UCODE_SIZE; i++)
RADEON_WRITE(R600_CP_PFP_UCODE_DATA, RS780_pfp_microcode[i]);
}
@@ -783,6 +784,7 @@ static void r600_gfx_init(struct drm_device *dev,
break;
case CHIP_RV610:
case CHIP_RS780:
+ case CHIP_RS880:
case CHIP_RV620:
dev_priv->r600_max_pipes = 1;
dev_priv->r600_max_tile_pipes = 1;
@@ -917,7 +919,8 @@ static void r600_gfx_init(struct drm_device *dev,
((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV630) ||
((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV610) ||
((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV620) ||
- ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780))
+ ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780) ||
+ ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS880))
RADEON_WRITE(R600_DB_DEBUG, R600_PREZ_MUST_WAIT_FOR_POSTZ_DONE);
else
RADEON_WRITE(R600_DB_DEBUG, 0);
@@ -935,7 +938,8 @@ static void r600_gfx_init(struct drm_device *dev,
sq_ms_fifo_sizes = RADEON_READ(R600_SQ_MS_FIFO_SIZES);
if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV610) ||
((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV620) ||
- ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780)) {
+ ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780) ||
+ ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS880)) {
sq_ms_fifo_sizes = (R600_CACHE_FIFO_SIZE(0xa) |
R600_FETCH_FIFO_HIWATER(0xa) |
R600_DONE_FIFO_HIWATER(0xe0) |
@@ -978,7 +982,8 @@ static void r600_gfx_init(struct drm_device *dev,
R600_NUM_ES_STACK_ENTRIES(0));
} else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV610) ||
((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV620) ||
- ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780)) {
+ ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780) ||
+ ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS880)) {
/* no vertex cache */
sq_config &= ~R600_VC_ENABLE;
@@ -1035,7 +1040,8 @@ static void r600_gfx_init(struct drm_device *dev,
if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV610) ||
((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV620) ||
- ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780))
+ ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS780) ||
+ ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS880))
RADEON_WRITE(R600_VGT_CACHE_INVALIDATION, R600_CACHE_INVALIDATION(R600_TC_ONLY));
else
RADEON_WRITE(R600_VGT_CACHE_INVALIDATION, R600_CACHE_INVALIDATION(R600_VC_AND_TC));
@@ -1078,6 +1084,7 @@ static void r600_gfx_init(struct drm_device *dev,
break;
case CHIP_RV610:
case CHIP_RS780:
+ case CHIP_RS880:
case CHIP_RV620:
gs_prim_buffer_depth = 32;
break;
@@ -1123,6 +1130,7 @@ static void r600_gfx_init(struct drm_device *dev,
switch (dev_priv->flags & RADEON_FAMILY_MASK) {
case CHIP_RV610:
case CHIP_RS780:
+ case CHIP_RS880:
case CHIP_RV620:
tc_cntl = R600_TC_L2_SIZE(8);
break;
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index b1d945b8ed6..b519fb2fecb 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -242,6 +242,7 @@ int radeon_object_pin(struct radeon_object *robj, uint32_t domain,
uint64_t *gpu_addr);
void radeon_object_unpin(struct radeon_object *robj);
int radeon_object_wait(struct radeon_object *robj);
+int radeon_object_busy_domain(struct radeon_object *robj, uint32_t *cur_placement);
int radeon_object_evict_vram(struct radeon_device *rdev);
int radeon_object_mmap(struct radeon_object *robj, uint64_t *offset);
void radeon_object_force_delete(struct radeon_device *rdev);
@@ -574,6 +575,7 @@ struct radeon_asic {
void (*ring_start)(struct radeon_device *rdev);
int (*irq_set)(struct radeon_device *rdev);
int (*irq_process)(struct radeon_device *rdev);
+ u32 (*get_vblank_counter)(struct radeon_device *rdev, int crtc);
void (*fence_ring_emit)(struct radeon_device *rdev, struct radeon_fence *fence);
int (*cs_parse)(struct radeon_cs_parser *p);
int (*copy_blit)(struct radeon_device *rdev,
@@ -653,6 +655,7 @@ struct radeon_device {
int usec_timeout;
enum radeon_pll_errata pll_errata;
int num_gb_pipes;
+ int num_z_pipes;
int disp_priority;
/* BIOS */
uint8_t *bios;
@@ -666,14 +669,11 @@ struct radeon_device {
resource_size_t rmmio_base;
resource_size_t rmmio_size;
void *rmmio;
- radeon_rreg_t mm_rreg;
- radeon_wreg_t mm_wreg;
radeon_rreg_t mc_rreg;
radeon_wreg_t mc_wreg;
radeon_rreg_t pll_rreg;
radeon_wreg_t pll_wreg;
- radeon_rreg_t pcie_rreg;
- radeon_wreg_t pcie_wreg;
+ uint32_t pcie_reg_mask;
radeon_rreg_t pciep_rreg;
radeon_wreg_t pciep_wreg;
struct radeon_clock clock;
@@ -705,22 +705,42 @@ int radeon_device_init(struct radeon_device *rdev,
void radeon_device_fini(struct radeon_device *rdev);
int radeon_gpu_wait_for_idle(struct radeon_device *rdev);
+static inline uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg)
+{
+ if (reg < 0x10000)
+ return readl(((void __iomem *)rdev->rmmio) + reg);
+ else {
+ writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
+ return readl(((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
+ }
+}
+
+static inline void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
+{
+ if (reg < 0x10000)
+ writel(v, ((void __iomem *)rdev->rmmio) + reg);
+ else {
+ writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
+ writel(v, ((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
+ }
+}
+
/*
* Registers read & write functions.
*/
#define RREG8(reg) readb(((void __iomem *)rdev->rmmio) + (reg))
#define WREG8(reg, v) writeb(v, ((void __iomem *)rdev->rmmio) + (reg))
-#define RREG32(reg) rdev->mm_rreg(rdev, (reg))
-#define WREG32(reg, v) rdev->mm_wreg(rdev, (reg), (v))
+#define RREG32(reg) r100_mm_rreg(rdev, (reg))
+#define WREG32(reg, v) r100_mm_wreg(rdev, (reg), (v))
#define REG_SET(FIELD, v) (((v) << FIELD##_SHIFT) & FIELD##_MASK)
#define REG_GET(FIELD, v) (((v) << FIELD##_SHIFT) & FIELD##_MASK)
#define RREG32_PLL(reg) rdev->pll_rreg(rdev, (reg))
#define WREG32_PLL(reg, v) rdev->pll_wreg(rdev, (reg), (v))
#define RREG32_MC(reg) rdev->mc_rreg(rdev, (reg))
#define WREG32_MC(reg, v) rdev->mc_wreg(rdev, (reg), (v))
-#define RREG32_PCIE(reg) rdev->pcie_rreg(rdev, (reg))
-#define WREG32_PCIE(reg, v) rdev->pcie_wreg(rdev, (reg), (v))
+#define RREG32_PCIE(reg) rv370_pcie_rreg(rdev, (reg))
+#define WREG32_PCIE(reg, v) rv370_pcie_wreg(rdev, (reg), (v))
#define WREG32_P(reg, val, mask) \
do { \
uint32_t tmp_ = RREG32(reg); \
@@ -736,6 +756,24 @@ int radeon_gpu_wait_for_idle(struct radeon_device *rdev);
WREG32_PLL(reg, tmp_); \
} while (0)
+/*
+ * Indirect registers accessor
+ */
+static inline uint32_t rv370_pcie_rreg(struct radeon_device *rdev, uint32_t reg)
+{
+ uint32_t r;
+
+ WREG32(RADEON_PCIE_INDEX, ((reg) & rdev->pcie_reg_mask));
+ r = RREG32(RADEON_PCIE_DATA);
+ return r;
+}
+
+static inline void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
+{
+ WREG32(RADEON_PCIE_INDEX, ((reg) & rdev->pcie_reg_mask));
+ WREG32(RADEON_PCIE_DATA, (v));
+}
+
void r100_pll_errata_after_index(struct radeon_device *rdev);
@@ -862,6 +900,7 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v)
#define radeon_ring_start(rdev) (rdev)->asic->ring_start((rdev))
#define radeon_irq_set(rdev) (rdev)->asic->irq_set((rdev))
#define radeon_irq_process(rdev) (rdev)->asic->irq_process((rdev))
+#define radeon_get_vblank_counter(rdev, crtc) (rdev)->asic->get_vblank_counter((rdev), (crtc))
#define radeon_fence_ring_emit(rdev, fence) (rdev)->asic->fence_ring_emit((rdev), (fence))
#define radeon_copy_blit(rdev, s, d, np, f) (rdev)->asic->copy_blit((rdev), (s), (d), (np), (f))
#define radeon_copy_dma(rdev, s, d, np, f) (rdev)->asic->copy_dma((rdev), (s), (d), (np), (f))
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index 9a75876e0c3..93d8f888930 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -49,6 +49,7 @@ void r100_vram_info(struct radeon_device *rdev);
int r100_gpu_reset(struct radeon_device *rdev);
int r100_mc_init(struct radeon_device *rdev);
void r100_mc_fini(struct radeon_device *rdev);
+u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc);
int r100_wb_init(struct radeon_device *rdev);
void r100_wb_fini(struct radeon_device *rdev);
int r100_gart_enable(struct radeon_device *rdev);
@@ -96,6 +97,7 @@ static struct radeon_asic r100_asic = {
.ring_start = &r100_ring_start,
.irq_set = &r100_irq_set,
.irq_process = &r100_irq_process,
+ .get_vblank_counter = &r100_get_vblank_counter,
.fence_ring_emit = &r100_fence_ring_emit,
.cs_parse = &r100_cs_parse,
.copy_blit = &r100_copy_blit,
@@ -156,6 +158,7 @@ static struct radeon_asic r300_asic = {
.ring_start = &r300_ring_start,
.irq_set = &r100_irq_set,
.irq_process = &r100_irq_process,
+ .get_vblank_counter = &r100_get_vblank_counter,
.fence_ring_emit = &r300_fence_ring_emit,
.cs_parse = &r300_cs_parse,
.copy_blit = &r100_copy_blit,
@@ -196,6 +199,7 @@ static struct radeon_asic r420_asic = {
.ring_start = &r300_ring_start,
.irq_set = &r100_irq_set,
.irq_process = &r100_irq_process,
+ .get_vblank_counter = &r100_get_vblank_counter,
.fence_ring_emit = &r300_fence_ring_emit,
.cs_parse = &r300_cs_parse,
.copy_blit = &r100_copy_blit,
@@ -243,6 +247,7 @@ static struct radeon_asic rs400_asic = {
.ring_start = &r300_ring_start,
.irq_set = &r100_irq_set,
.irq_process = &r100_irq_process,
+ .get_vblank_counter = &r100_get_vblank_counter,
.fence_ring_emit = &r300_fence_ring_emit,
.cs_parse = &r300_cs_parse,
.copy_blit = &r100_copy_blit,
@@ -261,11 +266,14 @@ static struct radeon_asic rs400_asic = {
/*
* rs600.
*/
+int rs600_init(struct radeon_device *dev);
void rs600_errata(struct radeon_device *rdev);
void rs600_vram_info(struct radeon_device *rdev);
int rs600_mc_init(struct radeon_device *rdev);
void rs600_mc_fini(struct radeon_device *rdev);
int rs600_irq_set(struct radeon_device *rdev);
+int rs600_irq_process(struct radeon_device *rdev);
+u32 rs600_get_vblank_counter(struct radeon_device *rdev, int crtc);
int rs600_gart_enable(struct radeon_device *rdev);
void rs600_gart_disable(struct radeon_device *rdev);
void rs600_gart_tlb_flush(struct radeon_device *rdev);
@@ -274,7 +282,7 @@ uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg);
void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
void rs600_bandwidth_update(struct radeon_device *rdev);
static struct radeon_asic rs600_asic = {
- .init = &r300_init,
+ .init = &rs600_init,
.errata = &rs600_errata,
.vram_info = &rs600_vram_info,
.gpu_reset = &r300_gpu_reset,
@@ -291,7 +299,8 @@ static struct radeon_asic rs600_asic = {
.cp_disable = &r100_cp_disable,
.ring_start = &r300_ring_start,
.irq_set = &rs600_irq_set,
- .irq_process = &r100_irq_process,
+ .irq_process = &rs600_irq_process,
+ .get_vblank_counter = &rs600_get_vblank_counter,
.fence_ring_emit = &r300_fence_ring_emit,
.cs_parse = &r300_cs_parse,
.copy_blit = &r100_copy_blit,
@@ -316,7 +325,7 @@ uint32_t rs690_mc_rreg(struct radeon_device *rdev, uint32_t reg);
void rs690_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
void rs690_bandwidth_update(struct radeon_device *rdev);
static struct radeon_asic rs690_asic = {
- .init = &r300_init,
+ .init = &rs600_init,
.errata = &rs690_errata,
.vram_info = &rs690_vram_info,
.gpu_reset = &r300_gpu_reset,
@@ -333,7 +342,8 @@ static struct radeon_asic rs690_asic = {
.cp_disable = &r100_cp_disable,
.ring_start = &r300_ring_start,
.irq_set = &rs600_irq_set,
- .irq_process = &r100_irq_process,
+ .irq_process = &rs600_irq_process,
+ .get_vblank_counter = &rs600_get_vblank_counter,
.fence_ring_emit = &r300_fence_ring_emit,
.cs_parse = &r300_cs_parse,
.copy_blit = &r100_copy_blit,
@@ -381,8 +391,9 @@ static struct radeon_asic rv515_asic = {
.cp_fini = &r100_cp_fini,
.cp_disable = &r100_cp_disable,
.ring_start = &rv515_ring_start,
- .irq_set = &r100_irq_set,
- .irq_process = &r100_irq_process,
+ .irq_set = &rs600_irq_set,
+ .irq_process = &rs600_irq_process,
+ .get_vblank_counter = &rs600_get_vblank_counter,
.fence_ring_emit = &r300_fence_ring_emit,
.cs_parse = &r300_cs_parse,
.copy_blit = &r100_copy_blit,
@@ -423,8 +434,9 @@ static struct radeon_asic r520_asic = {
.cp_fini = &r100_cp_fini,
.cp_disable = &r100_cp_disable,
.ring_start = &rv515_ring_start,
- .irq_set = &r100_irq_set,
- .irq_process = &r100_irq_process,
+ .irq_set = &rs600_irq_set,
+ .irq_process = &rs600_irq_process,
+ .get_vblank_counter = &rs600_get_vblank_counter,
.fence_ring_emit = &r300_fence_ring_emit,
.cs_parse = &r300_cs_parse,
.copy_blit = &r100_copy_blit,
diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
index afc4db280b9..2a027e00762 100644
--- a/drivers/gpu/drm/radeon/radeon_combios.c
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
@@ -685,23 +685,15 @@ static const uint32_t default_tvdac_adj[CHIP_LAST] = {
0x00780000, /* rs480 */
};
-static struct radeon_encoder_tv_dac
- *radeon_legacy_get_tv_dac_info_from_table(struct radeon_device *rdev)
+static void radeon_legacy_get_tv_dac_info_from_table(struct radeon_device *rdev,
+ struct radeon_encoder_tv_dac *tv_dac)
{
- struct radeon_encoder_tv_dac *tv_dac = NULL;
-
- tv_dac = kzalloc(sizeof(struct radeon_encoder_tv_dac), GFP_KERNEL);
-
- if (!tv_dac)
- return NULL;
-
tv_dac->ps2_tvdac_adj = default_tvdac_adj[rdev->family];
if ((rdev->flags & RADEON_IS_MOBILITY) && (rdev->family == CHIP_RV250))
tv_dac->ps2_tvdac_adj = 0x00880000;
tv_dac->pal_tvdac_adj = tv_dac->ps2_tvdac_adj;
tv_dac->ntsc_tvdac_adj = tv_dac->ps2_tvdac_adj;
-
- return tv_dac;
+ return;
}
struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct
@@ -713,19 +705,18 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct
uint16_t dac_info;
uint8_t rev, bg, dac;
struct radeon_encoder_tv_dac *tv_dac = NULL;
+ int found = 0;
+
+ tv_dac = kzalloc(sizeof(struct radeon_encoder_tv_dac), GFP_KERNEL);
+ if (!tv_dac)
+ return NULL;
if (rdev->bios == NULL)
- return radeon_legacy_get_tv_dac_info_from_table(rdev);
+ goto out;
/* first check TV table */
dac_info = combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE);
if (dac_info) {
- tv_dac =
- kzalloc(sizeof(struct radeon_encoder_tv_dac), GFP_KERNEL);
-
- if (!tv_dac)
- return NULL;
-
rev = RBIOS8(dac_info + 0x3);
if (rev > 4) {
bg = RBIOS8(dac_info + 0xc) & 0xf;
@@ -739,6 +730,7 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct
bg = RBIOS8(dac_info + 0x10) & 0xf;
dac = RBIOS8(dac_info + 0x11) & 0xf;
tv_dac->ntsc_tvdac_adj = (bg << 16) | (dac << 20);
+ found = 1;
} else if (rev > 1) {
bg = RBIOS8(dac_info + 0xc) & 0xf;
dac = (RBIOS8(dac_info + 0xc) >> 4) & 0xf;
@@ -751,22 +743,15 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct
bg = RBIOS8(dac_info + 0xe) & 0xf;
dac = (RBIOS8(dac_info + 0xe) >> 4) & 0xf;
tv_dac->ntsc_tvdac_adj = (bg << 16) | (dac << 20);
+ found = 1;
}
-
tv_dac->tv_std = radeon_combios_get_tv_info(encoder);
-
- } else {
+ }
+ if (!found) {
/* then check CRT table */
dac_info =
combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE);
if (dac_info) {
- tv_dac =
- kzalloc(sizeof(struct radeon_encoder_tv_dac),
- GFP_KERNEL);
-
- if (!tv_dac)
- return NULL;
-
rev = RBIOS8(dac_info) & 0x3;
if (rev < 2) {
bg = RBIOS8(dac_info + 0x3) & 0xf;
@@ -775,6 +760,7 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct
(bg << 16) | (dac << 20);
tv_dac->pal_tvdac_adj = tv_dac->ps2_tvdac_adj;
tv_dac->ntsc_tvdac_adj = tv_dac->ps2_tvdac_adj;
+ found = 1;
} else {
bg = RBIOS8(dac_info + 0x4) & 0xf;
dac = RBIOS8(dac_info + 0x5) & 0xf;
@@ -782,13 +768,17 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct
(bg << 16) | (dac << 20);
tv_dac->pal_tvdac_adj = tv_dac->ps2_tvdac_adj;
tv_dac->ntsc_tvdac_adj = tv_dac->ps2_tvdac_adj;
+ found = 1;
}
} else {
DRM_INFO("No TV DAC info found in BIOS\n");
- return radeon_legacy_get_tv_dac_info_from_table(rdev);
}
}
+out:
+ if (!found) /* fallback to defaults */
+ radeon_legacy_get_tv_dac_info_from_table(rdev, tv_dac);
+
return tv_dac;
}
diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c
index d8356827ef1..7a52c461145 100644
--- a/drivers/gpu/drm/radeon/radeon_cp.c
+++ b/drivers/gpu/drm/radeon/radeon_cp.c
@@ -406,6 +406,15 @@ static void radeon_init_pipes(drm_radeon_private_t *dev_priv)
{
uint32_t gb_tile_config, gb_pipe_sel = 0;
+ if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV530) {
+ uint32_t z_pipe_sel = RADEON_READ(RV530_GB_PIPE_SELECT2);
+ if ((z_pipe_sel & 3) == 3)
+ dev_priv->num_z_pipes = 2;
+ else
+ dev_priv->num_z_pipes = 1;
+ } else
+ dev_priv->num_z_pipes = 1;
+
/* RS4xx/RS6xx/R4xx/R5xx */
if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R420) {
gb_pipe_sel = RADEON_READ(R400_GB_PIPE_SELECT);
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index a162ade74b7..7693f7c67bd 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -152,7 +152,9 @@ int radeon_mc_setup(struct radeon_device *rdev)
}
} else {
rdev->mc.vram_location = 0;
- rdev->mc.gtt_location = rdev->mc.mc_vram_size;
+ tmp = rdev->mc.mc_vram_size;
+ tmp = (tmp + rdev->mc.gtt_size - 1) & ~(rdev->mc.gtt_size - 1);
+ rdev->mc.gtt_location = tmp;
}
DRM_INFO("radeon: VRAM %uM\n", rdev->mc.real_vram_size >> 20);
DRM_INFO("radeon: VRAM from 0x%08X to 0x%08X\n",
@@ -223,25 +225,18 @@ void radeon_invalid_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
void radeon_register_accessor_init(struct radeon_device *rdev)
{
- rdev->mm_rreg = &r100_mm_rreg;
- rdev->mm_wreg = &r100_mm_wreg;
rdev->mc_rreg = &radeon_invalid_rreg;
rdev->mc_wreg = &radeon_invalid_wreg;
rdev->pll_rreg = &radeon_invalid_rreg;
rdev->pll_wreg = &radeon_invalid_wreg;
- rdev->pcie_rreg = &radeon_invalid_rreg;
- rdev->pcie_wreg = &radeon_invalid_wreg;
rdev->pciep_rreg = &radeon_invalid_rreg;
rdev->pciep_wreg = &radeon_invalid_wreg;
/* Don't change order as we are overridding accessor. */
if (rdev->family < CHIP_RV515) {
- rdev->pcie_rreg = &rv370_pcie_rreg;
- rdev->pcie_wreg = &rv370_pcie_wreg;
- }
- if (rdev->family >= CHIP_RV515) {
- rdev->pcie_rreg = &rv515_pcie_rreg;
- rdev->pcie_wreg = &rv515_pcie_wreg;
+ rdev->pcie_reg_mask = 0xff;
+ } else {
+ rdev->pcie_reg_mask = 0x7ff;
}
/* FIXME: not sure here */
if (rdev->family <= CHIP_R580) {
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 3cfcee17dc5..0bd5879a495 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -318,6 +318,14 @@ static int __init radeon_init(void)
driver = &driver_old;
driver->num_ioctls = radeon_max_ioctl;
#if defined(CONFIG_DRM_RADEON_KMS)
+#ifdef CONFIG_VGA_CONSOLE
+ if (vgacon_text_force() && radeon_modeset == -1) {
+ DRM_INFO("VGACON disable radeon kernel modesetting.\n");
+ driver = &driver_old;
+ driver->driver_features &= ~DRIVER_MODESET;
+ radeon_modeset = 0;
+ }
+#endif
/* if enabled by default */
if (radeon_modeset == -1) {
DRM_INFO("radeon default to kernel modesetting.\n");
@@ -329,17 +337,8 @@ static int __init radeon_init(void)
driver->driver_features |= DRIVER_MODESET;
driver->num_ioctls = radeon_max_kms_ioctl;
}
-
/* if the vga console setting is enabled still
* let modprobe override it */
-#ifdef CONFIG_VGA_CONSOLE
- if (vgacon_text_force() && radeon_modeset == -1) {
- DRM_INFO("VGACON disable radeon kernel modesetting.\n");
- driver = &driver_old;
- driver->driver_features &= ~DRIVER_MODESET;
- radeon_modeset = 0;
- }
-#endif
#endif
return drm_init(driver);
}
diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h
index 127d0456f62..6fa32dac4e9 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.h
+++ b/drivers/gpu/drm/radeon/radeon_drv.h
@@ -100,9 +100,10 @@
* 1.28- Add support for VBL on CRTC2
* 1.29- R500 3D cmd buffer support
* 1.30- Add support for occlusion queries
+ * 1.31- Add support for num Z pipes from GET_PARAM
*/
#define DRIVER_MAJOR 1
-#define DRIVER_MINOR 30
+#define DRIVER_MINOR 31
#define DRIVER_PATCHLEVEL 0
/*
@@ -143,6 +144,7 @@ enum radeon_family {
CHIP_RV635,
CHIP_RV670,
CHIP_RS780,
+ CHIP_RS880,
CHIP_RV770,
CHIP_RV730,
CHIP_RV710,
@@ -328,6 +330,7 @@ typedef struct drm_radeon_private {
resource_size_t fb_aper_offset;
int num_gb_pipes;
+ int num_z_pipes;
int track_flush;
drm_local_map_t *mmio;
@@ -688,6 +691,7 @@ extern void r600_page_table_cleanup(struct drm_device *dev, struct drm_ati_pciga
/* pipe config regs */
#define R400_GB_PIPE_SELECT 0x402c
+#define RV530_GB_PIPE_SELECT2 0x4124
#define R500_DYN_SCLK_PWMEM_PIPE 0x000d /* PLL */
#define R300_GB_TILE_CONFIG 0x4018
# define R300_ENABLE_TILING (1 << 0)
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index 3206c0ad7b6..ec383edf5f3 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -574,6 +574,8 @@ int radeonfb_create(struct radeon_device *rdev,
goto out_unref;
}
+ memset_io(fbptr, 0, aligned_size);
+
strcpy(info->fix.id, "radeondrmfb");
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_TRUECOLOR;
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index cded5180c75..d880edf254d 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -262,8 +262,34 @@ int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data,
int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp)
{
- /* FIXME: implement */
- return 0;
+ struct drm_radeon_gem_busy *args = data;
+ struct drm_gem_object *gobj;
+ struct radeon_object *robj;
+ int r;
+ uint32_t cur_placement;
+
+ gobj = drm_gem_object_lookup(dev, filp, args->handle);
+ if (gobj == NULL) {
+ return -EINVAL;
+ }
+ robj = gobj->driver_private;
+ r = radeon_object_busy_domain(robj, &cur_placement);
+ switch (cur_placement) {
+ case TTM_PL_VRAM:
+ args->domain = RADEON_GEM_DOMAIN_VRAM;
+ break;
+ case TTM_PL_TT:
+ args->domain = RADEON_GEM_DOMAIN_GTT;
+ break;
+ case TTM_PL_SYSTEM:
+ args->domain = RADEON_GEM_DOMAIN_CPU;
+ default:
+ break;
+ }
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(gobj);
+ mutex_unlock(&dev->struct_mutex);
+ return r;
}
int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index 491d569deb0..9805e4b6ca1 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -32,60 +32,6 @@
#include "radeon.h"
#include "atom.h"
-static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
-{
- uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS);
- uint32_t irq_mask = RADEON_SW_INT_TEST;
-
- if (irqs) {
- WREG32(RADEON_GEN_INT_STATUS, irqs);
- }
- return irqs & irq_mask;
-}
-
-int r100_irq_set(struct radeon_device *rdev)
-{
- uint32_t tmp = 0;
-
- if (rdev->irq.sw_int) {
- tmp |= RADEON_SW_INT_ENABLE;
- }
- /* Todo go through CRTC and enable vblank int or not */
- WREG32(RADEON_GEN_INT_CNTL, tmp);
- return 0;
-}
-
-int r100_irq_process(struct radeon_device *rdev)
-{
- uint32_t status;
-
- status = r100_irq_ack(rdev);
- if (!status) {
- return IRQ_NONE;
- }
- while (status) {
- /* SW interrupt */
- if (status & RADEON_SW_INT_TEST) {
- radeon_fence_process(rdev);
- }
- status = r100_irq_ack(rdev);
- }
- return IRQ_HANDLED;
-}
-
-int rs600_irq_set(struct radeon_device *rdev)
-{
- uint32_t tmp = 0;
-
- if (rdev->irq.sw_int) {
- tmp |= RADEON_SW_INT_ENABLE;
- }
- WREG32(RADEON_GEN_INT_CNTL, tmp);
- /* Todo go through CRTC and enable vblank int or not */
- WREG32(R500_DxMODE_INT_MASK, 0);
- return 0;
-}
-
irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *) arg;
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index 937a2f1cdb4..dce09ada32b 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -58,6 +58,8 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
if (r) {
DRM_ERROR("Failed to initialize radeon, disabling IOCTL\n");
radeon_device_fini(rdev);
+ kfree(rdev);
+ dev->dev_private = NULL;
return r;
}
return 0;
@@ -93,6 +95,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
case RADEON_INFO_NUM_GB_PIPES:
value = rdev->num_gb_pipes;
break;
+ case RADEON_INFO_NUM_Z_PIPES:
+ value = rdev->num_z_pipes;
+ break;
default:
DRM_DEBUG("Invalid request %d\n", info->request);
return -EINVAL;
@@ -139,19 +144,42 @@ void radeon_driver_preclose_kms(struct drm_device *dev,
*/
u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc)
{
- /* FIXME: implement */
- return 0;
+ struct radeon_device *rdev = dev->dev_private;
+
+ if (crtc < 0 || crtc > 1) {
+ DRM_ERROR("Invalid crtc %d\n", crtc);
+ return -EINVAL;
+ }
+
+ return radeon_get_vblank_counter(rdev, crtc);
}
int radeon_enable_vblank_kms(struct drm_device *dev, int crtc)
{
- /* FIXME: implement */
- return 0;
+ struct radeon_device *rdev = dev->dev_private;
+
+ if (crtc < 0 || crtc > 1) {
+ DRM_ERROR("Invalid crtc %d\n", crtc);
+ return -EINVAL;
+ }
+
+ rdev->irq.crtc_vblank_int[crtc] = true;
+
+ return radeon_irq_set(rdev);
}
void radeon_disable_vblank_kms(struct drm_device *dev, int crtc)
{
- /* FIXME: implement */
+ struct radeon_device *rdev = dev->dev_private;
+
+ if (crtc < 0 || crtc > 1) {
+ DRM_ERROR("Invalid crtc %d\n", crtc);
+ return;
+ }
+
+ rdev->irq.crtc_vblank_int[crtc] = false;
+
+ radeon_irq_set(rdev);
}
@@ -293,5 +321,6 @@ struct drm_ioctl_desc radeon_ioctls_kms[] = {
DRM_IOCTL_DEF(DRM_RADEON_INFO, radeon_info_ioctl, DRM_AUTH),
DRM_IOCTL_DEF(DRM_RADEON_GEM_SET_TILING, radeon_gem_set_tiling_ioctl, DRM_AUTH),
DRM_IOCTL_DEF(DRM_RADEON_GEM_GET_TILING, radeon_gem_get_tiling_ioctl, DRM_AUTH),
+ DRM_IOCTL_DEF(DRM_RADEON_GEM_BUSY, radeon_gem_busy_ioctl, DRM_AUTH),
};
int radeon_max_kms_ioctl = DRM_ARRAY_SIZE(radeon_ioctls_kms);
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
index 7d06dc98a42..0da72f18fd3 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
@@ -310,10 +310,13 @@ void radeon_crtc_dpms(struct drm_crtc *crtc, int mode)
RADEON_CRTC_DISP_REQ_EN_B));
WREG32_P(RADEON_CRTC_EXT_CNTL, 0, ~mask);
}
+ drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
+ radeon_crtc_load_lut(crtc);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
+ drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id);
if (radeon_crtc->crtc_id)
WREG32_P(RADEON_CRTC2_GEN_CNTL, mask, ~mask);
else {
@@ -323,10 +326,6 @@ void radeon_crtc_dpms(struct drm_crtc *crtc, int mode)
}
break;
}
-
- if (mode != DRM_MODE_DPMS_OFF) {
- radeon_crtc_load_lut(crtc);
- }
}
/* properly set crtc bpp when using atombios */
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
index 34d0f58eb94..9322675ef6d 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
@@ -1066,6 +1066,7 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t
switch (radeon_encoder->encoder_id) {
case ENCODER_OBJECT_ID_INTERNAL_LVDS:
+ encoder->possible_crtcs = 0x1;
drm_encoder_init(dev, encoder, &radeon_legacy_lvds_enc_funcs, DRM_MODE_ENCODER_LVDS);
drm_encoder_helper_add(encoder, &radeon_legacy_lvds_helper_funcs);
if (rdev->is_atom_bios)
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index dd9ac2fed6d..b85fb83d7ae 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -106,7 +106,7 @@ static inline uint32_t radeon_object_flags_from_domain(uint32_t domain)
flags |= TTM_PL_FLAG_VRAM | TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED;
}
if (domain & RADEON_GEM_DOMAIN_GTT) {
- flags |= TTM_PL_FLAG_TT | TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED;
+ flags |= TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
}
if (domain & RADEON_GEM_DOMAIN_CPU) {
flags |= TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING;
@@ -316,6 +316,25 @@ int radeon_object_wait(struct radeon_object *robj)
return r;
}
+int radeon_object_busy_domain(struct radeon_object *robj, uint32_t *cur_placement)
+{
+ int r = 0;
+
+ r = radeon_object_reserve(robj, true);
+ if (unlikely(r != 0)) {
+ DRM_ERROR("radeon: failed to reserve object for waiting.\n");
+ return r;
+ }
+ spin_lock(&robj->tobj.lock);
+ *cur_placement = robj->tobj.mem.mem_type;
+ if (robj->tobj.sync_obj) {
+ r = ttm_bo_wait(&robj->tobj, true, true, true);
+ }
+ spin_unlock(&robj->tobj.lock);
+ radeon_object_unreserve(robj);
+ return r;
+}
+
int radeon_object_evict_vram(struct radeon_device *rdev)
{
if (rdev->flags & RADEON_IS_IGP) {
diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h
index e1b61857446..4df43f62c67 100644
--- a/drivers/gpu/drm/radeon/radeon_reg.h
+++ b/drivers/gpu/drm/radeon/radeon_reg.h
@@ -982,12 +982,15 @@
# define RS400_TMDS2_PLLRST (1 << 1)
#define RADEON_GEN_INT_CNTL 0x0040
+# define RADEON_CRTC_VBLANK_MASK (1 << 0)
+# define RADEON_CRTC2_VBLANK_MASK (1 << 9)
# define RADEON_SW_INT_ENABLE (1 << 25)
#define RADEON_GEN_INT_STATUS 0x0044
-# define RADEON_VSYNC_INT_AK (1 << 2)
-# define RADEON_VSYNC_INT (1 << 2)
-# define RADEON_VSYNC2_INT_AK (1 << 6)
-# define RADEON_VSYNC2_INT (1 << 6)
+# define AVIVO_DISPLAY_INT_STATUS (1 << 0)
+# define RADEON_CRTC_VBLANK_STAT (1 << 0)
+# define RADEON_CRTC_VBLANK_STAT_ACK (1 << 0)
+# define RADEON_CRTC2_VBLANK_STAT (1 << 9)
+# define RADEON_CRTC2_VBLANK_STAT_ACK (1 << 9)
# define RADEON_SW_INT_FIRE (1 << 26)
# define RADEON_SW_INT_TEST (1 << 25)
# define RADEON_SW_INT_TEST_ACK (1 << 25)
@@ -2334,6 +2337,9 @@
# define RADEON_RE_WIDTH_SHIFT 0
# define RADEON_RE_HEIGHT_SHIFT 16
+#define RADEON_RB3D_ZPASS_DATA 0x3290
+#define RADEON_RB3D_ZPASS_ADDR 0x3294
+
#define RADEON_SE_CNTL 0x1c4c
# define RADEON_FFACE_CULL_CW (0 << 0)
# define RADEON_FFACE_CULL_CCW (1 << 0)
@@ -3568,4 +3574,6 @@
#define RADEON_SCRATCH_REG4 0x15f0
#define RADEON_SCRATCH_REG5 0x15f4
+#define RV530_GB_PIPE_SELECT2 0x4124
+
#endif
diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c
index 46645f3e032..2882f40d5ec 100644
--- a/drivers/gpu/drm/radeon/radeon_state.c
+++ b/drivers/gpu/drm/radeon/radeon_state.c
@@ -3081,6 +3081,9 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil
case RADEON_PARAM_NUM_GB_PIPES:
value = dev_priv->num_gb_pipes;
break;
+ case RADEON_PARAM_NUM_Z_PIPES:
+ value = dev_priv->num_z_pipes;
+ break;
default:
DRM_DEBUG("Invalid parameter %d\n", param->param);
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index bbea6dee4a9..02fd11aad6a 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -240,6 +240,88 @@ void rs600_mc_fini(struct radeon_device *rdev)
/*
+ * Interrupts
+ */
+int rs600_irq_set(struct radeon_device *rdev)
+{
+ uint32_t tmp = 0;
+ uint32_t mode_int = 0;
+
+ if (rdev->irq.sw_int) {
+ tmp |= RADEON_SW_INT_ENABLE;
+ }
+ if (rdev->irq.crtc_vblank_int[0]) {
+ tmp |= AVIVO_DISPLAY_INT_STATUS;
+ mode_int |= AVIVO_D1MODE_INT_MASK;
+ }
+ if (rdev->irq.crtc_vblank_int[1]) {
+ tmp |= AVIVO_DISPLAY_INT_STATUS;
+ mode_int |= AVIVO_D2MODE_INT_MASK;
+ }
+ WREG32(RADEON_GEN_INT_CNTL, tmp);
+ WREG32(AVIVO_DxMODE_INT_MASK, mode_int);
+ return 0;
+}
+
+static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_int)
+{
+ uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS);
+ uint32_t irq_mask = RADEON_SW_INT_TEST;
+
+ if (irqs & AVIVO_DISPLAY_INT_STATUS) {
+ *r500_disp_int = RREG32(AVIVO_DISP_INTERRUPT_STATUS);
+ if (*r500_disp_int & AVIVO_D1_VBLANK_INTERRUPT) {
+ WREG32(AVIVO_D1MODE_VBLANK_STATUS, AVIVO_VBLANK_ACK);
+ }
+ if (*r500_disp_int & AVIVO_D2_VBLANK_INTERRUPT) {
+ WREG32(AVIVO_D2MODE_VBLANK_STATUS, AVIVO_VBLANK_ACK);
+ }
+ } else {
+ *r500_disp_int = 0;
+ }
+
+ if (irqs) {
+ WREG32(RADEON_GEN_INT_STATUS, irqs);
+ }
+ return irqs & irq_mask;
+}
+
+int rs600_irq_process(struct radeon_device *rdev)
+{
+ uint32_t status;
+ uint32_t r500_disp_int;
+
+ status = rs600_irq_ack(rdev, &r500_disp_int);
+ if (!status && !r500_disp_int) {
+ return IRQ_NONE;
+ }
+ while (status || r500_disp_int) {
+ /* SW interrupt */
+ if (status & RADEON_SW_INT_TEST) {
+ radeon_fence_process(rdev);
+ }
+ /* Vertical blank interrupts */
+ if (r500_disp_int & AVIVO_D1_VBLANK_INTERRUPT) {
+ drm_handle_vblank(rdev->ddev, 0);
+ }
+ if (r500_disp_int & AVIVO_D2_VBLANK_INTERRUPT) {
+ drm_handle_vblank(rdev->ddev, 1);
+ }
+ status = rs600_irq_ack(rdev, &r500_disp_int);
+ }
+ return IRQ_HANDLED;
+}
+
+u32 rs600_get_vblank_counter(struct radeon_device *rdev, int crtc)
+{
+ if (crtc == 0)
+ return RREG32(AVIVO_D1CRTC_FRAME_COUNT);
+ else
+ return RREG32(AVIVO_D2CRTC_FRAME_COUNT);
+}
+
+
+/*
* Global GPU functions
*/
void rs600_disable_vga(struct radeon_device *rdev)
@@ -327,3 +409,68 @@ void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
((reg) & RS600_MC_ADDR_MASK));
WREG32(RS600_MC_DATA, v);
}
+
+static const unsigned rs600_reg_safe_bm[219] = {
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0x17FF1FFF, 0xFFFFFFFC, 0xFFFFFFFF, 0xFF30FFBF,
+ 0xFFFFFFF8, 0xC3E6FFFF, 0xFFFFF6DF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFF03F,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFEFCE, 0xF00EBFFF, 0x007C0000,
+ 0xF0000078, 0xFF000009, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFF7FF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFC78, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF,
+ 0x38FF8F50, 0xFFF88082, 0xF000000C, 0xFAE009FF,
+ 0x0000FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+ 0x00000000, 0x0000C100, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0xFFFF0000, 0xFFFFFFFF, 0xFF80FFFF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x0003FC01, 0xFFFFFCF8, 0xFF800B19, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+};
+
+int rs600_init(struct radeon_device *rdev)
+{
+ rdev->config.r300.reg_safe_bm = rs600_reg_safe_bm;
+ rdev->config.r300.reg_safe_bm_size = ARRAY_SIZE(rs600_reg_safe_bm);
+ return 0;
+}
diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c
index 839595b0072..879882533e4 100644
--- a/drivers/gpu/drm/radeon/rs690.c
+++ b/drivers/gpu/drm/radeon/rs690.c
@@ -652,3 +652,4 @@ void rs690_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
WREG32(RS690_MC_DATA, v);
WREG32(RS690_MC_INDEX, RS690_MC_INDEX_WR_ACK);
}
+
diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c
index 551e608702e..0566fb67e46 100644
--- a/drivers/gpu/drm/radeon/rv515.c
+++ b/drivers/gpu/drm/radeon/rv515.c
@@ -370,6 +370,7 @@ void rv515_vram_info(struct radeon_device *rdev)
rv515_vram_get_type(rdev);
+ r100_vram_init_sizes(rdev);
/* FIXME: we should enforce default clock in case GPU is not in
* default setup
*/
@@ -399,25 +400,6 @@ void rv515_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
WREG32(MC_IND_INDEX, 0);
}
-uint32_t rv515_pcie_rreg(struct radeon_device *rdev, uint32_t reg)
-{
- uint32_t r;
-
- WREG32(PCIE_INDEX, ((reg) & 0x7ff));
- (void)RREG32(PCIE_INDEX);
- r = RREG32(PCIE_DATA);
- return r;
-}
-
-void rv515_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
-{
- WREG32(PCIE_INDEX, ((reg) & 0x7ff));
- (void)RREG32(PCIE_INDEX);
- WREG32(PCIE_DATA, (v));
- (void)RREG32(PCIE_DATA);
-}
-
-
/*
* Debugfs info
*/
@@ -526,7 +508,7 @@ static const unsigned r500_reg_safe_bm[219] = {
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF80FFFF,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x0003FC01, 0x3FFFFCF8, 0xFE800B19, 0xFFFFFFFF,
+ 0x0003FC01, 0x3FFFFCF8, 0xFF800B19, 0xFFDFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 6538d423698..c2b0d710d10 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1182,13 +1182,14 @@ static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev,
int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type)
{
- struct ttm_mem_type_manager *man = &bdev->man[mem_type];
+ struct ttm_mem_type_manager *man;
int ret = -EINVAL;
if (mem_type >= TTM_NUM_MEM_TYPES) {
printk(KERN_ERR TTM_PFX "Illegal memory type %d\n", mem_type);
return ret;
}
+ man = &bdev->man[mem_type];
if (!man->has_type) {
printk(KERN_ERR TTM_PFX "Trying to take down uninitialized "
@@ -1575,6 +1576,10 @@ int ttm_bo_wait(struct ttm_buffer_object *bo,
driver->sync_obj_unref(&sync_obj);
driver->sync_obj_unref(&tmp_obj);
spin_lock(&bo->lock);
+ } else {
+ spin_unlock(&bo->lock);
+ driver->sync_obj_unref(&sync_obj);
+ spin_lock(&bo->lock);
}
}
return 0;
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index ce2e6f38ea0..ad4ada07c6c 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -150,7 +150,7 @@ static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src,
#ifdef CONFIG_X86
dst = kmap_atomic_prot(d, KM_USER0, prot);
#else
- if (prot != PAGE_KERNEL)
+ if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
dst = vmap(&d, 1, 0, prot);
else
dst = kmap(d);
@@ -163,7 +163,7 @@ static int ttm_copy_io_ttm_page(struct ttm_tt *ttm, void *src,
#ifdef CONFIG_X86
kunmap_atomic(dst, KM_USER0);
#else
- if (prot != PAGE_KERNEL)
+ if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
vunmap(dst);
else
kunmap(d);
@@ -186,7 +186,7 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst,
#ifdef CONFIG_X86
src = kmap_atomic_prot(s, KM_USER0, prot);
#else
- if (prot != PAGE_KERNEL)
+ if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
src = vmap(&s, 1, 0, prot);
else
src = kmap(s);
@@ -199,7 +199,7 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst,
#ifdef CONFIG_X86
kunmap_atomic(src, KM_USER0);
#else
- if (prot != PAGE_KERNEL)
+ if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL))
vunmap(src);
else
kunmap(s);
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index d258b02aef4..827da085813 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -674,7 +674,14 @@ omap_i2c_isr(int this_irq, void *dev_id)
err = 0;
complete:
- omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat);
+ /*
+ * Ack the stat in one go, but [R/X]DR and [R/X]RDY should be
+ * acked after the data operation is complete.
+ * Ref: TRM SWPU114Q Figure 18-31
+ */
+ omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat &
+ ~(OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR |
+ OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
if (stat & OMAP_I2C_STAT_NACK) {
err |= OMAP_I2C_STAT_NACK;
@@ -687,6 +694,9 @@ complete:
}
if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
OMAP_I2C_STAT_AL)) {
+ omap_i2c_ack_stat(dev, stat &
+ (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR |
+ OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
omap_i2c_complete_cmd(dev, err);
return IRQ_HANDLED;
}
@@ -774,7 +784,7 @@ complete:
* memory to the I2C interface.
*/
- if (cpu_is_omap34xx()) {
+ if (dev->rev <= OMAP_I2C_REV_ON_3430) {
while (!(stat & OMAP_I2C_STAT_XUDF)) {
if (stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) {
omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c
index 182e711318b..d2728a28a8d 100644
--- a/drivers/i2c/busses/i2c-stu300.c
+++ b/drivers/i2c/busses/i2c-stu300.c
@@ -117,7 +117,8 @@ enum stu300_error {
STU300_ERROR_NONE = 0,
STU300_ERROR_ACKNOWLEDGE_FAILURE,
STU300_ERROR_BUS_ERROR,
- STU300_ERROR_ARBITRATION_LOST
+ STU300_ERROR_ARBITRATION_LOST,
+ STU300_ERROR_UNKNOWN
};
/* timeout waiting for the controller to respond */
@@ -127,7 +128,7 @@ enum stu300_error {
* The number of address send athemps tried before giving up.
* If the first one failes it seems like 5 to 8 attempts are required.
*/
-#define NUM_ADDR_RESEND_ATTEMPTS 10
+#define NUM_ADDR_RESEND_ATTEMPTS 12
/* I2C clock speed, in Hz 0-400kHz*/
static unsigned int scl_frequency = 100000;
@@ -149,6 +150,7 @@ module_param(scl_frequency, uint, 0644);
* @msg_index: index of current message
* @msg_len: length of current message
*/
+
struct stu300_dev {
struct platform_device *pdev;
struct i2c_adapter adapter;
@@ -188,6 +190,27 @@ static inline u32 stu300_r8(void __iomem *address)
return readl(address) & 0x000000FFU;
}
+static void stu300_irq_enable(struct stu300_dev *dev)
+{
+ u32 val;
+ val = stu300_r8(dev->virtbase + I2C_CR);
+ val |= I2C_CR_INTERRUPT_ENABLE;
+ /* Twice paranoia (possible HW glitch) */
+ stu300_wr8(val, dev->virtbase + I2C_CR);
+ stu300_wr8(val, dev->virtbase + I2C_CR);
+}
+
+static void stu300_irq_disable(struct stu300_dev *dev)
+{
+ u32 val;
+ val = stu300_r8(dev->virtbase + I2C_CR);
+ val &= ~I2C_CR_INTERRUPT_ENABLE;
+ /* Twice paranoia (possible HW glitch) */
+ stu300_wr8(val, dev->virtbase + I2C_CR);
+ stu300_wr8(val, dev->virtbase + I2C_CR);
+}
+
+
/*
* Tells whether a certain event or events occurred in
* response to a command. The events represent states in
@@ -196,9 +219,10 @@ static inline u32 stu300_r8(void __iomem *address)
* documentation and can only be treated as abstract state
* machine states.
*
- * @ret 0 = event has not occurred, any other value means
- * the event occurred.
+ * @ret 0 = event has not occurred or unknown error, any
+ * other value means the correct event occurred or an error.
*/
+
static int stu300_event_occurred(struct stu300_dev *dev,
enum stu300_event mr_event) {
u32 status1;
@@ -206,11 +230,28 @@ static int stu300_event_occurred(struct stu300_dev *dev,
/* What event happened? */
status1 = stu300_r8(dev->virtbase + I2C_SR1);
+
if (!(status1 & I2C_SR1_EVF_IND))
/* No event at all */
return 0;
+
status2 = stu300_r8(dev->virtbase + I2C_SR2);
+ /* Block any multiple interrupts */
+ stu300_irq_disable(dev);
+
+ /* Check for errors first */
+ if (status2 & I2C_SR2_AF_IND) {
+ dev->cmd_err = STU300_ERROR_ACKNOWLEDGE_FAILURE;
+ return 1;
+ } else if (status2 & I2C_SR2_BERR_IND) {
+ dev->cmd_err = STU300_ERROR_BUS_ERROR;
+ return 1;
+ } else if (status2 & I2C_SR2_ARLO_IND) {
+ dev->cmd_err = STU300_ERROR_ARBITRATION_LOST;
+ return 1;
+ }
+
switch (mr_event) {
case STU300_EVENT_1:
if (status1 & I2C_SR1_ADSL_IND)
@@ -221,10 +262,6 @@ static int stu300_event_occurred(struct stu300_dev *dev,
case STU300_EVENT_7:
case STU300_EVENT_8:
if (status1 & I2C_SR1_BTF_IND) {
- if (status2 & I2C_SR2_AF_IND)
- dev->cmd_err = STU300_ERROR_ACKNOWLEDGE_FAILURE;
- else if (status2 & I2C_SR2_BERR_IND)
- dev->cmd_err = STU300_ERROR_BUS_ERROR;
return 1;
}
break;
@@ -240,8 +277,6 @@ static int stu300_event_occurred(struct stu300_dev *dev,
case STU300_EVENT_6:
if (status2 & I2C_SR2_ENDAD_IND) {
/* First check for any errors */
- if (status2 & I2C_SR2_AF_IND)
- dev->cmd_err = STU300_ERROR_ACKNOWLEDGE_FAILURE;
return 1;
}
break;
@@ -252,8 +287,15 @@ static int stu300_event_occurred(struct stu300_dev *dev,
default:
break;
}
- if (status2 & I2C_SR2_ARLO_IND)
- dev->cmd_err = STU300_ERROR_ARBITRATION_LOST;
+ /* If we get here, we're on thin ice.
+ * Here we are in a status where we have
+ * gotten a response that does not match
+ * what we requested.
+ */
+ dev->cmd_err = STU300_ERROR_UNKNOWN;
+ dev_err(&dev->pdev->dev,
+ "Unhandled interrupt! %d sr1: 0x%x sr2: 0x%x\n",
+ mr_event, status1, status2);
return 0;
}
@@ -262,21 +304,20 @@ static irqreturn_t stu300_irh(int irq, void *data)
struct stu300_dev *dev = data;
int res;
+ /* Just make sure that the block is clocked */
+ clk_enable(dev->clk);
+
/* See if this was what we were waiting for */
spin_lock(&dev->cmd_issue_lock);
- if (dev->cmd_event != STU300_EVENT_NONE) {
- res = stu300_event_occurred(dev, dev->cmd_event);
- if (res || dev->cmd_err != STU300_ERROR_NONE) {
- u32 val;
-
- complete(&dev->cmd_complete);
- /* Block any multiple interrupts */
- val = stu300_r8(dev->virtbase + I2C_CR);
- val &= ~I2C_CR_INTERRUPT_ENABLE;
- stu300_wr8(val, dev->virtbase + I2C_CR);
- }
- }
+
+ res = stu300_event_occurred(dev, dev->cmd_event);
+ if (res || dev->cmd_err != STU300_ERROR_NONE)
+ complete(&dev->cmd_complete);
+
spin_unlock(&dev->cmd_issue_lock);
+
+ clk_disable(dev->clk);
+
return IRQ_HANDLED;
}
@@ -308,7 +349,6 @@ static int stu300_start_and_await_event(struct stu300_dev *dev,
stu300_wr8(cr_value, dev->virtbase + I2C_CR);
ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
STU300_TIMEOUT);
-
if (ret < 0) {
dev_err(&dev->pdev->dev,
"wait_for_completion_interruptible_timeout() "
@@ -342,7 +382,6 @@ static int stu300_await_event(struct stu300_dev *dev,
enum stu300_event mr_event)
{
int ret;
- u32 val;
if (unlikely(irqs_disabled())) {
/* TODO: implement polling for this case if need be. */
@@ -354,36 +393,18 @@ static int stu300_await_event(struct stu300_dev *dev,
/* Is it already here? */
spin_lock_irq(&dev->cmd_issue_lock);
dev->cmd_err = STU300_ERROR_NONE;
- if (stu300_event_occurred(dev, mr_event)) {
- spin_unlock_irq(&dev->cmd_issue_lock);
- goto exit_await_check_err;
- }
- init_completion(&dev->cmd_complete);
- dev->cmd_err = STU300_ERROR_NONE;
dev->cmd_event = mr_event;
- /* Turn on the I2C interrupt for current operation */
- val = stu300_r8(dev->virtbase + I2C_CR);
- val |= I2C_CR_INTERRUPT_ENABLE;
- stu300_wr8(val, dev->virtbase + I2C_CR);
-
- /* Twice paranoia (possible HW glitch) */
- stu300_wr8(val, dev->virtbase + I2C_CR);
+ init_completion(&dev->cmd_complete);
- /* Check again: is it already here? */
- if (unlikely(stu300_event_occurred(dev, mr_event))) {
- /* Disable IRQ again. */
- val &= ~I2C_CR_INTERRUPT_ENABLE;
- stu300_wr8(val, dev->virtbase + I2C_CR);
- spin_unlock_irq(&dev->cmd_issue_lock);
- goto exit_await_check_err;
- }
+ /* Turn on the I2C interrupt for current operation */
+ stu300_irq_enable(dev);
/* Unlock the command block and wait for the event to occur */
spin_unlock_irq(&dev->cmd_issue_lock);
+
ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
STU300_TIMEOUT);
-
if (ret < 0) {
dev_err(&dev->pdev->dev,
"wait_for_completion_interruptible_timeout()"
@@ -401,7 +422,6 @@ static int stu300_await_event(struct stu300_dev *dev,
return -ETIMEDOUT;
}
- exit_await_check_err:
if (dev->cmd_err != STU300_ERROR_NONE) {
if (mr_event != STU300_EVENT_6) {
dev_err(&dev->pdev->dev, "controller "
@@ -457,18 +477,19 @@ struct stu300_clkset {
};
static const struct stu300_clkset stu300_clktable[] = {
- { 0, 0xFFU },
- { 2500000, I2C_OAR2_FR_25_10MHZ },
- { 10000000, I2C_OAR2_FR_10_1667MHZ },
- { 16670000, I2C_OAR2_FR_1667_2667MHZ },
- { 26670000, I2C_OAR2_FR_2667_40MHZ },
- { 40000000, I2C_OAR2_FR_40_5333MHZ },
- { 53330000, I2C_OAR2_FR_5333_66MHZ },
- { 66000000, I2C_OAR2_FR_66_80MHZ },
- { 80000000, I2C_OAR2_FR_80_100MHZ },
+ { 0, 0xFFU },
+ { 2500000, I2C_OAR2_FR_25_10MHZ },
+ { 10000000, I2C_OAR2_FR_10_1667MHZ },
+ { 16670000, I2C_OAR2_FR_1667_2667MHZ },
+ { 26670000, I2C_OAR2_FR_2667_40MHZ },
+ { 40000000, I2C_OAR2_FR_40_5333MHZ },
+ { 53330000, I2C_OAR2_FR_5333_66MHZ },
+ { 66000000, I2C_OAR2_FR_66_80MHZ },
+ { 80000000, I2C_OAR2_FR_80_100MHZ },
{ 100000000, 0xFFU },
};
+
static int stu300_set_clk(struct stu300_dev *dev, unsigned long clkrate)
{
@@ -494,10 +515,10 @@ static int stu300_set_clk(struct stu300_dev *dev, unsigned long clkrate)
if (dev->speed > 100000)
/* Fast Mode I2C */
- val = ((clkrate/dev->speed)-9)/3;
+ val = ((clkrate/dev->speed) - 9)/3 + 1;
else
/* Standard Mode I2C */
- val = ((clkrate/dev->speed)-7)/2;
+ val = ((clkrate/dev->speed) - 7)/2 + 1;
/* According to spec the divider must be > 2 */
if (val < 0x002) {
@@ -557,6 +578,7 @@ static int stu300_init_hw(struct stu300_dev *dev)
*/
clkrate = clk_get_rate(dev->clk);
ret = stu300_set_clk(dev, clkrate);
+
if (ret)
return ret;
/*
@@ -641,7 +663,6 @@ static int stu300_xfer_msg(struct i2c_adapter *adap,
int attempts = 0;
struct stu300_dev *dev = i2c_get_adapdata(adap);
-
clk_enable(dev->clk);
/* Remove this if (0) to trace each and every message. */
@@ -715,14 +736,15 @@ static int stu300_xfer_msg(struct i2c_adapter *adap,
if (attempts < NUM_ADDR_RESEND_ATTEMPTS && attempts > 0) {
dev_dbg(&dev->pdev->dev, "managed to get address "
- "through after %d attempts\n", attempts);
+ "through after %d attempts\n", attempts);
} else if (attempts == NUM_ADDR_RESEND_ATTEMPTS) {
dev_dbg(&dev->pdev->dev, "I give up, tried %d times "
- "to resend address.\n",
- NUM_ADDR_RESEND_ATTEMPTS);
+ "to resend address.\n",
+ NUM_ADDR_RESEND_ATTEMPTS);
goto exit_disable;
}
+
if (msg->flags & I2C_M_RD) {
/* READ: we read the actual bytes one at a time */
for (i = 0; i < msg->len; i++) {
@@ -804,8 +826,10 @@ static int stu300_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
{
int ret = -1;
int i;
+
struct stu300_dev *dev = i2c_get_adapdata(adap);
dev->msg_len = num;
+
for (i = 0; i < num; i++) {
/*
* Another driver appears to send stop for each message,
@@ -817,6 +841,7 @@ static int stu300_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
dev->msg_index = i;
ret = stu300_xfer_msg(adap, &msgs[i], (i == (num - 1)));
+
if (ret != 0) {
num = ret;
break;
@@ -845,6 +870,7 @@ stu300_probe(struct platform_device *pdev)
struct resource *res;
int bus_nr;
int ret = 0;
+ char clk_name[] = "I2C0";
dev = kzalloc(sizeof(struct stu300_dev), GFP_KERNEL);
if (!dev) {
@@ -854,7 +880,8 @@ stu300_probe(struct platform_device *pdev)
}
bus_nr = pdev->id;
- dev->clk = clk_get(&pdev->dev, NULL);
+ clk_name[3] += (char)bus_nr;
+ dev->clk = clk_get(&pdev->dev, clk_name);
if (IS_ERR(dev->clk)) {
ret = PTR_ERR(dev->clk);
dev_err(&pdev->dev, "could not retrieve i2c bus clock\n");
diff --git a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c
index 527908ff298..063b933d864 100644
--- a/drivers/ide/ide-cs.c
+++ b/drivers/ide/ide-cs.c
@@ -408,6 +408,7 @@ static struct pcmcia_device_id ide_ids[] = {
PCMCIA_DEVICE_PROD_ID123("PCMCIA", "IDE CARD", "F1", 0x281f1c5d, 0x1907960c, 0xf7fde8b9),
PCMCIA_DEVICE_PROD_ID12("ARGOSY", "CD-ROM", 0x78f308dc, 0x66536591),
PCMCIA_DEVICE_PROD_ID12("ARGOSY", "PnPIDE", 0x78f308dc, 0x0c694728),
+ PCMCIA_DEVICE_PROD_ID12("CNF ", "CD-ROM", 0x46d7db81, 0x66536591),
PCMCIA_DEVICE_PROD_ID12("CNF CD-M", "CD-ROM", 0x7d93b852, 0x66536591),
PCMCIA_DEVICE_PROD_ID12("Creative Technology Ltd.", "PCMCIA CD-ROM Interface Card", 0xff8c8a45, 0xfe8020c4),
PCMCIA_DEVICE_PROD_ID12("Digital Equipment Corporation.", "Digital Mobile Media CD-ROM", 0x17692a66, 0xef1dcbde),
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index 4cfd084fa89..9a1d55b74d7 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -456,8 +456,11 @@ static int joydev_ioctl_common(struct joydev *joydev,
unsigned int cmd, void __user *argp)
{
struct input_dev *dev = joydev->handle.dev;
+ size_t len;
int i, j;
+ const char *name;
+ /* Process fixed-sized commands. */
switch (cmd) {
case JS_SET_CAL:
@@ -499,9 +502,22 @@ static int joydev_ioctl_common(struct joydev *joydev,
return copy_to_user(argp, joydev->corr,
sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0;
- case JSIOCSAXMAP:
- if (copy_from_user(joydev->abspam, argp,
- sizeof(__u8) * (ABS_MAX + 1)))
+ }
+
+ /*
+ * Process variable-sized commands (the axis and button map commands
+ * are considered variable-sized to decouple them from the values of
+ * ABS_MAX and KEY_MAX).
+ */
+ switch (cmd & ~IOCSIZE_MASK) {
+
+ case (JSIOCSAXMAP & ~IOCSIZE_MASK):
+ len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->abspam));
+ /*
+ * FIXME: we should not copy into our axis map before
+ * validating the data.
+ */
+ if (copy_from_user(joydev->abspam, argp, len))
return -EFAULT;
for (i = 0; i < joydev->nabs; i++) {
@@ -511,13 +527,17 @@ static int joydev_ioctl_common(struct joydev *joydev,
}
return 0;
- case JSIOCGAXMAP:
- return copy_to_user(argp, joydev->abspam,
- sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0;
-
- case JSIOCSBTNMAP:
- if (copy_from_user(joydev->keypam, argp,
- sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)))
+ case (JSIOCGAXMAP & ~IOCSIZE_MASK):
+ len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->abspam));
+ return copy_to_user(argp, joydev->abspam, len) ? -EFAULT : 0;
+
+ case (JSIOCSBTNMAP & ~IOCSIZE_MASK):
+ len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->keypam));
+ /*
+ * FIXME: we should not copy into our keymap before
+ * validating the data.
+ */
+ if (copy_from_user(joydev->keypam, argp, len))
return -EFAULT;
for (i = 0; i < joydev->nkey; i++) {
@@ -529,25 +549,19 @@ static int joydev_ioctl_common(struct joydev *joydev,
return 0;
- case JSIOCGBTNMAP:
- return copy_to_user(argp, joydev->keypam,
- sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0;
+ case (JSIOCGBTNMAP & ~IOCSIZE_MASK):
+ len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->keypam));
+ return copy_to_user(argp, joydev->keypam, len) ? -EFAULT : 0;
- default:
- if ((cmd & ~IOCSIZE_MASK) == JSIOCGNAME(0)) {
- int len;
- const char *name = dev->name;
-
- if (!name)
- return 0;
- len = strlen(name) + 1;
- if (len > _IOC_SIZE(cmd))
- len = _IOC_SIZE(cmd);
- if (copy_to_user(argp, name, len))
- return -EFAULT;
- return len;
- }
+ case JSIOCGNAME(0):
+ name = dev->name;
+ if (!name)
+ return 0;
+
+ len = min_t(size_t, _IOC_SIZE(cmd), strlen(name) + 1);
+ return copy_to_user(argp, name, len) ? -EFAULT : len;
}
+
return -EINVAL;
}
diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c
index baabf830264..f6c688cae33 100644
--- a/drivers/input/joystick/iforce/iforce-main.c
+++ b/drivers/input/joystick/iforce/iforce-main.c
@@ -74,6 +74,7 @@ static struct iforce_device iforce_device[] = {
{ 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_avb_wheel, abs_wheel, ff_iforce },
{ 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel", btn_avb_tw, abs_wheel, ff_iforce }, //?
{ 0x061c, 0xc0a4, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, //?
+ { 0x061c, 0xc084, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce },
{ 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, //?
{ 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //?
{ 0x06f8, 0x0004, "Gullemot Jet Leader 3D", btn_joystick, abs_joystick, ff_iforce }, //?
diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c
index f83185aeb51..9f289d8f52c 100644
--- a/drivers/input/joystick/iforce/iforce-usb.c
+++ b/drivers/input/joystick/iforce/iforce-usb.c
@@ -223,6 +223,7 @@ static struct usb_device_id iforce_usb_ids [] = {
{ USB_DEVICE(0x05ef, 0x8884) }, /* AVB Mag Turbo Force */
{ USB_DEVICE(0x05ef, 0x8888) }, /* AVB Top Shot FFB Racing Wheel */
{ USB_DEVICE(0x061c, 0xc0a4) }, /* ACT LABS Force RS */
+ { USB_DEVICE(0x061c, 0xc084) }, /* ACT LABS Force RS */
{ USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */
{ USB_DEVICE(0x06f8, 0x0004) }, /* Guillemot Force Feedback Racing Wheel */
{ USB_DEVICE(0x06f8, 0xa302) }, /* Guillemot Jet Leader 3D */
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index 95fe0452dae..6c6a09b1c0f 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -880,6 +880,14 @@ static unsigned int atkbd_hp_zv6100_forced_release_keys[] = {
};
/*
+ * Perform fixup for HP (Compaq) Presario R4000 R4100 R4200 that don't generate
+ * release for their volume buttons
+ */
+static unsigned int atkbd_hp_r4000_forced_release_keys[] = {
+ 0xae, 0xb0, -1U
+};
+
+/*
* Samsung NC10,NC20 with Fn+F? key release not working
*/
static unsigned int atkbd_samsung_forced_release_keys[] = {
@@ -1537,6 +1545,33 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_hp_zv6100_forced_release_keys,
},
{
+ .ident = "HP Presario R4000",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4000"),
+ },
+ .callback = atkbd_setup_forced_release,
+ .driver_data = atkbd_hp_r4000_forced_release_keys,
+ },
+ {
+ .ident = "HP Presario R4100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4100"),
+ },
+ .callback = atkbd_setup_forced_release,
+ .driver_data = atkbd_hp_r4000_forced_release_keys,
+ },
+ {
+ .ident = "HP Presario R4200",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4200"),
+ },
+ .callback = atkbd_setup_forced_release,
+ .driver_data = atkbd_hp_r4000_forced_release_keys,
+ },
+ {
.ident = "Inventec Symphony",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"),
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index e9b2e7cb05b..541b981ff07 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -27,6 +27,7 @@ struct matrix_keypad {
const struct matrix_keypad_platform_data *pdata;
struct input_dev *input_dev;
unsigned short *keycodes;
+ unsigned int row_shift;
uint32_t last_key_state[MATRIX_MAX_COLS];
struct delayed_work work;
@@ -136,7 +137,7 @@ static void matrix_keypad_scan(struct work_struct *work)
if ((bits_changed & (1 << row)) == 0)
continue;
- code = (row << 4) + col;
+ code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
input_event(input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(input_dev,
keypad->keycodes[code],
@@ -317,6 +318,7 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
struct matrix_keypad *keypad;
struct input_dev *input_dev;
unsigned short *keycodes;
+ unsigned int row_shift;
int i;
int err;
@@ -332,14 +334,11 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
return -EINVAL;
}
- if (!keymap_data->max_keymap_size) {
- dev_err(&pdev->dev, "invalid keymap data supplied\n");
- return -EINVAL;
- }
+ row_shift = get_count_order(pdata->num_col_gpios);
keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL);
- keycodes = kzalloc(keymap_data->max_keymap_size *
- sizeof(keypad->keycodes),
+ keycodes = kzalloc((pdata->num_row_gpios << row_shift) *
+ sizeof(*keycodes),
GFP_KERNEL);
input_dev = input_allocate_device();
if (!keypad || !keycodes || !input_dev) {
@@ -350,6 +349,7 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
keypad->input_dev = input_dev;
keypad->pdata = pdata;
keypad->keycodes = keycodes;
+ keypad->row_shift = row_shift;
keypad->stopped = true;
INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan);
spin_lock_init(&keypad->lock);
@@ -363,7 +363,7 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
input_dev->keycode = keycodes;
input_dev->keycodesize = sizeof(*keycodes);
- input_dev->keycodemax = keymap_data->max_keymap_size;
+ input_dev->keycodemax = pdata->num_row_gpios << keypad->row_shift;
for (i = 0; i < keymap_data->keymap_size; i++) {
unsigned int key = keymap_data->keymap[i];
@@ -371,7 +371,7 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
unsigned int col = KEY_COL(key);
unsigned short code = KEY_VAL(key);
- keycodes[(row << 4) + col] = code;
+ keycodes[MATRIX_SCAN_CODE(row, col, row_shift)] = code;
__set_bit(code, input_dev->keybit);
}
__clear_bit(KEY_RESERVED, input_dev->keybit);
diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c
index 26e17a9a22e..27ee976eb54 100644
--- a/drivers/input/misc/wistron_btns.c
+++ b/drivers/input/misc/wistron_btns.c
@@ -611,6 +611,20 @@ static struct key_entry keymap_wistron_generic[] __initdata = {
{ KE_END, 0 }
};
+static struct key_entry keymap_prestigio[] __initdata = {
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x22, {KEY_REWIND} },
+ { KE_KEY, 0x23, {KEY_FORWARD} },
+ { KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x25, {KEY_STOPCD} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, 0 }
+};
+
+
/*
* If your machine is not here (which is currently rather likely), please send
* a list of buttons and their key codes (reported when loading this module
@@ -971,6 +985,8 @@ static int __init select_keymap(void)
if (keymap_name != NULL) {
if (strcmp (keymap_name, "1557/MS2141") == 0)
keymap = keymap_wistron_ms2141;
+ else if (strcmp (keymap_name, "prestigio") == 0)
+ keymap = keymap_prestigio;
else if (strcmp (keymap_name, "generic") == 0)
keymap = keymap_wistron_generic;
else {
diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c
index b587e2d576a..820e51673b2 100644
--- a/drivers/input/serio/hp_sdc_mlc.c
+++ b/drivers/input/serio/hp_sdc_mlc.c
@@ -296,7 +296,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc)
priv->tseq[3] = 0;
if (mlc->opacket & HIL_CTRL_APE) {
priv->tseq[3] |= HP_SDC_LPC_APE_IPF;
- down_trylock(&mlc->csem);
+ BUG_ON(down_trylock(&mlc->csem));
}
enqueue:
hp_sdc_enqueue_transaction(&priv->trans);
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 924e8ed7f2c..ccbf23ece8e 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -78,6 +78,14 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
},
},
{
+ .ident = "ASUS G1S",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_BOARD_NAME, "G1S"),
+ DMI_MATCH(DMI_BOARD_VERSION, "1.0"),
+ },
+ },
+ {
/* AUX LOOP command does not raise AUX IRQ */
.ident = "ASUS P65UP5",
.matches = {
@@ -374,6 +382,14 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"),
},
},
+ {
+ .ident = "Acer Aspire 5536",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
+ },
+ },
{ }
};
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index a9d5031b855..ea30c983a33 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -388,6 +388,32 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
return result;
}
+static int wacom_query_tablet_data(struct usb_interface *intf)
+{
+ unsigned char *rep_data;
+ int limit = 0;
+ int error;
+
+ rep_data = kmalloc(2, GFP_KERNEL);
+ if (!rep_data)
+ return -ENOMEM;
+
+ do {
+ rep_data[0] = 2;
+ rep_data[1] = 2;
+ error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
+ 2, rep_data, 2);
+ if (error >= 0)
+ error = usb_get_report(intf,
+ WAC_HID_FEATURE_REPORT, 2,
+ rep_data, 2);
+ } while ((error < 0 || rep_data[1] != 2) && limit++ < 5);
+
+ kfree(rep_data);
+
+ return error < 0 ? error : 0;
+}
+
static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
@@ -398,7 +424,6 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
struct wacom_features *features;
struct input_dev *input_dev;
int error = -ENOMEM;
- char rep_data[2], limit = 0;
struct hid_descriptor *hid_desc;
wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
@@ -489,20 +514,10 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
/*
* Ask the tablet to report tablet data if it is not a Tablet PC.
- * Repeat until it succeeds
+ * Note that if query fails it is not a hard failure.
*/
- if (wacom_wac->features->type != TABLETPC) {
- do {
- rep_data[0] = 2;
- rep_data[1] = 2;
- error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
- 2, rep_data, 2);
- if (error >= 0)
- error = usb_get_report(intf,
- WAC_HID_FEATURE_REPORT, 2,
- rep_data, 2);
- } while ((error < 0 || rep_data[1] != 2) && limit++ < 5);
- }
+ if (wacom_wac->features->type != TABLETPC)
+ wacom_query_tablet_data(intf);
usb_set_intfdata(intf, wacom);
return 0;
diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c
index 6954f550010..3a7a58222f8 100644
--- a/drivers/input/touchscreen/ucb1400_ts.c
+++ b/drivers/input/touchscreen/ucb1400_ts.c
@@ -170,11 +170,11 @@ static void ucb1400_handle_pending_irq(struct ucb1400_ts *ucb)
ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, isr);
ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
- if (isr & UCB_IE_TSPX) {
+ if (isr & UCB_IE_TSPX)
ucb1400_ts_irq_disable(ucb->ac97);
- enable_irq(ucb->irq);
- } else
- printk(KERN_ERR "ucb1400: unexpected IE_STATUS = %#x\n", isr);
+ else
+ dev_dbg(&ucb->ts_idev->dev, "ucb1400: unexpected IE_STATUS = %#x\n", isr);
+ enable_irq(ucb->irq);
}
static int ucb1400_ts_thread(void *_ucb)
@@ -345,6 +345,7 @@ static int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb)
static int ucb1400_ts_probe(struct platform_device *dev)
{
int error, x_res, y_res;
+ u16 fcsr;
struct ucb1400_ts *ucb = dev->dev.platform_data;
ucb->ts_idev = input_allocate_device();
@@ -382,6 +383,14 @@ static int ucb1400_ts_probe(struct platform_device *dev)
ucb->ts_idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
ucb->ts_idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ /*
+ * Enable ADC filter to prevent horrible jitter on Colibri.
+ * This also further reduces jitter on boards where ADCSYNC
+ * pin is connected.
+ */
+ fcsr = ucb1400_reg_read(ucb->ac97, UCB_FCSR);
+ ucb1400_reg_write(ucb->ac97, UCB_FCSR, fcsr | UCB_FCSR_AVE);
+
ucb1400_adc_enable(ucb->ac97);
x_res = ucb1400_ts_read_xres(ucb);
y_res = ucb1400_ts_read_yres(ucb);
diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c
index c3b661a666c..7e5f30dbc0a 100644
--- a/drivers/isdn/mISDN/l1oip_core.c
+++ b/drivers/isdn/mISDN/l1oip_core.c
@@ -1480,7 +1480,7 @@ l1oip_init(void)
return -ENOMEM;
l1oip_cnt = 0;
- while (type[l1oip_cnt] && l1oip_cnt < MAX_CARDS) {
+ while (l1oip_cnt < MAX_CARDS && type[l1oip_cnt]) {
switch (type[l1oip_cnt] & 0xff) {
case 1:
pri = 0;
diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/ledtrig-gpio.c
index a247ae63374..1bc5db4ece0 100644
--- a/drivers/leds/ledtrig-gpio.c
+++ b/drivers/leds/ledtrig-gpio.c
@@ -117,6 +117,9 @@ static ssize_t gpio_trig_inverted_store(struct device *dev,
gpio_data->inverted = !!inverted;
+ /* After inverting, we need to update the LED. */
+ schedule_work(&gpio_data->work);
+
return n;
}
static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show,
@@ -146,20 +149,26 @@ static ssize_t gpio_trig_gpio_store(struct device *dev,
return -EINVAL;
}
+ if (gpio_data->gpio == gpio)
+ return n;
+
if (!gpio) {
- free_irq(gpio_to_irq(gpio_data->gpio), led);
+ if (gpio_data->gpio != 0)
+ free_irq(gpio_to_irq(gpio_data->gpio), led);
+ gpio_data->gpio = 0;
return n;
}
- if (gpio_data->gpio > 0 && gpio_data->gpio != gpio)
- free_irq(gpio_to_irq(gpio_data->gpio), led);
-
- gpio_data->gpio = gpio;
ret = request_irq(gpio_to_irq(gpio), gpio_trig_irq,
IRQF_SHARED | IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
- if (ret)
+ if (ret) {
dev_err(dev, "request_irq failed with error %d\n", ret);
+ } else {
+ if (gpio_data->gpio != 0)
+ free_irq(gpio_to_irq(gpio_data->gpio), led);
+ gpio_data->gpio = gpio;
+ }
return ret ? ret : n;
}
@@ -211,7 +220,8 @@ static void gpio_trig_deactivate(struct led_classdev *led)
device_remove_file(led->dev, &dev_attr_inverted);
device_remove_file(led->dev, &dev_attr_desired_brightness);
flush_work(&gpio_data->work);
- free_irq(gpio_to_irq(gpio_data->gpio),led);
+ if (gpio_data->gpio != 0)
+ free_irq(gpio_to_irq(gpio_data->gpio), led);
kfree(gpio_data);
}
}
diff --git a/drivers/macintosh/via-maciisi.c b/drivers/macintosh/via-maciisi.c
index 4d686c0bdea..9ab5b0c34f0 100644
--- a/drivers/macintosh/via-maciisi.c
+++ b/drivers/macintosh/via-maciisi.c
@@ -288,7 +288,7 @@ static void maciisi_sync(struct adb_request *req)
}
/* This could be BAD... when the ADB controller doesn't respond
* for this long, it's probably not coming back :-( */
- if(count >= 50) /* Hopefully shouldn't happen */
+ if (count > 50) /* Hopefully shouldn't happen */
printk(KERN_ERR "maciisi_send_request: poll timed out!\n");
}
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c
index 3710ff88fc1..556acff3952 100644
--- a/drivers/md/dm-exception-store.c
+++ b/drivers/md/dm-exception-store.c
@@ -171,6 +171,14 @@ static int set_chunk_size(struct dm_exception_store *store,
*/
chunk_size_ulong = round_up(chunk_size_ulong, PAGE_SIZE >> 9);
+ return dm_exception_store_set_chunk_size(store, chunk_size_ulong,
+ error);
+}
+
+int dm_exception_store_set_chunk_size(struct dm_exception_store *store,
+ unsigned long chunk_size_ulong,
+ char **error)
+{
/* Check chunk_size is a power of 2 */
if (!is_power_of_2(chunk_size_ulong)) {
*error = "Chunk size is not a power of 2";
@@ -183,6 +191,11 @@ static int set_chunk_size(struct dm_exception_store *store,
return -EINVAL;
}
+ if (chunk_size_ulong > INT_MAX >> SECTOR_SHIFT) {
+ *error = "Chunk size is too high";
+ return -EINVAL;
+ }
+
store->chunk_size = chunk_size_ulong;
store->chunk_mask = chunk_size_ulong - 1;
store->chunk_shift = ffs(chunk_size_ulong) - 1;
diff --git a/drivers/md/dm-exception-store.h b/drivers/md/dm-exception-store.h
index 2442c8c0789..812c71872ba 100644
--- a/drivers/md/dm-exception-store.h
+++ b/drivers/md/dm-exception-store.h
@@ -168,6 +168,10 @@ static inline chunk_t sector_to_chunk(struct dm_exception_store *store,
int dm_exception_store_type_register(struct dm_exception_store_type *type);
int dm_exception_store_type_unregister(struct dm_exception_store_type *type);
+int dm_exception_store_set_chunk_size(struct dm_exception_store *store,
+ unsigned long chunk_size_ulong,
+ char **error);
+
int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
unsigned *args_used,
struct dm_exception_store **store);
diff --git a/drivers/md/dm-log-userspace-base.c b/drivers/md/dm-log-userspace-base.c
index e69b9656099..6e186b1a062 100644
--- a/drivers/md/dm-log-userspace-base.c
+++ b/drivers/md/dm-log-userspace-base.c
@@ -21,6 +21,7 @@ struct log_c {
struct dm_target *ti;
uint32_t region_size;
region_t region_count;
+ uint64_t luid;
char uuid[DM_UUID_LEN];
char *usr_argv_str;
@@ -63,7 +64,7 @@ static int userspace_do_request(struct log_c *lc, const char *uuid,
* restored.
*/
retry:
- r = dm_consult_userspace(uuid, request_type, data,
+ r = dm_consult_userspace(uuid, lc->luid, request_type, data,
data_size, rdata, rdata_size);
if (r != -ESRCH)
@@ -74,14 +75,15 @@ retry:
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2*HZ);
DMWARN("Attempting to contact userspace log server...");
- r = dm_consult_userspace(uuid, DM_ULOG_CTR, lc->usr_argv_str,
+ r = dm_consult_userspace(uuid, lc->luid, DM_ULOG_CTR,
+ lc->usr_argv_str,
strlen(lc->usr_argv_str) + 1,
NULL, NULL);
if (!r)
break;
}
DMINFO("Reconnected to userspace log server... DM_ULOG_CTR complete");
- r = dm_consult_userspace(uuid, DM_ULOG_RESUME, NULL,
+ r = dm_consult_userspace(uuid, lc->luid, DM_ULOG_RESUME, NULL,
0, NULL, NULL);
if (!r)
goto retry;
@@ -111,10 +113,9 @@ static int build_constructor_string(struct dm_target *ti,
return -ENOMEM;
}
- for (i = 0, str_size = 0; i < argc; i++)
- str_size += sprintf(str + str_size, "%s ", argv[i]);
- str_size += sprintf(str + str_size, "%llu",
- (unsigned long long)ti->len);
+ str_size = sprintf(str, "%llu", (unsigned long long)ti->len);
+ for (i = 0; i < argc; i++)
+ str_size += sprintf(str + str_size, " %s", argv[i]);
*ctr_str = str;
return str_size;
@@ -154,6 +155,9 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
return -ENOMEM;
}
+ /* The ptr value is sufficient for local unique id */
+ lc->luid = (uint64_t)lc;
+
lc->ti = ti;
if (strlen(argv[0]) > (DM_UUID_LEN - 1)) {
@@ -173,7 +177,7 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
}
/* Send table string */
- r = dm_consult_userspace(lc->uuid, DM_ULOG_CTR,
+ r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_CTR,
ctr_str, str_size, NULL, NULL);
if (r == -ESRCH) {
@@ -183,7 +187,7 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
/* Since the region size does not change, get it now */
rdata_size = sizeof(rdata);
- r = dm_consult_userspace(lc->uuid, DM_ULOG_GET_REGION_SIZE,
+ r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_GET_REGION_SIZE,
NULL, 0, (char *)&rdata, &rdata_size);
if (r) {
@@ -212,7 +216,7 @@ static void userspace_dtr(struct dm_dirty_log *log)
int r;
struct log_c *lc = log->context;
- r = dm_consult_userspace(lc->uuid, DM_ULOG_DTR,
+ r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_DTR,
NULL, 0,
NULL, NULL);
@@ -227,7 +231,7 @@ static int userspace_presuspend(struct dm_dirty_log *log)
int r;
struct log_c *lc = log->context;
- r = dm_consult_userspace(lc->uuid, DM_ULOG_PRESUSPEND,
+ r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_PRESUSPEND,
NULL, 0,
NULL, NULL);
@@ -239,7 +243,7 @@ static int userspace_postsuspend(struct dm_dirty_log *log)
int r;
struct log_c *lc = log->context;
- r = dm_consult_userspace(lc->uuid, DM_ULOG_POSTSUSPEND,
+ r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_POSTSUSPEND,
NULL, 0,
NULL, NULL);
@@ -252,7 +256,7 @@ static int userspace_resume(struct dm_dirty_log *log)
struct log_c *lc = log->context;
lc->in_sync_hint = 0;
- r = dm_consult_userspace(lc->uuid, DM_ULOG_RESUME,
+ r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_RESUME,
NULL, 0,
NULL, NULL);
@@ -561,6 +565,7 @@ static int userspace_status(struct dm_dirty_log *log, status_type_t status_type,
char *result, unsigned maxlen)
{
int r = 0;
+ char *table_args;
size_t sz = (size_t)maxlen;
struct log_c *lc = log->context;
@@ -577,8 +582,12 @@ static int userspace_status(struct dm_dirty_log *log, status_type_t status_type,
break;
case STATUSTYPE_TABLE:
sz = 0;
- DMEMIT("%s %u %s %s", log->type->name, lc->usr_argc + 1,
- lc->uuid, lc->usr_argv_str);
+ table_args = strstr(lc->usr_argv_str, " ");
+ BUG_ON(!table_args); /* There will always be a ' ' */
+ table_args++;
+
+ DMEMIT("%s %u %s %s ", log->type->name, lc->usr_argc,
+ lc->uuid, table_args);
break;
}
return (r) ? 0 : (int)sz;
diff --git a/drivers/md/dm-log-userspace-transfer.c b/drivers/md/dm-log-userspace-transfer.c
index 0ca1ee768a1..ba0edad2d04 100644
--- a/drivers/md/dm-log-userspace-transfer.c
+++ b/drivers/md/dm-log-userspace-transfer.c
@@ -108,7 +108,7 @@ static int fill_pkg(struct cn_msg *msg, struct dm_ulog_request *tfr)
*(pkg->data_size) = 0;
} else if (tfr->data_size > *(pkg->data_size)) {
DMERR("Insufficient space to receive package [%u] "
- "(%u vs %lu)", tfr->request_type,
+ "(%u vs %zu)", tfr->request_type,
tfr->data_size, *(pkg->data_size));
*(pkg->data_size) = 0;
@@ -147,7 +147,8 @@ static void cn_ulog_callback(void *data)
/**
* dm_consult_userspace
- * @uuid: log's uuid (must be DM_UUID_LEN in size)
+ * @uuid: log's universal unique identifier (must be DM_UUID_LEN in size)
+ * @luid: log's local unique identifier
* @request_type: found in include/linux/dm-log-userspace.h
* @data: data to tx to the server
* @data_size: size of data in bytes
@@ -163,7 +164,7 @@ static void cn_ulog_callback(void *data)
*
* Returns: 0 on success, -EXXX on failure
**/
-int dm_consult_userspace(const char *uuid, int request_type,
+int dm_consult_userspace(const char *uuid, uint64_t luid, int request_type,
char *data, size_t data_size,
char *rdata, size_t *rdata_size)
{
@@ -190,6 +191,7 @@ resend:
memset(tfr, 0, DM_ULOG_PREALLOCED_SIZE - overhead_size);
memcpy(tfr->uuid, uuid, DM_UUID_LEN);
+ tfr->luid = luid;
tfr->seq = dm_ulog_seq++;
/*
diff --git a/drivers/md/dm-log-userspace-transfer.h b/drivers/md/dm-log-userspace-transfer.h
index c26d8e4e271..04ee874f915 100644
--- a/drivers/md/dm-log-userspace-transfer.h
+++ b/drivers/md/dm-log-userspace-transfer.h
@@ -11,7 +11,7 @@
int dm_ulog_tfr_init(void);
void dm_ulog_tfr_exit(void);
-int dm_consult_userspace(const char *uuid, int request_type,
+int dm_consult_userspace(const char *uuid, uint64_t luid, int request_type,
char *data, size_t data_size,
char *rdata, size_t *rdata_size);
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index 9726577cde4..33f179e66bf 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -648,7 +648,13 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes)
*/
dm_rh_inc_pending(ms->rh, &sync);
dm_rh_inc_pending(ms->rh, &nosync);
- ms->log_failure = dm_rh_flush(ms->rh) ? 1 : 0;
+
+ /*
+ * If the flush fails on a previous call and succeeds here,
+ * we must not reset the log_failure variable. We need
+ * userspace interaction to do that.
+ */
+ ms->log_failure = dm_rh_flush(ms->rh) ? 1 : ms->log_failure;
/*
* Dispatch io.
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index 6e3fe4f1493..d5b2e08750d 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -106,6 +106,13 @@ struct pstore {
void *zero_area;
/*
+ * An area used for header. The header can be written
+ * concurrently with metadata (when invalidating the snapshot),
+ * so it needs a separate buffer.
+ */
+ void *header_area;
+
+ /*
* Used to keep track of which metadata area the data in
* 'chunk' refers to.
*/
@@ -148,16 +155,27 @@ static int alloc_area(struct pstore *ps)
*/
ps->area = vmalloc(len);
if (!ps->area)
- return r;
+ goto err_area;
ps->zero_area = vmalloc(len);
- if (!ps->zero_area) {
- vfree(ps->area);
- return r;
- }
+ if (!ps->zero_area)
+ goto err_zero_area;
memset(ps->zero_area, 0, len);
+ ps->header_area = vmalloc(len);
+ if (!ps->header_area)
+ goto err_header_area;
+
return 0;
+
+err_header_area:
+ vfree(ps->zero_area);
+
+err_zero_area:
+ vfree(ps->area);
+
+err_area:
+ return r;
}
static void free_area(struct pstore *ps)
@@ -169,6 +187,10 @@ static void free_area(struct pstore *ps)
if (ps->zero_area)
vfree(ps->zero_area);
ps->zero_area = NULL;
+
+ if (ps->header_area)
+ vfree(ps->header_area);
+ ps->header_area = NULL;
}
struct mdata_req {
@@ -188,7 +210,8 @@ static void do_metadata(struct work_struct *work)
/*
* Read or write a chunk aligned and sized block of data from a device.
*/
-static int chunk_io(struct pstore *ps, chunk_t chunk, int rw, int metadata)
+static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw,
+ int metadata)
{
struct dm_io_region where = {
.bdev = ps->store->cow->bdev,
@@ -198,7 +221,7 @@ static int chunk_io(struct pstore *ps, chunk_t chunk, int rw, int metadata)
struct dm_io_request io_req = {
.bi_rw = rw,
.mem.type = DM_IO_VMA,
- .mem.ptr.vma = ps->area,
+ .mem.ptr.vma = area,
.client = ps->io_client,
.notify.fn = NULL,
};
@@ -240,7 +263,7 @@ static int area_io(struct pstore *ps, int rw)
chunk = area_location(ps, ps->current_area);
- r = chunk_io(ps, chunk, rw, 0);
+ r = chunk_io(ps, ps->area, chunk, rw, 0);
if (r)
return r;
@@ -254,20 +277,7 @@ static void zero_memory_area(struct pstore *ps)
static int zero_disk_area(struct pstore *ps, chunk_t area)
{
- struct dm_io_region where = {
- .bdev = ps->store->cow->bdev,
- .sector = ps->store->chunk_size * area_location(ps, area),
- .count = ps->store->chunk_size,
- };
- struct dm_io_request io_req = {
- .bi_rw = WRITE,
- .mem.type = DM_IO_VMA,
- .mem.ptr.vma = ps->zero_area,
- .client = ps->io_client,
- .notify.fn = NULL,
- };
-
- return dm_io(&io_req, 1, &where, NULL);
+ return chunk_io(ps, ps->zero_area, area_location(ps, area), WRITE, 0);
}
static int read_header(struct pstore *ps, int *new_snapshot)
@@ -276,6 +286,7 @@ static int read_header(struct pstore *ps, int *new_snapshot)
struct disk_header *dh;
chunk_t chunk_size;
int chunk_size_supplied = 1;
+ char *chunk_err;
/*
* Use default chunk size (or hardsect_size, if larger) if none supplied
@@ -297,11 +308,11 @@ static int read_header(struct pstore *ps, int *new_snapshot)
if (r)
return r;
- r = chunk_io(ps, 0, READ, 1);
+ r = chunk_io(ps, ps->header_area, 0, READ, 1);
if (r)
goto bad;
- dh = (struct disk_header *) ps->area;
+ dh = ps->header_area;
if (le32_to_cpu(dh->magic) == 0) {
*new_snapshot = 1;
@@ -319,20 +330,25 @@ static int read_header(struct pstore *ps, int *new_snapshot)
ps->version = le32_to_cpu(dh->version);
chunk_size = le32_to_cpu(dh->chunk_size);
- if (!chunk_size_supplied || ps->store->chunk_size == chunk_size)
+ if (ps->store->chunk_size == chunk_size)
return 0;
- DMWARN("chunk size %llu in device metadata overrides "
- "table chunk size of %llu.",
- (unsigned long long)chunk_size,
- (unsigned long long)ps->store->chunk_size);
+ if (chunk_size_supplied)
+ DMWARN("chunk size %llu in device metadata overrides "
+ "table chunk size of %llu.",
+ (unsigned long long)chunk_size,
+ (unsigned long long)ps->store->chunk_size);
/* We had a bogus chunk_size. Fix stuff up. */
free_area(ps);
- ps->store->chunk_size = chunk_size;
- ps->store->chunk_mask = chunk_size - 1;
- ps->store->chunk_shift = ffs(chunk_size) - 1;
+ r = dm_exception_store_set_chunk_size(ps->store, chunk_size,
+ &chunk_err);
+ if (r) {
+ DMERR("invalid on-disk chunk size %llu: %s.",
+ (unsigned long long)chunk_size, chunk_err);
+ return r;
+ }
r = dm_io_client_resize(sectors_to_pages(ps->store->chunk_size),
ps->io_client);
@@ -351,15 +367,15 @@ static int write_header(struct pstore *ps)
{
struct disk_header *dh;
- memset(ps->area, 0, ps->store->chunk_size << SECTOR_SHIFT);
+ memset(ps->header_area, 0, ps->store->chunk_size << SECTOR_SHIFT);
- dh = (struct disk_header *) ps->area;
+ dh = ps->header_area;
dh->magic = cpu_to_le32(SNAP_MAGIC);
dh->valid = cpu_to_le32(ps->valid);
dh->version = cpu_to_le32(ps->version);
dh->chunk_size = cpu_to_le32(ps->store->chunk_size);
- return chunk_io(ps, 0, WRITE, 1);
+ return chunk_io(ps, ps->header_area, 0, WRITE, 1);
}
/*
@@ -679,6 +695,8 @@ static int persistent_ctr(struct dm_exception_store *store,
ps->valid = 1;
ps->version = SNAPSHOT_DISK_VERSION;
ps->area = NULL;
+ ps->zero_area = NULL;
+ ps->header_area = NULL;
ps->next_free = 2; /* skipping the header and first area */
ps->current_committed = 0;
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index d573165cd2b..57f1bf7f3b7 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -1176,6 +1176,15 @@ static int snapshot_status(struct dm_target *ti, status_type_t type,
return 0;
}
+static int snapshot_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn, void *data)
+{
+ struct dm_snapshot *snap = ti->private;
+
+ return fn(ti, snap->origin, 0, ti->len, data);
+}
+
+
/*-----------------------------------------------------------------
* Origin methods
*---------------------------------------------------------------*/
@@ -1410,20 +1419,29 @@ static int origin_status(struct dm_target *ti, status_type_t type, char *result,
return 0;
}
+static int origin_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn, void *data)
+{
+ struct dm_dev *dev = ti->private;
+
+ return fn(ti, dev, 0, ti->len, data);
+}
+
static struct target_type origin_target = {
.name = "snapshot-origin",
- .version = {1, 6, 0},
+ .version = {1, 7, 0},
.module = THIS_MODULE,
.ctr = origin_ctr,
.dtr = origin_dtr,
.map = origin_map,
.resume = origin_resume,
.status = origin_status,
+ .iterate_devices = origin_iterate_devices,
};
static struct target_type snapshot_target = {
.name = "snapshot",
- .version = {1, 6, 0},
+ .version = {1, 7, 0},
.module = THIS_MODULE,
.ctr = snapshot_ctr,
.dtr = snapshot_dtr,
@@ -1431,6 +1449,7 @@ static struct target_type snapshot_target = {
.end_io = snapshot_end_io,
.resume = snapshot_resume,
.status = snapshot_status,
+ .iterate_devices = snapshot_iterate_devices,
};
static int __init dm_snapshot_init(void)
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c
index 4e0e5937e42..3e563d25173 100644
--- a/drivers/md/dm-stripe.c
+++ b/drivers/md/dm-stripe.c
@@ -329,9 +329,19 @@ static int stripe_iterate_devices(struct dm_target *ti,
return ret;
}
+static void stripe_io_hints(struct dm_target *ti,
+ struct queue_limits *limits)
+{
+ struct stripe_c *sc = ti->private;
+ unsigned chunk_size = (sc->chunk_mask + 1) << 9;
+
+ blk_limits_io_min(limits, chunk_size);
+ limits->io_opt = chunk_size * sc->stripes;
+}
+
static struct target_type stripe_target = {
.name = "striped",
- .version = {1, 2, 0},
+ .version = {1, 3, 0},
.module = THIS_MODULE,
.ctr = stripe_ctr,
.dtr = stripe_dtr,
@@ -339,6 +349,7 @@ static struct target_type stripe_target = {
.end_io = stripe_end_io,
.status = stripe_status,
.iterate_devices = stripe_iterate_devices,
+ .io_hints = stripe_io_hints,
};
int __init dm_stripe_init(void)
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index d952b344191..1a6cb3c7822 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -343,10 +343,10 @@ static void close_dev(struct dm_dev_internal *d, struct mapped_device *md)
}
/*
- * If possible, this checks an area of a destination device is valid.
+ * If possible, this checks an area of a destination device is invalid.
*/
-static int device_area_is_valid(struct dm_target *ti, struct dm_dev *dev,
- sector_t start, sector_t len, void *data)
+static int device_area_is_invalid(struct dm_target *ti, struct dm_dev *dev,
+ sector_t start, sector_t len, void *data)
{
struct queue_limits *limits = data;
struct block_device *bdev = dev->bdev;
@@ -357,36 +357,40 @@ static int device_area_is_valid(struct dm_target *ti, struct dm_dev *dev,
char b[BDEVNAME_SIZE];
if (!dev_size)
- return 1;
+ return 0;
if ((start >= dev_size) || (start + len > dev_size)) {
- DMWARN("%s: %s too small for target",
- dm_device_name(ti->table->md), bdevname(bdev, b));
- return 0;
+ DMWARN("%s: %s too small for target: "
+ "start=%llu, len=%llu, dev_size=%llu",
+ dm_device_name(ti->table->md), bdevname(bdev, b),
+ (unsigned long long)start,
+ (unsigned long long)len,
+ (unsigned long long)dev_size);
+ return 1;
}
if (logical_block_size_sectors <= 1)
- return 1;
+ return 0;
if (start & (logical_block_size_sectors - 1)) {
DMWARN("%s: start=%llu not aligned to h/w "
- "logical block size %hu of %s",
+ "logical block size %u of %s",
dm_device_name(ti->table->md),
(unsigned long long)start,
limits->logical_block_size, bdevname(bdev, b));
- return 0;
+ return 1;
}
if (len & (logical_block_size_sectors - 1)) {
DMWARN("%s: len=%llu not aligned to h/w "
- "logical block size %hu of %s",
+ "logical block size %u of %s",
dm_device_name(ti->table->md),
(unsigned long long)len,
limits->logical_block_size, bdevname(bdev, b));
- return 0;
+ return 1;
}
- return 1;
+ return 0;
}
/*
@@ -496,8 +500,15 @@ int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
}
if (blk_stack_limits(limits, &q->limits, start << 9) < 0)
- DMWARN("%s: target device %s is misaligned",
- dm_device_name(ti->table->md), bdevname(bdev, b));
+ DMWARN("%s: target device %s is misaligned: "
+ "physical_block_size=%u, logical_block_size=%u, "
+ "alignment_offset=%u, start=%llu",
+ dm_device_name(ti->table->md), bdevname(bdev, b),
+ q->limits.physical_block_size,
+ q->limits.logical_block_size,
+ q->limits.alignment_offset,
+ (unsigned long long) start << 9);
+
/*
* Check if merge fn is supported.
@@ -698,7 +709,7 @@ static int validate_hardware_logical_block_alignment(struct dm_table *table,
if (remaining) {
DMWARN("%s: table line %u (start sect %llu len %llu) "
- "not aligned to h/w logical block size %hu",
+ "not aligned to h/w logical block size %u",
dm_device_name(table->md), i,
(unsigned long long) ti->begin,
(unsigned long long) ti->len,
@@ -996,12 +1007,16 @@ int dm_calculate_queue_limits(struct dm_table *table,
ti->type->iterate_devices(ti, dm_set_device_limits,
&ti_limits);
+ /* Set I/O hints portion of queue limits */
+ if (ti->type->io_hints)
+ ti->type->io_hints(ti, &ti_limits);
+
/*
* Check each device area is consistent with the target's
* overall queue limits.
*/
- if (!ti->type->iterate_devices(ti, device_area_is_valid,
- &ti_limits))
+ if (ti->type->iterate_devices(ti, device_area_is_invalid,
+ &ti_limits))
return -EINVAL;
combine_limits:
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 8a311ea0d44..b4845b14740 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -738,16 +738,22 @@ static void rq_completed(struct mapped_device *md, int run_queue)
dm_put(md);
}
+static void free_rq_clone(struct request *clone)
+{
+ struct dm_rq_target_io *tio = clone->end_io_data;
+
+ blk_rq_unprep_clone(clone);
+ free_rq_tio(tio);
+}
+
static void dm_unprep_request(struct request *rq)
{
struct request *clone = rq->special;
- struct dm_rq_target_io *tio = clone->end_io_data;
rq->special = NULL;
rq->cmd_flags &= ~REQ_DONTPREP;
- blk_rq_unprep_clone(clone);
- free_rq_tio(tio);
+ free_rq_clone(clone);
}
/*
@@ -825,8 +831,7 @@ static void dm_end_request(struct request *clone, int error)
rq->sense_len = clone->sense_len;
}
- BUG_ON(clone->bio);
- free_rq_tio(tio);
+ free_rq_clone(clone);
blk_end_request_all(rq, error);
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index 5810fa906af..5fe39c2a3d2 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -220,6 +220,7 @@ static int linear_run (mddev_t *mddev)
mddev->queue->unplug_fn = linear_unplug;
mddev->queue->backing_dev_info.congested_fn = linear_congested;
mddev->queue->backing_dev_info.congested_data = mddev;
+ md_integrity_register(mddev);
return 0;
}
@@ -256,6 +257,7 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
rcu_assign_pointer(mddev->private, newconf);
md_set_array_sectors(mddev, linear_size(mddev, 0, 0));
set_capacity(mddev->gendisk, mddev->array_sectors);
+ revalidate_disk(mddev->gendisk);
call_rcu(&oldconf->rcu, free_conf);
return 0;
}
diff --git a/drivers/md/md.c b/drivers/md/md.c
index d4351ff0849..9dd872000ce 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -359,6 +359,7 @@ static mddev_t * mddev_find(dev_t unit)
else
new->md_minor = MINOR(unit) >> MdpMinorShift;
+ mutex_init(&new->open_mutex);
mutex_init(&new->reconfig_mutex);
INIT_LIST_HEAD(&new->disks);
INIT_LIST_HEAD(&new->all_mddevs);
@@ -1308,7 +1309,12 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
}
if (mddev->level != LEVEL_MULTIPATH) {
int role;
- role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]);
+ if (rdev->desc_nr < 0 ||
+ rdev->desc_nr >= le32_to_cpu(sb->max_dev)) {
+ role = 0xffff;
+ rdev->desc_nr = -1;
+ } else
+ role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]);
switch(role) {
case 0xffff: /* spare */
break;
@@ -1394,8 +1400,14 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
if (rdev2->desc_nr+1 > max_dev)
max_dev = rdev2->desc_nr+1;
- if (max_dev > le32_to_cpu(sb->max_dev))
+ if (max_dev > le32_to_cpu(sb->max_dev)) {
+ int bmask;
sb->max_dev = cpu_to_le32(max_dev);
+ rdev->sb_size = max_dev * 2 + 256;
+ bmask = queue_logical_block_size(rdev->bdev->bd_disk->queue)-1;
+ if (rdev->sb_size & bmask)
+ rdev->sb_size = (rdev->sb_size | bmask) + 1;
+ }
for (i=0; i<max_dev;i++)
sb->dev_roles[i] = cpu_to_le16(0xfffe);
@@ -1487,37 +1499,76 @@ static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2)
static LIST_HEAD(pending_raid_disks);
-static void md_integrity_check(mdk_rdev_t *rdev, mddev_t *mddev)
+/*
+ * Try to register data integrity profile for an mddev
+ *
+ * This is called when an array is started and after a disk has been kicked
+ * from the array. It only succeeds if all working and active component devices
+ * are integrity capable with matching profiles.
+ */
+int md_integrity_register(mddev_t *mddev)
+{
+ mdk_rdev_t *rdev, *reference = NULL;
+
+ if (list_empty(&mddev->disks))
+ return 0; /* nothing to do */
+ if (blk_get_integrity(mddev->gendisk))
+ return 0; /* already registered */
+ list_for_each_entry(rdev, &mddev->disks, same_set) {
+ /* skip spares and non-functional disks */
+ if (test_bit(Faulty, &rdev->flags))
+ continue;
+ if (rdev->raid_disk < 0)
+ continue;
+ /*
+ * If at least one rdev is not integrity capable, we can not
+ * enable data integrity for the md device.
+ */
+ if (!bdev_get_integrity(rdev->bdev))
+ return -EINVAL;
+ if (!reference) {
+ /* Use the first rdev as the reference */
+ reference = rdev;
+ continue;
+ }
+ /* does this rdev's profile match the reference profile? */
+ if (blk_integrity_compare(reference->bdev->bd_disk,
+ rdev->bdev->bd_disk) < 0)
+ return -EINVAL;
+ }
+ /*
+ * All component devices are integrity capable and have matching
+ * profiles, register the common profile for the md device.
+ */
+ if (blk_integrity_register(mddev->gendisk,
+ bdev_get_integrity(reference->bdev)) != 0) {
+ printk(KERN_ERR "md: failed to register integrity for %s\n",
+ mdname(mddev));
+ return -EINVAL;
+ }
+ printk(KERN_NOTICE "md: data integrity on %s enabled\n",
+ mdname(mddev));
+ return 0;
+}
+EXPORT_SYMBOL(md_integrity_register);
+
+/* Disable data integrity if non-capable/non-matching disk is being added */
+void md_integrity_add_rdev(mdk_rdev_t *rdev, mddev_t *mddev)
{
- struct mdk_personality *pers = mddev->pers;
- struct gendisk *disk = mddev->gendisk;
struct blk_integrity *bi_rdev = bdev_get_integrity(rdev->bdev);
- struct blk_integrity *bi_mddev = blk_get_integrity(disk);
+ struct blk_integrity *bi_mddev = blk_get_integrity(mddev->gendisk);
- /* Data integrity passthrough not supported on RAID 4, 5 and 6 */
- if (pers && pers->level >= 4 && pers->level <= 6)
+ if (!bi_mddev) /* nothing to do */
return;
-
- /* If rdev is integrity capable, register profile for mddev */
- if (!bi_mddev && bi_rdev) {
- if (blk_integrity_register(disk, bi_rdev))
- printk(KERN_ERR "%s: %s Could not register integrity!\n",
- __func__, disk->disk_name);
- else
- printk(KERN_NOTICE "Enabling data integrity on %s\n",
- disk->disk_name);
+ if (rdev->raid_disk < 0) /* skip spares */
return;
- }
-
- /* Check that mddev and rdev have matching profiles */
- if (blk_integrity_compare(disk, rdev->bdev->bd_disk) < 0) {
- printk(KERN_ERR "%s: %s/%s integrity mismatch!\n", __func__,
- disk->disk_name, rdev->bdev->bd_disk->disk_name);
- printk(KERN_NOTICE "Disabling data integrity on %s\n",
- disk->disk_name);
- blk_integrity_unregister(disk);
- }
+ if (bi_rdev && blk_integrity_compare(mddev->gendisk,
+ rdev->bdev->bd_disk) >= 0)
+ return;
+ printk(KERN_NOTICE "disabling data integrity on %s\n", mdname(mddev));
+ blk_integrity_unregister(mddev->gendisk);
}
+EXPORT_SYMBOL(md_integrity_add_rdev);
static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
{
@@ -1591,7 +1642,6 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
/* May as well allow recovery to be retried once */
mddev->recovery_disabled = 0;
- md_integrity_check(rdev, mddev);
return 0;
fail:
@@ -1925,17 +1975,14 @@ repeat:
/* otherwise we have to go forward and ... */
mddev->events ++;
if (!mddev->in_sync || mddev->recovery_cp != MaxSector) { /* not clean */
- /* .. if the array isn't clean, insist on an odd 'events' */
- if ((mddev->events&1)==0) {
- mddev->events++;
+ /* .. if the array isn't clean, an 'even' event must also go
+ * to spares. */
+ if ((mddev->events&1)==0)
nospares = 0;
- }
} else {
- /* otherwise insist on an even 'events' (for clean states) */
- if ((mddev->events&1)) {
- mddev->events++;
+ /* otherwise an 'odd' event must go to spares */
+ if ((mddev->events&1))
nospares = 0;
- }
}
}
@@ -2657,6 +2704,7 @@ level_store(mddev_t *mddev, const char *buf, size_t len)
ssize_t rv = len;
struct mdk_personality *pers;
void *priv;
+ mdk_rdev_t *rdev;
if (mddev->pers == NULL) {
if (len == 0)
@@ -2736,6 +2784,12 @@ level_store(mddev_t *mddev, const char *buf, size_t len)
mddev_suspend(mddev);
mddev->pers->stop(mddev);
module_put(mddev->pers->owner);
+ /* Invalidate devices that are now superfluous */
+ list_for_each_entry(rdev, &mddev->disks, same_set)
+ if (rdev->raid_disk >= mddev->raid_disks) {
+ rdev->raid_disk = -1;
+ clear_bit(In_sync, &rdev->flags);
+ }
mddev->pers = pers;
mddev->private = priv;
strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel));
@@ -3545,6 +3599,7 @@ max_sync_store(mddev_t *mddev, const char *buf, size_t len)
if (max < mddev->resync_min)
return -EINVAL;
if (max < mddev->resync_max &&
+ mddev->ro == 0 &&
test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
return -EBUSY;
@@ -3685,17 +3740,8 @@ array_size_store(mddev_t *mddev, const char *buf, size_t len)
mddev->array_sectors = sectors;
set_capacity(mddev->gendisk, mddev->array_sectors);
- if (mddev->pers) {
- struct block_device *bdev = bdget_disk(mddev->gendisk, 0);
-
- if (bdev) {
- mutex_lock(&bdev->bd_inode->i_mutex);
- i_size_write(bdev->bd_inode,
- (loff_t)mddev->array_sectors << 9);
- mutex_unlock(&bdev->bd_inode->i_mutex);
- bdput(bdev);
- }
- }
+ if (mddev->pers)
+ revalidate_disk(mddev->gendisk);
return len;
}
@@ -4048,10 +4094,6 @@ static int do_md_run(mddev_t * mddev)
}
strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel));
- if (pers->level >= 4 && pers->level <= 6)
- /* Cannot support integrity (yet) */
- blk_integrity_unregister(mddev->gendisk);
-
if (mddev->reshape_position != MaxSector &&
pers->start_reshape == NULL) {
/* This personality cannot handle reshaping... */
@@ -4189,6 +4231,7 @@ static int do_md_run(mddev_t * mddev)
md_wakeup_thread(mddev->thread);
md_wakeup_thread(mddev->sync_thread); /* possibly kick off a reshape */
+ revalidate_disk(mddev->gendisk);
mddev->changed = 1;
md_new_event(mddev);
sysfs_notify_dirent(mddev->sysfs_state);
@@ -4260,12 +4303,11 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open)
struct gendisk *disk = mddev->gendisk;
mdk_rdev_t *rdev;
+ mutex_lock(&mddev->open_mutex);
if (atomic_read(&mddev->openers) > is_open) {
printk("md: %s still in use.\n",mdname(mddev));
- return -EBUSY;
- }
-
- if (mddev->pers) {
+ err = -EBUSY;
+ } else if (mddev->pers) {
if (mddev->sync_thread) {
set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
@@ -4322,8 +4364,12 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open)
if (mode == 1)
set_disk_ro(disk, 1);
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+ err = 0;
}
-
+out:
+ mutex_unlock(&mddev->open_mutex);
+ if (err)
+ return err;
/*
* Free resources if final stop
*/
@@ -4389,7 +4435,6 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open)
blk_integrity_unregister(disk);
md_new_event(mddev);
sysfs_notify_dirent(mddev->sysfs_state);
-out:
return err;
}
@@ -5087,18 +5132,8 @@ static int update_size(mddev_t *mddev, sector_t num_sectors)
return -ENOSPC;
}
rv = mddev->pers->resize(mddev, num_sectors);
- if (!rv) {
- struct block_device *bdev;
-
- bdev = bdget_disk(mddev->gendisk, 0);
- if (bdev) {
- mutex_lock(&bdev->bd_inode->i_mutex);
- i_size_write(bdev->bd_inode,
- (loff_t)mddev->array_sectors << 9);
- mutex_unlock(&bdev->bd_inode->i_mutex);
- bdput(bdev);
- }
- }
+ if (!rv)
+ revalidate_disk(mddev->gendisk);
return rv;
}
@@ -5484,12 +5519,12 @@ static int md_open(struct block_device *bdev, fmode_t mode)
}
BUG_ON(mddev != bdev->bd_disk->private_data);
- if ((err = mutex_lock_interruptible_nested(&mddev->reconfig_mutex, 1)))
+ if ((err = mutex_lock_interruptible(&mddev->open_mutex)))
goto out;
err = 0;
atomic_inc(&mddev->openers);
- mddev_unlock(mddev);
+ mutex_unlock(&mddev->open_mutex);
check_disk_change(bdev);
out:
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 9430a110db9..f8fc188bc76 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -223,6 +223,16 @@ struct mddev_s
* so we don't loop trying */
int in_sync; /* know to not need resync */
+ /* 'open_mutex' avoids races between 'md_open' and 'do_md_stop', so
+ * that we are never stopping an array while it is open.
+ * 'reconfig_mutex' protects all other reconfiguration.
+ * These locks are separate due to conflicting interactions
+ * with bdev->bd_mutex.
+ * Lock ordering is:
+ * reconfig_mutex -> bd_mutex : e.g. do_md_run -> revalidate_disk
+ * bd_mutex -> open_mutex: e.g. __blkdev_get -> md_open
+ */
+ struct mutex open_mutex;
struct mutex reconfig_mutex;
atomic_t active; /* general refcount */
atomic_t openers; /* number of active opens */
@@ -431,5 +441,7 @@ extern int md_allow_write(mddev_t *mddev);
extern void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev);
extern void md_set_array_sectors(mddev_t *mddev, sector_t array_sectors);
extern int md_check_no_bitmap(mddev_t *mddev);
+extern int md_integrity_register(mddev_t *mddev);
+void md_integrity_add_rdev(mdk_rdev_t *rdev, mddev_t *mddev);
#endif /* _MD_MD_H */
diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c
index 237fe3fd235..7140909f666 100644
--- a/drivers/md/multipath.c
+++ b/drivers/md/multipath.c
@@ -313,6 +313,7 @@ static int multipath_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
set_bit(In_sync, &rdev->flags);
rcu_assign_pointer(p->rdev, rdev);
err = 0;
+ md_integrity_add_rdev(rdev, mddev);
break;
}
@@ -345,7 +346,9 @@ static int multipath_remove_disk(mddev_t *mddev, int number)
/* lost the race, try later */
err = -EBUSY;
p->rdev = rdev;
+ goto abort;
}
+ md_integrity_register(mddev);
}
abort:
@@ -519,7 +522,7 @@ static int multipath_run (mddev_t *mddev)
mddev->queue->unplug_fn = multipath_unplug;
mddev->queue->backing_dev_info.congested_fn = multipath_congested;
mddev->queue->backing_dev_info.congested_data = mddev;
-
+ md_integrity_register(mddev);
return 0;
out_free_conf:
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index 335f490dcad..898e2bdfee4 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -351,6 +351,7 @@ static int raid0_run(mddev_t *mddev)
blk_queue_merge_bvec(mddev->queue, raid0_mergeable_bvec);
dump_zones(mddev);
+ md_integrity_register(mddev);
return 0;
}
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 0569efba0c0..8726fd7ebce 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1144,7 +1144,7 @@ static int raid1_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
rcu_assign_pointer(p->rdev, rdev);
break;
}
-
+ md_integrity_add_rdev(rdev, mddev);
print_conf(conf);
return err;
}
@@ -1178,7 +1178,9 @@ static int raid1_remove_disk(mddev_t *mddev, int number)
/* lost the race, try later */
err = -EBUSY;
p->rdev = rdev;
+ goto abort;
}
+ md_integrity_register(mddev);
}
abort:
@@ -2067,7 +2069,7 @@ static int run(mddev_t *mddev)
mddev->queue->unplug_fn = raid1_unplug;
mddev->queue->backing_dev_info.congested_fn = raid1_congested;
mddev->queue->backing_dev_info.congested_data = mddev;
-
+ md_integrity_register(mddev);
return 0;
out_no_mem:
@@ -2132,6 +2134,7 @@ static int raid1_resize(mddev_t *mddev, sector_t sectors)
return -EINVAL;
set_capacity(mddev->gendisk, mddev->array_sectors);
mddev->changed = 1;
+ revalidate_disk(mddev->gendisk);
if (sectors > mddev->dev_sectors &&
mddev->recovery_cp == MaxSector) {
mddev->recovery_cp = mddev->dev_sectors;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 7298a5e5a18..3d9020cf6f6 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1170,6 +1170,7 @@ static int raid10_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
break;
}
+ md_integrity_add_rdev(rdev, mddev);
print_conf(conf);
return err;
}
@@ -1203,7 +1204,9 @@ static int raid10_remove_disk(mddev_t *mddev, int number)
/* lost the race, try later */
err = -EBUSY;
p->rdev = rdev;
+ goto abort;
}
+ md_integrity_register(mddev);
}
abort:
@@ -2225,6 +2228,7 @@ static int run(mddev_t *mddev)
if (conf->near_copies < mddev->raid_disks)
blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec);
+ md_integrity_register(mddev);
return 0;
out_free_conf:
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 37835538b58..b8a2c5dc67b 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -3785,7 +3785,7 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped
conf->reshape_progress < raid5_size(mddev, 0, 0)) {
sector_nr = raid5_size(mddev, 0, 0)
- conf->reshape_progress;
- } else if (mddev->delta_disks > 0 &&
+ } else if (mddev->delta_disks >= 0 &&
conf->reshape_progress > 0)
sector_nr = conf->reshape_progress;
sector_div(sector_nr, new_data_disks);
@@ -3999,6 +3999,9 @@ static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *ski
return 0;
}
+ /* Allow raid5_quiesce to complete */
+ wait_event(conf->wait_for_overlap, conf->quiesce != 2);
+
if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
return reshape_request(mddev, sector_nr, skipped);
@@ -4316,6 +4319,15 @@ raid5_size(mddev_t *mddev, sector_t sectors, int raid_disks)
return sectors * (raid_disks - conf->max_degraded);
}
+static void free_conf(raid5_conf_t *conf)
+{
+ shrink_stripes(conf);
+ safe_put_page(conf->spare_page);
+ kfree(conf->disks);
+ kfree(conf->stripe_hashtbl);
+ kfree(conf);
+}
+
static raid5_conf_t *setup_conf(mddev_t *mddev)
{
raid5_conf_t *conf;
@@ -4447,11 +4459,7 @@ static raid5_conf_t *setup_conf(mddev_t *mddev)
abort:
if (conf) {
- shrink_stripes(conf);
- safe_put_page(conf->spare_page);
- kfree(conf->disks);
- kfree(conf->stripe_hashtbl);
- kfree(conf);
+ free_conf(conf);
return ERR_PTR(-EIO);
} else
return ERR_PTR(-ENOMEM);
@@ -4501,7 +4509,26 @@ static int run(mddev_t *mddev)
(old_disks-max_degraded));
/* here_old is the first stripe that we might need to read
* from */
- if (here_new >= here_old) {
+ if (mddev->delta_disks == 0) {
+ /* We cannot be sure it is safe to start an in-place
+ * reshape. It is only safe if user-space if monitoring
+ * and taking constant backups.
+ * mdadm always starts a situation like this in
+ * readonly mode so it can take control before
+ * allowing any writes. So just check for that.
+ */
+ if ((here_new * mddev->new_chunk_sectors !=
+ here_old * mddev->chunk_sectors) ||
+ mddev->ro == 0) {
+ printk(KERN_ERR "raid5: in-place reshape must be started"
+ " in read-only mode - aborting\n");
+ return -EINVAL;
+ }
+ } else if (mddev->delta_disks < 0
+ ? (here_new * mddev->new_chunk_sectors <=
+ here_old * mddev->chunk_sectors)
+ : (here_new * mddev->new_chunk_sectors >=
+ here_old * mddev->chunk_sectors)) {
/* Reading from the same stripe as writing to - bad */
printk(KERN_ERR "raid5: reshape_position too early for "
"auto-recovery - aborting.\n");
@@ -4629,12 +4656,8 @@ abort:
md_unregister_thread(mddev->thread);
mddev->thread = NULL;
if (conf) {
- shrink_stripes(conf);
print_raid5_conf(conf);
- safe_put_page(conf->spare_page);
- kfree(conf->disks);
- kfree(conf->stripe_hashtbl);
- kfree(conf);
+ free_conf(conf);
}
mddev->private = NULL;
printk(KERN_ALERT "raid5: failed to run raid set %s\n", mdname(mddev));
@@ -4649,13 +4672,10 @@ static int stop(mddev_t *mddev)
md_unregister_thread(mddev->thread);
mddev->thread = NULL;
- shrink_stripes(conf);
- kfree(conf->stripe_hashtbl);
mddev->queue->backing_dev_info.congested_fn = NULL;
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
sysfs_remove_group(&mddev->kobj, &raid5_attrs_group);
- kfree(conf->disks);
- kfree(conf);
+ free_conf(conf);
mddev->private = NULL;
return 0;
}
@@ -4857,6 +4877,7 @@ static int raid5_resize(mddev_t *mddev, sector_t sectors)
return -EINVAL;
set_capacity(mddev->gendisk, mddev->array_sectors);
mddev->changed = 1;
+ revalidate_disk(mddev->gendisk);
if (sectors > mddev->dev_sectors && mddev->recovery_cp == MaxSector) {
mddev->recovery_cp = mddev->dev_sectors;
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
@@ -5002,7 +5023,7 @@ static int raid5_start_reshape(mddev_t *mddev)
spin_unlock_irqrestore(&conf->device_lock, flags);
}
mddev->raid_disks = conf->raid_disks;
- mddev->reshape_position = 0;
+ mddev->reshape_position = conf->reshape_progress;
set_bit(MD_CHANGE_DEVS, &mddev->flags);
clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
@@ -5057,7 +5078,6 @@ static void end_reshape(raid5_conf_t *conf)
*/
static void raid5_finish_reshape(mddev_t *mddev)
{
- struct block_device *bdev;
raid5_conf_t *conf = mddev->private;
if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
@@ -5066,15 +5086,7 @@ static void raid5_finish_reshape(mddev_t *mddev)
md_set_array_sectors(mddev, raid5_size(mddev, 0, 0));
set_capacity(mddev->gendisk, mddev->array_sectors);
mddev->changed = 1;
-
- bdev = bdget_disk(mddev->gendisk, 0);
- if (bdev) {
- mutex_lock(&bdev->bd_inode->i_mutex);
- i_size_write(bdev->bd_inode,
- (loff_t)mddev->array_sectors << 9);
- mutex_unlock(&bdev->bd_inode->i_mutex);
- bdput(bdev);
- }
+ revalidate_disk(mddev->gendisk);
} else {
int d;
mddev->degraded = conf->raid_disks;
@@ -5085,8 +5097,15 @@ static void raid5_finish_reshape(mddev_t *mddev)
mddev->degraded--;
for (d = conf->raid_disks ;
d < conf->raid_disks - mddev->delta_disks;
- d++)
- raid5_remove_disk(mddev, d);
+ d++) {
+ mdk_rdev_t *rdev = conf->disks[d].rdev;
+ if (rdev && raid5_remove_disk(mddev, d) == 0) {
+ char nm[20];
+ sprintf(nm, "rd%d", rdev->raid_disk);
+ sysfs_remove_link(&mddev->kobj, nm);
+ rdev->raid_disk = -1;
+ }
+ }
}
mddev->layout = conf->algorithm;
mddev->chunk_sectors = conf->chunk_sectors;
@@ -5106,12 +5125,18 @@ static void raid5_quiesce(mddev_t *mddev, int state)
case 1: /* stop all writes */
spin_lock_irq(&conf->device_lock);
- conf->quiesce = 1;
+ /* '2' tells resync/reshape to pause so that all
+ * active stripes can drain
+ */
+ conf->quiesce = 2;
wait_event_lock_irq(conf->wait_for_stripe,
atomic_read(&conf->active_stripes) == 0 &&
atomic_read(&conf->active_aligned_reads) == 0,
conf->device_lock, /* nothing */);
+ conf->quiesce = 1;
spin_unlock_irq(&conf->device_lock);
+ /* allow reshape to continue */
+ wake_up(&conf->wait_for_overlap);
break;
case 0: /* re-enable writes */
diff --git a/drivers/media/common/tuners/qt1010.c b/drivers/media/common/tuners/qt1010.c
index 825aa1412e6..9f5dba244cb 100644
--- a/drivers/media/common/tuners/qt1010.c
+++ b/drivers/media/common/tuners/qt1010.c
@@ -64,24 +64,22 @@ static int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val)
/* dump all registers */
static void qt1010_dump_regs(struct qt1010_priv *priv)
{
- char buf[52], buf2[4];
u8 reg, val;
for (reg = 0; ; reg++) {
if (reg % 16 == 0) {
if (reg)
- printk("%s\n", buf);
- sprintf(buf, "%02x: ", reg);
+ printk(KERN_CONT "\n");
+ printk(KERN_DEBUG "%02x:", reg);
}
if (qt1010_readreg(priv, reg, &val) == 0)
- sprintf(buf2, "%02x ", val);
+ printk(KERN_CONT " %02x", val);
else
- strcpy(buf2, "-- ");
- strcat(buf, buf2);
+ printk(KERN_CONT " --");
if (reg == 0x2f)
break;
}
- printk("%s\n", buf);
+ printk(KERN_CONT "\n");
}
static int qt1010_set_params(struct dvb_frontend *fe,
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
index aa20ce8cc66..f270e605da8 100644
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ b/drivers/media/common/tuners/tuner-xc2028.c
@@ -1119,8 +1119,8 @@ static int xc2028_sleep(struct dvb_frontend *fe)
struct xc2028_data *priv = fe->tuner_priv;
int rc = 0;
- /* Avoid firmware reload on slow devices */
- if (no_poweroff)
+ /* Avoid firmware reload on slow devices or if PM disabled */
+ if (no_poweroff || priv->ctrl.disable_power_mgmt)
return 0;
tuner_dbg("Putting xc2028/3028 into poweroff mode.\n");
diff --git a/drivers/media/common/tuners/tuner-xc2028.h b/drivers/media/common/tuners/tuner-xc2028.h
index 19de7928a74..a90c35d50ad 100644
--- a/drivers/media/common/tuners/tuner-xc2028.h
+++ b/drivers/media/common/tuners/tuner-xc2028.h
@@ -38,6 +38,7 @@ struct xc2028_ctrl {
unsigned int input1:1;
unsigned int vhfbw7:1;
unsigned int uhfbw8:1;
+ unsigned int disable_power_mgmt:1;
unsigned int demod;
enum firmware_type type:2;
};
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
index 4cb31e7c13c..26690dfb326 100644
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -81,7 +81,6 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
switch (req->cmd) {
case GET_CONFIG:
- case BOOT:
case READ_MEMORY:
case RECONNECT_USB:
case GET_IR_CODE:
@@ -100,6 +99,7 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
case WRITE_VIRTUAL_MEMORY:
case COPY_FIRMWARE:
case DOWNLOAD_FIRMWARE:
+ case BOOT:
break;
default:
err("unknown command:%d", req->cmd);
diff --git a/drivers/media/dvb/frontends/cx22700.c b/drivers/media/dvb/frontends/cx22700.c
index ace5cb17165..fbd838eca26 100644
--- a/drivers/media/dvb/frontends/cx22700.c
+++ b/drivers/media/dvb/frontends/cx22700.c
@@ -380,7 +380,7 @@ struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
struct cx22700_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct cx22700_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct cx22700_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c
index 5d1abe34bdd..00b5c7e91d5 100644
--- a/drivers/media/dvb/frontends/cx22702.c
+++ b/drivers/media/dvb/frontends/cx22702.c
@@ -580,7 +580,7 @@ struct dvb_frontend *cx22702_attach(const struct cx22702_config *config,
struct cx22702_state *state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct cx22702_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct cx22702_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c
index 87ae29db024..ffbcfabd83f 100644
--- a/drivers/media/dvb/frontends/cx24110.c
+++ b/drivers/media/dvb/frontends/cx24110.c
@@ -598,7 +598,7 @@ struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
int ret;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct cx24110_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct cx24110_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.c b/drivers/media/dvb/frontends/dvb_dummy_fe.c
index db8a937cc63..a7fc7e53a55 100644
--- a/drivers/media/dvb/frontends/dvb_dummy_fe.c
+++ b/drivers/media/dvb/frontends/dvb_dummy_fe.c
@@ -117,7 +117,7 @@ struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void)
struct dvb_dummy_fe_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
if (state == NULL) goto error;
/* create dvb_frontend */
@@ -137,7 +137,7 @@ struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void)
struct dvb_dummy_fe_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
if (state == NULL) goto error;
/* create dvb_frontend */
@@ -157,7 +157,7 @@ struct dvb_frontend *dvb_dummy_fe_qam_attach(void)
struct dvb_dummy_fe_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
if (state == NULL) goto error;
/* create dvb_frontend */
diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb/frontends/l64781.c
index e1e70e9e0cb..3051b64aa17 100644
--- a/drivers/media/dvb/frontends/l64781.c
+++ b/drivers/media/dvb/frontends/l64781.c
@@ -501,7 +501,7 @@ struct dvb_frontend* l64781_attach(const struct l64781_config* config,
{ .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct l64781_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct l64781_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/drivers/media/dvb/frontends/lgs8gl5.c b/drivers/media/dvb/frontends/lgs8gl5.c
index 855852fddf2..bb37ed289a0 100644
--- a/drivers/media/dvb/frontends/lgs8gl5.c
+++ b/drivers/media/dvb/frontends/lgs8gl5.c
@@ -387,7 +387,7 @@ lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c)
dprintk("%s\n", __func__);
/* Allocate memory for the internal state */
- state = kmalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/drivers/media/dvb/frontends/mt312.c b/drivers/media/dvb/frontends/mt312.c
index a621f727935..f69daaac78c 100644
--- a/drivers/media/dvb/frontends/mt312.c
+++ b/drivers/media/dvb/frontends/mt312.c
@@ -782,7 +782,7 @@ struct dvb_frontend *mt312_attach(const struct mt312_config *config,
struct mt312_state *state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct mt312_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct mt312_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/drivers/media/dvb/frontends/nxt6000.c b/drivers/media/dvb/frontends/nxt6000.c
index 0eef22dbf8a..a763ec756f7 100644
--- a/drivers/media/dvb/frontends/nxt6000.c
+++ b/drivers/media/dvb/frontends/nxt6000.c
@@ -545,7 +545,7 @@ struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
struct nxt6000_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct nxt6000_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct nxt6000_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c
index 8133ea3cddd..38e67accb8c 100644
--- a/drivers/media/dvb/frontends/or51132.c
+++ b/drivers/media/dvb/frontends/or51132.c
@@ -562,7 +562,7 @@ struct dvb_frontend* or51132_attach(const struct or51132_config* config,
struct or51132_state* state = NULL;
/* Allocate memory for the internal state */
- state = kmalloc(sizeof(struct or51132_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct or51132_state), GFP_KERNEL);
if (state == NULL)
return NULL;
diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c
index 16cf2fdd5d7..c709ce6771c 100644
--- a/drivers/media/dvb/frontends/or51211.c
+++ b/drivers/media/dvb/frontends/or51211.c
@@ -527,7 +527,7 @@ struct dvb_frontend* or51211_attach(const struct or51211_config* config,
struct or51211_state* state = NULL;
/* Allocate memory for the internal state */
- state = kmalloc(sizeof(struct or51211_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct or51211_state), GFP_KERNEL);
if (state == NULL)
return NULL;
diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb/frontends/s5h1409.c
index 3e08d985d6e..fb301151842 100644
--- a/drivers/media/dvb/frontends/s5h1409.c
+++ b/drivers/media/dvb/frontends/s5h1409.c
@@ -796,7 +796,7 @@ struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config,
u16 reg;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct s5h1409_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct s5h1409_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/drivers/media/dvb/frontends/s5h1411.c b/drivers/media/dvb/frontends/s5h1411.c
index 66e2dd6d6fe..d8adf1e3201 100644
--- a/drivers/media/dvb/frontends/s5h1411.c
+++ b/drivers/media/dvb/frontends/s5h1411.c
@@ -844,7 +844,7 @@ struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config,
u16 reg;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct s5h1411_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct s5h1411_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c
index 0bd16af8a6c..9552a22ccff 100644
--- a/drivers/media/dvb/frontends/si21xx.c
+++ b/drivers/media/dvb/frontends/si21xx.c
@@ -928,7 +928,7 @@ struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
dprintk("%s\n", __func__);
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct si21xx_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct si21xx_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/drivers/media/dvb/frontends/sp8870.c b/drivers/media/dvb/frontends/sp8870.c
index 1c9a9b4051b..b85eb60a893 100644
--- a/drivers/media/dvb/frontends/sp8870.c
+++ b/drivers/media/dvb/frontends/sp8870.c
@@ -557,7 +557,7 @@ struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
struct sp8870_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct sp8870_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct sp8870_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb/frontends/sp887x.c
index 559509ab4da..4a7c3d84260 100644
--- a/drivers/media/dvb/frontends/sp887x.c
+++ b/drivers/media/dvb/frontends/sp887x.c
@@ -557,7 +557,7 @@ struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
struct sp887x_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct sp887x_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct sp887x_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb/frontends/stv0288.c
index ff1194de34c..2930a5d6768 100644
--- a/drivers/media/dvb/frontends/stv0288.c
+++ b/drivers/media/dvb/frontends/stv0288.c
@@ -570,7 +570,7 @@ struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
int id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct stv0288_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct stv0288_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb/frontends/stv0297.c
index 62caf802ed9..4fd7479bb62 100644
--- a/drivers/media/dvb/frontends/stv0297.c
+++ b/drivers/media/dvb/frontends/stv0297.c
@@ -663,7 +663,7 @@ struct dvb_frontend *stv0297_attach(const struct stv0297_config *config,
struct stv0297_state *state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct stv0297_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct stv0297_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c
index 6c1cb1973c6..96887446972 100644
--- a/drivers/media/dvb/frontends/stv0299.c
+++ b/drivers/media/dvb/frontends/stv0299.c
@@ -667,7 +667,7 @@ struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
int id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct stv0299_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct stv0299_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c
index f648fdb64bb..f5d7b3277a2 100644
--- a/drivers/media/dvb/frontends/tda10021.c
+++ b/drivers/media/dvb/frontends/tda10021.c
@@ -413,7 +413,7 @@ struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
u8 id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda10021_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda10021_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/drivers/media/dvb/frontends/tda10048.c b/drivers/media/dvb/frontends/tda10048.c
index cc8862ce4aa..4e2a7c8b2f6 100644
--- a/drivers/media/dvb/frontends/tda10048.c
+++ b/drivers/media/dvb/frontends/tda10048.c
@@ -1095,7 +1095,7 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config,
dprintk(1, "%s()\n", __func__);
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda10048_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda10048_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
index 4981cef8b44..f2a8abe0a24 100644
--- a/drivers/media/dvb/frontends/tda1004x.c
+++ b/drivers/media/dvb/frontends/tda1004x.c
@@ -1269,7 +1269,7 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
int id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
if (!state) {
printk(KERN_ERR "Can't alocate memory for tda10045 state\n");
return NULL;
@@ -1339,7 +1339,7 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
int id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
if (!state) {
printk(KERN_ERR "Can't alocate memory for tda10046 state\n");
return NULL;
diff --git a/drivers/media/dvb/frontends/tda10086.c b/drivers/media/dvb/frontends/tda10086.c
index a17ce3c4ad8..f2c8faac6f3 100644
--- a/drivers/media/dvb/frontends/tda10086.c
+++ b/drivers/media/dvb/frontends/tda10086.c
@@ -745,7 +745,7 @@ struct dvb_frontend* tda10086_attach(const struct tda10086_config* config,
dprintk ("%s\n", __func__);
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda10086_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda10086_state), GFP_KERNEL);
if (!state)
return NULL;
diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb/frontends/tda8083.c
index 5b843b2e67e..9369f7442f2 100644
--- a/drivers/media/dvb/frontends/tda8083.c
+++ b/drivers/media/dvb/frontends/tda8083.c
@@ -417,7 +417,7 @@ struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
struct tda8083_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda8083_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda8083_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/drivers/media/dvb/frontends/ves1820.c b/drivers/media/dvb/frontends/ves1820.c
index a184597f1d9..6e78e486551 100644
--- a/drivers/media/dvb/frontends/ves1820.c
+++ b/drivers/media/dvb/frontends/ves1820.c
@@ -374,7 +374,7 @@ struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
struct ves1820_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct ves1820_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/drivers/media/dvb/frontends/ves1x93.c b/drivers/media/dvb/frontends/ves1x93.c
index bd558960bd8..8d7854c2fb0 100644
--- a/drivers/media/dvb/frontends/ves1x93.c
+++ b/drivers/media/dvb/frontends/ves1x93.c
@@ -456,7 +456,7 @@ struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
u8 identity;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct ves1x93_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c
index 148b6f7f6cb..66f5c1fb307 100644
--- a/drivers/media/dvb/frontends/zl10353.c
+++ b/drivers/media/dvb/frontends/zl10353.c
@@ -98,7 +98,6 @@ static int zl10353_read_register(struct zl10353_state *state, u8 reg)
static void zl10353_dump_regs(struct dvb_frontend *fe)
{
struct zl10353_state *state = fe->demodulator_priv;
- char buf[52], buf2[4];
int ret;
u8 reg;
@@ -106,19 +105,18 @@ static void zl10353_dump_regs(struct dvb_frontend *fe)
for (reg = 0; ; reg++) {
if (reg % 16 == 0) {
if (reg)
- printk(KERN_DEBUG "%s\n", buf);
- sprintf(buf, "%02x: ", reg);
+ printk(KERN_CONT "\n");
+ printk(KERN_DEBUG "%02x:", reg);
}
ret = zl10353_read_register(state, reg);
if (ret >= 0)
- sprintf(buf2, "%02x ", (u8)ret);
+ printk(KERN_CONT " %02x", (u8)ret);
else
- strcpy(buf2, "-- ");
- strcat(buf, buf2);
+ printk(KERN_CONT " --");
if (reg == 0xff)
break;
}
- printk(KERN_DEBUG "%s\n", buf);
+ printk(KERN_CONT "\n");
}
static void zl10353_calc_nominal_rate(struct dvb_frontend *fe,
diff --git a/drivers/media/dvb/siano/Kconfig b/drivers/media/dvb/siano/Kconfig
index dd863f26167..8c1aed77ea3 100644
--- a/drivers/media/dvb/siano/Kconfig
+++ b/drivers/media/dvb/siano/Kconfig
@@ -2,25 +2,33 @@
# Siano Mobile Silicon Digital TV device configuration
#
-config DVB_SIANO_SMS1XXX
- tristate "Siano SMS1XXX USB dongle support"
- depends on DVB_CORE && USB
+config SMS_SIANO_MDTV
+ tristate "Siano SMS1xxx based MDTV receiver"
+ depends on DVB_CORE && INPUT
---help---
- Choose Y here if you have a USB dongle with a SMS1XXX chipset.
+ Choose Y or M here if you have MDTV receiver with a Siano chipset.
- To compile this driver as a module, choose M here: the
- module will be called sms1xxx.
+ To compile this driver as a module, choose M here
+ (The module will be called smsmdtv).
-config DVB_SIANO_SMS1XXX_SMS_IDS
- bool "Enable support for Siano Mobile Silicon default USB IDs"
- depends on DVB_SIANO_SMS1XXX
- default y
- ---help---
- Choose Y here if you have a USB dongle with a SMS1XXX chipset
- that uses Siano Mobile Silicon's default usb vid:pid.
+ Further documentation on this driver can be found on the WWW
+ at http://www.siano-ms.com/
+
+if SMS_SIANO_MDTV
+menu "Siano module components"
- Choose N here if you would prefer to use Siano's external driver.
+# Hardware interfaces support
- Further documentation on this driver can be found on the WWW at
- <http://www.siano-ms.com/>.
+config SMS_USB_DRV
+ tristate "USB interface support"
+ depends on DVB_CORE && USB
+ ---help---
+ Choose if you would like to have Siano's support for USB interface
+config SMS_SDIO_DRV
+ tristate "SDIO interface support"
+ depends on DVB_CORE && MMC
+ ---help---
+ Choose if you would like to have Siano's support for SDIO interface
+endmenu
+endif # SMS_SIANO_MDTV
diff --git a/drivers/media/dvb/siano/Makefile b/drivers/media/dvb/siano/Makefile
index c6644d90943..c54140b5ab5 100644
--- a/drivers/media/dvb/siano/Makefile
+++ b/drivers/media/dvb/siano/Makefile
@@ -1,8 +1,9 @@
-sms1xxx-objs := smscoreapi.o sms-cards.o smsendian.o smsir.o
-obj-$(CONFIG_DVB_SIANO_SMS1XXX) += sms1xxx.o
-obj-$(CONFIG_DVB_SIANO_SMS1XXX) += smsusb.o
-obj-$(CONFIG_DVB_SIANO_SMS1XXX) += smsdvb.o
+smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o smsir.o
+
+obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o
+obj-$(CONFIG_SMS_USB_DRV) += smsusb.o
+obj-$(CONFIG_SMS_SDIO_DRV) += smssdio.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c
index d8b15d583bd..0420e2885e7 100644
--- a/drivers/media/dvb/siano/sms-cards.c
+++ b/drivers/media/dvb/siano/sms-cards.c
@@ -116,99 +116,21 @@ static inline void sms_gpio_assign_11xx_default_led_config(
int sms_board_event(struct smscore_device_t *coredev,
enum SMS_BOARD_EVENTS gevent) {
- int board_id = smscore_get_board_id(coredev);
- struct sms_board *board = sms_get_board(board_id);
struct smscore_gpio_config MyGpioConfig;
sms_gpio_assign_11xx_default_led_config(&MyGpioConfig);
switch (gevent) {
case BOARD_EVENT_POWER_INIT: /* including hotplug */
- switch (board_id) {
- case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
- /* set I/O and turn off all LEDs */
- smscore_gpio_configure(coredev,
- board->board_cfg.leds_power,
- &MyGpioConfig);
- smscore_gpio_set_level(coredev,
- board->board_cfg.leds_power, 0);
- smscore_gpio_configure(coredev, board->board_cfg.led0,
- &MyGpioConfig);
- smscore_gpio_set_level(coredev,
- board->board_cfg.led0, 0);
- smscore_gpio_configure(coredev, board->board_cfg.led1,
- &MyGpioConfig);
- smscore_gpio_set_level(coredev,
- board->board_cfg.led1, 0);
- break;
- case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
- case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
- /* set I/O and turn off LNA */
- smscore_gpio_configure(coredev,
- board->board_cfg.foreign_lna0_ctrl,
- &MyGpioConfig);
- smscore_gpio_set_level(coredev,
- board->board_cfg.foreign_lna0_ctrl,
- 0);
- break;
- }
break; /* BOARD_EVENT_BIND */
case BOARD_EVENT_POWER_SUSPEND:
- switch (board_id) {
- case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
- smscore_gpio_set_level(coredev,
- board->board_cfg.leds_power, 0);
- smscore_gpio_set_level(coredev,
- board->board_cfg.led0, 0);
- smscore_gpio_set_level(coredev,
- board->board_cfg.led1, 0);
- break;
- case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
- case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
- smscore_gpio_set_level(coredev,
- board->board_cfg.foreign_lna0_ctrl,
- 0);
- break;
- }
break; /* BOARD_EVENT_POWER_SUSPEND */
case BOARD_EVENT_POWER_RESUME:
- switch (board_id) {
- case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
- smscore_gpio_set_level(coredev,
- board->board_cfg.leds_power, 1);
- smscore_gpio_set_level(coredev,
- board->board_cfg.led0, 1);
- smscore_gpio_set_level(coredev,
- board->board_cfg.led1, 0);
- break;
- case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
- case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
- smscore_gpio_set_level(coredev,
- board->board_cfg.foreign_lna0_ctrl,
- 1);
- break;
- }
break; /* BOARD_EVENT_POWER_RESUME */
case BOARD_EVENT_BIND:
- switch (board_id) {
- case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
- smscore_gpio_set_level(coredev,
- board->board_cfg.leds_power, 1);
- smscore_gpio_set_level(coredev,
- board->board_cfg.led0, 1);
- smscore_gpio_set_level(coredev,
- board->board_cfg.led1, 0);
- break;
- case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
- case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
- smscore_gpio_set_level(coredev,
- board->board_cfg.foreign_lna0_ctrl,
- 1);
- break;
- }
break; /* BOARD_EVENT_BIND */
case BOARD_EVENT_SCAN_PROG:
@@ -218,20 +140,8 @@ int sms_board_event(struct smscore_device_t *coredev,
case BOARD_EVENT_EMERGENCY_WARNING_SIGNAL:
break; /* BOARD_EVENT_EMERGENCY_WARNING_SIGNAL */
case BOARD_EVENT_FE_LOCK:
- switch (board_id) {
- case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
- smscore_gpio_set_level(coredev,
- board->board_cfg.led1, 1);
- break;
- }
break; /* BOARD_EVENT_FE_LOCK */
case BOARD_EVENT_FE_UNLOCK:
- switch (board_id) {
- case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
- smscore_gpio_set_level(coredev,
- board->board_cfg.led1, 0);
- break;
- }
break; /* BOARD_EVENT_FE_UNLOCK */
case BOARD_EVENT_DEMOD_LOCK:
break; /* BOARD_EVENT_DEMOD_LOCK */
@@ -248,20 +158,8 @@ int sms_board_event(struct smscore_device_t *coredev,
case BOARD_EVENT_RECEPTION_LOST_0:
break; /* BOARD_EVENT_RECEPTION_LOST_0 */
case BOARD_EVENT_MULTIPLEX_OK:
- switch (board_id) {
- case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
- smscore_gpio_set_level(coredev,
- board->board_cfg.led1, 1);
- break;
- }
break; /* BOARD_EVENT_MULTIPLEX_OK */
case BOARD_EVENT_MULTIPLEX_ERRORS:
- switch (board_id) {
- case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
- smscore_gpio_set_level(coredev,
- board->board_cfg.led1, 0);
- break;
- }
break; /* BOARD_EVENT_MULTIPLEX_ERRORS */
default:
diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c
index a246903c334..bd9ab9d0d12 100644
--- a/drivers/media/dvb/siano/smscoreapi.c
+++ b/drivers/media/dvb/siano/smscoreapi.c
@@ -816,7 +816,7 @@ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode)
sms_debug("set device mode to %d", mode);
if (coredev->device_flags & SMS_DEVICE_FAMILY2) {
- if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_RAW_TUNER) {
+ if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) {
sms_err("invalid mode specified %d", mode);
return -EINVAL;
}
diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c
index 3ee1c3902c5..266033ae278 100644
--- a/drivers/media/dvb/siano/smsdvb.c
+++ b/drivers/media/dvb/siano/smsdvb.c
@@ -325,6 +325,16 @@ static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client,
0 : -ETIME;
}
+static inline int led_feedback(struct smsdvb_client_t *client)
+{
+ if (client->fe_status & FE_HAS_LOCK)
+ return sms_board_led_feedback(client->coredev,
+ (client->sms_stat_dvb.ReceptionData.BER
+ == 0) ? SMS_LED_HI : SMS_LED_LO);
+ else
+ return sms_board_led_feedback(client->coredev, SMS_LED_OFF);
+}
+
static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat)
{
struct smsdvb_client_t *client;
@@ -332,6 +342,8 @@ static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat)
*stat = client->fe_status;
+ led_feedback(client);
+
return 0;
}
@@ -342,6 +354,8 @@ static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber)
*ber = client->sms_stat_dvb.ReceptionData.BER;
+ led_feedback(client);
+
return 0;
}
@@ -359,6 +373,8 @@ static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
(client->sms_stat_dvb.ReceptionData.InBandPwr
+ 95) * 3 / 2;
+ led_feedback(client);
+
return 0;
}
@@ -369,6 +385,8 @@ static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr)
*snr = client->sms_stat_dvb.ReceptionData.SNR;
+ led_feedback(client);
+
return 0;
}
@@ -379,6 +397,8 @@ static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
*ucblocks = client->sms_stat_dvb.ReceptionData.ErrorTSPackets;
+ led_feedback(client);
+
return 0;
}
@@ -404,6 +424,8 @@ static int smsdvb_set_frontend(struct dvb_frontend *fe,
u32 Data[3];
} Msg;
+ int ret;
+
client->fe_status = FE_HAS_SIGNAL;
client->event_fe_state = -1;
client->event_unc_state = -1;
@@ -426,6 +448,23 @@ static int smsdvb_set_frontend(struct dvb_frontend *fe,
case BANDWIDTH_AUTO: return -EOPNOTSUPP;
default: return -EINVAL;
}
+ /* Disable LNA, if any. An error is returned if no LNA is present */
+ ret = sms_board_lna_control(client->coredev, 0);
+ if (ret == 0) {
+ fe_status_t status;
+
+ /* tune with LNA off at first */
+ ret = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
+ &client->tune_done);
+
+ smsdvb_read_status(fe, &status);
+
+ if (status & FE_HAS_LOCK)
+ return ret;
+
+ /* previous tune didnt lock - enable LNA and tune again */
+ sms_board_lna_control(client->coredev, 1);
+ }
return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
&client->tune_done);
@@ -451,6 +490,8 @@ static int smsdvb_init(struct dvb_frontend *fe)
struct smsdvb_client_t *client =
container_of(fe, struct smsdvb_client_t, frontend);
+ sms_board_power(client->coredev, 1);
+
sms_board_dvb3_event(client, DVB3_EVENT_INIT);
return 0;
}
@@ -460,6 +501,9 @@ static int smsdvb_sleep(struct dvb_frontend *fe)
struct smsdvb_client_t *client =
container_of(fe, struct smsdvb_client_t, frontend);
+ sms_board_led_feedback(client->coredev, SMS_LED_OFF);
+ sms_board_power(client->coredev, 0);
+
sms_board_dvb3_event(client, DVB3_EVENT_SLEEP);
return 0;
diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c
index dfaa49a53f3..d1d652e7f89 100644
--- a/drivers/media/dvb/siano/smssdio.c
+++ b/drivers/media/dvb/siano/smssdio.c
@@ -46,6 +46,7 @@
#define SMSSDIO_DATA 0x00
#define SMSSDIO_INT 0x04
+#define SMSSDIO_BLOCK_SIZE 128
static const struct sdio_device_id smssdio_ids[] = {
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR),
@@ -85,7 +86,8 @@ static int smssdio_sendrequest(void *context, void *buffer, size_t size)
sdio_claim_host(smsdev->func);
while (size >= smsdev->func->cur_blksize) {
- ret = sdio_write_blocks(smsdev->func, SMSSDIO_DATA, buffer, 1);
+ ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA,
+ buffer, smsdev->func->cur_blksize);
if (ret)
goto out;
@@ -94,8 +96,8 @@ static int smssdio_sendrequest(void *context, void *buffer, size_t size)
}
if (size) {
- ret = sdio_write_bytes(smsdev->func, SMSSDIO_DATA,
- buffer, size);
+ ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA,
+ buffer, size);
}
out:
@@ -125,23 +127,23 @@ static void smssdio_interrupt(struct sdio_func *func)
*/
isr = sdio_readb(func, SMSSDIO_INT, &ret);
if (ret) {
- dev_err(&smsdev->func->dev,
- "Unable to read interrupt register!\n");
+ sms_err("Unable to read interrupt register!\n");
return;
}
if (smsdev->split_cb == NULL) {
cb = smscore_getbuffer(smsdev->coredev);
if (!cb) {
- dev_err(&smsdev->func->dev,
- "Unable to allocate data buffer!\n");
+ sms_err("Unable to allocate data buffer!\n");
return;
}
- ret = sdio_read_blocks(smsdev->func, cb->p, SMSSDIO_DATA, 1);
+ ret = sdio_memcpy_fromio(smsdev->func,
+ cb->p,
+ SMSSDIO_DATA,
+ SMSSDIO_BLOCK_SIZE);
if (ret) {
- dev_err(&smsdev->func->dev,
- "Error %d reading initial block!\n", ret);
+ sms_err("Error %d reading initial block!\n", ret);
return;
}
@@ -152,7 +154,10 @@ static void smssdio_interrupt(struct sdio_func *func)
return;
}
- size = hdr->msgLength - smsdev->func->cur_blksize;
+ if (hdr->msgLength > smsdev->func->cur_blksize)
+ size = hdr->msgLength - smsdev->func->cur_blksize;
+ else
+ size = 0;
} else {
cb = smsdev->split_cb;
hdr = cb->p;
@@ -162,23 +167,24 @@ static void smssdio_interrupt(struct sdio_func *func)
smsdev->split_cb = NULL;
}
- if (hdr->msgLength > smsdev->func->cur_blksize) {
+ if (size) {
void *buffer;
- size = ALIGN(size, 128);
- buffer = cb->p + hdr->msgLength;
+ buffer = cb->p + (hdr->msgLength - size);
+ size = ALIGN(size, SMSSDIO_BLOCK_SIZE);
- BUG_ON(smsdev->func->cur_blksize != 128);
+ BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE);
/*
* First attempt to transfer all of it in one go...
*/
- ret = sdio_read_blocks(smsdev->func, buffer,
- SMSSDIO_DATA, size / 128);
+ ret = sdio_memcpy_fromio(smsdev->func,
+ buffer,
+ SMSSDIO_DATA,
+ size);
if (ret && ret != -EINVAL) {
smscore_putbuffer(smsdev->coredev, cb);
- dev_err(&smsdev->func->dev,
- "Error %d reading data from card!\n", ret);
+ sms_err("Error %d reading data from card!\n", ret);
return;
}
@@ -191,12 +197,12 @@ static void smssdio_interrupt(struct sdio_func *func)
*/
if (ret == -EINVAL) {
while (size) {
- ret = sdio_read_blocks(smsdev->func,
- buffer, SMSSDIO_DATA, 1);
+ ret = sdio_memcpy_fromio(smsdev->func,
+ buffer, SMSSDIO_DATA,
+ smsdev->func->cur_blksize);
if (ret) {
smscore_putbuffer(smsdev->coredev, cb);
- dev_err(&smsdev->func->dev,
- "Error %d reading "
+ sms_err("Error %d reading "
"data from card!\n", ret);
return;
}
@@ -269,7 +275,7 @@ static int smssdio_probe(struct sdio_func *func,
if (ret)
goto release;
- ret = sdio_set_block_size(func, 128);
+ ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE);
if (ret)
goto disable;
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 84b6fc15519..dcf9fa9264b 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -920,6 +920,8 @@ source "drivers/media/video/pwc/Kconfig"
config USB_ZR364XX
tristate "USB ZR364XX Camera support"
depends on VIDEO_V4L2
+ select VIDEOBUF_GEN
+ select VIDEOBUF_VMALLOC
---help---
Say Y here if you want to connect this type of camera to your
computer's USB port.
diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c
index 10dbd4a11b3..9e39bc5f7b0 100644
--- a/drivers/media/video/bw-qcam.c
+++ b/drivers/media/video/bw-qcam.c
@@ -992,7 +992,7 @@ static int accept_bwqcam(struct parport *port)
if (parport[0] && strncmp(parport[0], "auto", 4) != 0) {
/* user gave parport parameters */
- for(n=0; parport[n] && n<MAX_CAMS; n++){
+ for (n = 0; n < MAX_CAMS && parport[n]; n++) {
char *ep;
unsigned long r;
r = simple_strtoul(parport[n], &ep, 0);
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
index 5136df19833..93f0dae0135 100644
--- a/drivers/media/video/cx18/cx18-controls.c
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -20,6 +20,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
+#include <linux/kernel.h>
#include "cx18-driver.h"
#include "cx18-cards.h"
@@ -317,7 +318,7 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
idx = p.audio_properties & 0x03;
/* The audio clock of the digitizer must match the codec sample
rate otherwise you get some very strange effects. */
- if (idx < sizeof(freqs))
+ if (idx < ARRAY_SIZE(freqs))
cx18_call_all(cx, audio, s_clock_freq, freqs[idx]);
return err;
}
diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c
index e0cf21e0b1b..1a1048b18f7 100644
--- a/drivers/media/video/cx23885/cx23885-417.c
+++ b/drivers/media/video/cx23885/cx23885-417.c
@@ -1715,6 +1715,8 @@ static struct video_device cx23885_mpeg_template = {
.fops = &mpeg_fops,
.ioctl_ops = &mpeg_ioctl_ops,
.minor = -1,
+ .tvnorms = CX23885_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
};
void cx23885_417_unregister(struct cx23885_dev *dev)
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index a5cc1c1fc2d..39465301ec9 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -3003,6 +3003,14 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl)
case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
ctl->demod = XC3028_FE_OREN538;
break;
+ case CX88_BOARD_GENIATECH_X8000_MT:
+ /* FIXME: For this board, the xc3028 never recovers after being
+ powered down (the reset GPIO probably is not set properly).
+ We don't have access to the hardware so we cannot determine
+ which GPIO is used for xc3028, so just disable power xc3028
+ power management for now */
+ ctl->disable_power_mgmt = 1;
+ break;
case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
case CX88_BOARD_PROLINK_PV_8000GT:
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index c44e8760021..e237b507659 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -501,6 +501,7 @@ static struct zl10353_config cx88_pinnacle_hybrid_pctv = {
static struct zl10353_config cx88_geniatech_x8000_mt = {
.demod_address = (0x1e >> 1),
.no_tuner = 1,
+ .disable_i2c_gate_ctrl = 1,
};
static struct s5h1411_config dvico_fusionhdtv7_config = {
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index da4e3912cd3..7172dcf2a4f 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -116,6 +116,10 @@ static int cx8802_start_dma(struct cx8802_dev *dev,
udelay(100);
break;
case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* Enable MPEG parallel IO and video signal pins */
+ cx_write(MO_PINMUX_IO, 0x88);
+ cx_write(TS_SOP_STAT, 0);
+ cx_write(TS_VALERR_CNTRL, 0);
break;
case CX88_BOARD_PINNACLE_PCTV_HD_800i:
/* Enable MPEG parallel IO and video signal pins */
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 320f1f60276..1c2e544eda7 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -218,7 +218,7 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = {
struct em28xx_board em28xx_boards[] = {
[EM2750_BOARD_UNKNOWN] = {
.name = "EM2710/EM2750/EM2751 webcam grabber",
- .xclk = EM28XX_XCLK_FREQUENCY_48MHZ,
+ .xclk = EM28XX_XCLK_FREQUENCY_20MHZ,
.tuner_type = TUNER_ABSENT,
.is_webcam = 1,
.input = { {
@@ -622,22 +622,27 @@ struct em28xx_board em28xx_boards[] = {
},
[EM2861_BOARD_PLEXTOR_PX_TV100U] = {
.name = "Plextor ConvertX PX-TV100U",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_TNF_5335MF,
+ .xclk = EM28XX_XCLK_I2S_MSB_TIMING |
+ EM28XX_XCLK_FREQUENCY_12MHZ,
.tda9887_conf = TDA9887_PRESENT,
.decoder = EM28XX_TVP5150,
+ .has_msp34xx = 1,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
.amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
} },
},
@@ -1544,6 +1549,8 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2750_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2800),
.driver_info = EM2800_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2710),
+ .driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2820),
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2821),
@@ -1723,6 +1730,25 @@ static inline void em28xx_set_model(struct em28xx *dev)
EM28XX_I2C_FREQ_100_KHZ;
}
+
+/* FIXME: Should be replaced by a proper mt9m111 driver */
+static int em28xx_initialize_mt9m111(struct em28xx *dev)
+{
+ int i;
+ unsigned char regs[][3] = {
+ { 0x0d, 0x00, 0x01, }, /* reset and use defaults */
+ { 0x0d, 0x00, 0x00, },
+ { 0x0a, 0x00, 0x21, },
+ { 0x21, 0x04, 0x00, }, /* full readout speed, no row/col skipping */
+ };
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, &regs[i][0], 3);
+
+ return 0;
+}
+
+
/* FIXME: Should be replaced by a proper mt9m001 driver */
static int em28xx_initialize_mt9m001(struct em28xx *dev)
{
@@ -1751,7 +1777,7 @@ static int em28xx_initialize_mt9m001(struct em28xx *dev)
/* HINT method: webcam I2C chips
*
- * This method work for webcams with Micron sensors
+ * This method works for webcams with Micron sensors
*/
static int em28xx_hint_sensor(struct em28xx *dev)
{
@@ -1761,6 +1787,7 @@ static int em28xx_hint_sensor(struct em28xx *dev)
__be16 version_be;
u16 version;
+ /* Micron sensor detection */
dev->i2c_client.addr = 0xba >> 1;
cmd = 0;
i2c_master_send(&dev->i2c_client, &cmd, 1);
@@ -1769,23 +1796,54 @@ static int em28xx_hint_sensor(struct em28xx *dev)
return -EINVAL;
version = be16_to_cpu(version_be);
-
switch (version) {
- case 0x8243: /* mt9v011 640x480 1.3 Mpix sensor */
+ case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */
+ case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */
dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
+ em28xx_set_model(dev);
+
sensor_name = "mt9v011";
dev->em28xx_sensor = EM28XX_MT9V011;
dev->sensor_xres = 640;
dev->sensor_yres = 480;
- dev->sensor_xtal = 6300000;
+ /*
+ * FIXME: mt9v011 uses I2S speed as xtal clk - at least with
+ * the Silvercrest cam I have here for testing - for higher
+ * resolutions, a high clock cause horizontal artifacts, so we
+ * need to use a lower xclk frequency.
+ * Yet, it would be possible to adjust xclk depending on the
+ * desired resolution, since this affects directly the
+ * frame rate.
+ */
+ dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ;
+ dev->sensor_xtal = 4300000;
/* probably means GRGB 16 bit bayer */
dev->vinmode = 0x0d;
dev->vinctl = 0x00;
break;
+
+ case 0x143a: /* MT9M111 as found in the ECS G200 */
+ dev->model = EM2750_BOARD_UNKNOWN;
+ em28xx_set_model(dev);
+
+ sensor_name = "mt9m111";
+ dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ;
+ dev->em28xx_sensor = EM28XX_MT9M111;
+ em28xx_initialize_mt9m111(dev);
+ dev->sensor_xres = 640;
+ dev->sensor_yres = 512;
+
+ dev->vinmode = 0x0a;
+ dev->vinctl = 0x00;
+
+ break;
+
case 0x8431:
dev->model = EM2750_BOARD_UNKNOWN;
+ em28xx_set_model(dev);
+
sensor_name = "mt9m001";
dev->em28xx_sensor = EM28XX_MT9M001;
em28xx_initialize_mt9m001(dev);
@@ -1798,10 +1856,13 @@ static int em28xx_hint_sensor(struct em28xx *dev)
break;
default:
- printk("Unknown Micron Sensor 0x%04x\n", be16_to_cpu(version));
+ printk("Unknown Micron Sensor 0x%04x\n", version);
return -EINVAL;
}
+ /* Setup webcam defaults */
+ em28xx_pre_card_setup(dev);
+
em28xx_errdev("Sensor is %s, using model %s entry.\n",
sensor_name, em28xx_boards[dev->model].name);
@@ -1813,60 +1874,6 @@ static int em28xx_hint_sensor(struct em28xx *dev)
*/
void em28xx_pre_card_setup(struct em28xx *dev)
{
- int rc;
-
- em28xx_set_model(dev);
-
- em28xx_info("Identified as %s (card=%d)\n",
- dev->board.name, dev->model);
-
- /* Set the default GPO/GPIO for legacy devices */
- dev->reg_gpo_num = EM2880_R04_GPO;
- dev->reg_gpio_num = EM28XX_R08_GPIO;
-
- dev->wait_after_write = 5;
-
- /* Based on the Chip ID, set the device configuration */
- rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
- if (rc > 0) {
- dev->chip_id = rc;
-
- switch (dev->chip_id) {
- case CHIP_ID_EM2750:
- em28xx_info("chip ID is em2750\n");
- break;
- case CHIP_ID_EM2820:
- em28xx_info("chip ID is em2710 or em2820\n");
- break;
- case CHIP_ID_EM2840:
- em28xx_info("chip ID is em2840\n");
- break;
- case CHIP_ID_EM2860:
- em28xx_info("chip ID is em2860\n");
- break;
- case CHIP_ID_EM2870:
- em28xx_info("chip ID is em2870\n");
- dev->wait_after_write = 0;
- break;
- case CHIP_ID_EM2874:
- em28xx_info("chip ID is em2874\n");
- dev->reg_gpio_num = EM2874_R80_GPIO;
- dev->wait_after_write = 0;
- break;
- case CHIP_ID_EM2883:
- em28xx_info("chip ID is em2882/em2883\n");
- dev->wait_after_write = 0;
- break;
- default:
- em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
- }
- }
-
- /* Prepopulate cached GPO register content */
- rc = em28xx_read_reg(dev, dev->reg_gpo_num);
- if (rc >= 0)
- dev->reg_gpo = rc;
-
/* Set the initial XCLK and I2C clock values based on the board
definition */
em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f);
@@ -1876,9 +1883,8 @@ void em28xx_pre_card_setup(struct em28xx *dev)
/* request some modules */
switch (dev->model) {
case EM2861_BOARD_PLEXTOR_PX_TV100U:
- /* FIXME guess */
- /* Turn on analog audio output */
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ /* Sets the msp34xx I2S speed */
+ dev->i2s_speed = 2048000;
break;
case EM2861_BOARD_KWORLD_PVRTV_300U:
case EM2880_BOARD_KWORLD_DVB_305U:
@@ -2216,7 +2222,20 @@ void em28xx_register_i2c_ir(struct em28xx *dev)
void em28xx_card_setup(struct em28xx *dev)
{
- em28xx_set_model(dev);
+ /*
+ * If the device can be a webcam, seek for a sensor.
+ * If sensor is not found, then it isn't a webcam.
+ */
+ if (dev->board.is_webcam) {
+ if (em28xx_hint_sensor(dev) < 0)
+ dev->board.is_webcam = 0;
+ else
+ dev->progressive = 1;
+ } else
+ em28xx_set_model(dev);
+
+ em28xx_info("Identified as %s (card=%d)\n",
+ dev->board.name, dev->model);
dev->tuner_type = em28xx_boards[dev->model].tuner_type;
if (em28xx_boards[dev->model].tuner_addr)
@@ -2290,10 +2309,6 @@ void em28xx_card_setup(struct em28xx *dev)
em28xx_gpio_set(dev, dev->board.tuner_gpio);
em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
break;
- case EM2820_BOARD_SILVERCREST_WEBCAM:
- /* FIXME: need to document the registers bellow */
- em28xx_write_reg(dev, 0x0d, 0x42);
- em28xx_write_reg(dev, 0x13, 0x08);
}
if (dev->board.has_snapshot_button)
@@ -2367,7 +2382,9 @@ void em28xx_card_setup(struct em28xx *dev)
}
em28xx_tuner_setup(dev);
- em28xx_ir_init(dev);
+
+ if(!disable_ir)
+ em28xx_ir_init(dev);
}
@@ -2433,7 +2450,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
int minor)
{
struct em28xx *dev = *devhandle;
- int retval = -ENOMEM;
+ int retval;
int errCode;
dev->udev = udev;
@@ -2450,6 +2467,58 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
dev->em28xx_read_reg_req = em28xx_read_reg_req;
dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
+ em28xx_set_model(dev);
+
+ /* Set the default GPO/GPIO for legacy devices */
+ dev->reg_gpo_num = EM2880_R04_GPO;
+ dev->reg_gpio_num = EM28XX_R08_GPIO;
+
+ dev->wait_after_write = 5;
+
+ /* Based on the Chip ID, set the device configuration */
+ retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
+ if (retval > 0) {
+ dev->chip_id = retval;
+
+ switch (dev->chip_id) {
+ case CHIP_ID_EM2710:
+ em28xx_info("chip ID is em2710\n");
+ break;
+ case CHIP_ID_EM2750:
+ em28xx_info("chip ID is em2750\n");
+ break;
+ case CHIP_ID_EM2820:
+ em28xx_info("chip ID is em2820 (or em2710)\n");
+ break;
+ case CHIP_ID_EM2840:
+ em28xx_info("chip ID is em2840\n");
+ break;
+ case CHIP_ID_EM2860:
+ em28xx_info("chip ID is em2860\n");
+ break;
+ case CHIP_ID_EM2870:
+ em28xx_info("chip ID is em2870\n");
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2874:
+ em28xx_info("chip ID is em2874\n");
+ dev->reg_gpio_num = EM2874_R80_GPIO;
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2883:
+ em28xx_info("chip ID is em2882/em2883\n");
+ dev->wait_after_write = 0;
+ break;
+ default:
+ em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
+ }
+ }
+
+ /* Prepopulate cached GPO register content */
+ retval = em28xx_read_reg(dev, dev->reg_gpo_num);
+ if (retval >= 0)
+ dev->reg_gpo = retval;
+
em28xx_pre_card_setup(dev);
if (!dev->board.is_em2800) {
@@ -2484,14 +2553,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
dev->vinmode = 0x10;
dev->vinctl = 0x11;
- /*
- * If the device can be a webcam, seek for a sensor.
- * If sensor is not found, then it isn't a webcam.
- */
- if (dev->board.is_webcam)
- if (em28xx_hint_sensor(dev) < 0)
- dev->board.is_webcam = 0;
-
/* Do board specific init and eeprom reading */
em28xx_card_setup(dev);
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 5b78e199abd..98e140b5d95 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -632,6 +632,9 @@ int em28xx_capture_start(struct em28xx *dev, int start)
return rc;
}
+ if (dev->board.is_webcam)
+ rc = em28xx_write_reg(dev, 0x13, 0x0c);
+
/* enable video capture */
rc = em28xx_write_reg(dev, 0x48, 0x00);
@@ -720,7 +723,10 @@ int em28xx_resolution_set(struct em28xx *dev)
{
int width, height;
width = norm_maxw(dev);
- height = norm_maxh(dev) >> 1;
+ height = norm_maxh(dev);
+
+ if (!dev->progressive)
+ height >>= norm_maxh(dev);
em28xx_set_outfmt(dev);
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index cf0ac7f2a30..d603575431b 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -478,7 +478,6 @@ static int dvb_init(struct em28xx *dev)
}
break;
case EM2880_BOARD_KWORLD_DVB_310U:
- case EM2880_BOARD_EMPIRE_DUAL_TV:
dvb->frontend = dvb_attach(zl10353_attach,
&em28xx_zl10353_with_xc3028,
&dev->i2c_adap);
@@ -488,6 +487,7 @@ static int dvb_init(struct em28xx *dev)
}
break;
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2880_BOARD_EMPIRE_DUAL_TV:
dvb->frontend = dvb_attach(zl10353_attach,
&em28xx_zl10353_xc3028_no_i2c_gate,
&dev->i2c_adap);
diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h
index a2676d63cfd..6bf84bd787d 100644
--- a/drivers/media/video/em28xx/em28xx-reg.h
+++ b/drivers/media/video/em28xx/em28xx-reg.h
@@ -176,7 +176,8 @@
/* FIXME: Need to be populated with the other chip ID's */
enum em28xx_chip_id {
- CHIP_ID_EM2820 = 18, /* Also used by em2710 */
+ CHIP_ID_EM2710 = 17,
+ CHIP_ID_EM2820 = 18, /* Also used by some em2710 */
CHIP_ID_EM2840 = 20,
CHIP_ID_EM2750 = 33,
CHIP_ID_EM2860 = 34,
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index ff37b4c15f4..ab079d9256c 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -194,15 +194,24 @@ static void em28xx_copy_video(struct em28xx *dev,
startread = p;
remain = len;
- /* Interlaces frame */
- if (buf->top_field)
+ if (dev->progressive)
fieldstart = outp;
- else
- fieldstart = outp + bytesperline;
+ else {
+ /* Interlaces two half frames */
+ if (buf->top_field)
+ fieldstart = outp;
+ else
+ fieldstart = outp + bytesperline;
+ }
linesdone = dma_q->pos / bytesperline;
currlinedone = dma_q->pos % bytesperline;
- offset = linesdone * bytesperline * 2 + currlinedone;
+
+ if (dev->progressive)
+ offset = linesdone * bytesperline + currlinedone;
+ else
+ offset = linesdone * bytesperline * 2 + currlinedone;
+
startwrite = fieldstart + offset;
lencopy = bytesperline - currlinedone;
lencopy = lencopy > remain ? remain : lencopy;
@@ -376,7 +385,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],
len, (p[2] & 1) ? "odd" : "even");
- if (!(p[2] & 1)) {
+ if (dev->progressive || !(p[2] & 1)) {
if (buf != NULL)
buffer_filled(dev, dma_q, buf);
get_next_buf(dma_q, &buf);
@@ -689,7 +698,10 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
/* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
- f->fmt.pix.field = dev->interlaced ?
+ if (dev->progressive)
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ else
+ f->fmt.pix.field = dev->interlaced ?
V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
mutex_unlock(&dev->lock);
@@ -753,7 +765,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3;
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ if (dev->progressive)
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ else
+ f->fmt.pix.field = dev->interlaced ?
+ V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
return 0;
}
@@ -846,6 +862,41 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
return 0;
}
+static int vidioc_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *p)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc = 0;
+
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (dev->board.is_webcam)
+ rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0,
+ video, g_parm, p);
+ else
+ v4l2_video_std_frame_period(dev->norm,
+ &p->parm.capture.timeperframe);
+
+ return rc;
+}
+
+static int vidioc_s_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *p)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ if (!dev->board.is_webcam)
+ return -EINVAL;
+
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p);
+}
+
static const char *iname[] = {
[EM28XX_VMUX_COMPOSITE1] = "Composite1",
[EM28XX_VMUX_COMPOSITE2] = "Composite2",
@@ -1624,6 +1675,7 @@ static int em28xx_v4l2_open(struct file *filp)
struct em28xx *dev;
enum v4l2_buf_type fh_type;
struct em28xx_fh *fh;
+ enum v4l2_field field;
dev = em28xx_get_device(minor, &fh_type, &radio);
@@ -1665,8 +1717,13 @@ static int em28xx_v4l2_open(struct file *filp)
dev->users++;
+ if (dev->progressive)
+ field = V4L2_FIELD_NONE;
+ else
+ field = V4L2_FIELD_INTERLACED;
+
videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops,
- NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED,
+ NULL, &dev->slock, fh->type, field,
sizeof(struct em28xx_buffer), fh);
mutex_unlock(&dev->lock);
@@ -1885,6 +1942,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_s_std = vidioc_s_std,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 45bd513f62d..a2add61f7d5 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -367,6 +367,7 @@ enum em28xx_sensor {
EM28XX_NOSENSOR = 0,
EM28XX_MT9V011,
EM28XX_MT9M001,
+ EM28XX_MT9M111,
};
enum em28xx_adecoder {
@@ -484,6 +485,9 @@ struct em28xx {
int sensor_xres, sensor_yres;
int sensor_xtal;
+ /* Allows progressive (e. g. non-interlaced) mode */
+ int progressive;
+
/* Vinmode/Vinctl used at the driver */
int vinmode, vinctl;
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
index 34f46f2bc04..e994dcac43f 100644
--- a/drivers/media/video/gspca/Kconfig
+++ b/drivers/media/video/gspca/Kconfig
@@ -114,7 +114,7 @@ config USB_GSPCA_SN9C20X
config USB_GSPCA_SN9C20X_EVDEV
bool "Enable evdev support"
- depends on USB_GSPCA_SN9C20X
+ depends on USB_GSPCA_SN9C20X && INPUT
---help---
Say Y here in order to enable evdev support for sn9c20x webcam button.
diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
index ccd47f57f42..d678765cbba 100644
--- a/drivers/media/video/hdpvr/hdpvr-video.c
+++ b/drivers/media/video/hdpvr/hdpvr-video.c
@@ -1220,6 +1220,8 @@ static const struct video_device hdpvr_video_template = {
V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I |
V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N |
V4L2_STD_PAL_60,
+ .current_norm = V4L2_STD_NTSC | V4L2_STD_PAL_M |
+ V4L2_STD_PAL_60,
};
int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c
index a3b77ed3f08..4a9c8ce0ecb 100644
--- a/drivers/media/video/ivtv/ivtv-controls.c
+++ b/drivers/media/video/ivtv/ivtv-controls.c
@@ -17,6 +17,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/kernel.h>
#include "ivtv-driver.h"
#include "ivtv-cards.h"
@@ -281,7 +282,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
idx = p.audio_properties & 0x03;
/* The audio clock of the digitizer must match the codec sample
rate otherwise you get some very strange effects. */
- if (idx < sizeof(freqs))
+ if (idx < ARRAY_SIZE(freqs))
ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
return err;
}
diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c
index b2260de645f..cc85f77a570 100644
--- a/drivers/media/video/mt9v011.c
+++ b/drivers/media/video/mt9v011.c
@@ -52,13 +52,34 @@ static struct v4l2_queryctrl mt9v011_qctrl[] = {
.step = 1,
.default_value = 0,
.flags = 0,
- },
+ }, {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ }
};
struct mt9v011 {
struct v4l2_subdev sd;
unsigned width, height;
unsigned xtal;
+ unsigned hflip:1;
+ unsigned vflip:1;
u16 global_gain, red_bal, blue_bal;
};
@@ -131,7 +152,6 @@ static const struct i2c_reg_value mt9v011_init_default[] = {
{ R0A_MT9V011_CLK_SPEED, 0x0000 },
{ R1E_MT9V011_DIGITAL_ZOOM, 0x0000 },
- { R20_MT9V011_READ_MODE, 0x1000 },
{ R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */
};
@@ -156,7 +176,7 @@ static void set_balance(struct v4l2_subdev *sd)
mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain);
}
-static void calc_fps(struct v4l2_subdev *sd)
+static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator)
{
struct mt9v011 *core = to_mt9v011(sd);
unsigned height, width, hblank, vblank, speed;
@@ -179,6 +199,51 @@ static void calc_fps(struct v4l2_subdev *sd)
v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n",
tmp / 1000, tmp % 1000, t_time);
+
+ if (numerator && denominator) {
+ *numerator = 1000;
+ *denominator = (u32)frames_per_ms;
+ }
+}
+
+static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ unsigned height, width, hblank, vblank;
+ unsigned row_time, line_time;
+ u64 t_time, speed;
+
+ /* Avoid bogus calculus */
+ if (!numerator || !denominator)
+ return 0;
+
+ height = mt9v011_read(sd, R03_MT9V011_HEIGHT);
+ width = mt9v011_read(sd, R04_MT9V011_WIDTH);
+ hblank = mt9v011_read(sd, R05_MT9V011_HBLANK);
+ vblank = mt9v011_read(sd, R06_MT9V011_VBLANK);
+
+ row_time = width + 113 + hblank;
+ line_time = height + vblank + 1;
+
+ t_time = core->xtal * ((u64)numerator);
+ /* round to the closest value */
+ t_time += denominator / 2;
+ do_div(t_time, denominator);
+
+ speed = t_time;
+ do_div(speed, row_time * line_time);
+
+ /* Avoid having a negative value for speed */
+ if (speed < 2)
+ speed = 0;
+ else
+ speed -= 2;
+
+ /* Avoid speed overflow */
+ if (speed > 15)
+ return 15;
+
+ return (u16)speed;
}
static void set_res(struct v4l2_subdev *sd)
@@ -207,9 +272,23 @@ static void set_res(struct v4l2_subdev *sd)
mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height);
mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height);
- calc_fps(sd);
+ calc_fps(sd, NULL, NULL);
};
+static void set_read_mode(struct v4l2_subdev *sd)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ unsigned mode = 0x1000;
+
+ if (core->hflip)
+ mode |= 0x4000;
+
+ if (core->vflip)
+ mode |= 0x8000;
+
+ mt9v011_write(sd, R20_MT9V011_READ_MODE, mode);
+}
+
static int mt9v011_reset(struct v4l2_subdev *sd, u32 val)
{
int i;
@@ -220,6 +299,7 @@ static int mt9v011_reset(struct v4l2_subdev *sd, u32 val)
set_balance(sd);
set_res(sd);
+ set_read_mode(sd);
return 0;
};
@@ -240,6 +320,12 @@ static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
case V4L2_CID_BLUE_BALANCE:
ctrl->value = core->blue_bal;
return 0;
+ case V4L2_CID_HFLIP:
+ ctrl->value = core->hflip ? 1 : 0;
+ return 0;
+ case V4L2_CID_VFLIP:
+ ctrl->value = core->vflip ? 1 : 0;
+ return 0;
}
return -EINVAL;
}
@@ -288,6 +374,14 @@ static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
case V4L2_CID_BLUE_BALANCE:
core->blue_bal = ctrl->value;
break;
+ case V4L2_CID_HFLIP:
+ core->hflip = ctrl->value;
+ set_read_mode(sd);
+ return 0;
+ case V4L2_CID_VFLIP:
+ core->vflip = ctrl->value;
+ set_read_mode(sd);
+ return 0;
default:
return -EINVAL;
}
@@ -322,6 +416,44 @@ static int mt9v011_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
return 0;
}
+static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(cp, 0, sizeof(struct v4l2_captureparm));
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ calc_fps(sd,
+ &cp->timeperframe.numerator,
+ &cp->timeperframe.denominator);
+
+ return 0;
+}
+
+static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ u16 speed;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (cp->extendedmode != 0)
+ return -EINVAL;
+
+ speed = calc_speed(sd, tpf->numerator, tpf->denominator);
+
+ mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed);
+ v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed);
+
+ /* Recalculate and update fps info */
+ calc_fps(sd, &tpf->numerator, &tpf->denominator);
+
+ return 0;
+}
+
static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
struct v4l2_pix_format *pix = &fmt->fmt.pix;
@@ -393,10 +525,13 @@ static int mt9v011_s_register(struct v4l2_subdev *sd,
static int mt9v011_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *chip)
{
+ u16 version;
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION);
+
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011,
- MT9V011_VERSION);
+ version);
}
static const struct v4l2_subdev_core_ops mt9v011_core_ops = {
@@ -416,6 +551,8 @@ static const struct v4l2_subdev_video_ops mt9v011_video_ops = {
.enum_fmt = mt9v011_enum_fmt,
.try_fmt = mt9v011_try_fmt,
.s_fmt = mt9v011_s_fmt,
+ .g_parm = mt9v011_g_parm,
+ .s_parm = mt9v011_s_parm,
};
static const struct v4l2_subdev_ops mt9v011_ops = {
@@ -449,8 +586,9 @@ static int mt9v011_probe(struct i2c_client *c,
/* Check if the sensor is really a MT9V011 */
version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION);
- if (version != MT9V011_VERSION) {
- v4l2_info(sd, "*** unknown micron chip detected (0x%04x.\n",
+ if ((version != MT9V011_VERSION) &&
+ (version != MT9V011_REV_B_VERSION)) {
+ v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n",
version);
kfree(core);
return -EINVAL;
@@ -461,8 +599,8 @@ static int mt9v011_probe(struct i2c_client *c,
core->height = 480;
core->xtal = 27000000; /* Hz */
- v4l_info(c, "chip found @ 0x%02x (%s)\n",
- c->addr << 1, c->adapter->name);
+ v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n",
+ c->addr << 1, c->adapter->name, version);
return 0;
}
diff --git a/drivers/media/video/mt9v011.h b/drivers/media/video/mt9v011.h
index 9e443ee3055..3350fd6083c 100644
--- a/drivers/media/video/mt9v011.h
+++ b/drivers/media/video/mt9v011.h
@@ -30,6 +30,7 @@
#define R35_MT9V011_GLOBAL_GAIN 0x35
#define RF1_MT9V011_CHIP_ENABLE 0xf1
-#define MT9V011_VERSION 0x8243
+#define MT9V011_VERSION 0x8232
+#define MT9V011_REV_B_VERSION 0x8243
#endif
diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c
index 2d075205bdf..736c31d2319 100644
--- a/drivers/media/video/mx1_camera.c
+++ b/drivers/media/video/mx1_camera.c
@@ -234,6 +234,7 @@ static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev)
return ret;
}
+/* Called under spinlock_irqsave(&pcdev->lock, ...) */
static void mx1_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
@@ -241,13 +242,10 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq,
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx1_camera_dev *pcdev = ici->priv;
struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
- unsigned long flags;
dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
- spin_lock_irqsave(&pcdev->lock, flags);
-
list_add_tail(&vb->queue, &pcdev->capture);
vb->state = VIDEOBUF_ACTIVE;
@@ -264,8 +262,6 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq,
__raw_writel(temp, pcdev->base + CSICR1);
}
}
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
}
static void mx1_videobuf_release(struct videobuf_queue *vq,
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
index e605c076ed8..9770cb7932c 100644
--- a/drivers/media/video/mx3_camera.c
+++ b/drivers/media/video/mx3_camera.c
@@ -332,7 +332,10 @@ static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc)
}
}
-/* Called with .vb_lock held */
+/*
+ * Called with .vb_lock mutex held and
+ * under spinlock_irqsave(&mx3_cam->lock, ...)
+ */
static void mx3_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
@@ -346,7 +349,8 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq,
struct idmac_video_param *video = &ichan->params.video;
const struct soc_camera_data_format *data_fmt = icd->current_fmt;
dma_cookie_t cookie;
- unsigned long flags;
+
+ BUG_ON(!irqs_disabled());
/* This is the configuration of one sg-element */
video->out_pixel_fmt = fourcc_to_ipu_pix(data_fmt->fourcc);
@@ -359,8 +363,6 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq,
memset((void *)vb->baddr, 0xaa, vb->bsize);
#endif
- spin_lock_irqsave(&mx3_cam->lock, flags);
-
list_add_tail(&vb->queue, &mx3_cam->capture);
if (!mx3_cam->active) {
@@ -370,24 +372,23 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq,
vb->state = VIDEOBUF_QUEUED;
}
- spin_unlock_irqrestore(&mx3_cam->lock, flags);
+ spin_unlock_irq(&mx3_cam->lock);
cookie = txd->tx_submit(txd);
dev_dbg(&icd->dev, "Submitted cookie %d DMA 0x%08x\n", cookie, sg_dma_address(&buf->sg));
+
+ spin_lock_irq(&mx3_cam->lock);
+
if (cookie >= 0)
return;
/* Submit error */
vb->state = VIDEOBUF_PREPARED;
- spin_lock_irqsave(&mx3_cam->lock, flags);
-
list_del_init(&vb->queue);
if (mx3_cam->active == buf)
mx3_cam->active = NULL;
-
- spin_unlock_irqrestore(&mx3_cam->lock, flags);
}
/* Called with .vb_lock held */
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index 46e0d8ad880..016bb45ba0c 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -612,6 +612,7 @@ static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev)
dev_dbg(pcdev->soc_host.dev, "%s\n", __func__);
}
+/* Called under spinlock_irqsave(&pcdev->lock, ...) */
static void pxa_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
@@ -619,13 +620,10 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
- unsigned long flags;
dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d active=%p\n", __func__,
vb, vb->baddr, vb->bsize, pcdev->active);
- spin_lock_irqsave(&pcdev->lock, flags);
-
list_add_tail(&vb->queue, &pcdev->capture);
vb->state = VIDEOBUF_ACTIVE;
@@ -633,8 +631,6 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
if (!pcdev->active)
pxa_camera_start_capture(pcdev);
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
}
static void pxa_videobuf_release(struct videobuf_queue *vq,
@@ -1579,6 +1575,7 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev)
pcdev->mclk = 20000000;
}
+ pcdev->soc_host.dev = &pdev->dev;
pcdev->mclk_divisor = mclk_get_divisor(pcdev);
INIT_LIST_HEAD(&pcdev->capture);
@@ -1644,7 +1641,6 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev)
pcdev->soc_host.drv_name = PXA_CAM_DRV_NAME;
pcdev->soc_host.ops = &pxa_soc_camera_host_ops;
pcdev->soc_host.priv = pcdev;
- pcdev->soc_host.dev = &pdev->dev;
pcdev->soc_host.nr = pdev->id;
err = soc_camera_host_register(&pcdev->soc_host);
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 06861b782b9..6eebe3ef97d 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -3331,8 +3331,8 @@ struct saa7134_board saa7134_boards[] = {
.gpio = 0x0200100,
},
},
- [SAA7134_BOARD_HAUPPAUGE_HVR1120] = {
- .name = "Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid",
+ [SAA7134_BOARD_HAUPPAUGE_HVR1150] = {
+ .name = "Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid",
.audio_clock = 0x00187de7,
.tuner_type = TUNER_PHILIPS_TDA8290,
.radio_type = UNSET,
@@ -3363,8 +3363,8 @@ struct saa7134_board saa7134_boards[] = {
.gpio = 0x0800100, /* GPIO 23 HI for FM */
},
},
- [SAA7134_BOARD_HAUPPAUGE_HVR1110R3] = {
- .name = "Hauppauge WinTV-HVR1110r3 DVB-T/Hybrid",
+ [SAA7134_BOARD_HAUPPAUGE_HVR1120] = {
+ .name = "Hauppauge WinTV-HVR1120 DVB-T/Hybrid",
.audio_clock = 0x00187de7,
.tuner_type = TUNER_PHILIPS_TDA8290,
.radio_type = UNSET,
@@ -5862,31 +5862,31 @@ struct pci_device_id saa7134_pci_tbl[] = {
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x0070,
.subdevice = 0x6706,
- .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x0070,
.subdevice = 0x6707,
- .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x0070,
.subdevice = 0x6708,
- .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x0070,
.subdevice = 0x6709,
- .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x0070,
.subdevice = 0x670a,
- .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
@@ -6363,8 +6363,8 @@ static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev,
switch (command) {
case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */
switch (dev->board) {
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
case SAA7134_BOARD_HAUPPAUGE_HVR1120:
- case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg);
break;
default:
@@ -6384,8 +6384,8 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev,
int ret;
switch (dev->board) {
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
case SAA7134_BOARD_HAUPPAUGE_HVR1120:
- case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
/* tda8290 + tda18271 */
ret = saa7134_tda8290_18271_callback(dev, command, arg);
break;
@@ -6427,7 +6427,7 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data)
switch (tv.model) {
case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */
case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */
- case 67201: /* WinTV-HVR1120 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */
+ case 67201: /* WinTV-HVR1150 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */
case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */
case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */
case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
@@ -6435,7 +6435,7 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data)
case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */
case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */
case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */
- case 67651: /* WinTV-HVR1120 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
+ case 67651: /* WinTV-HVR1150 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
break;
default:
@@ -6625,8 +6625,8 @@ int saa7134_board_init1(struct saa7134_dev *dev)
saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00);
break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
case SAA7134_BOARD_HAUPPAUGE_HVR1120:
- case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
/* GPIO 26 high for digital, low for analog */
saa7134_set_gpio(dev, 26, 0);
msleep(1);
@@ -6891,8 +6891,8 @@ int saa7134_board_init2(struct saa7134_dev *dev)
dev->name, saa7134_boards[dev->board].name);
}
break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
case SAA7134_BOARD_HAUPPAUGE_HVR1120:
- case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
hauppauge_eeprom(dev, dev->eedata+0x80);
break;
case SAA7134_BOARD_HAUPPAUGE_HVR1110:
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index 31930f26ffc..98f3efd1e94 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -1119,7 +1119,7 @@ static int dvb_init(struct saa7134_dev *dev)
&tda827x_cfg_2) < 0)
goto dettach_frontend;
break;
- case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
+ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
fe0->dvb.frontend = dvb_attach(tda10048_attach,
&hcw_tda10048_config,
&dev->i2c_adap);
@@ -1147,7 +1147,7 @@ static int dvb_init(struct saa7134_dev *dev)
&tda827x_cfg_1) < 0)
goto dettach_frontend;
break;
- case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
&hcw_lgdt3305_config,
&dev->i2c_adap);
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 82268848f26..fb564f14887 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -278,8 +278,8 @@ struct saa7134_format {
#define SAA7134_BOARD_ASUSTeK_TIGER 152
#define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153
#define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154
-#define SAA7134_BOARD_HAUPPAUGE_HVR1120 155
-#define SAA7134_BOARD_HAUPPAUGE_HVR1110R3 156
+#define SAA7134_BOARD_HAUPPAUGE_HVR1150 155
+#define SAA7134_BOARD_HAUPPAUGE_HVR1120 156
#define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157
#define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158
#define SAA7134_BOARD_BEHOLD_505RDS 159
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 0db88a53d92..e86878deea7 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -282,27 +282,24 @@ out:
return ret;
}
+/* Called under spinlock_irqsave(&pcdev->lock, ...) */
static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct soc_camera_device *icd = vq->priv_data;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- unsigned long flags;
dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
vb, vb->baddr, vb->bsize);
vb->state = VIDEOBUF_QUEUED;
- spin_lock_irqsave(&pcdev->lock, flags);
list_add_tail(&vb->queue, &pcdev->capture);
if (!pcdev->active) {
pcdev->active = vb;
sh_mobile_ceu_capture(pcdev);
}
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
}
static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq,
diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c
index 4d6785e6345..b154bd961e3 100644
--- a/drivers/media/video/stk-webcam.c
+++ b/drivers/media/video/stk-webcam.c
@@ -1050,8 +1050,8 @@ static int stk_setup_format(struct stk_camera *dev)
depth = 1;
else
depth = 2;
- while (stk_sizes[i].m != dev->vsettings.mode
- && i < ARRAY_SIZE(stk_sizes))
+ while (i < ARRAY_SIZE(stk_sizes) &&
+ stk_sizes[i].m != dev->vsettings.mode)
i++;
if (i == ARRAY_SIZE(stk_sizes)) {
STK_ERROR("Something is broken in %s\n", __func__);
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index 89927b7aec2..04b47832fa0 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -1845,11 +1845,29 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
- /* ViMicro */
- { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ /* ViMicro Vega */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0ac8,
+ .idProduct = 0x332d,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
+ /* ViMicro - Minoru3D */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0ac8,
+ .idProduct = 0x3410,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
+ /* ViMicro Venus - Minoru3D */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0ac8,
- .idProduct = 0x0000,
+ .idProduct = 0x3420,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c
index f152a990386..1ca6dff7361 100644
--- a/drivers/media/video/uvc/uvc_status.c
+++ b/drivers/media/video/uvc/uvc_status.c
@@ -145,8 +145,8 @@ static void uvc_status_complete(struct urb *urb)
break;
default:
- uvc_printk(KERN_INFO, "unknown event type %u.\n",
- dev->status[0]);
+ uvc_trace(UVC_TRACE_STATUS, "Unknown status event "
+ "type %u.\n", dev->status[0]);
break;
}
}
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index be64a502ea2..f2afc4e0837 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -1081,8 +1081,10 @@ static long __video_do_ioctl(struct file *file,
/* Calls the specific handler */
if (ops->vidioc_g_std)
ret = ops->vidioc_g_std(file, fh, id);
- else
+ else if (vfd->current_norm)
*id = vfd->current_norm;
+ else
+ ret = -EINVAL;
if (!ret)
dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id);
@@ -1553,12 +1555,19 @@ static long __video_do_ioctl(struct file *file,
break;
ret = ops->vidioc_g_parm(file, fh, p);
} else {
+ v4l2_std_id std = vfd->current_norm;
+
if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- v4l2_video_std_frame_period(vfd->current_norm,
- &p->parm.capture.timeperframe);
ret = 0;
+ if (ops->vidioc_g_std)
+ ret = ops->vidioc_g_std(file, fh, &std);
+ else if (std == 0)
+ ret = -EINVAL;
+ if (ret == 0)
+ v4l2_video_std_frame_period(std,
+ &p->parm.capture.timeperframe);
}
dbgarg(cmd, "type=%d\n", p->type);
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
index fc976f42f43..2622a6e63da 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/video/zr364xx.c
@@ -695,7 +695,7 @@ static int zr364xx_release(struct file *file)
for (i = 0; i < 2; i++) {
err =
send_control_msg(udev, 1, init[cam->method][i].value,
- 0, init[i][cam->method].bytes,
+ 0, init[cam->method][i].bytes,
init[cam->method][i].size);
if (err < 0) {
dev_err(&udev->dev, "error during release sequence\n");
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index bae61b22501..7d430835655 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -180,14 +180,9 @@ static struct completion irq_event;
static int twl4030_irq_thread(void *data)
{
long irq = (long)data;
- struct irq_desc *desc = irq_to_desc(irq);
static unsigned i2c_errors;
static const unsigned max_i2c_errors = 100;
- if (!desc) {
- pr_err("twl4030: Invalid IRQ: %ld\n", irq);
- return -EINVAL;
- }
current->flags |= PF_NOFREEZE;
@@ -240,7 +235,7 @@ static int twl4030_irq_thread(void *data)
}
local_irq_enable();
- desc->chip->unmask(irq);
+ enable_irq(irq);
}
return 0;
@@ -255,25 +250,13 @@ static int twl4030_irq_thread(void *data)
* thread. All we do here is acknowledge and mask the interrupt and wakeup
* the kernel thread.
*/
-static void handle_twl4030_pih(unsigned int irq, struct irq_desc *desc)
+static irqreturn_t handle_twl4030_pih(int irq, void *devid)
{
/* Acknowledge, clear *AND* mask the interrupt... */
- desc->chip->ack(irq);
- complete(&irq_event);
-}
-
-static struct task_struct *start_twl4030_irq_thread(long irq)
-{
- struct task_struct *thread;
-
- init_completion(&irq_event);
- thread = kthread_run(twl4030_irq_thread, (void *)irq, "twl4030-irq");
- if (!thread)
- pr_err("twl4030: could not create irq %ld thread!\n", irq);
-
- return thread;
+ disable_irq_nosync(irq);
+ complete(devid);
+ return IRQ_HANDLED;
}
-
/*----------------------------------------------------------------------*/
/*
@@ -734,18 +717,28 @@ int twl_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
}
/* install an irq handler to demultiplex the TWL4030 interrupt */
- task = start_twl4030_irq_thread(irq_num);
- if (!task) {
- pr_err("twl4030: irq thread FAIL\n");
- status = -ESRCH;
- goto fail;
- }
- set_irq_data(irq_num, task);
- set_irq_chained_handler(irq_num, handle_twl4030_pih);
- return status;
+ init_completion(&irq_event);
+ status = request_irq(irq_num, handle_twl4030_pih, IRQF_DISABLED,
+ "TWL4030-PIH", &irq_event);
+ if (status < 0) {
+ pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status);
+ goto fail_rqirq;
+ }
+
+ task = kthread_run(twl4030_irq_thread, (void *)irq_num, "twl4030-irq");
+ if (IS_ERR(task)) {
+ pr_err("twl4030: could not create irq %d thread!\n", irq_num);
+ status = PTR_ERR(task);
+ goto fail_kthread;
+ }
+ return status;
+fail_kthread:
+ free_irq(irq_num, &irq_event);
+fail_rqirq:
+ /* clean up twl4030_sih_setup */
fail:
for (i = irq_base; i < irq_end; i++)
set_irq_chip_and_handler(i, NULL, NULL);
diff --git a/drivers/mmc/host/sdhci-of.c b/drivers/mmc/host/sdhci-of.c
index 908844327db..1e8aa590bb3 100644
--- a/drivers/mmc/host/sdhci-of.c
+++ b/drivers/mmc/host/sdhci-of.c
@@ -234,7 +234,7 @@ static int __devinit sdhci_of_probe(struct of_device *ofdev,
return -ENODEV;
host = sdhci_alloc_host(&ofdev->dev, sizeof(*of_host));
- if (!host)
+ if (IS_ERR(host))
return -ENOMEM;
of_host = sdhci_priv(host);
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index ae5fe91867e..10ed195c0c1 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -736,7 +736,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
flash->partitioned = 1;
return add_mtd_partitions(&flash->mtd, parts, nr_parts);
}
- } else if (data->nr_parts)
+ } else if (data && data->nr_parts)
dev_warn(&spi->dev, "ignoring %d default partitions on %s\n",
data->nr_parts, data->name);
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 0b98654d8ee..7a58bd5522f 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -284,13 +284,6 @@ config MTD_L440GX
BE VERY CAREFUL.
-config MTD_SBC8240
- tristate "Flash device on SBC8240"
- depends on MTD_JEDECPROBE && 8260
- help
- Flash access on the SBC8240 board from Wind River. See
- <http://www.windriver.com/products/sbc8240/>
-
config MTD_TQM8XXL
tristate "CFI Flash device mapped on TQM8XXL"
depends on MTD_CFI && TQM8xxL
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 8bae7f9850c..5beb0662d72 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -50,7 +50,6 @@ obj-$(CONFIG_MTD_UCLINUX) += uclinux.o
obj-$(CONFIG_MTD_NETtel) += nettel.o
obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
obj-$(CONFIG_MTD_H720X) += h720x-flash.o
-obj-$(CONFIG_MTD_SBC8240) += sbc8240.o
obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o
obj-$(CONFIG_MTD_IXP2000) += ixp2000.o
obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o
diff --git a/drivers/mtd/maps/sbc8240.c b/drivers/mtd/maps/sbc8240.c
deleted file mode 100644
index d5374cdcb16..00000000000
--- a/drivers/mtd/maps/sbc8240.c
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Handle mapping of the flash memory access routines on the SBC8240 board.
- *
- * Carolyn Smith, Tektronix, Inc.
- *
- * This code is GPLed
- */
-
-/*
- * The SBC8240 has 2 flash banks.
- * Bank 0 is a 512 KiB AMD AM29F040B; 8 x 64 KiB sectors.
- * It contains the U-Boot code (7 sectors) and the environment (1 sector).
- * Bank 1 is 4 x 1 MiB AMD AM29LV800BT; 15 x 64 KiB sectors, 1 x 32 KiB sector,
- * 2 x 8 KiB sectors, 1 x 16 KiB sectors.
- * Both parts are JEDEC compatible.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <asm/io.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/cfi.h>
-
-#ifdef CONFIG_MTD_PARTITIONS
-#include <linux/mtd/partitions.h>
-#endif
-
-#define DEBUG
-
-#ifdef DEBUG
-# define debugk(fmt,args...) printk(fmt ,##args)
-#else
-# define debugk(fmt,args...)
-#endif
-
-
-#define WINDOW_ADDR0 0xFFF00000 /* 512 KiB */
-#define WINDOW_SIZE0 0x00080000
-#define BUSWIDTH0 1
-
-#define WINDOW_ADDR1 0xFF000000 /* 4 MiB */
-#define WINDOW_SIZE1 0x00400000
-#define BUSWIDTH1 8
-
-#define MSG_PREFIX "sbc8240:" /* prefix for our printk()'s */
-#define MTDID "sbc8240-%d" /* for mtdparts= partitioning */
-
-
-static struct map_info sbc8240_map[2] = {
- {
- .name = "sbc8240 Flash Bank #0",
- .size = WINDOW_SIZE0,
- .bankwidth = BUSWIDTH0,
- },
- {
- .name = "sbc8240 Flash Bank #1",
- .size = WINDOW_SIZE1,
- .bankwidth = BUSWIDTH1,
- }
-};
-
-#define NUM_FLASH_BANKS ARRAY_SIZE(sbc8240_map)
-
-/*
- * The following defines the partition layout of SBC8240 boards.
- *
- * See include/linux/mtd/partitions.h for definition of the
- * mtd_partition structure.
- *
- * The *_max_flash_size is the maximum possible mapped flash size
- * which is not necessarily the actual flash size. It must correspond
- * to the value specified in the mapping definition defined by the
- * "struct map_desc *_io_desc" for the corresponding machine.
- */
-
-#ifdef CONFIG_MTD_PARTITIONS
-
-static struct mtd_partition sbc8240_uboot_partitions [] = {
- /* Bank 0 */
- {
- .name = "U-boot", /* U-Boot Firmware */
- .offset = 0,
- .size = 0x00070000, /* 7 x 64 KiB sectors */
- .mask_flags = MTD_WRITEABLE, /* force read-only */
- },
- {
- .name = "environment", /* U-Boot environment */
- .offset = 0x00070000,
- .size = 0x00010000, /* 1 x 64 KiB sector */
- },
-};
-
-static struct mtd_partition sbc8240_fs_partitions [] = {
- {
- .name = "jffs", /* JFFS filesystem */
- .offset = 0,
- .size = 0x003C0000, /* 4 * 15 * 64KiB */
- },
- {
- .name = "tmp32",
- .offset = 0x003C0000,
- .size = 0x00020000, /* 4 * 32KiB */
- },
- {
- .name = "tmp8a",
- .offset = 0x003E0000,
- .size = 0x00008000, /* 4 * 8KiB */
- },
- {
- .name = "tmp8b",
- .offset = 0x003E8000,
- .size = 0x00008000, /* 4 * 8KiB */
- },
- {
- .name = "tmp16",
- .offset = 0x003F0000,
- .size = 0x00010000, /* 4 * 16KiB */
- }
-};
-
-/* trivial struct to describe partition information */
-struct mtd_part_def
-{
- int nums;
- unsigned char *type;
- struct mtd_partition* mtd_part;
-};
-
-static struct mtd_info *sbc8240_mtd[NUM_FLASH_BANKS];
-static struct mtd_part_def sbc8240_part_banks[NUM_FLASH_BANKS];
-
-
-#endif /* CONFIG_MTD_PARTITIONS */
-
-
-static int __init init_sbc8240_mtd (void)
-{
- static struct _cjs {
- u_long addr;
- u_long size;
- } pt[NUM_FLASH_BANKS] = {
- {
- .addr = WINDOW_ADDR0,
- .size = WINDOW_SIZE0
- },
- {
- .addr = WINDOW_ADDR1,
- .size = WINDOW_SIZE1
- },
- };
-
- int devicesfound = 0;
- int i,j;
-
- for (i = 0; i < NUM_FLASH_BANKS; i++) {
- printk (KERN_NOTICE MSG_PREFIX
- "Probing 0x%08lx at 0x%08lx\n", pt[i].size, pt[i].addr);
-
- sbc8240_map[i].map_priv_1 =
- (unsigned long) ioremap (pt[i].addr, pt[i].size);
- if (!sbc8240_map[i].map_priv_1) {
- printk (MSG_PREFIX "failed to ioremap\n");
- for (j = 0; j < i; j++) {
- iounmap((void *) sbc8240_map[j].map_priv_1);
- sbc8240_map[j].map_priv_1 = 0;
- }
- return -EIO;
- }
- simple_map_init(&sbc8240_mtd[i]);
-
- sbc8240_mtd[i] = do_map_probe("jedec_probe", &sbc8240_map[i]);
-
- if (sbc8240_mtd[i]) {
- sbc8240_mtd[i]->module = THIS_MODULE;
- devicesfound++;
- } else {
- if (sbc8240_map[i].map_priv_1) {
- iounmap((void *) sbc8240_map[i].map_priv_1);
- sbc8240_map[i].map_priv_1 = 0;
- }
- }
- }
-
- if (!devicesfound) {
- printk(KERN_NOTICE MSG_PREFIX
- "No suppported flash chips found!\n");
- return -ENXIO;
- }
-
-#ifdef CONFIG_MTD_PARTITIONS
- sbc8240_part_banks[0].mtd_part = sbc8240_uboot_partitions;
- sbc8240_part_banks[0].type = "static image";
- sbc8240_part_banks[0].nums = ARRAY_SIZE(sbc8240_uboot_partitions);
- sbc8240_part_banks[1].mtd_part = sbc8240_fs_partitions;
- sbc8240_part_banks[1].type = "static file system";
- sbc8240_part_banks[1].nums = ARRAY_SIZE(sbc8240_fs_partitions);
-
- for (i = 0; i < NUM_FLASH_BANKS; i++) {
-
- if (!sbc8240_mtd[i]) continue;
- if (sbc8240_part_banks[i].nums == 0) {
- printk (KERN_NOTICE MSG_PREFIX
- "No partition info available, registering whole device\n");
- add_mtd_device(sbc8240_mtd[i]);
- } else {
- printk (KERN_NOTICE MSG_PREFIX
- "Using %s partition definition\n", sbc8240_part_banks[i].mtd_part->name);
- add_mtd_partitions (sbc8240_mtd[i],
- sbc8240_part_banks[i].mtd_part,
- sbc8240_part_banks[i].nums);
- }
- }
-#else
- printk(KERN_NOTICE MSG_PREFIX
- "Registering %d flash banks at once\n", devicesfound);
-
- for (i = 0; i < devicesfound; i++) {
- add_mtd_device(sbc8240_mtd[i]);
- }
-#endif /* CONFIG_MTD_PARTITIONS */
-
- return devicesfound == 0 ? -ENXIO : 0;
-}
-
-static void __exit cleanup_sbc8240_mtd (void)
-{
- int i;
-
- for (i = 0; i < NUM_FLASH_BANKS; i++) {
- if (sbc8240_mtd[i]) {
- del_mtd_device (sbc8240_mtd[i]);
- map_destroy (sbc8240_mtd[i]);
- }
- if (sbc8240_map[i].map_priv_1) {
- iounmap ((void *) sbc8240_map[i].map_priv_1);
- sbc8240_map[i].map_priv_1 = 0;
- }
- }
-}
-
-module_init (init_sbc8240_mtd);
-module_exit (cleanup_sbc8240_mtd);
-
-MODULE_LICENSE ("GPL");
-MODULE_AUTHOR ("Carolyn Smith <carolyn.smith@tektronix.com>");
-MODULE_DESCRIPTION ("MTD map driver for SBC8240 boards");
-
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index c3f62654b6d..7baba40c1ed 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -144,7 +144,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
struct mtd_blktrans_ops *tr = dev->tr;
int ret = -ENODEV;
- if (!try_module_get(dev->mtd->owner))
+ if (!get_mtd_device(NULL, dev->mtd->index))
goto out;
if (!try_module_get(tr->owner))
@@ -158,7 +158,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
ret = 0;
if (tr->open && (ret = tr->open(dev))) {
dev->mtd->usecount--;
- module_put(dev->mtd->owner);
+ put_mtd_device(dev->mtd);
out_tr:
module_put(tr->owner);
}
@@ -177,7 +177,7 @@ static int blktrans_release(struct gendisk *disk, fmode_t mode)
if (!ret) {
dev->mtd->usecount--;
- module_put(dev->mtd->owner);
+ put_mtd_device(dev->mtd);
module_put(tr->owner);
}
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
index 208c6faa035..77db5ce24d9 100644
--- a/drivers/mtd/mtdblock.c
+++ b/drivers/mtd/mtdblock.c
@@ -29,6 +29,8 @@ static struct mtdblk_dev {
enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
} *mtdblks[MAX_MTD_DEVICES];
+static struct mutex mtdblks_lock;
+
/*
* Cache stuff...
*
@@ -270,15 +272,19 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n");
+ mutex_lock(&mtdblks_lock);
if (mtdblks[dev]) {
mtdblks[dev]->count++;
+ mutex_unlock(&mtdblks_lock);
return 0;
}
/* OK, it's not open. Create cache info for it */
mtdblk = kzalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);
- if (!mtdblk)
+ if (!mtdblk) {
+ mutex_unlock(&mtdblks_lock);
return -ENOMEM;
+ }
mtdblk->count = 1;
mtdblk->mtd = mtd;
@@ -291,6 +297,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
}
mtdblks[dev] = mtdblk;
+ mutex_unlock(&mtdblks_lock);
DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
@@ -304,6 +311,8 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd)
DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
+ mutex_lock(&mtdblks_lock);
+
mutex_lock(&mtdblk->cache_mutex);
write_cached_data(mtdblk);
mutex_unlock(&mtdblk->cache_mutex);
@@ -316,6 +325,9 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd)
vfree(mtdblk->cache_data);
kfree(mtdblk);
}
+
+ mutex_unlock(&mtdblks_lock);
+
DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
return 0;
@@ -376,6 +388,8 @@ static struct mtd_blktrans_ops mtdblock_tr = {
static int __init init_mtdblock(void)
{
+ mutex_init(&mtdblks_lock);
+
return register_mtd_blktrans(&mtdblock_tr);
}
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index fac54a3fa3f..00ebf7af746 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -65,8 +65,8 @@ static void mtd_release(struct device *dev)
static int mtd_cls_suspend(struct device *dev, pm_message_t state)
{
struct mtd_info *mtd = dev_to_mtd(dev);
-
- if (mtd->suspend)
+
+ if (mtd && mtd->suspend)
return mtd->suspend(mtd);
else
return 0;
@@ -76,7 +76,7 @@ static int mtd_cls_resume(struct device *dev)
{
struct mtd_info *mtd = dev_to_mtd(dev);
- if (mtd->resume)
+ if (mtd && mtd->resume)
mtd->resume(mtd);
return 0;
}
@@ -298,6 +298,7 @@ int add_mtd_device(struct mtd_info *mtd)
mtd->dev.class = &mtd_class;
mtd->dev.devt = MTD_DEVT(i);
dev_set_name(&mtd->dev, "mtd%d", i);
+ dev_set_drvdata(&mtd->dev, mtd);
if (device_register(&mtd->dev) != 0) {
mtd_table[i] = NULL;
break;
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c
index 7ad972229db..0d9d4bc9c76 100644
--- a/drivers/mtd/nand/orion_nand.c
+++ b/drivers/mtd/nand/orion_nand.c
@@ -61,7 +61,7 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
buf64 = (uint64_t *)buf;
while (i < len/8) {
uint64_t x;
- asm ("ldrd\t%0, [%1]" : "=r" (x) : "r" (io_base));
+ asm volatile ("ldrd\t%0, [%1]" : "=&r" (x) : "r" (io_base));
buf64[i++] = x;
}
i *= 8;
diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c
index fb86cacd5bd..1002e188299 100644
--- a/drivers/mtd/nftlcore.c
+++ b/drivers/mtd/nftlcore.c
@@ -135,16 +135,17 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
size_t *retlen, uint8_t *buf)
{
+ loff_t mask = mtd->writesize - 1;
struct mtd_oob_ops ops;
int res;
ops.mode = MTD_OOB_PLACE;
- ops.ooboffs = offs & (mtd->writesize - 1);
+ ops.ooboffs = offs & mask;
ops.ooblen = len;
ops.oobbuf = buf;
ops.datbuf = NULL;
- res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+ res = mtd->read_oob(mtd, offs & ~mask, &ops);
*retlen = ops.oobretlen;
return res;
}
@@ -155,16 +156,17 @@ int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
size_t *retlen, uint8_t *buf)
{
+ loff_t mask = mtd->writesize - 1;
struct mtd_oob_ops ops;
int res;
ops.mode = MTD_OOB_PLACE;
- ops.ooboffs = offs & (mtd->writesize - 1);
+ ops.ooboffs = offs & mask;
ops.ooblen = len;
ops.oobbuf = buf;
ops.datbuf = NULL;
- res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+ res = mtd->write_oob(mtd, offs & ~mask, &ops);
*retlen = ops.oobretlen;
return res;
}
@@ -177,17 +179,18 @@ int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
size_t *retlen, uint8_t *buf, uint8_t *oob)
{
+ loff_t mask = mtd->writesize - 1;
struct mtd_oob_ops ops;
int res;
ops.mode = MTD_OOB_PLACE;
- ops.ooboffs = offs;
+ ops.ooboffs = offs & mask;
ops.ooblen = mtd->oobsize;
ops.oobbuf = oob;
ops.datbuf = buf;
ops.len = len;
- res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
+ res = mtd->write_oob(mtd, offs & ~mask, &ops);
*retlen = ops.retlen;
return res;
}
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
index 38d656b9b2e..0108ed42e87 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/onenand/omap2.c
@@ -266,7 +266,7 @@ static inline int omap2_onenand_bufferram_offset(struct mtd_info *mtd, int area)
if (ONENAND_CURRENT_BUFFERRAM(this)) {
if (area == ONENAND_DATARAM)
- return mtd->writesize;
+ return this->writesize;
if (area == ONENAND_SPARERAM)
return mtd->oobsize;
}
@@ -770,6 +770,7 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev)
}
iounmap(c->onenand.base);
release_mem_region(c->phys_base, ONENAND_IO_SIZE);
+ gpmc_cs_free(c->gpmc_cs);
kfree(c);
return 0;
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index 0f2034c3ed2..e4d9ef0c965 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -1254,6 +1254,7 @@ out_free:
if (!ubi->volumes[i])
continue;
kfree(ubi->volumes[i]->eba_tbl);
+ ubi->volumes[i]->eba_tbl = NULL;
}
return err;
}
diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c
index a423131b617..b847745394b 100644
--- a/drivers/mtd/ubi/scan.c
+++ b/drivers/mtd/ubi/scan.c
@@ -781,11 +781,22 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si,
return -EINVAL;
}
+ /*
+ * Make sure that all PEBs have the same image sequence number.
+ * This allows us to detect situations when users flash UBI
+ * images incorrectly, so that the flash has the new UBI image
+ * and leftovers from the old one. This feature was added
+ * relatively recently, and the sequence number was always
+ * zero, because old UBI implementations always set it to zero.
+ * For this reasons, we do not panic if some PEBs have zero
+ * sequence number, while other PEBs have non-zero sequence
+ * number.
+ */
image_seq = be32_to_cpu(ech->image_seq);
if (!si->image_seq_set) {
ubi->image_seq = image_seq;
si->image_seq_set = 1;
- } else if (ubi->image_seq != image_seq) {
+ } else if (ubi->image_seq && ubi->image_seq != image_seq) {
ubi_err("bad image sequence number %d in PEB %d, "
"expected %d", image_seq, pnum, ubi->image_seq);
ubi_dbg_dump_ec_hdr(ech);
diff --git a/drivers/net/3c515.c b/drivers/net/3c515.c
index 3e00fa8ea65..4a7c32895be 100644
--- a/drivers/net/3c515.c
+++ b/drivers/net/3c515.c
@@ -832,7 +832,9 @@ static int corkscrew_open(struct net_device *dev)
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
vp->rx_ring[i].addr = isa_virt_to_bus(skb->data);
}
- vp->rx_ring[i - 1].next = isa_virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */
+ if (i != 0)
+ vp->rx_ring[i - 1].next =
+ isa_virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */
outl(isa_virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
}
if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index c34aee91250..45675889850 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -235,6 +235,7 @@ enum vortex_chips {
CH_3C900B_FL,
CH_3C905_1,
CH_3C905_2,
+ CH_3C905B_TX,
CH_3C905B_1,
CH_3C905B_2,
@@ -307,6 +308,8 @@ static struct vortex_chip_info {
PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_RESET, 64, },
{"3c905 Boomerang 100baseT4",
PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_RESET, 64, },
+ {"3C905B-TX Fast Etherlink XL PCI",
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
{"3c905B Cyclone 100baseTx",
PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
@@ -389,6 +392,7 @@ static struct pci_device_id vortex_pci_tbl[] = {
{ 0x10B7, 0x900A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900B_FL },
{ 0x10B7, 0x9050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_1 },
{ 0x10B7, 0x9051, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_2 },
+ { 0x10B7, 0x9054, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_TX },
{ 0x10B7, 0x9055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_1 },
{ 0x10B7, 0x9058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_2 },
@@ -2721,13 +2725,15 @@ dump_tx_ring(struct net_device *dev)
&vp->tx_ring[vp->dirty_tx % TX_RING_SIZE]);
issue_and_wait(dev, DownStall);
for (i = 0; i < TX_RING_SIZE; i++) {
- pr_err(" %d: @%p length %8.8x status %8.8x\n", i,
- &vp->tx_ring[i],
+ unsigned int length;
+
#if DO_ZEROCOPY
- le32_to_cpu(vp->tx_ring[i].frag[0].length),
+ length = le32_to_cpu(vp->tx_ring[i].frag[0].length);
#else
- le32_to_cpu(vp->tx_ring[i].length),
+ length = le32_to_cpu(vp->tx_ring[i].length);
#endif
+ pr_err(" %d: @%p length %8.8x status %8.8x\n",
+ i, &vp->tx_ring[i], length,
le32_to_cpu(vp->tx_ring[i].status));
}
if (!stalled)
diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c
index 50efde11ea6..d0dbbf39349 100644
--- a/drivers/net/8139cp.c
+++ b/drivers/net/8139cp.c
@@ -515,7 +515,7 @@ rx_status_loop:
dma_addr_t mapping;
struct sk_buff *skb, *new_skb;
struct cp_desc *desc;
- unsigned buflen;
+ const unsigned buflen = cp->rx_buf_sz;
skb = cp->rx_skb[rx_tail];
BUG_ON(!skb);
@@ -549,8 +549,7 @@ rx_status_loop:
pr_debug("%s: rx slot %d status 0x%x len %d\n",
dev->name, rx_tail, status, len);
- buflen = cp->rx_buf_sz + NET_IP_ALIGN;
- new_skb = netdev_alloc_skb(dev, buflen);
+ new_skb = netdev_alloc_skb(dev, buflen + NET_IP_ALIGN);
if (!new_skb) {
dev->stats.rx_dropped++;
goto rx_next;
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 5f6509a5f64..5ce7cbabd7a 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1727,12 +1727,14 @@ config KS8842
tristate "Micrel KSZ8842"
depends on HAS_IOMEM
help
- This platform driver is for Micrel KSZ8842 chip.
+ This platform driver is for Micrel KSZ8842 / KS8842
+ 2-port ethernet switch chip (managed, VLAN, QoS).
config KS8851
tristate "Micrel KS8851 SPI"
depends on SPI
select MII
+ select CRC32
help
SPI driver for Micrel KS8851 SPI attached network chip.
diff --git a/drivers/net/arm/w90p910_ether.c b/drivers/net/arm/w90p910_ether.c
index 616fb7985a3..ddd231cb54b 100644
--- a/drivers/net/arm/w90p910_ether.c
+++ b/drivers/net/arm/w90p910_ether.c
@@ -1080,7 +1080,7 @@ static struct platform_driver w90p910_ether_driver = {
.probe = w90p910_ether_probe,
.remove = __devexit_p(w90p910_ether_remove),
.driver = {
- .name = "w90p910-emc",
+ .name = "nuc900-emc",
.owner = THIS_MODULE,
},
};
@@ -1101,5 +1101,5 @@ module_exit(w90p910_ether_exit);
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
MODULE_DESCRIPTION("w90p910 MAC driver!");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:w90p910-emc");
+MODULE_ALIAS("platform:nuc900-emc");
diff --git a/drivers/net/atl1c/atl1c_ethtool.c b/drivers/net/atl1c/atl1c_ethtool.c
index 607007d75b6..00d11b480af 100644
--- a/drivers/net/atl1c/atl1c_ethtool.c
+++ b/drivers/net/atl1c/atl1c_ethtool.c
@@ -232,11 +232,11 @@ static void atl1c_get_drvinfo(struct net_device *netdev,
{
struct atl1c_adapter *adapter = netdev_priv(netdev);
- strncpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver));
- strncpy(drvinfo->version, atl1c_driver_version,
+ strlcpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver));
+ strlcpy(drvinfo->version, atl1c_driver_version,
sizeof(drvinfo->version));
- strncpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
- strncpy(drvinfo->bus_info, pci_name(adapter->pdev),
+ strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
+ strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
sizeof(drvinfo->bus_info));
drvinfo->n_stats = 0;
drvinfo->testinfo_len = 0;
diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c
index 94d7325caf4..8bca12f7139 100644
--- a/drivers/net/atlx/atl1.c
+++ b/drivers/net/atlx/atl1.c
@@ -3378,11 +3378,11 @@ static void atl1_get_drvinfo(struct net_device *netdev,
{
struct atl1_adapter *adapter = netdev_priv(netdev);
- strncpy(drvinfo->driver, ATLX_DRIVER_NAME, sizeof(drvinfo->driver));
- strncpy(drvinfo->version, ATLX_DRIVER_VERSION,
+ strlcpy(drvinfo->driver, ATLX_DRIVER_NAME, sizeof(drvinfo->driver));
+ strlcpy(drvinfo->version, ATLX_DRIVER_VERSION,
sizeof(drvinfo->version));
- strncpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
- strncpy(drvinfo->bus_info, pci_name(adapter->pdev),
+ strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
+ strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
sizeof(drvinfo->bus_info));
drvinfo->eedump_len = ATL1_EEDUMP_LEN;
}
diff --git a/drivers/net/b44.c b/drivers/net/b44.c
index 36d4d377ec2..bafca672ea7 100644
--- a/drivers/net/b44.c
+++ b/drivers/net/b44.c
@@ -952,9 +952,10 @@ static int b44_start_xmit(struct sk_buff *skb, struct net_device *dev)
int rc = NETDEV_TX_OK;
dma_addr_t mapping;
u32 len, entry, ctrl;
+ unsigned long flags;
len = skb->len;
- spin_lock_irq(&bp->lock);
+ spin_lock_irqsave(&bp->lock, flags);
/* This is a hard error, log it. */
if (unlikely(TX_BUFFS_AVAIL(bp) < 1)) {
@@ -1027,7 +1028,7 @@ static int b44_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev->trans_start = jiffies;
out_unlock:
- spin_unlock_irq(&bp->lock);
+ spin_unlock_irqrestore(&bp->lock, flags);
return rc;
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index b70cc99962f..06b901152d4 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -399,9 +399,11 @@ static int bnx2_unregister_cnic(struct net_device *dev)
struct bnx2_napi *bnapi = &bp->bnx2_napi[0];
struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+ mutex_lock(&bp->cnic_lock);
cp->drv_state = 0;
bnapi->cnic_present = 0;
rcu_assign_pointer(bp->cnic_ops, NULL);
+ mutex_unlock(&bp->cnic_lock);
synchronize_rcu();
return 0;
}
@@ -429,13 +431,13 @@ bnx2_cnic_stop(struct bnx2 *bp)
struct cnic_ops *c_ops;
struct cnic_ctl_info info;
- rcu_read_lock();
- c_ops = rcu_dereference(bp->cnic_ops);
+ mutex_lock(&bp->cnic_lock);
+ c_ops = bp->cnic_ops;
if (c_ops) {
info.cmd = CNIC_CTL_STOP_CMD;
c_ops->cnic_ctl(bp->cnic_data, &info);
}
- rcu_read_unlock();
+ mutex_unlock(&bp->cnic_lock);
}
static void
@@ -444,8 +446,8 @@ bnx2_cnic_start(struct bnx2 *bp)
struct cnic_ops *c_ops;
struct cnic_ctl_info info;
- rcu_read_lock();
- c_ops = rcu_dereference(bp->cnic_ops);
+ mutex_lock(&bp->cnic_lock);
+ c_ops = bp->cnic_ops;
if (c_ops) {
if (!(bp->flags & BNX2_FLAG_USING_MSIX)) {
struct bnx2_napi *bnapi = &bp->bnx2_napi[0];
@@ -455,7 +457,7 @@ bnx2_cnic_start(struct bnx2 *bp)
info.cmd = CNIC_CTL_START_CMD;
c_ops->cnic_ctl(bp->cnic_data, &info);
}
- rcu_read_unlock();
+ mutex_unlock(&bp->cnic_lock);
}
#else
@@ -7663,6 +7665,9 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
spin_lock_init(&bp->phy_lock);
spin_lock_init(&bp->indirect_lock);
+#ifdef BCM_CNIC
+ mutex_init(&bp->cnic_lock);
+#endif
INIT_WORK(&bp->reset_task, bnx2_reset_task);
dev->base_addr = dev->mem_start = pci_resource_start(pdev, 0);
diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h
index f1edfaa9e56..a4f12fd0ecd 100644
--- a/drivers/net/bnx2.h
+++ b/drivers/net/bnx2.h
@@ -6902,6 +6902,7 @@ struct bnx2 {
u32 idle_chk_status_idx;
#ifdef BCM_CNIC
+ struct mutex cnic_lock;
struct cnic_eth_dev cnic_eth_dev;
#endif
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index 9e4283aff82..e1a4f821423 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -611,11 +611,18 @@ nla_put_failure:
return -EMSGSIZE;
}
+static int can_newlink(struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ return -EOPNOTSUPP;
+}
+
static struct rtnl_link_ops can_link_ops __read_mostly = {
.kind = "can",
.maxtype = IFLA_CAN_MAX,
.policy = can_policy,
.setup = can_setup,
+ .newlink = can_newlink,
.changelink = can_changelink,
.fill_info = can_fill_info,
.fill_xstats = can_fill_xstats,
diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c
index 4869d77cbe9..74c342959b7 100644
--- a/drivers/net/cnic.c
+++ b/drivers/net/cnic.c
@@ -138,6 +138,16 @@ static struct cnic_dev *cnic_from_netdev(struct net_device *netdev)
return NULL;
}
+static inline void ulp_get(struct cnic_ulp_ops *ulp_ops)
+{
+ atomic_inc(&ulp_ops->ref_count);
+}
+
+static inline void ulp_put(struct cnic_ulp_ops *ulp_ops)
+{
+ atomic_dec(&ulp_ops->ref_count);
+}
+
static void cnic_ctx_wr(struct cnic_dev *dev, u32 cid_addr, u32 off, u32 val)
{
struct cnic_local *cp = dev->cnic_priv;
@@ -358,6 +368,7 @@ int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops)
}
read_unlock(&cnic_dev_lock);
+ atomic_set(&ulp_ops->ref_count, 0);
rcu_assign_pointer(cnic_ulp_tbl[ulp_type], ulp_ops);
mutex_unlock(&cnic_lock);
@@ -379,6 +390,8 @@ int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops)
int cnic_unregister_driver(int ulp_type)
{
struct cnic_dev *dev;
+ struct cnic_ulp_ops *ulp_ops;
+ int i = 0;
if (ulp_type >= MAX_CNIC_ULP_TYPE) {
printk(KERN_ERR PFX "cnic_unregister_driver: Bad type %d\n",
@@ -386,7 +399,8 @@ int cnic_unregister_driver(int ulp_type)
return -EINVAL;
}
mutex_lock(&cnic_lock);
- if (!cnic_ulp_tbl[ulp_type]) {
+ ulp_ops = cnic_ulp_tbl[ulp_type];
+ if (!ulp_ops) {
printk(KERN_ERR PFX "cnic_unregister_driver: Type %d has not "
"been registered\n", ulp_type);
goto out_unlock;
@@ -411,6 +425,14 @@ int cnic_unregister_driver(int ulp_type)
mutex_unlock(&cnic_lock);
synchronize_rcu();
+ while ((atomic_read(&ulp_ops->ref_count) != 0) && (i < 20)) {
+ msleep(100);
+ i++;
+ }
+
+ if (atomic_read(&ulp_ops->ref_count) != 0)
+ printk(KERN_WARNING PFX "%s: Failed waiting for ref count to go"
+ " to zero.\n", dev->netdev->name);
return 0;
out_unlock:
@@ -466,6 +488,7 @@ EXPORT_SYMBOL(cnic_register_driver);
static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
{
struct cnic_local *cp = dev->cnic_priv;
+ int i = 0;
if (ulp_type >= MAX_CNIC_ULP_TYPE) {
printk(KERN_ERR PFX "cnic_unregister_device: Bad type %d\n",
@@ -486,6 +509,15 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
synchronize_rcu();
+ while (test_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[ulp_type]) &&
+ i < 20) {
+ msleep(100);
+ i++;
+ }
+ if (test_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[ulp_type]))
+ printk(KERN_WARNING PFX "%s: Failed waiting for ULP up call"
+ " to complete.\n", dev->netdev->name);
+
return 0;
}
EXPORT_SYMBOL(cnic_unregister_driver);
@@ -1076,18 +1108,23 @@ static void cnic_ulp_stop(struct cnic_dev *dev)
if (cp->cnic_uinfo)
cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL);
- rcu_read_lock();
for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
struct cnic_ulp_ops *ulp_ops;
- ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
- if (!ulp_ops)
+ mutex_lock(&cnic_lock);
+ ulp_ops = cp->ulp_ops[if_type];
+ if (!ulp_ops) {
+ mutex_unlock(&cnic_lock);
continue;
+ }
+ set_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
+ mutex_unlock(&cnic_lock);
if (test_and_clear_bit(ULP_F_START, &cp->ulp_flags[if_type]))
ulp_ops->cnic_stop(cp->ulp_handle[if_type]);
+
+ clear_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
}
- rcu_read_unlock();
}
static void cnic_ulp_start(struct cnic_dev *dev)
@@ -1095,18 +1132,23 @@ static void cnic_ulp_start(struct cnic_dev *dev)
struct cnic_local *cp = dev->cnic_priv;
int if_type;
- rcu_read_lock();
for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
struct cnic_ulp_ops *ulp_ops;
- ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
- if (!ulp_ops || !ulp_ops->cnic_start)
+ mutex_lock(&cnic_lock);
+ ulp_ops = cp->ulp_ops[if_type];
+ if (!ulp_ops || !ulp_ops->cnic_start) {
+ mutex_unlock(&cnic_lock);
continue;
+ }
+ set_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
+ mutex_unlock(&cnic_lock);
if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[if_type]))
ulp_ops->cnic_start(cp->ulp_handle[if_type]);
+
+ clear_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
}
- rcu_read_unlock();
}
static int cnic_ctl(void *data, struct cnic_ctl_info *info)
@@ -1116,22 +1158,18 @@ static int cnic_ctl(void *data, struct cnic_ctl_info *info)
switch (info->cmd) {
case CNIC_CTL_STOP_CMD:
cnic_hold(dev);
- mutex_lock(&cnic_lock);
cnic_ulp_stop(dev);
cnic_stop_hw(dev);
- mutex_unlock(&cnic_lock);
cnic_put(dev);
break;
case CNIC_CTL_START_CMD:
cnic_hold(dev);
- mutex_lock(&cnic_lock);
if (!cnic_start_hw(dev))
cnic_ulp_start(dev);
- mutex_unlock(&cnic_lock);
cnic_put(dev);
break;
default:
@@ -1145,19 +1183,23 @@ static void cnic_ulp_init(struct cnic_dev *dev)
int i;
struct cnic_local *cp = dev->cnic_priv;
- rcu_read_lock();
for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) {
struct cnic_ulp_ops *ulp_ops;
- ulp_ops = rcu_dereference(cnic_ulp_tbl[i]);
- if (!ulp_ops || !ulp_ops->cnic_init)
+ mutex_lock(&cnic_lock);
+ ulp_ops = cnic_ulp_tbl[i];
+ if (!ulp_ops || !ulp_ops->cnic_init) {
+ mutex_unlock(&cnic_lock);
continue;
+ }
+ ulp_get(ulp_ops);
+ mutex_unlock(&cnic_lock);
if (!test_and_set_bit(ULP_F_INIT, &cp->ulp_flags[i]))
ulp_ops->cnic_init(dev);
+ ulp_put(ulp_ops);
}
- rcu_read_unlock();
}
static void cnic_ulp_exit(struct cnic_dev *dev)
@@ -1165,19 +1207,23 @@ static void cnic_ulp_exit(struct cnic_dev *dev)
int i;
struct cnic_local *cp = dev->cnic_priv;
- rcu_read_lock();
for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) {
struct cnic_ulp_ops *ulp_ops;
- ulp_ops = rcu_dereference(cnic_ulp_tbl[i]);
- if (!ulp_ops || !ulp_ops->cnic_exit)
+ mutex_lock(&cnic_lock);
+ ulp_ops = cnic_ulp_tbl[i];
+ if (!ulp_ops || !ulp_ops->cnic_exit) {
+ mutex_unlock(&cnic_lock);
continue;
+ }
+ ulp_get(ulp_ops);
+ mutex_unlock(&cnic_lock);
if (test_and_clear_bit(ULP_F_INIT, &cp->ulp_flags[i]))
ulp_ops->cnic_exit(dev);
+ ulp_put(ulp_ops);
}
- rcu_read_unlock();
}
static int cnic_cm_offload_pg(struct cnic_sock *csk)
@@ -2393,21 +2439,45 @@ static int cnic_start_bnx2_hw(struct cnic_dev *dev)
return 0;
}
-static int cnic_start_hw(struct cnic_dev *dev)
+static int cnic_register_netdev(struct cnic_dev *dev)
{
struct cnic_local *cp = dev->cnic_priv;
struct cnic_eth_dev *ethdev = cp->ethdev;
int err;
- if (test_bit(CNIC_F_CNIC_UP, &dev->flags))
- return -EALREADY;
+ if (!ethdev)
+ return -ENODEV;
+
+ if (ethdev->drv_state & CNIC_DRV_STATE_REGD)
+ return 0;
err = ethdev->drv_register_cnic(dev->netdev, cp->cnic_ops, dev);
- if (err) {
+ if (err)
printk(KERN_ERR PFX "%s: register_cnic failed\n",
dev->netdev->name);
- goto err2;
- }
+
+ return err;
+}
+
+static void cnic_unregister_netdev(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+
+ if (!ethdev)
+ return;
+
+ ethdev->drv_unregister_cnic(dev->netdev);
+}
+
+static int cnic_start_hw(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ int err;
+
+ if (test_bit(CNIC_F_CNIC_UP, &dev->flags))
+ return -EALREADY;
dev->regview = ethdev->io_base;
cp->chip_id = ethdev->chip_id;
@@ -2438,18 +2508,13 @@ static int cnic_start_hw(struct cnic_dev *dev)
return 0;
err1:
- ethdev->drv_unregister_cnic(dev->netdev);
cp->free_resc(dev);
pci_dev_put(dev->pcidev);
-err2:
return err;
}
static void cnic_stop_bnx2_hw(struct cnic_dev *dev)
{
- struct cnic_local *cp = dev->cnic_priv;
- struct cnic_eth_dev *ethdev = cp->ethdev;
-
cnic_disable_bnx2_int_sync(dev);
cnic_reg_wr_ind(dev, BNX2_CP_SCRATCH + 0x20, 0);
@@ -2461,8 +2526,6 @@ static void cnic_stop_bnx2_hw(struct cnic_dev *dev)
cnic_setup_5709_context(dev, 0);
cnic_free_irq(dev);
- ethdev->drv_unregister_cnic(dev->netdev);
-
cnic_free_resc(dev);
}
@@ -2543,7 +2606,7 @@ static struct cnic_dev *init_bnx2_cnic(struct net_device *dev)
probe = symbol_get(bnx2_cnic_probe);
if (probe) {
ethdev = (*probe)(dev);
- symbol_put_addr(probe);
+ symbol_put(bnx2_cnic_probe);
}
if (!ethdev)
return NULL;
@@ -2646,10 +2709,12 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event,
else if (event == NETDEV_UNREGISTER)
cnic_ulp_exit(dev);
else if (event == NETDEV_UP) {
- mutex_lock(&cnic_lock);
+ if (cnic_register_netdev(dev) != 0) {
+ cnic_put(dev);
+ goto done;
+ }
if (!cnic_start_hw(dev))
cnic_ulp_start(dev);
- mutex_unlock(&cnic_lock);
}
rcu_read_lock();
@@ -2668,10 +2733,9 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event,
rcu_read_unlock();
if (event == NETDEV_GOING_DOWN) {
- mutex_lock(&cnic_lock);
cnic_ulp_stop(dev);
cnic_stop_hw(dev);
- mutex_unlock(&cnic_lock);
+ cnic_unregister_netdev(dev);
} else if (event == NETDEV_UNREGISTER) {
write_lock(&cnic_dev_lock);
list_del_init(&dev->list);
@@ -2703,6 +2767,7 @@ static void cnic_release(void)
}
cnic_ulp_exit(dev);
+ cnic_unregister_netdev(dev);
list_del_init(&dev->list);
cnic_free_dev(dev);
}
diff --git a/drivers/net/cnic.h b/drivers/net/cnic.h
index 5192d4a9df5..a94b302bb46 100644
--- a/drivers/net/cnic.h
+++ b/drivers/net/cnic.h
@@ -176,6 +176,7 @@ struct cnic_local {
unsigned long ulp_flags[MAX_CNIC_ULP_TYPE];
#define ULP_F_INIT 0
#define ULP_F_START 1
+#define ULP_F_CALL_PENDING 2
struct cnic_ulp_ops *ulp_ops[MAX_CNIC_ULP_TYPE];
/* protected by ulp_lock */
diff --git a/drivers/net/cnic_if.h b/drivers/net/cnic_if.h
index d1bce27ee99..a49235739ee 100644
--- a/drivers/net/cnic_if.h
+++ b/drivers/net/cnic_if.h
@@ -290,6 +290,7 @@ struct cnic_ulp_ops {
void (*iscsi_nl_send_msg)(struct cnic_dev *dev, u32 msg_type,
char *data, u16 data_size);
struct module *owner;
+ atomic_t ref_count;
};
extern int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops);
diff --git a/drivers/net/e100.c b/drivers/net/e100.c
index 41b648a67fe..3a6735dc9f6 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -1899,7 +1899,7 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx,
nic->ru_running = RU_SUSPENDED;
pci_dma_sync_single_for_device(nic->pdev, rx->dma_addr,
sizeof(struct rfd),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_FROMDEVICE);
return -ENODATA;
}
diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c
index d56c7473144..99df2abf82a 100644
--- a/drivers/net/e1000e/ich8lan.c
+++ b/drivers/net/e1000e/ich8lan.c
@@ -338,10 +338,7 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw)
{
struct e1000_nvm_info *nvm = &hw->nvm;
struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
- union ich8_hws_flash_status hsfsts;
- u32 gfpreg;
- u32 sector_base_addr;
- u32 sector_end_addr;
+ u32 gfpreg, sector_base_addr, sector_end_addr;
u16 i;
/* Can't read flash registers if the register set isn't mapped. */
@@ -375,20 +372,6 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw)
/* Adjust to word count */
nvm->flash_bank_size /= sizeof(u16);
- /*
- * Make sure the flash bank size does not overwrite the 4k
- * sector ranges. We may have 64k allotted to us but we only care
- * about the first 2 4k sectors. Therefore, if we have anything less
- * than 64k set in the HSFSTS register, we will reduce the bank size
- * down to 4k and let the rest remain unused. If berasesz == 3, then
- * we are working in 64k mode. Otherwise we are not.
- */
- if (nvm->flash_bank_size > E1000_ICH8_SHADOW_RAM_WORDS) {
- hsfsts.regval = er16flash(ICH_FLASH_HSFSTS);
- if (hsfsts.hsf_status.berasesz != 3)
- nvm->flash_bank_size = E1000_ICH8_SHADOW_RAM_WORDS;
- }
-
nvm->word_size = E1000_ICH8_SHADOW_RAM_WORDS;
/* Clear shadow ram */
@@ -594,8 +577,8 @@ static DEFINE_MUTEX(nvm_mutex);
**/
static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
{
- u32 extcnf_ctrl;
- u32 timeout = PHY_CFG_TIMEOUT;
+ u32 extcnf_ctrl, timeout = PHY_CFG_TIMEOUT;
+ s32 ret_val = 0;
might_sleep();
@@ -603,28 +586,46 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
while (timeout) {
extcnf_ctrl = er32(EXTCNF_CTRL);
+ if (!(extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG))
+ break;
- if (!(extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)) {
- extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG;
- ew32(EXTCNF_CTRL, extcnf_ctrl);
+ mdelay(1);
+ timeout--;
+ }
+
+ if (!timeout) {
+ hw_dbg(hw, "SW/FW/HW has locked the resource for too long.\n");
+ ret_val = -E1000_ERR_CONFIG;
+ goto out;
+ }
+
+ timeout = PHY_CFG_TIMEOUT * 2;
+
+ extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG;
+ ew32(EXTCNF_CTRL, extcnf_ctrl);
+
+ while (timeout) {
+ extcnf_ctrl = er32(EXTCNF_CTRL);
+ if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)
+ break;
- extcnf_ctrl = er32(EXTCNF_CTRL);
- if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)
- break;
- }
mdelay(1);
timeout--;
}
if (!timeout) {
- hw_dbg(hw, "FW or HW has locked the resource for too long.\n");
+ hw_dbg(hw, "Failed to acquire the semaphore.\n");
extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
ew32(EXTCNF_CTRL, extcnf_ctrl);
- mutex_unlock(&nvm_mutex);
- return -E1000_ERR_CONFIG;
+ ret_val = -E1000_ERR_CONFIG;
+ goto out;
}
- return 0;
+out:
+ if (ret_val)
+ mutex_unlock(&nvm_mutex);
+
+ return ret_val;
}
/**
@@ -1306,7 +1307,7 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
struct e1000_nvm_info *nvm = &hw->nvm;
struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
u32 act_offset;
- s32 ret_val;
+ s32 ret_val = 0;
u32 bank = 0;
u16 i, word;
@@ -1321,12 +1322,15 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
goto out;
ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
- if (ret_val)
- goto release;
+ if (ret_val) {
+ hw_dbg(hw, "Could not detect valid bank, assuming bank 0\n");
+ bank = 0;
+ }
act_offset = (bank) ? nvm->flash_bank_size : 0;
act_offset += offset;
+ ret_val = 0;
for (i = 0; i < words; i++) {
if ((dev_spec->shadow_ram) &&
(dev_spec->shadow_ram[offset+i].modified)) {
@@ -1341,7 +1345,6 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
}
}
-release:
e1000_release_swflag_ich8lan(hw);
out:
@@ -1592,7 +1595,6 @@ static s32 e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
{
struct e1000_nvm_info *nvm = &hw->nvm;
struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
- s32 ret_val;
u16 i;
if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) ||
@@ -1601,17 +1603,11 @@ static s32 e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
return -E1000_ERR_NVM;
}
- ret_val = e1000_acquire_swflag_ich8lan(hw);
- if (ret_val)
- return ret_val;
-
for (i = 0; i < words; i++) {
dev_spec->shadow_ram[offset+i].modified = 1;
dev_spec->shadow_ram[offset+i].value = data[i];
}
- e1000_release_swflag_ich8lan(hw);
-
return 0;
}
@@ -1652,8 +1648,8 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
*/
ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
if (ret_val) {
- e1000_release_swflag_ich8lan(hw);
- goto out;
+ hw_dbg(hw, "Could not detect valid bank, assuming bank 0\n");
+ bank = 0;
}
if (bank == 0) {
@@ -2039,12 +2035,8 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank)
iteration = 1;
break;
case 2:
- if (hw->mac.type == e1000_ich9lan) {
- sector_size = ICH_FLASH_SEG_SIZE_8K;
- iteration = flash_bank_size / ICH_FLASH_SEG_SIZE_8K;
- } else {
- return -E1000_ERR_NVM;
- }
+ sector_size = ICH_FLASH_SEG_SIZE_8K;
+ iteration = 1;
break;
case 3:
sector_size = ICH_FLASH_SEG_SIZE_64K;
@@ -2056,7 +2048,7 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank)
/* Start with the base address, then add the sector offset. */
flash_linear_addr = hw->nvm.flash_base_addr;
- flash_linear_addr += (bank) ? (sector_size * iteration) : 0;
+ flash_linear_addr += (bank) ? flash_bank_size : 0;
for (j = 0; j < iteration ; j++) {
do {
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c
index 63415bb6f48..fa92a683aef 100644
--- a/drivers/net/e1000e/netdev.c
+++ b/drivers/net/e1000e/netdev.c
@@ -4538,8 +4538,7 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake)
/* Allow time for pending master requests to run */
e1000e_disable_pcie_master(&adapter->hw);
- if ((adapter->flags2 & FLAG2_HAS_PHY_WAKEUP) &&
- !(hw->mac.ops.check_mng_mode(hw))) {
+ if (adapter->flags2 & FLAG2_HAS_PHY_WAKEUP) {
/* enable wakeup by the PHY */
retval = e1000_init_phy_wakeup(adapter, wufc);
if (retval)
@@ -4557,7 +4556,8 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake)
*enable_wake = !!wufc;
/* make sure adapter isn't asleep if manageability is enabled */
- if (adapter->flags & FLAG_MNG_PT_ENABLED)
+ if ((adapter->flags & FLAG_MNG_PT_ENABLED) ||
+ (hw->mac.ops.check_mng_mode(hw)))
*enable_wake = true;
if (adapter->hw.phy.type == e1000_phy_igp_3)
@@ -4670,14 +4670,6 @@ static int e1000_resume(struct pci_dev *pdev)
return err;
}
- /* AER (Advanced Error Reporting) hooks */
- err = pci_enable_pcie_error_reporting(pdev);
- if (err) {
- dev_err(&pdev->dev, "pci_enable_pcie_error_reporting failed "
- "0x%x\n", err);
- /* non-fatal, continue */
- }
-
pci_set_master(pdev);
pci_enable_wake(pdev, PCI_D3hot, 0);
@@ -4990,6 +4982,14 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
if (err)
goto err_pci_reg;
+ /* AER (Advanced Error Reporting) hooks */
+ err = pci_enable_pcie_error_reporting(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "pci_enable_pcie_error_reporting failed "
+ "0x%x\n", err);
+ /* non-fatal, continue */
+ }
+
pci_set_master(pdev);
/* PCI config space info */
err = pci_save_state(pdev);
diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c
index 1686dca2874..1f016d66684 100644
--- a/drivers/net/eexpress.c
+++ b/drivers/net/eexpress.c
@@ -1474,13 +1474,13 @@ static void eexp_hw_init586(struct net_device *dev)
outw(0x0000, ioaddr + 0x800c);
outw(0x0000, ioaddr + 0x800e);
- for (i = 0; i < (sizeof(start_code)); i+=32) {
+ for (i = 0; i < ARRAY_SIZE(start_code) * 2; i+=32) {
int j;
outw(i, ioaddr + SM_PTR);
- for (j = 0; j < 16; j+=2)
+ for (j = 0; j < 16 && (i+j)/2 < ARRAY_SIZE(start_code); j+=2)
outw(start_code[(i+j)/2],
ioaddr+0x4000+j);
- for (j = 0; j < 16; j+=2)
+ for (j = 0; j < 16 && (i+j+16)/2 < ARRAY_SIZE(start_code); j+=2)
outw(start_code[(i+j+16)/2],
ioaddr+0x8000+j);
}
diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h
index 78952f8324e..fa311a95099 100644
--- a/drivers/net/ehea/ehea.h
+++ b/drivers/net/ehea/ehea.h
@@ -40,7 +40,7 @@
#include <asm/io.h>
#define DRV_NAME "ehea"
-#define DRV_VERSION "EHEA_0101"
+#define DRV_VERSION "EHEA_0102"
/* eHEA capability flags */
#define DLPAR_PORT_ADD_REM 1
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index e8d46cc1bec..977c3d35827 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -1545,6 +1545,9 @@ static int ehea_clean_portres(struct ehea_port *port, struct ehea_port_res *pr)
{
int ret, i;
+ if (pr->qp)
+ netif_napi_del(&pr->napi);
+
ret = ehea_destroy_qp(pr->qp);
if (!ret) {
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index d4b98074b1b..c9fd82d3a80 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -285,6 +285,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
struct bufdesc *bdp;
+ void *bufaddr;
unsigned short status;
unsigned long flags;
@@ -312,7 +313,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
status &= ~BD_ENET_TX_STATS;
/* Set buffer length and buffer pointer */
- bdp->cbd_bufaddr = __pa(skb->data);
+ bufaddr = skb->data;
bdp->cbd_datlen = skb->len;
/*
@@ -320,11 +321,11 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
* 4-byte boundaries. Use bounce buffers to copy data
* and get it aligned. Ugh.
*/
- if (bdp->cbd_bufaddr & FEC_ALIGNMENT) {
+ if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
unsigned int index;
index = bdp - fep->tx_bd_base;
memcpy(fep->tx_bounce[index], (void *)skb->data, skb->len);
- bdp->cbd_bufaddr = __pa(fep->tx_bounce[index]);
+ bufaddr = fep->tx_bounce[index];
}
/* Save skb pointer */
@@ -336,7 +337,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Push the data cache so the CPM does not get stale memory
* data.
*/
- bdp->cbd_bufaddr = dma_map_single(&dev->dev, skb->data,
+ bdp->cbd_bufaddr = dma_map_single(&dev->dev, bufaddr,
FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
/* Send it on its way. Tell FEC it's ready, interrupt when done,
diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c
index cc786333d95..c40113f5896 100644
--- a/drivers/net/fec_mpc52xx.c
+++ b/drivers/net/fec_mpc52xx.c
@@ -309,6 +309,7 @@ static int mpc52xx_fec_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct mpc52xx_fec_priv *priv = netdev_priv(dev);
struct bcom_fec_bd *bd;
+ unsigned long flags;
if (bcom_queue_full(priv->tx_dmatsk)) {
if (net_ratelimit())
@@ -316,7 +317,7 @@ static int mpc52xx_fec_start_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_BUSY;
}
- spin_lock_irq(&priv->lock);
+ spin_lock_irqsave(&priv->lock, flags);
dev->trans_start = jiffies;
bd = (struct bcom_fec_bd *)
@@ -332,7 +333,7 @@ static int mpc52xx_fec_start_xmit(struct sk_buff *skb, struct net_device *dev)
netif_stop_queue(dev);
}
- spin_unlock_irq(&priv->lock);
+ spin_unlock_irqrestore(&priv->lock, flags);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index f8ffcbf0bc3..a00ec639c38 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -491,6 +491,7 @@ static int gfar_remove(struct of_device *ofdev)
dev_set_drvdata(&ofdev->dev, NULL);
+ unregister_netdev(priv->ndev);
iounmap(priv->regs);
free_netdev(priv->ndev);
@@ -936,6 +937,7 @@ int startup_gfar(struct net_device *dev)
struct gfar __iomem *regs = priv->regs;
int err = 0;
u32 rctrl = 0;
+ u32 tctrl = 0;
u32 attrs = 0;
gfar_write(&regs->imask, IMASK_INIT_CLEAR);
@@ -1111,11 +1113,19 @@ int startup_gfar(struct net_device *dev)
rctrl |= RCTRL_PADDING(priv->padding);
}
+ /* keep vlan related bits if it's enabled */
+ if (priv->vlgrp) {
+ rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT;
+ tctrl |= TCTRL_VLINS;
+ }
+
/* Init rctrl based on our settings */
gfar_write(&priv->regs->rctrl, rctrl);
if (dev->features & NETIF_F_IP_CSUM)
- gfar_write(&priv->regs->tctrl, TCTRL_INIT_CSUM);
+ tctrl |= TCTRL_INIT_CSUM;
+
+ gfar_write(&priv->regs->tctrl, tctrl);
/* Set the extraction length and index */
attrs = ATTRELI_EL(priv->rx_stash_size) |
@@ -1450,7 +1460,6 @@ static void gfar_vlan_rx_register(struct net_device *dev,
/* Enable VLAN tag extraction */
tempval = gfar_read(&priv->regs->rctrl);
- tempval |= RCTRL_VLEX;
tempval |= (RCTRL_VLEX | RCTRL_PRSDEP_INIT);
gfar_write(&priv->regs->rctrl, tempval);
} else {
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index dbf06e9313c..2234118eedb 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -366,9 +366,8 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
return -EINVAL;
}
- priv->rxic = mk_ic_value(
- gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs),
- cvals->rx_max_coalesced_frames);
+ priv->rxic = mk_ic_value(cvals->rx_max_coalesced_frames,
+ gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs));
/* Set up tx coalescing */
if ((cvals->tx_coalesce_usecs == 0) ||
@@ -390,9 +389,8 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
return -EINVAL;
}
- priv->txic = mk_ic_value(
- gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs),
- cvals->tx_max_coalesced_frames);
+ priv->txic = mk_ic_value(cvals->tx_max_coalesced_frames,
+ gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs));
gfar_write(&priv->regs->rxic, 0);
if (priv->rxcoalescing)
diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c
index beb84213b67..f0f89080371 100644
--- a/drivers/net/ibm_newemac/core.c
+++ b/drivers/net/ibm_newemac/core.c
@@ -1305,6 +1305,8 @@ static int emac_close(struct net_device *ndev)
free_irq(dev->emac_irq, dev);
+ netif_carrier_off(ndev);
+
return 0;
}
diff --git a/drivers/net/igbvf/vf.c b/drivers/net/igbvf/vf.c
index 2a4faf9ade6..a9a61efa964 100644
--- a/drivers/net/igbvf/vf.c
+++ b/drivers/net/igbvf/vf.c
@@ -274,6 +274,8 @@ static s32 e1000_set_vfta_vf(struct e1000_hw *hw, u16 vid, bool set)
err = mbx->ops.read_posted(hw, msgbuf, 2);
+ msgbuf[0] &= ~E1000_VT_MSGTYPE_CTS;
+
/* if nacked the vlan was rejected */
if (!err && (msgbuf[0] == (E1000_VF_SET_VLAN | E1000_VT_MSGTYPE_NACK)))
err = -E1000_ERR_MAC_INIT;
@@ -317,6 +319,8 @@ static void e1000_rar_set_vf(struct e1000_hw *hw, u8 * addr, u32 index)
if (!ret_val)
ret_val = mbx->ops.read_posted(hw, msgbuf, 3);
+ msgbuf[0] &= ~E1000_VT_MSGTYPE_CTS;
+
/* if nacked the address was rejected, use "perm_addr" */
if (!ret_val &&
(msgbuf[0] == (E1000_VF_SET_MAC_ADDR | E1000_VT_MSGTYPE_NACK)))
diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c
index c4361d46659..ee1cff5c9b2 100644
--- a/drivers/net/irda/au1k_ir.c
+++ b/drivers/net/irda/au1k_ir.c
@@ -23,7 +23,6 @@
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
#include <linux/slab.h>
#include <linux/rtnetlink.h>
#include <linux/interrupt.h>
@@ -205,9 +204,6 @@ static const struct net_device_ops au1k_irda_netdev_ops = {
.ndo_start_xmit = au1k_irda_hard_xmit,
.ndo_tx_timeout = au1k_tx_timeout,
.ndo_do_ioctl = au1k_irda_ioctl,
- .ndo_change_mtu = eth_change_mtu,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_set_mac_address = eth_mac_addr,
};
static int au1k_irda_net_init(struct net_device *dev)
diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c
index 3376a4f39e0..77d10edefd2 100644
--- a/drivers/net/irda/pxaficp_ir.c
+++ b/drivers/net/irda/pxaficp_ir.c
@@ -803,9 +803,6 @@ static const struct net_device_ops pxa_irda_netdev_ops = {
.ndo_stop = pxa_irda_stop,
.ndo_start_xmit = pxa_irda_hard_xmit,
.ndo_do_ioctl = pxa_irda_ioctl,
- .ndo_change_mtu = eth_change_mtu,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_set_mac_address = eth_mac_addr,
};
static int pxa_irda_probe(struct platform_device *pdev)
@@ -830,6 +827,7 @@ static int pxa_irda_probe(struct platform_device *pdev)
if (!dev)
goto err_mem_3;
+ SET_NETDEV_DEV(dev, &pdev->dev);
si = netdev_priv(dev);
si->dev = &pdev->dev;
si->pdata = pdev->dev.platform_data;
diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c
index 2aeb2e6aec1..b039cb081e9 100644
--- a/drivers/net/irda/sa1100_ir.c
+++ b/drivers/net/irda/sa1100_ir.c
@@ -24,7 +24,6 @@
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
#include <linux/slab.h>
#include <linux/rtnetlink.h>
#include <linux/interrupt.h>
@@ -881,9 +880,6 @@ static const struct net_device_ops sa1100_irda_netdev_ops = {
.ndo_stop = sa1100_irda_stop,
.ndo_start_xmit = sa1100_irda_hard_xmit,
.ndo_do_ioctl = sa1100_irda_ioctl,
- .ndo_change_mtu = eth_change_mtu,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_set_mac_address = eth_mac_addr,
};
static int sa1100_irda_probe(struct platform_device *pdev)
diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c
index d0883835b0c..fe4f2b2bff9 100644
--- a/drivers/net/irda/w83977af_ir.c
+++ b/drivers/net/irda/w83977af_ir.c
@@ -115,7 +115,7 @@ static int __init w83977af_init(void)
IRDA_DEBUG(0, "%s()\n", __func__ );
- for (i=0; (io[i] < 2000) && (i < ARRAY_SIZE(dev_self)); i++) {
+ for (i=0; i < ARRAY_SIZE(dev_self) && io[i] < 2000; i++) {
if (w83977af_open(i, io[i], irq[i], dma[i]) == 0)
return 0;
}
diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h
index 1b12c7ba275..2c4dc8221dc 100644
--- a/drivers/net/ixgbe/ixgbe.h
+++ b/drivers/net/ixgbe/ixgbe.h
@@ -96,6 +96,8 @@
#define IXGBE_TX_FLAGS_VLAN_PRIO_MASK 0x0000e000
#define IXGBE_TX_FLAGS_VLAN_SHIFT 16
+#define IXGBE_MAX_RSC_INT_RATE 162760
+
/* wrapper around a pointer to a socket buffer,
* so a DMA handle can be stored along with the buffer */
struct ixgbe_tx_buffer {
@@ -134,6 +136,8 @@ struct ixgbe_ring {
u8 queue_index; /* needed for multiqueue queue management */
+#define IXGBE_RING_RX_PS_ENABLED (u8)(1)
+ u8 flags; /* per ring feature flags */
u16 head;
u16 tail;
diff --git a/drivers/net/ixgbe/ixgbe_82598.c b/drivers/net/ixgbe/ixgbe_82598.c
index b9923047ce1..522c03bc1da 100644
--- a/drivers/net/ixgbe/ixgbe_82598.c
+++ b/drivers/net/ixgbe/ixgbe_82598.c
@@ -50,6 +50,51 @@ static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset,
u8 *eeprom_data);
/**
+ * ixgbe_set_pcie_completion_timeout - set pci-e completion timeout
+ * @hw: pointer to the HW structure
+ *
+ * The defaults for 82598 should be in the range of 50us to 50ms,
+ * however the hardware default for these parts is 500us to 1ms which is less
+ * than the 10ms recommended by the pci-e spec. To address this we need to
+ * increase the value to either 10ms to 250ms for capability version 1 config,
+ * or 16ms to 55ms for version 2.
+ **/
+void ixgbe_set_pcie_completion_timeout(struct ixgbe_hw *hw)
+{
+ struct ixgbe_adapter *adapter = hw->back;
+ u32 gcr = IXGBE_READ_REG(hw, IXGBE_GCR);
+ u16 pcie_devctl2;
+
+ /* only take action if timeout value is defaulted to 0 */
+ if (gcr & IXGBE_GCR_CMPL_TMOUT_MASK)
+ goto out;
+
+ /*
+ * if capababilities version is type 1 we can write the
+ * timeout of 10ms to 250ms through the GCR register
+ */
+ if (!(gcr & IXGBE_GCR_CAP_VER2)) {
+ gcr |= IXGBE_GCR_CMPL_TMOUT_10ms;
+ goto out;
+ }
+
+ /*
+ * for version 2 capabilities we need to write the config space
+ * directly in order to set the completion timeout value for
+ * 16ms to 55ms
+ */
+ pci_read_config_word(adapter->pdev,
+ IXGBE_PCI_DEVICE_CONTROL2, &pcie_devctl2);
+ pcie_devctl2 |= IXGBE_PCI_DEVICE_CONTROL2_16ms;
+ pci_write_config_word(adapter->pdev,
+ IXGBE_PCI_DEVICE_CONTROL2, pcie_devctl2);
+out:
+ /* disable completion timeout resend */
+ gcr &= ~IXGBE_GCR_CMPL_TMOUT_RESEND;
+ IXGBE_WRITE_REG(hw, IXGBE_GCR, gcr);
+}
+
+/**
* ixgbe_get_pcie_msix_count_82598 - Gets MSI-X vector count
* @hw: pointer to hardware structure
*
@@ -153,6 +198,26 @@ out:
}
/**
+ * ixgbe_start_hw_82598 - Prepare hardware for Tx/Rx
+ * @hw: pointer to hardware structure
+ *
+ * Starts the hardware using the generic start_hw function.
+ * Then set pcie completion timeout
+ **/
+s32 ixgbe_start_hw_82598(struct ixgbe_hw *hw)
+{
+ s32 ret_val = 0;
+
+ ret_val = ixgbe_start_hw_generic(hw);
+
+ /* set the completion timeout for interface */
+ if (ret_val == 0)
+ ixgbe_set_pcie_completion_timeout(hw);
+
+ return ret_val;
+}
+
+/**
* ixgbe_get_link_capabilities_82598 - Determines link capabilities
* @hw: pointer to hardware structure
* @speed: pointer to link speed
@@ -1085,7 +1150,7 @@ out:
static struct ixgbe_mac_operations mac_ops_82598 = {
.init_hw = &ixgbe_init_hw_generic,
.reset_hw = &ixgbe_reset_hw_82598,
- .start_hw = &ixgbe_start_hw_generic,
+ .start_hw = &ixgbe_start_hw_82598,
.clear_hw_cntrs = &ixgbe_clear_hw_cntrs_generic,
.get_media_type = &ixgbe_get_media_type_82598,
.get_supported_physical_layer = &ixgbe_get_supported_physical_layer_82598,
diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c
index 2a978008fd6..dff8dfac7ed 100644
--- a/drivers/net/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ixgbe/ixgbe_ethtool.c
@@ -1948,6 +1948,7 @@ static int ixgbe_set_coalesce(struct net_device *netdev,
struct ethtool_coalesce *ec)
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_q_vector *q_vector;
int i;
if (ec->tx_max_coalesced_frames_irq)
@@ -1975,18 +1976,31 @@ static int ixgbe_set_coalesce(struct net_device *netdev,
* any other value means disable eitr, which is best
* served by setting the interrupt rate very high
*/
- adapter->eitr_param = IXGBE_MAX_INT_RATE;
+ if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED)
+ adapter->eitr_param = IXGBE_MAX_RSC_INT_RATE;
+ else
+ adapter->eitr_param = IXGBE_MAX_INT_RATE;
adapter->itr_setting = 0;
}
- for (i = 0; i < adapter->num_msix_vectors - NON_Q_VECTORS; i++) {
- struct ixgbe_q_vector *q_vector = adapter->q_vector[i];
- if (q_vector->txr_count && !q_vector->rxr_count)
- /* tx vector gets half the rate */
- q_vector->eitr = (adapter->eitr_param >> 1);
- else
- /* rx only or mixed */
- q_vector->eitr = adapter->eitr_param;
+ /* MSI/MSIx Interrupt Mode */
+ if (adapter->flags &
+ (IXGBE_FLAG_MSIX_ENABLED | IXGBE_FLAG_MSI_ENABLED)) {
+ int num_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
+ for (i = 0; i < num_vectors; i++) {
+ q_vector = adapter->q_vector[i];
+ if (q_vector->txr_count && !q_vector->rxr_count)
+ /* tx vector gets half the rate */
+ q_vector->eitr = (adapter->eitr_param >> 1);
+ else
+ /* rx only or mixed */
+ q_vector->eitr = adapter->eitr_param;
+ ixgbe_write_eitr(q_vector);
+ }
+ /* Legacy Interrupt Mode */
+ } else {
+ q_vector = adapter->q_vector[0];
+ q_vector->eitr = adapter->eitr_param;
ixgbe_write_eitr(q_vector);
}
@@ -1999,13 +2013,13 @@ static int ixgbe_set_flags(struct net_device *netdev, u32 data)
ethtool_op_set_flags(netdev, data);
- if (!(adapter->flags & IXGBE_FLAG2_RSC_CAPABLE))
+ if (!(adapter->flags2 & IXGBE_FLAG2_RSC_CAPABLE))
return 0;
/* if state changes we need to update adapter->flags and reset */
if ((!!(data & ETH_FLAG_LRO)) !=
- (!!(adapter->flags & IXGBE_FLAG2_RSC_ENABLED))) {
- adapter->flags ^= IXGBE_FLAG2_RSC_ENABLED;
+ (!!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED))) {
+ adapter->flags2 ^= IXGBE_FLAG2_RSC_ENABLED;
if (netif_running(netdev))
ixgbe_reinit_locked(adapter);
else
diff --git a/drivers/net/ixgbe/ixgbe_fcoe.c b/drivers/net/ixgbe/ixgbe_fcoe.c
index fa9f24e2368..28cf104e36c 100644
--- a/drivers/net/ixgbe/ixgbe_fcoe.c
+++ b/drivers/net/ixgbe/ixgbe_fcoe.c
@@ -336,7 +336,7 @@ int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
/* return 0 to bypass going to ULD for DDPed data */
if (fcstat == IXGBE_RXDADV_STAT_FCSTAT_DDP)
rc = 0;
- else
+ else if (ddp->len)
rc = ddp->len;
}
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
index 200454f30f6..77b0381a2b5 100644
--- a/drivers/net/ixgbe/ixgbe_main.c
+++ b/drivers/net/ixgbe/ixgbe_main.c
@@ -492,12 +492,12 @@ static void ixgbe_receive_skb(struct ixgbe_q_vector *q_vector,
skb_record_rx_queue(skb, ring->queue_index);
if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) {
- if (adapter->vlgrp && is_vlan && (tag != 0))
+ if (adapter->vlgrp && is_vlan && (tag & VLAN_VID_MASK))
vlan_gro_receive(napi, adapter->vlgrp, tag, skb);
else
napi_gro_receive(napi, skb);
} else {
- if (adapter->vlgrp && is_vlan && (tag != 0))
+ if (adapter->vlgrp && is_vlan && (tag & VLAN_VID_MASK))
vlan_hwaccel_rx(skb, adapter->vlgrp, tag);
else
netif_rx(skb);
@@ -585,7 +585,7 @@ static void ixgbe_alloc_rx_buffers(struct ixgbe_adapter *adapter,
rx_desc = IXGBE_RX_DESC_ADV(*rx_ring, i);
if (!bi->page_dma &&
- (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED)) {
+ (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED)) {
if (!bi->page) {
bi->page = alloc_page(GFP_ATOMIC);
if (!bi->page) {
@@ -629,7 +629,7 @@ static void ixgbe_alloc_rx_buffers(struct ixgbe_adapter *adapter,
}
/* Refresh the desc even if buffer_addrs didn't change because
* each write-back erases this info. */
- if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+ if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) {
rx_desc->read.pkt_addr = cpu_to_le64(bi->page_dma);
rx_desc->read.hdr_addr = cpu_to_le64(bi->dma);
} else {
@@ -726,7 +726,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
break;
(*work_done)++;
- if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+ if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) {
hdr_info = le16_to_cpu(ixgbe_get_hdr_info(rx_desc));
len = (hdr_info & IXGBE_RXDADV_HDRBUFLEN_MASK) >>
IXGBE_RXDADV_HDRBUFLEN_SHIFT;
@@ -780,7 +780,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
prefetch(next_rxd);
cleaned_count++;
- if (adapter->flags & IXGBE_FLAG2_RSC_CAPABLE)
+ if (adapter->flags2 & IXGBE_FLAG2_RSC_CAPABLE)
rsc_count = ixgbe_get_rsc_count(rx_desc);
if (rsc_count) {
@@ -798,7 +798,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
rx_ring->stats.packets++;
rx_ring->stats.bytes += skb->len;
} else {
- if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+ if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) {
rx_buffer_info->skb = next_buffer->skb;
rx_buffer_info->dma = next_buffer->dma;
next_buffer->skb = skb;
@@ -1898,46 +1898,19 @@ static void ixgbe_configure_tx(struct ixgbe_adapter *adapter)
#define IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT 2
-static void ixgbe_configure_srrctl(struct ixgbe_adapter *adapter, int index)
+static void ixgbe_configure_srrctl(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *rx_ring)
{
- struct ixgbe_ring *rx_ring;
u32 srrctl;
- int queue0 = 0;
- unsigned long mask;
+ int index;
struct ixgbe_ring_feature *feature = adapter->ring_feature;
- if (adapter->hw.mac.type == ixgbe_mac_82599EB) {
- if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
- int dcb_i = feature[RING_F_DCB].indices;
- if (dcb_i == 8)
- queue0 = index >> 4;
- else if (dcb_i == 4)
- queue0 = index >> 5;
- else
- dev_err(&adapter->pdev->dev, "Invalid DCB "
- "configuration\n");
-#ifdef IXGBE_FCOE
- if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED) {
- struct ixgbe_ring_feature *f;
-
- rx_ring = &adapter->rx_ring[queue0];
- f = &adapter->ring_feature[RING_F_FCOE];
- if ((queue0 == 0) && (index > rx_ring->reg_idx))
- queue0 = f->mask + index -
- rx_ring->reg_idx - 1;
- }
-#endif /* IXGBE_FCOE */
- } else {
- queue0 = index;
- }
- } else {
+ index = rx_ring->reg_idx;
+ if (adapter->hw.mac.type == ixgbe_mac_82598EB) {
+ unsigned long mask;
mask = (unsigned long) feature[RING_F_RSS].mask;
- queue0 = index & mask;
index = index & mask;
}
-
- rx_ring = &adapter->rx_ring[queue0];
-
srrctl = IXGBE_READ_REG(&adapter->hw, IXGBE_SRRCTL(index));
srrctl &= ~IXGBE_SRRCTL_BSIZEHDR_MASK;
@@ -1946,7 +1919,7 @@ static void ixgbe_configure_srrctl(struct ixgbe_adapter *adapter, int index)
srrctl |= (IXGBE_RX_HDR_SIZE << IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT) &
IXGBE_SRRCTL_BSIZEHDR_MASK;
- if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+ if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) {
#if (PAGE_SIZE / 2) > IXGBE_MAX_RXBUFFER
srrctl |= IXGBE_MAX_RXBUFFER >> IXGBE_SRRCTL_BSIZEPKT_SHIFT;
#else
@@ -2002,6 +1975,7 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter)
{
u64 rdba;
struct ixgbe_hw *hw = &adapter->hw;
+ struct ixgbe_ring *rx_ring;
struct net_device *netdev = adapter->netdev;
int max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN;
int i, j;
@@ -2018,11 +1992,6 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter)
/* Decide whether to use packet split mode or not */
adapter->flags |= IXGBE_FLAG_RX_PS_ENABLED;
-#ifdef IXGBE_FCOE
- if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED)
- adapter->flags &= ~IXGBE_FLAG_RX_PS_ENABLED;
-#endif /* IXGBE_FCOE */
-
/* Set the RX buffer length according to the mode */
if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
rx_buf_len = IXGBE_RX_HDR_SIZE;
@@ -2036,7 +2005,7 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter)
IXGBE_WRITE_REG(hw, IXGBE_PSRTYPE(0), psrtype);
}
} else {
- if (!(adapter->flags & IXGBE_FLAG2_RSC_ENABLED) &&
+ if (!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) &&
(netdev->mtu <= ETH_DATA_LEN))
rx_buf_len = MAXIMUM_ETHERNET_VLAN_SIZE;
else
@@ -2070,29 +2039,35 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter)
* the Base and Length of the Rx Descriptor Ring
*/
for (i = 0; i < adapter->num_rx_queues; i++) {
- rdba = adapter->rx_ring[i].dma;
- j = adapter->rx_ring[i].reg_idx;
+ rx_ring = &adapter->rx_ring[i];
+ rdba = rx_ring->dma;
+ j = rx_ring->reg_idx;
IXGBE_WRITE_REG(hw, IXGBE_RDBAL(j), (rdba & DMA_BIT_MASK(32)));
IXGBE_WRITE_REG(hw, IXGBE_RDBAH(j), (rdba >> 32));
IXGBE_WRITE_REG(hw, IXGBE_RDLEN(j), rdlen);
IXGBE_WRITE_REG(hw, IXGBE_RDH(j), 0);
IXGBE_WRITE_REG(hw, IXGBE_RDT(j), 0);
- adapter->rx_ring[i].head = IXGBE_RDH(j);
- adapter->rx_ring[i].tail = IXGBE_RDT(j);
- adapter->rx_ring[i].rx_buf_len = rx_buf_len;
+ rx_ring->head = IXGBE_RDH(j);
+ rx_ring->tail = IXGBE_RDT(j);
+ rx_ring->rx_buf_len = rx_buf_len;
+
+ if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED)
+ rx_ring->flags |= IXGBE_RING_RX_PS_ENABLED;
#ifdef IXGBE_FCOE
if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED) {
struct ixgbe_ring_feature *f;
f = &adapter->ring_feature[RING_F_FCOE];
- if ((rx_buf_len < IXGBE_FCOE_JUMBO_FRAME_SIZE) &&
- (i >= f->mask) && (i < f->mask + f->indices))
- adapter->rx_ring[i].rx_buf_len =
- IXGBE_FCOE_JUMBO_FRAME_SIZE;
+ if ((i >= f->mask) && (i < f->mask + f->indices)) {
+ rx_ring->flags &= ~IXGBE_RING_RX_PS_ENABLED;
+ if (rx_buf_len < IXGBE_FCOE_JUMBO_FRAME_SIZE)
+ rx_ring->rx_buf_len =
+ IXGBE_FCOE_JUMBO_FRAME_SIZE;
+ }
}
#endif /* IXGBE_FCOE */
- ixgbe_configure_srrctl(adapter, j);
+ ixgbe_configure_srrctl(adapter, rx_ring);
}
if (hw->mac.type == ixgbe_mac_82598EB) {
@@ -2165,10 +2140,11 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter)
IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, rdrxctl);
}
- if (adapter->flags & IXGBE_FLAG2_RSC_ENABLED) {
+ if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) {
/* Enable 82599 HW-RSC */
for (i = 0; i < adapter->num_rx_queues; i++) {
- j = adapter->rx_ring[i].reg_idx;
+ rx_ring = &adapter->rx_ring[i];
+ j = rx_ring->reg_idx;
rscctrl = IXGBE_READ_REG(hw, IXGBE_RSCCTL(j));
rscctrl |= IXGBE_RSCCTL_RSCEN;
/*
@@ -2176,7 +2152,7 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter)
* total size of max desc * buf_len is not greater
* than 65535
*/
- if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+ if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) {
#if (MAX_SKB_FRAGS > 16)
rscctrl |= IXGBE_RSCCTL_MAXDESC_16;
#elif (MAX_SKB_FRAGS > 8)
@@ -3812,8 +3788,8 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter)
adapter->max_msix_q_vectors = MAX_MSIX_Q_VECTORS_82598;
} else if (hw->mac.type == ixgbe_mac_82599EB) {
adapter->max_msix_q_vectors = MAX_MSIX_Q_VECTORS_82599;
- adapter->flags |= IXGBE_FLAG2_RSC_CAPABLE;
- adapter->flags |= IXGBE_FLAG2_RSC_ENABLED;
+ adapter->flags2 |= IXGBE_FLAG2_RSC_CAPABLE;
+ adapter->flags2 |= IXGBE_FLAG2_RSC_ENABLED;
adapter->flags |= IXGBE_FLAG_FDIR_HASH_CAPABLE;
adapter->ring_feature[RING_F_FDIR].indices =
IXGBE_MAX_FDIR_INDICES;
@@ -5360,12 +5336,19 @@ static int ixgbe_del_sanmac_netdev(struct net_device *dev)
static void ixgbe_netpoll(struct net_device *netdev)
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ int i;
- disable_irq(adapter->pdev->irq);
adapter->flags |= IXGBE_FLAG_IN_NETPOLL;
- ixgbe_intr(adapter->pdev->irq, netdev);
+ if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
+ int num_q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
+ for (i = 0; i < num_q_vectors; i++) {
+ struct ixgbe_q_vector *q_vector = adapter->q_vector[i];
+ ixgbe_msix_clean_many(0, q_vector);
+ }
+ } else {
+ ixgbe_intr(adapter->pdev->irq, netdev);
+ }
adapter->flags &= ~IXGBE_FLAG_IN_NETPOLL;
- enable_irq(adapter->pdev->irq);
}
#endif
@@ -5611,7 +5594,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
if (pci_using_dac)
netdev->features |= NETIF_F_HIGHDMA;
- if (adapter->flags & IXGBE_FLAG2_RSC_ENABLED)
+ if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED)
netdev->features |= NETIF_F_LRO;
/* make sure the EEPROM is good */
diff --git a/drivers/net/ixgbe/ixgbe_type.h b/drivers/net/ixgbe/ixgbe_type.h
index fa87309dc08..be90eb4575f 100644
--- a/drivers/net/ixgbe/ixgbe_type.h
+++ b/drivers/net/ixgbe/ixgbe_type.h
@@ -718,6 +718,12 @@
#define IXGBE_ECC_STATUS_82599 0x110E0
#define IXGBE_BAR_CTRL_82599 0x110F4
+/* PCI Express Control */
+#define IXGBE_GCR_CMPL_TMOUT_MASK 0x0000F000
+#define IXGBE_GCR_CMPL_TMOUT_10ms 0x00001000
+#define IXGBE_GCR_CMPL_TMOUT_RESEND 0x00010000
+#define IXGBE_GCR_CAP_VER2 0x00040000
+
/* Time Sync Registers */
#define IXGBE_TSYNCRXCTL 0x05188 /* Rx Time Sync Control register - RW */
#define IXGBE_TSYNCTXCTL 0x08C00 /* Tx Time Sync Control register - RW */
@@ -1521,6 +1527,7 @@
/* PCI Bus Info */
#define IXGBE_PCI_LINK_STATUS 0xB2
+#define IXGBE_PCI_DEVICE_CONTROL2 0xC8
#define IXGBE_PCI_LINK_WIDTH 0x3F0
#define IXGBE_PCI_LINK_WIDTH_1 0x10
#define IXGBE_PCI_LINK_WIDTH_2 0x20
@@ -1531,6 +1538,7 @@
#define IXGBE_PCI_LINK_SPEED_5000 0x2
#define IXGBE_PCI_HEADER_TYPE_REGISTER 0x0E
#define IXGBE_PCI_HEADER_TYPE_MULTIFUNC 0x80
+#define IXGBE_PCI_DEVICE_CONTROL2_16ms 0x0005
/* Number of 100 microseconds we wait for PCI Express master disable */
#define IXGBE_PCI_MASTER_DISABLE_TIMEOUT 800
diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c
index 2a0174b62e9..92fb8235c76 100644
--- a/drivers/net/ixp2000/ixpdev.c
+++ b/drivers/net/ixp2000/ixpdev.c
@@ -41,6 +41,7 @@ static int ixpdev_xmit(struct sk_buff *skb, struct net_device *dev)
struct ixpdev_priv *ip = netdev_priv(dev);
struct ixpdev_tx_desc *desc;
int entry;
+ unsigned long flags;
if (unlikely(skb->len > PAGE_SIZE)) {
/* @@@ Count drops. */
@@ -63,11 +64,11 @@ static int ixpdev_xmit(struct sk_buff *skb, struct net_device *dev)
dev->trans_start = jiffies;
- local_irq_disable();
+ local_irq_save(flags);
ip->tx_queue_entries++;
if (ip->tx_queue_entries == TX_BUF_COUNT_PER_CHAN)
netif_stop_queue(dev);
- local_irq_enable();
+ local_irq_restore(flags);
return 0;
}
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index 5b5c25368d1..e3601cf3f93 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -620,6 +620,7 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
dma_addr_t mapping;
unsigned int len, entry;
u32 ctrl;
+ unsigned long flags;
#ifdef DEBUG
int i;
@@ -635,12 +636,12 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
#endif
len = skb->len;
- spin_lock_irq(&bp->lock);
+ spin_lock_irqsave(&bp->lock, flags);
/* This is a hard error, log it. */
if (TX_BUFFS_AVAIL(bp) < 1) {
netif_stop_queue(dev);
- spin_unlock_irq(&bp->lock);
+ spin_unlock_irqrestore(&bp->lock, flags);
dev_err(&bp->pdev->dev,
"BUG! Tx Ring full when queue awake!\n");
dev_dbg(&bp->pdev->dev, "tx_head = %u, tx_tail = %u\n",
@@ -674,7 +675,7 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (TX_BUFFS_AVAIL(bp) < 1)
netif_stop_queue(dev);
- spin_unlock_irq(&bp->lock);
+ spin_unlock_irqrestore(&bp->lock, flags);
dev->trans_start = jiffies;
diff --git a/drivers/net/mlx4/en_rx.c b/drivers/net/mlx4/en_rx.c
index 91bdfdfd431..3ac0404d0d1 100644
--- a/drivers/net/mlx4/en_rx.c
+++ b/drivers/net/mlx4/en_rx.c
@@ -506,8 +506,9 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv,
PCI_DMA_FROMDEVICE);
}
/* Adjust size of last fragment to match actual length */
- skb_frags_rx[nr - 1].size = length -
- priv->frag_info[nr - 1].frag_prefix_size;
+ if (nr > 0)
+ skb_frags_rx[nr - 1].size = length -
+ priv->frag_info[nr - 1].frag_prefix_size;
return nr;
fail:
diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c
index 08c43f2ae72..62208401c4d 100644
--- a/drivers/net/mlx4/en_tx.c
+++ b/drivers/net/mlx4/en_tx.c
@@ -249,6 +249,7 @@ static u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv,
pci_unmap_page(mdev->pdev,
(dma_addr_t) be64_to_cpu(data->addr),
frag->size, PCI_DMA_TODEVICE);
+ ++data;
}
}
/* Stamp the freed descriptor */
@@ -436,6 +437,7 @@ static inline void mlx4_en_xmit_poll(struct mlx4_en_priv *priv, int tx_ind)
{
struct mlx4_en_cq *cq = &priv->tx_cq[tx_ind];
struct mlx4_en_tx_ring *ring = &priv->tx_ring[tx_ind];
+ unsigned long flags;
/* If we don't have a pending timer, set one up to catch our recent
post in case the interface becomes idle */
@@ -444,9 +446,9 @@ static inline void mlx4_en_xmit_poll(struct mlx4_en_priv *priv, int tx_ind)
/* Poll the CQ every mlx4_en_TX_MODER_POLL packets */
if ((++ring->poll_cnt & (MLX4_EN_TX_POLL_MODER - 1)) == 0)
- if (spin_trylock_irq(&ring->comp_lock)) {
+ if (spin_trylock_irqsave(&ring->comp_lock, flags)) {
mlx4_en_process_tx_cq(priv->dev, cq);
- spin_unlock_irq(&ring->comp_lock);
+ spin_unlock_irqrestore(&ring->comp_lock, flags);
}
}
diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h
index f86e05047d1..a9c1fcca5e7 100644
--- a/drivers/net/netxen/netxen_nic.h
+++ b/drivers/net/netxen/netxen_nic.h
@@ -1254,7 +1254,7 @@ struct netxen_adapter {
u8 mc_enabled;
u8 max_mc_count;
u8 rss_supported;
- u8 resv2;
+ u8 link_changed;
u32 resv3;
u8 has_link_events;
diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c
index 7acf204e38c..5d3343ef3d8 100644
--- a/drivers/net/netxen/netxen_nic_init.c
+++ b/drivers/net/netxen/netxen_nic_init.c
@@ -184,13 +184,6 @@ void netxen_free_sw_resources(struct netxen_adapter *adapter)
kfree(recv_ctx->rds_rings);
skip_rds:
- if (recv_ctx->sds_rings == NULL)
- goto skip_sds;
-
- for(ring = 0; ring < adapter->max_sds_rings; ring++)
- recv_ctx->sds_rings[ring].consumer = 0;
-
-skip_sds:
if (adapter->tx_ring == NULL)
return;
diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c
index 637ac8b89ba..28f270f5ac7 100644
--- a/drivers/net/netxen/netxen_nic_main.c
+++ b/drivers/net/netxen/netxen_nic_main.c
@@ -94,10 +94,6 @@ static struct pci_device_id netxen_pci_tbl[] __devinitdata = {
MODULE_DEVICE_TABLE(pci, netxen_pci_tbl);
-static struct workqueue_struct *netxen_workq;
-#define SCHEDULE_WORK(tp) queue_work(netxen_workq, tp)
-#define FLUSH_SCHEDULED_WORK() flush_workqueue(netxen_workq)
-
static void netxen_watchdog(unsigned long);
static uint32_t crb_cmd_producer[4] = {
@@ -171,6 +167,8 @@ netxen_free_sds_rings(struct netxen_recv_context *recv_ctx)
{
if (recv_ctx->sds_rings != NULL)
kfree(recv_ctx->sds_rings);
+
+ recv_ctx->sds_rings = NULL;
}
static int
@@ -193,6 +191,21 @@ netxen_napi_add(struct netxen_adapter *adapter, struct net_device *netdev)
}
static void
+netxen_napi_del(struct netxen_adapter *adapter)
+{
+ int ring;
+ struct nx_host_sds_ring *sds_ring;
+ struct netxen_recv_context *recv_ctx = &adapter->recv_ctx;
+
+ for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ sds_ring = &recv_ctx->sds_rings[ring];
+ netif_napi_del(&sds_ring->napi);
+ }
+
+ netxen_free_sds_rings(&adapter->recv_ctx);
+}
+
+static void
netxen_napi_enable(struct netxen_adapter *adapter)
{
int ring;
@@ -221,7 +234,7 @@ netxen_napi_disable(struct netxen_adapter *adapter)
}
}
-static int nx_set_dma_mask(struct netxen_adapter *adapter, uint8_t revision_id)
+static int nx_set_dma_mask(struct netxen_adapter *adapter)
{
struct pci_dev *pdev = adapter->pdev;
uint64_t mask, cmask;
@@ -229,19 +242,17 @@ static int nx_set_dma_mask(struct netxen_adapter *adapter, uint8_t revision_id)
adapter->pci_using_dac = 0;
mask = DMA_BIT_MASK(32);
- /*
- * Consistent DMA mask is set to 32 bit because it cannot be set to
- * 35 bits. For P3 also leave it at 32 bits for now. Only the rings
- * come off this pool.
- */
cmask = DMA_BIT_MASK(32);
+ if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
#ifndef CONFIG_IA64
- if (revision_id >= NX_P3_B0)
- mask = DMA_BIT_MASK(39);
- else if (revision_id == NX_P2_C1)
mask = DMA_BIT_MASK(35);
#endif
+ } else {
+ mask = DMA_BIT_MASK(39);
+ cmask = mask;
+ }
+
if (pci_set_dma_mask(pdev, mask) == 0 &&
pci_set_consistent_dma_mask(pdev, cmask) == 0) {
adapter->pci_using_dac = 1;
@@ -256,13 +267,13 @@ static int
nx_update_dma_mask(struct netxen_adapter *adapter)
{
int change, shift, err;
- uint64_t mask, old_mask;
+ uint64_t mask, old_mask, old_cmask;
struct pci_dev *pdev = adapter->pdev;
change = 0;
shift = NXRD32(adapter, CRB_DMA_SHIFT);
- if (shift >= 32)
+ if (shift > 32)
return 0;
if (NX_IS_REVISION_P3(adapter->ahw.revision_id) && (shift > 9))
@@ -272,14 +283,29 @@ nx_update_dma_mask(struct netxen_adapter *adapter)
if (change) {
old_mask = pdev->dma_mask;
- mask = (1ULL<<(32+shift)) - 1;
+ old_cmask = pdev->dev.coherent_dma_mask;
+
+ mask = DMA_BIT_MASK(32+shift);
err = pci_set_dma_mask(pdev, mask);
if (err)
- return pci_set_dma_mask(pdev, old_mask);
+ goto err_out;
+
+ if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) {
+
+ err = pci_set_consistent_dma_mask(pdev, mask);
+ if (err)
+ goto err_out;
+ }
+ dev_info(&pdev->dev, "using %d-bit dma mask\n", 32+shift);
}
return 0;
+
+err_out:
+ pci_set_dma_mask(pdev, old_mask);
+ pci_set_consistent_dma_mask(pdev, old_cmask);
+ return err;
}
static void netxen_check_options(struct netxen_adapter *adapter)
@@ -867,7 +893,6 @@ netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev)
spin_unlock(&adapter->tx_clean_lock);
del_timer_sync(&adapter->watchdog_timer);
- FLUSH_SCHEDULED_WORK();
}
@@ -881,10 +906,12 @@ netxen_nic_attach(struct netxen_adapter *adapter)
struct nx_host_tx_ring *tx_ring;
err = netxen_init_firmware(adapter);
- if (err != 0) {
- printk(KERN_ERR "Failed to init firmware\n");
- return -EIO;
- }
+ if (err)
+ return err;
+
+ err = netxen_napi_add(adapter, netdev);
+ if (err)
+ return err;
if (adapter->fw_major < 4)
adapter->max_rds_rings = 3;
@@ -948,6 +975,7 @@ netxen_nic_detach(struct netxen_adapter *adapter)
netxen_free_hw_resources(adapter);
netxen_release_rx_buffers(adapter);
netxen_nic_free_irq(adapter);
+ netxen_napi_del(adapter);
netxen_free_sw_resources(adapter);
adapter->is_up = 0;
@@ -1006,7 +1034,7 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
revision_id = pdev->revision;
adapter->ahw.revision_id = revision_id;
- err = nx_set_dma_mask(adapter, revision_id);
+ err = nx_set_dma_mask(adapter);
if (err)
goto err_out_free_netdev;
@@ -1092,9 +1120,6 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
netdev->irq = adapter->msix_entries[0].vector;
- if (netxen_napi_add(adapter, netdev))
- goto err_out_disable_msi;
-
init_timer(&adapter->watchdog_timer);
adapter->watchdog_timer.function = &netxen_watchdog;
adapter->watchdog_timer.data = (unsigned long)adapter;
@@ -1164,6 +1189,9 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev)
unregister_netdev(netdev);
+ cancel_work_sync(&adapter->watchdog_task);
+ cancel_work_sync(&adapter->tx_timeout_task);
+
if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC) {
netxen_nic_detach(adapter);
}
@@ -1172,7 +1200,6 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev)
netxen_free_adapter_offload(adapter);
netxen_teardown_intr(adapter);
- netxen_free_sds_rings(&adapter->recv_ctx);
netxen_cleanup_pci_map(adapter);
@@ -1198,6 +1225,9 @@ netxen_nic_suspend(struct pci_dev *pdev, pm_message_t state)
if (netif_running(netdev))
netxen_nic_down(adapter, netdev);
+ cancel_work_sync(&adapter->watchdog_task);
+ cancel_work_sync(&adapter->tx_timeout_task);
+
if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC)
netxen_nic_detach(adapter);
@@ -1536,11 +1566,6 @@ static int netxen_nic_check_temp(struct netxen_adapter *adapter)
"%s: Device temperature %d degrees C exceeds"
" maximum allowed. Hardware has been shut down.\n",
netdev->name, temp_val);
-
- netif_device_detach(netdev);
- netxen_nic_down(adapter, netdev);
- netxen_nic_detach(adapter);
-
rv = 1;
} else if (temp_state == NX_TEMP_WARN) {
if (adapter->temp == NX_TEMP_NORMAL) {
@@ -1574,10 +1599,7 @@ void netxen_advert_link_change(struct netxen_adapter *adapter, int linkup)
netif_carrier_off(netdev);
netif_stop_queue(netdev);
}
-
- if (!adapter->has_link_events)
- netxen_nic_set_link_parameters(adapter);
-
+ adapter->link_changed = !adapter->has_link_events;
} else if (!adapter->ahw.linkup && linkup) {
printk(KERN_INFO "%s: %s NIC Link is up\n",
netxen_nic_driver_name, netdev->name);
@@ -1586,9 +1608,7 @@ void netxen_advert_link_change(struct netxen_adapter *adapter, int linkup)
netif_carrier_on(netdev);
netif_wake_queue(netdev);
}
-
- if (!adapter->has_link_events)
- netxen_nic_set_link_parameters(adapter);
+ adapter->link_changed = !adapter->has_link_events;
}
}
@@ -1615,11 +1635,36 @@ static void netxen_nic_handle_phy_intr(struct netxen_adapter *adapter)
netxen_advert_link_change(adapter, linkup);
}
+static void netxen_nic_thermal_shutdown(struct netxen_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+
+ netif_device_detach(netdev);
+ netxen_nic_down(adapter, netdev);
+ netxen_nic_detach(adapter);
+}
+
static void netxen_watchdog(unsigned long v)
{
struct netxen_adapter *adapter = (struct netxen_adapter *)v;
- SCHEDULE_WORK(&adapter->watchdog_task);
+ if (netxen_nic_check_temp(adapter))
+ goto do_sched;
+
+ if (!adapter->has_link_events) {
+ netxen_nic_handle_phy_intr(adapter);
+
+ if (adapter->link_changed)
+ goto do_sched;
+ }
+
+ if (netif_running(adapter->netdev))
+ mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ);
+
+ return;
+
+do_sched:
+ schedule_work(&adapter->watchdog_task);
}
void netxen_watchdog_task(struct work_struct *work)
@@ -1627,11 +1672,13 @@ void netxen_watchdog_task(struct work_struct *work)
struct netxen_adapter *adapter =
container_of(work, struct netxen_adapter, watchdog_task);
- if (netxen_nic_check_temp(adapter))
+ if (adapter->temp == NX_TEMP_PANIC) {
+ netxen_nic_thermal_shutdown(adapter);
return;
+ }
- if (!adapter->has_link_events)
- netxen_nic_handle_phy_intr(adapter);
+ if (adapter->link_changed)
+ netxen_nic_set_link_parameters(adapter);
if (netif_running(adapter->netdev))
mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ);
@@ -1639,9 +1686,8 @@ void netxen_watchdog_task(struct work_struct *work)
static void netxen_tx_timeout(struct net_device *netdev)
{
- struct netxen_adapter *adapter = (struct netxen_adapter *)
- netdev_priv(netdev);
- SCHEDULE_WORK(&adapter->tx_timeout_task);
+ struct netxen_adapter *adapter = netdev_priv(netdev);
+ schedule_work(&adapter->tx_timeout_task);
}
static void netxen_tx_timeout_task(struct work_struct *work)
@@ -1798,9 +1844,6 @@ static int __init netxen_init_module(void)
{
printk(KERN_INFO "%s\n", netxen_nic_driver_string);
- if ((netxen_workq = create_singlethread_workqueue("netxen")) == NULL)
- return -ENOMEM;
-
return pci_register_driver(&netxen_driver);
}
@@ -1809,7 +1852,6 @@ module_init(netxen_init_module);
static void __exit netxen_exit_module(void)
{
pci_unregister_driver(&netxen_driver);
- destroy_workqueue(netxen_workq);
}
module_exit(netxen_exit_module);
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index 28368157dac..23e1a0750fe 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -1611,8 +1611,11 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
if (pcnet32_dwio_read_csr(ioaddr, 0) == 4
&& pcnet32_dwio_check(ioaddr)) {
a = &pcnet32_dwio;
- } else
+ } else {
+ if (pcnet32_debug & NETIF_MSG_PROBE)
+ printk(KERN_ERR PFX "No access methods\n");
goto err_release_region;
+ }
}
chip_version =
@@ -1719,7 +1722,9 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
ret = -ENOMEM;
goto err_release_region;
}
- SET_NETDEV_DEV(dev, &pdev->dev);
+
+ if (pdev)
+ SET_NETDEV_DEV(dev, &pdev->dev);
if (pcnet32_debug & NETIF_MSG_PROBE)
printk(KERN_INFO PFX "%s at %#3lx,", chipname, ioaddr);
@@ -1818,7 +1823,6 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
spin_lock_init(&lp->lock);
- SET_NETDEV_DEV(dev, &pdev->dev);
lp->name = chipname;
lp->shared_irq = shared;
lp->tx_ring_size = TX_RING_SIZE; /* default tx ring size */
@@ -1835,7 +1839,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
lp->chip_version = chip_version;
lp->msg_enable = pcnet32_debug;
if ((cards_found >= MAX_UNITS)
- || (options[cards_found] > sizeof(options_mapping)))
+ || (options[cards_found] >= sizeof(options_mapping)))
lp->options = PCNET32_PORT_ASEL;
else
lp->options = options_mapping[options[cards_found]];
@@ -1852,12 +1856,6 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
((cards_found >= MAX_UNITS) || full_duplex[cards_found]))
lp->options |= PCNET32_PORT_FD;
- if (!a) {
- if (pcnet32_debug & NETIF_MSG_PROBE)
- printk(KERN_ERR PFX "No access methods\n");
- ret = -ENODEV;
- goto err_free_consistent;
- }
lp->a = *a;
/* prior to register_netdev, dev->name is not yet correct */
@@ -1973,14 +1971,13 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
return 0;
- err_free_ring:
+err_free_ring:
pcnet32_free_ring(dev);
- err_free_consistent:
pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block),
lp->init_block, lp->init_dma_addr);
- err_free_netdev:
+err_free_netdev:
free_netdev(dev);
- err_release_region:
+err_release_region:
release_region(ioaddr, PCNET32_TOTAL_SIZE);
return ret;
}
@@ -2089,6 +2086,7 @@ static void pcnet32_free_ring(struct net_device *dev)
static int pcnet32_open(struct net_device *dev)
{
struct pcnet32_private *lp = netdev_priv(dev);
+ struct pci_dev *pdev = lp->pci_dev;
unsigned long ioaddr = dev->base_addr;
u16 val;
int i;
@@ -2149,9 +2147,9 @@ static int pcnet32_open(struct net_device *dev)
lp->a.write_csr(ioaddr, 124, val);
/* Allied Telesyn AT 2700/2701 FX are 100Mbit only and do not negotiate */
- if (lp->pci_dev->subsystem_vendor == PCI_VENDOR_ID_AT &&
- (lp->pci_dev->subsystem_device == PCI_SUBDEVICE_ID_AT_2700FX ||
- lp->pci_dev->subsystem_device == PCI_SUBDEVICE_ID_AT_2701FX)) {
+ if (pdev && pdev->subsystem_vendor == PCI_VENDOR_ID_AT &&
+ (pdev->subsystem_device == PCI_SUBDEVICE_ID_AT_2700FX ||
+ pdev->subsystem_device == PCI_SUBDEVICE_ID_AT_2701FX)) {
if (lp->options & PCNET32_PORT_ASEL) {
lp->options = PCNET32_PORT_FD | PCNET32_PORT_100;
if (netif_msg_link(lp))
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 639d11bc444..cd37d739ac7 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -1384,7 +1384,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
/* create a fragment for each channel */
bits = B;
- while (nfree > 0 && len > 0) {
+ while (len > 0) {
list = list->next;
if (list == &ppp->channels) {
i = 0;
@@ -1431,29 +1431,31 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
*otherwise divide it according to the speed
*of the channel we are going to transmit on
*/
- if (pch->speed == 0) {
- flen = totlen/nfree ;
- if (nbigger > 0) {
- flen++;
- nbigger--;
- }
- } else {
- flen = (((totfree - nzero)*(totlen + hdrlen*totfree)) /
- ((totspeed*totfree)/pch->speed)) - hdrlen;
- if (nbigger > 0) {
- flen += ((totfree - nzero)*pch->speed)/totspeed;
- nbigger -= ((totfree - nzero)*pch->speed)/
+ if (nfree > 0) {
+ if (pch->speed == 0) {
+ flen = totlen/nfree ;
+ if (nbigger > 0) {
+ flen++;
+ nbigger--;
+ }
+ } else {
+ flen = (((totfree - nzero)*(totlen + hdrlen*totfree)) /
+ ((totspeed*totfree)/pch->speed)) - hdrlen;
+ if (nbigger > 0) {
+ flen += ((totfree - nzero)*pch->speed)/totspeed;
+ nbigger -= ((totfree - nzero)*pch->speed)/
totspeed;
+ }
}
+ nfree--;
}
- nfree--;
/*
*check if we are on the last channel or
*we exceded the lenght of the data to
*fragment
*/
- if ((nfree == 0) || (flen > len))
+ if ((nfree <= 0) || (flen > len))
flen = len;
/*
*it is not worth to tx on slow channels:
@@ -1467,7 +1469,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
continue;
}
- mtu = pch->chan->mtu + 2 - hdrlen;
+ mtu = pch->chan->mtu - hdrlen;
if (mtu < 4)
mtu = 4;
if (flen > mtu)
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c
index f0031f1f97e..5f2090233d7 100644
--- a/drivers/net/pppoe.c
+++ b/drivers/net/pppoe.c
@@ -1063,6 +1063,7 @@ static void *pppoe_seq_next(struct seq_file *seq, void *v, loff_t *pos)
else {
int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote);
+ po = NULL;
while (++hash < PPPOE_HASH_SIZE) {
po = pn->hash_table[hash];
if (po)
diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c
index e7935d09c89..e0f9219a0ae 100644
--- a/drivers/net/pppol2tp.c
+++ b/drivers/net/pppol2tp.c
@@ -2680,6 +2680,7 @@ out_unregister_pppol2tp_proto:
static void __exit pppol2tp_exit(void)
{
unregister_pppox_proto(PX_PROTO_OL2TP);
+ unregister_pernet_gen_device(pppol2tp_net_id, &pppol2tp_net_ops);
proto_unregister(&pppol2tp_sk_proto);
}
diff --git a/drivers/net/s6gmac.c b/drivers/net/s6gmac.c
index 5345e47b35a..4525cbe8dd6 100644
--- a/drivers/net/s6gmac.c
+++ b/drivers/net/s6gmac.c
@@ -793,7 +793,7 @@ static inline int s6gmac_phy_start(struct net_device *dev)
struct s6gmac *pd = netdev_priv(dev);
int i = 0;
struct phy_device *p = NULL;
- while ((!(p = pd->mii.bus->phy_map[i])) && (i < PHY_MAX_ADDR))
+ while ((i < PHY_MAX_ADDR) && (!(p = pd->mii.bus->phy_map[i])))
i++;
p = phy_connect(dev, dev_name(&p->dev), &s6gmac_adjust_link, 0,
PHY_INTERFACE_MODE_RGMII);
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index 3550c5dcd93..0a551d8f5d9 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -1488,6 +1488,8 @@ static int sky2_up(struct net_device *dev)
sky2_set_vlan_mode(hw, port, sky2->vlgrp != NULL);
#endif
+ sky2->restarting = 0;
+
err = sky2_rx_start(sky2);
if (err)
goto err_out;
@@ -1500,6 +1502,9 @@ static int sky2_up(struct net_device *dev)
sky2_set_multicast(dev);
+ /* wake queue incase we are restarting */
+ netif_wake_queue(dev);
+
if (netif_msg_ifup(sky2))
printk(KERN_INFO PFX "%s: enabling interface\n", dev->name);
return 0;
@@ -1533,6 +1538,8 @@ static inline int tx_dist(unsigned tail, unsigned head)
/* Number of list elements available for next tx */
static inline int tx_avail(const struct sky2_port *sky2)
{
+ if (unlikely(sky2->restarting))
+ return 0;
return sky2->tx_pending - tx_dist(sky2->tx_cons, sky2->tx_prod);
}
@@ -1818,6 +1825,10 @@ static int sky2_down(struct net_device *dev)
if (netif_msg_ifdown(sky2))
printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
+ /* explicitly shut off tx incase we're restarting */
+ sky2->restarting = 1;
+ netif_tx_disable(dev);
+
/* Force flow control off */
sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
@@ -2359,7 +2370,7 @@ static inline void sky2_tx_done(struct net_device *dev, u16 last)
{
struct sky2_port *sky2 = netdev_priv(dev);
- if (netif_running(dev)) {
+ if (likely(netif_running(dev) && !sky2->restarting)) {
netif_tx_lock(dev);
sky2_tx_complete(sky2, last);
netif_tx_unlock(dev);
@@ -4283,6 +4294,7 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
spin_lock_init(&sky2->phy_lock);
sky2->tx_pending = TX_DEF_PENDING;
sky2->rx_pending = RX_DEF_PENDING;
+ sky2->restarting = 0;
hw->dev[port] = dev;
diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h
index b5549c9e510..4486b066b43 100644
--- a/drivers/net/sky2.h
+++ b/drivers/net/sky2.h
@@ -2051,6 +2051,7 @@ struct sky2_port {
u8 duplex; /* DUPLEX_HALF, DUPLEX_FULL */
u8 rx_csum;
u8 wol;
+ u8 restarting;
enum flow_control flow_mode;
enum flow_control flow_status;
diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c
index 1c70e999cc5..7567f510eff 100644
--- a/drivers/net/smc91x.c
+++ b/drivers/net/smc91x.c
@@ -196,21 +196,23 @@ static void PRINT_PKT(u_char *buf, int length)
/* this enables an interrupt in the interrupt mask register */
#define SMC_ENABLE_INT(lp, x) do { \
unsigned char mask; \
- spin_lock_irq(&lp->lock); \
+ unsigned long smc_enable_flags; \
+ spin_lock_irqsave(&lp->lock, smc_enable_flags); \
mask = SMC_GET_INT_MASK(lp); \
mask |= (x); \
SMC_SET_INT_MASK(lp, mask); \
- spin_unlock_irq(&lp->lock); \
+ spin_unlock_irqrestore(&lp->lock, smc_enable_flags); \
} while (0)
/* this disables an interrupt from the interrupt mask register */
#define SMC_DISABLE_INT(lp, x) do { \
unsigned char mask; \
- spin_lock_irq(&lp->lock); \
+ unsigned long smc_disable_flags; \
+ spin_lock_irqsave(&lp->lock, smc_disable_flags); \
mask = SMC_GET_INT_MASK(lp); \
mask &= ~(x); \
SMC_SET_INT_MASK(lp, mask); \
- spin_unlock_irq(&lp->lock); \
+ spin_unlock_irqrestore(&lp->lock, smc_disable_flags); \
} while (0)
/*
@@ -520,21 +522,21 @@ static inline void smc_rcv(struct net_device *dev)
* any other concurrent access and C would always interrupt B. But life
* isn't that easy in a SMP world...
*/
-#define smc_special_trylock(lock) \
+#define smc_special_trylock(lock, flags) \
({ \
int __ret; \
- local_irq_disable(); \
+ local_irq_save(flags); \
__ret = spin_trylock(lock); \
if (!__ret) \
- local_irq_enable(); \
+ local_irq_restore(flags); \
__ret; \
})
-#define smc_special_lock(lock) spin_lock_irq(lock)
-#define smc_special_unlock(lock) spin_unlock_irq(lock)
+#define smc_special_lock(lock, flags) spin_lock_irqsave(lock, flags)
+#define smc_special_unlock(lock, flags) spin_unlock_irqrestore(lock, flags)
#else
-#define smc_special_trylock(lock) (1)
-#define smc_special_lock(lock) do { } while (0)
-#define smc_special_unlock(lock) do { } while (0)
+#define smc_special_trylock(lock, flags) (1)
+#define smc_special_lock(lock, flags) do { } while (0)
+#define smc_special_unlock(lock, flags) do { } while (0)
#endif
/*
@@ -548,10 +550,11 @@ static void smc_hardware_send_pkt(unsigned long data)
struct sk_buff *skb;
unsigned int packet_no, len;
unsigned char *buf;
+ unsigned long flags;
DBG(3, "%s: %s\n", dev->name, __func__);
- if (!smc_special_trylock(&lp->lock)) {
+ if (!smc_special_trylock(&lp->lock, flags)) {
netif_stop_queue(dev);
tasklet_schedule(&lp->tx_task);
return;
@@ -559,7 +562,7 @@ static void smc_hardware_send_pkt(unsigned long data)
skb = lp->pending_tx_skb;
if (unlikely(!skb)) {
- smc_special_unlock(&lp->lock);
+ smc_special_unlock(&lp->lock, flags);
return;
}
lp->pending_tx_skb = NULL;
@@ -569,7 +572,7 @@ static void smc_hardware_send_pkt(unsigned long data)
printk("%s: Memory allocation failed.\n", dev->name);
dev->stats.tx_errors++;
dev->stats.tx_fifo_errors++;
- smc_special_unlock(&lp->lock);
+ smc_special_unlock(&lp->lock, flags);
goto done;
}
@@ -608,7 +611,7 @@ static void smc_hardware_send_pkt(unsigned long data)
/* queue the packet for TX */
SMC_SET_MMU_CMD(lp, MC_ENQUEUE);
- smc_special_unlock(&lp->lock);
+ smc_special_unlock(&lp->lock, flags);
dev->trans_start = jiffies;
dev->stats.tx_packets++;
@@ -633,6 +636,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct smc_local *lp = netdev_priv(dev);
void __iomem *ioaddr = lp->base;
unsigned int numPages, poll_count, status;
+ unsigned long flags;
DBG(3, "%s: %s\n", dev->name, __func__);
@@ -658,7 +662,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
return 0;
}
- smc_special_lock(&lp->lock);
+ smc_special_lock(&lp->lock, flags);
/* now, try to allocate the memory */
SMC_SET_MMU_CMD(lp, MC_ALLOC | numPages);
@@ -676,7 +680,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
} while (--poll_count);
- smc_special_unlock(&lp->lock);
+ smc_special_unlock(&lp->lock, flags);
lp->pending_tx_skb = skb;
if (!poll_count) {
diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c
index eb72d2e9ab3..acfdccd4456 100644
--- a/drivers/net/tulip/de4x5.c
+++ b/drivers/net/tulip/de4x5.c
@@ -5059,7 +5059,7 @@ mii_get_phy(struct net_device *dev)
if ((id == 0) || (id == 65535)) continue; /* Valid ID? */
for (j=0; j<limit; j++) { /* Search PHY table */
if (id != phy_info[j].id) continue; /* ID match? */
- for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++);
+ for (k=0; k < DE4X5_MAX_PHY && lp->phy[k].id; k++);
if (k < DE4X5_MAX_PHY) {
memcpy((char *)&lp->phy[k],
(char *)&phy_info[j], sizeof(struct phy_table));
@@ -5072,7 +5072,7 @@ mii_get_phy(struct net_device *dev)
break;
}
if ((j == limit) && (i < DE4X5_MAX_MII)) {
- for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++);
+ for (k=0; k < DE4X5_MAX_PHY && lp->phy[k].id; k++);
lp->phy[k].addr = i;
lp->phy[k].id = id;
lp->phy[k].spd.reg = GENERIC_REG; /* ANLPA register */
@@ -5091,7 +5091,7 @@ mii_get_phy(struct net_device *dev)
purgatory:
lp->active = 0;
if (lp->phy[0].id) { /* Reset the PHY devices */
- for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/
+ for (k=0; k < DE4X5_MAX_PHY && lp->phy[k].id; k++) { /*For each PHY*/
mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII);
while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST);
diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c
index 99a63649f4f..4cf9a658875 100644
--- a/drivers/net/tulip/tulip_core.c
+++ b/drivers/net/tulip/tulip_core.c
@@ -652,8 +652,9 @@ tulip_start_xmit(struct sk_buff *skb, struct net_device *dev)
int entry;
u32 flag;
dma_addr_t mapping;
+ unsigned long flags;
- spin_lock_irq(&tp->lock);
+ spin_lock_irqsave(&tp->lock, flags);
/* Calculate the next Tx descriptor entry. */
entry = tp->cur_tx % TX_RING_SIZE;
@@ -688,7 +689,7 @@ tulip_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Trigger an immediate transmit demand. */
iowrite32(0, tp->base_addr + CSR1);
- spin_unlock_irq(&tp->lock);
+ spin_unlock_irqrestore(&tp->lock, flags);
dev->trans_start = jiffies;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 027f7aba26a..42b6c6319bc 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1048,20 +1048,15 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
return err;
}
-static int tun_get_iff(struct net *net, struct file *file, struct ifreq *ifr)
+static int tun_get_iff(struct net *net, struct tun_struct *tun,
+ struct ifreq *ifr)
{
- struct tun_struct *tun = tun_get(file);
-
- if (!tun)
- return -EBADFD;
-
DBG(KERN_INFO "%s: tun_get_iff\n", tun->dev->name);
strcpy(ifr->ifr_name, tun->dev->name);
ifr->ifr_flags = tun_flags(tun);
- tun_put(tun);
return 0;
}
@@ -1105,8 +1100,8 @@ static int set_offload(struct net_device *dev, unsigned long arg)
return 0;
}
-static int tun_chr_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static long tun_chr_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
struct tun_file *tfile = file->private_data;
struct tun_struct *tun;
@@ -1128,34 +1123,32 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
(unsigned int __user*)argp);
}
+ rtnl_lock();
+
tun = __tun_get(tfile);
if (cmd == TUNSETIFF && !tun) {
- int err;
-
ifr.ifr_name[IFNAMSIZ-1] = '\0';
- rtnl_lock();
- err = tun_set_iff(tfile->net, file, &ifr);
- rtnl_unlock();
+ ret = tun_set_iff(tfile->net, file, &ifr);
- if (err)
- return err;
+ if (ret)
+ goto unlock;
if (copy_to_user(argp, &ifr, sizeof(ifr)))
- return -EFAULT;
- return 0;
+ ret = -EFAULT;
+ goto unlock;
}
-
+ ret = -EBADFD;
if (!tun)
- return -EBADFD;
+ goto unlock;
DBG(KERN_INFO "%s: tun_chr_ioctl cmd %d\n", tun->dev->name, cmd);
ret = 0;
switch (cmd) {
case TUNGETIFF:
- ret = tun_get_iff(current->nsproxy->net_ns, file, &ifr);
+ ret = tun_get_iff(current->nsproxy->net_ns, tun, &ifr);
if (ret)
break;
@@ -1201,7 +1194,6 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
case TUNSETLINK:
/* Only allow setting the type when the interface is down */
- rtnl_lock();
if (tun->dev->flags & IFF_UP) {
DBG(KERN_INFO "%s: Linktype set failed because interface is up\n",
tun->dev->name);
@@ -1211,7 +1203,6 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
DBG(KERN_INFO "%s: linktype set to %d\n", tun->dev->name, tun->dev->type);
ret = 0;
}
- rtnl_unlock();
break;
#ifdef TUN_DEBUG
@@ -1220,9 +1211,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
break;
#endif
case TUNSETOFFLOAD:
- rtnl_lock();
ret = set_offload(tun->dev, arg);
- rtnl_unlock();
break;
case TUNSETTXFILTER:
@@ -1230,9 +1219,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
ret = -EINVAL;
if ((tun->flags & TUN_TYPE_MASK) != TUN_TAP_DEV)
break;
- rtnl_lock();
ret = update_filter(&tun->txflt, (void __user *)arg);
- rtnl_unlock();
break;
case SIOCGIFHWADDR:
@@ -1248,9 +1235,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
DBG(KERN_DEBUG "%s: set hw address: %pM\n",
tun->dev->name, ifr.ifr_hwaddr.sa_data);
- rtnl_lock();
ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr);
- rtnl_unlock();
break;
case TUNGETSNDBUF:
@@ -1273,7 +1258,10 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
break;
};
- tun_put(tun);
+unlock:
+ rtnl_unlock();
+ if (tun)
+ tun_put(tun);
return ret;
}
@@ -1361,7 +1349,7 @@ static const struct file_operations tun_fops = {
.write = do_sync_write,
.aio_write = tun_chr_aio_write,
.poll = tun_chr_poll,
- .ioctl = tun_chr_ioctl,
+ .unlocked_ioctl = tun_chr_ioctl,
.open = tun_chr_open,
.release = tun_chr_close,
.fasync = tun_chr_fasync
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
index 3b957e6412e..8a7b8c7bd78 100644
--- a/drivers/net/ucc_geth.c
+++ b/drivers/net/ucc_geth.c
@@ -3111,10 +3111,11 @@ static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev)
u8 __iomem *bd; /* BD pointer */
u32 bd_status;
u8 txQ = 0;
+ unsigned long flags;
ugeth_vdbg("%s: IN", __func__);
- spin_lock_irq(&ugeth->lock);
+ spin_lock_irqsave(&ugeth->lock, flags);
dev->stats.tx_bytes += skb->len;
@@ -3171,7 +3172,7 @@ static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev)
uccf = ugeth->uccf;
out_be16(uccf->p_utodr, UCC_FAST_TOD);
#endif
- spin_unlock_irq(&ugeth->lock);
+ spin_unlock_irqrestore(&ugeth->lock, flags);
return 0;
}
diff --git a/drivers/net/usb/pegasus.h b/drivers/net/usb/pegasus.h
index c7467823cd1..f968c834ff6 100644
--- a/drivers/net/usb/pegasus.h
+++ b/drivers/net/usb/pegasus.h
@@ -250,6 +250,8 @@ PEGASUS_DEV( "IO DATA USB ET/TX", VENDOR_IODATA, 0x0904,
DEFAULT_GPIO_RESET )
PEGASUS_DEV( "IO DATA USB ET/TX-S", VENDOR_IODATA, 0x0913,
DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "IO DATA USB ETX-US2", VENDOR_IODATA, 0x092a,
+ DEFAULT_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "Kingston KNU101TX Ethernet", VENDOR_KINGSTON, 0x000a,
DEFAULT_GPIO_RESET)
PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x4002,
diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
index 88c30a58b4b..934f7671650 100644
--- a/drivers/net/via-rhine.c
+++ b/drivers/net/via-rhine.c
@@ -1218,6 +1218,7 @@ static int rhine_start_tx(struct sk_buff *skb, struct net_device *dev)
struct rhine_private *rp = netdev_priv(dev);
void __iomem *ioaddr = rp->base;
unsigned entry;
+ unsigned long flags;
/* Caution: the write order is important here, set the field
with the "ownership" bits last. */
@@ -1261,7 +1262,7 @@ static int rhine_start_tx(struct sk_buff *skb, struct net_device *dev)
cpu_to_le32(TXDESC | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
/* lock eth irq */
- spin_lock_irq(&rp->lock);
+ spin_lock_irqsave(&rp->lock, flags);
wmb();
rp->tx_ring[entry].tx_status = cpu_to_le32(DescOwn);
wmb();
@@ -1280,7 +1281,7 @@ static int rhine_start_tx(struct sk_buff *skb, struct net_device *dev)
dev->trans_start = jiffies;
- spin_unlock_irq(&rp->lock);
+ spin_unlock_irqrestore(&rp->lock, flags);
if (debug > 4) {
printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c
index 3ba35956327..cee08a1e497 100644
--- a/drivers/net/via-velocity.c
+++ b/drivers/net/via-velocity.c
@@ -1778,7 +1778,7 @@ static void velocity_error(struct velocity_info *vptr, int status)
* mode
*/
if (vptr->rev_id < REV_ID_VT3216_A0) {
- if (vptr->mii_status | VELOCITY_DUPLEX_FULL)
+ if (vptr->mii_status & VELOCITY_DUPLEX_FULL)
BYTE_REG_BITS_ON(TCR_TB2BDIS, &regs->TCR);
else
BYTE_REG_BITS_OFF(TCR_TB2BDIS, &regs->TCR);
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 2a6e81d5b57..bbedf03a212 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -70,6 +70,9 @@ struct virtnet_info
struct sk_buff_head recv;
struct sk_buff_head send;
+ /* Work struct for refilling if we run low on memory. */
+ struct delayed_work refill;
+
/* Chain pages by the private ptr. */
struct page *pages;
};
@@ -273,19 +276,22 @@ drop:
dev_kfree_skb(skb);
}
-static void try_fill_recv_maxbufs(struct virtnet_info *vi)
+static bool try_fill_recv_maxbufs(struct virtnet_info *vi, gfp_t gfp)
{
struct sk_buff *skb;
struct scatterlist sg[2+MAX_SKB_FRAGS];
int num, err, i;
+ bool oom = false;
sg_init_table(sg, 2+MAX_SKB_FRAGS);
for (;;) {
struct virtio_net_hdr *hdr;
skb = netdev_alloc_skb(vi->dev, MAX_PACKET_LEN + NET_IP_ALIGN);
- if (unlikely(!skb))
+ if (unlikely(!skb)) {
+ oom = true;
break;
+ }
skb_reserve(skb, NET_IP_ALIGN);
skb_put(skb, MAX_PACKET_LEN);
@@ -296,7 +302,7 @@ static void try_fill_recv_maxbufs(struct virtnet_info *vi)
if (vi->big_packets) {
for (i = 0; i < MAX_SKB_FRAGS; i++) {
skb_frag_t *f = &skb_shinfo(skb)->frags[i];
- f->page = get_a_page(vi, GFP_ATOMIC);
+ f->page = get_a_page(vi, gfp);
if (!f->page)
break;
@@ -325,31 +331,35 @@ static void try_fill_recv_maxbufs(struct virtnet_info *vi)
if (unlikely(vi->num > vi->max))
vi->max = vi->num;
vi->rvq->vq_ops->kick(vi->rvq);
+ return !oom;
}
-static void try_fill_recv(struct virtnet_info *vi)
+/* Returns false if we couldn't fill entirely (OOM). */
+static bool try_fill_recv(struct virtnet_info *vi, gfp_t gfp)
{
struct sk_buff *skb;
struct scatterlist sg[1];
int err;
+ bool oom = false;
- if (!vi->mergeable_rx_bufs) {
- try_fill_recv_maxbufs(vi);
- return;
- }
+ if (!vi->mergeable_rx_bufs)
+ return try_fill_recv_maxbufs(vi, gfp);
for (;;) {
skb_frag_t *f;
skb = netdev_alloc_skb(vi->dev, GOOD_COPY_LEN + NET_IP_ALIGN);
- if (unlikely(!skb))
+ if (unlikely(!skb)) {
+ oom = true;
break;
+ }
skb_reserve(skb, NET_IP_ALIGN);
f = &skb_shinfo(skb)->frags[0];
- f->page = get_a_page(vi, GFP_ATOMIC);
+ f->page = get_a_page(vi, gfp);
if (!f->page) {
+ oom = true;
kfree_skb(skb);
break;
}
@@ -373,6 +383,7 @@ static void try_fill_recv(struct virtnet_info *vi)
if (unlikely(vi->num > vi->max))
vi->max = vi->num;
vi->rvq->vq_ops->kick(vi->rvq);
+ return !oom;
}
static void skb_recv_done(struct virtqueue *rvq)
@@ -385,6 +396,23 @@ static void skb_recv_done(struct virtqueue *rvq)
}
}
+static void refill_work(struct work_struct *work)
+{
+ struct virtnet_info *vi;
+ bool still_empty;
+
+ vi = container_of(work, struct virtnet_info, refill.work);
+ napi_disable(&vi->napi);
+ try_fill_recv(vi, GFP_KERNEL);
+ still_empty = (vi->num == 0);
+ napi_enable(&vi->napi);
+
+ /* In theory, this can happen: if we don't get any buffers in
+ * we will *never* try to fill again. */
+ if (still_empty)
+ schedule_delayed_work(&vi->refill, HZ/2);
+}
+
static int virtnet_poll(struct napi_struct *napi, int budget)
{
struct virtnet_info *vi = container_of(napi, struct virtnet_info, napi);
@@ -400,10 +428,10 @@ again:
received++;
}
- /* FIXME: If we oom and completely run out of inbufs, we need
- * to start a timer trying to fill more. */
- if (vi->num < vi->max / 2)
- try_fill_recv(vi);
+ if (vi->num < vi->max / 2) {
+ if (!try_fill_recv(vi, GFP_ATOMIC))
+ schedule_delayed_work(&vi->refill, 0);
+ }
/* Out of packets? */
if (received < budget) {
@@ -893,6 +921,7 @@ static int virtnet_probe(struct virtio_device *vdev)
vi->vdev = vdev;
vdev->priv = vi;
vi->pages = NULL;
+ INIT_DELAYED_WORK(&vi->refill, refill_work);
/* If they give us a callback when all buffers are done, we don't need
* the timer. */
@@ -941,7 +970,7 @@ static int virtnet_probe(struct virtio_device *vdev)
}
/* Last of all, set up some receive buffers. */
- try_fill_recv(vi);
+ try_fill_recv(vi, GFP_KERNEL);
/* If we didn't even get one input buffer, we're useless. */
if (vi->num == 0) {
@@ -958,6 +987,7 @@ static int virtnet_probe(struct virtio_device *vdev)
unregister:
unregister_netdev(dev);
+ cancel_delayed_work_sync(&vi->refill);
free_vqs:
vdev->config->del_vqs(vdev);
free:
@@ -986,6 +1016,7 @@ static void virtnet_remove(struct virtio_device *vdev)
BUG_ON(vi->num != 0);
unregister_netdev(vi->dev);
+ cancel_delayed_work_sync(&vi->refill);
vdev->config->del_vqs(vi->vdev);
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index c70604f0329..8ce5e4cee16 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -5918,20 +5918,19 @@ static int airo_set_essid(struct net_device *dev,
readSsidRid(local, &SSID_rid);
/* Check if we asked for `any' */
- if(dwrq->flags == 0) {
+ if (dwrq->flags == 0) {
/* Just send an empty SSID list */
memset(&SSID_rid, 0, sizeof(SSID_rid));
} else {
- int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+ unsigned index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
/* Check the size of the string */
- if(dwrq->length > IW_ESSID_MAX_SIZE) {
+ if (dwrq->length > IW_ESSID_MAX_SIZE)
return -E2BIG ;
- }
+
/* Check if index is valid */
- if((index < 0) || (index >= 4)) {
+ if (index >= ARRAY_SIZE(SSID_rid.ssids))
return -EINVAL;
- }
/* Set the SSID */
memset(SSID_rid.ssids[index].ssid, 0,
@@ -6819,7 +6818,7 @@ static int airo_set_txpow(struct net_device *dev,
return -EINVAL;
}
clear_bit (FLAG_RADIO_OFF, &local->flags);
- for (i = 0; cap_rid.txPowerLevels[i] && (i < 8); i++)
+ for (i = 0; i < 8 && cap_rid.txPowerLevels[i]; i++)
if (v == cap_rid.txPowerLevels[i]) {
readConfigRid(local, 1);
local->config.txPower = v;
diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c
index 9d38cf60a0d..88c3d857386 100644
--- a/drivers/net/wireless/ath/ar9170/main.c
+++ b/drivers/net/wireless/ath/ar9170/main.c
@@ -1967,13 +1967,14 @@ static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue,
int ret;
mutex_lock(&ar->mutex);
- if ((param) && !(queue > __AR9170_NUM_TXQ)) {
+ if (queue < __AR9170_NUM_TXQ) {
memcpy(&ar->edcf[ar9170_qos_hwmap[queue]],
param, sizeof(*param));
ret = ar9170_set_qos(ar);
- } else
+ } else {
ret = -EINVAL;
+ }
mutex_unlock(&ar->mutex);
return ret;
diff --git a/drivers/net/wireless/ath/ar9170/usb.c b/drivers/net/wireless/ath/ar9170/usb.c
index 754b1f8d8da..007eb85fc67 100644
--- a/drivers/net/wireless/ath/ar9170/usb.c
+++ b/drivers/net/wireless/ath/ar9170/usb.c
@@ -598,11 +598,15 @@ static int ar9170_usb_request_firmware(struct ar9170_usb *aru)
err = request_firmware(&aru->init_values, "ar9170-1.fw",
&aru->udev->dev);
+ if (err) {
+ dev_err(&aru->udev->dev, "file with init values not found.\n");
+ return err;
+ }
err = request_firmware(&aru->firmware, "ar9170-2.fw", &aru->udev->dev);
if (err) {
release_firmware(aru->init_values);
- dev_err(&aru->udev->dev, "file with init values not found.\n");
+ dev_err(&aru->udev->dev, "firmware file not found.\n");
return err;
}
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c
index a2fda702b62..ce0e86c36a8 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -460,7 +460,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
integer = swab32(eep->modalHeader.antCtrlCommon);
eep->modalHeader.antCtrlCommon = integer;
- for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+ for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) {
integer = swab32(eep->modalHeader.antCtrlChain[i]);
eep->modalHeader.antCtrlChain[i] = integer;
}
@@ -914,7 +914,7 @@ static void ath9k_hw_set_4k_power_per_rate_table(struct ath_hw *ah,
ctlMode, numCtlModes, isHt40CtlMode,
(pCtlMode[ctlMode] & EXT_ADDITIVE));
- for (i = 0; (i < AR5416_NUM_CTLS) &&
+ for (i = 0; (i < AR5416_EEP4K_NUM_CTLS) &&
pEepData->ctlIndex[i]; i++) {
DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
" LOOP-Ctlidx %d: cfgCtl 0x%2.2x "
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c
index 44c29b3f672..f593fbbb4e5 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/ipw2x00/ipw2200.c
@@ -2874,45 +2874,27 @@ static int ipw_fw_dma_add_command_block(struct ipw_priv *priv,
return 0;
}
-static int ipw_fw_dma_add_buffer(struct ipw_priv *priv,
- u32 src_phys, u32 dest_address, u32 length)
+static int ipw_fw_dma_add_buffer(struct ipw_priv *priv, dma_addr_t *src_address,
+ int nr, u32 dest_address, u32 len)
{
- u32 bytes_left = length;
- u32 src_offset = 0;
- u32 dest_offset = 0;
- int status = 0;
+ int ret, i;
+ u32 size;
+
IPW_DEBUG_FW(">> \n");
- IPW_DEBUG_FW_INFO("src_phys=0x%x dest_address=0x%x length=0x%x\n",
- src_phys, dest_address, length);
- while (bytes_left > CB_MAX_LENGTH) {
- status = ipw_fw_dma_add_command_block(priv,
- src_phys + src_offset,
- dest_address +
- dest_offset,
- CB_MAX_LENGTH, 0, 0);
- if (status) {
+ IPW_DEBUG_FW_INFO("nr=%d dest_address=0x%x len=0x%x\n",
+ nr, dest_address, len);
+
+ for (i = 0; i < nr; i++) {
+ size = min_t(u32, len - i * CB_MAX_LENGTH, CB_MAX_LENGTH);
+ ret = ipw_fw_dma_add_command_block(priv, src_address[i],
+ dest_address +
+ i * CB_MAX_LENGTH, size,
+ 0, 0);
+ if (ret) {
IPW_DEBUG_FW_INFO(": Failed\n");
return -1;
} else
IPW_DEBUG_FW_INFO(": Added new cb\n");
-
- src_offset += CB_MAX_LENGTH;
- dest_offset += CB_MAX_LENGTH;
- bytes_left -= CB_MAX_LENGTH;
- }
-
- /* add the buffer tail */
- if (bytes_left > 0) {
- status =
- ipw_fw_dma_add_command_block(priv, src_phys + src_offset,
- dest_address + dest_offset,
- bytes_left, 0, 0);
- if (status) {
- IPW_DEBUG_FW_INFO(": Failed on the buffer tail\n");
- return -1;
- } else
- IPW_DEBUG_FW_INFO
- (": Adding new cb - the buffer tail\n");
}
IPW_DEBUG_FW("<< \n");
@@ -3160,59 +3142,91 @@ static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len)
static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len)
{
- int rc = -1;
+ int ret = -1;
int offset = 0;
struct fw_chunk *chunk;
- dma_addr_t shared_phys;
- u8 *shared_virt;
+ int total_nr = 0;
+ int i;
+ struct pci_pool *pool;
+ u32 *virts[CB_NUMBER_OF_ELEMENTS_SMALL];
+ dma_addr_t phys[CB_NUMBER_OF_ELEMENTS_SMALL];
IPW_DEBUG_TRACE("<< : \n");
- shared_virt = pci_alloc_consistent(priv->pci_dev, len, &shared_phys);
- if (!shared_virt)
+ pool = pci_pool_create("ipw2200", priv->pci_dev, CB_MAX_LENGTH, 0, 0);
+ if (!pool) {
+ IPW_ERROR("pci_pool_create failed\n");
return -ENOMEM;
-
- memmove(shared_virt, data, len);
+ }
/* Start the Dma */
- rc = ipw_fw_dma_enable(priv);
+ ret = ipw_fw_dma_enable(priv);
/* the DMA is already ready this would be a bug. */
BUG_ON(priv->sram_desc.last_cb_index > 0);
do {
+ u32 chunk_len;
+ u8 *start;
+ int size;
+ int nr = 0;
+
chunk = (struct fw_chunk *)(data + offset);
offset += sizeof(struct fw_chunk);
+ chunk_len = le32_to_cpu(chunk->length);
+ start = data + offset;
+
+ nr = (chunk_len + CB_MAX_LENGTH - 1) / CB_MAX_LENGTH;
+ for (i = 0; i < nr; i++) {
+ virts[total_nr] = pci_pool_alloc(pool, GFP_KERNEL,
+ &phys[total_nr]);
+ if (!virts[total_nr]) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ size = min_t(u32, chunk_len - i * CB_MAX_LENGTH,
+ CB_MAX_LENGTH);
+ memcpy(virts[total_nr], start, size);
+ start += size;
+ total_nr++;
+ /* We don't support fw chunk larger than 64*8K */
+ BUG_ON(total_nr > CB_NUMBER_OF_ELEMENTS_SMALL);
+ }
+
/* build DMA packet and queue up for sending */
/* dma to chunk->address, the chunk->length bytes from data +
* offeset*/
/* Dma loading */
- rc = ipw_fw_dma_add_buffer(priv, shared_phys + offset,
- le32_to_cpu(chunk->address),
- le32_to_cpu(chunk->length));
- if (rc) {
+ ret = ipw_fw_dma_add_buffer(priv, &phys[total_nr - nr],
+ nr, le32_to_cpu(chunk->address),
+ chunk_len);
+ if (ret) {
IPW_DEBUG_INFO("dmaAddBuffer Failed\n");
goto out;
}
- offset += le32_to_cpu(chunk->length);
+ offset += chunk_len;
} while (offset < len);
/* Run the DMA and wait for the answer */
- rc = ipw_fw_dma_kick(priv);
- if (rc) {
+ ret = ipw_fw_dma_kick(priv);
+ if (ret) {
IPW_ERROR("dmaKick Failed\n");
goto out;
}
- rc = ipw_fw_dma_wait(priv);
- if (rc) {
+ ret = ipw_fw_dma_wait(priv);
+ if (ret) {
IPW_ERROR("dmaWaitSync Failed\n");
goto out;
}
- out:
- pci_free_consistent(priv->pci_dev, len, shared_virt, shared_phys);
- return rc;
+ out:
+ for (i = 0; i < total_nr; i++)
+ pci_pool_free(pool, virts[i], phys[i]);
+
+ pci_pool_destroy(pool);
+
+ return ret;
}
/* stop nic */
@@ -6226,7 +6240,7 @@ static void ipw_add_scan_channels(struct ipw_priv *priv,
};
u8 channel;
- while (channel_index < IPW_SCAN_CHANNELS) {
+ while (channel_index < IPW_SCAN_CHANNELS - 1) {
channel =
priv->speed_scan[priv->speed_scan_pos];
if (channel == 0) {
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h
index fbb3a573463..2de6471d4be 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.h
@@ -112,7 +112,7 @@ enum iwl3945_antenna {
#define IWL_TX_FIFO_NONE 7
/* Minimum number of queues. MAX_NUM is defined in hw specific files */
-#define IWL_MIN_NUM_QUEUES 4
+#define IWL39_MIN_NUM_QUEUES 4
#define IEEE80211_DATA_LEN 2304
#define IEEE80211_4ADDR_LEN 30
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 6ab07165ea2..18b135f510e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -1332,6 +1332,9 @@ int iwl_setup_mac(struct iwl_priv *priv)
hw->wiphy->custom_regulatory = true;
+ /* Firmware does not support this */
+ hw->wiphy->disable_beacon_hints = true;
+
hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
/* we create the 802.11 header and a zero-length SSID element */
hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2;
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index 11e08c06891..ca00cc8ad4c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -308,18 +308,18 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file,
return -ENODATA;
}
+ ptr = priv->eeprom;
+ if (!ptr) {
+ IWL_ERR(priv, "Invalid EEPROM/OTP memory\n");
+ return -ENOMEM;
+ }
+
/* 4 characters for byte 0xYY */
buf = kzalloc(buf_size, GFP_KERNEL);
if (!buf) {
IWL_ERR(priv, "Can not allocate Buffer\n");
return -ENOMEM;
}
-
- ptr = priv->eeprom;
- if (!ptr) {
- IWL_ERR(priv, "Invalid EEPROM/OTP memory\n");
- return -ENOMEM;
- }
pos += scnprintf(buf + pos, buf_size - pos, "NVM Type: %s\n",
(priv->nvm_device_type == NVM_DEVICE_TYPE_OTP)
? "OTP" : "EEPROM");
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index e2d620f0b6e..650e20af20f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -258,8 +258,10 @@ struct iwl_channel_info {
#define IWL_TX_FIFO_HCCA_2 6
#define IWL_TX_FIFO_NONE 7
-/* Minimum number of queues. MAX_NUM is defined in hw specific files */
-#define IWL_MIN_NUM_QUEUES 4
+/* Minimum number of queues. MAX_NUM is defined in hw specific files.
+ * Set the minimum to accommodate the 4 standard TX queues, 1 command
+ * queue, 2 (unused) HCCA queues, and 4 HT queues (one for each AC) */
+#define IWL_MIN_NUM_QUEUES 10
/* Power management (not Tx power) structures */
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c
index 2addf735b19..ffd5c61a755 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sta.c
+++ b/drivers/net/wireless/iwlwifi/iwl-sta.c
@@ -566,6 +566,8 @@ int iwl_remove_default_wep_key(struct iwl_priv *priv,
unsigned long flags;
spin_lock_irqsave(&priv->sta_lock, flags);
+ IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n",
+ keyconf->keyidx);
if (!test_and_clear_bit(keyconf->keyidx, &priv->ucode_key_table))
IWL_ERR(priv, "index %d not used in uCode key table.\n",
@@ -573,6 +575,11 @@ int iwl_remove_default_wep_key(struct iwl_priv *priv,
priv->default_wep_key--;
memset(&priv->wep_keys[keyconf->keyidx], 0, sizeof(priv->wep_keys[0]));
+ if (iwl_is_rfkill(priv)) {
+ IWL_DEBUG_WEP(priv, "Not sending REPLY_WEPKEY command due to RFKILL.\n");
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
+ return 0;
+ }
ret = iwl_send_static_wepkey_cmd(priv, 1);
IWL_DEBUG_WEP(priv, "Remove default WEP key: idx=%d ret=%d\n",
keyconf->keyidx, ret);
@@ -853,6 +860,11 @@ int iwl_remove_dynamic_key(struct iwl_priv *priv,
priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ if (iwl_is_rfkill(priv)) {
+ IWL_DEBUG_WEP(priv, "Not sending REPLY_ADD_STA command because RFKILL enabled. \n");
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
+ return 0;
+ }
ret = iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
spin_unlock_irqrestore(&priv->sta_lock, flags);
return ret;
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index 9bbeec9427f..2e89040e63b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -720,8 +720,6 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
goto drop_unlock;
}
- spin_unlock_irqrestore(&priv->lock, flags);
-
hdr_len = ieee80211_hdrlen(fc);
/* Find (or create) index into station table for destination station */
@@ -729,7 +727,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
if (sta_id == IWL_INVALID_STATION) {
IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n",
hdr->addr1);
- goto drop;
+ goto drop_unlock;
}
IWL_DEBUG_TX(priv, "station Id %d\n", sta_id);
@@ -750,14 +748,17 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
txq_id = priv->stations[sta_id].tid[tid].agg.txq_id;
swq_id = iwl_virtual_agg_queue_num(swq_id, txq_id);
}
- priv->stations[sta_id].tid[tid].tfds_in_queue++;
}
txq = &priv->txq[txq_id];
q = &txq->q;
txq->swq_id = swq_id;
- spin_lock_irqsave(&priv->lock, flags);
+ if (unlikely(iwl_queue_space(q) < q->high_mark))
+ goto drop_unlock;
+
+ if (ieee80211_is_data_qos(fc))
+ priv->stations[sta_id].tid[tid].tfds_in_queue++;
/* Set up driver data for this TFD */
memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl_tx_info));
@@ -902,7 +903,6 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
drop_unlock:
spin_unlock_irqrestore(&priv->lock, flags);
-drop:
return -1;
}
EXPORT_SYMBOL(iwl_tx_skb);
@@ -1171,6 +1171,8 @@ int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn)
IWL_ERR(priv, "Start AGG on invalid station\n");
return -ENXIO;
}
+ if (unlikely(tid >= MAX_TID_COUNT))
+ return -EINVAL;
if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_OFF) {
IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n");
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 956798f2c80..523843369ca 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -3968,6 +3968,9 @@ static int iwl3945_setup_mac(struct iwl_priv *priv)
hw->wiphy->custom_regulatory = true;
+ /* Firmware does not support this */
+ hw->wiphy->disable_beacon_hints = true;
+
hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX_3945;
/* we create the 802.11 header and a zero-length SSID element */
hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2;
@@ -4018,10 +4021,10 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
SET_IEEE80211_DEV(hw, &pdev->dev);
if ((iwl3945_mod_params.num_of_queues > IWL39_MAX_NUM_QUEUES) ||
- (iwl3945_mod_params.num_of_queues < IWL_MIN_NUM_QUEUES)) {
+ (iwl3945_mod_params.num_of_queues < IWL39_MIN_NUM_QUEUES)) {
IWL_ERR(priv,
"invalid queues_num, should be between %d and %d\n",
- IWL_MIN_NUM_QUEUES, IWL39_MAX_NUM_QUEUES);
+ IWL39_MIN_NUM_QUEUES, IWL39_MAX_NUM_QUEUES);
err = -EINVAL;
goto out_ieee80211_free_hw;
}
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c
index 834a7f544e5..e2334d12359 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.c
+++ b/drivers/net/wireless/iwmc3200wifi/commands.c
@@ -220,6 +220,7 @@ int iwm_store_rxiq_calib_result(struct iwm_priv *iwm)
eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ);
if (IS_ERR(eeprom_rxiq)) {
IWM_ERR(iwm, "Couldn't access EEPROM RX IQ entry\n");
+ kfree(rxiq);
return PTR_ERR(eeprom_rxiq);
}
diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
index aea5ccf24cc..bf294e41753 100644
--- a/drivers/net/wireless/iwmc3200wifi/netdev.c
+++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
@@ -106,10 +106,8 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev,
int ret = 0;
wdev = iwm_wdev_alloc(sizeof_bus, dev);
- if (!wdev) {
- dev_err(dev, "no memory for wireless device instance\n");
- return ERR_PTR(-ENOMEM);
- }
+ if (IS_ERR(wdev))
+ return wdev;
iwm = wdev_to_iwm(wdev);
iwm->bus_ops = if_ops;
diff --git a/drivers/net/wireless/libertas/11d.c b/drivers/net/wireless/libertas/11d.c
index 9a5408e7d94..5c6968101f0 100644
--- a/drivers/net/wireless/libertas/11d.c
+++ b/drivers/net/wireless/libertas/11d.c
@@ -47,7 +47,7 @@ static u8 lbs_region_2_code(u8 *region)
{
u8 i;
- for (i = 0; region[i] && i < COUNTRY_CODE_LEN; i++)
+ for (i = 0; i < COUNTRY_CODE_LEN && region[i]; i++)
region[i] = toupper(region[i]);
for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) {
diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h
index 0a2e29140ad..c8a1998d474 100644
--- a/drivers/net/wireless/libertas/hostcmd.h
+++ b/drivers/net/wireless/libertas/hostcmd.h
@@ -56,8 +56,8 @@ struct rxpd {
u8 bss_type;
/* BSS number */
u8 bss_num;
- } bss;
- } u;
+ } __attribute__ ((packed)) bss;
+ } __attribute__ ((packed)) u;
/* SNR */
u8 snr;
diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c
index 601b5424967..6c95af3023c 100644
--- a/drivers/net/wireless/libertas/scan.c
+++ b/drivers/net/wireless/libertas/scan.c
@@ -5,6 +5,7 @@
* for sending scan commands to the firmware.
*/
#include <linux/types.h>
+#include <linux/kernel.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <asm/unaligned.h>
@@ -876,7 +877,7 @@ static inline char *lbs_translate_scan(struct lbs_private *priv,
iwe.u.bitrate.disabled = 0;
iwe.u.bitrate.value = 0;
- for (j = 0; bss->rates[j] && (j < sizeof(bss->rates)); j++) {
+ for (j = 0; j < ARRAY_SIZE(bss->rates) && bss->rates[j]; j++) {
/* Bit rate given in 500 kb/s units */
iwe.u.bitrate.value = bss->rates[j] * 500000;
current_val = iwe_stream_add_value(info, start, current_val,
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index a263d5c84c0..83967afe082 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -261,7 +261,7 @@ struct mwl8k_vif {
*/
};
-#define MWL8K_VIF(_vif) (struct mwl8k_vif *)(&((_vif)->drv_priv))
+#define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv))
static const struct ieee80211_channel mwl8k_channels[] = {
{ .center_freq = 2412, .hw_value = 1, },
@@ -1012,6 +1012,8 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit)
rmb();
skb = rxq->rx_skb[rxq->rx_head];
+ if (skb == NULL)
+ break;
rxq->rx_skb[rxq->rx_head] = NULL;
rxq->rx_head = (rxq->rx_head + 1) % MWL8K_RX_DESCS;
@@ -1591,6 +1593,9 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
timeout = wait_for_completion_timeout(&cmd_wait,
msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS));
+ pci_unmap_single(priv->pdev, dma_addr, dma_size,
+ PCI_DMA_BIDIRECTIONAL);
+
result = &cmd->result;
if (!timeout) {
spin_lock_irq(&priv->fw_lock);
@@ -1610,8 +1615,6 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
*result);
}
- pci_unmap_single(priv->pdev, dma_addr, dma_size,
- PCI_DMA_BIDIRECTIONAL);
return rc;
}
@@ -1654,18 +1657,18 @@ static int mwl8k_cmd_get_hw_spec(struct ieee80211_hw *hw)
memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));
cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rx_desc_dma);
- cmd->num_tx_queues = MWL8K_TX_QUEUES;
+ cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES);
for (i = 0; i < MWL8K_TX_QUEUES; i++)
cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].tx_desc_dma);
- cmd->num_tx_desc_per_queue = MWL8K_TX_DESCS;
- cmd->total_rx_desc = MWL8K_RX_DESCS;
+ cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
+ cmd->total_rx_desc = cpu_to_le32(MWL8K_RX_DESCS);
rc = mwl8k_post_cmd(hw, &cmd->header);
if (!rc) {
SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr);
priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
- priv->fw_rev = cmd->fw_rev;
+ priv->fw_rev = le32_to_cpu(cmd->fw_rev);
priv->hw_rev = cmd->hw_rev;
priv->region_code = le16_to_cpu(cmd->region_code);
}
@@ -3216,15 +3219,19 @@ static int mwl8k_configure_filter_wt(struct work_struct *wt)
struct dev_addr_list *mclist = worker->mclist;
struct mwl8k_priv *priv = hw->priv;
- struct mwl8k_vif *mv_vif;
int rc = 0;
if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
rc = mwl8k_cmd_set_pre_scan(hw);
else {
- mv_vif = MWL8K_VIF(priv->vif);
- rc = mwl8k_cmd_set_post_scan(hw, mv_vif->bssid);
+ u8 *bssid;
+
+ bssid = "\x00\x00\x00\x00\x00\x00";
+ if (priv->vif != NULL)
+ bssid = MWL8K_VIF(priv->vif)->bssid;
+
+ rc = mwl8k_cmd_set_post_scan(hw, bssid);
}
}
@@ -3726,6 +3733,8 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev)
ieee80211_stop_queues(hw);
+ ieee80211_unregister_hw(hw);
+
/* Remove tx reclaim tasklet */
tasklet_kill(&priv->tx_reclaim_task);
@@ -3739,8 +3748,6 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev)
for (i = 0; i < MWL8K_TX_QUEUES; i++)
mwl8k_txq_reclaim(hw, i, 1);
- ieee80211_unregister_hw(hw);
-
for (i = 0; i < MWL8K_TX_QUEUES; i++)
mwl8k_txq_deinit(hw, i);
diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c
index 632fac86a30..b3946272c72 100644
--- a/drivers/net/wireless/orinoco/hw.c
+++ b/drivers/net/wireless/orinoco/hw.c
@@ -70,7 +70,7 @@ int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc)
int err = 0;
u8 tsc_arr[4][IW_ENCODE_SEQ_MAX_SIZE];
- if ((key < 0) || (key > 4))
+ if ((key < 0) || (key >= 4))
return -EINVAL;
err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index a498dde024e..49c9e2c1433 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -849,13 +849,15 @@ struct rt2x00_dev {
static inline void rt2x00_rf_read(struct rt2x00_dev *rt2x00dev,
const unsigned int word, u32 *data)
{
- *data = rt2x00dev->rf[word];
+ BUG_ON(word < 1 || word > rt2x00dev->ops->rf_size / sizeof(u32));
+ *data = rt2x00dev->rf[word - 1];
}
static inline void rt2x00_rf_write(struct rt2x00_dev *rt2x00dev,
const unsigned int word, u32 data)
{
- rt2x00dev->rf[word] = data;
+ BUG_ON(word < 1 || word > rt2x00dev->ops->rf_size / sizeof(u32));
+ rt2x00dev->rf[word - 1] = data;
}
/*
diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c
index 294250e294d..87a95588a8e 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -869,6 +869,9 @@ static int rtl8187b_init_hw(struct ieee80211_hw *dev)
priv->aifsn[3] = 3; /* AIFSN[AC_BE] */
rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0);
+ /* ENEDCA flag must always be set, transmit issues? */
+ rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_ENEDCA);
+
return 0;
}
@@ -1173,13 +1176,16 @@ static void rtl8187_bss_info_changed(struct ieee80211_hw *dev,
rtl818x_iowrite8(priv, &priv->map->BSSID[i],
info->bssid[i]);
+ if (priv->is_rtl8187b)
+ reg = RTL818X_MSR_ENEDCA;
+ else
+ reg = 0;
+
if (is_valid_ether_addr(info->bssid)) {
- reg = RTL818X_MSR_INFRA;
- if (priv->is_rtl8187b)
- reg |= RTL818X_MSR_ENEDCA;
+ reg |= RTL818X_MSR_INFRA;
rtl818x_iowrite8(priv, &priv->map->MSR, reg);
} else {
- reg = RTL818X_MSR_NO_LINK;
+ reg |= RTL818X_MSR_NO_LINK;
rtl818x_iowrite8(priv, &priv->map->MSR, reg);
}
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 40b07b98822..3bd3c779fff 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -698,7 +698,7 @@ int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length)
&& !mac->pass_ctrl)
return 0;
- fc = *(__le16 *)buffer;
+ fc = get_unaligned((__le16*)buffer);
need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
skb = dev_alloc_skb(length + (need_padding ? 2 : 0));
diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c
index a07580138e8..c2fd6187773 100644
--- a/drivers/net/yellowfin.c
+++ b/drivers/net/yellowfin.c
@@ -346,7 +346,7 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static int yellowfin_open(struct net_device *dev);
static void yellowfin_timer(unsigned long data);
static void yellowfin_tx_timeout(struct net_device *dev);
-static void yellowfin_init_ring(struct net_device *dev);
+static int yellowfin_init_ring(struct net_device *dev);
static int yellowfin_start_xmit(struct sk_buff *skb, struct net_device *dev);
static irqreturn_t yellowfin_interrupt(int irq, void *dev_instance);
static int yellowfin_rx(struct net_device *dev);
@@ -573,19 +573,24 @@ static int yellowfin_open(struct net_device *dev)
{
struct yellowfin_private *yp = netdev_priv(dev);
void __iomem *ioaddr = yp->base;
- int i;
+ int i, ret;
/* Reset the chip. */
iowrite32(0x80000000, ioaddr + DMACtrl);
- i = request_irq(dev->irq, &yellowfin_interrupt, IRQF_SHARED, dev->name, dev);
- if (i) return i;
+ ret = request_irq(dev->irq, &yellowfin_interrupt, IRQF_SHARED, dev->name, dev);
+ if (ret)
+ return ret;
if (yellowfin_debug > 1)
printk(KERN_DEBUG "%s: yellowfin_open() irq %d.\n",
dev->name, dev->irq);
- yellowfin_init_ring(dev);
+ ret = yellowfin_init_ring(dev);
+ if (ret) {
+ free_irq(dev->irq, dev);
+ return ret;
+ }
iowrite32(yp->rx_ring_dma, ioaddr + RxPtr);
iowrite32(yp->tx_ring_dma, ioaddr + TxPtr);
@@ -725,10 +730,10 @@ static void yellowfin_tx_timeout(struct net_device *dev)
}
/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void yellowfin_init_ring(struct net_device *dev)
+static int yellowfin_init_ring(struct net_device *dev)
{
struct yellowfin_private *yp = netdev_priv(dev);
- int i;
+ int i, j;
yp->tx_full = 0;
yp->cur_rx = yp->cur_tx = 0;
@@ -753,6 +758,11 @@ static void yellowfin_init_ring(struct net_device *dev)
yp->rx_ring[i].addr = cpu_to_le32(pci_map_single(yp->pci_dev,
skb->data, yp->rx_buf_sz, PCI_DMA_FROMDEVICE));
}
+ if (i != RX_RING_SIZE) {
+ for (j = 0; j < i; j++)
+ dev_kfree_skb(yp->rx_skbuff[j]);
+ return -ENOMEM;
+ }
yp->rx_ring[i-1].dbdma_cmd = cpu_to_le32(CMD_STOP);
yp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
@@ -769,8 +779,6 @@ static void yellowfin_init_ring(struct net_device *dev)
yp->tx_ring[--i].dbdma_cmd = cpu_to_le32(CMD_STOP | BRANCH_ALWAYS);
#else
{
- int j;
-
/* Tx ring needs a pair of descriptors, the second for the status. */
for (i = 0; i < TX_RING_SIZE; i++) {
j = 2*i;
@@ -805,7 +813,7 @@ static void yellowfin_init_ring(struct net_device *dev)
}
#endif
yp->tx_tail_desc = &yp->tx_status[0];
- return;
+ return 0;
}
static int yellowfin_start_xmit(struct sk_buff *skb, struct net_device *dev)
diff --git a/drivers/net/zorro8390.c b/drivers/net/zorro8390.c
index 37c84e3b8be..81c753a617a 100644
--- a/drivers/net/zorro8390.c
+++ b/drivers/net/zorro8390.c
@@ -120,6 +120,9 @@ static int __devinit zorro8390_init_one(struct zorro_dev *z,
for (i = ARRAY_SIZE(cards)-1; i >= 0; i--)
if (z->id == cards[i].id)
break;
+ if (i < 0)
+ return -ENODEV;
+
board = z->resource.start;
ioaddr = board+cards[i].offset;
dev = alloc_ei_netdev();
diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c
index 0f0e0b919ef..a45b0c0d574 100644
--- a/drivers/parisc/ccio-dma.c
+++ b/drivers/parisc/ccio-dma.c
@@ -70,7 +70,6 @@
#undef CCIO_COLLECT_STATS
#endif
-#include <linux/proc_fs.h>
#include <asm/runway.h> /* for proc_runway_root */
#ifdef DEBUG_CCIO_INIT
diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c
index c590974e981..d69bde6a234 100644
--- a/drivers/parisc/dino.c
+++ b/drivers/parisc/dino.c
@@ -614,7 +614,7 @@ dino_fixup_bus(struct pci_bus *bus)
dev_name(&bus->self->dev), i,
bus->self->resource[i].start,
bus->self->resource[i].end);
- pci_assign_resource(bus->self, i);
+ WARN_ON(pci_assign_resource(bus->self, i));
DBG("DEBUG %s after assign %d [0x%lx,0x%lx]\n",
dev_name(&bus->self->dev), i,
bus->self->resource[i].start,
diff --git a/drivers/parisc/eisa_eeprom.c b/drivers/parisc/eisa_eeprom.c
index 685d94e69d4..8c0b26e9b98 100644
--- a/drivers/parisc/eisa_eeprom.c
+++ b/drivers/parisc/eisa_eeprom.c
@@ -55,7 +55,7 @@ static ssize_t eisa_eeprom_read(struct file * file,
ssize_t ret;
int i;
- if (*ppos >= HPEE_MAX_LENGTH)
+ if (*ppos < 0 || *ppos >= HPEE_MAX_LENGTH)
return 0;
count = *ppos + count < HPEE_MAX_LENGTH ? count : HPEE_MAX_LENGTH - *ppos;
diff --git a/drivers/parisc/hppb.c b/drivers/parisc/hppb.c
index 13856415b43..815db175d42 100644
--- a/drivers/parisc/hppb.c
+++ b/drivers/parisc/hppb.c
@@ -62,7 +62,8 @@ static int hppb_probe(struct parisc_device *dev)
}
card = card->next;
}
- printk(KERN_INFO "Found GeckoBoa at 0x%x\n", dev->hpa.start);
+ printk(KERN_INFO "Found GeckoBoa at 0x%llx\n",
+ (unsigned long long) dev->hpa.start);
card->hpa = dev->hpa.start;
card->mmio_region.name = "HP-PB Bus";
@@ -73,8 +74,10 @@ static int hppb_probe(struct parisc_device *dev)
status = ccio_request_resource(dev, &card->mmio_region);
if(status < 0) {
- printk(KERN_ERR "%s: failed to claim HP-PB bus space (%08x, %08x)\n",
- __FILE__, card->mmio_region.start, card->mmio_region.end);
+ printk(KERN_ERR "%s: failed to claim HP-PB "
+ "bus space (0x%08llx, 0x%08llx)\n",
+ __FILE__, (unsigned long long) card->mmio_region.start,
+ (unsigned long long) card->mmio_region.end);
}
return 0;
diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c
index ede614616f8..3aeb3279c92 100644
--- a/drivers/parisc/lba_pci.c
+++ b/drivers/parisc/lba_pci.c
@@ -992,7 +992,7 @@ lba_pat_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev)
return;
io_pdc_cell = kzalloc(sizeof(pdc_pat_cell_mod_maddr_block_t), GFP_KERNEL);
- if (!pa_pdc_cell) {
+ if (!io_pdc_cell) {
kfree(pa_pdc_cell);
return;
}
diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c
index f9f9a5f1bbd..13a64bc081b 100644
--- a/drivers/parisc/pdc_stable.c
+++ b/drivers/parisc/pdc_stable.c
@@ -370,7 +370,7 @@ pdcspath_layer_read(struct pdcspath_entry *entry, char *buf)
if (!i) /* entry is not ready */
return -ENODATA;
- for (i = 0; devpath->layers[i] && (likely(i < 6)); i++)
+ for (i = 0; i < 6 && devpath->layers[i]; i++)
out += sprintf(out, "%u ", devpath->layers[i]);
out += sprintf(out, "\n");
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index a4494d78e7c..8aebe1e9d3d 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -90,11 +90,10 @@ static struct hotplug_slot_ops sn_hotplug_slot_ops = {
static DEFINE_MUTEX(sn_hotplug_mutex);
-static ssize_t path_show (struct hotplug_slot *bss_hotplug_slot,
- char *buf)
+static ssize_t path_show(struct pci_slot *pci_slot, char *buf)
{
int retval = -ENOENT;
- struct slot *slot = bss_hotplug_slot->private;
+ struct slot *slot = pci_slot->hotplug->private;
if (!slot)
return retval;
@@ -103,7 +102,7 @@ static ssize_t path_show (struct hotplug_slot *bss_hotplug_slot,
return retval;
}
-static struct hotplug_slot_attribute sn_slot_path_attr = __ATTR_RO(path);
+static struct pci_slot_attribute sn_slot_path_attr = __ATTR_RO(path);
static int sn_pci_slot_valid(struct pci_bus *pci_bus, int device)
{
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index ebc9b8dca88..2314ad7ee5f 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -1505,7 +1505,6 @@ static int domain_context_mapping_one(struct dmar_domain *domain, int segment,
}
set_bit(num, iommu->domain_ids);
- set_bit(iommu->seq_id, &domain->iommu_bmp);
iommu->domains[num] = domain;
id = num;
}
@@ -1648,6 +1647,14 @@ static int domain_context_mapped(struct pci_dev *pdev)
tmp->devfn);
}
+/* Returns a number of VTD pages, but aligned to MM page size */
+static inline unsigned long aligned_nrpages(unsigned long host_addr,
+ size_t size)
+{
+ host_addr &= ~PAGE_MASK;
+ return PAGE_ALIGN(host_addr + size) >> VTD_PAGE_SHIFT;
+}
+
static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
struct scatterlist *sg, unsigned long phys_pfn,
unsigned long nr_pages, int prot)
@@ -1675,7 +1682,7 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
uint64_t tmp;
if (!sg_res) {
- sg_res = (sg->offset + sg->length + VTD_PAGE_SIZE - 1) >> VTD_PAGE_SHIFT;
+ sg_res = aligned_nrpages(sg->offset, sg->length);
sg->dma_address = ((dma_addr_t)iov_pfn << VTD_PAGE_SHIFT) + sg->offset;
sg->dma_length = sg->length;
pteval = page_to_phys(sg_page(sg)) | prot;
@@ -2415,14 +2422,6 @@ error:
return ret;
}
-/* Returns a number of VTD pages, but aligned to MM page size */
-static inline unsigned long aligned_nrpages(unsigned long host_addr,
- size_t size)
-{
- host_addr &= ~PAGE_MASK;
- return PAGE_ALIGN(host_addr + size) >> VTD_PAGE_SHIFT;
-}
-
/* This takes a number of _MM_ pages, not VTD pages */
static struct iova *intel_alloc_iova(struct device *dev,
struct dmar_domain *domain,
@@ -2551,6 +2550,7 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
int prot = 0;
int ret;
struct intel_iommu *iommu;
+ unsigned long paddr_pfn = paddr >> PAGE_SHIFT;
BUG_ON(dir == DMA_NONE);
@@ -2585,7 +2585,7 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr,
* is not a big problem
*/
ret = domain_pfn_mapping(domain, mm_to_dma_pfn(iova->pfn_lo),
- paddr >> VTD_PAGE_SHIFT, size, prot);
+ mm_to_dma_pfn(paddr_pfn), size, prot);
if (ret)
goto error;
@@ -2875,7 +2875,7 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne
start_vpfn = mm_to_dma_pfn(iova->pfn_lo);
- ret = domain_sg_mapping(domain, start_vpfn, sglist, mm_to_dma_pfn(size), prot);
+ ret = domain_sg_mapping(domain, start_vpfn, sglist, size, prot);
if (unlikely(ret)) {
/* clear the page */
dma_pte_clear_range(domain, start_vpfn,
@@ -3408,6 +3408,7 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width)
domain->iommu_count = 0;
domain->iommu_coherency = 0;
+ domain->iommu_snooping = 0;
domain->max_addr = 0;
/* always allocate the top pgd */
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index e3a87210e94..e03fe98f061 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -598,6 +598,29 @@ int pci_iov_resource_bar(struct pci_dev *dev, int resno,
}
/**
+ * pci_sriov_resource_alignment - get resource alignment for VF BAR
+ * @dev: the PCI device
+ * @resno: the resource number
+ *
+ * Returns the alignment of the VF BAR found in the SR-IOV capability.
+ * This is not the same as the resource size which is defined as
+ * the VF BAR size multiplied by the number of VFs. The alignment
+ * is just the VF BAR size.
+ */
+int pci_sriov_resource_alignment(struct pci_dev *dev, int resno)
+{
+ struct resource tmp;
+ enum pci_bar_type type;
+ int reg = pci_iov_resource_bar(dev, resno, &type);
+
+ if (!reg)
+ return 0;
+
+ __pci_read_base(dev, type, &tmp, reg);
+ return resource_alignment(&tmp);
+}
+
+/**
* pci_restore_iov_state - restore the state of the IOV capability
* @dev: the PCI device
*/
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index d76c4c85367..f99bc7f089f 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -508,7 +508,7 @@ static int pci_restore_standard_config(struct pci_dev *pci_dev)
return error;
}
- return pci_dev->state_saved ? pci_restore_state(pci_dev) : 0;
+ return pci_restore_state(pci_dev);
}
static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index dbd0f947f49..7b70312181d 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -846,6 +846,8 @@ pci_restore_state(struct pci_dev *dev)
int i;
u32 val;
+ if (!dev->state_saved)
+ return 0;
/* PCI Express register must be restored first */
pci_restore_pcie_state(dev);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index f73bcbedf37..5ff4d25bf0e 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -243,6 +243,7 @@ extern int pci_iov_init(struct pci_dev *dev);
extern void pci_iov_release(struct pci_dev *dev);
extern int pci_iov_resource_bar(struct pci_dev *dev, int resno,
enum pci_bar_type *type);
+extern int pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
extern void pci_restore_iov_state(struct pci_dev *dev);
extern int pci_iov_bus_range(struct pci_bus *bus);
@@ -298,4 +299,16 @@ static inline int pci_ats_enabled(struct pci_dev *dev)
}
#endif /* CONFIG_PCI_IOV */
+static inline int pci_resource_alignment(struct pci_dev *dev,
+ struct resource *res)
+{
+#ifdef CONFIG_PCI_IOV
+ int resno = res - dev->resource;
+
+ if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END)
+ return pci_sriov_resource_alignment(dev, resno);
+#endif
+ return resource_alignment(res);
+}
+
#endif /* DRIVERS_PCI_H */
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index b636e245445..7c443b4583a 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -25,7 +25,7 @@
#include <linux/ioport.h>
#include <linux/cache.h>
#include <linux/slab.h>
-
+#include "pci.h"
static void pbus_assign_resources_sorted(const struct pci_bus *bus)
{
@@ -384,7 +384,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long
continue;
r_size = resource_size(r);
/* For bridges size != alignment */
- align = resource_alignment(r);
+ align = pci_resource_alignment(dev, r);
order = __ffs(align) - 20;
if (order > 11) {
dev_warn(&dev->dev, "BAR %d bad alignment %llx: "
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index b711fb7181e..88cdd1a937d 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -100,16 +100,16 @@ int pci_claim_resource(struct pci_dev *dev, int resource)
{
struct resource *res = &dev->resource[resource];
struct resource *root;
- char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge";
int err;
root = pci_find_parent_resource(dev, res);
err = -EINVAL;
if (root != NULL)
- err = insert_resource(root, res);
+ err = request_resource(root, res);
if (err) {
+ const char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge";
dev_err(&dev->dev, "BAR %d: %s of %s %pR\n",
resource,
root ? "address space collision on" :
@@ -144,7 +144,7 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
size = resource_size(res);
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
- align = resource_alignment(res);
+ align = pci_resource_alignment(dev, res);
/* First, try exact prefetching match.. */
ret = pci_bus_alloc_resource(bus, res, size, align, min,
@@ -178,7 +178,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
struct pci_bus *bus;
int ret;
- align = resource_alignment(res);
+ align = pci_resource_alignment(dev, res);
if (!align) {
dev_info(&dev->dev, "BAR %d: can't allocate resource (bogus "
"alignment) %pR flags %#lx\n",
@@ -259,7 +259,7 @@ void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
if (!(r->flags) || r->parent)
continue;
- r_align = resource_alignment(r);
+ r_align = pci_resource_alignment(dev, r);
if (!r_align) {
dev_warn(&dev->dev, "BAR %d: bogus alignment "
"%pR flags %#lx\n",
@@ -271,7 +271,7 @@ void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
struct resource_list *ln = list->next;
if (ln)
- align = resource_alignment(ln->res);
+ align = pci_resource_alignment(ln->dev, ln->res);
if (r_align > align) {
tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 46dad12f952..77c6097ced8 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -277,31 +277,6 @@ config THINKPAD_ACPI_UNSAFE_LEDS
Say N here, unless you are building a kernel for your own
use, and need to control the important firmware LEDs.
-config THINKPAD_ACPI_DOCK
- bool "Legacy Docking Station Support"
- depends on THINKPAD_ACPI
- depends on ACPI_DOCK=n
- default n
- ---help---
- Allows the thinkpad_acpi driver to handle docking station events.
- This support was made obsolete by the generic ACPI docking station
- support (CONFIG_ACPI_DOCK). It will allow locking and removing the
- laptop from the docking station, but will not properly connect PCI
- devices.
-
- If you are not sure, say N here.
-
-config THINKPAD_ACPI_BAY
- bool "Legacy Removable Bay Support"
- depends on THINKPAD_ACPI
- default y
- ---help---
- Allows the thinkpad_acpi driver to handle removable bays. It will
- electrically disable the device in the bay, and also generate
- notifications when the bay lever is ejected or inserted.
-
- If you are not sure, say Y here.
-
config THINKPAD_ACPI_VIDEO
bool "Video output control support"
depends on THINKPAD_ACPI
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index ec560f16d72..222ffb892f2 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -143,6 +143,7 @@ struct eeepc_hotk {
struct rfkill *bluetooth_rfkill;
struct rfkill *wwan3g_rfkill;
struct hotplug_slot *hotplug_slot;
+ struct work_struct hotplug_work;
};
/* The actual device the driver binds to */
@@ -660,7 +661,7 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
return 0;
}
-static void eeepc_rfkill_hotplug(void)
+static void eeepc_hotplug_work(struct work_struct *work)
{
struct pci_dev *dev;
struct pci_bus *bus = pci_find_bus(0, 1);
@@ -701,7 +702,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
if (event != ACPI_NOTIFY_BUS_CHECK)
return;
- eeepc_rfkill_hotplug();
+ schedule_work(&ehotk->hotplug_work);
}
static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
@@ -892,7 +893,7 @@ static int eeepc_hotk_resume(struct acpi_device *device)
rfkill_set_sw_state(ehotk->wlan_rfkill, wlan != 1);
- eeepc_rfkill_hotplug();
+ schedule_work(&ehotk->hotplug_work);
}
if (ehotk->bluetooth_rfkill)
@@ -1093,6 +1094,8 @@ static int eeepc_rfkill_init(struct device *dev)
{
int result = 0;
+ INIT_WORK(&ehotk->hotplug_work, eeepc_hotplug_work);
+
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index ca508564a18..a2ad53e1587 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -520,11 +520,13 @@ static int hp_wmi_resume_handler(struct platform_device *device)
* the input layer will only actually pass it on if the state
* changed.
*/
-
- input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
- input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
- hp_wmi_tablet_state());
- input_sync(hp_wmi_input_dev);
+ if (hp_wmi_input_dev) {
+ input_report_switch(hp_wmi_input_dev, SW_DOCK,
+ hp_wmi_dock_state());
+ input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
+ hp_wmi_tablet_state());
+ input_sync(hp_wmi_input_dev);
+ }
return 0;
}
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index a463fd72c49..e8560085250 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -239,12 +239,6 @@ struct ibm_init_struct {
};
static struct {
-#ifdef CONFIG_THINKPAD_ACPI_BAY
- u32 bay_status:1;
- u32 bay_eject:1;
- u32 bay_status2:1;
- u32 bay_eject2:1;
-#endif
u32 bluetooth:1;
u32 hotkey:1;
u32 hotkey_mask:1;
@@ -589,18 +583,6 @@ static int acpi_ec_write(int i, u8 v)
return 1;
}
-#if defined(CONFIG_THINKPAD_ACPI_DOCK) || defined(CONFIG_THINKPAD_ACPI_BAY)
-static int _sta(acpi_handle handle)
-{
- int status;
-
- if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
- status = 0;
-
- return status;
-}
-#endif
-
static int issue_thinkpad_cmos_command(int cmos_cmd)
{
if (!cmos_handle)
@@ -784,6 +766,8 @@ static int dispatch_procfs_write(struct file *file,
if (!ibm || !ibm->write)
return -EINVAL;
+ if (count > PAGE_SIZE - 2)
+ return -EINVAL;
kernbuf = kmalloc(count + 2, GFP_KERNEL);
if (!kernbuf)
@@ -4442,293 +4426,6 @@ static struct ibm_struct light_driver_data = {
};
/*************************************************************************
- * Dock subdriver
- */
-
-#ifdef CONFIG_THINKPAD_ACPI_DOCK
-
-static void dock_notify(struct ibm_struct *ibm, u32 event);
-static int dock_read(char *p);
-static int dock_write(char *buf);
-
-TPACPI_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */
- "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
- "\\_SB.PCI0.PCI1.DOCK", /* all others */
- "\\_SB.PCI.ISA.SLCE", /* 570 */
- ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
-
-/* don't list other alternatives as we install a notify handler on the 570 */
-TPACPI_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */
-
-static const struct acpi_device_id ibm_pci_device_ids[] = {
- {PCI_ROOT_HID_STRING, 0},
- {"", 0},
-};
-
-static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = {
- {
- .notify = dock_notify,
- .handle = &dock_handle,
- .type = ACPI_SYSTEM_NOTIFY,
- },
- {
- /* THIS ONE MUST NEVER BE USED FOR DRIVER AUTOLOADING.
- * We just use it to get notifications of dock hotplug
- * in very old thinkpads */
- .hid = ibm_pci_device_ids,
- .notify = dock_notify,
- .handle = &pci_handle,
- .type = ACPI_SYSTEM_NOTIFY,
- },
-};
-
-static struct ibm_struct dock_driver_data[2] = {
- {
- .name = "dock",
- .read = dock_read,
- .write = dock_write,
- .acpi = &ibm_dock_acpidriver[0],
- },
- {
- .name = "dock",
- .acpi = &ibm_dock_acpidriver[1],
- },
-};
-
-#define dock_docked() (_sta(dock_handle) & 1)
-
-static int __init dock_init(struct ibm_init_struct *iibm)
-{
- vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver\n");
-
- TPACPI_ACPIHANDLE_INIT(dock);
-
- vdbg_printk(TPACPI_DBG_INIT, "dock is %s\n",
- str_supported(dock_handle != NULL));
-
- return (dock_handle)? 0 : 1;
-}
-
-static int __init dock_init2(struct ibm_init_struct *iibm)
-{
- int dock2_needed;
-
- vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver part 2\n");
-
- if (dock_driver_data[0].flags.acpi_driver_registered &&
- dock_driver_data[0].flags.acpi_notify_installed) {
- TPACPI_ACPIHANDLE_INIT(pci);
- dock2_needed = (pci_handle != NULL);
- vdbg_printk(TPACPI_DBG_INIT,
- "dock PCI handler for the TP 570 is %s\n",
- str_supported(dock2_needed));
- } else {
- vdbg_printk(TPACPI_DBG_INIT,
- "dock subdriver part 2 not required\n");
- dock2_needed = 0;
- }
-
- return (dock2_needed)? 0 : 1;
-}
-
-static void dock_notify(struct ibm_struct *ibm, u32 event)
-{
- int docked = dock_docked();
- int pci = ibm->acpi->hid && ibm->acpi->device &&
- acpi_match_device_ids(ibm->acpi->device, ibm_pci_device_ids);
- int data;
-
- if (event == 1 && !pci) /* 570 */
- data = 1; /* button */
- else if (event == 1 && pci) /* 570 */
- data = 3; /* dock */
- else if (event == 3 && docked)
- data = 1; /* button */
- else if (event == 3 && !docked)
- data = 2; /* undock */
- else if (event == 0 && docked)
- data = 3; /* dock */
- else {
- printk(TPACPI_ERR "unknown dock event %d, status %d\n",
- event, _sta(dock_handle));
- data = 0; /* unknown */
- }
- acpi_bus_generate_proc_event(ibm->acpi->device, event, data);
- acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
- dev_name(&ibm->acpi->device->dev),
- event, data);
-}
-
-static int dock_read(char *p)
-{
- int len = 0;
- int docked = dock_docked();
-
- if (!dock_handle)
- len += sprintf(p + len, "status:\t\tnot supported\n");
- else if (!docked)
- len += sprintf(p + len, "status:\t\tundocked\n");
- else {
- len += sprintf(p + len, "status:\t\tdocked\n");
- len += sprintf(p + len, "commands:\tdock, undock\n");
- }
-
- return len;
-}
-
-static int dock_write(char *buf)
-{
- char *cmd;
-
- if (!dock_docked())
- return -ENODEV;
-
- while ((cmd = next_cmd(&buf))) {
- if (strlencmp(cmd, "undock") == 0) {
- if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
- !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
- return -EIO;
- } else if (strlencmp(cmd, "dock") == 0) {
- if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
- return -EIO;
- } else
- return -EINVAL;
- }
-
- return 0;
-}
-
-#endif /* CONFIG_THINKPAD_ACPI_DOCK */
-
-/*************************************************************************
- * Bay subdriver
- */
-
-#ifdef CONFIG_THINKPAD_ACPI_BAY
-
-TPACPI_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */
- "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */
- "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */
- "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */
- ); /* A21e, R30, R31 */
-TPACPI_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */
- "_EJ0", /* all others */
- ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
-TPACPI_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
- "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */
- ); /* all others */
-TPACPI_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */
- "_EJ0", /* 770x */
- ); /* all others */
-
-static int __init bay_init(struct ibm_init_struct *iibm)
-{
- vdbg_printk(TPACPI_DBG_INIT, "initializing bay subdriver\n");
-
- TPACPI_ACPIHANDLE_INIT(bay);
- if (bay_handle)
- TPACPI_ACPIHANDLE_INIT(bay_ej);
- TPACPI_ACPIHANDLE_INIT(bay2);
- if (bay2_handle)
- TPACPI_ACPIHANDLE_INIT(bay2_ej);
-
- tp_features.bay_status = bay_handle &&
- acpi_evalf(bay_handle, NULL, "_STA", "qv");
- tp_features.bay_status2 = bay2_handle &&
- acpi_evalf(bay2_handle, NULL, "_STA", "qv");
-
- tp_features.bay_eject = bay_handle && bay_ej_handle &&
- (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
- tp_features.bay_eject2 = bay2_handle && bay2_ej_handle &&
- (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
-
- vdbg_printk(TPACPI_DBG_INIT,
- "bay 1: status %s, eject %s; bay 2: status %s, eject %s\n",
- str_supported(tp_features.bay_status),
- str_supported(tp_features.bay_eject),
- str_supported(tp_features.bay_status2),
- str_supported(tp_features.bay_eject2));
-
- return (tp_features.bay_status || tp_features.bay_eject ||
- tp_features.bay_status2 || tp_features.bay_eject2)? 0 : 1;
-}
-
-static void bay_notify(struct ibm_struct *ibm, u32 event)
-{
- acpi_bus_generate_proc_event(ibm->acpi->device, event, 0);
- acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
- dev_name(&ibm->acpi->device->dev),
- event, 0);
-}
-
-#define bay_occupied(b) (_sta(b##_handle) & 1)
-
-static int bay_read(char *p)
-{
- int len = 0;
- int occupied = bay_occupied(bay);
- int occupied2 = bay_occupied(bay2);
- int eject, eject2;
-
- len += sprintf(p + len, "status:\t\t%s\n",
- tp_features.bay_status ?
- (occupied ? "occupied" : "unoccupied") :
- "not supported");
- if (tp_features.bay_status2)
- len += sprintf(p + len, "status2:\t%s\n", occupied2 ?
- "occupied" : "unoccupied");
-
- eject = tp_features.bay_eject && occupied;
- eject2 = tp_features.bay_eject2 && occupied2;
-
- if (eject && eject2)
- len += sprintf(p + len, "commands:\teject, eject2\n");
- else if (eject)
- len += sprintf(p + len, "commands:\teject\n");
- else if (eject2)
- len += sprintf(p + len, "commands:\teject2\n");
-
- return len;
-}
-
-static int bay_write(char *buf)
-{
- char *cmd;
-
- if (!tp_features.bay_eject && !tp_features.bay_eject2)
- return -ENODEV;
-
- while ((cmd = next_cmd(&buf))) {
- if (tp_features.bay_eject && strlencmp(cmd, "eject") == 0) {
- if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
- return -EIO;
- } else if (tp_features.bay_eject2 &&
- strlencmp(cmd, "eject2") == 0) {
- if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1))
- return -EIO;
- } else
- return -EINVAL;
- }
-
- return 0;
-}
-
-static struct tp_acpi_drv_struct ibm_bay_acpidriver = {
- .notify = bay_notify,
- .handle = &bay_handle,
- .type = ACPI_SYSTEM_NOTIFY,
-};
-
-static struct ibm_struct bay_driver_data = {
- .name = "bay",
- .read = bay_read,
- .write = bay_write,
- .acpi = &ibm_bay_acpidriver,
-};
-
-#endif /* CONFIG_THINKPAD_ACPI_BAY */
-
-/*************************************************************************
* CMOS subdriver
*/
@@ -5945,14 +5642,48 @@ static struct backlight_ops ibm_backlight_data = {
/* --------------------------------------------------------------------- */
+/*
+ * These are only useful for models that have only one possibility
+ * of GPU. If the BIOS model handles both ATI and Intel, don't use
+ * these quirks.
+ */
+#define TPACPI_BRGHT_Q_NOEC 0x0001 /* Must NOT use EC HBRV */
+#define TPACPI_BRGHT_Q_EC 0x0002 /* Should or must use EC HBRV */
+#define TPACPI_BRGHT_Q_ASK 0x8000 /* Ask for user report */
+
+static const struct tpacpi_quirk brightness_quirk_table[] __initconst = {
+ /* Models with ATI GPUs known to require ECNVRAM mode */
+ TPACPI_Q_IBM('1', 'Y', TPACPI_BRGHT_Q_EC), /* T43/p ATI */
+
+ /* Models with ATI GPUs (waiting confirmation) */
+ TPACPI_Q_IBM('1', 'R', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC),
+ TPACPI_Q_IBM('1', 'Q', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC),
+ TPACPI_Q_IBM('7', '6', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC),
+ TPACPI_Q_IBM('7', '8', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC),
+
+ /* Models with Intel Extreme Graphics 2 (waiting confirmation) */
+ TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC),
+ TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC),
+ TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC),
+
+ /* Models with Intel GMA900 */
+ TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC), /* T43, R52 */
+ TPACPI_Q_IBM('7', '4', TPACPI_BRGHT_Q_NOEC), /* X41 */
+ TPACPI_Q_IBM('7', '5', TPACPI_BRGHT_Q_NOEC), /* X41 Tablet */
+};
+
static int __init brightness_init(struct ibm_init_struct *iibm)
{
int b;
+ unsigned long quirks;
vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n");
mutex_init(&brightness_mutex);
+ quirks = tpacpi_check_quirks(brightness_quirk_table,
+ ARRAY_SIZE(brightness_quirk_table));
+
/*
* We always attempt to detect acpi support, so as to switch
* Lenovo Vista BIOS to ACPI brightness mode even if we are not
@@ -6009,23 +5740,13 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
/* TPACPI_BRGHT_MODE_AUTO not implemented yet, just use default */
if (brightness_mode == TPACPI_BRGHT_MODE_AUTO ||
brightness_mode == TPACPI_BRGHT_MODE_MAX) {
- if (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) {
- /*
- * IBM models that define HBRV probably have
- * EC-based backlight level control
- */
- if (acpi_evalf(ec_handle, NULL, "HBRV", "qd"))
- /* T40-T43, R50-R52, R50e, R51e, X31-X41 */
- brightness_mode = TPACPI_BRGHT_MODE_ECNVRAM;
- else
- /* all other IBM ThinkPads */
- brightness_mode = TPACPI_BRGHT_MODE_UCMS_STEP;
- } else
- /* All Lenovo ThinkPads */
+ if (quirks & TPACPI_BRGHT_Q_EC)
+ brightness_mode = TPACPI_BRGHT_MODE_ECNVRAM;
+ else
brightness_mode = TPACPI_BRGHT_MODE_UCMS_STEP;
dbg_printk(TPACPI_DBG_BRGHT,
- "selected brightness_mode=%d\n",
+ "driver auto-selected brightness_mode=%d\n",
brightness_mode);
}
@@ -6052,6 +5773,15 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
"brightness is supported\n");
+ if (quirks & TPACPI_BRGHT_Q_ASK) {
+ printk(TPACPI_NOTICE
+ "brightness: will use unverified default: "
+ "brightness_mode=%d\n", brightness_mode);
+ printk(TPACPI_NOTICE
+ "brightness: please report to %s whether it works well "
+ "or not on your ThinkPad\n", TPACPI_MAIL);
+ }
+
ibm_backlight_device->props.max_brightness =
(tp_features.bright_16levels)? 15 : 7;
ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK;
@@ -7854,22 +7584,6 @@ static struct ibm_init_struct ibms_init[] __initdata = {
.init = light_init,
.data = &light_driver_data,
},
-#ifdef CONFIG_THINKPAD_ACPI_DOCK
- {
- .init = dock_init,
- .data = &dock_driver_data[0],
- },
- {
- .init = dock_init2,
- .data = &dock_driver_data[1],
- },
-#endif
-#ifdef CONFIG_THINKPAD_ACPI_BAY
- {
- .init = bay_init,
- .data = &bay_driver_data,
- },
-#endif
{
.init = cmos_init,
.data = &cmos_driver_data,
@@ -7968,12 +7682,6 @@ TPACPI_PARAM(hotkey);
TPACPI_PARAM(bluetooth);
TPACPI_PARAM(video);
TPACPI_PARAM(light);
-#ifdef CONFIG_THINKPAD_ACPI_DOCK
-TPACPI_PARAM(dock);
-#endif
-#ifdef CONFIG_THINKPAD_ACPI_BAY
-TPACPI_PARAM(bay);
-#endif /* CONFIG_THINKPAD_ACPI_BAY */
TPACPI_PARAM(cmos);
TPACPI_PARAM(led);
TPACPI_PARAM(beep);
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 81d31ea507d..51c0a8bee41 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -335,6 +335,7 @@ static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
if (hci_result != HCI_SUCCESS) {
/* Can't do anything useful */
mutex_unlock(&dev->mutex);
+ return;
}
new_rfk_state = value;
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 043b208d971..f215a591919 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -270,7 +270,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
acpi_status status;
struct acpi_object_list input;
union acpi_object params[3];
- char method[4] = "WM";
+ char method[5] = "WM";
if (!find_guid(guid_string, &wblock))
return AE_ERROR;
@@ -328,8 +328,8 @@ struct acpi_buffer *out)
acpi_status status, wc_status = AE_ERROR;
struct acpi_object_list input, wc_input;
union acpi_object wc_params[1], wq_params[1];
- char method[4];
- char wc_method[4] = "WC";
+ char method[5];
+ char wc_method[5] = "WC";
if (!guid_string || !out)
return AE_BAD_PARAMETER;
@@ -410,7 +410,7 @@ const struct acpi_buffer *in)
acpi_handle handle;
struct acpi_object_list input;
union acpi_object params[2];
- char method[4] = "WS";
+ char method[5] = "WS";
if (!guid_string || !in)
return AE_BAD_DATA;
diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c
index ac8cc8cea1e..fea17e7805e 100644
--- a/drivers/pps/pps.c
+++ b/drivers/pps/pps.c
@@ -244,7 +244,7 @@ int pps_register_cdev(struct pps_device *pps)
}
pps->dev = device_create(pps_class, pps->info.dev, pps->devno, NULL,
"pps%d", pps->id);
- if (err)
+ if (IS_ERR(pps->dev))
goto del_cdev;
dev_set_drvdata(pps->dev, pps);
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 74983666865..3f62dd50bbb 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -2135,9 +2135,9 @@ static int dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
struct dasd_device *base;
block = bdev->bd_disk->private_data;
- base = block->base;
if (!block)
return -ENODEV;
+ base = block->base;
if (!base->discipline ||
!base->discipline->fill_geometry)
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 3c57c1a18bb..d593bc76afe 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -772,10 +772,8 @@ static struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch)
cdev = io_subchannel_allocate_dev(sch);
if (!IS_ERR(cdev)) {
ret = io_subchannel_initialize_dev(sch, cdev);
- if (ret) {
- kfree(cdev);
+ if (ret)
cdev = ERR_PTR(ret);
- }
}
return cdev;
}
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 8030e25152f..c75d6f35cb5 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -553,40 +553,35 @@ static void _zfcp_erp_unit_reopen_all(struct zfcp_port *port, int clear,
_zfcp_erp_unit_reopen(unit, clear, id, ref);
}
-static void zfcp_erp_strategy_followup_actions(struct zfcp_erp_action *act)
+static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act)
{
- struct zfcp_adapter *adapter = act->adapter;
- struct zfcp_port *port = act->port;
- struct zfcp_unit *unit = act->unit;
- u32 status = act->status;
-
- /* initiate follow-up actions depending on success of finished action */
switch (act->action) {
-
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
- if (status == ZFCP_ERP_SUCCEEDED)
- _zfcp_erp_port_reopen_all(adapter, 0, "ersfa_1", NULL);
- else
- _zfcp_erp_adapter_reopen(adapter, 0, "ersfa_2", NULL);
+ _zfcp_erp_adapter_reopen(act->adapter, 0, "ersff_1", NULL);
break;
-
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
- if (status == ZFCP_ERP_SUCCEEDED)
- _zfcp_erp_port_reopen(port, 0, "ersfa_3", NULL);
- else
- _zfcp_erp_adapter_reopen(adapter, 0, "ersfa_4", NULL);
+ _zfcp_erp_port_forced_reopen(act->port, 0, "ersff_2", NULL);
break;
-
case ZFCP_ERP_ACTION_REOPEN_PORT:
- if (status == ZFCP_ERP_SUCCEEDED)
- _zfcp_erp_unit_reopen_all(port, 0, "ersfa_5", NULL);
- else
- _zfcp_erp_port_forced_reopen(port, 0, "ersfa_6", NULL);
+ _zfcp_erp_port_reopen(act->port, 0, "ersff_3", NULL);
break;
-
case ZFCP_ERP_ACTION_REOPEN_UNIT:
- if (status != ZFCP_ERP_SUCCEEDED)
- _zfcp_erp_port_reopen(unit->port, 0, "ersfa_7", NULL);
+ _zfcp_erp_unit_reopen(act->unit, 0, "ersff_4", NULL);
+ break;
+ }
+}
+
+static void zfcp_erp_strategy_followup_success(struct zfcp_erp_action *act)
+{
+ switch (act->action) {
+ case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+ _zfcp_erp_port_reopen_all(act->adapter, 0, "ersfs_1", NULL);
+ break;
+ case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+ _zfcp_erp_port_reopen(act->port, 0, "ersfs_2", NULL);
+ break;
+ case ZFCP_ERP_ACTION_REOPEN_PORT:
+ _zfcp_erp_unit_reopen_all(act->port, 0, "ersfs_3", NULL);
break;
}
}
@@ -801,7 +796,7 @@ static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action)
return ZFCP_ERP_FAILED;
case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
- if (status & ZFCP_STATUS_PORT_PHYS_OPEN)
+ if (!(status & ZFCP_STATUS_PORT_PHYS_OPEN))
return ZFCP_ERP_SUCCEEDED;
}
return ZFCP_ERP_FAILED;
@@ -853,11 +848,17 @@ void zfcp_erp_port_strategy_open_lookup(struct work_struct *work)
gid_pn_work);
retval = zfcp_fc_ns_gid_pn(&port->erp_action);
- if (retval == -ENOMEM)
- zfcp_erp_notify(&port->erp_action, ZFCP_ERP_NOMEM);
- port->erp_action.step = ZFCP_ERP_STEP_NAMESERVER_LOOKUP;
- if (retval)
- zfcp_erp_notify(&port->erp_action, ZFCP_ERP_FAILED);
+ if (!retval) {
+ port->erp_action.step = ZFCP_ERP_STEP_NAMESERVER_LOOKUP;
+ goto out;
+ }
+ if (retval == -ENOMEM) {
+ zfcp_erp_notify(&port->erp_action, ZFCP_STATUS_ERP_LOWMEM);
+ goto out;
+ }
+ /* all other error condtions */
+ zfcp_erp_notify(&port->erp_action, 0);
+out:
zfcp_port_put(port);
}
@@ -1289,7 +1290,10 @@ static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
retval = zfcp_erp_strategy_statechange(erp_action, retval);
if (retval == ZFCP_ERP_EXIT)
goto unlock;
- zfcp_erp_strategy_followup_actions(erp_action);
+ if (retval == ZFCP_ERP_SUCCEEDED)
+ zfcp_erp_strategy_followup_success(erp_action);
+ if (retval == ZFCP_ERP_FAILED)
+ zfcp_erp_strategy_followup_failed(erp_action);
unlock:
write_unlock(&adapter->erp_lock);
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
index 2f0705d76b7..47daebfa7e5 100644
--- a/drivers/s390/scsi/zfcp_fc.c
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -79,11 +79,9 @@ static int zfcp_wka_port_get(struct zfcp_wka_port *wka_port)
mutex_unlock(&wka_port->mutex);
- wait_event_timeout(
- wka_port->completion_wq,
- wka_port->status == ZFCP_WKA_PORT_ONLINE ||
- wka_port->status == ZFCP_WKA_PORT_OFFLINE,
- HZ >> 1);
+ wait_event(wka_port->completion_wq,
+ wka_port->status == ZFCP_WKA_PORT_ONLINE ||
+ wka_port->status == ZFCP_WKA_PORT_OFFLINE);
if (wka_port->status == ZFCP_WKA_PORT_ONLINE) {
atomic_inc(&wka_port->refcount);
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index c57658f3d34..47795fbf081 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -670,8 +670,11 @@ static int zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter)
zfcp_fsf_sbal_check(adapter), 5 * HZ);
if (ret > 0)
return 0;
- if (!ret)
+ if (!ret) {
atomic_inc(&adapter->qdio_outb_full);
+ /* assume hanging outbound queue, try queue recovery */
+ zfcp_erp_adapter_reopen(adapter, 0, "fsrsg_1", NULL);
+ }
spin_lock_bh(&adapter->req_q_lock);
return -EIO;
@@ -722,7 +725,7 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_adapter *adapter,
req = zfcp_fsf_alloc_qtcb(pool);
if (unlikely(!req))
- return ERR_PTR(-EIO);
+ return ERR_PTR(-ENOMEM);
if (adapter->req_no == 0)
adapter->req_no++;
@@ -1010,6 +1013,23 @@ skip_fsfstatus:
send_ct->handler(send_ct->handler_data);
}
+static void zfcp_fsf_setup_ct_els_unchained(struct qdio_buffer_element *sbale,
+ struct scatterlist *sg_req,
+ struct scatterlist *sg_resp)
+{
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
+ sbale[2].addr = sg_virt(sg_req);
+ sbale[2].length = sg_req->length;
+ sbale[3].addr = sg_virt(sg_resp);
+ sbale[3].length = sg_resp->length;
+ sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
+}
+
+static int zfcp_fsf_one_sbal(struct scatterlist *sg)
+{
+ return sg_is_last(sg) && sg->length <= PAGE_SIZE;
+}
+
static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
struct scatterlist *sg_req,
struct scatterlist *sg_resp,
@@ -1020,30 +1040,30 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
int bytes;
if (!(feat & FSF_FEATURE_ELS_CT_CHAINED_SBALS)) {
- if (sg_req->length > PAGE_SIZE || sg_resp->length > PAGE_SIZE ||
- !sg_is_last(sg_req) || !sg_is_last(sg_resp))
+ if (!zfcp_fsf_one_sbal(sg_req) || !zfcp_fsf_one_sbal(sg_resp))
return -EOPNOTSUPP;
- sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
- sbale[2].addr = sg_virt(sg_req);
- sbale[2].length = sg_req->length;
- sbale[3].addr = sg_virt(sg_resp);
- sbale[3].length = sg_resp->length;
- sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
+ zfcp_fsf_setup_ct_els_unchained(sbale, sg_req, sg_resp);
+ return 0;
+ }
+
+ /* use single, unchained SBAL if it can hold the request */
+ if (zfcp_fsf_one_sbal(sg_req) && zfcp_fsf_one_sbal(sg_resp)) {
+ zfcp_fsf_setup_ct_els_unchained(sbale, sg_req, sg_resp);
return 0;
}
bytes = zfcp_qdio_sbals_from_sg(req, SBAL_FLAGS0_TYPE_WRITE_READ,
sg_req, max_sbals);
if (bytes <= 0)
- return -ENOMEM;
+ return -EIO;
req->qtcb->bottom.support.req_buf_length = bytes;
req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
bytes = zfcp_qdio_sbals_from_sg(req, SBAL_FLAGS0_TYPE_WRITE_READ,
sg_resp, max_sbals);
if (bytes <= 0)
- return -ENOMEM;
+ return -EIO;
req->qtcb->bottom.support.resp_buf_length = bytes;
return 0;
@@ -1607,10 +1627,10 @@ static void zfcp_fsf_open_wka_port_handler(struct zfcp_fsf_req *req)
case FSF_ACCESS_DENIED:
wka_port->status = ZFCP_WKA_PORT_OFFLINE;
break;
- case FSF_PORT_ALREADY_OPEN:
- break;
case FSF_GOOD:
wka_port->handle = header->port_handle;
+ /* fall through */
+ case FSF_PORT_ALREADY_OPEN:
wka_port->status = ZFCP_WKA_PORT_ONLINE;
}
out:
@@ -1731,15 +1751,16 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req)
zfcp_fsf_access_denied_port(req, port);
break;
case FSF_PORT_BOXED:
- zfcp_erp_port_boxed(port, "fscpph2", req);
- req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY;
/* can't use generic zfcp_erp_modify_port_status because
* ZFCP_STATUS_COMMON_OPEN must not be reset for the port */
atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
list_for_each_entry(unit, &port->unit_list_head, list)
atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
&unit->status);
+ zfcp_erp_port_boxed(port, "fscpph2", req);
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+ ZFCP_STATUS_FSFREQ_RETRY;
+
break;
case FSF_ADAPTER_STATUS_AVAILABLE:
switch (header->fsf_status_qual.word[0]) {
@@ -2541,7 +2562,6 @@ struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter,
bytes = zfcp_qdio_sbals_from_sg(req, direction, fsf_cfdc->sg,
FSF_MAX_SBALS_PER_REQ);
if (bytes != ZFCP_CFDC_MAX_SIZE) {
- retval = -ENOMEM;
zfcp_fsf_req_free(req);
goto out;
}
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 967ede73f4c..6925a178468 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -167,20 +167,21 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
struct zfcp_unit *unit = scpnt->device->hostdata;
struct zfcp_fsf_req *old_req, *abrt_req;
unsigned long flags;
- unsigned long old_req_id = (unsigned long) scpnt->host_scribble;
+ unsigned long old_reqid = (unsigned long) scpnt->host_scribble;
int retval = SUCCESS;
int retry = 3;
+ char *dbf_tag;
/* avoid race condition between late normal completion and abort */
write_lock_irqsave(&adapter->abort_lock, flags);
spin_lock(&adapter->req_list_lock);
- old_req = zfcp_reqlist_find(adapter, old_req_id);
+ old_req = zfcp_reqlist_find(adapter, old_reqid);
spin_unlock(&adapter->req_list_lock);
if (!old_req) {
write_unlock_irqrestore(&adapter->abort_lock, flags);
zfcp_scsi_dbf_event_abort("lte1", adapter, scpnt, NULL,
- old_req_id);
+ old_reqid);
return FAILED; /* completion could be in progress */
}
old_req->data = NULL;
@@ -189,7 +190,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
write_unlock_irqrestore(&adapter->abort_lock, flags);
while (retry--) {
- abrt_req = zfcp_fsf_abort_fcp_command(old_req_id, unit);
+ abrt_req = zfcp_fsf_abort_fcp_command(old_reqid, unit);
if (abrt_req)
break;
@@ -197,7 +198,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
if (!(atomic_read(&adapter->status) &
ZFCP_STATUS_COMMON_RUNNING)) {
zfcp_scsi_dbf_event_abort("nres", adapter, scpnt, NULL,
- old_req_id);
+ old_reqid);
return SUCCESS;
}
}
@@ -208,13 +209,14 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
abrt_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
if (abrt_req->status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED)
- zfcp_scsi_dbf_event_abort("okay", adapter, scpnt, abrt_req, 0);
+ dbf_tag = "okay";
else if (abrt_req->status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED)
- zfcp_scsi_dbf_event_abort("lte2", adapter, scpnt, abrt_req, 0);
+ dbf_tag = "lte2";
else {
- zfcp_scsi_dbf_event_abort("fail", adapter, scpnt, abrt_req, 0);
+ dbf_tag = "fail";
retval = FAILED;
}
+ zfcp_scsi_dbf_event_abort(dbf_tag, adapter, scpnt, abrt_req, old_reqid);
zfcp_fsf_req_free(abrt_req);
return retval;
}
@@ -534,6 +536,9 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port)
struct fc_rport_identifiers ids;
struct fc_rport *rport;
+ if (port->rport)
+ return;
+
ids.node_name = port->wwnn;
ids.port_name = port->wwpn;
ids.port_id = port->d_id;
@@ -557,8 +562,10 @@ static void zfcp_scsi_rport_block(struct zfcp_port *port)
{
struct fc_rport *rport = port->rport;
- if (rport)
+ if (rport) {
fc_remote_port_delete(rport);
+ port->rport = NULL;
+ }
}
void zfcp_scsi_schedule_rport_register(struct zfcp_port *port)
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
index 3e51e64d110..0fe5cce818c 100644
--- a/drivers/s390/scsi/zfcp_sysfs.c
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -494,9 +494,14 @@ static ssize_t zfcp_sysfs_adapter_q_full_show(struct device *dev,
struct Scsi_Host *scsi_host = class_to_shost(dev);
struct zfcp_adapter *adapter =
(struct zfcp_adapter *) scsi_host->hostdata[0];
+ u64 util;
+
+ spin_lock_bh(&adapter->qdio_stat_lock);
+ util = adapter->req_q_util;
+ spin_unlock_bh(&adapter->qdio_stat_lock);
return sprintf(buf, "%d %llu\n", atomic_read(&adapter->qdio_outb_full),
- (unsigned long long)adapter->req_q_util);
+ (unsigned long long)util);
}
static DEVICE_ATTR(queue_full, S_IRUGO, zfcp_sysfs_adapter_q_full_show, NULL);
diff --git a/drivers/sbus/char/bbc_envctrl.c b/drivers/sbus/char/bbc_envctrl.c
index 15dab96d05e..7c815d3327f 100644
--- a/drivers/sbus/char/bbc_envctrl.c
+++ b/drivers/sbus/char/bbc_envctrl.c
@@ -537,8 +537,12 @@ int bbc_envctrl_init(struct bbc_i2c_bus *bp)
}
if (temp_index != 0 && fan_index != 0) {
kenvctrld_task = kthread_run(kenvctrld, NULL, "kenvctrld");
- if (IS_ERR(kenvctrld_task))
- return PTR_ERR(kenvctrld_task);
+ if (IS_ERR(kenvctrld_task)) {
+ int err = PTR_ERR(kenvctrld_task);
+
+ kenvctrld_task = NULL;
+ return err;
+ }
}
return 0;
@@ -561,7 +565,8 @@ void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp)
struct bbc_cpu_temperature *tp, *tpos;
struct bbc_fan_control *fp, *fpos;
- kthread_stop(kenvctrld_task);
+ if (kenvctrld_task)
+ kthread_stop(kenvctrld_task);
list_for_each_entry_safe(tp, tpos, &bp->temps, bp_list) {
list_del(&tp->bp_list);
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 2bc22be5f84..145ab9ba55e 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -415,9 +415,9 @@ static void fc_exch_timeout(struct work_struct *work)
e_stat = ep->esb_stat;
if (e_stat & ESB_ST_COMPLETE) {
ep->esb_stat = e_stat & ~ESB_ST_REC_QUAL;
+ spin_unlock_bh(&ep->ex_lock);
if (e_stat & ESB_ST_REC_QUAL)
fc_exch_rrq(ep);
- spin_unlock_bh(&ep->ex_lock);
goto done;
} else {
resp = ep->resp;
@@ -1624,14 +1624,14 @@ static void fc_exch_rrq(struct fc_exch *ep)
struct fc_lport *lp;
struct fc_els_rrq *rrq;
struct fc_frame *fp;
- struct fc_seq *rrq_sp;
u32 did;
lp = ep->lp;
fp = fc_frame_alloc(lp, sizeof(*rrq));
if (!fp)
- return;
+ goto retry;
+
rrq = fc_frame_payload_get(fp, sizeof(*rrq));
memset(rrq, 0, sizeof(*rrq));
rrq->rrq_cmd = ELS_RRQ;
@@ -1647,13 +1647,20 @@ static void fc_exch_rrq(struct fc_exch *ep)
fc_host_port_id(lp->host), FC_TYPE_ELS,
FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
- rrq_sp = fc_exch_seq_send(lp, fp, fc_exch_rrq_resp, NULL, ep,
- lp->e_d_tov);
- if (!rrq_sp) {
- ep->esb_stat |= ESB_ST_REC_QUAL;
- fc_exch_timer_set_locked(ep, ep->r_a_tov);
+ if (fc_exch_seq_send(lp, fp, fc_exch_rrq_resp, NULL, ep, lp->e_d_tov))
+ return;
+
+retry:
+ spin_lock_bh(&ep->ex_lock);
+ if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE)) {
+ spin_unlock_bh(&ep->ex_lock);
+ /* drop hold for rec qual */
+ fc_exch_release(ep);
return;
}
+ ep->esb_stat |= ESB_ST_REC_QUAL;
+ fc_exch_timer_set_locked(ep, ep->r_a_tov);
+ spin_unlock_bh(&ep->ex_lock);
}
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 716cc344c5d..a751f6230c2 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -1974,10 +1974,10 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
* good and have never sent us a successful tmf response
* then sent more data for the cmd.
*/
- spin_lock(&session->lock);
+ spin_lock_bh(&session->lock);
fail_scsi_task(task, DID_ABORT);
conn->tmf_state = TMF_INITIAL;
- spin_unlock(&session->lock);
+ spin_unlock_bh(&session->lock);
iscsi_start_tx(conn);
goto success_unlocked;
case TMF_TIMEDOUT:
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 54fa1e42dc4..b3381959acc 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -766,6 +766,7 @@ static int sas_ex_join_wide_port(struct domain_device *parent, int phy_id)
if (!memcmp(phy->attached_sas_addr, ephy->attached_sas_addr,
SAS_ADDR_SIZE) && ephy->port) {
sas_port_add_phy(ephy->port, phy->phy);
+ phy->port = ephy->port;
phy->phy_state = PHY_DEVICE_DISCOVERED;
return 0;
}
@@ -945,11 +946,21 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
if (ex->ex_phy[i].phy_state == PHY_VACANT ||
ex->ex_phy[i].phy_state == PHY_NOT_PRESENT)
continue;
-
+ /*
+ * Due to races, the phy might not get added to the
+ * wide port, so we add the phy to the wide port here.
+ */
if (SAS_ADDR(ex->ex_phy[i].attached_sas_addr) ==
- SAS_ADDR(child->sas_addr))
+ SAS_ADDR(child->sas_addr)) {
ex->ex_phy[i].phy_state= PHY_DEVICE_DISCOVERED;
+ res = sas_ex_join_wide_port(dev, i);
+ if (!res)
+ SAS_DPRINTK("Attaching ex phy%d to wide port %016llx\n",
+ i, SAS_ADDR(ex->ex_phy[i].attached_sas_addr));
+
+ }
}
+ res = 0;
}
return res;
@@ -1598,7 +1609,7 @@ static int sas_get_phy_attached_sas_addr(struct domain_device *dev,
}
static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id,
- int from_phy)
+ int from_phy, bool update)
{
struct expander_device *ex = &dev->ex_dev;
int res = 0;
@@ -1611,7 +1622,9 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id,
if (res)
goto out;
else if (phy_change_count != ex->ex_phy[i].phy_change_count) {
- ex->ex_phy[i].phy_change_count = phy_change_count;
+ if (update)
+ ex->ex_phy[i].phy_change_count =
+ phy_change_count;
*phy_id = i;
return 0;
}
@@ -1653,31 +1666,52 @@ out:
kfree(rg_req);
return res;
}
+/**
+ * sas_find_bcast_dev - find the device issue BROADCAST(CHANGE).
+ * @dev:domain device to be detect.
+ * @src_dev: the device which originated BROADCAST(CHANGE).
+ *
+ * Add self-configuration expander suport. Suppose two expander cascading,
+ * when the first level expander is self-configuring, hotplug the disks in
+ * second level expander, BROADCAST(CHANGE) will not only be originated
+ * in the second level expander, but also be originated in the first level
+ * expander (see SAS protocol SAS 2r-14, 7.11 for detail), it is to say,
+ * expander changed count in two level expanders will all increment at least
+ * once, but the phy which chang count has changed is the source device which
+ * we concerned.
+ */
static int sas_find_bcast_dev(struct domain_device *dev,
struct domain_device **src_dev)
{
struct expander_device *ex = &dev->ex_dev;
int ex_change_count = -1;
+ int phy_id = -1;
int res;
+ struct domain_device *ch;
res = sas_get_ex_change_count(dev, &ex_change_count);
if (res)
goto out;
- if (ex_change_count != -1 &&
- ex_change_count != ex->ex_change_count) {
- *src_dev = dev;
- ex->ex_change_count = ex_change_count;
- } else {
- struct domain_device *ch;
-
- list_for_each_entry(ch, &ex->children, siblings) {
- if (ch->dev_type == EDGE_DEV ||
- ch->dev_type == FANOUT_DEV) {
- res = sas_find_bcast_dev(ch, src_dev);
- if (src_dev)
- return res;
- }
+ if (ex_change_count != -1 && ex_change_count != ex->ex_change_count) {
+ /* Just detect if this expander phys phy change count changed,
+ * in order to determine if this expander originate BROADCAST,
+ * and do not update phy change count field in our structure.
+ */
+ res = sas_find_bcast_phy(dev, &phy_id, 0, false);
+ if (phy_id != -1) {
+ *src_dev = dev;
+ ex->ex_change_count = ex_change_count;
+ SAS_DPRINTK("Expander phy change count has changed\n");
+ return res;
+ } else
+ SAS_DPRINTK("Expander phys DID NOT change\n");
+ }
+ list_for_each_entry(ch, &ex->children, siblings) {
+ if (ch->dev_type == EDGE_DEV || ch->dev_type == FANOUT_DEV) {
+ res = sas_find_bcast_dev(ch, src_dev);
+ if (src_dev)
+ return res;
}
}
out:
@@ -1700,24 +1734,26 @@ static void sas_unregister_ex_tree(struct domain_device *dev)
}
static void sas_unregister_devs_sas_addr(struct domain_device *parent,
- int phy_id)
+ int phy_id, bool last)
{
struct expander_device *ex_dev = &parent->ex_dev;
struct ex_phy *phy = &ex_dev->ex_phy[phy_id];
struct domain_device *child, *n;
-
- list_for_each_entry_safe(child, n, &ex_dev->children, siblings) {
- if (SAS_ADDR(child->sas_addr) ==
- SAS_ADDR(phy->attached_sas_addr)) {
- if (child->dev_type == EDGE_DEV ||
- child->dev_type == FANOUT_DEV)
- sas_unregister_ex_tree(child);
- else
- sas_unregister_dev(child);
- break;
+ if (last) {
+ list_for_each_entry_safe(child, n,
+ &ex_dev->children, siblings) {
+ if (SAS_ADDR(child->sas_addr) ==
+ SAS_ADDR(phy->attached_sas_addr)) {
+ if (child->dev_type == EDGE_DEV ||
+ child->dev_type == FANOUT_DEV)
+ sas_unregister_ex_tree(child);
+ else
+ sas_unregister_dev(child);
+ break;
+ }
}
+ sas_disable_routing(parent, phy->attached_sas_addr);
}
- sas_disable_routing(parent, phy->attached_sas_addr);
memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
sas_port_delete_phy(phy->port, phy->phy);
if (phy->port->num_phys == 0)
@@ -1770,15 +1806,31 @@ static int sas_discover_new(struct domain_device *dev, int phy_id)
{
struct ex_phy *ex_phy = &dev->ex_dev.ex_phy[phy_id];
struct domain_device *child;
- int res;
+ bool found = false;
+ int res, i;
SAS_DPRINTK("ex %016llx phy%d new device attached\n",
SAS_ADDR(dev->sas_addr), phy_id);
res = sas_ex_phy_discover(dev, phy_id);
if (res)
goto out;
+ /* to support the wide port inserted */
+ for (i = 0; i < dev->ex_dev.num_phys; i++) {
+ struct ex_phy *ex_phy_temp = &dev->ex_dev.ex_phy[i];
+ if (i == phy_id)
+ continue;
+ if (SAS_ADDR(ex_phy_temp->attached_sas_addr) ==
+ SAS_ADDR(ex_phy->attached_sas_addr)) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ sas_ex_join_wide_port(dev, phy_id);
+ return 0;
+ }
res = sas_ex_discover_devices(dev, phy_id);
- if (res)
+ if (!res)
goto out;
list_for_each_entry(child, &dev->ex_dev.children, siblings) {
if (SAS_ADDR(child->sas_addr) ==
@@ -1793,7 +1845,7 @@ out:
return res;
}
-static int sas_rediscover_dev(struct domain_device *dev, int phy_id)
+static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last)
{
struct expander_device *ex = &dev->ex_dev;
struct ex_phy *phy = &ex->ex_phy[phy_id];
@@ -1804,11 +1856,11 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id)
switch (res) {
case SMP_RESP_NO_PHY:
phy->phy_state = PHY_NOT_PRESENT;
- sas_unregister_devs_sas_addr(dev, phy_id);
+ sas_unregister_devs_sas_addr(dev, phy_id, last);
goto out; break;
case SMP_RESP_PHY_VACANT:
phy->phy_state = PHY_VACANT;
- sas_unregister_devs_sas_addr(dev, phy_id);
+ sas_unregister_devs_sas_addr(dev, phy_id, last);
goto out; break;
case SMP_RESP_FUNC_ACC:
break;
@@ -1816,7 +1868,7 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id)
if (SAS_ADDR(attached_sas_addr) == 0) {
phy->phy_state = PHY_EMPTY;
- sas_unregister_devs_sas_addr(dev, phy_id);
+ sas_unregister_devs_sas_addr(dev, phy_id, last);
} else if (SAS_ADDR(attached_sas_addr) ==
SAS_ADDR(phy->attached_sas_addr)) {
SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n",
@@ -1828,12 +1880,27 @@ out:
return res;
}
+/**
+ * sas_rediscover - revalidate the domain.
+ * @dev:domain device to be detect.
+ * @phy_id: the phy id will be detected.
+ *
+ * NOTE: this process _must_ quit (return) as soon as any connection
+ * errors are encountered. Connection recovery is done elsewhere.
+ * Discover process only interrogates devices in order to discover the
+ * domain.For plugging out, we un-register the device only when it is
+ * the last phy in the port, for other phys in this port, we just delete it
+ * from the port.For inserting, we do discovery when it is the
+ * first phy,for other phys in this port, we add it to the port to
+ * forming the wide-port.
+ */
static int sas_rediscover(struct domain_device *dev, const int phy_id)
{
struct expander_device *ex = &dev->ex_dev;
struct ex_phy *changed_phy = &ex->ex_phy[phy_id];
int res = 0;
int i;
+ bool last = true; /* is this the last phy of the port */
SAS_DPRINTK("ex %016llx phy%d originated BROADCAST(CHANGE)\n",
SAS_ADDR(dev->sas_addr), phy_id);
@@ -1848,13 +1915,13 @@ static int sas_rediscover(struct domain_device *dev, const int phy_id)
SAS_ADDR(changed_phy->attached_sas_addr)) {
SAS_DPRINTK("phy%d part of wide port with "
"phy%d\n", phy_id, i);
- goto out;
+ last = false;
+ break;
}
}
- res = sas_rediscover_dev(dev, phy_id);
+ res = sas_rediscover_dev(dev, phy_id, last);
} else
res = sas_discover_new(dev, phy_id);
-out:
return res;
}
@@ -1881,7 +1948,7 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev)
do {
phy_id = -1;
- res = sas_find_bcast_phy(dev, &phy_id, i);
+ res = sas_find_bcast_phy(dev, &phy_id, i, true);
if (phy_id == -1)
break;
res = sas_rediscover(dev, phy_id);
diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c
index e6ac59c023f..fe8b74c706d 100644
--- a/drivers/scsi/libsas/sas_port.c
+++ b/drivers/scsi/libsas/sas_port.c
@@ -56,7 +56,7 @@ static void sas_form_port(struct asd_sas_phy *phy)
}
}
- /* find a port */
+ /* see if the phy should be part of a wide port */
spin_lock_irqsave(&sas_ha->phy_port_lock, flags);
for (i = 0; i < sas_ha->num_phys; i++) {
port = sas_ha->sas_port[i];
@@ -69,12 +69,23 @@ static void sas_form_port(struct asd_sas_phy *phy)
SAS_DPRINTK("phy%d matched wide port%d\n", phy->id,
port->id);
break;
- } else if (*(u64 *) port->sas_addr == 0 && port->num_phys==0) {
- memcpy(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE);
- break;
}
spin_unlock(&port->phy_list_lock);
}
+ /* The phy does not match any existing port, create a new one */
+ if (i == sas_ha->num_phys) {
+ for (i = 0; i < sas_ha->num_phys; i++) {
+ port = sas_ha->sas_port[i];
+ spin_lock(&port->phy_list_lock);
+ if (*(u64 *)port->sas_addr == 0
+ && port->num_phys == 0) {
+ memcpy(port->sas_addr, phy->sas_addr,
+ SAS_ADDR_SIZE);
+ break;
+ }
+ spin_unlock(&port->phy_list_lock);
+ }
+ }
if (i >= sas_ha->num_phys) {
printk(KERN_NOTICE "%s: couldn't find a free port, bug?\n",
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c
index f3da592f7bc..35a13867495 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -119,6 +119,64 @@ _base_fault_reset_work(struct work_struct *work)
spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
}
+/**
+ * mpt2sas_base_start_watchdog - start the fault_reset_work_q
+ * @ioc: pointer to scsi command object
+ * Context: sleep.
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc)
+{
+ unsigned long flags;
+
+ if (ioc->fault_reset_work_q)
+ return;
+
+ /* initialize fault polling */
+ INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work);
+ snprintf(ioc->fault_reset_work_q_name,
+ sizeof(ioc->fault_reset_work_q_name), "poll_%d_status", ioc->id);
+ ioc->fault_reset_work_q =
+ create_singlethread_workqueue(ioc->fault_reset_work_q_name);
+ if (!ioc->fault_reset_work_q) {
+ printk(MPT2SAS_ERR_FMT "%s: failed (line=%d)\n",
+ ioc->name, __func__, __LINE__);
+ return;
+ }
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->fault_reset_work_q)
+ queue_delayed_work(ioc->fault_reset_work_q,
+ &ioc->fault_reset_work,
+ msecs_to_jiffies(FAULT_POLLING_INTERVAL));
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+}
+
+/**
+ * mpt2sas_base_stop_watchdog - stop the fault_reset_work_q
+ * @ioc: pointer to scsi command object
+ * Context: sleep.
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc)
+{
+ unsigned long flags;
+ struct workqueue_struct *wq;
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ wq = ioc->fault_reset_work_q;
+ ioc->fault_reset_work_q = NULL;
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ if (wq) {
+ if (!cancel_delayed_work(&ioc->fault_reset_work))
+ flush_workqueue(wq);
+ destroy_workqueue(wq);
+ }
+}
+
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
/**
* _base_sas_ioc_info - verbose translation of the ioc status
@@ -440,6 +498,10 @@ _base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info)
if (sas_loginfo.dw.bus_type != 3 /*SAS*/)
return;
+ /* each nexus loss loginfo */
+ if (log_info == 0x31170000)
+ return;
+
/* eat the loginfos associated with task aborts */
if (ioc->ignore_loginfos && (log_info == 30050000 || log_info ==
0x31140000 || log_info == 0x31130000))
@@ -1109,7 +1171,6 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc)
}
}
- pci_set_drvdata(pdev, ioc->shost);
_base_mask_interrupts(ioc);
r = _base_enable_msix(ioc);
if (r)
@@ -1132,7 +1193,6 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc)
ioc->pci_irq = -1;
pci_release_selected_regions(ioc->pdev, ioc->bars);
pci_disable_device(pdev);
- pci_set_drvdata(pdev, NULL);
return r;
}
@@ -3191,7 +3251,6 @@ mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc)
ioc->chip_phys = 0;
pci_release_selected_regions(ioc->pdev, ioc->bars);
pci_disable_device(pdev);
- pci_set_drvdata(pdev, NULL);
return;
}
@@ -3205,7 +3264,6 @@ int
mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
{
int r, i;
- unsigned long flags;
dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
__func__));
@@ -3214,6 +3272,7 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
if (r)
return r;
+ pci_set_drvdata(ioc->pdev, ioc->shost);
r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
if (r)
goto out_free_resources;
@@ -3288,23 +3347,7 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
if (r)
goto out_free_resources;
- /* initialize fault polling */
- INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work);
- snprintf(ioc->fault_reset_work_q_name,
- sizeof(ioc->fault_reset_work_q_name), "poll_%d_status", ioc->id);
- ioc->fault_reset_work_q =
- create_singlethread_workqueue(ioc->fault_reset_work_q_name);
- if (!ioc->fault_reset_work_q) {
- printk(MPT2SAS_ERR_FMT "%s: failed (line=%d)\n",
- ioc->name, __func__, __LINE__);
- goto out_free_resources;
- }
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- if (ioc->fault_reset_work_q)
- queue_delayed_work(ioc->fault_reset_work_q,
- &ioc->fault_reset_work,
- msecs_to_jiffies(FAULT_POLLING_INTERVAL));
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ mpt2sas_base_start_watchdog(ioc);
return 0;
out_free_resources:
@@ -3312,6 +3355,7 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
ioc->remove_host = 1;
mpt2sas_base_free_resources(ioc);
_base_release_memory_pools(ioc);
+ pci_set_drvdata(ioc->pdev, NULL);
kfree(ioc->tm_cmds.reply);
kfree(ioc->transport_cmds.reply);
kfree(ioc->config_cmds.reply);
@@ -3337,22 +3381,14 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
void
mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc)
{
- unsigned long flags;
- struct workqueue_struct *wq;
dexitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
__func__));
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- wq = ioc->fault_reset_work_q;
- ioc->fault_reset_work_q = NULL;
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
- if (!cancel_delayed_work(&ioc->fault_reset_work))
- flush_workqueue(wq);
- destroy_workqueue(wq);
-
+ mpt2sas_base_stop_watchdog(ioc);
mpt2sas_base_free_resources(ioc);
_base_release_memory_pools(ioc);
+ pci_set_drvdata(ioc->pdev, NULL);
kfree(ioc->pfacts);
kfree(ioc->ctl_cmds.reply);
kfree(ioc->base_cmds.reply);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h
index 286c185fa9e..acdcff150a3 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.h
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.h
@@ -69,10 +69,10 @@
#define MPT2SAS_DRIVER_NAME "mpt2sas"
#define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>"
#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver"
-#define MPT2SAS_DRIVER_VERSION "01.100.03.00"
+#define MPT2SAS_DRIVER_VERSION "01.100.04.00"
#define MPT2SAS_MAJOR_VERSION 01
#define MPT2SAS_MINOR_VERSION 100
-#define MPT2SAS_BUILD_VERSION 03
+#define MPT2SAS_BUILD_VERSION 04
#define MPT2SAS_RELEASE_VERSION 00
/*
@@ -673,6 +673,8 @@ typedef void (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID,
/* base shared API */
extern struct list_head mpt2sas_ioc_list;
+void mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc);
+void mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc);
int mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc);
void mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c
index 58cfb97846f..6ddee161beb 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_config.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_config.c
@@ -236,17 +236,25 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
Mpi2ConfigRequest_t *config_request;
int r;
u8 retry_count;
- u8 issue_reset;
+ u8 issue_host_reset = 0;
u16 wait_state_count;
+ mutex_lock(&ioc->config_cmds.mutex);
if (ioc->config_cmds.status != MPT2_CMD_NOT_USED) {
printk(MPT2SAS_ERR_FMT "%s: config_cmd in use\n",
ioc->name, __func__);
+ mutex_unlock(&ioc->config_cmds.mutex);
return -EAGAIN;
}
retry_count = 0;
retry_config:
+ if (retry_count) {
+ if (retry_count > 2) /* attempt only 2 retries */
+ return -EFAULT;
+ printk(MPT2SAS_INFO_FMT "%s: attempting retry (%d)\n",
+ ioc->name, __func__, retry_count);
+ }
wait_state_count = 0;
ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
@@ -254,8 +262,8 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
printk(MPT2SAS_ERR_FMT
"%s: failed due to ioc not operational\n",
ioc->name, __func__);
- ioc->config_cmds.status = MPT2_CMD_NOT_USED;
- return -EFAULT;
+ r = -EFAULT;
+ goto out;
}
ssleep(1);
ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
@@ -271,8 +279,8 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
if (!smid) {
printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
ioc->name, __func__);
- ioc->config_cmds.status = MPT2_CMD_NOT_USED;
- return -EAGAIN;
+ r = -EAGAIN;
+ goto out;
}
r = 0;
@@ -292,9 +300,15 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
ioc->name, __func__);
_debug_dump_mf(mpi_request,
sizeof(Mpi2ConfigRequest_t)/4);
- if (!(ioc->config_cmds.status & MPT2_CMD_RESET))
- issue_reset = 1;
- goto issue_host_reset;
+ retry_count++;
+ if (ioc->config_cmds.smid == smid)
+ mpt2sas_base_free_smid(ioc, smid);
+ if ((ioc->shost_recovery) ||
+ (ioc->config_cmds.status & MPT2_CMD_RESET))
+ goto retry_config;
+ issue_host_reset = 1;
+ r = -EFAULT;
+ goto out;
}
if (ioc->config_cmds.status & MPT2_CMD_REPLY_VALID)
memcpy(mpi_reply, ioc->config_cmds.reply,
@@ -302,21 +316,13 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
if (retry_count)
printk(MPT2SAS_INFO_FMT "%s: retry completed!!\n",
ioc->name, __func__);
+out:
ioc->config_cmds.status = MPT2_CMD_NOT_USED;
- return r;
-
- issue_host_reset:
- if (issue_reset)
+ mutex_unlock(&ioc->config_cmds.mutex);
+ if (issue_host_reset)
mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
FORCE_BIG_HAMMER);
- ioc->config_cmds.status = MPT2_CMD_NOT_USED;
- if (!retry_count) {
- printk(MPT2SAS_INFO_FMT "%s: attempting retry\n",
- ioc->name, __func__);
- retry_count++;
- goto retry_config;
- }
- return -EFAULT;
+ return r;
}
/**
@@ -375,7 +381,6 @@ mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc,
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2ManufacturingPage0_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -417,7 +422,6 @@ mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc,
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -438,7 +442,6 @@ mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc,
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2BiosPage2_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -480,7 +483,6 @@ mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc,
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -501,7 +503,6 @@ mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2BiosPage3_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -543,7 +544,6 @@ mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -564,7 +564,6 @@ mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc,
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2IOUnitPage0_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -606,7 +605,6 @@ mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc,
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -627,7 +625,6 @@ mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2IOUnitPage1_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -669,7 +666,6 @@ mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -690,7 +686,6 @@ mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -732,7 +727,6 @@ mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -753,7 +747,6 @@ mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc,
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2IOCPage8_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -795,7 +788,6 @@ mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc,
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -818,7 +810,6 @@ mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2SasDevicePage0_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -863,7 +854,6 @@ mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -886,7 +876,6 @@ mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2SasDevicePage1_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -931,7 +920,6 @@ mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -953,7 +941,6 @@ mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys)
Mpi2ConfigReply_t mpi_reply;
Mpi2SasIOUnitPage0_t config_page;
- mutex_lock(&ioc->config_cmds.mutex);
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -1002,7 +989,6 @@ mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys)
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -1026,8 +1012,6 @@ mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
Mpi2ConfigRequest_t mpi_request;
int r;
struct config_request mem;
-
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sz);
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -1070,7 +1054,6 @@ mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -1095,7 +1078,6 @@ mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sz);
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -1138,7 +1120,6 @@ mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -1161,7 +1142,6 @@ mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2ExpanderPage0_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -1206,7 +1186,6 @@ mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -1230,7 +1209,6 @@ mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2ExpanderPage1_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -1277,7 +1255,6 @@ mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -1300,7 +1277,6 @@ mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2SasEnclosurePage0_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -1345,7 +1321,6 @@ mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -1367,7 +1342,6 @@ mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2SasPhyPage0_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -1413,7 +1387,6 @@ mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -1435,7 +1408,6 @@ mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2SasPhyPage1_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -1481,7 +1453,6 @@ mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -1505,7 +1476,6 @@ mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc,
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(config_page, 0, sizeof(Mpi2RaidVolPage1_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -1548,7 +1518,6 @@ mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc,
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -1572,7 +1541,6 @@ mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle,
struct config_request mem;
u16 ioc_status;
- mutex_lock(&ioc->config_cmds.mutex);
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
*num_pds = 0;
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -1620,7 +1588,6 @@ mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle,
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -1645,7 +1612,6 @@ mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc,
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
memset(config_page, 0, sz);
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -1687,7 +1653,6 @@ mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc,
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -1711,7 +1676,6 @@ mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
int r;
struct config_request mem;
- mutex_lock(&ioc->config_cmds.mutex);
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
memset(config_page, 0, sizeof(Mpi2RaidPhysDiskPage0_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -1754,7 +1718,6 @@ mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
@@ -1778,7 +1741,6 @@ mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle,
struct config_request mem;
u16 ioc_status;
- mutex_lock(&ioc->config_cmds.mutex);
*volume_handle = 0;
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -1842,7 +1804,6 @@ mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle,
_config_free_config_dma_memory(ioc, &mem);
out:
- mutex_unlock(&ioc->config_cmds.mutex);
return r;
}
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
index 2a01a5f2a84..2e9a4445596 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -2767,6 +2767,10 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
char *desc_ioc_state = NULL;
char *desc_scsi_status = NULL;
char *desc_scsi_state = ioc->tmp_string;
+ u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
+
+ if (log_info == 0x31170000)
+ return;
switch (ioc_status) {
case MPI2_IOCSTATUS_SUCCESS:
@@ -3426,7 +3430,7 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
__le64 sas_address;
int i;
unsigned long flags;
- struct _sas_port *mpt2sas_port;
+ struct _sas_port *mpt2sas_port = NULL;
int rc = 0;
if (!handle)
@@ -3518,12 +3522,20 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
&expander_pg1, i, handle))) {
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
ioc->name, __FILE__, __LINE__, __func__);
- continue;
+ rc = -1;
+ goto out_fail;
}
sas_expander->phy[i].handle = handle;
sas_expander->phy[i].phy_id = i;
- mpt2sas_transport_add_expander_phy(ioc, &sas_expander->phy[i],
- expander_pg1, sas_expander->parent_dev);
+
+ if ((mpt2sas_transport_add_expander_phy(ioc,
+ &sas_expander->phy[i], expander_pg1,
+ sas_expander->parent_dev))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
}
if (sas_expander->enclosure_handle) {
@@ -3540,8 +3552,9 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
out_fail:
- if (sas_expander)
- kfree(sas_expander->phy);
+ if (mpt2sas_port)
+ mpt2sas_transport_port_remove(ioc, sas_expander->sas_address,
+ sas_expander->parent_handle);
kfree(sas_expander);
return rc;
}
@@ -3663,12 +3676,11 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
sas_device->hidden_raid_component = is_pd;
/* get enclosure_logical_id */
- if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply, &enclosure_pg0,
- MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
- sas_device->enclosure_handle))) {
+ if (sas_device->enclosure_handle && !(mpt2sas_config_get_enclosure_pg0(
+ ioc, &mpi_reply, &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
+ sas_device->enclosure_handle)))
sas_device->enclosure_logical_id =
le64_to_cpu(enclosure_pg0.EnclosureLogicalID);
- }
/* get device name */
sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName);
@@ -4250,12 +4262,6 @@ _scsih_sas_volume_add(struct MPT2SAS_ADAPTER *ioc,
u16 handle = le16_to_cpu(element->VolDevHandle);
int rc;
-#if 0 /* RAID_HACKS */
- if (le32_to_cpu(event_data->Flags) &
- MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG)
- return;
-#endif
-
mpt2sas_config_get_volume_wwid(ioc, handle, &wwid);
if (!wwid) {
printk(MPT2SAS_ERR_FMT
@@ -4310,12 +4316,6 @@ _scsih_sas_volume_delete(struct MPT2SAS_ADAPTER *ioc,
unsigned long flags;
struct MPT2SAS_TARGET *sas_target_priv_data;
-#if 0 /* RAID_HACKS */
- if (le32_to_cpu(event_data->Flags) &
- MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG)
- return;
-#endif
-
spin_lock_irqsave(&ioc->raid_device_lock, flags);
raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
@@ -4428,14 +4428,38 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc,
struct _sas_device *sas_device;
unsigned long flags;
u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ u32 ioc_status;
spin_lock_irqsave(&ioc->sas_device_lock, flags);
sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (sas_device)
+ if (sas_device) {
sas_device->hidden_raid_component = 1;
- else
- _scsih_add_device(ioc, handle, 0, 1);
+ return;
+ }
+
+ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ _scsih_link_change(ioc,
+ le16_to_cpu(sas_device_pg0.ParentDevHandle),
+ handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+
+ _scsih_add_device(ioc, handle, 0, 1);
}
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
@@ -4535,12 +4559,15 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
{
Mpi2EventIrConfigElement_t *element;
int i;
+ u8 foreign_config;
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
_scsih_sas_ir_config_change_event_debug(ioc, event_data);
#endif
+ foreign_config = (le32_to_cpu(event_data->Flags) &
+ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0;
element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
for (i = 0; i < event_data->NumElements; i++, element++) {
@@ -4548,11 +4575,13 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
switch (element->ReasonCode) {
case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED:
case MPI2_EVENT_IR_CHANGE_RC_ADDED:
- _scsih_sas_volume_add(ioc, element);
+ if (!foreign_config)
+ _scsih_sas_volume_add(ioc, element);
break;
case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED:
case MPI2_EVENT_IR_CHANGE_RC_REMOVED:
- _scsih_sas_volume_delete(ioc, element);
+ if (!foreign_config)
+ _scsih_sas_volume_delete(ioc, element);
break;
case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED:
_scsih_sas_pd_hide(ioc, element);
@@ -4671,6 +4700,9 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
u32 state;
struct _sas_device *sas_device;
unsigned long flags;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ u32 ioc_status;
if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED)
return;
@@ -4687,22 +4719,40 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
switch (state) {
-#if 0
- case MPI2_RAID_PD_STATE_OFFLINE:
- if (sas_device)
- _scsih_remove_device(ioc, handle);
- break;
-#endif
case MPI2_RAID_PD_STATE_ONLINE:
case MPI2_RAID_PD_STATE_DEGRADED:
case MPI2_RAID_PD_STATE_REBUILDING:
case MPI2_RAID_PD_STATE_OPTIMAL:
- if (sas_device)
+ if (sas_device) {
sas_device->hidden_raid_component = 1;
- else
- _scsih_add_device(ioc, handle, 0, 1);
+ return;
+ }
+
+ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+ handle))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ _scsih_link_change(ioc,
+ le16_to_cpu(sas_device_pg0.ParentDevHandle),
+ handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+
+ _scsih_add_device(ioc, handle, 0, 1);
+
break;
+ case MPI2_RAID_PD_STATE_OFFLINE:
case MPI2_RAID_PD_STATE_NOT_CONFIGURED:
case MPI2_RAID_PD_STATE_NOT_COMPATIBLE:
case MPI2_RAID_PD_STATE_HOT_SPARE:
@@ -5774,6 +5824,7 @@ _scsih_suspend(struct pci_dev *pdev, pm_message_t state)
struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
u32 device_state;
+ mpt2sas_base_stop_watchdog(ioc);
flush_scheduled_work();
scsi_block_requests(shost);
device_state = pci_choose_state(pdev, state);
@@ -5816,6 +5867,7 @@ _scsih_resume(struct pci_dev *pdev)
mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, SOFT_RESET);
scsi_unblock_requests(shost);
+ mpt2sas_base_start_watchdog(ioc);
return 0;
}
#endif /* CONFIG_PM */
diff --git a/drivers/scsi/qla4xxx/ql4_dbg.c b/drivers/scsi/qla4xxx/ql4_dbg.c
index fcc184cd066..cbceb0ebabf 100644
--- a/drivers/scsi/qla4xxx/ql4_dbg.c
+++ b/drivers/scsi/qla4xxx/ql4_dbg.c
@@ -15,19 +15,18 @@ void qla4xxx_dump_buffer(void *b, uint32_t size)
uint32_t cnt;
uint8_t *c = b;
- printk(" 0 1 2 3 4 5 6 7 8 9 Ah Bh Ch Dh Eh "
+ printk(" 0 1 2 3 4 5 6 7 8 9 Ah Bh Ch Dh Eh "
"Fh\n");
printk("------------------------------------------------------------"
"--\n");
- for (cnt = 0; cnt < size; cnt++, c++) {
- printk(KERN_DEBUG "%02x", *c);
- if (!(cnt % 16))
- printk(KERN_DEBUG "\n");
+ for (cnt = 0; cnt < size; c++) {
+ printk(KERN_INFO "%02x", *c);
+ if (!(++cnt % 16))
+ printk(KERN_INFO "\n");
else
- printk(KERN_DEBUG " ");
+ printk(KERN_INFO " ");
}
- if (cnt % 16)
- printk(KERN_DEBUG "\n");
+ printk(KERN_INFO "\n");
}
diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h
index b586f27c3bd..81b5f29254e 100644
--- a/drivers/scsi/qla4xxx/ql4_def.h
+++ b/drivers/scsi/qla4xxx/ql4_def.h
@@ -100,7 +100,6 @@
#define MAX_SRBS MAX_CMDS_TO_RISC
#define MBOX_AEN_REG_COUNT 5
#define MAX_INIT_RETRIES 5
-#define IOCB_HIWAT_CUSHION 16
/*
* Buffer sizes
@@ -184,6 +183,11 @@ struct srb {
uint16_t cc_stat;
u_long r_start; /* Time we recieve a cmd from OS */
u_long u_start; /* Time when we handed the cmd to F/W */
+
+ /* Used for extended sense / status continuation */
+ uint8_t *req_sense_ptr;
+ uint16_t req_sense_len;
+ uint16_t reserved2;
};
/*
@@ -302,7 +306,6 @@ struct scsi_qla_host {
uint32_t tot_ddbs;
uint16_t iocb_cnt;
- uint16_t iocb_hiwat;
/* SRB cache. */
#define SRB_MIN_REQ 128
@@ -436,6 +439,8 @@ struct scsi_qla_host {
/* Map ddb_list entry by FW ddb index */
struct ddb_entry *fw_ddb_index_map[MAX_DDB_ENTRIES];
+ /* Saved srb for status continuation entry processing */
+ struct srb *status_srb;
};
static inline int is_qla4010(struct scsi_qla_host *ha)
diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h
index 1b667a70cff..9cd7a608df3 100644
--- a/drivers/scsi/qla4xxx/ql4_fw.h
+++ b/drivers/scsi/qla4xxx/ql4_fw.h
@@ -572,6 +572,7 @@ struct conn_event_log_entry {
*************************************************************************/
#define IOCB_MAX_CDB_LEN 16 /* Bytes in a CBD */
#define IOCB_MAX_SENSEDATA_LEN 32 /* Bytes of sense data */
+#define IOCB_MAX_EXT_SENSEDATA_LEN 60 /* Bytes of extended sense data */
/* IOCB header structure */
struct qla4_header {
@@ -733,6 +734,12 @@ struct status_entry {
};
+/* Status Continuation entry */
+struct status_cont_entry {
+ struct qla4_header hdr; /* 00-03 */
+ uint8_t ext_sense_data[IOCB_MAX_EXT_SENSEDATA_LEN]; /* 04-63 */
+};
+
struct passthru0 {
struct qla4_header hdr; /* 00-03 */
uint32_t handle; /* 04-07 */
diff --git a/drivers/scsi/qla4xxx/ql4_iocb.c b/drivers/scsi/qla4xxx/ql4_iocb.c
index 912a67494ad..e0c32159749 100644
--- a/drivers/scsi/qla4xxx/ql4_iocb.c
+++ b/drivers/scsi/qla4xxx/ql4_iocb.c
@@ -10,9 +10,42 @@
#include "ql4_dbg.h"
#include "ql4_inline.h"
-
#include <scsi/scsi_tcq.h>
+static int
+qla4xxx_space_in_req_ring(struct scsi_qla_host *ha, uint16_t req_cnt)
+{
+ uint16_t cnt;
+
+ /* Calculate number of free request entries. */
+ if ((req_cnt + 2) >= ha->req_q_count) {
+ cnt = (uint16_t) le32_to_cpu(ha->shadow_regs->req_q_out);
+ if (ha->request_in < cnt)
+ ha->req_q_count = cnt - ha->request_in;
+ else
+ ha->req_q_count = REQUEST_QUEUE_DEPTH -
+ (ha->request_in - cnt);
+ }
+
+ /* Check if room for request in request ring. */
+ if ((req_cnt + 2) < ha->req_q_count)
+ return 1;
+ else
+ return 0;
+}
+
+static void qla4xxx_advance_req_ring_ptr(struct scsi_qla_host *ha)
+{
+ /* Advance request queue pointer */
+ if (ha->request_in == (REQUEST_QUEUE_DEPTH - 1)) {
+ ha->request_in = 0;
+ ha->request_ptr = ha->request_ring;
+ } else {
+ ha->request_in++;
+ ha->request_ptr++;
+ }
+}
+
/**
* qla4xxx_get_req_pkt - returns a valid entry in request queue.
* @ha: Pointer to host adapter structure.
@@ -26,35 +59,18 @@
static int qla4xxx_get_req_pkt(struct scsi_qla_host *ha,
struct queue_entry **queue_entry)
{
- uint16_t request_in;
- uint8_t status = QLA_SUCCESS;
-
- *queue_entry = ha->request_ptr;
+ uint16_t req_cnt = 1;
- /* get the latest request_in and request_out index */
- request_in = ha->request_in;
- ha->request_out = (uint16_t) le32_to_cpu(ha->shadow_regs->req_q_out);
-
- /* Advance request queue pointer and check for queue full */
- if (request_in == (REQUEST_QUEUE_DEPTH - 1)) {
- request_in = 0;
- ha->request_ptr = ha->request_ring;
- } else {
- request_in++;
- ha->request_ptr++;
- }
-
- /* request queue is full, try again later */
- if ((ha->iocb_cnt + 1) >= ha->iocb_hiwat) {
- /* restore request pointer */
- ha->request_ptr = *queue_entry;
- status = QLA_ERROR;
- } else {
- ha->request_in = request_in;
+ if (qla4xxx_space_in_req_ring(ha, req_cnt)) {
+ *queue_entry = ha->request_ptr;
memset(*queue_entry, 0, sizeof(**queue_entry));
+
+ qla4xxx_advance_req_ring_ptr(ha);
+ ha->req_q_count -= req_cnt;
+ return QLA_SUCCESS;
}
- return status;
+ return QLA_ERROR;
}
/**
@@ -100,21 +116,14 @@ exit_send_marker:
return status;
}
-static struct continuation_t1_entry* qla4xxx_alloc_cont_entry(
- struct scsi_qla_host *ha)
+static struct continuation_t1_entry *
+qla4xxx_alloc_cont_entry(struct scsi_qla_host *ha)
{
struct continuation_t1_entry *cont_entry;
cont_entry = (struct continuation_t1_entry *)ha->request_ptr;
- /* Advance request queue pointer */
- if (ha->request_in == (REQUEST_QUEUE_DEPTH - 1)) {
- ha->request_in = 0;
- ha->request_ptr = ha->request_ring;
- } else {
- ha->request_in++;
- ha->request_ptr++;
- }
+ qla4xxx_advance_req_ring_ptr(ha);
/* Load packet defaults */
cont_entry->hdr.entryType = ET_CONTINUE;
@@ -197,13 +206,10 @@ int qla4xxx_send_command_to_isp(struct scsi_qla_host *ha, struct srb * srb)
struct scsi_cmnd *cmd = srb->cmd;
struct ddb_entry *ddb_entry;
struct command_t3_entry *cmd_entry;
-
int nseg;
uint16_t tot_dsds;
uint16_t req_cnt;
-
unsigned long flags;
- uint16_t cnt;
uint32_t index;
char tag[2];
@@ -217,6 +223,19 @@ int qla4xxx_send_command_to_isp(struct scsi_qla_host *ha, struct srb * srb)
index = (uint32_t)cmd->request->tag;
+ /*
+ * Check to see if adapter is online before placing request on
+ * request queue. If a reset occurs and a request is in the queue,
+ * the firmware will still attempt to process the request, retrieving
+ * garbage for pointers.
+ */
+ if (!test_bit(AF_ONLINE, &ha->flags)) {
+ DEBUG2(printk("scsi%ld: %s: Adapter OFFLINE! "
+ "Do not issue command.\n",
+ ha->host_no, __func__));
+ goto queuing_error;
+ }
+
/* Calculate the number of request entries needed. */
nseg = scsi_dma_map(cmd);
if (nseg < 0)
@@ -224,17 +243,7 @@ int qla4xxx_send_command_to_isp(struct scsi_qla_host *ha, struct srb * srb)
tot_dsds = nseg;
req_cnt = qla4xxx_calc_request_entries(tot_dsds);
-
- if (ha->req_q_count < (req_cnt + 2)) {
- cnt = (uint16_t) le32_to_cpu(ha->shadow_regs->req_q_out);
- if (ha->request_in < cnt)
- ha->req_q_count = cnt - ha->request_in;
- else
- ha->req_q_count = REQUEST_QUEUE_DEPTH -
- (ha->request_in - cnt);
- }
-
- if (ha->req_q_count < (req_cnt + 2))
+ if (!qla4xxx_space_in_req_ring(ha, req_cnt))
goto queuing_error;
/* total iocbs active */
@@ -286,32 +295,10 @@ int qla4xxx_send_command_to_isp(struct scsi_qla_host *ha, struct srb * srb)
break;
}
-
- /* Advance request queue pointer */
- ha->request_in++;
- if (ha->request_in == REQUEST_QUEUE_DEPTH) {
- ha->request_in = 0;
- ha->request_ptr = ha->request_ring;
- } else
- ha->request_ptr++;
-
-
+ qla4xxx_advance_req_ring_ptr(ha);
qla4xxx_build_scsi_iocbs(srb, cmd_entry, tot_dsds);
wmb();
- /*
- * Check to see if adapter is online before placing request on
- * request queue. If a reset occurs and a request is in the queue,
- * the firmware will still attempt to process the request, retrieving
- * garbage for pointers.
- */
- if (!test_bit(AF_ONLINE, &ha->flags)) {
- DEBUG2(printk("scsi%ld: %s: Adapter OFFLINE! "
- "Do not issue command.\n",
- ha->host_no, __func__));
- goto queuing_error;
- }
-
srb->cmd->host_scribble = (unsigned char *)srb;
/* update counters */
diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c
index 799120fcb9b..8025ee16588 100644
--- a/drivers/scsi/qla4xxx/ql4_isr.c
+++ b/drivers/scsi/qla4xxx/ql4_isr.c
@@ -11,6 +11,98 @@
#include "ql4_inline.h"
/**
+ * qla4xxx_copy_sense - copy sense data into cmd sense buffer
+ * @ha: Pointer to host adapter structure.
+ * @sts_entry: Pointer to status entry structure.
+ * @srb: Pointer to srb structure.
+ **/
+static void qla4xxx_copy_sense(struct scsi_qla_host *ha,
+ struct status_entry *sts_entry,
+ struct srb *srb)
+{
+ struct scsi_cmnd *cmd = srb->cmd;
+ uint16_t sense_len;
+
+ memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+ sense_len = le16_to_cpu(sts_entry->senseDataByteCnt);
+ if (sense_len == 0)
+ return;
+
+ /* Save total available sense length,
+ * not to exceed cmd's sense buffer size */
+ sense_len = min_t(uint16_t, sense_len, SCSI_SENSE_BUFFERSIZE);
+ srb->req_sense_ptr = cmd->sense_buffer;
+ srb->req_sense_len = sense_len;
+
+ /* Copy sense from sts_entry pkt */
+ sense_len = min_t(uint16_t, sense_len, IOCB_MAX_SENSEDATA_LEN);
+ memcpy(cmd->sense_buffer, sts_entry->senseData, sense_len);
+
+ DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%d: %s: sense key = %x, "
+ "ASL= %02x, ASC/ASCQ = %02x/%02x\n", ha->host_no,
+ cmd->device->channel, cmd->device->id,
+ cmd->device->lun, __func__,
+ sts_entry->senseData[2] & 0x0f,
+ sts_entry->senseData[7],
+ sts_entry->senseData[12],
+ sts_entry->senseData[13]));
+
+ DEBUG5(qla4xxx_dump_buffer(cmd->sense_buffer, sense_len));
+ srb->flags |= SRB_GOT_SENSE;
+
+ /* Update srb, in case a sts_cont pkt follows */
+ srb->req_sense_ptr += sense_len;
+ srb->req_sense_len -= sense_len;
+ if (srb->req_sense_len != 0)
+ ha->status_srb = srb;
+ else
+ ha->status_srb = NULL;
+}
+
+/**
+ * qla4xxx_status_cont_entry - Process a Status Continuations entry.
+ * @ha: SCSI driver HA context
+ * @sts_cont: Entry pointer
+ *
+ * Extended sense data.
+ */
+static void
+qla4xxx_status_cont_entry(struct scsi_qla_host *ha,
+ struct status_cont_entry *sts_cont)
+{
+ struct srb *srb = ha->status_srb;
+ struct scsi_cmnd *cmd;
+ uint8_t sense_len;
+
+ if (srb == NULL)
+ return;
+
+ cmd = srb->cmd;
+ if (cmd == NULL) {
+ DEBUG2(printk(KERN_INFO "scsi%ld: %s: Cmd already returned "
+ "back to OS srb=%p srb->state:%d\n", ha->host_no,
+ __func__, srb, srb->state));
+ ha->status_srb = NULL;
+ return;
+ }
+
+ /* Copy sense data. */
+ sense_len = min_t(uint16_t, srb->req_sense_len,
+ IOCB_MAX_EXT_SENSEDATA_LEN);
+ memcpy(srb->req_sense_ptr, sts_cont->ext_sense_data, sense_len);
+ DEBUG5(qla4xxx_dump_buffer(srb->req_sense_ptr, sense_len));
+
+ srb->req_sense_ptr += sense_len;
+ srb->req_sense_len -= sense_len;
+
+ /* Place command on done queue. */
+ if (srb->req_sense_len == 0) {
+ qla4xxx_srb_compl(ha, srb);
+ ha->status_srb = NULL;
+ }
+}
+
+/**
* qla4xxx_status_entry - processes status IOCBs
* @ha: Pointer to host adapter structure.
* @sts_entry: Pointer to status entry structure.
@@ -23,7 +115,6 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
struct srb *srb;
struct ddb_entry *ddb_entry;
uint32_t residual;
- uint16_t sensebytecnt;
srb = qla4xxx_del_from_active_array(ha, le32_to_cpu(sts_entry->handle));
if (!srb) {
@@ -92,24 +183,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
break;
/* Copy Sense Data into sense buffer. */
- memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
-
- sensebytecnt = le16_to_cpu(sts_entry->senseDataByteCnt);
- if (sensebytecnt == 0)
- break;
-
- memcpy(cmd->sense_buffer, sts_entry->senseData,
- min_t(uint16_t, sensebytecnt, SCSI_SENSE_BUFFERSIZE));
-
- DEBUG2(printk("scsi%ld:%d:%d:%d: %s: sense key = %x, "
- "ASC/ASCQ = %02x/%02x\n", ha->host_no,
- cmd->device->channel, cmd->device->id,
- cmd->device->lun, __func__,
- sts_entry->senseData[2] & 0x0f,
- sts_entry->senseData[12],
- sts_entry->senseData[13]));
-
- srb->flags |= SRB_GOT_SENSE;
+ qla4xxx_copy_sense(ha, sts_entry, srb);
break;
case SCS_INCOMPLETE:
@@ -176,23 +250,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
break;
/* Copy Sense Data into sense buffer. */
- memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
-
- sensebytecnt =
- le16_to_cpu(sts_entry->senseDataByteCnt);
- if (sensebytecnt == 0)
- break;
-
- memcpy(cmd->sense_buffer, sts_entry->senseData,
- min_t(uint16_t, sensebytecnt, SCSI_SENSE_BUFFERSIZE));
-
- DEBUG2(printk("scsi%ld:%d:%d:%d: %s: sense key = %x, "
- "ASC/ASCQ = %02x/%02x\n", ha->host_no,
- cmd->device->channel, cmd->device->id,
- cmd->device->lun, __func__,
- sts_entry->senseData[2] & 0x0f,
- sts_entry->senseData[12],
- sts_entry->senseData[13]));
+ qla4xxx_copy_sense(ha, sts_entry, srb);
} else {
/*
* If RISC reports underrun and target does not
@@ -268,9 +326,10 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
status_entry_exit:
- /* complete the request */
+ /* complete the request, if not waiting for status_continuation pkt */
srb->cc_stat = sts_entry->completionStatus;
- qla4xxx_srb_compl(ha, srb);
+ if (ha->status_srb == NULL)
+ qla4xxx_srb_compl(ha, srb);
}
/**
@@ -305,10 +364,7 @@ static void qla4xxx_process_response_queue(struct scsi_qla_host * ha)
/* process entry */
switch (sts_entry->hdr.entryType) {
case ET_STATUS:
- /*
- * Common status - Single completion posted in single
- * IOSB.
- */
+ /* Common status */
qla4xxx_status_entry(ha, sts_entry);
break;
@@ -316,9 +372,8 @@ static void qla4xxx_process_response_queue(struct scsi_qla_host * ha)
break;
case ET_STATUS_CONTINUATION:
- /* Just throw away the status continuation entries */
- DEBUG2(printk("scsi%ld: %s: Status Continuation entry "
- "- ignoring\n", ha->host_no, __func__));
+ qla4xxx_status_cont_entry(ha,
+ (struct status_cont_entry *) sts_entry);
break;
case ET_COMMAND:
diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c
index 051b0f5e8c8..09d6d4b76f3 100644
--- a/drivers/scsi/qla4xxx/ql4_mbx.c
+++ b/drivers/scsi/qla4xxx/ql4_mbx.c
@@ -385,16 +385,6 @@ int qla4xxx_get_firmware_status(struct scsi_qla_host * ha)
mbox_sts[0]));
return QLA_ERROR;
}
-
- /* High-water mark of IOCBs */
- ha->iocb_hiwat = mbox_sts[2];
- if (ha->iocb_hiwat > IOCB_HIWAT_CUSHION)
- ha->iocb_hiwat -= IOCB_HIWAT_CUSHION;
- else
- dev_info(&ha->pdev->dev, "WARNING!!! You have less than %d "
- "firmware IOCBs available (%d).\n",
- IOCB_HIWAT_CUSHION, ha->iocb_hiwat);
-
return QLA_SUCCESS;
}
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index ec9da6ce848..40e3cafb3a9 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -66,6 +66,7 @@ static int qla4xxx_sess_get_param(struct iscsi_cls_session *sess,
static int qla4xxx_host_get_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf);
static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session);
+static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc);
/*
* SCSI host template entry points
@@ -89,6 +90,7 @@ static struct scsi_host_template qla4xxx_driver_template = {
.eh_device_reset_handler = qla4xxx_eh_device_reset,
.eh_target_reset_handler = qla4xxx_eh_target_reset,
.eh_host_reset_handler = qla4xxx_eh_host_reset,
+ .eh_timed_out = qla4xxx_eh_cmd_timed_out,
.slave_configure = qla4xxx_slave_configure,
.slave_alloc = qla4xxx_slave_alloc,
@@ -124,6 +126,21 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
static struct scsi_transport_template *qla4xxx_scsi_transport;
+static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc)
+{
+ struct iscsi_cls_session *session;
+ struct ddb_entry *ddb_entry;
+
+ session = starget_to_session(scsi_target(sc->device));
+ ddb_entry = session->dd_data;
+
+ /* if we are not logged in then the LLD is going to clean up the cmd */
+ if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE)
+ return BLK_EH_RESET_TIMER;
+ else
+ return BLK_EH_NOT_HANDLED;
+}
+
static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session)
{
struct ddb_entry *ddb_entry = session->dd_data;
@@ -904,18 +921,17 @@ static int qla4xxx_recover_adapter(struct scsi_qla_host *ha,
/* Flush any pending ddb changed AENs */
qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
+ qla4xxx_flush_active_srbs(ha);
+
/* Reset the firmware. If successful, function
* returns with ISP interrupts enabled.
*/
- if (status == QLA_SUCCESS) {
- DEBUG2(printk("scsi%ld: %s - Performing soft reset..\n",
- ha->host_no, __func__));
- qla4xxx_flush_active_srbs(ha);
- if (ql4xxx_lock_drvr_wait(ha) == QLA_SUCCESS)
- status = qla4xxx_soft_reset(ha);
- else
- status = QLA_ERROR;
- }
+ DEBUG2(printk("scsi%ld: %s - Performing soft reset..\n",
+ ha->host_no, __func__));
+ if (ql4xxx_lock_drvr_wait(ha) == QLA_SUCCESS)
+ status = qla4xxx_soft_reset(ha);
+ else
+ status = QLA_ERROR;
/* Flush any pending ddb changed AENs */
qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
@@ -1527,11 +1543,9 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd)
{
struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
struct ddb_entry *ddb_entry = cmd->device->hostdata;
- struct srb *sp;
int ret = FAILED, stat;
- sp = (struct srb *) cmd->SCp.ptr;
- if (!sp || !ddb_entry)
+ if (!ddb_entry)
return ret;
dev_info(&ha->pdev->dev,
@@ -1644,7 +1658,7 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd)
ha = (struct scsi_qla_host *) cmd->device->host->hostdata;
dev_info(&ha->pdev->dev,
- "scsi(%ld:%d:%d:%d): ADAPTER RESET ISSUED.\n", ha->host_no,
+ "scsi(%ld:%d:%d:%d): HOST RESET ISSUED.\n", ha->host_no,
cmd->device->channel, cmd->device->id, cmd->device->lun);
if (qla4xxx_wait_for_hba_online(ha) != QLA_SUCCESS) {
diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h
index ab984cb89ce..6980cb279c8 100644
--- a/drivers/scsi/qla4xxx/ql4_version.h
+++ b/drivers/scsi/qla4xxx/ql4_version.h
@@ -5,5 +5,5 @@
* See LICENSE.qla4xxx for copyright and licensing details.
*/
-#define QLA4XXX_DRIVER_VERSION "5.01.00-k8"
+#define QLA4XXX_DRIVER_VERSION "5.01.00-k9"
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 783e33c65eb..b47240ca4b1 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -990,7 +990,7 @@ int iscsi_offload_mesg(struct Scsi_Host *shost,
struct iscsi_uevent *ev;
int len = NLMSG_SPACE(sizeof(*ev) + data_size);
- skb = alloc_skb(len, GFP_NOIO);
+ skb = alloc_skb(len, GFP_ATOMIC);
if (!skb) {
printk(KERN_ERR "can not deliver iscsi offload message:OOM\n");
return -ENOMEM;
@@ -1012,7 +1012,7 @@ int iscsi_offload_mesg(struct Scsi_Host *shost,
memcpy((char *)ev + sizeof(*ev), data, data_size);
- return iscsi_multicast_skb(skb, ISCSI_NL_GRP_UIP, GFP_NOIO);
+ return iscsi_multicast_skb(skb, ISCSI_NL_GRP_UIP, GFP_ATOMIC);
}
EXPORT_SYMBOL_GPL(iscsi_offload_mesg);
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 5616cd780ff..b7b9fec67a9 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1840,6 +1840,18 @@ static void sd_read_block_characteristics(struct scsi_disk *sdkp)
kfree(buffer);
}
+static int sd_try_extended_inquiry(struct scsi_device *sdp)
+{
+ /*
+ * Although VPD inquiries can go to SCSI-2 type devices,
+ * some USB ones crash on receiving them, and the pages
+ * we currently ask for are for SPC-3 and beyond
+ */
+ if (sdp->scsi_level > SCSI_SPC_2)
+ return 1;
+ return 0;
+}
+
/**
* sd_revalidate_disk - called the first time a new disk is seen,
* performs disk spin up, read_capacity, etc.
@@ -1877,8 +1889,12 @@ static int sd_revalidate_disk(struct gendisk *disk)
*/
if (sdkp->media_present) {
sd_read_capacity(sdkp, buffer);
- sd_read_block_limits(sdkp);
- sd_read_block_characteristics(sdkp);
+
+ if (sd_try_extended_inquiry(sdp)) {
+ sd_read_block_limits(sdkp);
+ sd_read_block_characteristics(sdkp);
+ }
+
sd_read_write_protect_flag(sdkp, buffer);
sd_read_cache_type(sdkp, buffer);
sd_read_app_tag_own(sdkp, buffer);
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 037c1e0b7c4..6553833c12d 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -527,7 +527,7 @@ config SERIAL_S3C24A0
config SERIAL_S3C6400
tristate "Samsung S3C6400/S3C6410 Serial port support"
- depends on SERIAL_SAMSUNG && (CPU_S3C600 || CPU_S3C6410)
+ depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410)
default y
help
Serial port support for the Samsung S3C6400 and S3C6410
diff --git a/drivers/serial/s3c2400.c b/drivers/serial/s3c2400.c
index fb00ed5296e..fed1a9a1ffb 100644
--- a/drivers/serial/s3c2400.c
+++ b/drivers/serial/s3c2400.c
@@ -76,7 +76,7 @@ static int s3c2400_serial_probe(struct platform_device *dev)
return s3c24xx_serial_probe(dev, &s3c2400_uart_inf);
}
-static struct platform_driver s3c2400_serial_drv = {
+static struct platform_driver s3c2400_serial_driver = {
.probe = s3c2400_serial_probe,
.remove = __devexit_p(s3c24xx_serial_remove),
.driver = {
@@ -85,16 +85,16 @@ static struct platform_driver s3c2400_serial_drv = {
},
};
-s3c24xx_console_init(&s3c2400_serial_drv, &s3c2400_uart_inf);
+s3c24xx_console_init(&s3c2400_serial_driver, &s3c2400_uart_inf);
static inline int s3c2400_serial_init(void)
{
- return s3c24xx_serial_init(&s3c2400_serial_drv, &s3c2400_uart_inf);
+ return s3c24xx_serial_init(&s3c2400_serial_driver, &s3c2400_uart_inf);
}
static inline void s3c2400_serial_exit(void)
{
- platform_driver_unregister(&s3c2400_serial_drv);
+ platform_driver_unregister(&s3c2400_serial_driver);
}
module_init(s3c2400_serial_init);
diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c
index b5d7cbcba2a..c99f0821cae 100644
--- a/drivers/serial/s3c2410.c
+++ b/drivers/serial/s3c2410.c
@@ -88,7 +88,7 @@ static int s3c2410_serial_probe(struct platform_device *dev)
return s3c24xx_serial_probe(dev, &s3c2410_uart_inf);
}
-static struct platform_driver s3c2410_serial_drv = {
+static struct platform_driver s3c2410_serial_driver = {
.probe = s3c2410_serial_probe,
.remove = __devexit_p(s3c24xx_serial_remove),
.driver = {
@@ -97,16 +97,16 @@ static struct platform_driver s3c2410_serial_drv = {
},
};
-s3c24xx_console_init(&s3c2410_serial_drv, &s3c2410_uart_inf);
+s3c24xx_console_init(&s3c2410_serial_driver, &s3c2410_uart_inf);
static int __init s3c2410_serial_init(void)
{
- return s3c24xx_serial_init(&s3c2410_serial_drv, &s3c2410_uart_inf);
+ return s3c24xx_serial_init(&s3c2410_serial_driver, &s3c2410_uart_inf);
}
static void __exit s3c2410_serial_exit(void)
{
- platform_driver_unregister(&s3c2410_serial_drv);
+ platform_driver_unregister(&s3c2410_serial_driver);
}
module_init(s3c2410_serial_init);
diff --git a/drivers/serial/s3c2412.c b/drivers/serial/s3c2412.c
index 11dcb90bdfe..6e057d8809d 100644
--- a/drivers/serial/s3c2412.c
+++ b/drivers/serial/s3c2412.c
@@ -121,7 +121,7 @@ static int s3c2412_serial_probe(struct platform_device *dev)
return s3c24xx_serial_probe(dev, &s3c2412_uart_inf);
}
-static struct platform_driver s3c2412_serial_drv = {
+static struct platform_driver s3c2412_serial_driver = {
.probe = s3c2412_serial_probe,
.remove = __devexit_p(s3c24xx_serial_remove),
.driver = {
@@ -130,16 +130,16 @@ static struct platform_driver s3c2412_serial_drv = {
},
};
-s3c24xx_console_init(&s3c2412_serial_drv, &s3c2412_uart_inf);
+s3c24xx_console_init(&s3c2412_serial_driver, &s3c2412_uart_inf);
static inline int s3c2412_serial_init(void)
{
- return s3c24xx_serial_init(&s3c2412_serial_drv, &s3c2412_uart_inf);
+ return s3c24xx_serial_init(&s3c2412_serial_driver, &s3c2412_uart_inf);
}
static inline void s3c2412_serial_exit(void)
{
- platform_driver_unregister(&s3c2412_serial_drv);
+ platform_driver_unregister(&s3c2412_serial_driver);
}
module_init(s3c2412_serial_init);
diff --git a/drivers/serial/s3c2440.c b/drivers/serial/s3c2440.c
index 06c5b0cc47a..69ff5d340f0 100644
--- a/drivers/serial/s3c2440.c
+++ b/drivers/serial/s3c2440.c
@@ -151,7 +151,7 @@ static int s3c2440_serial_probe(struct platform_device *dev)
return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);
}
-static struct platform_driver s3c2440_serial_drv = {
+static struct platform_driver s3c2440_serial_driver = {
.probe = s3c2440_serial_probe,
.remove = __devexit_p(s3c24xx_serial_remove),
.driver = {
@@ -160,16 +160,16 @@ static struct platform_driver s3c2440_serial_drv = {
},
};
-s3c24xx_console_init(&s3c2440_serial_drv, &s3c2440_uart_inf);
+s3c24xx_console_init(&s3c2440_serial_driver, &s3c2440_uart_inf);
static int __init s3c2440_serial_init(void)
{
- return s3c24xx_serial_init(&s3c2440_serial_drv, &s3c2440_uart_inf);
+ return s3c24xx_serial_init(&s3c2440_serial_driver, &s3c2440_uart_inf);
}
static void __exit s3c2440_serial_exit(void)
{
- platform_driver_unregister(&s3c2440_serial_drv);
+ platform_driver_unregister(&s3c2440_serial_driver);
}
module_init(s3c2440_serial_init);
diff --git a/drivers/serial/s3c24a0.c b/drivers/serial/s3c24a0.c
index 786a067d62a..26c49e18bdd 100644
--- a/drivers/serial/s3c24a0.c
+++ b/drivers/serial/s3c24a0.c
@@ -92,7 +92,7 @@ static int s3c24a0_serial_probe(struct platform_device *dev)
return s3c24xx_serial_probe(dev, &s3c24a0_uart_inf);
}
-static struct platform_driver s3c24a0_serial_drv = {
+static struct platform_driver s3c24a0_serial_driver = {
.probe = s3c24a0_serial_probe,
.remove = __devexit_p(s3c24xx_serial_remove),
.driver = {
@@ -101,16 +101,16 @@ static struct platform_driver s3c24a0_serial_drv = {
},
};
-s3c24xx_console_init(&s3c24a0_serial_drv, &s3c24a0_uart_inf);
+s3c24xx_console_init(&s3c24a0_serial_driver, &s3c24a0_uart_inf);
static int __init s3c24a0_serial_init(void)
{
- return s3c24xx_serial_init(&s3c24a0_serial_drv, &s3c24a0_uart_inf);
+ return s3c24xx_serial_init(&s3c24a0_serial_driver, &s3c24a0_uart_inf);
}
static void __exit s3c24a0_serial_exit(void)
{
- platform_driver_unregister(&s3c24a0_serial_drv);
+ platform_driver_unregister(&s3c24a0_serial_driver);
}
module_init(s3c24a0_serial_init);
diff --git a/drivers/serial/s3c6400.c b/drivers/serial/s3c6400.c
index 48f1a3781f0..4be92ab5005 100644
--- a/drivers/serial/s3c6400.c
+++ b/drivers/serial/s3c6400.c
@@ -122,7 +122,7 @@ static int s3c6400_serial_probe(struct platform_device *dev)
return s3c24xx_serial_probe(dev, &s3c6400_uart_inf);
}
-static struct platform_driver s3c6400_serial_drv = {
+static struct platform_driver s3c6400_serial_driver = {
.probe = s3c6400_serial_probe,
.remove = __devexit_p(s3c24xx_serial_remove),
.driver = {
@@ -131,16 +131,16 @@ static struct platform_driver s3c6400_serial_drv = {
},
};
-s3c24xx_console_init(&s3c6400_serial_drv, &s3c6400_uart_inf);
+s3c24xx_console_init(&s3c6400_serial_driver, &s3c6400_uart_inf);
static int __init s3c6400_serial_init(void)
{
- return s3c24xx_serial_init(&s3c6400_serial_drv, &s3c6400_uart_inf);
+ return s3c24xx_serial_init(&s3c6400_serial_driver, &s3c6400_uart_inf);
}
static void __exit s3c6400_serial_exit(void)
{
- platform_driver_unregister(&s3c6400_serial_drv);
+ platform_driver_unregister(&s3c6400_serial_driver);
}
module_init(s3c6400_serial_init);
diff --git a/drivers/serial/serial_ks8695.c b/drivers/serial/serial_ks8695.c
index 998e89dc5aa..e0665630e4d 100644
--- a/drivers/serial/serial_ks8695.c
+++ b/drivers/serial/serial_ks8695.c
@@ -549,7 +549,7 @@ static struct uart_port ks8695uart_ports[SERIAL_KS8695_NR] = {
.mapbase = KS8695_UART_VA,
.iotype = SERIAL_IO_MEM,
.irq = KS8695_IRQ_UART_TX,
- .uartclk = CLOCK_TICK_RATE * 16,
+ .uartclk = KS8695_CLOCK_RATE * 16,
.fifosize = 16,
.ops = &ks8695uart_pops,
.flags = ASYNC_BOOT_AUTOCONF,
diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c
index e0d44af4745..3f3119d760d 100644
--- a/drivers/spi/spi_s3c24xx.c
+++ b/drivers/spi/spi_s3c24xx.c
@@ -111,29 +111,32 @@ static int s3c24xx_spi_setupxfer(struct spi_device *spi,
unsigned int bpw;
unsigned int hz;
unsigned int div;
+ unsigned long clk;
bpw = t ? t->bits_per_word : spi->bits_per_word;
hz = t ? t->speed_hz : spi->max_speed_hz;
+ if (!bpw)
+ bpw = 8;
+
+ if (!hz)
+ hz = spi->max_speed_hz;
+
if (bpw != 8) {
dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw);
return -EINVAL;
}
- div = clk_get_rate(hw->clk) / hz;
-
- /* is clk = pclk / (2 * (pre+1)), or is it
- * clk = (pclk * 2) / ( pre + 1) */
-
- div /= 2;
-
- if (div > 0)
- div -= 1;
+ clk = clk_get_rate(hw->clk);
+ div = DIV_ROUND_UP(clk, hz * 2) - 1;
if (div > 255)
div = 255;
- dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)\n", div, hz);
+ dev_dbg(&spi->dev, "setting pre-scaler to %d (wanted %d, got %ld)\n",
+ div, hz, clk / (2 * (div + 1)));
+
+
writeb(div, hw->regs + S3C2410_SPPRE);
spin_lock(&hw->bitbang.lock);
diff --git a/drivers/staging/b3dfg/Kconfig b/drivers/staging/b3dfg/Kconfig
index 524231047de..9e6573cf97d 100644
--- a/drivers/staging/b3dfg/Kconfig
+++ b/drivers/staging/b3dfg/Kconfig
@@ -1,5 +1,6 @@
config B3DFG
tristate "Brontes 3d Frame Framegrabber"
+ depends on PCI
default n
---help---
This driver provides support for the Brontes 3d Framegrabber
diff --git a/drivers/staging/heci/Kconfig b/drivers/staging/heci/Kconfig
index ae8d588d3a2..c7206f8bcd9 100644
--- a/drivers/staging/heci/Kconfig
+++ b/drivers/staging/heci/Kconfig
@@ -1,5 +1,6 @@
config HECI
tristate "Intel Management Engine Interface (MEI) Support"
+ depends on PCI
---help---
The Intel Management Engine Interface (Intel MEI) driver allows
applications to access the Active Management Technology
diff --git a/drivers/staging/rspiusb/rspiusb.c b/drivers/staging/rspiusb/rspiusb.c
index 2f8155c1968..04e2f92c0f6 100644
--- a/drivers/staging/rspiusb/rspiusb.c
+++ b/drivers/staging/rspiusb/rspiusb.c
@@ -716,6 +716,8 @@ static int MapUserBuffer(struct ioctl_struct *io, struct device_extension *pdx)
pdx->PixelUrb[frameInfo][i]->transfer_flags =
URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT;
}
+ if (i == 0)
+ return -EINVAL;
/* only interrupt when last URB completes */
pdx->PixelUrb[frameInfo][--i]->transfer_flags &= ~URB_NO_INTERRUPT;
pdx->pendedPixelUrbs[frameInfo] =
diff --git a/drivers/staging/rt2860/rt_linux.h b/drivers/staging/rt2860/rt_linux.h
index 85175c18243..25b53ac3f82 100644
--- a/drivers/staging/rt2860/rt_linux.h
+++ b/drivers/staging/rt2860/rt_linux.h
@@ -43,9 +43,6 @@
#include "rtmp_type.h"
#include <linux/module.h>
#include <linux/kernel.h>
-#if !defined(RT2860) && !defined(RT30xx)
-#include <linux/kthread.h>
-#endif
#include <linux/spinlock.h>
#include <linux/init.h>
@@ -166,9 +163,7 @@ typedef int (*HARD_START_XMIT_FUNC)(struct sk_buff *skb, struct net_device *net_
#ifndef RT30xx
typedef struct pid * THREAD_PID;
-#ifdef RT2860
#define THREAD_PID_INIT_VALUE NULL
-#endif
#define GET_PID(_v) find_get_pid(_v)
#define GET_PID_NUMBER(_v) pid_nr(_v)
#define CHECK_PID_LEGALITY(_pid) if (pid_nr(_pid) >= 0)
@@ -188,12 +183,12 @@ struct os_cookie {
dma_addr_t pAd_pa;
#endif
#ifdef RT2870
- struct usb_device *pUsb_Dev;
+ struct usb_device *pUsb_Dev;
#ifndef RT30xx
- struct task_struct *MLMEThr_task;
- struct task_struct *RTUSBCmdThr_task;
- struct task_struct *TimerQThr_task;
+ THREAD_PID MLMEThr_pid;
+ THREAD_PID RTUSBCmdThr_pid;
+ THREAD_PID TimerQThr_pid;
#endif
#ifdef RT30xx
struct pid *MLMEThr_pid;
diff --git a/drivers/staging/rt2870/2870_main_dev.c b/drivers/staging/rt2870/2870_main_dev.c
index dd01c64fbf6..a4e8696ca39 100644
--- a/drivers/staging/rt2870/2870_main_dev.c
+++ b/drivers/staging/rt2870/2870_main_dev.c
@@ -235,7 +235,7 @@ INT MlmeThread(
DBGPRINT(RT_DEBUG_TRACE,( "<---%s\n",__func__));
#ifndef RT30xx
- pObj->MLMEThr_task = NULL;
+ pObj->MLMEThr_pid = THREAD_PID_INIT_VALUE;
#endif
#ifdef RT30xx
pObj->MLMEThr_pid = NULL;
@@ -348,7 +348,7 @@ INT RTUSBCmdThread(
DBGPRINT(RT_DEBUG_TRACE,( "<---RTUSBCmdThread\n"));
#ifndef RT30xx
- pObj->RTUSBCmdThr_task = NULL;
+ pObj->RTUSBCmdThr_pid = THREAD_PID_INIT_VALUE;
#endif
#ifdef RT30xx
pObj->RTUSBCmdThr_pid = NULL;
@@ -447,7 +447,7 @@ INT TimerQThread(
DBGPRINT(RT_DEBUG_TRACE,( "<---%s\n",__func__));
#ifndef RT30xx
- pObj->TimerQThr_task = NULL;
+ pObj->TimerQThr_pid = THREAD_PID_INIT_VALUE;
#endif
#ifdef RT30xx
pObj->TimerQThr_pid = NULL;
@@ -883,46 +883,69 @@ VOID RT28xxThreadTerminate(
// Terminate Threads
#ifndef RT30xx
- BUG_ON(pObj->TimerQThr_task == NULL);
- CHECK_PID_LEGALITY(task_pid(pObj->TimerQThr_task))
+ CHECK_PID_LEGALITY(pObj->TimerQThr_pid)
{
POS_COOKIE pObj = (POS_COOKIE)pAd->OS_Cookie;
- printk(KERN_DEBUG "Terminate the TimerQThr pid=%d!\n",
- pid_nr(task_pid(pObj->TimerQThr_task)));
+ printk("Terminate the TimerQThr_pid=%d!\n", GET_PID_NUMBER(pObj->TimerQThr_pid));
mb();
pAd->TimerFunc_kill = 1;
mb();
- kthread_stop(pObj->TimerQThr_task);
- pObj->TimerQThr_task = NULL;
+ ret = KILL_THREAD_PID(pObj->TimerQThr_pid, SIGTERM, 1);
+ if (ret)
+ {
+ printk(KERN_WARNING "%s: unable to stop TimerQThread, pid=%d, ret=%d!\n",
+ pAd->net_dev->name, GET_PID_NUMBER(pObj->TimerQThr_pid), ret);
+ }
+ else
+ {
+ wait_for_completion(&pAd->TimerQComplete);
+ pObj->TimerQThr_pid = THREAD_PID_INIT_VALUE;
+ }
}
- BUG_ON(pObj->MLMEThr_task == NULL);
- CHECK_PID_LEGALITY(task_pid(pObj->MLMEThr_task))
+ CHECK_PID_LEGALITY(pObj->MLMEThr_pid)
{
- printk(KERN_DEBUG "Terminate the MLMEThr pid=%d!\n",
- pid_nr(task_pid(pObj->MLMEThr_task)));
+ printk("Terminate the MLMEThr_pid=%d!\n", GET_PID_NUMBER(pObj->MLMEThr_pid));
mb();
pAd->mlme_kill = 1;
//RT28XX_MLME_HANDLER(pAd);
mb();
- kthread_stop(pObj->MLMEThr_task);
- pObj->MLMEThr_task = NULL;
+ ret = KILL_THREAD_PID(pObj->MLMEThr_pid, SIGTERM, 1);
+ if (ret)
+ {
+ printk (KERN_WARNING "%s: unable to Mlme thread, pid=%d, ret=%d!\n",
+ pAd->net_dev->name, GET_PID_NUMBER(pObj->MLMEThr_pid), ret);
+ }
+ else
+ {
+ //wait_for_completion (&pAd->notify);
+ wait_for_completion (&pAd->mlmeComplete);
+ pObj->MLMEThr_pid = THREAD_PID_INIT_VALUE;
+ }
}
- BUG_ON(pObj->RTUSBCmdThr_task == NULL);
- CHECK_PID_LEGALITY(task_pid(pObj->RTUSBCmdThr_task))
+ CHECK_PID_LEGALITY(pObj->RTUSBCmdThr_pid)
{
- printk(KERN_DEBUG "Terminate the RTUSBCmdThr pid=%d!\n",
- pid_nr(task_pid(pObj->RTUSBCmdThr_task)));
+ printk("Terminate the RTUSBCmdThr_pid=%d!\n", GET_PID_NUMBER(pObj->RTUSBCmdThr_pid));
mb();
NdisAcquireSpinLock(&pAd->CmdQLock);
pAd->CmdQ.CmdQState = RT2870_THREAD_STOPED;
NdisReleaseSpinLock(&pAd->CmdQLock);
mb();
//RTUSBCMDUp(pAd);
- kthread_stop(pObj->RTUSBCmdThr_task);
- pObj->RTUSBCmdThr_task = NULL;
+ ret = KILL_THREAD_PID(pObj->RTUSBCmdThr_pid, SIGTERM, 1);
+ if (ret)
+ {
+ printk(KERN_WARNING "%s: unable to RTUSBCmd thread, pid=%d, ret=%d!\n",
+ pAd->net_dev->name, GET_PID_NUMBER(pObj->RTUSBCmdThr_pid), ret);
+ }
+ else
+ {
+ //wait_for_completion (&pAd->notify);
+ wait_for_completion (&pAd->CmdQComplete);
+ pObj->RTUSBCmdThr_pid = THREAD_PID_INIT_VALUE;
+ }
}
#endif
#ifdef RT30xx
@@ -1045,7 +1068,7 @@ BOOLEAN RT28XXChipsetCheck(
dev_p->descriptor.idProduct == rtusb_usb_id[i].idProduct)
{
#ifndef RT30xx
- printk(KERN_DEBUG "rt2870: idVendor = 0x%x, idProduct = 0x%x\n",
+ printk("rt2870: idVendor = 0x%x, idProduct = 0x%x\n",
#endif
#ifdef RT30xx
printk("rt2870: idVendor = 0x%x, idProduct = 0x%x\n",
diff --git a/drivers/staging/rt2870/common/2870_rtmp_init.c b/drivers/staging/rt2870/common/2870_rtmp_init.c
index 0f4c8af97e4..80909e9ab5a 100644
--- a/drivers/staging/rt2870/common/2870_rtmp_init.c
+++ b/drivers/staging/rt2870/common/2870_rtmp_init.c
@@ -700,8 +700,8 @@ NDIS_STATUS AdapterBlockAllocateMemory(
usb_dev = pObj->pUsb_Dev;
#ifndef RT30xx
- pObj->MLMEThr_task = NULL;
- pObj->RTUSBCmdThr_task = NULL;
+ pObj->MLMEThr_pid = THREAD_PID_INIT_VALUE;
+ pObj->RTUSBCmdThr_pid = THREAD_PID_INIT_VALUE;
#endif
#ifdef RT30xx
pObj->MLMEThr_pid = NULL;
@@ -743,7 +743,7 @@ NDIS_STATUS CreateThreads(
PRTMP_ADAPTER pAd = net_dev->ml_priv;
POS_COOKIE pObj = (POS_COOKIE) pAd->OS_Cookie;
#ifndef RT30xx
- struct task_struct *tsk;
+ pid_t pid_number = -1;
#endif
#ifdef RT30xx
pid_t pid_number;
@@ -762,10 +762,10 @@ NDIS_STATUS CreateThreads(
// Creat MLME Thread
#ifndef RT30xx
- pObj->MLMEThr_task = NULL;
- tsk = kthread_run(MlmeThread, pAd, "%s", pAd->net_dev->name);
-
- if (IS_ERR(tsk)) {
+ pObj->MLMEThr_pid= THREAD_PID_INIT_VALUE;
+ pid_number = kernel_thread(MlmeThread, pAd, CLONE_VM);
+ if (pid_number < 0)
+ {
#endif
#ifdef RT30xx
pObj->MLMEThr_pid = NULL;
@@ -778,7 +778,7 @@ NDIS_STATUS CreateThreads(
}
#ifndef RT30xx
- pObj->MLMEThr_task = tsk;
+ pObj->MLMEThr_pid = GET_PID(pid_number);
#endif
#ifdef RT30xx
pObj->MLMEThr_pid = find_get_pid(pid_number);
@@ -788,10 +788,9 @@ NDIS_STATUS CreateThreads(
// Creat Command Thread
#ifndef RT30xx
- pObj->RTUSBCmdThr_task = NULL;
- tsk = kthread_run(RTUSBCmdThread, pAd, "%s", pAd->net_dev->name);
-
- if (IS_ERR(tsk) < 0)
+ pObj->RTUSBCmdThr_pid= THREAD_PID_INIT_VALUE;
+ pid_number = kernel_thread(RTUSBCmdThread, pAd, CLONE_VM);
+ if (pid_number < 0)
#endif
#ifdef RT30xx
pObj->RTUSBCmdThr_pid = NULL;
@@ -804,7 +803,7 @@ NDIS_STATUS CreateThreads(
}
#ifndef RT30xx
- pObj->RTUSBCmdThr_task = tsk;
+ pObj->RTUSBCmdThr_pid = GET_PID(pid_number);
#endif
#ifdef RT30xx
pObj->RTUSBCmdThr_pid = find_get_pid(pid_number);
@@ -812,9 +811,9 @@ NDIS_STATUS CreateThreads(
wait_for_completion(&(pAd->CmdQComplete));
#ifndef RT30xx
- pObj->TimerQThr_task = NULL;
- tsk = kthread_run(TimerQThread, pAd, "%s", pAd->net_dev->name);
- if (IS_ERR(tsk) < 0)
+ pObj->TimerQThr_pid= THREAD_PID_INIT_VALUE;
+ pid_number = kernel_thread(TimerQThread, pAd, CLONE_VM);
+ if (pid_number < 0)
#endif
#ifdef RT30xx
pObj->TimerQThr_pid = NULL;
@@ -826,7 +825,7 @@ NDIS_STATUS CreateThreads(
return NDIS_STATUS_FAILURE;
}
#ifndef RT30xx
- pObj->TimerQThr_task = tsk;
+ pObj->TimerQThr_pid = GET_PID(pid_number);
#endif
#ifdef RT30xx
pObj->TimerQThr_pid = find_get_pid(pid_number);
diff --git a/drivers/staging/rt2870/common/rtusb_io.c b/drivers/staging/rt2870/common/rtusb_io.c
index fd1b0c18f2a..704b5c2d509 100644
--- a/drivers/staging/rt2870/common/rtusb_io.c
+++ b/drivers/staging/rt2870/common/rtusb_io.c
@@ -984,8 +984,7 @@ NDIS_STATUS RTUSBEnqueueCmdFromNdis(
POS_COOKIE pObj = (POS_COOKIE) pAd->OS_Cookie;
#ifndef RT30xx
- BUG_ON(pObj->RTUSBCmdThr_task == NULL);
- CHECK_PID_LEGALITY(task_pid(pObj->RTUSBCmdThr_task))
+ CHECK_PID_LEGALITY(pObj->RTUSBCmdThr_pid)
#endif
#ifdef RT30xx
if (pObj->RTUSBCmdThr_pid < 0)
diff --git a/drivers/staging/rt2870/rt2870.h b/drivers/staging/rt2870/rt2870.h
index 29e3b53e52a..2b8872b2fd9 100644
--- a/drivers/staging/rt2870/rt2870.h
+++ b/drivers/staging/rt2870/rt2870.h
@@ -79,6 +79,7 @@
{ \
{USB_DEVICE(0x148F,0x2770)}, /* Ralink */ \
{USB_DEVICE(0x1737,0x0071)}, /* Linksys WUSB600N */ \
+ {USB_DEVICE(0x1737,0x0070)}, /* Linksys */ \
{USB_DEVICE(0x148F,0x2870)}, /* Ralink */ \
{USB_DEVICE(0x148F,0x3070)}, /* Ralink */ \
{USB_DEVICE(0x0B05,0x1731)}, /* Asus */ \
@@ -93,12 +94,14 @@
{USB_DEVICE(0x14B2,0x3C06)}, /* Conceptronic */ \
{USB_DEVICE(0x14B2,0x3C28)}, /* Conceptronic */ \
{USB_DEVICE(0x2019,0xED06)}, /* Planex Communications, Inc. */ \
+ {USB_DEVICE(0x2019,0xED14)}, /* Planex Communications, Inc. */ \
{USB_DEVICE(0x2019,0xAB25)}, /* Planex Communications, Inc. RT3070 */ \
{USB_DEVICE(0x07D1,0x3C09)}, /* D-Link */ \
{USB_DEVICE(0x07D1,0x3C11)}, /* D-Link */ \
{USB_DEVICE(0x14B2,0x3C07)}, /* AL */ \
{USB_DEVICE(0x14B2,0x3C12)}, /* AL */ \
{USB_DEVICE(0x050D,0x8053)}, /* Belkin */ \
+ {USB_DEVICE(0x050D,0x815C)}, /* Belkin */ \
{USB_DEVICE(0x14B2,0x3C23)}, /* Airlink */ \
{USB_DEVICE(0x14B2,0x3C27)}, /* Airlink */ \
{USB_DEVICE(0x07AA,0x002F)}, /* Corega */ \
@@ -587,16 +590,14 @@ VOID RTUSBBulkRxComplete(purbb_t pUrb, struct pt_regs *pt_regs);
#define RTUSBMlmeUp(pAd) \
{ \
POS_COOKIE pObj = (POS_COOKIE) pAd->OS_Cookie; \
- BUG_ON(pObj->MLMEThr_task == NULL); \
- CHECK_PID_LEGALITY(task_pid(pObj->MLMEThr_task)) \
+ CHECK_PID_LEGALITY(pObj->MLMEThr_pid) \
up(&(pAd->mlme_semaphore)); \
}
#define RTUSBCMDUp(pAd) \
{ \
POS_COOKIE pObj = (POS_COOKIE) pAd->OS_Cookie; \
- BUG_ON(pObj->RTUSBCmdThr_task == NULL); \
- CHECK_PID_LEGALITY(task_pid(pObj->RTUSBCmdThr_task)) \
+ CHECK_PID_LEGALITY(pObj->RTUSBCmdThr_pid) \
up(&(pAd->RTUSBCmd_semaphore)); \
}
#endif
diff --git a/drivers/staging/rtl8192su/ieee80211.h b/drivers/staging/rtl8192su/ieee80211.h
index 0edb09a536f..ea973931803 100644
--- a/drivers/staging/rtl8192su/ieee80211.h
+++ b/drivers/staging/rtl8192su/ieee80211.h
@@ -2645,7 +2645,7 @@ extern int ieee80211_encrypt_fragment(
struct sk_buff *frag,
int hdr_len);
-extern int ieee80211_xmit(struct sk_buff *skb,
+extern int rtl8192_ieee80211_xmit(struct sk_buff *skb,
struct net_device *dev);
extern void ieee80211_txb_free(struct ieee80211_txb *);
diff --git a/drivers/staging/rtl8192su/ieee80211/ieee80211.h b/drivers/staging/rtl8192su/ieee80211/ieee80211.h
index 720bfcbfadc..5e3a2cbed2b 100644
--- a/drivers/staging/rtl8192su/ieee80211/ieee80211.h
+++ b/drivers/staging/rtl8192su/ieee80211/ieee80211.h
@@ -2645,7 +2645,7 @@ extern int ieee80211_encrypt_fragment(
struct sk_buff *frag,
int hdr_len);
-extern int ieee80211_xmit(struct sk_buff *skb,
+extern int rtl8192_ieee80211_xmit(struct sk_buff *skb,
struct net_device *dev);
extern void ieee80211_txb_free(struct ieee80211_txb *);
diff --git a/drivers/staging/rtl8192su/ieee80211/ieee80211_tx.c b/drivers/staging/rtl8192su/ieee80211/ieee80211_tx.c
index 7294572b990..cba12b84be5 100644
--- a/drivers/staging/rtl8192su/ieee80211/ieee80211_tx.c
+++ b/drivers/staging/rtl8192su/ieee80211/ieee80211_tx.c
@@ -618,7 +618,7 @@ void ieee80211_query_seqnum(struct ieee80211_device*ieee, struct sk_buff* skb, u
}
}
-int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
+int rtl8192_ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0))
struct ieee80211_device *ieee = netdev_priv(dev);
@@ -943,5 +943,6 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
return 1;
}
+EXPORT_SYMBOL(rtl8192_ieee80211_xmit);
EXPORT_SYMBOL(ieee80211_txb_free);
diff --git a/drivers/staging/rtl8192su/r8192U_core.c b/drivers/staging/rtl8192su/r8192U_core.c
index 4ab250743e8..70f81a8f129 100644
--- a/drivers/staging/rtl8192su/r8192U_core.c
+++ b/drivers/staging/rtl8192su/r8192U_core.c
@@ -12142,7 +12142,7 @@ static const struct net_device_ops rtl8192_netdev_ops = {
.ndo_set_mac_address = r8192_set_mac_adr,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = eth_change_mtu,
- .ndo_start_xmit = ieee80211_xmit,
+ .ndo_start_xmit = rtl8192_ieee80211_xmit,
};
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 0a69672097a..4e83c297ec9 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -953,7 +953,12 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
mutex_lock(&tz->lock);
- tz->ops->get_temp(tz, &temp);
+ if (tz->ops->get_temp(tz, &temp)) {
+ /* get_temp failed - retry it later */
+ printk(KERN_WARNING PREFIX "failed to read out thermal zone "
+ "%d\n", tz->id);
+ goto leave;
+ }
for (count = 0; count < tz->trips; count++) {
tz->ops->get_trip_type(tz, count, &trip_type);
@@ -1005,6 +1010,8 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
THERMAL_TRIPS_NONE);
tz->last_temperature = temp;
+
+ leave:
if (tz->passive)
thermal_zone_device_set_polling(tz, tz->passive_delay);
else if (tz->polling_delay)
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index e1f89416ef8..2bfc41ece0e 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -387,7 +387,6 @@ static void acm_rx_tasklet(unsigned long _acm)
struct acm_ru *rcv;
unsigned long flags;
unsigned char throttled;
- struct usb_host_endpoint *ep;
dbg("Entering acm_rx_tasklet");
@@ -463,14 +462,12 @@ urbs:
rcv->buffer = buf;
- ep = (usb_pipein(acm->rx_endpoint) ? acm->dev->ep_in : acm->dev->ep_out)
- [usb_pipeendpoint(acm->rx_endpoint)];
- if (usb_endpoint_xfer_int(&ep->desc))
+ if (acm->is_int_ep)
usb_fill_int_urb(rcv->urb, acm->dev,
acm->rx_endpoint,
buf->base,
acm->readsize,
- acm_read_bulk, rcv, ep->desc.bInterval);
+ acm_read_bulk, rcv, acm->bInterval);
else
usb_fill_bulk_urb(rcv->urb, acm->dev,
acm->rx_endpoint,
@@ -1183,6 +1180,9 @@ made_compressed_probe:
spin_lock_init(&acm->read_lock);
mutex_init(&acm->mutex);
acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
+ acm->is_int_ep = usb_endpoint_xfer_int(epread);
+ if (acm->is_int_ep)
+ acm->bInterval = epread->bInterval;
tty_port_init(&acm->port);
acm->port.ops = &acm_port_ops;
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 1602324808b..c4a0ee8ffcc 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -126,6 +126,8 @@ struct acm {
unsigned int ctrl_caps; /* control capabilities from the class specific header */
unsigned int susp_count; /* number of suspended interfaces */
int combined_interfaces:1; /* control and data collapsed */
+ int is_int_ep:1; /* interrupt endpoints contrary to spec used */
+ u8 bInterval;
struct acm_wb *delayed_wb; /* write queued for a device about to be woken */
};
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 38b8bce782d..4247eccf858 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -595,7 +595,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
if (!ps)
goto out;
- ret = -ENOENT;
+ ret = -ENODEV;
/* usbdev device-node */
if (imajor(inode) == USB_DEVICE_MAJOR)
@@ -1321,7 +1321,8 @@ static int get_urb32(struct usbdevfs_urb *kurb,
struct usbdevfs_urb32 __user *uurb)
{
__u32 uptr;
- if (get_user(kurb->type, &uurb->type) ||
+ if (!access_ok(VERIFY_READ, uurb, sizeof(*uurb)) ||
+ __get_user(kurb->type, &uurb->type) ||
__get_user(kurb->endpoint, &uurb->endpoint) ||
__get_user(kurb->status, &uurb->status) ||
__get_user(kurb->flags, &uurb->flags) ||
@@ -1536,8 +1537,9 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)
u32 udata;
uioc = compat_ptr((long)arg);
- if (get_user(ctrl.ifno, &uioc->ifno) ||
- get_user(ctrl.ioctl_code, &uioc->ioctl_code) ||
+ if (!access_ok(VERIFY_READ, uioc, sizeof(*uioc)) ||
+ __get_user(ctrl.ifno, &uioc->ifno) ||
+ __get_user(ctrl.ioctl_code, &uioc->ioctl_code) ||
__get_user(udata, &uioc->data))
return -EFAULT;
ctrl.data = compat_ptr(udata);
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 7d03549c333..11c627ce602 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -903,7 +903,8 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
/* already started */
break;
case QH_STATE_IDLE:
- WARN_ON(1);
+ /* QH might be waiting for a Clear-TT-Buffer */
+ qh_completions(ehci, qh);
break;
}
break;
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 9a1384747f3..7673554fa64 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -375,12 +375,11 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
*/
if ((token & QTD_STS_XACT) &&
QTD_CERR(token) == 0 &&
- --qh->xacterrs > 0 &&
+ ++qh->xacterrs < QH_XACTERR_MAX &&
!urb->unlinked) {
ehci_dbg(ehci,
"detected XactErr len %zu/%zu retry %d\n",
- qtd->length - QTD_LENGTH(token), qtd->length,
- QH_XACTERR_MAX - qh->xacterrs);
+ qtd->length - QTD_LENGTH(token), qtd->length, qh->xacterrs);
/* reset the token in the qtd and the
* qh overlay (which still contains
@@ -494,7 +493,7 @@ halt:
last = qtd;
/* reinit the xacterr counter for the next qtd */
- qh->xacterrs = QH_XACTERR_MAX;
+ qh->xacterrs = 0;
}
/* last urb's completion might still need calling */
@@ -940,7 +939,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
head->qh_next.qh = qh;
head->hw_next = dma;
- qh->xacterrs = QH_XACTERR_MAX;
+ qh_get(qh);
+ qh->xacterrs = 0;
qh->qh_state = QH_STATE_LINKED;
/* qtd completions reported later by interrupt */
}
@@ -1080,7 +1080,7 @@ submit_async (
* the HC and TT handle it when the TT has a buffer ready.
*/
if (likely (qh->qh_state == QH_STATE_IDLE))
- qh_link_async (ehci, qh_get (qh));
+ qh_link_async(ehci, qh);
done:
spin_unlock_irqrestore (&ehci->lock, flags);
if (unlikely (qh == NULL))
@@ -1115,8 +1115,6 @@ static void end_unlink_async (struct ehci_hcd *ehci)
&& HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
qh_link_async (ehci, qh);
else {
- qh_put (qh); // refcount from async list
-
/* it's not free to turn the async schedule on/off; leave it
* active but idle for a while once it empties.
*/
@@ -1124,6 +1122,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
&& ehci->async->qh_next.qh == NULL)
timer_action (ehci, TIMER_ASYNC_OFF);
}
+ qh_put(qh); /* refcount from async list */
if (next) {
ehci->reclaim = NULL;
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 74f7f83b29a..edd61ee9032 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -542,6 +542,7 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
}
}
qh->qh_state = QH_STATE_LINKED;
+ qh->xacterrs = 0;
qh_get (qh);
/* update per-qh bandwidth for usbfs */
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 70073b157f0..803adcb5ac1 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -12,6 +12,7 @@ config USB_MUSB_HDRC
depends on !SUPERH
select NOP_USB_XCEIV if ARCH_DAVINCI
select TWL4030_USB if MACH_OMAP_3430SDP
+ select NOP_USB_XCEIV if MACH_OMAP3EVM
select USB_OTG_UTILS
tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
help
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index b574878c78b..8fec5d4455c 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -699,6 +699,9 @@ static struct usb_device_id id_table_combined [] = {
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) },
{ USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) },
+ { USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) },
+ { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID),
+ .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index 24dbd99e87d..8c92b88166a 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -954,6 +954,20 @@
#define AURICAL_USB_PID 0x0010 /* Aurical USB Audiometer */
/*
+ * Bayer Ascensia Contour blood glucose meter USB-converter cable.
+ * http://winglucofacts.com/cables/
+ */
+#define BAYER_VID 0x1A79
+#define BAYER_CONTOUR_CABLE_PID 0x6001
+
+/*
+ * Marvell OpenRD Base, Client
+ * http://www.open-rd.org
+ * OpenRD Base, Client use VID 0x0403
+ */
+#define MARVELL_OPENRD_PID 0x9e90
+
+/*
* BmRequestType: 1100 0000b
* bRequest: FTDI_E2_READ
* wValue: 0
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 7d15bfa7c2d..3e86815b270 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -95,6 +95,7 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) },
{ USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) },
{ USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) },
+ { USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 12aac7d2462..ee9505e1dd9 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -126,3 +126,7 @@
/* Cressi Edy (diving computer) PC interface */
#define CRESSI_VENDOR_ID 0x04b8
#define CRESSI_EDY_PRODUCT_ID 0x0521
+
+/* Sony, USB data cable for CMD-Jxx mobile phones */
+#define SONY_VENDOR_ID 0x054c
+#define SONY_QN3USB_PRODUCT_ID 0x0437
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 1b9c5dd0fb2..7477d411959 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -838,6 +838,13 @@ UNUSUAL_DEV( 0x066f, 0x8000, 0x0001, 0x0001,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY ),
+/* Reported by Rogerio Brito <rbrito@ime.usp.br> */
+UNUSUAL_DEV( 0x067b, 0x2317, 0x0001, 0x001,
+ "Prolific Technology, Inc.",
+ "Mass Storage Device",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_NOT_LOCKABLE ),
+
/* Reported by Richard -=[]=- <micro_flyer@hotmail.com> */
/* Change to bcdDeviceMin (0x0100 to 0x0001) reported by
* Thomas Bartosik <tbartdev@gmx-topmail.de> */
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 471a9a60376..3a44695b9c0 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -1082,7 +1082,6 @@ static void fbcon_init(struct vc_data *vc, int init)
new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
new_cols /= vc->vc_font.width;
new_rows /= vc->vc_font.height;
- vc_resize(vc, new_cols, new_rows);
/*
* We must always set the mode. The mode of the previous console
@@ -1111,10 +1110,11 @@ static void fbcon_init(struct vc_data *vc, int init)
* vc_{cols,rows}, but we must not set those if we are only
* resizing the console.
*/
- if (!init) {
+ if (init) {
vc->vc_cols = new_cols;
vc->vc_rows = new_rows;
- }
+ } else
+ vc_resize(vc, new_cols, new_rows);
if (logo)
fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
diff --git a/drivers/video/console/fbcon_rotate.h b/drivers/video/console/fbcon_rotate.h
index 75be5ce53dc..e233444cda6 100644
--- a/drivers/video/console/fbcon_rotate.h
+++ b/drivers/video/console/fbcon_rotate.h
@@ -45,7 +45,7 @@ static inline void rotate_ud(const char *in, char *out, u32 width, u32 height)
width = (width + 7) & ~7;
for (i = 0; i < height; i++) {
- for (j = 0; j < width; j++) {
+ for (j = 0; j < width - shift; j++) {
if (pattern_test_bit(j, i, width, in))
pattern_set_bit(width - (1 + j + shift),
height - (1 + i),
diff --git a/drivers/video/console/sticore.c b/drivers/video/console/sticore.c
index ef7870f5ea0..857b3668b3b 100644
--- a/drivers/video/console/sticore.c
+++ b/drivers/video/console/sticore.c
@@ -957,9 +957,14 @@ static int __devinit sticore_pci_init(struct pci_dev *pd,
#ifdef CONFIG_PCI
unsigned long fb_base, rom_base;
unsigned int fb_len, rom_len;
+ int err;
struct sti_struct *sti;
- pci_enable_device(pd);
+ err = pci_enable_device(pd);
+ if (err < 0) {
+ dev_err(&pd->dev, "Cannot enable PCI device\n");
+ return err;
+ }
fb_base = pci_resource_start(pd, 0);
fb_len = pci_resource_len(pd, 0);
@@ -1048,7 +1053,7 @@ static void __devinit sti_init_roms(void)
/* Register drivers for native & PCI cards */
register_parisc_driver(&pa_sti_driver);
- pci_register_driver(&pci_sti_driver);
+ WARN_ON(pci_register_driver(&pci_sti_driver));
/* if we didn't find the given default sti, take the first one */
if (!default_sti)
diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c
index f8778cde218..054ef29be47 100644
--- a/drivers/video/mx3fb.c
+++ b/drivers/video/mx3fb.c
@@ -669,7 +669,8 @@ static uint32_t bpp_to_pixfmt(int bpp)
}
static int mx3fb_blank(int blank, struct fb_info *fbi);
-static int mx3fb_map_video_memory(struct fb_info *fbi, unsigned int mem_len);
+static int mx3fb_map_video_memory(struct fb_info *fbi, unsigned int mem_len,
+ bool lock);
static int mx3fb_unmap_video_memory(struct fb_info *fbi);
/**
@@ -711,12 +712,7 @@ static void mx3fb_dma_done(void *arg)
complete(&mx3_fbi->flip_cmpl);
}
-/**
- * mx3fb_set_par() - set framebuffer parameters and change the operating mode.
- * @fbi: framebuffer information pointer.
- * @return: 0 on success or negative error code on failure.
- */
-static int mx3fb_set_par(struct fb_info *fbi)
+static int __set_par(struct fb_info *fbi, bool lock)
{
u32 mem_len;
struct ipu_di_signal_cfg sig_cfg;
@@ -727,10 +723,6 @@ static int mx3fb_set_par(struct fb_info *fbi)
struct idmac_video_param *video = &ichan->params.video;
struct scatterlist *sg = mx3_fbi->sg;
- dev_dbg(mx3fb->dev, "%s [%c]\n", __func__, list_empty(&ichan->queue) ? '-' : '+');
-
- mutex_lock(&mx3_fbi->mutex);
-
/* Total cleanup */
if (mx3_fbi->txd)
sdc_disable_channel(mx3_fbi);
@@ -742,10 +734,8 @@ static int mx3fb_set_par(struct fb_info *fbi)
if (fbi->fix.smem_start)
mx3fb_unmap_video_memory(fbi);
- if (mx3fb_map_video_memory(fbi, mem_len) < 0) {
- mutex_unlock(&mx3_fbi->mutex);
+ if (mx3fb_map_video_memory(fbi, mem_len, lock) < 0)
return -ENOMEM;
- }
}
sg_init_table(&sg[0], 1);
@@ -791,7 +781,6 @@ static int mx3fb_set_par(struct fb_info *fbi)
fbi->var.vsync_len,
fbi->var.lower_margin +
fbi->var.vsync_len, sig_cfg) != 0) {
- mutex_unlock(&mx3_fbi->mutex);
dev_err(fbi->device,
"mx3fb: Error initializing panel.\n");
return -EINVAL;
@@ -810,9 +799,30 @@ static int mx3fb_set_par(struct fb_info *fbi)
if (mx3_fbi->blank == FB_BLANK_UNBLANK)
sdc_enable_channel(mx3_fbi);
+ return 0;
+}
+
+/**
+ * mx3fb_set_par() - set framebuffer parameters and change the operating mode.
+ * @fbi: framebuffer information pointer.
+ * @return: 0 on success or negative error code on failure.
+ */
+static int mx3fb_set_par(struct fb_info *fbi)
+{
+ struct mx3fb_info *mx3_fbi = fbi->par;
+ struct mx3fb_data *mx3fb = mx3_fbi->mx3fb;
+ struct idmac_channel *ichan = mx3_fbi->idmac_channel;
+ int ret;
+
+ dev_dbg(mx3fb->dev, "%s [%c]\n", __func__, list_empty(&ichan->queue) ? '-' : '+');
+
+ mutex_lock(&mx3_fbi->mutex);
+
+ ret = __set_par(fbi, true);
+
mutex_unlock(&mx3_fbi->mutex);
- return 0;
+ return ret;
}
/**
@@ -966,21 +976,11 @@ static int mx3fb_setcolreg(unsigned int regno, unsigned int red,
return ret;
}
-/**
- * mx3fb_blank() - blank the display.
- */
-static int mx3fb_blank(int blank, struct fb_info *fbi)
+static void __blank(int blank, struct fb_info *fbi)
{
struct mx3fb_info *mx3_fbi = fbi->par;
struct mx3fb_data *mx3fb = mx3_fbi->mx3fb;
- dev_dbg(fbi->device, "%s, blank = %d, base %p, len %u\n", __func__,
- blank, fbi->screen_base, fbi->fix.smem_len);
-
- if (mx3_fbi->blank == blank)
- return 0;
-
- mutex_lock(&mx3_fbi->mutex);
mx3_fbi->blank = blank;
switch (blank) {
@@ -999,6 +999,23 @@ static int mx3fb_blank(int blank, struct fb_info *fbi)
sdc_set_brightness(mx3fb, mx3fb->backlight_level);
break;
}
+}
+
+/**
+ * mx3fb_blank() - blank the display.
+ */
+static int mx3fb_blank(int blank, struct fb_info *fbi)
+{
+ struct mx3fb_info *mx3_fbi = fbi->par;
+
+ dev_dbg(fbi->device, "%s, blank = %d, base %p, len %u\n", __func__,
+ blank, fbi->screen_base, fbi->fix.smem_len);
+
+ if (mx3_fbi->blank == blank)
+ return 0;
+
+ mutex_lock(&mx3_fbi->mutex);
+ __blank(blank, fbi);
mutex_unlock(&mx3_fbi->mutex);
return 0;
@@ -1198,6 +1215,7 @@ static int mx3fb_resume(struct platform_device *pdev)
* mx3fb_map_video_memory() - allocates the DRAM memory for the frame buffer.
* @fbi: framebuffer information pointer
* @mem_len: length of mapped memory
+ * @lock: do not lock during initialisation
* @return: Error code indicating success or failure
*
* This buffer is remapped into a non-cached, non-buffered, memory region to
@@ -1205,7 +1223,8 @@ static int mx3fb_resume(struct platform_device *pdev)
* area is remapped, all virtual memory access to the video memory should occur
* at the new region.
*/
-static int mx3fb_map_video_memory(struct fb_info *fbi, unsigned int mem_len)
+static int mx3fb_map_video_memory(struct fb_info *fbi, unsigned int mem_len,
+ bool lock)
{
int retval = 0;
dma_addr_t addr;
@@ -1221,10 +1240,12 @@ static int mx3fb_map_video_memory(struct fb_info *fbi, unsigned int mem_len)
goto err0;
}
- mutex_lock(&fbi->mm_lock);
+ if (lock)
+ mutex_lock(&fbi->mm_lock);
fbi->fix.smem_start = addr;
fbi->fix.smem_len = mem_len;
- mutex_unlock(&fbi->mm_lock);
+ if (lock)
+ mutex_unlock(&fbi->mm_lock);
dev_dbg(fbi->device, "allocated fb @ p=0x%08x, v=0x%p, size=%d.\n",
(uint32_t) fbi->fix.smem_start, fbi->screen_base, fbi->fix.smem_len);
@@ -1365,6 +1386,11 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan)
init_completion(&mx3fbi->flip_cmpl);
disable_irq(ichan->eof_irq);
dev_dbg(mx3fb->dev, "disabling irq %d\n", ichan->eof_irq);
+ ret = __set_par(fbi, false);
+ if (ret < 0)
+ goto esetpar;
+
+ __blank(FB_BLANK_UNBLANK, fbi);
dev_info(dev, "registered, using mode %s\n", fb_mode);
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 8f24564f77b..07f22b62563 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -481,6 +481,9 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
/* tell the board code to enable the panel */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
ch = &priv->ch[k];
+ if (!ch->enabled)
+ continue;
+
board_cfg = &ch->cfg.board_cfg;
if (board_cfg->display_on)
board_cfg->display_on(board_cfg->board_data);
@@ -498,6 +501,8 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
/* clean up deferred io and ask board code to disable panel */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
ch = &priv->ch[k];
+ if (!ch->enabled)
+ continue;
/* deferred io mode:
* flush frame, and wait for frame end interrupt
diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c
index fcd53ceb88f..c8960003f47 100644
--- a/drivers/video/via/hw.c
+++ b/drivers/video/via/hw.c
@@ -2407,14 +2407,14 @@ int viafb_setmode(int vmode_index, int hor_res, int ver_res, int video_bpp,
viafb_dvi_set_mode(viafb_get_mode_index
(viaparinfo->tmds_setting_info->h_active,
viaparinfo->tmds_setting_info->
- v_active, 1),
+ v_active),
video_bpp1, viaparinfo->
tmds_setting_info->iga_path);
} else {
viafb_dvi_set_mode(viafb_get_mode_index
(viaparinfo->tmds_setting_info->h_active,
viaparinfo->
- tmds_setting_info->v_active, 0),
+ tmds_setting_info->v_active),
video_bpp, viaparinfo->
tmds_setting_info->iga_path);
}
diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c
index 6c7290a6a44..78c6b338794 100644
--- a/drivers/video/via/lcd.c
+++ b/drivers/video/via/lcd.c
@@ -580,10 +580,7 @@ static void load_lcd_k400_patch_tbl(int set_hres, int set_vres,
int reg_num = 0;
struct io_reg *lcd_patch_reg = NULL;
- if (viaparinfo->lvds_setting_info->iga_path == IGA2)
- vmode_index = viafb_get_mode_index(set_hres, set_vres, 1);
- else
- vmode_index = viafb_get_mode_index(set_hres, set_vres, 0);
+ vmode_index = viafb_get_mode_index(set_hres, set_vres);
switch (panel_id) {
/* LCD 800x600 */
case LCD_PANEL_ID1_800X600:
@@ -761,10 +758,7 @@ static void load_lcd_p880_patch_tbl(int set_hres, int set_vres,
int reg_num = 0;
struct io_reg *lcd_patch_reg = NULL;
- if (viaparinfo->lvds_setting_info->iga_path == IGA2)
- vmode_index = viafb_get_mode_index(set_hres, set_vres, 1);
- else
- vmode_index = viafb_get_mode_index(set_hres, set_vres, 0);
+ vmode_index = viafb_get_mode_index(set_hres, set_vres);
switch (panel_id) {
case LCD_PANEL_ID5_1400X1050:
@@ -832,10 +826,7 @@ static void load_lcd_patch_regs(int set_hres, int set_vres,
{
int vmode_index;
- if (viaparinfo->lvds_setting_info->iga_path == IGA2)
- vmode_index = viafb_get_mode_index(set_hres, set_vres, 1);
- else
- vmode_index = viafb_get_mode_index(set_hres, set_vres, 0);
+ vmode_index = viafb_get_mode_index(set_hres, set_vres);
viafb_unlock_crt();
diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c
index a0fec298216..72833f3334b 100644
--- a/drivers/video/via/viafbdev.c
+++ b/drivers/video/via/viafbdev.c
@@ -32,7 +32,6 @@ static u32 pseudo_pal[17];
/* video mode */
static char *viafb_mode = "640x480";
static char *viafb_mode1 = "640x480";
-static int viafb_resMode = VIA_RES_640X480;
/* Added for specifying active devices.*/
char *viafb_active_dev = "";
@@ -56,47 +55,47 @@ static void viafb_get_video_device(u32 *video_dev_info);
/* Mode information */
static const struct viafb_modeinfo viafb_modentry[] = {
- {480, 640, VIA_RES_480X640, "480x640"},
- {640, 480, VIA_RES_640X480, "640x480"},
- {800, 480, VIA_RES_800X480, "800x480"},
- {800, 600, VIA_RES_800X600, "800x600"},
- {1024, 768, VIA_RES_1024X768, "1024x768"},
- {1152, 864, VIA_RES_1152X864, "1152x864"},
- {1280, 1024, VIA_RES_1280X1024, "1280x1024"},
- {1600, 1200, VIA_RES_1600X1200, "1600x1200"},
- {1440, 1050, VIA_RES_1440X1050, "1440x1050"},
- {1280, 768, VIA_RES_1280X768, "1280x768"},
- {1280, 800, VIA_RES_1280X800, "1280x800"},
- {1280, 960, VIA_RES_1280X960, "1280x960"},
- {1920, 1440, VIA_RES_1920X1440, "1920x1440"},
- {848, 480, VIA_RES_848X480, "848x480"},
- {1400, 1050, VIA_RES_1400X1050, "1400x1050"},
- {720, 480, VIA_RES_720X480, "720x480"},
- {720, 576, VIA_RES_720X576, "720x576"},
- {1024, 512, VIA_RES_1024X512, "1024x512"},
- {1024, 576, VIA_RES_1024X576, "1024x576"},
- {1024, 600, VIA_RES_1024X600, "1024x600"},
- {1280, 720, VIA_RES_1280X720, "1280x720"},
- {1920, 1080, VIA_RES_1920X1080, "1920x1080"},
- {1366, 768, VIA_RES_1368X768, "1368x768"},
- {1680, 1050, VIA_RES_1680X1050, "1680x1050"},
- {960, 600, VIA_RES_960X600, "960x600"},
- {1000, 600, VIA_RES_1000X600, "1000x600"},
- {1024, 576, VIA_RES_1024X576, "1024x576"},
- {1024, 600, VIA_RES_1024X600, "1024x600"},
- {1088, 612, VIA_RES_1088X612, "1088x612"},
- {1152, 720, VIA_RES_1152X720, "1152x720"},
- {1200, 720, VIA_RES_1200X720, "1200x720"},
- {1280, 600, VIA_RES_1280X600, "1280x600"},
- {1360, 768, VIA_RES_1360X768, "1360x768"},
- {1440, 900, VIA_RES_1440X900, "1440x900"},
- {1600, 900, VIA_RES_1600X900, "1600x900"},
- {1600, 1024, VIA_RES_1600X1024, "1600x1024"},
- {1792, 1344, VIA_RES_1792X1344, "1792x1344"},
- {1856, 1392, VIA_RES_1856X1392, "1856x1392"},
- {1920, 1200, VIA_RES_1920X1200, "1920x1200"},
- {2048, 1536, VIA_RES_2048X1536, "2048x1536"},
- {0, 0, VIA_RES_INVALID, "640x480"}
+ {480, 640, VIA_RES_480X640},
+ {640, 480, VIA_RES_640X480},
+ {800, 480, VIA_RES_800X480},
+ {800, 600, VIA_RES_800X600},
+ {1024, 768, VIA_RES_1024X768},
+ {1152, 864, VIA_RES_1152X864},
+ {1280, 1024, VIA_RES_1280X1024},
+ {1600, 1200, VIA_RES_1600X1200},
+ {1440, 1050, VIA_RES_1440X1050},
+ {1280, 768, VIA_RES_1280X768,},
+ {1280, 800, VIA_RES_1280X800},
+ {1280, 960, VIA_RES_1280X960},
+ {1920, 1440, VIA_RES_1920X1440},
+ {848, 480, VIA_RES_848X480},
+ {1400, 1050, VIA_RES_1400X1050},
+ {720, 480, VIA_RES_720X480},
+ {720, 576, VIA_RES_720X576},
+ {1024, 512, VIA_RES_1024X512},
+ {1024, 576, VIA_RES_1024X576},
+ {1024, 600, VIA_RES_1024X600},
+ {1280, 720, VIA_RES_1280X720},
+ {1920, 1080, VIA_RES_1920X1080},
+ {1366, 768, VIA_RES_1368X768},
+ {1680, 1050, VIA_RES_1680X1050},
+ {960, 600, VIA_RES_960X600},
+ {1000, 600, VIA_RES_1000X600},
+ {1024, 576, VIA_RES_1024X576},
+ {1024, 600, VIA_RES_1024X600},
+ {1088, 612, VIA_RES_1088X612},
+ {1152, 720, VIA_RES_1152X720},
+ {1200, 720, VIA_RES_1200X720},
+ {1280, 600, VIA_RES_1280X600},
+ {1360, 768, VIA_RES_1360X768},
+ {1440, 900, VIA_RES_1440X900},
+ {1600, 900, VIA_RES_1600X900},
+ {1600, 1024, VIA_RES_1600X1024},
+ {1792, 1344, VIA_RES_1792X1344},
+ {1856, 1392, VIA_RES_1856X1392},
+ {1920, 1200, VIA_RES_1920X1200},
+ {2048, 1536, VIA_RES_2048X1536},
+ {0, 0, VIA_RES_INVALID}
};
static struct fb_ops viafb_ops;
@@ -177,7 +176,7 @@ static int viafb_check_var(struct fb_var_screeninfo *var,
if (var->vmode & FB_VMODE_INTERLACED || var->vmode & FB_VMODE_DOUBLE)
return -EINVAL;
- vmode_index = viafb_get_mode_index(var->xres, var->yres, 0);
+ vmode_index = viafb_get_mode_index(var->xres, var->yres);
if (vmode_index == VIA_RES_INVALID) {
DEBUG_MSG(KERN_INFO
"viafb: Mode %dx%dx%d not supported!!\n",
@@ -233,14 +232,14 @@ static int viafb_set_par(struct fb_info *info)
viafb_update_device_setting(info->var.xres, info->var.yres,
info->var.bits_per_pixel, viafb_refresh, 0);
- vmode_index = viafb_get_mode_index(info->var.xres, info->var.yres, 0);
+ vmode_index = viafb_get_mode_index(info->var.xres, info->var.yres);
if (viafb_SAMM_ON == 1) {
DEBUG_MSG(KERN_INFO
"viafb_second_xres = %d, viafb_second_yres = %d, bpp = %d\n",
viafb_second_xres, viafb_second_yres, viafb_bpp1);
vmode_index1 = viafb_get_mode_index(viafb_second_xres,
- viafb_second_yres, 1);
+ viafb_second_yres);
DEBUG_MSG(KERN_INFO "->viafb_SAMM_ON: index=%d\n",
vmode_index1);
@@ -1262,7 +1261,7 @@ static int viafb_sync(struct fb_info *info)
return 0;
}
-int viafb_get_mode_index(int hres, int vres, int flag)
+int viafb_get_mode_index(int hres, int vres)
{
u32 i;
DEBUG_MSG(KERN_INFO "viafb_get_mode_index!\n");
@@ -1272,13 +1271,7 @@ int viafb_get_mode_index(int hres, int vres, int flag)
viafb_modentry[i].yres == vres)
break;
- viafb_resMode = viafb_modentry[i].mode_index;
- if (flag)
- viafb_mode1 = viafb_modentry[i].mode_res;
- else
- viafb_mode = viafb_modentry[i].mode_res;
-
- return viafb_resMode;
+ return viafb_modentry[i].mode_index;
}
static void check_available_device_to_enable(int device_id)
@@ -2199,7 +2192,7 @@ static int __devinit via_pci_probe(void)
strict_strtoul(tmpc, 0, &default_xres);
strict_strtoul(tmpm, 0, &default_yres);
- vmode_index = viafb_get_mode_index(default_xres, default_yres, 0);
+ vmode_index = viafb_get_mode_index(default_xres, default_yres);
DEBUG_MSG(KERN_INFO "0->index=%d\n", vmode_index);
if (viafb_SAMM_ON == 1) {
diff --git a/drivers/video/via/viafbdev.h b/drivers/video/via/viafbdev.h
index a4158e87287..227b000feb3 100644
--- a/drivers/video/via/viafbdev.h
+++ b/drivers/video/via/viafbdev.h
@@ -81,7 +81,6 @@ struct viafb_modeinfo {
u32 xres;
u32 yres;
int mode_index;
- char *mode_res;
};
extern unsigned int viafb_second_virtual_yres;
extern unsigned int viafb_second_virtual_xres;
@@ -102,7 +101,7 @@ extern int strict_strtoul(const char *cp, unsigned int base,
void viafb_memory_pitch_patch(struct fb_info *info);
void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, int refresh,
int mode_index);
-int viafb_get_mode_index(int hres, int vres, int flag);
+int viafb_get_mode_index(int hres, int vres);
u8 viafb_gpio_i2c_read_lvds(struct lvds_setting_information
*plvds_setting_info, struct lvds_chip_information
*plvds_chip_info, u8 index);
diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
index 15502d5e364..54cd9161017 100644
--- a/drivers/video/xen-fbfront.c
+++ b/drivers/video/xen-fbfront.c
@@ -454,6 +454,10 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
xenfb_init_shared_page(info, fb_info);
+ ret = xenfb_connect_backend(dev, info);
+ if (ret < 0)
+ goto error;
+
ret = register_framebuffer(fb_info);
if (ret) {
fb_deferred_io_cleanup(fb_info);
@@ -464,10 +468,6 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
}
info->fb_info = fb_info;
- ret = xenfb_connect_backend(dev, info);
- if (ret < 0)
- goto error;
-
xenfb_make_preferred_console();
return 0;
diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c
index a7e3b706b9d..0d92969404c 100644
--- a/drivers/w1/masters/omap_hdq.c
+++ b/drivers/w1/masters/omap_hdq.c
@@ -687,6 +687,7 @@ static int omap_hdq_remove(struct platform_device *pdev)
if (hdq_data->hdq_usecount) {
dev_dbg(&pdev->dev, "removed when use count is not zero\n");
+ mutex_unlock(&hdq_data->hdq_mutex);
return -EBUSY;
}
diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c
index 3fe9742c23c..2f8643efe92 100644
--- a/drivers/watchdog/ar7_wdt.c
+++ b/drivers/watchdog/ar7_wdt.c
@@ -37,7 +37,7 @@
#include <linux/uaccess.h>
#include <asm/addrspace.h>
-#include <asm/ar7/ar7.h>
+#include <asm/mach-ar7/ar7.h>
#define DRVNAME "ar7_wdt"
#define LONGNAME "TI AR7 Watchdog Timer"
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c
index fecb307d28e..aec7cefdef2 100644
--- a/drivers/watchdog/coh901327_wdt.c
+++ b/drivers/watchdog/coh901327_wdt.c
@@ -18,6 +18,7 @@
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <linux/clk.h>
+#include <linux/delay.h>
#define DRV_NAME "WDOG COH 901 327"
@@ -92,6 +93,8 @@ static struct clk *clk;
static void coh901327_enable(u16 timeout)
{
u16 val;
+ unsigned long freq;
+ unsigned long delay_ns;
clk_enable(clk);
/* Restart timer if it is disabled */
@@ -102,6 +105,14 @@ static void coh901327_enable(u16 timeout)
/* Acknowledge any pending interrupt so it doesn't just fire off */
writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE,
virtbase + U300_WDOG_IER);
+ /*
+ * The interrupt is cleared in the 32 kHz clock domain.
+ * Wait 3 32 kHz cycles for it to take effect
+ */
+ freq = clk_get_rate(clk);
+ delay_ns = (1000000000 + freq - 1) / freq; /* Freq to ns and round up */
+ delay_ns = 3 * delay_ns; /* Wait 3 cycles */
+ ndelay(delay_ns);
/* Enable the watchdog interrupt */
writew(U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE, virtbase + U300_WDOG_IMR);
/* Activate the watchdog timer */
diff --git a/drivers/watchdog/ks8695_wdt.c b/drivers/watchdog/ks8695_wdt.c
index 00b03eb43bf..e1c82769b08 100644
--- a/drivers/watchdog/ks8695_wdt.c
+++ b/drivers/watchdog/ks8695_wdt.c
@@ -66,7 +66,7 @@ static inline void ks8695_wdt_stop(void)
static inline void ks8695_wdt_start(void)
{
unsigned long tmcon;
- unsigned long tval = wdt_time * CLOCK_TICK_RATE;
+ unsigned long tval = wdt_time * KS8695_CLOCK_RATE;
spin_lock(&ks8695_lock);
/* disable timer0 */
@@ -103,7 +103,7 @@ static inline void ks8695_wdt_reload(void)
static int ks8695_wdt_settimeout(int new_time)
{
/*
- * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz
+ * All counting occurs at KS8695_CLOCK_RATE / 128 = 0.256 Hz
*
* Since WDV is a 16-bit counter, the maximum period is
* 65536 / 0.256 = 256 seconds.
diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c
index 332b5ff02fe..f7003cfac63 100644
--- a/fs/9p/v9fs.c
+++ b/fs/9p/v9fs.c
@@ -76,7 +76,7 @@ static const match_table_t tokens = {
* Return 0 upon success, -ERRNO upon failure.
*/
-static int v9fs_parse_options(struct v9fs_session_info *v9ses)
+static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
{
char *options;
substring_t args[MAX_OPT_ARGS];
@@ -90,10 +90,10 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses)
v9ses->debug = 0;
v9ses->cache = 0;
- if (!v9ses->options)
+ if (!opts)
return 0;
- options = kstrdup(v9ses->options, GFP_KERNEL);
+ options = kstrdup(opts, GFP_KERNEL);
if (!options) {
P9_DPRINTK(P9_DEBUG_ERROR,
"failed to allocate copy of option string\n");
@@ -206,24 +206,14 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
v9ses->uid = ~0;
v9ses->dfltuid = V9FS_DEFUID;
v9ses->dfltgid = V9FS_DEFGID;
- if (data) {
- v9ses->options = kstrdup(data, GFP_KERNEL);
- if (!v9ses->options) {
- P9_DPRINTK(P9_DEBUG_ERROR,
- "failed to allocate copy of option string\n");
- retval = -ENOMEM;
- goto error;
- }
- }
- rc = v9fs_parse_options(v9ses);
+ rc = v9fs_parse_options(v9ses, data);
if (rc < 0) {
retval = rc;
goto error;
}
- v9ses->clnt = p9_client_create(dev_name, v9ses->options);
-
+ v9ses->clnt = p9_client_create(dev_name, data);
if (IS_ERR(v9ses->clnt)) {
retval = PTR_ERR(v9ses->clnt);
v9ses->clnt = NULL;
@@ -280,7 +270,6 @@ void v9fs_session_close(struct v9fs_session_info *v9ses)
__putname(v9ses->uname);
__putname(v9ses->aname);
- kfree(v9ses->options);
}
/**
diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h
index a7d56719299..38762bf102a 100644
--- a/fs/9p/v9fs.h
+++ b/fs/9p/v9fs.h
@@ -85,7 +85,6 @@ struct v9fs_session_info {
unsigned int afid;
unsigned int cache;
- char *options; /* copy of mount options */
char *uname; /* user name to mount as */
char *aname; /* name of remote hierarchy being mounted */
unsigned int maxdata; /* max data for client interface */
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 81f8bbf12f9..06a223d50a8 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -171,7 +171,6 @@ int v9fs_uflags2omode(int uflags, int extended)
/**
* v9fs_blank_wstat - helper function to setup a 9P stat structure
- * @v9ses: 9P session info (for determining extended mode)
* @wstat: structure to initialize
*
*/
@@ -207,65 +206,72 @@ v9fs_blank_wstat(struct p9_wstat *wstat)
struct inode *v9fs_get_inode(struct super_block *sb, int mode)
{
+ int err;
struct inode *inode;
struct v9fs_session_info *v9ses = sb->s_fs_info;
P9_DPRINTK(P9_DEBUG_VFS, "super block: %p mode: %o\n", sb, mode);
inode = new_inode(sb);
- if (inode) {
- inode->i_mode = mode;
- inode->i_uid = current_fsuid();
- inode->i_gid = current_fsgid();
- inode->i_blocks = 0;
- inode->i_rdev = 0;
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->i_mapping->a_ops = &v9fs_addr_operations;
-
- switch (mode & S_IFMT) {
- case S_IFIFO:
- case S_IFBLK:
- case S_IFCHR:
- case S_IFSOCK:
- if (!v9fs_extended(v9ses)) {
- P9_DPRINTK(P9_DEBUG_ERROR,
- "special files without extended mode\n");
- return ERR_PTR(-EINVAL);
- }
- init_special_inode(inode, inode->i_mode,
- inode->i_rdev);
- break;
- case S_IFREG:
- inode->i_op = &v9fs_file_inode_operations;
- inode->i_fop = &v9fs_file_operations;
- break;
- case S_IFLNK:
- if (!v9fs_extended(v9ses)) {
- P9_DPRINTK(P9_DEBUG_ERROR,
- "extended modes used w/o 9P2000.u\n");
- return ERR_PTR(-EINVAL);
- }
- inode->i_op = &v9fs_symlink_inode_operations;
- break;
- case S_IFDIR:
- inc_nlink(inode);
- if (v9fs_extended(v9ses))
- inode->i_op = &v9fs_dir_inode_operations_ext;
- else
- inode->i_op = &v9fs_dir_inode_operations;
- inode->i_fop = &v9fs_dir_operations;
- break;
- default:
- P9_DPRINTK(P9_DEBUG_ERROR,
- "BAD mode 0x%x S_IFMT 0x%x\n",
- mode, mode & S_IFMT);
- return ERR_PTR(-EINVAL);
- }
- } else {
+ if (!inode) {
P9_EPRINTK(KERN_WARNING, "Problem allocating inode\n");
return ERR_PTR(-ENOMEM);
}
+
+ inode->i_mode = mode;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
+ inode->i_blocks = 0;
+ inode->i_rdev = 0;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_mapping->a_ops = &v9fs_addr_operations;
+
+ switch (mode & S_IFMT) {
+ case S_IFIFO:
+ case S_IFBLK:
+ case S_IFCHR:
+ case S_IFSOCK:
+ if (!v9fs_extended(v9ses)) {
+ P9_DPRINTK(P9_DEBUG_ERROR,
+ "special files without extended mode\n");
+ err = -EINVAL;
+ goto error;
+ }
+ init_special_inode(inode, inode->i_mode, inode->i_rdev);
+ break;
+ case S_IFREG:
+ inode->i_op = &v9fs_file_inode_operations;
+ inode->i_fop = &v9fs_file_operations;
+ break;
+ case S_IFLNK:
+ if (!v9fs_extended(v9ses)) {
+ P9_DPRINTK(P9_DEBUG_ERROR,
+ "extended modes used w/o 9P2000.u\n");
+ err = -EINVAL;
+ goto error;
+ }
+ inode->i_op = &v9fs_symlink_inode_operations;
+ break;
+ case S_IFDIR:
+ inc_nlink(inode);
+ if (v9fs_extended(v9ses))
+ inode->i_op = &v9fs_dir_inode_operations_ext;
+ else
+ inode->i_op = &v9fs_dir_inode_operations;
+ inode->i_fop = &v9fs_dir_operations;
+ break;
+ default:
+ P9_DPRINTK(P9_DEBUG_ERROR, "BAD mode 0x%x S_IFMT 0x%x\n",
+ mode, mode & S_IFMT);
+ err = -EINVAL;
+ goto error;
+ }
+
return inode;
+
+error:
+ iput(inode);
+ return ERR_PTR(err);
}
/*
@@ -338,30 +344,25 @@ v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
ret = NULL;
st = p9_client_stat(fid);
- if (IS_ERR(st)) {
- err = PTR_ERR(st);
- st = NULL;
- goto error;
- }
+ if (IS_ERR(st))
+ return ERR_CAST(st);
umode = p9mode2unixmode(v9ses, st->mode);
ret = v9fs_get_inode(sb, umode);
if (IS_ERR(ret)) {
err = PTR_ERR(ret);
- ret = NULL;
goto error;
}
v9fs_stat2inode(st, ret, sb);
ret->i_ino = v9fs_qid2ino(&st->qid);
+ p9stat_free(st);
kfree(st);
return ret;
error:
+ p9stat_free(st);
kfree(st);
- if (ret)
- iput(ret);
-
return ERR_PTR(err);
}
@@ -403,9 +404,9 @@ v9fs_open_created(struct inode *inode, struct file *file)
* @v9ses: session information
* @dir: directory that dentry is being created in
* @dentry: dentry that is being created
+ * @extension: 9p2000.u extension string to support devices, etc.
* @perm: create permissions
* @mode: open mode
- * @extension: 9p2000.u extension string to support devices, etc.
*
*/
static struct p9_fid *
@@ -470,7 +471,10 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
dentry->d_op = &v9fs_dentry_operations;
d_instantiate(dentry, inode);
- v9fs_fid_add(dentry, fid);
+ err = v9fs_fid_add(dentry, fid);
+ if (err < 0)
+ goto error;
+
return ofid;
error:
diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c
index 38d695d66a0..8961f1a8f66 100644
--- a/fs/9p/vfs_super.c
+++ b/fs/9p/vfs_super.c
@@ -81,7 +81,7 @@ static int v9fs_set_super(struct super_block *s, void *data)
static void
v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses,
- int flags)
+ int flags, void *data)
{
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize_bits = fls(v9ses->maxdata - 1);
@@ -91,6 +91,8 @@ v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses,
sb->s_flags = flags | MS_ACTIVE | MS_SYNCHRONOUS | MS_DIRSYNC |
MS_NOATIME;
+
+ save_mount_options(sb, data);
}
/**
@@ -113,14 +115,11 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
struct v9fs_session_info *v9ses = NULL;
struct p9_wstat *st = NULL;
int mode = S_IRWXUGO | S_ISVTX;
- uid_t uid = current_fsuid();
- gid_t gid = current_fsgid();
struct p9_fid *fid;
int retval = 0;
P9_DPRINTK(P9_DEBUG_VFS, " \n");
- st = NULL;
v9ses = kzalloc(sizeof(struct v9fs_session_info), GFP_KERNEL);
if (!v9ses)
return -ENOMEM;
@@ -142,7 +141,7 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
retval = PTR_ERR(sb);
goto free_stat;
}
- v9fs_fill_super(sb, v9ses, flags);
+ v9fs_fill_super(sb, v9ses, flags, data);
inode = v9fs_get_inode(sb, S_IFDIR | mode);
if (IS_ERR(inode)) {
@@ -150,9 +149,6 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
goto release_sb;
}
- inode->i_uid = uid;
- inode->i_gid = gid;
-
root = d_alloc_root(inode);
if (!root) {
iput(inode);
@@ -173,10 +169,8 @@ P9_DPRINTK(P9_DEBUG_VFS, " simple set mount, return 0\n");
simple_set_mnt(mnt, sb);
return 0;
-release_sb:
- deactivate_locked_super(sb);
-
free_stat:
+ p9stat_free(st);
kfree(st);
clunk_fid:
@@ -185,7 +179,12 @@ clunk_fid:
close_session:
v9fs_session_close(v9ses);
kfree(v9ses);
+ return retval;
+release_sb:
+ p9stat_free(st);
+ kfree(st);
+ deactivate_locked_super(sb);
return retval;
}
@@ -207,24 +206,10 @@ static void v9fs_kill_super(struct super_block *s)
v9fs_session_close(v9ses);
kfree(v9ses);
+ s->s_fs_info = NULL;
P9_DPRINTK(P9_DEBUG_VFS, "exiting kill_super\n");
}
-/**
- * v9fs_show_options - Show mount options in /proc/mounts
- * @m: seq_file to write to
- * @mnt: mount descriptor
- *
- */
-
-static int v9fs_show_options(struct seq_file *m, struct vfsmount *mnt)
-{
- struct v9fs_session_info *v9ses = mnt->mnt_sb->s_fs_info;
-
- seq_printf(m, "%s", v9ses->options);
- return 0;
-}
-
static void
v9fs_umount_begin(struct super_block *sb)
{
@@ -237,7 +222,7 @@ v9fs_umount_begin(struct super_block *sb)
static const struct super_operations v9fs_super_ops = {
.statfs = simple_statfs,
.clear_inode = v9fs_clear_inode,
- .show_options = v9fs_show_options,
+ .show_options = generic_show_options,
.umount_begin = v9fs_umount_begin,
};
diff --git a/fs/afs/file.c b/fs/afs/file.c
index 0149dab365e..681c2a7b013 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -134,9 +134,16 @@ static int afs_readpage(struct file *file, struct page *page)
inode = page->mapping->host;
- ASSERT(file != NULL);
- key = file->private_data;
- ASSERT(key != NULL);
+ if (file) {
+ key = file->private_data;
+ ASSERT(key != NULL);
+ } else {
+ key = afs_request_key(AFS_FS_S(inode->i_sb)->volume->cell);
+ if (IS_ERR(key)) {
+ ret = PTR_ERR(key);
+ goto error_nokey;
+ }
+ }
_enter("{%x},{%lu},{%lu}", key_serial(key), inode->i_ino, page->index);
@@ -207,12 +214,17 @@ static int afs_readpage(struct file *file, struct page *page)
unlock_page(page);
}
+ if (!file)
+ key_put(key);
_leave(" = 0");
return 0;
error:
SetPageError(page);
unlock_page(page);
+ if (!file)
+ key_put(key);
+error_nokey:
_leave(" = %d", ret);
return ret;
}
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index aa39ae83f01..3da18d45348 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -77,7 +77,7 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry)
}
/* Update the expiry counter if fs is busy */
- if (!may_umount_tree(mnt)) {
+ if (!may_umount_tree(path.mnt)) {
struct autofs_info *ino = autofs4_dentry_ino(top);
ino->last_used = jiffies;
goto done;
diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c
index 697f6b5f131..e92f229e3c6 100644
--- a/fs/binfmt_flat.c
+++ b/fs/binfmt_flat.c
@@ -828,15 +828,22 @@ static int load_flat_shared_library(int id, struct lib_info *libs)
if (IS_ERR(bprm.file))
return res;
+ bprm.cred = prepare_exec_creds();
+ res = -ENOMEM;
+ if (!bprm.cred)
+ goto out;
+
res = prepare_binprm(&bprm);
if (res <= (unsigned long)-4096)
res = load_flat_file(&bprm, libs, id, NULL);
- if (bprm.file) {
- allow_write_access(bprm.file);
- fput(bprm.file);
- bprm.file = NULL;
- }
+
+ abort_creds(bprm.cred);
+
+out:
+ allow_write_access(bprm.file);
+ fput(bprm.file);
+
return(res);
}
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index dc84daee6bc..72a2b9c28e9 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -265,10 +265,6 @@ static int caching_kthread(void *data)
atomic_inc(&block_group->space_info->caching_threads);
last = max_t(u64, block_group->key.objectid, BTRFS_SUPER_INFO_OFFSET);
-again:
- /* need to make sure the commit_root doesn't disappear */
- down_read(&fs_info->extent_commit_sem);
-
/*
* We don't want to deadlock with somebody trying to allocate a new
* extent for the extent root while also trying to search the extent
@@ -282,6 +278,10 @@ again:
key.objectid = last;
key.offset = 0;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
+again:
+ /* need to make sure the commit_root doesn't disappear */
+ down_read(&fs_info->extent_commit_sem);
+
ret = btrfs_search_slot(NULL, fs_info->extent_root, &key, path, 0, 0);
if (ret < 0)
goto err;
@@ -304,6 +304,19 @@ again:
if (need_resched() ||
btrfs_transaction_in_commit(fs_info)) {
+ leaf = path->nodes[0];
+
+ /* this shouldn't happen, but if the
+ * leaf is empty just move on.
+ */
+ if (btrfs_header_nritems(leaf) == 0)
+ break;
+ /*
+ * we need to copy the key out so that
+ * we are sure the next search advances
+ * us forward in the btree.
+ */
+ btrfs_item_key_to_cpu(leaf, &key, 0);
btrfs_release_path(fs_info->extent_root, path);
up_read(&fs_info->extent_commit_sem);
schedule_timeout(1);
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index af99b78b288..5edcee3a617 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -414,11 +414,29 @@ static noinline int remove_from_bitmap(struct btrfs_block_group_cache *block_gro
u64 *offset, u64 *bytes)
{
u64 end;
+ u64 search_start, search_bytes;
+ int ret;
again:
end = bitmap_info->offset +
(u64)(BITS_PER_BITMAP * block_group->sectorsize) - 1;
+ /*
+ * XXX - this can go away after a few releases.
+ *
+ * since the only user of btrfs_remove_free_space is the tree logging
+ * stuff, and the only way to test that is under crash conditions, we
+ * want to have this debug stuff here just in case somethings not
+ * working. Search the bitmap for the space we are trying to use to
+ * make sure its actually there. If its not there then we need to stop
+ * because something has gone wrong.
+ */
+ search_start = *offset;
+ search_bytes = *bytes;
+ ret = search_bitmap(block_group, bitmap_info, &search_start,
+ &search_bytes);
+ BUG_ON(ret < 0 || search_start != *offset);
+
if (*offset > bitmap_info->offset && *offset + *bytes > end) {
bitmap_clear_bits(block_group, bitmap_info, *offset,
end - *offset + 1);
@@ -430,6 +448,7 @@ again:
}
if (*bytes) {
+ struct rb_node *next = rb_next(&bitmap_info->offset_index);
if (!bitmap_info->bytes) {
unlink_free_space(block_group, bitmap_info);
kfree(bitmap_info->bitmap);
@@ -438,16 +457,36 @@ again:
recalculate_thresholds(block_group);
}
- bitmap_info = tree_search_offset(block_group,
- offset_to_bitmap(block_group,
- *offset),
- 1, 0);
- if (!bitmap_info)
+ /*
+ * no entry after this bitmap, but we still have bytes to
+ * remove, so something has gone wrong.
+ */
+ if (!next)
return -EINVAL;
+ bitmap_info = rb_entry(next, struct btrfs_free_space,
+ offset_index);
+
+ /*
+ * if the next entry isn't a bitmap we need to return to let the
+ * extent stuff do its work.
+ */
if (!bitmap_info->bitmap)
return -EAGAIN;
+ /*
+ * Ok the next item is a bitmap, but it may not actually hold
+ * the information for the rest of this free space stuff, so
+ * look for it, and if we don't find it return so we can try
+ * everything over again.
+ */
+ search_start = *offset;
+ search_bytes = *bytes;
+ ret = search_bitmap(block_group, bitmap_info, &search_start,
+ &search_bytes);
+ if (ret < 0 || search_start != *offset)
+ return -EAGAIN;
+
goto again;
} else if (!bitmap_info->bytes) {
unlink_free_space(block_group, bitmap_info);
@@ -644,8 +683,17 @@ int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group,
again:
info = tree_search_offset(block_group, offset, 0, 0);
if (!info) {
- WARN_ON(1);
- goto out_lock;
+ /*
+ * oops didn't find an extent that matched the space we wanted
+ * to remove, look for a bitmap instead
+ */
+ info = tree_search_offset(block_group,
+ offset_to_bitmap(block_group, offset),
+ 1, 0);
+ if (!info) {
+ WARN_ON(1);
+ goto out_lock;
+ }
}
if (info->bytes < bytes && rb_next(&info->offset_index)) {
@@ -957,8 +1005,15 @@ static u64 btrfs_alloc_from_bitmap(struct btrfs_block_group_cache *block_group,
if (cluster->block_group != block_group)
goto out;
- entry = tree_search_offset(block_group, search_start, 0, 0);
-
+ /*
+ * search_start is the beginning of the bitmap, but at some point it may
+ * be a good idea to point to the actual start of the free area in the
+ * bitmap, so do the offset_to_bitmap trick anyway, and set bitmap_only
+ * to 1 to make sure we get the bitmap entry
+ */
+ entry = tree_search_offset(block_group,
+ offset_to_bitmap(block_group, search_start),
+ 1, 0);
if (!entry || !entry->bitmap)
goto out;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 56fe83fa60c..59cba180fe8 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3099,8 +3099,12 @@ static void inode_tree_add(struct inode *inode)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_inode *entry;
- struct rb_node **p = &root->inode_tree.rb_node;
- struct rb_node *parent = NULL;
+ struct rb_node **p;
+ struct rb_node *parent;
+
+again:
+ p = &root->inode_tree.rb_node;
+ parent = NULL;
spin_lock(&root->inode_lock);
while (*p) {
@@ -3108,13 +3112,16 @@ static void inode_tree_add(struct inode *inode)
entry = rb_entry(parent, struct btrfs_inode, rb_node);
if (inode->i_ino < entry->vfs_inode.i_ino)
- p = &(*p)->rb_left;
+ p = &parent->rb_left;
else if (inode->i_ino > entry->vfs_inode.i_ino)
- p = &(*p)->rb_right;
+ p = &parent->rb_right;
else {
WARN_ON(!(entry->vfs_inode.i_state &
(I_WILL_FREE | I_FREEING | I_CLEAR)));
- break;
+ rb_erase(parent, &root->inode_tree);
+ RB_CLEAR_NODE(parent);
+ spin_unlock(&root->inode_lock);
+ goto again;
}
}
rb_link_node(&BTRFS_I(inode)->rb_node, parent, p);
@@ -3126,12 +3133,12 @@ static void inode_tree_del(struct inode *inode)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
+ spin_lock(&root->inode_lock);
if (!RB_EMPTY_NODE(&BTRFS_I(inode)->rb_node)) {
- spin_lock(&root->inode_lock);
rb_erase(&BTRFS_I(inode)->rb_node, &root->inode_tree);
- spin_unlock(&root->inode_lock);
RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node);
}
+ spin_unlock(&root->inode_lock);
}
static noinline void init_btrfs_i(struct inode *inode)
@@ -4785,8 +4792,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
* and the replacement file is large. Start IO on it now so
* we don't add too much work to the end of the transaction
*/
- if (new_inode && old_inode && S_ISREG(old_inode->i_mode) &&
- new_inode->i_size &&
+ if (new_inode && S_ISREG(old_inode->i_mode) && new_inode->i_size &&
old_inode->i_size > BTRFS_ORDERED_OPERATIONS_FLUSH_LIMIT)
filemap_flush(old_inode->i_mapping);
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index e71264d1c2c..c04f7f21260 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -2553,8 +2553,13 @@ int relocate_inode_pages(struct inode *inode, u64 start, u64 len)
last_index = (start + len - 1) >> PAGE_CACHE_SHIFT;
/* make sure the dirty trick played by the caller work */
- ret = invalidate_inode_pages2_range(inode->i_mapping,
- first_index, last_index);
+ while (1) {
+ ret = invalidate_inode_pages2_range(inode->i_mapping,
+ first_index, last_index);
+ if (ret != -EBUSY)
+ break;
+ schedule_timeout(HZ/10);
+ }
if (ret)
goto out_unlock;
diff --git a/fs/btrfs/zlib.c b/fs/btrfs/zlib.c
index ecfbce836d3..3e2b90eaa23 100644
--- a/fs/btrfs/zlib.c
+++ b/fs/btrfs/zlib.c
@@ -208,7 +208,7 @@ int btrfs_zlib_compress_pages(struct address_space *mapping,
*total_in = 0;
workspace = find_zlib_workspace();
- if (!workspace)
+ if (IS_ERR(workspace))
return -1;
if (Z_OK != zlib_deflateInit(&workspace->def_strm, 3)) {
@@ -366,7 +366,7 @@ int btrfs_zlib_decompress_biovec(struct page **pages_in,
char *kaddr;
workspace = find_zlib_workspace();
- if (!workspace)
+ if (IS_ERR(workspace))
return -ENOMEM;
data_in = kmap(pages_in[page_in_index]);
@@ -547,7 +547,7 @@ int btrfs_zlib_decompress(unsigned char *data_in,
return -ENOMEM;
workspace = find_zlib_workspace();
- if (!workspace)
+ if (IS_ERR(workspace))
return -ENOMEM;
workspace->inf_strm.next_in = data_in;
diff --git a/fs/buffer.c b/fs/buffer.c
index a3ef091a45b..28f320fac4d 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -1165,8 +1165,11 @@ void mark_buffer_dirty(struct buffer_head *bh)
if (!test_set_buffer_dirty(bh)) {
struct page *page = bh->b_page;
- if (!TestSetPageDirty(page))
- __set_page_dirty(page, page_mapping(page), 0);
+ if (!TestSetPageDirty(page)) {
+ struct address_space *mapping = page_mapping(page);
+ if (mapping)
+ __set_page_dirty(page, mapping, 0);
+ }
}
}
diff --git a/fs/char_dev.c b/fs/char_dev.c
index a173551e19d..2f18c1e4e30 100644
--- a/fs/char_dev.c
+++ b/fs/char_dev.c
@@ -237,8 +237,10 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
}
/**
- * register_chrdev() - Register a major number for character devices.
+ * __register_chrdev() - create and register a cdev occupying a range of minors
* @major: major device number or 0 for dynamic allocation
+ * @baseminor: first of the requested range of minor numbers
+ * @count: the number of minor numbers required
* @name: name of this range of devices
* @fops: file operations associated with this devices
*
@@ -254,19 +256,17 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
* /dev. It only helps to keep track of the different owners of devices. If
* your module name has only one type of devices it's ok to use e.g. the name
* of the module here.
- *
- * This function registers a range of 256 minor numbers. The first minor number
- * is 0.
*/
-int register_chrdev(unsigned int major, const char *name,
- const struct file_operations *fops)
+int __register_chrdev(unsigned int major, unsigned int baseminor,
+ unsigned int count, const char *name,
+ const struct file_operations *fops)
{
struct char_device_struct *cd;
struct cdev *cdev;
char *s;
int err = -ENOMEM;
- cd = __register_chrdev_region(major, 0, 256, name);
+ cd = __register_chrdev_region(major, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
@@ -280,7 +280,7 @@ int register_chrdev(unsigned int major, const char *name,
for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
*s = '!';
- err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
+ err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
if (err)
goto out;
@@ -290,7 +290,7 @@ int register_chrdev(unsigned int major, const char *name,
out:
kobject_put(&cdev->kobj);
out2:
- kfree(__unregister_chrdev_region(cd->major, 0, 256));
+ kfree(__unregister_chrdev_region(cd->major, baseminor, count));
return err;
}
@@ -316,10 +316,23 @@ void unregister_chrdev_region(dev_t from, unsigned count)
}
}
-void unregister_chrdev(unsigned int major, const char *name)
+/**
+ * __unregister_chrdev - unregister and destroy a cdev
+ * @major: major device number
+ * @baseminor: first of the range of minor numbers
+ * @count: the number of minor numbers this cdev is occupying
+ * @name: name of this range of devices
+ *
+ * Unregister and destroy the cdev occupying the region described by
+ * @major, @baseminor and @count. This function undoes what
+ * __register_chrdev() did.
+ */
+void __unregister_chrdev(unsigned int major, unsigned int baseminor,
+ unsigned int count, const char *name)
{
struct char_device_struct *cd;
- cd = __unregister_chrdev_region(major, 0, 256);
+
+ cd = __unregister_chrdev_region(major, baseminor, count);
if (cd && cd->cdev)
cdev_del(cd->cdev);
kfree(cd);
@@ -568,6 +581,6 @@ EXPORT_SYMBOL(cdev_alloc);
EXPORT_SYMBOL(cdev_del);
EXPORT_SYMBOL(cdev_add);
EXPORT_SYMBOL(cdev_index);
-EXPORT_SYMBOL(register_chrdev);
-EXPORT_SYMBOL(unregister_chrdev);
+EXPORT_SYMBOL(__register_chrdev);
+EXPORT_SYMBOL(__unregister_chrdev);
EXPORT_SYMBOL(directly_mappable_cdev_bdi);
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index 92888aa9074..e85b1e4389e 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -1,3 +1,10 @@
+Version 1.60
+-------------
+Fix memory leak in reconnect. Fix oops in DFS mount error path.
+Set s_maxbytes to smaller (the max that vfs can handle) so that
+sendfile will now work over cifs mounts again. Add noforcegid
+and noforceuid mount parameters.
+
Version 1.59
------------
Client uses server inode numbers (which are persistent) rather than
diff --git a/fs/cifs/README b/fs/cifs/README
index ad92921dbde..79c1a93400b 100644
--- a/fs/cifs/README
+++ b/fs/cifs/README
@@ -262,11 +262,11 @@ A partial list of the supported mount options follows:
mount.
domain Set the SMB/CIFS workgroup name prepended to the
username during CIFS session establishment
- forceuid Set the default uid for inodes based on the uid
- passed in. For mounts to servers
+ forceuid Set the default uid for inodes to the uid
+ passed in on mount. For mounts to servers
which do support the CIFS Unix extensions, such as a
properly configured Samba server, the server provides
- the uid, gid and mode so this parameter should not be
+ the uid, gid and mode so this parameter should not be
specified unless the server and clients uid and gid
numbering differ. If the server and client are in the
same domain (e.g. running winbind or nss_ldap) and
@@ -278,11 +278,7 @@ A partial list of the supported mount options follows:
of existing files will be the uid (gid) of the person
who executed the mount (root, except when mount.cifs
is configured setuid for user mounts) unless the "uid="
- (gid) mount option is specified. For the uid (gid) of newly
- created files and directories, ie files created since
- the last mount of the server share, the expected uid
- (gid) is cached as long as the inode remains in
- memory on the client. Also note that permission
+ (gid) mount option is specified. Also note that permission
checks (authorization checks) on accesses to a file occur
at the server, but there are cases in which an administrator
may want to restrict at the client as well. For those
@@ -290,12 +286,15 @@ A partial list of the supported mount options follows:
(such as Windows), permissions can also be checked at the
client, and a crude form of client side permission checking
can be enabled by specifying file_mode and dir_mode on
- the client. Note that the mount.cifs helper must be
- at version 1.10 or higher to support specifying the uid
- (or gid) in non-numeric form.
- forcegid (similar to above but for the groupid instead of uid)
+ the client. (default)
+ forcegid (similar to above but for the groupid instead of uid) (default)
+ noforceuid Fill in file owner information (uid) by requesting it from
+ the server if possible. With this option, the value given in
+ the uid= option (on mount) will only be used if the server
+ can not support returning uids on inodes.
+ noforcegid (similar to above but for the group owner, gid, instead of uid)
uid Set the default uid for inodes, and indicate to the
- cifs kernel driver which local user mounted . If the server
+ cifs kernel driver which local user mounted. If the server
supports the unix extensions the default uid is
not used to fill in the owner fields of inodes (files)
unless the "forceuid" parameter is specified.
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index 3bb11be8b6a..606912d8f2a 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -55,7 +55,7 @@ void cifs_dfs_release_automount_timer(void)
* i.e. strips from UNC trailing path that is not part of share
* name and fixup missing '\' in the begining of DFS node refferal
* if neccessary.
- * Returns pointer to share name on success or NULL on error.
+ * Returns pointer to share name on success or ERR_PTR on error.
* Caller is responsible for freeing returned string.
*/
static char *cifs_get_share_name(const char *node_name)
@@ -68,7 +68,7 @@ static char *cifs_get_share_name(const char *node_name)
UNC = kmalloc(len+2 /*for term null and additional \ if it's missed */,
GFP_KERNEL);
if (!UNC)
- return NULL;
+ return ERR_PTR(-ENOMEM);
/* get share name and server name */
if (node_name[1] != '\\') {
@@ -87,7 +87,7 @@ static char *cifs_get_share_name(const char *node_name)
cERROR(1, ("%s: no server name end in node name: %s",
__func__, node_name));
kfree(UNC);
- return NULL;
+ return ERR_PTR(-EINVAL);
}
/* find sharename end */
@@ -133,6 +133,12 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
return ERR_PTR(-EINVAL);
*devname = cifs_get_share_name(ref->node_name);
+ if (IS_ERR(*devname)) {
+ rc = PTR_ERR(*devname);
+ *devname = NULL;
+ goto compose_mount_options_err;
+ }
+
rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
if (rc != 0) {
cERROR(1, ("%s: Failed to resolve server part of %s to IP: %d",
diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c
index 60e3c4253de..714a542cbaf 100644
--- a/fs/cifs/cifs_unicode.c
+++ b/fs/cifs/cifs_unicode.c
@@ -44,7 +44,7 @@ cifs_ucs2_bytes(const __le16 *from, int maxbytes,
int maxwords = maxbytes / 2;
char tmp[NLS_MAX_CHARSET_SIZE];
- for (i = 0; from[i] && i < maxwords; i++) {
+ for (i = 0; i < maxwords && from[i]; i++) {
charlen = codepage->uni2char(le16_to_cpu(from[i]), tmp,
NLS_MAX_CHARSET_SIZE);
if (charlen > 0)
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 44f30504b82..84b75253b05 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -376,10 +376,14 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m)
seq_printf(s, ",uid=%d", cifs_sb->mnt_uid);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
seq_printf(s, ",forceuid");
+ else
+ seq_printf(s, ",noforceuid");
seq_printf(s, ",gid=%d", cifs_sb->mnt_gid);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)
seq_printf(s, ",forcegid");
+ else
+ seq_printf(s, ",noforcegid");
cifs_show_address(s, tcon->ses->server);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index fc44d316d0b..1f3345d7fa7 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -803,6 +803,10 @@ cifs_parse_mount_options(char *options, const char *devname,
char *data;
unsigned int temp_len, i, j;
char separator[2];
+ short int override_uid = -1;
+ short int override_gid = -1;
+ bool uid_specified = false;
+ bool gid_specified = false;
separator[0] = ',';
separator[1] = 0;
@@ -1093,18 +1097,20 @@ cifs_parse_mount_options(char *options, const char *devname,
"too long.\n");
return 1;
}
- } else if (strnicmp(data, "uid", 3) == 0) {
- if (value && *value)
- vol->linux_uid =
- simple_strtoul(value, &value, 0);
- } else if (strnicmp(data, "forceuid", 8) == 0) {
- vol->override_uid = 1;
- } else if (strnicmp(data, "gid", 3) == 0) {
- if (value && *value)
- vol->linux_gid =
- simple_strtoul(value, &value, 0);
- } else if (strnicmp(data, "forcegid", 8) == 0) {
- vol->override_gid = 1;
+ } else if (!strnicmp(data, "uid", 3) && value && *value) {
+ vol->linux_uid = simple_strtoul(value, &value, 0);
+ uid_specified = true;
+ } else if (!strnicmp(data, "forceuid", 8)) {
+ override_uid = 1;
+ } else if (!strnicmp(data, "noforceuid", 10)) {
+ override_uid = 0;
+ } else if (!strnicmp(data, "gid", 3) && value && *value) {
+ vol->linux_gid = simple_strtoul(value, &value, 0);
+ gid_specified = true;
+ } else if (!strnicmp(data, "forcegid", 8)) {
+ override_gid = 1;
+ } else if (!strnicmp(data, "noforcegid", 10)) {
+ override_gid = 0;
} else if (strnicmp(data, "file_mode", 4) == 0) {
if (value && *value) {
vol->file_mode =
@@ -1355,6 +1361,18 @@ cifs_parse_mount_options(char *options, const char *devname,
if (vol->UNCip == NULL)
vol->UNCip = &vol->UNC[2];
+ if (uid_specified)
+ vol->override_uid = override_uid;
+ else if (override_uid == 1)
+ printk(KERN_NOTICE "CIFS: ignoring forceuid mount option "
+ "specified with no uid= option.\n");
+
+ if (gid_specified)
+ vol->override_gid = override_gid;
+ else if (override_gid == 1)
+ printk(KERN_NOTICE "CIFS: ignoring forcegid mount option "
+ "specified with no gid= option.\n");
+
return 0;
}
@@ -2544,11 +2562,20 @@ remote_path_check:
if (mount_data != mount_data_global)
kfree(mount_data);
+
mount_data = cifs_compose_mount_options(
cifs_sb->mountdata, full_path + 1,
referrals, &fake_devname);
- kfree(fake_devname);
+
free_dfs_info_array(referrals, num_referrals);
+ kfree(fake_devname);
+ kfree(full_path);
+
+ if (IS_ERR(mount_data)) {
+ rc = PTR_ERR(mount_data);
+ mount_data = NULL;
+ goto mount_fail_check;
+ }
if (tcon)
cifs_put_tcon(tcon);
@@ -2556,8 +2583,6 @@ remote_path_check:
cifs_put_smb_ses(pSesInfo);
cleanup_volume_info(&volume_info);
- FreeXid(xid);
- kfree(full_path);
referral_walks_count++;
goto try_mount_again;
}
diff --git a/fs/compat.c b/fs/compat.c
index 94502dab972..6d6f98fe64a 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1485,20 +1485,15 @@ int compat_do_execve(char * filename,
if (!bprm)
goto out_files;
- retval = -ERESTARTNOINTR;
- if (mutex_lock_interruptible(&current->cred_guard_mutex))
+ retval = prepare_bprm_creds(bprm);
+ if (retval)
goto out_free;
- current->in_execve = 1;
-
- retval = -ENOMEM;
- bprm->cred = prepare_exec_creds();
- if (!bprm->cred)
- goto out_unlock;
retval = check_unsafe_exec(bprm);
if (retval < 0)
- goto out_unlock;
+ goto out_free;
clear_in_exec = retval;
+ current->in_execve = 1;
file = open_exec(filename);
retval = PTR_ERR(file);
@@ -1547,7 +1542,6 @@ int compat_do_execve(char * filename,
/* execve succeeded */
current->fs->in_exec = 0;
current->in_execve = 0;
- mutex_unlock(&current->cred_guard_mutex);
acct_update_integrals(current);
free_bprm(bprm);
if (displaced)
@@ -1567,10 +1561,7 @@ out_file:
out_unmark:
if (clear_in_exec)
current->fs->in_exec = 0;
-
-out_unlock:
current->in_execve = 0;
- mutex_unlock(&current->cred_guard_mutex);
out_free:
free_bprm(bprm);
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index f28f070a60f..f91fd51b32e 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -1905,6 +1905,7 @@ COMPATIBLE_IOCTL(FIONCLEX)
COMPATIBLE_IOCTL(FIOASYNC)
COMPATIBLE_IOCTL(FIONBIO)
COMPATIBLE_IOCTL(FIONREAD) /* This is also TIOCINQ */
+COMPATIBLE_IOCTL(FS_IOC_FIEMAP)
/* 0x00 */
COMPATIBLE_IOCTL(FIBMAP)
COMPATIBLE_IOCTL(FIGETBSZ)
diff --git a/fs/exec.c b/fs/exec.c
index 4a8849e45b2..172ceb6edde 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -678,8 +678,8 @@ exit:
}
EXPORT_SYMBOL(open_exec);
-int kernel_read(struct file *file, unsigned long offset,
- char *addr, unsigned long count)
+int kernel_read(struct file *file, loff_t offset,
+ char *addr, unsigned long count)
{
mm_segment_t old_fs;
loff_t pos = offset;
@@ -1016,6 +1016,35 @@ out:
EXPORT_SYMBOL(flush_old_exec);
/*
+ * Prepare credentials and lock ->cred_guard_mutex.
+ * install_exec_creds() commits the new creds and drops the lock.
+ * Or, if exec fails before, free_bprm() should release ->cred and
+ * and unlock.
+ */
+int prepare_bprm_creds(struct linux_binprm *bprm)
+{
+ if (mutex_lock_interruptible(&current->cred_guard_mutex))
+ return -ERESTARTNOINTR;
+
+ bprm->cred = prepare_exec_creds();
+ if (likely(bprm->cred))
+ return 0;
+
+ mutex_unlock(&current->cred_guard_mutex);
+ return -ENOMEM;
+}
+
+void free_bprm(struct linux_binprm *bprm)
+{
+ free_arg_pages(bprm);
+ if (bprm->cred) {
+ mutex_unlock(&current->cred_guard_mutex);
+ abort_creds(bprm->cred);
+ }
+ kfree(bprm);
+}
+
+/*
* install the new credentials for this executable
*/
void install_exec_creds(struct linux_binprm *bprm)
@@ -1024,12 +1053,13 @@ void install_exec_creds(struct linux_binprm *bprm)
commit_creds(bprm->cred);
bprm->cred = NULL;
-
- /* cred_guard_mutex must be held at least to this point to prevent
+ /*
+ * cred_guard_mutex must be held at least to this point to prevent
* ptrace_attach() from altering our determination of the task's
- * credentials; any time after this it may be unlocked */
-
+ * credentials; any time after this it may be unlocked.
+ */
security_bprm_committed_creds(bprm);
+ mutex_unlock(&current->cred_guard_mutex);
}
EXPORT_SYMBOL(install_exec_creds);
@@ -1246,14 +1276,6 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
EXPORT_SYMBOL(search_binary_handler);
-void free_bprm(struct linux_binprm *bprm)
-{
- free_arg_pages(bprm);
- if (bprm->cred)
- abort_creds(bprm->cred);
- kfree(bprm);
-}
-
/*
* sys_execve() executes a new program.
*/
@@ -1277,20 +1299,15 @@ int do_execve(char * filename,
if (!bprm)
goto out_files;
- retval = -ERESTARTNOINTR;
- if (mutex_lock_interruptible(&current->cred_guard_mutex))
+ retval = prepare_bprm_creds(bprm);
+ if (retval)
goto out_free;
- current->in_execve = 1;
-
- retval = -ENOMEM;
- bprm->cred = prepare_exec_creds();
- if (!bprm->cred)
- goto out_unlock;
retval = check_unsafe_exec(bprm);
if (retval < 0)
- goto out_unlock;
+ goto out_free;
clear_in_exec = retval;
+ current->in_execve = 1;
file = open_exec(filename);
retval = PTR_ERR(file);
@@ -1340,7 +1357,6 @@ int do_execve(char * filename,
/* execve succeeded */
current->fs->in_exec = 0;
current->in_execve = 0;
- mutex_unlock(&current->cred_guard_mutex);
acct_update_integrals(current);
free_bprm(bprm);
if (displaced)
@@ -1360,10 +1376,7 @@ out_file:
out_unmark:
if (clear_in_exec)
current->fs->in_exec = 0;
-
-out_unlock:
current->in_execve = 0;
- mutex_unlock(&current->cred_guard_mutex);
out_free:
free_bprm(bprm);
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index e1dedb0f787..78d9b925fc9 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -362,6 +362,10 @@ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry,
if (dir_de) {
if (old_dir != new_dir)
ext2_set_link(old_inode, dir_de, dir_page, new_dir, 0);
+ else {
+ kunmap(dir_page);
+ page_cache_release(dir_page);
+ }
inode_dec_link_count(old_dir);
}
return 0;
diff --git a/fs/ext3/Kconfig b/fs/ext3/Kconfig
index fb3c1a21b13..522b15498f4 100644
--- a/fs/ext3/Kconfig
+++ b/fs/ext3/Kconfig
@@ -29,23 +29,25 @@ config EXT3_FS
module will be called ext3.
config EXT3_DEFAULTS_TO_ORDERED
- bool "Default to 'data=ordered' in ext3 (legacy option)"
+ bool "Default to 'data=ordered' in ext3"
depends on EXT3_FS
help
- If a filesystem does not explicitly specify a data ordering
- mode, and the journal capability allowed it, ext3 used to
- historically default to 'data=ordered'.
-
- That was a rather unfortunate choice, because it leads to all
- kinds of latency problems, and the 'data=writeback' mode is more
- appropriate these days.
-
- You should probably always answer 'n' here, and if you really
- want to use 'data=ordered' mode, set it in the filesystem itself
- with 'tune2fs -o journal_data_ordered'.
-
- But if you really want to enable the legacy default, you can do
- so by answering 'y' to this question.
+ The journal mode options for ext3 have different tradeoffs
+ between when data is guaranteed to be on disk and
+ performance. The use of "data=writeback" can cause
+ unwritten data to appear in files after an system crash or
+ power failure, which can be a security issue. However,
+ "data=ordered" mode can also result in major performance
+ problems, including seconds-long delays before an fsync()
+ call returns. For details, see:
+
+ http://ext4.wiki.kernel.org/index.php/Ext3_data_mode_tradeoffs
+
+ If you have been historically happy with ext3's performance,
+ data=ordered mode will be a safe choice and you should
+ answer 'y' here. If you understand the reliability and data
+ privacy issues of data=writeback and are willing to make
+ that trade off, answer 'n'.
config EXT3_FS_XATTR
bool "Ext3 extended attributes"
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index 524b349c629..a8d80a7f110 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -543,6 +543,19 @@ static inline void ext3_show_quota_options(struct seq_file *seq, struct super_bl
#endif
}
+static char *data_mode_string(unsigned long mode)
+{
+ switch (mode) {
+ case EXT3_MOUNT_JOURNAL_DATA:
+ return "journal";
+ case EXT3_MOUNT_ORDERED_DATA:
+ return "ordered";
+ case EXT3_MOUNT_WRITEBACK_DATA:
+ return "writeback";
+ }
+ return "unknown";
+}
+
/*
* Show an option if
* - it's set to a non-default value OR
@@ -616,13 +629,8 @@ static int ext3_show_options(struct seq_file *seq, struct vfsmount *vfs)
if (test_opt(sb, NOBH))
seq_puts(seq, ",nobh");
- if (test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_JOURNAL_DATA)
- seq_puts(seq, ",data=journal");
- else if (test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_ORDERED_DATA)
- seq_puts(seq, ",data=ordered");
- else if (test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_WRITEBACK_DATA)
- seq_puts(seq, ",data=writeback");
-
+ seq_printf(seq, ",data=%s", data_mode_string(sbi->s_mount_opt &
+ EXT3_MOUNT_DATA_FLAGS));
if (test_opt(sb, DATA_ERR_ABORT))
seq_puts(seq, ",data_err=abort");
@@ -1024,12 +1032,18 @@ static int parse_options (char *options, struct super_block *sb,
datacheck:
if (is_remount) {
if ((sbi->s_mount_opt & EXT3_MOUNT_DATA_FLAGS)
- != data_opt) {
- printk(KERN_ERR
- "EXT3-fs: cannot change data "
- "mode on remount\n");
- return 0;
- }
+ == data_opt)
+ break;
+ printk(KERN_ERR
+ "EXT3-fs (device %s): Cannot change "
+ "data mode on remount. The filesystem "
+ "is mounted in data=%s mode and you "
+ "try to remount it in data=%s mode.\n",
+ sb->s_id,
+ data_mode_string(sbi->s_mount_opt &
+ EXT3_MOUNT_DATA_FLAGS),
+ data_mode_string(data_opt));
+ return 0;
} else {
sbi->s_mount_opt &= ~EXT3_MOUNT_DATA_FLAGS;
sbi->s_mount_opt |= data_opt;
diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c
index 23419dc3027..a7cbfbd340c 100644
--- a/fs/gfs2/sys.c
+++ b/fs/gfs2/sys.c
@@ -386,16 +386,16 @@ static ssize_t jid_show(struct gfs2_sbd *sdp, char *buf)
#define GDLM_ATTR(_name,_mode,_show,_store) \
static struct gfs2_attr gdlm_attr_##_name = __ATTR(_name,_mode,_show,_store)
-GDLM_ATTR(proto_name, 0444, proto_name_show, NULL);
-GDLM_ATTR(block, 0644, block_show, block_store);
-GDLM_ATTR(withdraw, 0644, withdraw_show, withdraw_store);
-GDLM_ATTR(id, 0444, lkid_show, NULL);
-GDLM_ATTR(jid, 0444, jid_show, NULL);
-GDLM_ATTR(first, 0444, lkfirst_show, NULL);
-GDLM_ATTR(first_done, 0444, first_done_show, NULL);
-GDLM_ATTR(recover, 0200, NULL, recover_store);
-GDLM_ATTR(recover_done, 0444, recover_done_show, NULL);
-GDLM_ATTR(recover_status, 0444, recover_status_show, NULL);
+GDLM_ATTR(proto_name, 0444, proto_name_show, NULL);
+GDLM_ATTR(block, 0644, block_show, block_store);
+GDLM_ATTR(withdraw, 0644, withdraw_show, withdraw_store);
+GDLM_ATTR(id, 0444, lkid_show, NULL);
+GDLM_ATTR(jid, 0444, jid_show, NULL);
+GDLM_ATTR(first, 0444, lkfirst_show, NULL);
+GDLM_ATTR(first_done, 0444, first_done_show, NULL);
+GDLM_ATTR(recover, 0600, NULL, recover_store);
+GDLM_ATTR(recover_done, 0444, recover_done_show, NULL);
+GDLM_ATTR(recover_status, 0444, recover_status_show, NULL);
static struct attribute *lock_module_attrs[] = {
&gdlm_attr_proto_name.attr,
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 941c8425c10..cb88dac8cca 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -935,26 +935,28 @@ static int can_do_hugetlb_shm(void)
return capable(CAP_IPC_LOCK) || in_group_p(sysctl_hugetlb_shm_group);
}
-struct file *hugetlb_file_setup(const char *name, size_t size, int acctflag)
+struct file *hugetlb_file_setup(const char *name, size_t size, int acctflag,
+ struct user_struct **user)
{
int error = -ENOMEM;
- int unlock_shm = 0;
struct file *file;
struct inode *inode;
struct dentry *dentry, *root;
struct qstr quick_string;
- struct user_struct *user = current_user();
+ *user = NULL;
if (!hugetlbfs_vfsmount)
return ERR_PTR(-ENOENT);
if (!can_do_hugetlb_shm()) {
- if (user_shm_lock(size, user)) {
- unlock_shm = 1;
+ *user = current_user();
+ if (user_shm_lock(size, *user)) {
WARN_ONCE(1,
"Using mlock ulimits for SHM_HUGETLB deprecated\n");
- } else
+ } else {
+ *user = NULL;
return ERR_PTR(-EPERM);
+ }
}
root = hugetlbfs_vfsmount->mnt_root;
@@ -996,8 +998,10 @@ out_inode:
out_dentry:
dput(dentry);
out_shm_unlock:
- if (unlock_shm)
- user_shm_unlock(size, user);
+ if (*user) {
+ user_shm_unlock(size, *user);
+ *user = NULL;
+ }
return ERR_PTR(error);
}
diff --git a/fs/inode.c b/fs/inode.c
index 901bad1e5f1..ae7b67e4866 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -120,12 +120,11 @@ static void wake_up_inode(struct inode *inode)
* These are initializations that need to be done on every inode
* allocation as the fields are not initialised by slab allocation.
*/
-struct inode *inode_init_always(struct super_block *sb, struct inode *inode)
+int inode_init_always(struct super_block *sb, struct inode *inode)
{
static const struct address_space_operations empty_aops;
static struct inode_operations empty_iops;
static const struct file_operations empty_fops;
-
struct address_space *const mapping = &inode->i_data;
inode->i_sb = sb;
@@ -152,7 +151,7 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode)
inode->dirtied_when = 0;
if (security_inode_alloc(inode))
- goto out_free_inode;
+ goto out;
/* allocate and initialize an i_integrity */
if (ima_inode_alloc(inode))
@@ -198,16 +197,12 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode)
inode->i_fsnotify_mask = 0;
#endif
- return inode;
+ return 0;
out_free_security:
security_inode_free(inode);
-out_free_inode:
- if (inode->i_sb->s_op->destroy_inode)
- inode->i_sb->s_op->destroy_inode(inode);
- else
- kmem_cache_free(inode_cachep, (inode));
- return NULL;
+out:
+ return -ENOMEM;
}
EXPORT_SYMBOL(inode_init_always);
@@ -220,12 +215,21 @@ static struct inode *alloc_inode(struct super_block *sb)
else
inode = kmem_cache_alloc(inode_cachep, GFP_KERNEL);
- if (inode)
- return inode_init_always(sb, inode);
- return NULL;
+ if (!inode)
+ return NULL;
+
+ if (unlikely(inode_init_always(sb, inode))) {
+ if (inode->i_sb->s_op->destroy_inode)
+ inode->i_sb->s_op->destroy_inode(inode);
+ else
+ kmem_cache_free(inode_cachep, inode);
+ return NULL;
+ }
+
+ return inode;
}
-void destroy_inode(struct inode *inode)
+void __destroy_inode(struct inode *inode)
{
BUG_ON(inode_has_buffers(inode));
ima_inode_free(inode);
@@ -237,13 +241,17 @@ void destroy_inode(struct inode *inode)
if (inode->i_default_acl && inode->i_default_acl != ACL_NOT_CACHED)
posix_acl_release(inode->i_default_acl);
#endif
+}
+EXPORT_SYMBOL(__destroy_inode);
+
+void destroy_inode(struct inode *inode)
+{
+ __destroy_inode(inode);
if (inode->i_sb->s_op->destroy_inode)
inode->i_sb->s_op->destroy_inode(inode);
else
kmem_cache_free(inode_cachep, (inode));
}
-EXPORT_SYMBOL(destroy_inode);
-
/*
* These are initializations that only need to be done
diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c
index 5edc2bf2058..23c94753986 100644
--- a/fs/jffs2/file.c
+++ b/fs/jffs2/file.c
@@ -99,7 +99,7 @@ static int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
kunmap(pg);
D2(printk(KERN_DEBUG "readpage finished\n"));
- return 0;
+ return ret;
}
int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg)
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c
index d9a721e6db7..5ef7bac265e 100644
--- a/fs/jffs2/wbuf.c
+++ b/fs/jffs2/wbuf.c
@@ -1268,10 +1268,20 @@ int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) {
if (!c->wbuf)
return -ENOMEM;
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+ c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+ if (!c->wbuf_verify) {
+ kfree(c->wbuf);
+ return -ENOMEM;
+ }
+#endif
return 0;
}
void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c) {
+#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
+ kfree(c->wbuf_verify);
+#endif
kfree(c->wbuf);
}
diff --git a/fs/libfs.c b/fs/libfs.c
index ddfa89948c3..dcec3d3ea64 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -217,7 +217,7 @@ int get_sb_pseudo(struct file_system_type *fs_type, char *name,
return PTR_ERR(s);
s->s_flags = MS_NOUSER;
- s->s_maxbytes = ~0ULL;
+ s->s_maxbytes = MAX_LFS_FILESIZE;
s->s_blocksize = PAGE_SIZE;
s->s_blocksize_bits = PAGE_SHIFT;
s->s_magic = magic;
diff --git a/fs/namei.c b/fs/namei.c
index f3c5b278895..1f13751693a 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1542,28 +1542,31 @@ int may_open(struct path *path, int acc_mode, int flag)
* An append-only file must be opened in append mode for writing.
*/
if (IS_APPEND(inode)) {
+ error = -EPERM;
if ((flag & FMODE_WRITE) && !(flag & O_APPEND))
- return -EPERM;
+ goto err_out;
if (flag & O_TRUNC)
- return -EPERM;
+ goto err_out;
}
/* O_NOATIME can only be set by the owner or superuser */
if (flag & O_NOATIME)
- if (!is_owner_or_cap(inode))
- return -EPERM;
+ if (!is_owner_or_cap(inode)) {
+ error = -EPERM;
+ goto err_out;
+ }
/*
* Ensure there are no outstanding leases on the file.
*/
error = break_lease(inode, flag);
if (error)
- return error;
+ goto err_out;
if (flag & O_TRUNC) {
error = get_write_access(inode);
if (error)
- return error;
+ goto err_out;
/*
* Refuse to truncate files with mandatory locks held on them.
@@ -1581,12 +1584,17 @@ int may_open(struct path *path, int acc_mode, int flag)
}
put_write_access(inode);
if (error)
- return error;
+ goto err_out;
} else
if (flag & FMODE_WRITE)
vfs_dq_init(inode);
return 0;
+err_out:
+ ima_counts_put(path, acc_mode ?
+ acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC) :
+ ACC_MODE(flag) & (MAY_READ | MAY_WRITE));
+ return error;
}
/*
diff --git a/fs/namespace.c b/fs/namespace.c
index 277c28a63ea..7230787d18b 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -316,7 +316,8 @@ EXPORT_SYMBOL_GPL(mnt_clone_write);
*/
int mnt_want_write_file(struct file *file)
{
- if (!(file->f_mode & FMODE_WRITE))
+ struct inode *inode = file->f_dentry->d_inode;
+ if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode))
return mnt_want_write(file->f_path.mnt);
else
return mnt_clone_write(file->f_path.mnt);
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 489fc01a320..e4e089a8f29 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -255,7 +255,7 @@ static void nfs_direct_read_release(void *calldata)
if (put_dreq(dreq))
nfs_direct_complete(dreq);
- nfs_readdata_release(calldata);
+ nfs_readdata_free(data);
}
static const struct rpc_call_ops nfs_read_direct_ops = {
@@ -314,14 +314,14 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
data->npages, 1, 0, data->pagevec, NULL);
up_read(&current->mm->mmap_sem);
if (result < 0) {
- nfs_readdata_release(data);
+ nfs_readdata_free(data);
break;
}
if ((unsigned)result < data->npages) {
bytes = result * PAGE_SIZE;
if (bytes <= pgbase) {
nfs_direct_release_pages(data->pagevec, result);
- nfs_readdata_release(data);
+ nfs_readdata_free(data);
break;
}
bytes -= pgbase;
@@ -334,7 +334,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
data->inode = inode;
data->cred = msg.rpc_cred;
data->args.fh = NFS_FH(inode);
- data->args.context = get_nfs_open_context(ctx);
+ data->args.context = ctx;
data->args.offset = pos;
data->args.pgbase = pgbase;
data->args.pages = data->pagevec;
@@ -441,7 +441,7 @@ static void nfs_direct_free_writedata(struct nfs_direct_req *dreq)
struct nfs_write_data *data = list_entry(dreq->rewrite_list.next, struct nfs_write_data, pages);
list_del(&data->pages);
nfs_direct_release_pages(data->pagevec, data->npages);
- nfs_writedata_release(data);
+ nfs_writedata_free(data);
}
}
@@ -534,7 +534,7 @@ static void nfs_direct_commit_release(void *calldata)
dprintk("NFS: %5u commit returned %d\n", data->task.tk_pid, status);
nfs_direct_write_complete(dreq, data->inode);
- nfs_commitdata_release(calldata);
+ nfs_commit_free(data);
}
static const struct rpc_call_ops nfs_commit_direct_ops = {
@@ -570,7 +570,7 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
data->args.fh = NFS_FH(data->inode);
data->args.offset = 0;
data->args.count = 0;
- data->args.context = get_nfs_open_context(dreq->ctx);
+ data->args.context = dreq->ctx;
data->res.count = 0;
data->res.fattr = &data->fattr;
data->res.verf = &data->verf;
@@ -734,14 +734,14 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
data->npages, 0, 0, data->pagevec, NULL);
up_read(&current->mm->mmap_sem);
if (result < 0) {
- nfs_writedata_release(data);
+ nfs_writedata_free(data);
break;
}
if ((unsigned)result < data->npages) {
bytes = result * PAGE_SIZE;
if (bytes <= pgbase) {
nfs_direct_release_pages(data->pagevec, result);
- nfs_writedata_release(data);
+ nfs_writedata_free(data);
break;
}
bytes -= pgbase;
@@ -756,7 +756,7 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
data->inode = inode;
data->cred = msg.rpc_cred;
data->args.fh = NFS_FH(inode);
- data->args.context = get_nfs_open_context(ctx);
+ data->args.context = ctx;
data->args.offset = pos;
data->args.pgbase = pgbase;
data->args.pages = data->pagevec;
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 65ca8c18476..1434080aefe 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -1250,8 +1250,8 @@ static void nfs4_state_manager(struct nfs_client *clp)
continue;
}
/* Initialize or reset the session */
- if (nfs4_has_session(clp) &&
- test_and_clear_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state)) {
+ if (test_and_clear_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state)
+ && nfs4_has_session(clp)) {
if (clp->cl_cons_state == NFS_CS_SESSION_INITING)
status = nfs4_initialize_session(clp);
else
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 73ea5e8d66c..12c9e66d3f1 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -60,17 +60,15 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
return p;
}
-static void nfs_readdata_free(struct nfs_read_data *p)
+void nfs_readdata_free(struct nfs_read_data *p)
{
if (p && (p->pagevec != &p->page_array[0]))
kfree(p->pagevec);
mempool_free(p, nfs_rdata_mempool);
}
-void nfs_readdata_release(void *data)
+static void nfs_readdata_release(struct nfs_read_data *rdata)
{
- struct nfs_read_data *rdata = data;
-
put_nfs_open_context(rdata->args.context);
nfs_readdata_free(rdata);
}
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 0a0a2ff767c..a34fae21fe1 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -87,17 +87,15 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
return p;
}
-static void nfs_writedata_free(struct nfs_write_data *p)
+void nfs_writedata_free(struct nfs_write_data *p)
{
if (p && (p->pagevec != &p->page_array[0]))
kfree(p->pagevec);
mempool_free(p, nfs_wdata_mempool);
}
-void nfs_writedata_release(void *data)
+static void nfs_writedata_release(struct nfs_write_data *wdata)
{
- struct nfs_write_data *wdata = data;
-
put_nfs_open_context(wdata->args.context);
nfs_writedata_free(wdata);
}
diff --git a/fs/nilfs2/btnode.c b/fs/nilfs2/btnode.c
index 7e0b61be212..c668bca579c 100644
--- a/fs/nilfs2/btnode.c
+++ b/fs/nilfs2/btnode.c
@@ -209,6 +209,7 @@ int nilfs_btnode_prepare_change_key(struct address_space *btnc,
* We cannot call radix_tree_preload for the kernels older
* than 2.6.23, because it is not exported for modules.
*/
+retry:
err = radix_tree_preload(GFP_NOFS & ~__GFP_HIGHMEM);
if (err)
goto failed_unlock;
@@ -219,7 +220,6 @@ int nilfs_btnode_prepare_change_key(struct address_space *btnc,
(unsigned long long)oldkey,
(unsigned long long)newkey);
-retry:
spin_lock_irq(&btnc->tree_lock);
err = radix_tree_insert(&btnc->page_tree, newkey, obh->b_page);
spin_unlock_irq(&btnc->tree_lock);
diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c
index 3d3ddb3f517..2dfd47714ae 100644
--- a/fs/nilfs2/mdt.c
+++ b/fs/nilfs2/mdt.c
@@ -412,8 +412,10 @@ nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc)
return 0; /* Do not request flush for shadow page cache */
if (!sb) {
writer = nilfs_get_writer(NILFS_MDT(inode)->mi_nilfs);
- if (!writer)
+ if (!writer) {
+ nilfs_put_writer(NILFS_MDT(inode)->mi_nilfs);
return -EROFS;
+ }
sb = writer->s_super;
}
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index 8b5e4778cf2..51ff3d0a4ee 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -1859,12 +1859,26 @@ static void nilfs_end_page_io(struct page *page, int err)
if (!page)
return;
- if (buffer_nilfs_node(page_buffers(page)) && !PageWriteback(page))
+ if (buffer_nilfs_node(page_buffers(page)) && !PageWriteback(page)) {
/*
* For b-tree node pages, this function may be called twice
* or more because they might be split in a segment.
*/
+ if (PageDirty(page)) {
+ /*
+ * For pages holding split b-tree node buffers, dirty
+ * flag on the buffers may be cleared discretely.
+ * In that case, the page is once redirtied for
+ * remaining buffers, and it must be cancelled if
+ * all the buffers get cleaned later.
+ */
+ lock_page(page);
+ if (nilfs_page_buffers_clean(page))
+ __nilfs_clear_page_dirty(page);
+ unlock_page(page);
+ }
return;
+ }
__nilfs_end_page_io(page, err);
}
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 8e2ec43b18f..151964f0de4 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -416,8 +416,10 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
if (unlikely(err))
goto failed;
+ down_read(&nilfs->ns_segctor_sem);
err = nilfs_cpfile_get_checkpoint(nilfs->ns_cpfile, cno, 0, &raw_cp,
&bh_cp);
+ up_read(&nilfs->ns_segctor_sem);
if (unlikely(err)) {
if (err == -ENOENT || err == -EINVAL) {
printk(KERN_ERR
diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h
index e8adbffc626..1b9caafb866 100644
--- a/fs/nilfs2/the_nilfs.h
+++ b/fs/nilfs2/the_nilfs.h
@@ -253,7 +253,7 @@ nilfs_detach_writer(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi)
static inline void nilfs_put_sbinfo(struct nilfs_sb_info *sbi)
{
- if (!atomic_dec_and_test(&sbi->s_count))
+ if (atomic_dec_and_test(&sbi->s_count))
kfree(sbi);
}
diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c
index 47cd258fd24..c9ee67b442e 100644
--- a/fs/notify/inotify/inotify_fsnotify.c
+++ b/fs/notify/inotify/inotify_fsnotify.c
@@ -62,13 +62,14 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev
event_priv->wd = wd;
ret = fsnotify_add_notify_event(group, event, fsn_event_priv);
- /* EEXIST is not an error */
- if (ret == -EEXIST)
- ret = 0;
-
- /* did event_priv get attached? */
- if (list_empty(&fsn_event_priv->event_list))
+ if (ret) {
inotify_free_event_priv(fsn_event_priv);
+ /* EEXIST says we tail matched, EOVERFLOW isn't something
+ * to report up the stack. */
+ if ((ret == -EEXIST) ||
+ (ret == -EOVERFLOW))
+ ret = 0;
+ }
/*
* If we hold the entry until after the event is on the queue
@@ -104,16 +105,45 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode
return send;
}
+/*
+ * This is NEVER supposed to be called. Inotify marks should either have been
+ * removed from the idr when the watch was removed or in the
+ * fsnotify_destroy_mark_by_group() call when the inotify instance was being
+ * torn down. This is only called if the idr is about to be freed but there
+ * are still marks in it.
+ */
static int idr_callback(int id, void *p, void *data)
{
- BUG();
+ struct fsnotify_mark_entry *entry;
+ struct inotify_inode_mark_entry *ientry;
+ static bool warned = false;
+
+ if (warned)
+ return 0;
+
+ warned = false;
+ entry = p;
+ ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry);
+
+ WARN(1, "inotify closing but id=%d for entry=%p in group=%p still in "
+ "idr. Probably leaking memory\n", id, p, data);
+
+ /*
+ * I'm taking the liberty of assuming that the mark in question is a
+ * valid address and I'm dereferencing it. This might help to figure
+ * out why we got here and the panic is no worse than the original
+ * BUG() that was here.
+ */
+ if (entry)
+ printk(KERN_WARNING "entry->group=%p inode=%p wd=%d\n",
+ entry->group, entry->inode, ientry->wd);
return 0;
}
static void inotify_free_group_priv(struct fsnotify_group *group)
{
/* ideally the idr is empty and we won't hit the BUG in teh callback */
- idr_for_each(&group->inotify_data.idr, idr_callback, NULL);
+ idr_for_each(&group->inotify_data.idr, idr_callback, group);
idr_remove_all(&group->inotify_data.idr);
idr_destroy(&group->inotify_data.idr);
}
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index f30d9bbc2e1..dcd2040d330 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -47,9 +47,6 @@
static struct vfsmount *inotify_mnt __read_mostly;
-/* this just sits here and wastes global memory. used to just pad userspace messages with zeros */
-static struct inotify_event nul_inotify_event;
-
/* these are configurable via /proc/sys/fs/inotify/ */
static int inotify_max_user_instances __read_mostly;
static int inotify_max_queued_events __read_mostly;
@@ -157,7 +154,8 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group,
event = fsnotify_peek_notify_event(group);
- event_size += roundup(event->name_len, event_size);
+ if (event->name_len)
+ event_size += roundup(event->name_len + 1, event_size);
if (event_size > count)
return ERR_PTR(-EINVAL);
@@ -183,7 +181,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
struct fsnotify_event_private_data *fsn_priv;
struct inotify_event_private_data *priv;
size_t event_size = sizeof(struct inotify_event);
- size_t name_len;
+ size_t name_len = 0;
/* we get the inotify watch descriptor from the event private data */
spin_lock(&event->lock);
@@ -199,8 +197,12 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
inotify_free_event_priv(fsn_priv);
}
- /* round up event->name_len so it is a multiple of event_size */
- name_len = roundup(event->name_len, event_size);
+ /*
+ * round up event->name_len so it is a multiple of event_size
+ * plus an extra byte for the terminating '\0'.
+ */
+ if (event->name_len)
+ name_len = roundup(event->name_len + 1, event_size);
inotify_event.len = name_len;
inotify_event.mask = inotify_mask_to_arg(event->mask);
@@ -224,8 +226,8 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
return -EFAULT;
buf += event->name_len;
- /* fill userspace with 0's from nul_inotify_event */
- if (copy_to_user(buf, &nul_inotify_event, len_to_zero))
+ /* fill userspace with 0's */
+ if (clear_user(buf, len_to_zero))
return -EFAULT;
buf += len_to_zero;
event_size += name_len;
@@ -326,8 +328,9 @@ static long inotify_ioctl(struct file *file, unsigned int cmd,
list_for_each_entry(holder, &group->notification_list, event_list) {
event = holder->event;
send_len += sizeof(struct inotify_event);
- send_len += roundup(event->name_len,
- sizeof(struct inotify_event));
+ if (event->name_len)
+ send_len += roundup(event->name_len + 1,
+ sizeof(struct inotify_event));
}
mutex_unlock(&group->notification_mutex);
ret = put_user(send_len, (int __user *) p);
@@ -364,20 +367,53 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns
return error;
}
+/*
+ * Remove the mark from the idr (if present) and drop the reference
+ * on the mark because it was in the idr.
+ */
static void inotify_remove_from_idr(struct fsnotify_group *group,
struct inotify_inode_mark_entry *ientry)
{
struct idr *idr;
+ struct fsnotify_mark_entry *entry;
+ struct inotify_inode_mark_entry *found_ientry;
+ int wd;
spin_lock(&group->inotify_data.idr_lock);
idr = &group->inotify_data.idr;
- idr_remove(idr, ientry->wd);
- spin_unlock(&group->inotify_data.idr_lock);
+ wd = ientry->wd;
+
+ if (wd == -1)
+ goto out;
+
+ entry = idr_find(&group->inotify_data.idr, wd);
+ if (unlikely(!entry))
+ goto out;
+
+ found_ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry);
+ if (unlikely(found_ientry != ientry)) {
+ /* We found an entry in the idr with the right wd, but it's
+ * not the entry we were told to remove. eparis seriously
+ * fucked up somewhere. */
+ WARN_ON(1);
+ ientry->wd = -1;
+ goto out;
+ }
+
+ /* One ref for being in the idr, one ref held by the caller */
+ BUG_ON(atomic_read(&entry->refcnt) < 2);
+
+ idr_remove(idr, wd);
ientry->wd = -1;
+
+ /* removed from the idr, drop that ref */
+ fsnotify_put_mark(entry);
+out:
+ spin_unlock(&group->inotify_data.idr_lock);
}
+
/*
- * Send IN_IGNORED for this wd, remove this wd from the idr, and drop the
- * internal reference help on the mark because it is in the idr.
+ * Send IN_IGNORED for this wd, remove this wd from the idr.
*/
void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry,
struct fsnotify_group *group)
@@ -386,6 +422,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry,
struct fsnotify_event *ignored_event;
struct inotify_event_private_data *event_priv;
struct fsnotify_event_private_data *fsn_event_priv;
+ int ret;
ignored_event = fsnotify_create_event(NULL, FS_IN_IGNORED, NULL,
FSNOTIFY_EVENT_NONE, NULL, 0,
@@ -404,10 +441,8 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry,
fsn_event_priv->group = group;
event_priv->wd = ientry->wd;
- fsnotify_add_notify_event(group, ignored_event, fsn_event_priv);
-
- /* did the private data get added? */
- if (list_empty(&fsn_event_priv->event_list))
+ ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv);
+ if (ret)
inotify_free_event_priv(fsn_event_priv);
skip_send_ignore:
@@ -418,9 +453,6 @@ skip_send_ignore:
/* remove this entry from the idr */
inotify_remove_from_idr(group, ientry);
- /* removed from idr, drop that reference */
- fsnotify_put_mark(entry);
-
atomic_dec(&group->inotify_data.user->inotify_watches);
}
@@ -432,80 +464,29 @@ static void inotify_free_mark(struct fsnotify_mark_entry *entry)
kmem_cache_free(inotify_inode_mark_cachep, ientry);
}
-static int inotify_update_watch(struct fsnotify_group *group, struct inode *inode, u32 arg)
+static int inotify_update_existing_watch(struct fsnotify_group *group,
+ struct inode *inode,
+ u32 arg)
{
- struct fsnotify_mark_entry *entry = NULL;
+ struct fsnotify_mark_entry *entry;
struct inotify_inode_mark_entry *ientry;
- struct inotify_inode_mark_entry *tmp_ientry;
- int ret = 0;
- int add = (arg & IN_MASK_ADD);
- __u32 mask;
__u32 old_mask, new_mask;
+ __u32 mask;
+ int add = (arg & IN_MASK_ADD);
+ int ret;
/* don't allow invalid bits: we don't want flags set */
mask = inotify_arg_to_mask(arg);
if (unlikely(!mask))
return -EINVAL;
- tmp_ientry = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL);
- if (unlikely(!tmp_ientry))
- return -ENOMEM;
- /* we set the mask at the end after attaching it */
- fsnotify_init_mark(&tmp_ientry->fsn_entry, inotify_free_mark);
- tmp_ientry->wd = -1;
-
-find_entry:
spin_lock(&inode->i_lock);
entry = fsnotify_find_mark_entry(group, inode);
spin_unlock(&inode->i_lock);
- if (entry) {
- ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry);
- } else {
- ret = -ENOSPC;
- if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches)
- goto out_err;
-retry:
- ret = -ENOMEM;
- if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL)))
- goto out_err;
-
- spin_lock(&group->inotify_data.idr_lock);
- ret = idr_get_new_above(&group->inotify_data.idr, &tmp_ientry->fsn_entry,
- group->inotify_data.last_wd,
- &tmp_ientry->wd);
- spin_unlock(&group->inotify_data.idr_lock);
- if (ret) {
- if (ret == -EAGAIN)
- goto retry;
- goto out_err;
- }
+ if (!entry)
+ return -ENOENT;
- ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode);
- if (ret) {
- inotify_remove_from_idr(group, tmp_ientry);
- if (ret == -EEXIST)
- goto find_entry;
- goto out_err;
- }
-
- /* tmp_ientry has been added to the inode, so we are all set up.
- * now we just need to make sure tmp_ientry doesn't get freed and
- * we need to set up entry and ientry so the generic code can
- * do its thing. */
- ientry = tmp_ientry;
- entry = &ientry->fsn_entry;
- tmp_ientry = NULL;
-
- atomic_inc(&group->inotify_data.user->inotify_watches);
-
- /* update the idr hint */
- group->inotify_data.last_wd = ientry->wd;
-
- /* we put the mark on the idr, take a reference */
- fsnotify_get_mark(entry);
- }
-
- ret = ientry->wd;
+ ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry);
spin_lock(&entry->lock);
@@ -537,18 +518,107 @@ retry:
fsnotify_recalc_group_mask(group);
}
- /* this either matches fsnotify_find_mark_entry, or init_mark_entry
- * depending on which path we took... */
+ /* return the wd */
+ ret = ientry->wd;
+
+ /* match the get from fsnotify_find_mark_entry() */
fsnotify_put_mark(entry);
+ return ret;
+}
+
+static int inotify_new_watch(struct fsnotify_group *group,
+ struct inode *inode,
+ u32 arg)
+{
+ struct inotify_inode_mark_entry *tmp_ientry;
+ __u32 mask;
+ int ret;
+
+ /* don't allow invalid bits: we don't want flags set */
+ mask = inotify_arg_to_mask(arg);
+ if (unlikely(!mask))
+ return -EINVAL;
+
+ tmp_ientry = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL);
+ if (unlikely(!tmp_ientry))
+ return -ENOMEM;
+
+ fsnotify_init_mark(&tmp_ientry->fsn_entry, inotify_free_mark);
+ tmp_ientry->fsn_entry.mask = mask;
+ tmp_ientry->wd = -1;
+
+ ret = -ENOSPC;
+ if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches)
+ goto out_err;
+retry:
+ ret = -ENOMEM;
+ if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL)))
+ goto out_err;
+
+ spin_lock(&group->inotify_data.idr_lock);
+ ret = idr_get_new_above(&group->inotify_data.idr, &tmp_ientry->fsn_entry,
+ group->inotify_data.last_wd,
+ &tmp_ientry->wd);
+ spin_unlock(&group->inotify_data.idr_lock);
+ if (ret) {
+ /* idr was out of memory allocate and try again */
+ if (ret == -EAGAIN)
+ goto retry;
+ goto out_err;
+ }
+
+ /* we put the mark on the idr, take a reference */
+ fsnotify_get_mark(&tmp_ientry->fsn_entry);
+
+ /* we are on the idr, now get on the inode */
+ ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode);
+ if (ret) {
+ /* we failed to get on the inode, get off the idr */
+ inotify_remove_from_idr(group, tmp_ientry);
+ goto out_err;
+ }
+
+ /* update the idr hint, who cares about races, it's just a hint */
+ group->inotify_data.last_wd = tmp_ientry->wd;
+
+ /* increment the number of watches the user has */
+ atomic_inc(&group->inotify_data.user->inotify_watches);
+
+ /* return the watch descriptor for this new entry */
+ ret = tmp_ientry->wd;
+
+ /* match the ref from fsnotify_init_markentry() */
+ fsnotify_put_mark(&tmp_ientry->fsn_entry);
+
+ /* if this mark added a new event update the group mask */
+ if (mask & ~group->mask)
+ fsnotify_recalc_group_mask(group);
+
out_err:
- /* could be an error, could be that we found an existing mark */
- if (tmp_ientry) {
- /* on the idr but didn't make it on the inode */
- if (tmp_ientry->wd != -1)
- inotify_remove_from_idr(group, tmp_ientry);
+ if (ret < 0)
kmem_cache_free(inotify_inode_mark_cachep, tmp_ientry);
- }
+
+ return ret;
+}
+
+static int inotify_update_watch(struct fsnotify_group *group, struct inode *inode, u32 arg)
+{
+ int ret = 0;
+
+retry:
+ /* try to update and existing watch with the new arg */
+ ret = inotify_update_existing_watch(group, inode, arg);
+ /* no mark present, try to add a new one */
+ if (ret == -ENOENT)
+ ret = inotify_new_watch(group, inode, arg);
+ /*
+ * inotify_new_watch could race with another thread which did an
+ * inotify_new_watch between the update_existing and the add watch
+ * here, go back and try to update an existing mark again.
+ */
+ if (ret == -EEXIST)
+ goto retry;
return ret;
}
@@ -568,7 +638,7 @@ static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsign
spin_lock_init(&group->inotify_data.idr_lock);
idr_init(&group->inotify_data.idr);
- group->inotify_data.last_wd = 0;
+ group->inotify_data.last_wd = 1;
group->inotify_data.user = user;
group->inotify_data.fa = NULL;
diff --git a/fs/notify/notification.c b/fs/notify/notification.c
index 521368574e9..3816d5750dd 100644
--- a/fs/notify/notification.c
+++ b/fs/notify/notification.c
@@ -153,6 +153,10 @@ static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new
return true;
break;
case (FSNOTIFY_EVENT_NONE):
+ if (old->mask & FS_Q_OVERFLOW)
+ return true;
+ else if (old->mask & FS_IN_IGNORED)
+ return false;
return false;
};
}
@@ -171,9 +175,7 @@ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_even
struct list_head *list = &group->notification_list;
struct fsnotify_event_holder *last_holder;
struct fsnotify_event *last_event;
-
- /* easy to tell if priv was attached to the event */
- INIT_LIST_HEAD(&priv->event_list);
+ int ret = 0;
/*
* There is one fsnotify_event_holder embedded inside each fsnotify_event.
@@ -194,6 +196,7 @@ alloc_holder:
if (group->q_len >= group->max_events) {
event = &q_overflow_event;
+ ret = -EOVERFLOW;
/* sorry, no private data on the overflow event */
priv = NULL;
}
@@ -235,7 +238,7 @@ alloc_holder:
mutex_unlock(&group->notification_mutex);
wake_up(&group->notification_waitq);
- return 0;
+ return ret;
}
/*
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index 9edcde4974a..ab513ddaeff 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -1914,7 +1914,8 @@ static void ocfs2_adjust_adjacent_records(struct ocfs2_extent_rec *left_rec,
* immediately to their right.
*/
left_clusters = le32_to_cpu(right_child_el->l_recs[0].e_cpos);
- if (ocfs2_is_empty_extent(&right_child_el->l_recs[0])) {
+ if (!ocfs2_rec_clusters(right_child_el, &right_child_el->l_recs[0])) {
+ BUG_ON(right_child_el->l_tree_depth);
BUG_ON(le16_to_cpu(right_child_el->l_next_free_rec) <= 1);
left_clusters = le32_to_cpu(right_child_el->l_recs[1].e_cpos);
}
@@ -2476,15 +2477,37 @@ out_ret_path:
return ret;
}
-static void ocfs2_update_edge_lengths(struct inode *inode, handle_t *handle,
- struct ocfs2_path *path)
+static int ocfs2_update_edge_lengths(struct inode *inode, handle_t *handle,
+ int subtree_index, struct ocfs2_path *path)
{
- int i, idx;
+ int i, idx, ret;
struct ocfs2_extent_rec *rec;
struct ocfs2_extent_list *el;
struct ocfs2_extent_block *eb;
u32 range;
+ /*
+ * In normal tree rotation process, we will never touch the
+ * tree branch above subtree_index and ocfs2_extend_rotate_transaction
+ * doesn't reserve the credits for them either.
+ *
+ * But we do have a special case here which will update the rightmost
+ * records for all the bh in the path.
+ * So we have to allocate extra credits and access them.
+ */
+ ret = ocfs2_extend_trans(handle,
+ handle->h_buffer_credits + subtree_index);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ ret = ocfs2_journal_access_path(inode, handle, path);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
/* Path should always be rightmost. */
eb = (struct ocfs2_extent_block *)path_leaf_bh(path)->b_data;
BUG_ON(eb->h_next_leaf_blk != 0ULL);
@@ -2505,6 +2528,8 @@ static void ocfs2_update_edge_lengths(struct inode *inode, handle_t *handle,
ocfs2_journal_dirty(handle, path->p_node[i].bh);
}
+out:
+ return ret;
}
static void ocfs2_unlink_path(struct inode *inode, handle_t *handle,
@@ -2717,7 +2742,12 @@ static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle,
if (del_right_subtree) {
ocfs2_unlink_subtree(inode, handle, left_path, right_path,
subtree_index, dealloc);
- ocfs2_update_edge_lengths(inode, handle, left_path);
+ ret = ocfs2_update_edge_lengths(inode, handle, subtree_index,
+ left_path);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
eb = (struct ocfs2_extent_block *)path_leaf_bh(left_path)->b_data;
ocfs2_et_set_last_eb_blk(et, le64_to_cpu(eb->h_blkno));
@@ -3034,7 +3064,12 @@ static int ocfs2_remove_rightmost_path(struct inode *inode, handle_t *handle,
ocfs2_unlink_subtree(inode, handle, left_path, path,
subtree_index, dealloc);
- ocfs2_update_edge_lengths(inode, handle, left_path);
+ ret = ocfs2_update_edge_lengths(inode, handle, subtree_index,
+ left_path);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
eb = (struct ocfs2_extent_block *)path_leaf_bh(left_path)->b_data;
ocfs2_et_set_last_eb_blk(et, le64_to_cpu(eb->h_blkno));
@@ -6816,7 +6851,7 @@ static int ocfs2_do_truncate(struct ocfs2_super *osb,
}
status = 0;
bail:
-
+ brelse(last_eb_bh);
mlog_exit(status);
return status;
}
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index b2c52b3a148..8a1e61545f4 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -193,6 +193,7 @@ static int ocfs2_get_block(struct inode *inode, sector_t iblock,
(unsigned long long)OCFS2_I(inode)->ip_blkno);
mlog(ML_ERROR, "Size %llu, clusters %u\n", (unsigned long long)i_size_read(inode), OCFS2_I(inode)->ip_clusters);
dump_stack();
+ goto bail;
}
past_eof = ocfs2_blocks_for_bytes(inode->i_sb, i_size_read(inode));
@@ -894,18 +895,17 @@ struct ocfs2_write_cluster_desc {
*/
unsigned c_new;
unsigned c_unwritten;
+ unsigned c_needs_zero;
};
-static inline int ocfs2_should_zero_cluster(struct ocfs2_write_cluster_desc *d)
-{
- return d->c_new || d->c_unwritten;
-}
-
struct ocfs2_write_ctxt {
/* Logical cluster position / len of write */
u32 w_cpos;
u32 w_clen;
+ /* First cluster allocated in a nonsparse extend */
+ u32 w_first_new_cpos;
+
struct ocfs2_write_cluster_desc w_desc[OCFS2_MAX_CLUSTERS_PER_PAGE];
/*
@@ -983,6 +983,7 @@ static int ocfs2_alloc_write_ctxt(struct ocfs2_write_ctxt **wcp,
return -ENOMEM;
wc->w_cpos = pos >> osb->s_clustersize_bits;
+ wc->w_first_new_cpos = UINT_MAX;
cend = (pos + len - 1) >> osb->s_clustersize_bits;
wc->w_clen = cend - wc->w_cpos + 1;
get_bh(di_bh);
@@ -1217,20 +1218,18 @@ out:
*/
static int ocfs2_write_cluster(struct address_space *mapping,
u32 phys, unsigned int unwritten,
+ unsigned int should_zero,
struct ocfs2_alloc_context *data_ac,
struct ocfs2_alloc_context *meta_ac,
struct ocfs2_write_ctxt *wc, u32 cpos,
loff_t user_pos, unsigned user_len)
{
- int ret, i, new, should_zero = 0;
+ int ret, i, new;
u64 v_blkno, p_blkno;
struct inode *inode = mapping->host;
struct ocfs2_extent_tree et;
new = phys == 0 ? 1 : 0;
- if (new || unwritten)
- should_zero = 1;
-
if (new) {
u32 tmp_pos;
@@ -1301,7 +1300,7 @@ static int ocfs2_write_cluster(struct address_space *mapping,
if (tmpret) {
mlog_errno(tmpret);
if (ret == 0)
- tmpret = ret;
+ ret = tmpret;
}
}
@@ -1341,7 +1340,9 @@ static int ocfs2_write_cluster_by_desc(struct address_space *mapping,
local_len = osb->s_clustersize - cluster_off;
ret = ocfs2_write_cluster(mapping, desc->c_phys,
- desc->c_unwritten, data_ac, meta_ac,
+ desc->c_unwritten,
+ desc->c_needs_zero,
+ data_ac, meta_ac,
wc, desc->c_cpos, pos, local_len);
if (ret) {
mlog_errno(ret);
@@ -1391,14 +1392,14 @@ static void ocfs2_set_target_boundaries(struct ocfs2_super *osb,
* newly allocated cluster.
*/
desc = &wc->w_desc[0];
- if (ocfs2_should_zero_cluster(desc))
+ if (desc->c_needs_zero)
ocfs2_figure_cluster_boundaries(osb,
desc->c_cpos,
&wc->w_target_from,
NULL);
desc = &wc->w_desc[wc->w_clen - 1];
- if (ocfs2_should_zero_cluster(desc))
+ if (desc->c_needs_zero)
ocfs2_figure_cluster_boundaries(osb,
desc->c_cpos,
NULL,
@@ -1466,13 +1467,28 @@ static int ocfs2_populate_write_desc(struct inode *inode,
phys++;
}
+ /*
+ * If w_first_new_cpos is < UINT_MAX, we have a non-sparse
+ * file that got extended. w_first_new_cpos tells us
+ * where the newly allocated clusters are so we can
+ * zero them.
+ */
+ if (desc->c_cpos >= wc->w_first_new_cpos) {
+ BUG_ON(phys == 0);
+ desc->c_needs_zero = 1;
+ }
+
desc->c_phys = phys;
if (phys == 0) {
desc->c_new = 1;
+ desc->c_needs_zero = 1;
*clusters_to_alloc = *clusters_to_alloc + 1;
}
- if (ext_flags & OCFS2_EXT_UNWRITTEN)
+
+ if (ext_flags & OCFS2_EXT_UNWRITTEN) {
desc->c_unwritten = 1;
+ desc->c_needs_zero = 1;
+ }
num_clusters--;
}
@@ -1632,10 +1648,13 @@ static int ocfs2_expand_nonsparse_inode(struct inode *inode, loff_t pos,
if (newsize <= i_size_read(inode))
return 0;
- ret = ocfs2_extend_no_holes(inode, newsize, newsize - len);
+ ret = ocfs2_extend_no_holes(inode, newsize, pos);
if (ret)
mlog_errno(ret);
+ wc->w_first_new_cpos =
+ ocfs2_clusters_for_bytes(inode->i_sb, i_size_read(inode));
+
return ret;
}
@@ -1644,7 +1663,7 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
struct page **pagep, void **fsdata,
struct buffer_head *di_bh, struct page *mmap_page)
{
- int ret, credits = OCFS2_INODE_UPDATE_CREDITS;
+ int ret, cluster_of_pages, credits = OCFS2_INODE_UPDATE_CREDITS;
unsigned int clusters_to_alloc, extents_to_split;
struct ocfs2_write_ctxt *wc;
struct inode *inode = mapping->host;
@@ -1722,8 +1741,19 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
}
- ocfs2_set_target_boundaries(osb, wc, pos, len,
- clusters_to_alloc + extents_to_split);
+ /*
+ * We have to zero sparse allocated clusters, unwritten extent clusters,
+ * and non-sparse clusters we just extended. For non-sparse writes,
+ * we know zeros will only be needed in the first and/or last cluster.
+ */
+ if (clusters_to_alloc || extents_to_split ||
+ (wc->w_clen && (wc->w_desc[0].c_needs_zero ||
+ wc->w_desc[wc->w_clen - 1].c_needs_zero)))
+ cluster_of_pages = 1;
+ else
+ cluster_of_pages = 0;
+
+ ocfs2_set_target_boundaries(osb, wc, pos, len, cluster_of_pages);
handle = ocfs2_start_trans(osb, credits);
if (IS_ERR(handle)) {
@@ -1756,8 +1786,7 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
* extent.
*/
ret = ocfs2_grab_pages_for_write(mapping, wc, wc->w_cpos, pos,
- clusters_to_alloc + extents_to_split,
- mmap_page);
+ cluster_of_pages, mmap_page);
if (ret) {
mlog_errno(ret);
goto out_quota;
diff --git a/fs/ocfs2/dcache.c b/fs/ocfs2/dcache.c
index b574431a031..b4957c7d9fe 100644
--- a/fs/ocfs2/dcache.c
+++ b/fs/ocfs2/dcache.c
@@ -85,6 +85,17 @@ static int ocfs2_dentry_revalidate(struct dentry *dentry,
goto bail;
}
+ /*
+ * If the last lookup failed to create dentry lock, let us
+ * redo it.
+ */
+ if (!dentry->d_fsdata) {
+ mlog(0, "Inode %llu doesn't have dentry lock, "
+ "returning false\n",
+ (unsigned long long)OCFS2_I(inode)->ip_blkno);
+ goto bail;
+ }
+
ret = 1;
bail:
@@ -310,22 +321,19 @@ out_attach:
return ret;
}
-static DEFINE_SPINLOCK(dentry_list_lock);
+DEFINE_SPINLOCK(dentry_list_lock);
/* We limit the number of dentry locks to drop in one go. We have
* this limit so that we don't starve other users of ocfs2_wq. */
#define DL_INODE_DROP_COUNT 64
/* Drop inode references from dentry locks */
-void ocfs2_drop_dl_inodes(struct work_struct *work)
+static void __ocfs2_drop_dl_inodes(struct ocfs2_super *osb, int drop_count)
{
- struct ocfs2_super *osb = container_of(work, struct ocfs2_super,
- dentry_lock_work);
struct ocfs2_dentry_lock *dl;
- int drop_count = DL_INODE_DROP_COUNT;
spin_lock(&dentry_list_lock);
- while (osb->dentry_lock_list && drop_count--) {
+ while (osb->dentry_lock_list && (drop_count < 0 || drop_count--)) {
dl = osb->dentry_lock_list;
osb->dentry_lock_list = dl->dl_next;
spin_unlock(&dentry_list_lock);
@@ -333,11 +341,32 @@ void ocfs2_drop_dl_inodes(struct work_struct *work)
kfree(dl);
spin_lock(&dentry_list_lock);
}
- if (osb->dentry_lock_list)
+ spin_unlock(&dentry_list_lock);
+}
+
+void ocfs2_drop_dl_inodes(struct work_struct *work)
+{
+ struct ocfs2_super *osb = container_of(work, struct ocfs2_super,
+ dentry_lock_work);
+
+ __ocfs2_drop_dl_inodes(osb, DL_INODE_DROP_COUNT);
+ /*
+ * Don't queue dropping if umount is in progress. We flush the
+ * list in ocfs2_dismount_volume
+ */
+ spin_lock(&dentry_list_lock);
+ if (osb->dentry_lock_list &&
+ !ocfs2_test_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED))
queue_work(ocfs2_wq, &osb->dentry_lock_work);
spin_unlock(&dentry_list_lock);
}
+/* Flush the whole work queue */
+void ocfs2_drop_all_dl_inodes(struct ocfs2_super *osb)
+{
+ __ocfs2_drop_dl_inodes(osb, -1);
+}
+
/*
* ocfs2_dentry_iput() and friends.
*
@@ -368,7 +397,8 @@ static void ocfs2_drop_dentry_lock(struct ocfs2_super *osb,
/* We leave dropping of inode reference to ocfs2_wq as that can
* possibly lead to inode deletion which gets tricky */
spin_lock(&dentry_list_lock);
- if (!osb->dentry_lock_list)
+ if (!osb->dentry_lock_list &&
+ !ocfs2_test_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED))
queue_work(ocfs2_wq, &osb->dentry_lock_work);
dl->dl_next = osb->dentry_lock_list;
osb->dentry_lock_list = dl;
diff --git a/fs/ocfs2/dcache.h b/fs/ocfs2/dcache.h
index faa12e75f98..f5dd1789acf 100644
--- a/fs/ocfs2/dcache.h
+++ b/fs/ocfs2/dcache.h
@@ -49,10 +49,13 @@ struct ocfs2_dentry_lock {
int ocfs2_dentry_attach_lock(struct dentry *dentry, struct inode *inode,
u64 parent_blkno);
+extern spinlock_t dentry_list_lock;
+
void ocfs2_dentry_lock_put(struct ocfs2_super *osb,
struct ocfs2_dentry_lock *dl);
void ocfs2_drop_dl_inodes(struct work_struct *work);
+void ocfs2_drop_all_dl_inodes(struct ocfs2_super *osb);
struct dentry *ocfs2_find_local_alias(struct inode *inode, u64 parent_blkno,
int skip_unhashed);
diff --git a/fs/ocfs2/dlm/dlmast.c b/fs/ocfs2/dlm/dlmast.c
index d07ddbe4b28..81eff8e5832 100644
--- a/fs/ocfs2/dlm/dlmast.c
+++ b/fs/ocfs2/dlm/dlmast.c
@@ -103,7 +103,6 @@ static void __dlm_queue_ast(struct dlm_ctxt *dlm, struct dlm_lock *lock)
lock->ast_pending, lock->ml.type);
BUG();
}
- BUG_ON(!list_empty(&lock->ast_list));
if (lock->ast_pending)
mlog(0, "lock has an ast getting flushed right now\n");
diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c
index bcb9260c373..43e6e328056 100644
--- a/fs/ocfs2/dlm/dlmrecovery.c
+++ b/fs/ocfs2/dlm/dlmrecovery.c
@@ -1118,7 +1118,7 @@ static int dlm_send_mig_lockres_msg(struct dlm_ctxt *dlm,
mlog(0, "%s:%.*s: sending mig lockres (%s) to %u\n",
dlm->name, res->lockname.len, res->lockname.name,
- orig_flags & DLM_MRES_MIGRATION ? "migrate" : "recovery",
+ orig_flags & DLM_MRES_MIGRATION ? "migration" : "recovery",
send_to);
/* send it */
diff --git a/fs/ocfs2/dlm/dlmunlock.c b/fs/ocfs2/dlm/dlmunlock.c
index fcf879ed693..756f5b0998e 100644
--- a/fs/ocfs2/dlm/dlmunlock.c
+++ b/fs/ocfs2/dlm/dlmunlock.c
@@ -122,7 +122,7 @@ static enum dlm_status dlmunlock_common(struct dlm_ctxt *dlm,
* that still has AST's pending... */
in_use = !list_empty(&lock->ast_list);
spin_unlock(&dlm->ast_lock);
- if (in_use) {
+ if (in_use && !(flags & LKM_CANCEL)) {
mlog(ML_ERROR, "lockres %.*s: Someone is calling dlmunlock "
"while waiting for an ast!", res->lockname.len,
res->lockname.name);
@@ -131,7 +131,7 @@ static enum dlm_status dlmunlock_common(struct dlm_ctxt *dlm,
spin_lock(&res->spinlock);
if (res->state & DLM_LOCK_RES_IN_PROGRESS) {
- if (master_node) {
+ if (master_node && !(flags & LKM_CANCEL)) {
mlog(ML_ERROR, "lockres in progress!\n");
spin_unlock(&res->spinlock);
return DLM_FORWARD;
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 62442e413a0..aa501d3f93f 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1851,6 +1851,7 @@ relock:
if (ret)
goto out_dio;
+ count = ocount;
ret = generic_write_checks(file, ppos, &count,
S_ISBLK(inode->i_mode));
if (ret)
@@ -1918,8 +1919,10 @@ out_sems:
mutex_unlock(&inode->i_mutex);
+ if (written)
+ ret = written;
mlog_exit(ret);
- return written ? written : ret;
+ return ret;
}
static int ocfs2_splice_to_file(struct pipe_inode_info *pipe,
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index f033760ecbe..c48b93ac6b6 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -1954,10 +1954,16 @@ void ocfs2_orphan_scan_init(struct ocfs2_super *osb)
os->os_osb = osb;
os->os_count = 0;
os->os_seqno = 0;
- os->os_scantime = CURRENT_TIME;
mutex_init(&os->os_lock);
INIT_DELAYED_WORK(&os->os_orphan_scan_work, ocfs2_orphan_scan_work);
+}
+void ocfs2_orphan_scan_start(struct ocfs2_super *osb)
+{
+ struct ocfs2_orphan_scan *os;
+
+ os = &osb->osb_orphan_scan;
+ os->os_scantime = CURRENT_TIME;
if (ocfs2_is_hard_readonly(osb) || ocfs2_mount_local(osb))
atomic_set(&os->os_state, ORPHAN_SCAN_INACTIVE);
else {
diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h
index 5432c7f79cc..2c3222aec62 100644
--- a/fs/ocfs2/journal.h
+++ b/fs/ocfs2/journal.h
@@ -145,6 +145,7 @@ static inline void ocfs2_inode_set_new(struct ocfs2_super *osb,
/* Exported only for the journal struct init code in super.c. Do not call. */
void ocfs2_orphan_scan_init(struct ocfs2_super *osb);
+void ocfs2_orphan_scan_start(struct ocfs2_super *osb);
void ocfs2_orphan_scan_stop(struct ocfs2_super *osb);
void ocfs2_orphan_scan_exit(struct ocfs2_super *osb);
@@ -329,20 +330,27 @@ int ocfs2_journal_dirty(handle_t *handle,
/* extended attribute block update */
#define OCFS2_XATTR_BLOCK_UPDATE_CREDITS 1
+/* Update of a single quota block */
+#define OCFS2_QUOTA_BLOCK_UPDATE_CREDITS 1
+
/* global quotafile inode update, data block */
-#define OCFS2_QINFO_WRITE_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 1)
+#define OCFS2_QINFO_WRITE_CREDITS (OCFS2_INODE_UPDATE_CREDITS + \
+ OCFS2_QUOTA_BLOCK_UPDATE_CREDITS)
+#define OCFS2_LOCAL_QINFO_WRITE_CREDITS OCFS2_QUOTA_BLOCK_UPDATE_CREDITS
/*
* The two writes below can accidentally see global info dirty due
* to set_info() quotactl so make them prepared for the writes.
*/
/* quota data block, global info */
/* Write to local quota file */
-#define OCFS2_QWRITE_CREDITS (OCFS2_QINFO_WRITE_CREDITS + 1)
+#define OCFS2_QWRITE_CREDITS (OCFS2_QINFO_WRITE_CREDITS + \
+ OCFS2_QUOTA_BLOCK_UPDATE_CREDITS)
/* global quota data block, local quota data block, global quota inode,
* global quota info */
-#define OCFS2_QSYNC_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 3)
+#define OCFS2_QSYNC_CREDITS (OCFS2_QINFO_WRITE_CREDITS + \
+ 2 * OCFS2_QUOTA_BLOCK_UPDATE_CREDITS)
static inline int ocfs2_quota_trans_credits(struct super_block *sb)
{
@@ -355,11 +363,6 @@ static inline int ocfs2_quota_trans_credits(struct super_block *sb)
return credits;
}
-/* Number of credits needed for removing quota structure from file */
-int ocfs2_calc_qdel_credits(struct super_block *sb, int type);
-/* Number of credits needed for initialization of new quota structure */
-int ocfs2_calc_qinit_credits(struct super_block *sb, int type);
-
/* group extend. inode update and last group update. */
#define OCFS2_GROUP_EXTEND_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 1)
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index c9345ebb849..39e1d5a3950 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -224,10 +224,12 @@ enum ocfs2_mount_options
OCFS2_MOUNT_GRPQUOTA = 1 << 10, /* We support group quotas */
};
-#define OCFS2_OSB_SOFT_RO 0x0001
-#define OCFS2_OSB_HARD_RO 0x0002
-#define OCFS2_OSB_ERROR_FS 0x0004
-#define OCFS2_DEFAULT_ATIME_QUANTUM 60
+#define OCFS2_OSB_SOFT_RO 0x0001
+#define OCFS2_OSB_HARD_RO 0x0002
+#define OCFS2_OSB_ERROR_FS 0x0004
+#define OCFS2_OSB_DROP_DENTRY_LOCK_IMMED 0x0008
+
+#define OCFS2_DEFAULT_ATIME_QUANTUM 60
struct ocfs2_journal;
struct ocfs2_slot_info;
@@ -490,6 +492,18 @@ static inline void ocfs2_set_osb_flag(struct ocfs2_super *osb,
spin_unlock(&osb->osb_lock);
}
+
+static inline unsigned long ocfs2_test_osb_flag(struct ocfs2_super *osb,
+ unsigned long flag)
+{
+ unsigned long ret;
+
+ spin_lock(&osb->osb_lock);
+ ret = osb->osb_flags & flag;
+ spin_unlock(&osb->osb_lock);
+ return ret;
+}
+
static inline void ocfs2_set_ro_flag(struct ocfs2_super *osb,
int hard)
{
diff --git a/fs/ocfs2/ocfs2_lockid.h b/fs/ocfs2/ocfs2_lockid.h
index fcdba091af3..c212cf5a2bd 100644
--- a/fs/ocfs2/ocfs2_lockid.h
+++ b/fs/ocfs2/ocfs2_lockid.h
@@ -108,6 +108,7 @@ static char *ocfs2_lock_type_strings[] = {
[OCFS2_LOCK_TYPE_OPEN] = "Open",
[OCFS2_LOCK_TYPE_FLOCK] = "Flock",
[OCFS2_LOCK_TYPE_QINFO] = "Quota",
+ [OCFS2_LOCK_TYPE_NFS_SYNC] = "NFSSync",
[OCFS2_LOCK_TYPE_ORPHAN_SCAN] = "OrphanScan",
};
diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h
index 7365e2e0870..3fb96fcd4c8 100644
--- a/fs/ocfs2/quota.h
+++ b/fs/ocfs2/quota.h
@@ -50,7 +50,6 @@ struct ocfs2_mem_dqinfo {
unsigned int dqi_chunks; /* Number of chunks in local quota file */
unsigned int dqi_blocks; /* Number of blocks allocated for local quota file */
unsigned int dqi_syncms; /* How often should we sync with other nodes */
- unsigned int dqi_syncjiff; /* Precomputed dqi_syncms in jiffies */
struct list_head dqi_chunk; /* List of chunks */
struct inode *dqi_gqinode; /* Global quota file inode */
struct ocfs2_lock_res dqi_gqlock; /* Lock protecting quota information structure */
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
index edfa60cd155..44f2a5e1d04 100644
--- a/fs/ocfs2/quota_global.c
+++ b/fs/ocfs2/quota_global.c
@@ -23,6 +23,7 @@
#include "sysfile.h"
#include "dlmglue.h"
#include "uptodate.h"
+#include "super.h"
#include "quota.h"
static struct workqueue_struct *ocfs2_quota_wq = NULL;
@@ -69,6 +70,7 @@ static void ocfs2_global_mem2diskdqb(void *dp, struct dquot *dquot)
d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
d->dqb_btime = cpu_to_le64(m->dqb_btime);
d->dqb_itime = cpu_to_le64(m->dqb_itime);
+ d->dqb_pad1 = d->dqb_pad2 = 0;
}
static int ocfs2_global_is_id(void *dp, struct dquot *dquot)
@@ -113,6 +115,15 @@ int ocfs2_read_quota_block(struct inode *inode, u64 v_block,
int rc = 0;
struct buffer_head *tmp = *bh;
+ if (i_size_read(inode) >> inode->i_sb->s_blocksize_bits <= v_block) {
+ ocfs2_error(inode->i_sb,
+ "Quota file %llu is probably corrupted! Requested "
+ "to read block %Lu but file has size only %Lu\n",
+ (unsigned long long)OCFS2_I(inode)->ip_blkno,
+ (unsigned long long)v_block,
+ (unsigned long long)i_size_read(inode));
+ return -EIO;
+ }
rc = ocfs2_read_virt_blocks(inode, v_block, 1, &tmp, 0,
ocfs2_validate_quota_block);
if (rc)
@@ -211,14 +222,13 @@ ssize_t ocfs2_quota_write(struct super_block *sb, int type,
mutex_lock_nested(&gqinode->i_mutex, I_MUTEX_QUOTA);
if (gqinode->i_size < off + len) {
- down_write(&OCFS2_I(gqinode)->ip_alloc_sem);
- err = ocfs2_extend_no_holes(gqinode, off + len, off);
- up_write(&OCFS2_I(gqinode)->ip_alloc_sem);
- if (err < 0)
- goto out;
+ loff_t rounded_end =
+ ocfs2_align_bytes_to_blocks(sb, off + len);
+
+ /* Space is already allocated in ocfs2_global_read_dquot() */
err = ocfs2_simple_size_update(gqinode,
oinfo->dqi_gqi_bh,
- off + len);
+ rounded_end);
if (err < 0)
goto out;
new = 1;
@@ -234,7 +244,7 @@ ssize_t ocfs2_quota_write(struct super_block *sb, int type,
}
if (err) {
mlog_errno(err);
- return err;
+ goto out;
}
lock_buffer(bh);
if (new)
@@ -342,7 +352,6 @@ int ocfs2_global_read_info(struct super_block *sb, int type)
info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
oinfo->dqi_syncms = le32_to_cpu(dinfo.dqi_syncms);
- oinfo->dqi_syncjiff = msecs_to_jiffies(oinfo->dqi_syncms);
oinfo->dqi_gi.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
oinfo->dqi_gi.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
oinfo->dqi_gi.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
@@ -352,7 +361,7 @@ int ocfs2_global_read_info(struct super_block *sb, int type)
oinfo->dqi_gi.dqi_qtree_depth = qtree_depth(&oinfo->dqi_gi);
INIT_DELAYED_WORK(&oinfo->dqi_sync_work, qsync_work_fn);
queue_delayed_work(ocfs2_quota_wq, &oinfo->dqi_sync_work,
- oinfo->dqi_syncjiff);
+ msecs_to_jiffies(oinfo->dqi_syncms));
out_err:
mlog_exit(status);
@@ -402,13 +411,36 @@ int ocfs2_global_write_info(struct super_block *sb, int type)
return err;
}
+static int ocfs2_global_qinit_alloc(struct super_block *sb, int type)
+{
+ struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
+
+ /*
+ * We may need to allocate tree blocks and a leaf block but not the
+ * root block
+ */
+ return oinfo->dqi_gi.dqi_qtree_depth;
+}
+
+static int ocfs2_calc_global_qinit_credits(struct super_block *sb, int type)
+{
+ /* We modify all the allocated blocks, tree root, and info block */
+ return (ocfs2_global_qinit_alloc(sb, type) + 2) *
+ OCFS2_QUOTA_BLOCK_UPDATE_CREDITS;
+}
+
/* Read in information from global quota file and acquire a reference to it.
* dquot_acquire() has already started the transaction and locked quota file */
int ocfs2_global_read_dquot(struct dquot *dquot)
{
int err, err2, ex = 0;
- struct ocfs2_mem_dqinfo *info =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ struct super_block *sb = dquot->dq_sb;
+ int type = dquot->dq_type;
+ struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
+ struct ocfs2_super *osb = OCFS2_SB(sb);
+ struct inode *gqinode = info->dqi_gqinode;
+ int need_alloc = ocfs2_global_qinit_alloc(sb, type);
+ handle_t *handle = NULL;
err = ocfs2_qinfo_lock(info, 0);
if (err < 0)
@@ -419,14 +451,33 @@ int ocfs2_global_read_dquot(struct dquot *dquot)
OCFS2_DQUOT(dquot)->dq_use_count++;
OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace;
OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes;
+ ocfs2_qinfo_unlock(info, 0);
+
if (!dquot->dq_off) { /* No real quota entry? */
- /* Upgrade to exclusive lock for allocation */
- ocfs2_qinfo_unlock(info, 0);
- err = ocfs2_qinfo_lock(info, 1);
- if (err < 0)
- goto out_qlock;
ex = 1;
+ /*
+ * Add blocks to quota file before we start a transaction since
+ * locking allocators ranks above a transaction start
+ */
+ WARN_ON(journal_current_handle());
+ down_write(&OCFS2_I(gqinode)->ip_alloc_sem);
+ err = ocfs2_extend_no_holes(gqinode,
+ gqinode->i_size + (need_alloc << sb->s_blocksize_bits),
+ gqinode->i_size);
+ up_write(&OCFS2_I(gqinode)->ip_alloc_sem);
+ if (err < 0)
+ goto out;
}
+
+ handle = ocfs2_start_trans(osb,
+ ocfs2_calc_global_qinit_credits(sb, type));
+ if (IS_ERR(handle)) {
+ err = PTR_ERR(handle);
+ goto out;
+ }
+ err = ocfs2_qinfo_lock(info, ex);
+ if (err < 0)
+ goto out_trans;
err = qtree_write_dquot(&info->dqi_gi, dquot);
if (ex && info_dirty(sb_dqinfo(dquot->dq_sb, dquot->dq_type))) {
err2 = __ocfs2_global_write_info(dquot->dq_sb, dquot->dq_type);
@@ -438,6 +489,9 @@ out_qlock:
ocfs2_qinfo_unlock(info, 1);
else
ocfs2_qinfo_unlock(info, 0);
+out_trans:
+ if (handle)
+ ocfs2_commit_trans(osb, handle);
out:
if (err < 0)
mlog_errno(err);
@@ -607,7 +661,7 @@ static void qsync_work_fn(struct work_struct *work)
dquot_scan_active(sb, ocfs2_sync_dquot_helper, oinfo->dqi_type);
queue_delayed_work(ocfs2_quota_wq, &oinfo->dqi_sync_work,
- oinfo->dqi_syncjiff);
+ msecs_to_jiffies(oinfo->dqi_syncms));
}
/*
@@ -635,20 +689,18 @@ out:
return status;
}
-int ocfs2_calc_qdel_credits(struct super_block *sb, int type)
+static int ocfs2_calc_qdel_credits(struct super_block *sb, int type)
{
- struct ocfs2_mem_dqinfo *oinfo;
- int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
- OCFS2_FEATURE_RO_COMPAT_GRPQUOTA };
-
- if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type]))
- return 0;
-
- oinfo = sb_dqinfo(sb, type)->dqi_priv;
- /* We modify tree, leaf block, global info, local chunk header,
- * global and local inode */
- return oinfo->dqi_gi.dqi_qtree_depth + 2 + 1 +
- 2 * OCFS2_INODE_UPDATE_CREDITS;
+ struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
+ /*
+ * We modify tree, leaf block, global info, local chunk header,
+ * global and local inode; OCFS2_QINFO_WRITE_CREDITS already
+ * accounts for inode update
+ */
+ return (oinfo->dqi_gi.dqi_qtree_depth + 2) *
+ OCFS2_QUOTA_BLOCK_UPDATE_CREDITS +
+ OCFS2_QINFO_WRITE_CREDITS +
+ OCFS2_INODE_UPDATE_CREDITS;
}
static int ocfs2_release_dquot(struct dquot *dquot)
@@ -680,33 +732,10 @@ out:
return status;
}
-int ocfs2_calc_qinit_credits(struct super_block *sb, int type)
-{
- struct ocfs2_mem_dqinfo *oinfo;
- int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
- OCFS2_FEATURE_RO_COMPAT_GRPQUOTA };
- struct ocfs2_dinode *lfe, *gfe;
-
- if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type]))
- return 0;
-
- oinfo = sb_dqinfo(sb, type)->dqi_priv;
- gfe = (struct ocfs2_dinode *)oinfo->dqi_gqi_bh->b_data;
- lfe = (struct ocfs2_dinode *)oinfo->dqi_lqi_bh->b_data;
- /* We can extend local file + global file. In local file we
- * can modify info, chunk header block and dquot block. In
- * global file we can modify info, tree and leaf block */
- return ocfs2_calc_extend_credits(sb, &lfe->id2.i_list, 0) +
- ocfs2_calc_extend_credits(sb, &gfe->id2.i_list, 0) +
- 3 + oinfo->dqi_gi.dqi_qtree_depth + 2;
-}
-
static int ocfs2_acquire_dquot(struct dquot *dquot)
{
- handle_t *handle;
struct ocfs2_mem_dqinfo *oinfo =
sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
- struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
int status = 0;
mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type);
@@ -715,16 +744,7 @@ static int ocfs2_acquire_dquot(struct dquot *dquot)
status = ocfs2_lock_global_qf(oinfo, 1);
if (status < 0)
goto out;
- handle = ocfs2_start_trans(osb,
- ocfs2_calc_qinit_credits(dquot->dq_sb, dquot->dq_type));
- if (IS_ERR(handle)) {
- status = PTR_ERR(handle);
- mlog_errno(status);
- goto out_ilock;
- }
status = dquot_acquire(dquot);
- ocfs2_commit_trans(osb, handle);
-out_ilock:
ocfs2_unlock_global_qf(oinfo, 1);
out:
mlog_exit(status);
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index 5a460fa8255..bdb09cb6e1f 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -20,6 +20,7 @@
#include "sysfile.h"
#include "dlmglue.h"
#include "quota.h"
+#include "uptodate.h"
/* Number of local quota structures per block */
static inline unsigned int ol_quota_entries_per_block(struct super_block *sb)
@@ -100,7 +101,8 @@ static int ocfs2_modify_bh(struct inode *inode, struct buffer_head *bh,
handle_t *handle;
int status;
- handle = ocfs2_start_trans(OCFS2_SB(sb), 1);
+ handle = ocfs2_start_trans(OCFS2_SB(sb),
+ OCFS2_QUOTA_BLOCK_UPDATE_CREDITS);
if (IS_ERR(handle)) {
status = PTR_ERR(handle);
mlog_errno(status);
@@ -610,7 +612,8 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
goto out_bh;
/* Mark quota file as clean if we are recovering quota file of
* some other node. */
- handle = ocfs2_start_trans(osb, 1);
+ handle = ocfs2_start_trans(osb,
+ OCFS2_LOCAL_QINFO_WRITE_CREDITS);
if (IS_ERR(handle)) {
status = PTR_ERR(handle);
mlog_errno(status);
@@ -940,7 +943,7 @@ static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk(
struct ocfs2_local_disk_chunk *dchunk;
int status;
handle_t *handle;
- struct buffer_head *bh = NULL;
+ struct buffer_head *bh = NULL, *dbh = NULL;
u64 p_blkno;
/* We are protected by dqio_sem so no locking needed */
@@ -964,32 +967,35 @@ static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk(
mlog_errno(status);
goto out;
}
+ /* Local quota info and two new blocks we initialize */
+ handle = ocfs2_start_trans(OCFS2_SB(sb),
+ OCFS2_LOCAL_QINFO_WRITE_CREDITS +
+ 2 * OCFS2_QUOTA_BLOCK_UPDATE_CREDITS);
+ if (IS_ERR(handle)) {
+ status = PTR_ERR(handle);
+ mlog_errno(status);
+ goto out;
+ }
+ /* Initialize chunk header */
down_read(&OCFS2_I(lqinode)->ip_alloc_sem);
status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks,
&p_blkno, NULL, NULL);
up_read(&OCFS2_I(lqinode)->ip_alloc_sem);
if (status < 0) {
mlog_errno(status);
- goto out;
+ goto out_trans;
}
bh = sb_getblk(sb, p_blkno);
if (!bh) {
status = -ENOMEM;
mlog_errno(status);
- goto out;
+ goto out_trans;
}
dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data;
-
- handle = ocfs2_start_trans(OCFS2_SB(sb), 2);
- if (IS_ERR(handle)) {
- status = PTR_ERR(handle);
- mlog_errno(status);
- goto out;
- }
-
+ ocfs2_set_new_buffer_uptodate(lqinode, bh);
status = ocfs2_journal_access_dq(handle, lqinode, bh,
- OCFS2_JOURNAL_ACCESS_WRITE);
+ OCFS2_JOURNAL_ACCESS_CREATE);
if (status < 0) {
mlog_errno(status);
goto out_trans;
@@ -999,7 +1005,6 @@ static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk(
memset(dchunk->dqc_bitmap, 0,
sb->s_blocksize - sizeof(struct ocfs2_local_disk_chunk) -
OCFS2_QBLK_RESERVED_SPACE);
- set_buffer_uptodate(bh);
unlock_buffer(bh);
status = ocfs2_journal_dirty(handle, bh);
if (status < 0) {
@@ -1007,6 +1012,38 @@ static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk(
goto out_trans;
}
+ /* Initialize new block with structures */
+ down_read(&OCFS2_I(lqinode)->ip_alloc_sem);
+ status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks + 1,
+ &p_blkno, NULL, NULL);
+ up_read(&OCFS2_I(lqinode)->ip_alloc_sem);
+ if (status < 0) {
+ mlog_errno(status);
+ goto out_trans;
+ }
+ dbh = sb_getblk(sb, p_blkno);
+ if (!dbh) {
+ status = -ENOMEM;
+ mlog_errno(status);
+ goto out_trans;
+ }
+ ocfs2_set_new_buffer_uptodate(lqinode, dbh);
+ status = ocfs2_journal_access_dq(handle, lqinode, dbh,
+ OCFS2_JOURNAL_ACCESS_CREATE);
+ if (status < 0) {
+ mlog_errno(status);
+ goto out_trans;
+ }
+ lock_buffer(dbh);
+ memset(dbh->b_data, 0, sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE);
+ unlock_buffer(dbh);
+ status = ocfs2_journal_dirty(handle, dbh);
+ if (status < 0) {
+ mlog_errno(status);
+ goto out_trans;
+ }
+
+ /* Update local quotafile info */
oinfo->dqi_blocks += 2;
oinfo->dqi_chunks++;
status = ocfs2_local_write_info(sb, type);
@@ -1031,6 +1068,7 @@ out_trans:
ocfs2_commit_trans(OCFS2_SB(sb), handle);
out:
brelse(bh);
+ brelse(dbh);
kmem_cache_free(ocfs2_qf_chunk_cachep, chunk);
return ERR_PTR(status);
}
@@ -1048,6 +1086,8 @@ static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file(
struct ocfs2_local_disk_chunk *dchunk;
int epb = ol_quota_entries_per_block(sb);
unsigned int chunk_blocks;
+ struct buffer_head *bh;
+ u64 p_blkno;
int status;
handle_t *handle;
@@ -1075,12 +1115,49 @@ static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file(
mlog_errno(status);
goto out;
}
- handle = ocfs2_start_trans(OCFS2_SB(sb), 2);
+
+ /* Get buffer from the just added block */
+ down_read(&OCFS2_I(lqinode)->ip_alloc_sem);
+ status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks,
+ &p_blkno, NULL, NULL);
+ up_read(&OCFS2_I(lqinode)->ip_alloc_sem);
+ if (status < 0) {
+ mlog_errno(status);
+ goto out;
+ }
+ bh = sb_getblk(sb, p_blkno);
+ if (!bh) {
+ status = -ENOMEM;
+ mlog_errno(status);
+ goto out;
+ }
+ ocfs2_set_new_buffer_uptodate(lqinode, bh);
+
+ /* Local quota info, chunk header and the new block we initialize */
+ handle = ocfs2_start_trans(OCFS2_SB(sb),
+ OCFS2_LOCAL_QINFO_WRITE_CREDITS +
+ 2 * OCFS2_QUOTA_BLOCK_UPDATE_CREDITS);
if (IS_ERR(handle)) {
status = PTR_ERR(handle);
mlog_errno(status);
goto out;
}
+ /* Zero created block */
+ status = ocfs2_journal_access_dq(handle, lqinode, bh,
+ OCFS2_JOURNAL_ACCESS_CREATE);
+ if (status < 0) {
+ mlog_errno(status);
+ goto out_trans;
+ }
+ lock_buffer(bh);
+ memset(bh->b_data, 0, sb->s_blocksize);
+ unlock_buffer(bh);
+ status = ocfs2_journal_dirty(handle, bh);
+ if (status < 0) {
+ mlog_errno(status);
+ goto out_trans;
+ }
+ /* Update chunk header */
status = ocfs2_journal_access_dq(handle, lqinode, chunk->qc_headerbh,
OCFS2_JOURNAL_ACCESS_WRITE);
if (status < 0) {
@@ -1097,6 +1174,7 @@ static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file(
mlog_errno(status);
goto out_trans;
}
+ /* Update file header */
oinfo->dqi_blocks++;
status = ocfs2_local_write_info(sb, type);
if (status < 0) {
diff --git a/fs/ocfs2/stack_o2cb.c b/fs/ocfs2/stack_o2cb.c
index 3f661376a2d..e49c4105026 100644
--- a/fs/ocfs2/stack_o2cb.c
+++ b/fs/ocfs2/stack_o2cb.c
@@ -17,6 +17,7 @@
* General Public License for more details.
*/
+#include <linux/kernel.h>
#include <linux/crc32.h>
#include <linux/module.h>
@@ -153,7 +154,7 @@ static int status_map[] = {
static int dlm_status_to_errno(enum dlm_status status)
{
- BUG_ON(status > (sizeof(status_map) / sizeof(status_map[0])));
+ BUG_ON(status < 0 || status >= ARRAY_SIZE(status_map));
return status_map[status];
}
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 7efb349fb9b..a3f8871d21f 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -777,6 +777,7 @@ static int ocfs2_sb_probe(struct super_block *sb,
}
di = (struct ocfs2_dinode *) (*bh)->b_data;
memset(stats, 0, sizeof(struct ocfs2_blockcheck_stats));
+ spin_lock_init(&stats->b_lock);
status = ocfs2_verify_volume(di, *bh, blksize, stats);
if (status >= 0)
goto bail;
@@ -1182,7 +1183,7 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
wake_up(&osb->osb_mount_event);
/* Start this when the mount is almost sure of being successful */
- ocfs2_orphan_scan_init(osb);
+ ocfs2_orphan_scan_start(osb);
mlog_exit(status);
return status;
@@ -1213,14 +1214,31 @@ static int ocfs2_get_sb(struct file_system_type *fs_type,
mnt);
}
+static void ocfs2_kill_sb(struct super_block *sb)
+{
+ struct ocfs2_super *osb = OCFS2_SB(sb);
+
+ /* Failed mount? */
+ if (!osb || atomic_read(&osb->vol_state) == VOLUME_DISABLED)
+ goto out;
+
+ /* Prevent further queueing of inode drop events */
+ spin_lock(&dentry_list_lock);
+ ocfs2_set_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED);
+ spin_unlock(&dentry_list_lock);
+ /* Wait for work to finish and/or remove it */
+ cancel_work_sync(&osb->dentry_lock_work);
+out:
+ kill_block_super(sb);
+}
+
static struct file_system_type ocfs2_fs_type = {
.owner = THIS_MODULE,
.name = "ocfs2",
.get_sb = ocfs2_get_sb, /* is this called when we mount
* the fs? */
- .kill_sb = kill_block_super, /* set to the generic one
- * right now, but do we
- * need to change that? */
+ .kill_sb = ocfs2_kill_sb,
+
.fs_flags = FS_REQUIRES_DEV|FS_RENAME_DOES_D_MOVE,
.next = NULL
};
@@ -1819,6 +1837,12 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err)
debugfs_remove(osb->osb_ctxt);
+ /*
+ * Flush inode dropping work queue so that deletes are
+ * performed while the filesystem is still working
+ */
+ ocfs2_drop_all_dl_inodes(osb);
+
/* Orphan scan should be stopped as early as possible */
ocfs2_orphan_scan_stop(osb);
@@ -1981,6 +2005,8 @@ static int ocfs2_initialize_super(struct super_block *sb,
snprintf(osb->dev_str, sizeof(osb->dev_str), "%u,%u",
MAJOR(osb->sb->s_dev), MINOR(osb->sb->s_dev));
+ ocfs2_orphan_scan_init(osb);
+
status = ocfs2_recovery_init(osb);
if (status) {
mlog(ML_ERROR, "Unable to initialize recovery state\n");
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index ba320e25074..d1a27cda984 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -1052,7 +1052,8 @@ static int ocfs2_xattr_block_get(struct inode *inode,
struct ocfs2_xattr_block *xb;
struct ocfs2_xattr_value_root *xv;
size_t size;
- int ret = -ENODATA, name_offset, name_len, block_off, i;
+ int ret = -ENODATA, name_offset, name_len, i;
+ int uninitialized_var(block_off);
xs->bucket = ocfs2_xattr_bucket_new(inode);
if (!xs->bucket) {
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 3ce5ae9e3d2..6f742f6658a 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -234,23 +234,20 @@ static int check_mem_permission(struct task_struct *task)
struct mm_struct *mm_for_maps(struct task_struct *task)
{
- struct mm_struct *mm = get_task_mm(task);
- if (!mm)
+ struct mm_struct *mm;
+
+ if (mutex_lock_killable(&task->cred_guard_mutex))
return NULL;
- down_read(&mm->mmap_sem);
- task_lock(task);
- if (task->mm != mm)
- goto out;
- if (task->mm != current->mm &&
- __ptrace_may_access(task, PTRACE_MODE_READ) < 0)
- goto out;
- task_unlock(task);
+
+ mm = get_task_mm(task);
+ if (mm && mm != current->mm &&
+ !ptrace_may_access(task, PTRACE_MODE_READ)) {
+ mmput(mm);
+ mm = NULL;
+ }
+ mutex_unlock(&task->cred_guard_mutex);
+
return mm;
-out:
- task_unlock(task);
- up_read(&mm->mmap_sem);
- mmput(mm);
- return NULL;
}
static int proc_pid_cmdline(struct task_struct *task, char * buffer)
@@ -1006,12 +1003,7 @@ static ssize_t oom_adjust_read(struct file *file, char __user *buf,
if (!task)
return -ESRCH;
- task_lock(task);
- if (task->mm)
- oom_adjust = task->mm->oom_adj;
- else
- oom_adjust = OOM_DISABLE;
- task_unlock(task);
+ oom_adjust = task->oomkilladj;
put_task_struct(task);
len = snprintf(buffer, sizeof(buffer), "%i\n", oom_adjust);
@@ -1040,19 +1032,11 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf,
task = get_proc_task(file->f_path.dentry->d_inode);
if (!task)
return -ESRCH;
- task_lock(task);
- if (!task->mm) {
- task_unlock(task);
- put_task_struct(task);
- return -EINVAL;
- }
- if (oom_adjust < task->mm->oom_adj && !capable(CAP_SYS_RESOURCE)) {
- task_unlock(task);
+ if (oom_adjust < task->oomkilladj && !capable(CAP_SYS_RESOURCE)) {
put_task_struct(task);
return -EACCES;
}
- task->mm->oom_adj = oom_adjust;
- task_unlock(task);
+ task->oomkilladj = oom_adjust;
put_task_struct(task);
if (end - buffer == 0)
return -EIO;
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 6f61b7cc32e..9bd8be1d235 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -119,6 +119,7 @@ static void *m_start(struct seq_file *m, loff_t *pos)
mm = mm_for_maps(priv->task);
if (!mm)
return NULL;
+ down_read(&mm->mmap_sem);
tail_vma = get_gate_vma(priv->task);
priv->tail_vma = tail_vma;
diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
index 64a72e2e765..8f5c05d3dbd 100644
--- a/fs/proc/task_nommu.c
+++ b/fs/proc/task_nommu.c
@@ -189,6 +189,7 @@ static void *m_start(struct seq_file *m, loff_t *pos)
priv->task = NULL;
return NULL;
}
+ down_read(&mm->mmap_sem);
/* start from the Nth VMA */
for (p = rb_first(&mm->mm_rb); p; p = rb_next(p))
diff --git a/fs/select.c b/fs/select.c
index d870237e42c..8084834e123 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -110,6 +110,7 @@ void poll_initwait(struct poll_wqueues *pwq)
{
init_poll_funcptr(&pwq->pt, __pollwait);
pwq->polling_task = current;
+ pwq->triggered = 0;
pwq->error = 0;
pwq->table = NULL;
pwq->inline_index = 0;
diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c
index 0c93c7ef3d1..965df1227d6 100644
--- a/fs/xfs/linux-2.6/xfs_buf.c
+++ b/fs/xfs/linux-2.6/xfs_buf.c
@@ -770,7 +770,7 @@ xfs_buf_associate_memory(
bp->b_pages = NULL;
bp->b_addr = mem;
- rval = _xfs_buf_get_pages(bp, page_count, 0);
+ rval = _xfs_buf_get_pages(bp, page_count, XBF_DONT_BLOCK);
if (rval)
return rval;
diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.c b/fs/xfs/linux-2.6/xfs_ioctl32.c
index 0882d166239..eafcc7c1870 100644
--- a/fs/xfs/linux-2.6/xfs_ioctl32.c
+++ b/fs/xfs/linux-2.6/xfs_ioctl32.c
@@ -619,7 +619,7 @@ xfs_file_compat_ioctl(
case XFS_IOC_GETVERSION_32:
cmd = _NATIVE_IOC(cmd, long);
return xfs_file_ioctl(filp, cmd, p);
- case XFS_IOC_SWAPEXT: {
+ case XFS_IOC_SWAPEXT_32: {
struct xfs_swapext sxp;
struct compat_xfs_swapext __user *sxu = arg;
diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c
index b619d6b8ca4..98ef624d9ba 100644
--- a/fs/xfs/linux-2.6/xfs_sync.c
+++ b/fs/xfs/linux-2.6/xfs_sync.c
@@ -708,6 +708,16 @@ xfs_reclaim_inode(
return 0;
}
+void
+__xfs_inode_set_reclaim_tag(
+ struct xfs_perag *pag,
+ struct xfs_inode *ip)
+{
+ radix_tree_tag_set(&pag->pag_ici_root,
+ XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino),
+ XFS_ICI_RECLAIM_TAG);
+}
+
/*
* We set the inode flag atomically with the radix tree tag.
* Once we get tag lookups on the radix tree, this inode flag
@@ -722,8 +732,7 @@ xfs_inode_set_reclaim_tag(
read_lock(&pag->pag_ici_lock);
spin_lock(&ip->i_flags_lock);
- radix_tree_tag_set(&pag->pag_ici_root,
- XFS_INO_TO_AGINO(mp, ip->i_ino), XFS_ICI_RECLAIM_TAG);
+ __xfs_inode_set_reclaim_tag(pag, ip);
__xfs_iflags_set(ip, XFS_IRECLAIMABLE);
spin_unlock(&ip->i_flags_lock);
read_unlock(&pag->pag_ici_lock);
diff --git a/fs/xfs/linux-2.6/xfs_sync.h b/fs/xfs/linux-2.6/xfs_sync.h
index 2a10301c99c..59120602588 100644
--- a/fs/xfs/linux-2.6/xfs_sync.h
+++ b/fs/xfs/linux-2.6/xfs_sync.h
@@ -48,6 +48,7 @@ int xfs_reclaim_inode(struct xfs_inode *ip, int locked, int sync_mode);
int xfs_reclaim_inodes(struct xfs_mount *mp, int mode);
void xfs_inode_set_reclaim_tag(struct xfs_inode *ip);
+void __xfs_inode_set_reclaim_tag(struct xfs_perag *pag, struct xfs_inode *ip);
void xfs_inode_clear_reclaim_tag(struct xfs_inode *ip);
void __xfs_inode_clear_reclaim_tag(struct xfs_mount *mp, struct xfs_perag *pag,
struct xfs_inode *ip);
diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c
index db15feb906f..4ece1906bd4 100644
--- a/fs/xfs/xfs_attr.c
+++ b/fs/xfs/xfs_attr.c
@@ -2010,7 +2010,9 @@ xfs_attr_rmtval_get(xfs_da_args_t *args)
dblkno = XFS_FSB_TO_DADDR(mp, map[i].br_startblock);
blkcnt = XFS_FSB_TO_BB(mp, map[i].br_blockcount);
error = xfs_read_buf(mp, mp->m_ddev_targp, dblkno,
- blkcnt, XFS_BUF_LOCK, &bp);
+ blkcnt,
+ XFS_BUF_LOCK | XBF_DONT_BLOCK,
+ &bp);
if (error)
return(error);
@@ -2141,8 +2143,8 @@ xfs_attr_rmtval_set(xfs_da_args_t *args)
dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock),
blkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount);
- bp = xfs_buf_get_flags(mp->m_ddev_targp, dblkno,
- blkcnt, XFS_BUF_LOCK);
+ bp = xfs_buf_get_flags(mp->m_ddev_targp, dblkno, blkcnt,
+ XFS_BUF_LOCK | XBF_DONT_BLOCK);
ASSERT(bp);
ASSERT(!XFS_BUF_GETERROR(bp));
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index 7928b9983c1..8ee5b5a76a2 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -6009,7 +6009,7 @@ xfs_getbmap(
*/
error = ENOMEM;
subnex = 16;
- map = kmem_alloc(subnex * sizeof(*map), KM_MAYFAIL);
+ map = kmem_alloc(subnex * sizeof(*map), KM_MAYFAIL | KM_NOFS);
if (!map)
goto out_unlock_ilock;
diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c
index e9df9957482..26717388acf 100644
--- a/fs/xfs/xfs_btree.c
+++ b/fs/xfs/xfs_btree.c
@@ -120,8 +120,8 @@ xfs_btree_check_sblock(
XFS_RANDOM_BTREE_CHECK_SBLOCK))) {
if (bp)
xfs_buftrace("SBTREE ERROR", bp);
- XFS_ERROR_REPORT("xfs_btree_check_sblock", XFS_ERRLEVEL_LOW,
- cur->bc_mp);
+ XFS_CORRUPTION_ERROR("xfs_btree_check_sblock",
+ XFS_ERRLEVEL_LOW, cur->bc_mp, block);
return XFS_ERROR(EFSCORRUPTED);
}
return 0;
diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c
index 9ff6e57a507..2847bbc1c53 100644
--- a/fs/xfs/xfs_da_btree.c
+++ b/fs/xfs/xfs_da_btree.c
@@ -2201,7 +2201,7 @@ kmem_zone_t *xfs_dabuf_zone; /* dabuf zone */
xfs_da_state_t *
xfs_da_state_alloc(void)
{
- return kmem_zone_zalloc(xfs_da_state_zone, KM_SLEEP);
+ return kmem_zone_zalloc(xfs_da_state_zone, KM_NOFS);
}
/*
@@ -2261,9 +2261,9 @@ xfs_da_buf_make(int nbuf, xfs_buf_t **bps, inst_t *ra)
int off;
if (nbuf == 1)
- dabuf = kmem_zone_alloc(xfs_dabuf_zone, KM_SLEEP);
+ dabuf = kmem_zone_alloc(xfs_dabuf_zone, KM_NOFS);
else
- dabuf = kmem_alloc(XFS_DA_BUF_SIZE(nbuf), KM_SLEEP);
+ dabuf = kmem_alloc(XFS_DA_BUF_SIZE(nbuf), KM_NOFS);
dabuf->dirty = 0;
#ifdef XFS_DABUF_DEBUG
dabuf->ra = ra;
diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c
index c657bec6d95..bb1d58eb398 100644
--- a/fs/xfs/xfs_dir2.c
+++ b/fs/xfs/xfs_dir2.c
@@ -256,7 +256,7 @@ xfs_dir_cilookup_result(
!(args->op_flags & XFS_DA_OP_CILOOKUP))
return EEXIST;
- args->value = kmem_alloc(len, KM_MAYFAIL);
+ args->value = kmem_alloc(len, KM_NOFS | KM_MAYFAIL);
if (!args->value)
return ENOMEM;
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index cbd451bb484..2d0b3e1da9e 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -167,17 +167,25 @@ xfs_growfs_data_private(
new = nb - mp->m_sb.sb_dblocks;
oagcount = mp->m_sb.sb_agcount;
if (nagcount > oagcount) {
+ void *new_perag, *old_perag;
+
xfs_filestream_flush(mp);
+
+ new_perag = kmem_zalloc(sizeof(xfs_perag_t) * nagcount,
+ KM_MAYFAIL);
+ if (!new_perag)
+ return XFS_ERROR(ENOMEM);
+
down_write(&mp->m_peraglock);
- mp->m_perag = kmem_realloc(mp->m_perag,
- sizeof(xfs_perag_t) * nagcount,
- sizeof(xfs_perag_t) * oagcount,
- KM_SLEEP);
- memset(&mp->m_perag[oagcount], 0,
- (nagcount - oagcount) * sizeof(xfs_perag_t));
+ memcpy(new_perag, mp->m_perag, sizeof(xfs_perag_t) * oagcount);
+ old_perag = mp->m_perag;
+ mp->m_perag = new_perag;
+
mp->m_flags |= XFS_MOUNT_32BITINODES;
nagimax = xfs_initialize_perag(mp, nagcount);
up_write(&mp->m_peraglock);
+
+ kmem_free(old_perag);
}
tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFS);
tp->t_flags |= XFS_TRANS_RESERVE;
diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c
index 5fcec6f020a..ecbf8b4d2e2 100644
--- a/fs/xfs/xfs_iget.c
+++ b/fs/xfs/xfs_iget.c
@@ -64,6 +64,10 @@ xfs_inode_alloc(
ip = kmem_zone_alloc(xfs_inode_zone, KM_SLEEP);
if (!ip)
return NULL;
+ if (inode_init_always(mp->m_super, VFS_I(ip))) {
+ kmem_zone_free(xfs_inode_zone, ip);
+ return NULL;
+ }
ASSERT(atomic_read(&ip->i_iocount) == 0);
ASSERT(atomic_read(&ip->i_pincount) == 0);
@@ -105,17 +109,6 @@ xfs_inode_alloc(
#ifdef XFS_DIR2_TRACE
ip->i_dir_trace = ktrace_alloc(XFS_DIR2_KTRACE_SIZE, KM_NOFS);
#endif
- /*
- * Now initialise the VFS inode. We do this after the xfs_inode
- * initialisation as internal failures will result in ->destroy_inode
- * being called and that will pass down through the reclaim path and
- * free the XFS inode. This path requires the XFS inode to already be
- * initialised. Hence if this call fails, the xfs_inode has already
- * been freed and we should not reference it at all in the error
- * handling.
- */
- if (!inode_init_always(mp->m_super, VFS_I(ip)))
- return NULL;
/* prevent anyone from using this yet */
VFS_I(ip)->i_state = I_NEW|I_LOCK;
@@ -123,6 +116,71 @@ xfs_inode_alloc(
return ip;
}
+STATIC void
+xfs_inode_free(
+ struct xfs_inode *ip)
+{
+ switch (ip->i_d.di_mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFDIR:
+ case S_IFLNK:
+ xfs_idestroy_fork(ip, XFS_DATA_FORK);
+ break;
+ }
+
+ if (ip->i_afp)
+ xfs_idestroy_fork(ip, XFS_ATTR_FORK);
+
+#ifdef XFS_INODE_TRACE
+ ktrace_free(ip->i_trace);
+#endif
+#ifdef XFS_BMAP_TRACE
+ ktrace_free(ip->i_xtrace);
+#endif
+#ifdef XFS_BTREE_TRACE
+ ktrace_free(ip->i_btrace);
+#endif
+#ifdef XFS_RW_TRACE
+ ktrace_free(ip->i_rwtrace);
+#endif
+#ifdef XFS_ILOCK_TRACE
+ ktrace_free(ip->i_lock_trace);
+#endif
+#ifdef XFS_DIR2_TRACE
+ ktrace_free(ip->i_dir_trace);
+#endif
+
+ if (ip->i_itemp) {
+ /*
+ * Only if we are shutting down the fs will we see an
+ * inode still in the AIL. If it is there, we should remove
+ * it to prevent a use-after-free from occurring.
+ */
+ xfs_log_item_t *lip = &ip->i_itemp->ili_item;
+ struct xfs_ail *ailp = lip->li_ailp;
+
+ ASSERT(((lip->li_flags & XFS_LI_IN_AIL) == 0) ||
+ XFS_FORCED_SHUTDOWN(ip->i_mount));
+ if (lip->li_flags & XFS_LI_IN_AIL) {
+ spin_lock(&ailp->xa_lock);
+ if (lip->li_flags & XFS_LI_IN_AIL)
+ xfs_trans_ail_delete(ailp, lip);
+ else
+ spin_unlock(&ailp->xa_lock);
+ }
+ xfs_inode_item_destroy(ip);
+ ip->i_itemp = NULL;
+ }
+
+ /* asserts to verify all state is correct here */
+ ASSERT(atomic_read(&ip->i_iocount) == 0);
+ ASSERT(atomic_read(&ip->i_pincount) == 0);
+ ASSERT(!spin_is_locked(&ip->i_flags_lock));
+ ASSERT(completion_done(&ip->i_flush));
+
+ kmem_zone_free(xfs_inode_zone, ip);
+}
+
/*
* Check the validity of the inode we just found it the cache
*/
@@ -133,80 +191,82 @@ xfs_iget_cache_hit(
int flags,
int lock_flags) __releases(pag->pag_ici_lock)
{
+ struct inode *inode = VFS_I(ip);
struct xfs_mount *mp = ip->i_mount;
- int error = EAGAIN;
+ int error;
+
+ spin_lock(&ip->i_flags_lock);
/*
- * If INEW is set this inode is being set up
- * If IRECLAIM is set this inode is being torn down
- * Pause and try again.
+ * If we are racing with another cache hit that is currently
+ * instantiating this inode or currently recycling it out of
+ * reclaimabe state, wait for the initialisation to complete
+ * before continuing.
+ *
+ * XXX(hch): eventually we should do something equivalent to
+ * wait_on_inode to wait for these flags to be cleared
+ * instead of polling for it.
*/
- if (xfs_iflags_test(ip, (XFS_INEW|XFS_IRECLAIM))) {
+ if (ip->i_flags & (XFS_INEW|XFS_IRECLAIM)) {
XFS_STATS_INC(xs_ig_frecycle);
+ error = EAGAIN;
goto out_error;
}
- /* If IRECLAIMABLE is set, we've torn down the vfs inode part */
- if (xfs_iflags_test(ip, XFS_IRECLAIMABLE)) {
-
- /*
- * If lookup is racing with unlink, then we should return an
- * error immediately so we don't remove it from the reclaim
- * list and potentially leak the inode.
- */
- if ((ip->i_d.di_mode == 0) && !(flags & XFS_IGET_CREATE)) {
- error = ENOENT;
- goto out_error;
- }
+ /*
+ * If lookup is racing with unlink return an error immediately.
+ */
+ if (ip->i_d.di_mode == 0 && !(flags & XFS_IGET_CREATE)) {
+ error = ENOENT;
+ goto out_error;
+ }
+ /*
+ * If IRECLAIMABLE is set, we've torn down the VFS inode already.
+ * Need to carefully get it back into useable state.
+ */
+ if (ip->i_flags & XFS_IRECLAIMABLE) {
xfs_itrace_exit_tag(ip, "xfs_iget.alloc");
/*
- * We need to re-initialise the VFS inode as it has been
- * 'freed' by the VFS. Do this here so we can deal with
- * errors cleanly, then tag it so it can be set up correctly
- * later.
+ * We need to set XFS_INEW atomically with clearing the
+ * reclaimable tag so that we do have an indicator of the
+ * inode still being initialized.
*/
- if (!inode_init_always(mp->m_super, VFS_I(ip))) {
- error = ENOMEM;
- goto out_error;
- }
+ ip->i_flags |= XFS_INEW;
+ ip->i_flags &= ~XFS_IRECLAIMABLE;
+ __xfs_inode_clear_reclaim_tag(mp, pag, ip);
- /*
- * We must set the XFS_INEW flag before clearing the
- * XFS_IRECLAIMABLE flag so that if a racing lookup does
- * not find the XFS_IRECLAIMABLE above but has the igrab()
- * below succeed we can safely check XFS_INEW to detect
- * that this inode is still being initialised.
- */
- xfs_iflags_set(ip, XFS_INEW);
- xfs_iflags_clear(ip, XFS_IRECLAIMABLE);
+ spin_unlock(&ip->i_flags_lock);
+ read_unlock(&pag->pag_ici_lock);
- /* clear the radix tree reclaim flag as well. */
- __xfs_inode_clear_reclaim_tag(mp, pag, ip);
- } else if (!igrab(VFS_I(ip))) {
+ error = -inode_init_always(mp->m_super, inode);
+ if (error) {
+ /*
+ * Re-initializing the inode failed, and we are in deep
+ * trouble. Try to re-add it to the reclaim list.
+ */
+ read_lock(&pag->pag_ici_lock);
+ spin_lock(&ip->i_flags_lock);
+
+ ip->i_flags &= ~XFS_INEW;
+ ip->i_flags |= XFS_IRECLAIMABLE;
+ __xfs_inode_set_reclaim_tag(pag, ip);
+ goto out_error;
+ }
+ inode->i_state = I_LOCK|I_NEW;
+ } else {
/* If the VFS inode is being torn down, pause and try again. */
- XFS_STATS_INC(xs_ig_frecycle);
- goto out_error;
- } else if (xfs_iflags_test(ip, XFS_INEW)) {
- /*
- * We are racing with another cache hit that is
- * currently recycling this inode out of the XFS_IRECLAIMABLE
- * state. Wait for the initialisation to complete before
- * continuing.
- */
- wait_on_inode(VFS_I(ip));
- }
+ if (!igrab(inode)) {
+ error = EAGAIN;
+ goto out_error;
+ }
- if (ip->i_d.di_mode == 0 && !(flags & XFS_IGET_CREATE)) {
- error = ENOENT;
- iput(VFS_I(ip));
- goto out_error;
+ /* We've got a live one. */
+ spin_unlock(&ip->i_flags_lock);
+ read_unlock(&pag->pag_ici_lock);
}
- /* We've got a live one. */
- read_unlock(&pag->pag_ici_lock);
-
if (lock_flags != 0)
xfs_ilock(ip, lock_flags);
@@ -216,6 +276,7 @@ xfs_iget_cache_hit(
return 0;
out_error:
+ spin_unlock(&ip->i_flags_lock);
read_unlock(&pag->pag_ici_lock);
return error;
}
@@ -299,7 +360,8 @@ out_preload_end:
if (lock_flags)
xfs_iunlock(ip, lock_flags);
out_destroy:
- xfs_destroy_inode(ip);
+ __destroy_inode(VFS_I(ip));
+ xfs_inode_free(ip);
return error;
}
@@ -504,62 +566,7 @@ xfs_ireclaim(
xfs_qm_dqdetach(ip);
xfs_iunlock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
- switch (ip->i_d.di_mode & S_IFMT) {
- case S_IFREG:
- case S_IFDIR:
- case S_IFLNK:
- xfs_idestroy_fork(ip, XFS_DATA_FORK);
- break;
- }
-
- if (ip->i_afp)
- xfs_idestroy_fork(ip, XFS_ATTR_FORK);
-
-#ifdef XFS_INODE_TRACE
- ktrace_free(ip->i_trace);
-#endif
-#ifdef XFS_BMAP_TRACE
- ktrace_free(ip->i_xtrace);
-#endif
-#ifdef XFS_BTREE_TRACE
- ktrace_free(ip->i_btrace);
-#endif
-#ifdef XFS_RW_TRACE
- ktrace_free(ip->i_rwtrace);
-#endif
-#ifdef XFS_ILOCK_TRACE
- ktrace_free(ip->i_lock_trace);
-#endif
-#ifdef XFS_DIR2_TRACE
- ktrace_free(ip->i_dir_trace);
-#endif
- if (ip->i_itemp) {
- /*
- * Only if we are shutting down the fs will we see an
- * inode still in the AIL. If it is there, we should remove
- * it to prevent a use-after-free from occurring.
- */
- xfs_log_item_t *lip = &ip->i_itemp->ili_item;
- struct xfs_ail *ailp = lip->li_ailp;
-
- ASSERT(((lip->li_flags & XFS_LI_IN_AIL) == 0) ||
- XFS_FORCED_SHUTDOWN(ip->i_mount));
- if (lip->li_flags & XFS_LI_IN_AIL) {
- spin_lock(&ailp->xa_lock);
- if (lip->li_flags & XFS_LI_IN_AIL)
- xfs_trans_ail_delete(ailp, lip);
- else
- spin_unlock(&ailp->xa_lock);
- }
- xfs_inode_item_destroy(ip);
- ip->i_itemp = NULL;
- }
- /* asserts to verify all state is correct here */
- ASSERT(atomic_read(&ip->i_iocount) == 0);
- ASSERT(atomic_read(&ip->i_pincount) == 0);
- ASSERT(!spin_is_locked(&ip->i_flags_lock));
- ASSERT(completion_done(&ip->i_flush));
- kmem_zone_free(xfs_inode_zone, ip);
+ xfs_inode_free(ip);
}
/*
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 1f22d65fed0..da428b3fe0f 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -343,6 +343,16 @@ xfs_iformat(
return XFS_ERROR(EFSCORRUPTED);
}
+ if (unlikely((ip->i_d.di_flags & XFS_DIFLAG_REALTIME) &&
+ !ip->i_mount->m_rtdev_targp)) {
+ xfs_fs_repair_cmn_err(CE_WARN, ip->i_mount,
+ "corrupt dinode %Lu, has realtime flag set.",
+ ip->i_ino);
+ XFS_CORRUPTION_ERROR("xfs_iformat(realtime)",
+ XFS_ERRLEVEL_LOW, ip->i_mount, dip);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
switch (ip->i_d.di_mode & S_IFMT) {
case S_IFIFO:
case S_IFCHR:
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index 1804f866a71..65f24a3cc99 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -310,23 +310,6 @@ static inline struct inode *VFS_I(struct xfs_inode *ip)
}
/*
- * Get rid of a partially initialized inode.
- *
- * We have to go through destroy_inode to make sure allocations
- * from init_inode_always like the security data are undone.
- *
- * We mark the inode bad so that it takes the short cut in
- * the reclaim path instead of going through the flush path
- * which doesn't make sense for an inode that has never seen the
- * light of day.
- */
-static inline void xfs_destroy_inode(struct xfs_inode *ip)
-{
- make_bad_inode(VFS_I(ip));
- return destroy_inode(VFS_I(ip));
-}
-
-/*
* i_flags helper functions
*/
static inline void
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index 3750f04ede0..9dbdff3ea48 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -3180,7 +3180,7 @@ try_again:
STATIC void
xlog_state_want_sync(xlog_t *log, xlog_in_core_t *iclog)
{
- ASSERT(spin_is_locked(&log->l_icloglock));
+ assert_spin_locked(&log->l_icloglock);
if (iclog->ic_state == XLOG_STATE_ACTIVE) {
xlog_state_switch_iclogs(log, iclog, 0);
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
index c4eca5ed5da..492d75bae2b 100644
--- a/fs/xfs/xfs_vnodeops.c
+++ b/fs/xfs/xfs_vnodeops.c
@@ -538,7 +538,9 @@ xfs_readlink_bmap(
d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock);
byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount);
- bp = xfs_buf_read(mp->m_ddev_targp, d, BTOBB(byte_cnt), 0);
+ bp = xfs_buf_read_flags(mp->m_ddev_targp, d, BTOBB(byte_cnt),
+ XBF_LOCK | XBF_MAPPED |
+ XBF_DONT_BLOCK);
error = XFS_BUF_GETERROR(bp);
if (error) {
xfs_ioerror_alert("xfs_readlink",
diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h
index 3e798593b17..ab0b85cf21f 100644
--- a/include/acpi/acpiosxf.h
+++ b/include/acpi/acpiosxf.h
@@ -242,6 +242,10 @@ acpi_os_derive_pci_id(acpi_handle rhandle,
acpi_status acpi_os_validate_interface(char *interface);
acpi_status acpi_osi_invalidate(char* interface);
+acpi_status
+acpi_os_validate_address(u8 space_id, acpi_physical_address address,
+ acpi_size length, char *name);
+
u64 acpi_os_get_timer(void);
acpi_status acpi_os_signal(u32 function, void *info);
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index baf1e0a9a7e..740ac3ad8fd 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -174,7 +174,7 @@ struct acpi_processor_throttling {
cpumask_var_t shared_cpu_map;
int (*acpi_processor_get_throttling) (struct acpi_processor * pr);
int (*acpi_processor_set_throttling) (struct acpi_processor * pr,
- int state);
+ int state, bool force);
u32 address;
u8 duty_offset;
@@ -321,7 +321,8 @@ static inline int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
/* in processor_throttling.c */
int acpi_processor_tstate_has_changed(struct acpi_processor *pr);
int acpi_processor_get_throttling_info(struct acpi_processor *pr);
-extern int acpi_processor_set_throttling(struct acpi_processor *pr, int state);
+extern int acpi_processor_set_throttling(struct acpi_processor *pr,
+ int state, bool force);
extern const struct file_operations acpi_processor_throttling_fops;
extern void acpi_processor_throttling_init(void);
/* in processor_idle.c */
diff --git a/include/crypto/algapi.h b/include/crypto/algapi.h
index 010545436ef..5a2bd1cc965 100644
--- a/include/crypto/algapi.h
+++ b/include/crypto/algapi.h
@@ -137,6 +137,7 @@ struct crypto_instance *crypto_alloc_instance(const char *name,
void crypto_init_queue(struct crypto_queue *queue, unsigned int max_qlen);
int crypto_enqueue_request(struct crypto_queue *queue,
struct crypto_async_request *request);
+void *__crypto_dequeue_request(struct crypto_queue *queue, unsigned int offset);
struct crypto_async_request *crypto_dequeue_request(struct crypto_queue *queue);
int crypto_tfm_in_queue(struct crypto_queue *queue, struct crypto_tfm *tfm);
diff --git a/include/crypto/internal/skcipher.h b/include/crypto/internal/skcipher.h
index 2ba42cd7d6a..3a748a6bf77 100644
--- a/include/crypto/internal/skcipher.h
+++ b/include/crypto/internal/skcipher.h
@@ -79,8 +79,8 @@ static inline int skcipher_enqueue_givcrypt(
static inline struct skcipher_givcrypt_request *skcipher_dequeue_givcrypt(
struct crypto_queue *queue)
{
- return container_of(ablkcipher_dequeue_request(queue),
- struct skcipher_givcrypt_request, creq);
+ return __crypto_dequeue_request(
+ queue, offsetof(struct skcipher_givcrypt_request, creq.base));
}
static inline void *skcipher_givcrypt_reqctx(
diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h
index 7174818c2c1..853508499d2 100644
--- a/include/drm/drm_pciids.h
+++ b/include/drm/drm_pciids.h
@@ -257,9 +257,12 @@
{0x1002, 0x940F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x94A3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94B3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x94B4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94B5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x94B9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9441, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9442, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
@@ -288,6 +291,7 @@
{0x1002, 0x948F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9490, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9491, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x9495, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9498, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
{0x1002, 0x949C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
{0x1002, 0x949E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
@@ -325,6 +329,7 @@
{0x1002, 0x9552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9553, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9555, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x9557, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9580, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9581, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9583, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
@@ -365,6 +370,11 @@
{0x1002, 0x9614, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9615, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9616, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+ {0x1002, 0x9710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+ {0x1002, 0x9711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+ {0x1002, 0x9712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+ {0x1002, 0x9713, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
+ {0x1002, 0x9714, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0, 0, 0}
#define r128_PCI_IDS \
diff --git a/include/drm/radeon_drm.h b/include/drm/radeon_drm.h
index af4b4826997..2ba61e18fc8 100644
--- a/include/drm/radeon_drm.h
+++ b/include/drm/radeon_drm.h
@@ -508,6 +508,7 @@ typedef struct {
#define DRM_RADEON_INFO 0x27
#define DRM_RADEON_GEM_SET_TILING 0x28
#define DRM_RADEON_GEM_GET_TILING 0x29
+#define DRM_RADEON_GEM_BUSY 0x2a
#define DRM_IOCTL_RADEON_CP_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_INIT, drm_radeon_init_t)
#define DRM_IOCTL_RADEON_CP_START DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_START)
@@ -548,6 +549,7 @@ typedef struct {
#define DRM_IOCTL_RADEON_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_INFO, struct drm_radeon_info)
#define DRM_IOCTL_RADEON_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_TILING, struct drm_radeon_gem_set_tiling)
#define DRM_IOCTL_RADEON_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_GET_TILING, struct drm_radeon_gem_get_tiling)
+#define DRM_IOCTL_RADEON_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_BUSY, struct drm_radeon_gem_busy)
typedef struct drm_radeon_init {
enum {
@@ -707,6 +709,7 @@ typedef struct drm_radeon_indirect {
#define RADEON_PARAM_FB_LOCATION 14 /* FB location */
#define RADEON_PARAM_NUM_GB_PIPES 15 /* num GB pipes */
#define RADEON_PARAM_DEVICE_ID 16
+#define RADEON_PARAM_NUM_Z_PIPES 17 /* num Z pipes */
typedef struct drm_radeon_getparam {
int param;
@@ -838,7 +841,7 @@ struct drm_radeon_gem_wait_idle {
struct drm_radeon_gem_busy {
uint32_t handle;
- uint32_t busy;
+ uint32_t domain;
};
struct drm_radeon_gem_pread {
@@ -895,6 +898,7 @@ struct drm_radeon_cs {
#define RADEON_INFO_DEVICE_ID 0x00
#define RADEON_INFO_NUM_GB_PIPES 0x01
+#define RADEON_INFO_NUM_Z_PIPES 0x02
struct drm_radeon_info {
uint32_t request;
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 61ee18c1bdb..2046b5b8af4 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -117,6 +117,7 @@ extern int setup_arg_pages(struct linux_binprm * bprm,
int executable_stack);
extern int bprm_mm_init(struct linux_binprm *bprm);
extern int copy_strings_kernel(int argc,char ** argv,struct linux_binprm *bprm);
+extern int prepare_bprm_creds(struct linux_binprm *bprm);
extern void install_exec_creds(struct linux_binprm *bprm);
extern void do_coredump(long signr, int exit_code, struct pt_regs *regs);
extern int set_binfmt(struct linux_binfmt *new);
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index 2878811c613..756d78b8c1c 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -94,13 +94,13 @@ extern void __bitmap_shift_right(unsigned long *dst,
const unsigned long *src, int shift, int bits);
extern void __bitmap_shift_left(unsigned long *dst,
const unsigned long *src, int shift, int bits);
-extern void __bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
+extern int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits);
extern void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits);
extern void __bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits);
-extern void __bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
+extern int __bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits);
extern int __bitmap_intersects(const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits);
@@ -171,13 +171,12 @@ static inline void bitmap_copy(unsigned long *dst, const unsigned long *src,
}
}
-static inline void bitmap_and(unsigned long *dst, const unsigned long *src1,
+static inline int bitmap_and(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, int nbits)
{
if (small_const_nbits(nbits))
- *dst = *src1 & *src2;
- else
- __bitmap_and(dst, src1, src2, nbits);
+ return (*dst = *src1 & *src2) != 0;
+ return __bitmap_and(dst, src1, src2, nbits);
}
static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
@@ -198,13 +197,12 @@ static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1,
__bitmap_xor(dst, src1, src2, nbits);
}
-static inline void bitmap_andnot(unsigned long *dst, const unsigned long *src1,
+static inline int bitmap_andnot(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, int nbits)
{
if (small_const_nbits(nbits))
- *dst = *src1 & ~(*src2);
- else
- __bitmap_andnot(dst, src1, src2, nbits);
+ return (*dst = *src1 & ~(*src2)) != 0;
+ return __bitmap_andnot(dst, src1, src2, nbits);
}
static inline void bitmap_complement(unsigned long *dst, const unsigned long *src,
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index e7cb5dbf6c2..69103e053c9 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -913,6 +913,7 @@ extern void blk_queue_logical_block_size(struct request_queue *, unsigned short)
extern void blk_queue_physical_block_size(struct request_queue *, unsigned short);
extern void blk_queue_alignment_offset(struct request_queue *q,
unsigned int alignment);
+extern void blk_limits_io_min(struct queue_limits *limits, unsigned int min);
extern void blk_queue_io_min(struct request_queue *q, unsigned int min);
extern void blk_queue_io_opt(struct request_queue *q, unsigned int opt);
extern void blk_set_default_limits(struct queue_limits *lim);
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index c5ac87ca7bc..796df12091b 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -43,10 +43,10 @@
* int cpu_isset(cpu, mask) true iff bit 'cpu' set in mask
* int cpu_test_and_set(cpu, mask) test and set bit 'cpu' in mask
*
- * void cpus_and(dst, src1, src2) dst = src1 & src2 [intersection]
+ * int cpus_and(dst, src1, src2) dst = src1 & src2 [intersection]
* void cpus_or(dst, src1, src2) dst = src1 | src2 [union]
* void cpus_xor(dst, src1, src2) dst = src1 ^ src2
- * void cpus_andnot(dst, src1, src2) dst = src1 & ~src2
+ * int cpus_andnot(dst, src1, src2) dst = src1 & ~src2
* void cpus_complement(dst, src) dst = ~src
*
* int cpus_equal(mask1, mask2) Does mask1 == mask2?
@@ -179,10 +179,10 @@ static inline int __cpu_test_and_set(int cpu, cpumask_t *addr)
}
#define cpus_and(dst, src1, src2) __cpus_and(&(dst), &(src1), &(src2), NR_CPUS)
-static inline void __cpus_and(cpumask_t *dstp, const cpumask_t *src1p,
+static inline int __cpus_and(cpumask_t *dstp, const cpumask_t *src1p,
const cpumask_t *src2p, int nbits)
{
- bitmap_and(dstp->bits, src1p->bits, src2p->bits, nbits);
+ return bitmap_and(dstp->bits, src1p->bits, src2p->bits, nbits);
}
#define cpus_or(dst, src1, src2) __cpus_or(&(dst), &(src1), &(src2), NR_CPUS)
@@ -201,10 +201,10 @@ static inline void __cpus_xor(cpumask_t *dstp, const cpumask_t *src1p,
#define cpus_andnot(dst, src1, src2) \
__cpus_andnot(&(dst), &(src1), &(src2), NR_CPUS)
-static inline void __cpus_andnot(cpumask_t *dstp, const cpumask_t *src1p,
+static inline int __cpus_andnot(cpumask_t *dstp, const cpumask_t *src1p,
const cpumask_t *src2p, int nbits)
{
- bitmap_andnot(dstp->bits, src1p->bits, src2p->bits, nbits);
+ return bitmap_andnot(dstp->bits, src1p->bits, src2p->bits, nbits);
}
#define cpus_complement(dst, src) __cpus_complement(&(dst), &(src), NR_CPUS)
@@ -738,11 +738,11 @@ static inline void cpumask_clear(struct cpumask *dstp)
* @src1p: the first input
* @src2p: the second input
*/
-static inline void cpumask_and(struct cpumask *dstp,
+static inline int cpumask_and(struct cpumask *dstp,
const struct cpumask *src1p,
const struct cpumask *src2p)
{
- bitmap_and(cpumask_bits(dstp), cpumask_bits(src1p),
+ return bitmap_and(cpumask_bits(dstp), cpumask_bits(src1p),
cpumask_bits(src2p), nr_cpumask_bits);
}
@@ -779,11 +779,11 @@ static inline void cpumask_xor(struct cpumask *dstp,
* @src1p: the first input
* @src2p: the second input
*/
-static inline void cpumask_andnot(struct cpumask *dstp,
+static inline int cpumask_andnot(struct cpumask *dstp,
const struct cpumask *src1p,
const struct cpumask *src2p)
{
- bitmap_andnot(cpumask_bits(dstp), cpumask_bits(src1p),
+ return bitmap_andnot(cpumask_bits(dstp), cpumask_bits(src1p),
cpumask_bits(src2p), nr_cpumask_bits);
}
diff --git a/include/linux/decompress/generic.h b/include/linux/decompress/generic.h
index 6dfb856327b..0c7111a55a1 100644
--- a/include/linux/decompress/generic.h
+++ b/include/linux/decompress/generic.h
@@ -1,31 +1,37 @@
#ifndef DECOMPRESS_GENERIC_H
#define DECOMPRESS_GENERIC_H
-/* Minimal chunksize to be read.
- *Bzip2 prefers at least 4096
- *Lzma prefers 0x10000 */
-#define COMPR_IOBUF_SIZE 4096
-
typedef int (*decompress_fn) (unsigned char *inbuf, int len,
int(*fill)(void*, unsigned int),
- int(*writebb)(void*, unsigned int),
- unsigned char *output,
+ int(*flush)(void*, unsigned int),
+ unsigned char *outbuf,
int *posp,
void(*error)(char *x));
/* inbuf - input buffer
*len - len of pre-read data in inbuf
- *fill - function to fill inbuf if empty
- *writebb - function to write out outbug
+ *fill - function to fill inbuf when empty
+ *flush - function to write out outbuf
+ *outbuf - output buffer
*posp - if non-null, input position (number of bytes read) will be
* returned here
*
- *If len != 0, the inbuf is initialized (with as much data), and fill
- *should not be called
- *If len = 0, the inbuf is allocated, but empty. Its size is IOBUF_SIZE
- *fill should be called (repeatedly...) to read data, at most IOBUF_SIZE
+ *If len != 0, inbuf should contain all the necessary input data, and fill
+ *should be NULL
+ *If len = 0, inbuf can be NULL, in which case the decompressor will allocate
+ *the input buffer. If inbuf != NULL it must be at least XXX_IOBUF_SIZE bytes.
+ *fill will be called (repeatedly...) to read data, at most XXX_IOBUF_SIZE
+ *bytes should be read per call. Replace XXX with the appropriate decompressor
+ *name, i.e. LZMA_IOBUF_SIZE.
+ *
+ *If flush = NULL, outbuf must be large enough to buffer all the expected
+ *output. If flush != NULL, the output buffer will be allocated by the
+ *decompressor (outbuf = NULL), and the flush function will be called to
+ *flush the output buffer at the appropriate time (decompressor and stream
+ *dependent).
*/
+
/* Utility routine to detect the decompression method */
decompress_fn decompress_method(const unsigned char *inbuf, int len,
const char **name);
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 655e7721580..df7607e6dce 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -91,6 +91,9 @@ typedef int (*dm_iterate_devices_fn) (struct dm_target *ti,
iterate_devices_callout_fn fn,
void *data);
+typedef void (*dm_io_hints_fn) (struct dm_target *ti,
+ struct queue_limits *limits);
+
/*
* Returns:
* 0: The target can handle the next I/O immediately.
@@ -151,6 +154,7 @@ struct target_type {
dm_merge_fn merge;
dm_busy_fn busy;
dm_iterate_devices_fn iterate_devices;
+ dm_io_hints_fn io_hints;
/* For internal device-mapper use. */
struct list_head list;
diff --git a/include/linux/dm-log-userspace.h b/include/linux/dm-log-userspace.h
index 642e3017b51..8a1f972c0fe 100644
--- a/include/linux/dm-log-userspace.h
+++ b/include/linux/dm-log-userspace.h
@@ -371,7 +371,18 @@
(DM_ULOG_REQUEST_MASK & (request_type))
struct dm_ulog_request {
- char uuid[DM_UUID_LEN]; /* Ties a request to a specific mirror log */
+ /*
+ * The local unique identifier (luid) and the universally unique
+ * identifier (uuid) are used to tie a request to a specific
+ * mirror log. A single machine log could probably make due with
+ * just the 'luid', but a cluster-aware log must use the 'uuid' and
+ * the 'luid'. The uuid is what is required for node to node
+ * communication concerning a particular log, but the 'luid' helps
+ * differentiate between logs that are being swapped and have the
+ * same 'uuid'. (Think "live" and "inactive" device-mapper tables.)
+ */
+ uint64_t luid;
+ char uuid[DM_UUID_LEN];
char padding[7]; /* Padding because DM_UUID_LEN = 129 */
int32_t error; /* Used to report back processing errors */
diff --git a/include/linux/flex_array.h b/include/linux/flex_array.h
index 23c1ec79a31..45ff1849151 100644
--- a/include/linux/flex_array.h
+++ b/include/linux/flex_array.h
@@ -21,7 +21,7 @@ struct flex_array {
struct {
int element_size;
int total_nr_elements;
- struct flex_array_part *parts[0];
+ struct flex_array_part *parts[];
};
/*
* This little trick makes sure that
@@ -36,12 +36,14 @@ struct flex_array {
.total_nr_elements = (total), \
} } }
-struct flex_array *flex_array_alloc(int element_size, int total, gfp_t flags);
-int flex_array_prealloc(struct flex_array *fa, int start, int end, gfp_t flags);
+struct flex_array *flex_array_alloc(int element_size, unsigned int total,
+ gfp_t flags);
+int flex_array_prealloc(struct flex_array *fa, unsigned int start,
+ unsigned int end, gfp_t flags);
void flex_array_free(struct flex_array *fa);
void flex_array_free_parts(struct flex_array *fa);
-int flex_array_put(struct flex_array *fa, int element_nr, void *src,
+int flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src,
gfp_t flags);
-void *flex_array_get(struct flex_array *fa, int element_nr);
+void *flex_array_get(struct flex_array *fa, unsigned int element_nr);
#endif /* _FLEX_ARRAY_H */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index a36ffa5a77a..3972ffb597c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1998,12 +1998,25 @@ extern void bd_release_from_disk(struct block_device *, struct gendisk *);
#define CHRDEV_MAJOR_HASH_SIZE 255
extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
extern int register_chrdev_region(dev_t, unsigned, const char *);
-extern int register_chrdev(unsigned int, const char *,
- const struct file_operations *);
-extern void unregister_chrdev(unsigned int, const char *);
+extern int __register_chrdev(unsigned int major, unsigned int baseminor,
+ unsigned int count, const char *name,
+ const struct file_operations *fops);
+extern void __unregister_chrdev(unsigned int major, unsigned int baseminor,
+ unsigned int count, const char *name);
extern void unregister_chrdev_region(dev_t, unsigned);
extern void chrdev_show(struct seq_file *,off_t);
+static inline int register_chrdev(unsigned int major, const char *name,
+ const struct file_operations *fops)
+{
+ return __register_chrdev(major, 0, 256, name, fops);
+}
+
+static inline void unregister_chrdev(unsigned int major, const char *name)
+{
+ __unregister_chrdev(major, 0, 256, name);
+}
+
/* fs/block_dev.c */
#define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */
#define BDEVT_SIZE 10 /* Largest string for MAJ:MIN for blkdev */
@@ -2123,7 +2136,7 @@ extern struct file *do_filp_open(int dfd, const char *pathname,
int open_flag, int mode, int acc_mode);
extern int may_open(struct path *, int, int);
-extern int kernel_read(struct file *, unsigned long, char *, unsigned long);
+extern int kernel_read(struct file *, loff_t, char *, unsigned long);
extern struct file * open_exec(const char *);
/* fs/dcache.c -- generic fs support functions */
@@ -2137,7 +2150,7 @@ extern loff_t default_llseek(struct file *file, loff_t offset, int origin);
extern loff_t vfs_llseek(struct file *file, loff_t offset, int origin);
-extern struct inode * inode_init_always(struct super_block *, struct inode *);
+extern int inode_init_always(struct super_block *, struct inode *);
extern void inode_init_once(struct inode *);
extern void inode_add_to_lists(struct super_block *, struct inode *);
extern void iput(struct inode *);
@@ -2164,6 +2177,7 @@ extern void __iget(struct inode * inode);
extern void iget_failed(struct inode *);
extern void clear_inode(struct inode *);
extern void destroy_inode(struct inode *);
+extern void __destroy_inode(struct inode *);
extern struct inode *new_inode(struct super_block *);
extern int should_remove_suid(struct dentry *);
extern int file_remove_suid(struct file *);
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h
index 5c093ffc655..a81170de7f6 100644
--- a/include/linux/ftrace_event.h
+++ b/include/linux/ftrace_event.h
@@ -89,7 +89,9 @@ enum print_line_t {
TRACE_TYPE_NO_CONSUME = 3 /* Handled but ask to not consume */
};
-
+void tracing_generic_entry_update(struct trace_entry *entry,
+ unsigned long flags,
+ int pc);
struct ring_buffer_event *
trace_current_buffer_lock_reserve(int type, unsigned long len,
unsigned long flags, int pc);
@@ -119,11 +121,9 @@ struct ftrace_event_call {
void *filter;
void *mod;
-#ifdef CONFIG_EVENT_PROFILE
- atomic_t profile_count;
- int (*profile_enable)(struct ftrace_event_call *);
- void (*profile_disable)(struct ftrace_event_call *);
-#endif
+ atomic_t profile_count;
+ int (*profile_enable)(struct ftrace_event_call *);
+ void (*profile_disable)(struct ftrace_event_call *);
};
#define MAX_FILTER_PRED 32
diff --git a/include/linux/gen_stats.h b/include/linux/gen_stats.h
index 0ffa41df0ee..710e901085d 100644
--- a/include/linux/gen_stats.h
+++ b/include/linux/gen_stats.h
@@ -22,6 +22,11 @@ struct gnet_stats_basic
{
__u64 bytes;
__u32 packets;
+};
+struct gnet_stats_basic_packed
+{
+ __u64 bytes;
+ __u32 packets;
} __attribute__ ((packed));
/**
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 2723513a565..5cbc620bdfe 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -10,6 +10,7 @@
#include <asm/tlbflush.h>
struct ctl_table;
+struct user_struct;
int PageHuge(struct page *page);
@@ -146,7 +147,8 @@ static inline struct hugetlbfs_sb_info *HUGETLBFS_SB(struct super_block *sb)
extern const struct file_operations hugetlbfs_file_operations;
extern struct vm_operations_struct hugetlb_vm_ops;
-struct file *hugetlb_file_setup(const char *name, size_t, int);
+struct file *hugetlb_file_setup(const char *name, size_t size, int acct,
+ struct user_struct **user);
int hugetlb_get_quota(struct address_space *mapping, long delta);
void hugetlb_put_quota(struct address_space *mapping, long delta);
@@ -168,7 +170,7 @@ static inline void set_file_hugepages(struct file *file)
#define is_file_hugepages(file) 0
#define set_file_hugepages(file) BUG()
-#define hugetlb_file_setup(name,size,acctflag) ERR_PTR(-ENOSYS)
+#define hugetlb_file_setup(name,size,acct,user) ERR_PTR(-ENOSYS)
#endif /* !CONFIG_HUGETLBFS */
diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h
index acef2a770b6..ad27c7da879 100644
--- a/include/linux/inetdevice.h
+++ b/include/linux/inetdevice.h
@@ -82,7 +82,7 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev)
#define IN_DEV_FORWARD(in_dev) IN_DEV_CONF_GET((in_dev), FORWARDING)
#define IN_DEV_MFORWARD(in_dev) IN_DEV_ANDCONF((in_dev), MC_FORWARDING)
-#define IN_DEV_RPFILTER(in_dev) IN_DEV_ANDCONF((in_dev), RP_FILTER)
+#define IN_DEV_RPFILTER(in_dev) IN_DEV_MAXCONF((in_dev), RP_FILTER)
#define IN_DEV_SOURCE_ROUTE(in_dev) IN_DEV_ANDCONF((in_dev), \
ACCEPT_SOURCE_ROUTE)
#define IN_DEV_BOOTP_RELAY(in_dev) IN_DEV_ANDCONF((in_dev), BOOTP_RELAY)
diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h
index 7964516c695..15d5903af2d 100644
--- a/include/linux/input/matrix_keypad.h
+++ b/include/linux/input/matrix_keypad.h
@@ -15,12 +15,13 @@
#define KEY_COL(k) (((k) >> 16) & 0xff)
#define KEY_VAL(k) ((k) & 0xffff)
+#define MATRIX_SCAN_CODE(row, col, row_shift) (((row) << (row_shift)) + (col))
+
/**
* struct matrix_keymap_data - keymap for matrix keyboards
* @keymap: pointer to array of uint32 values encoded with KEY() macro
* representing keymap
* @keymap_size: number of entries (initialized) in this keymap
- * @max_keymap_size: maximum size of keymap supported by the device
*
* This structure is supposed to be used by platform code to supply
* keymaps to drivers that implement matrix-like keypads/keyboards.
@@ -28,14 +29,13 @@
struct matrix_keymap_data {
const uint32_t *keymap;
unsigned int keymap_size;
- unsigned int max_keymap_size;
};
/**
* struct matrix_keypad_platform_data - platform-dependent keypad data
* @keymap_data: pointer to &matrix_keymap_data
- * @row_gpios: array of gpio numbers reporesenting rows
- * @col_gpios: array of gpio numbers reporesenting colums
+ * @row_gpios: pointer to array of gpio numbers representing rows
+ * @col_gpios: pointer to array of gpio numbers reporesenting colums
* @num_row_gpios: actual number of row gpios used by device
* @num_col_gpios: actual number of col gpios used by device
* @col_scan_delay_us: delay, measured in microseconds, that is
@@ -48,8 +48,9 @@ struct matrix_keymap_data {
struct matrix_keypad_platform_data {
const struct matrix_keymap_data *keymap_data;
- unsigned int row_gpios[MATRIX_MAX_ROWS];
- unsigned int col_gpios[MATRIX_MAX_COLS];
+ const unsigned int *row_gpios;
+ const unsigned int *col_gpios;
+
unsigned int num_row_gpios;
unsigned int num_col_gpios;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 16713dc672e..3060bdc35ff 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -110,6 +110,7 @@ struct kvm_memory_slot {
struct kvm_kernel_irq_routing_entry {
u32 gsi;
+ u32 type;
int (*set)(struct kvm_kernel_irq_routing_entry *e,
struct kvm *kvm, int level);
union {
diff --git a/include/linux/lmb.h b/include/linux/lmb.h
index c46c89505da..2442e3f3d03 100644
--- a/include/linux/lmb.h
+++ b/include/linux/lmb.h
@@ -51,7 +51,7 @@ extern u64 __init lmb_alloc_base(u64 size,
extern u64 __init __lmb_alloc_base(u64 size,
u64 align, u64 max_addr);
extern u64 __init lmb_phys_mem_size(void);
-extern u64 __init lmb_end_of_DRAM(void);
+extern u64 lmb_end_of_DRAM(void);
extern void __init lmb_enforce_memory_limit(u64 memory_limit);
extern int __init lmb_is_reserved(u64 addr);
extern int lmb_find(struct lmb_property *res);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index ba3a7cb1eaa..9a72cc78e6b 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -34,8 +34,6 @@ extern int sysctl_legacy_va_layout;
#define sysctl_legacy_va_layout 0
#endif
-extern unsigned long mmap_min_addr;
-
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
@@ -575,19 +573,6 @@ static inline void set_page_links(struct page *page, enum zone_type zone,
}
/*
- * If a hint addr is less than mmap_min_addr change hint to be as
- * low as possible but still greater than mmap_min_addr
- */
-static inline unsigned long round_hint_to_min(unsigned long hint)
-{
- hint &= PAGE_MASK;
- if (((void *)hint != NULL) &&
- (hint < mmap_min_addr))
- return PAGE_ALIGN(mmap_min_addr);
- return hint;
-}
-
-/*
* Some inline functions in vmstat.h depend on page_zone()
*/
#include <linux/vmstat.h>
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 7acc8439d9b..0042090a4d7 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -240,8 +240,6 @@ struct mm_struct {
unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
- s8 oom_adj; /* OOM kill score adjustment (bit shift) */
-
cpumask_t cpu_vm_mask;
/* Architecture-specific MM context */
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 5675b63a063..0f32a9b6ff5 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -251,7 +251,7 @@ struct mtd_info {
static inline struct mtd_info *dev_to_mtd(struct device *dev)
{
- return dev ? container_of(dev, struct mtd_info, dev) : NULL;
+ return dev ? dev_get_drvdata(dev) : NULL;
}
static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd)
diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h
index af6dcb992bc..b70313d33ff 100644
--- a/include/linux/mtd/partitions.h
+++ b/include/linux/mtd/partitions.h
@@ -47,6 +47,8 @@ struct mtd_partition {
#define MTDPART_SIZ_FULL (0)
+struct mtd_info;
+
int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
int del_mtd_partitions(struct mtd_info *);
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index fdffb413b19..f6b90240dd4 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -473,7 +473,6 @@ extern int nfs_writepages(struct address_space *, struct writeback_control *);
extern int nfs_flush_incompatible(struct file *file, struct page *page);
extern int nfs_updatepage(struct file *, struct page *, unsigned int, unsigned int);
extern int nfs_writeback_done(struct rpc_task *, struct nfs_write_data *);
-extern void nfs_writedata_release(void *);
/*
* Try to write back everything synchronously (but check the
@@ -488,7 +487,6 @@ extern int nfs_wb_page_cancel(struct inode *inode, struct page* page);
extern int nfs_commit_inode(struct inode *, int);
extern struct nfs_write_data *nfs_commitdata_alloc(void);
extern void nfs_commit_free(struct nfs_write_data *wdata);
-extern void nfs_commitdata_release(void *wdata);
#else
static inline int
nfs_commit_inode(struct inode *inode, int how)
@@ -507,6 +505,7 @@ nfs_have_writebacks(struct inode *inode)
* Allocate nfs_write_data structures
*/
extern struct nfs_write_data *nfs_writedata_alloc(unsigned int npages);
+extern void nfs_writedata_free(struct nfs_write_data *);
/*
* linux/fs/nfs/read.c
@@ -515,7 +514,6 @@ extern int nfs_readpage(struct file *, struct page *);
extern int nfs_readpages(struct file *, struct address_space *,
struct list_head *, unsigned);
extern int nfs_readpage_result(struct rpc_task *, struct nfs_read_data *);
-extern void nfs_readdata_release(void *data);
extern int nfs_readpage_async(struct nfs_open_context *, struct inode *,
struct page *);
@@ -523,6 +521,7 @@ extern int nfs_readpage_async(struct nfs_open_context *, struct inode *,
* Allocate nfs_read_data structures
*/
extern struct nfs_read_data *nfs_readdata_alloc(unsigned int npages);
+extern void nfs_readdata_free(struct nfs_read_data *);
/*
* linux/fs/nfs3proc.c
diff --git a/include/linux/nodemask.h b/include/linux/nodemask.h
index 829b94b156f..b359c4a9ec9 100644
--- a/include/linux/nodemask.h
+++ b/include/linux/nodemask.h
@@ -82,6 +82,12 @@
* to generate slightly worse code. So use a simple one-line #define
* for node_isset(), instead of wrapping an inline inside a macro, the
* way we do the other calls.
+ *
+ * NODEMASK_SCRATCH
+ * When doing above logical AND, OR, XOR, Remap operations the callers tend to
+ * need temporary nodemask_t's on the stack. But if NODES_SHIFT is large,
+ * nodemask_t's consume too much stack space. NODEMASK_SCRATCH is a helper
+ * for such situations. See below and CPUMASK_ALLOC also.
*/
#include <linux/kernel.h>
@@ -473,4 +479,26 @@ static inline int num_node_state(enum node_states state)
#define for_each_node(node) for_each_node_state(node, N_POSSIBLE)
#define for_each_online_node(node) for_each_node_state(node, N_ONLINE)
+/*
+ * For nodemask scrach area.(See CPUMASK_ALLOC() in cpumask.h)
+ */
+
+#if NODES_SHIFT > 8 /* nodemask_t > 64 bytes */
+#define NODEMASK_ALLOC(x, m) struct x *m = kmalloc(sizeof(*m), GFP_KERNEL)
+#define NODEMASK_FREE(m) kfree(m)
+#else
+#define NODEMASK_ALLOC(x, m) struct x _m, *m = &_m
+#define NODEMASK_FREE(m)
+#endif
+
+/* A example struture for using NODEMASK_ALLOC, used in mempolicy. */
+struct nodemask_scratch {
+ nodemask_t mask1;
+ nodemask_t mask2;
+};
+
+#define NODEMASK_SCRATCH(x) NODEMASK_ALLOC(nodemask_scratch, x)
+#define NODEMASK_SCRATCH_FREE(x) NODEMASK_FREE(x)
+
+
#endif /* __LINUX_NODEMASK_H */
diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h
index bd15d7a5f5c..b53f7006cc4 100644
--- a/include/linux/perf_counter.h
+++ b/include/linux/perf_counter.h
@@ -115,27 +115,44 @@ enum perf_counter_sample_format {
PERF_SAMPLE_TID = 1U << 1,
PERF_SAMPLE_TIME = 1U << 2,
PERF_SAMPLE_ADDR = 1U << 3,
- PERF_SAMPLE_GROUP = 1U << 4,
+ PERF_SAMPLE_READ = 1U << 4,
PERF_SAMPLE_CALLCHAIN = 1U << 5,
PERF_SAMPLE_ID = 1U << 6,
PERF_SAMPLE_CPU = 1U << 7,
PERF_SAMPLE_PERIOD = 1U << 8,
PERF_SAMPLE_STREAM_ID = 1U << 9,
+ PERF_SAMPLE_RAW = 1U << 10,
- PERF_SAMPLE_MAX = 1U << 10, /* non-ABI */
+ PERF_SAMPLE_MAX = 1U << 11, /* non-ABI */
};
/*
- * Bits that can be set in attr.read_format to request that
- * reads on the counter should return the indicated quantities,
- * in increasing order of bit value, after the counter value.
+ * The format of the data returned by read() on a perf counter fd,
+ * as specified by attr.read_format:
+ *
+ * struct read_format {
+ * { u64 value;
+ * { u64 time_enabled; } && PERF_FORMAT_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_RUNNING
+ * { u64 id; } && PERF_FORMAT_ID
+ * } && !PERF_FORMAT_GROUP
+ *
+ * { u64 nr;
+ * { u64 time_enabled; } && PERF_FORMAT_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_RUNNING
+ * { u64 value;
+ * { u64 id; } && PERF_FORMAT_ID
+ * } cntr[nr];
+ * } && PERF_FORMAT_GROUP
+ * };
*/
enum perf_counter_read_format {
PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0,
PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1,
PERF_FORMAT_ID = 1U << 2,
+ PERF_FORMAT_GROUP = 1U << 3,
- PERF_FORMAT_MAX = 1U << 3, /* non-ABI */
+ PERF_FORMAT_MAX = 1U << 4, /* non-ABI */
};
#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */
@@ -181,8 +198,9 @@ struct perf_counter_attr {
freq : 1, /* use freq, not period */
inherit_stat : 1, /* per task counts */
enable_on_exec : 1, /* next exec enables */
+ task : 1, /* trace fork/exit */
- __reserved_1 : 51;
+ __reserved_1 : 50;
__u32 wakeup_events; /* wakeup every n events */
__u32 __reserved_2;
@@ -311,6 +329,15 @@ enum perf_event_type {
/*
* struct {
* struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * };
+ */
+ PERF_EVENT_EXIT = 4,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
* u64 time;
* u64 id;
* u64 stream_id;
@@ -323,6 +350,7 @@ enum perf_event_type {
* struct {
* struct perf_event_header header;
* u32 pid, ppid;
+ * u32 tid, ptid;
* };
*/
PERF_EVENT_FORK = 7,
@@ -331,10 +359,8 @@ enum perf_event_type {
* struct {
* struct perf_event_header header;
* u32 pid, tid;
- * u64 value;
- * { u64 time_enabled; } && PERF_FORMAT_ENABLED
- * { u64 time_running; } && PERF_FORMAT_RUNNING
- * { u64 parent_id; } && PERF_FORMAT_ID
+ *
+ * struct read_format values;
* };
*/
PERF_EVENT_READ = 8,
@@ -352,11 +378,24 @@ enum perf_event_type {
* { u32 cpu, res; } && PERF_SAMPLE_CPU
* { u64 period; } && PERF_SAMPLE_PERIOD
*
- * { u64 nr;
- * { u64 id, val; } cnt[nr]; } && PERF_SAMPLE_GROUP
+ * { struct read_format values; } && PERF_SAMPLE_READ
*
* { u64 nr,
* u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
+ *
+ * #
+ * # The RAW record below is opaque data wrt the ABI
+ * #
+ * # That is, the ABI doesn't make any promises wrt to
+ * # the stability of its content, it may vary depending
+ * # on event, hardware, kernel version and phase of
+ * # the moon.
+ * #
+ * # In other words, PERF_SAMPLE_RAW contents are not an ABI.
+ * #
+ *
+ * { u32 size;
+ * char data[size];}&& PERF_SAMPLE_RAW
* };
*/
PERF_EVENT_SAMPLE = 9,
@@ -402,6 +441,11 @@ struct perf_callchain_entry {
__u64 ip[PERF_MAX_STACK_DEPTH];
};
+struct perf_raw_record {
+ u32 size;
+ void *data;
+};
+
struct task_struct;
/**
@@ -670,10 +714,13 @@ struct perf_sample_data {
struct pt_regs *regs;
u64 addr;
u64 period;
+ struct perf_raw_record *raw;
};
extern int perf_counter_overflow(struct perf_counter *counter, int nmi,
struct perf_sample_data *data);
+extern void perf_counter_output(struct perf_counter *counter, int nmi,
+ struct perf_sample_data *data);
/*
* Return 1 for a software counter, 0 for a hardware counter
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 3ab08e4bb6b..0f1ea4a6695 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1198,6 +1198,7 @@ struct task_struct {
* a short time
*/
unsigned char fpu_counter;
+ s8 oomkilladj; /* OOM kill score adjustment (bit shift). */
#ifdef CONFIG_BLK_DEV_IO_TRACE
unsigned int btrace_seq;
#endif
diff --git a/include/linux/security.h b/include/linux/security.h
index 5eff459b383..1f16eea2017 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -28,6 +28,7 @@
#include <linux/resource.h>
#include <linux/sem.h>
#include <linux/shm.h>
+#include <linux/mm.h> /* PAGE_ALIGN */
#include <linux/msg.h>
#include <linux/sched.h>
#include <linux/key.h>
@@ -66,6 +67,9 @@ extern int cap_inode_setxattr(struct dentry *dentry, const char *name,
extern int cap_inode_removexattr(struct dentry *dentry, const char *name);
extern int cap_inode_need_killpriv(struct dentry *dentry);
extern int cap_inode_killpriv(struct dentry *dentry);
+extern int cap_file_mmap(struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags,
+ unsigned long addr, unsigned long addr_only);
extern int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags);
extern int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5);
@@ -92,6 +96,7 @@ extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb);
extern int cap_netlink_recv(struct sk_buff *skb, int cap);
extern unsigned long mmap_min_addr;
+extern unsigned long dac_mmap_min_addr;
/*
* Values used in the task_security_ops calls
*/
@@ -116,6 +121,21 @@ struct request_sock;
#define LSM_UNSAFE_PTRACE 2
#define LSM_UNSAFE_PTRACE_CAP 4
+/*
+ * If a hint addr is less than mmap_min_addr change hint to be as
+ * low as possible but still greater than mmap_min_addr
+ */
+static inline unsigned long round_hint_to_min(unsigned long hint)
+{
+ hint &= PAGE_MASK;
+ if (((void *)hint != NULL) &&
+ (hint < mmap_min_addr))
+ return PAGE_ALIGN(mmap_min_addr);
+ return hint;
+}
+extern int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp,
+ void __user *buffer, size_t *lenp, loff_t *ppos);
+
#ifdef CONFIG_SECURITY
struct security_mnt_opts {
@@ -2197,9 +2217,7 @@ static inline int security_file_mmap(struct file *file, unsigned long reqprot,
unsigned long addr,
unsigned long addr_only)
{
- if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO))
- return -EACCES;
- return 0;
+ return cap_file_mmap(file, reqprot, prot, flags, addr, addr_only);
}
static inline int security_file_mprotect(struct vm_area_struct *vma,
diff --git a/include/linux/tty.h b/include/linux/tty.h
index e8c6c9136c9..0d3974f59c5 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -23,7 +23,7 @@
*/
#define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */
#define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */
-#define NR_LDISCS 19
+#define NR_LDISCS 20
/* line disciplines */
#define N_TTY 0
@@ -47,6 +47,8 @@
#define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */
#define N_PPS 18 /* Pulse per Second */
+#define N_V253 19 /* Codec control over voice modem */
+
/*
* This character is the same as _POSIX_VDISABLE: it cannot be used as
* a c_cc[] character, but indicates that a particular special character
diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h
index 40f38d89677..0c4ee9b88f8 100644
--- a/include/linux/tty_ldisc.h
+++ b/include/linux/tty_ldisc.h
@@ -144,7 +144,7 @@ struct tty_ldisc_ops {
struct tty_ldisc {
struct tty_ldisc_ops *ops;
- int refcount;
+ atomic_t users;
};
#define TTY_LDISC_MAGIC 0x5403
diff --git a/include/linux/ucb1400.h b/include/linux/ucb1400.h
index ed889f4168f..ae779bb8cc0 100644
--- a/include/linux/ucb1400.h
+++ b/include/linux/ucb1400.h
@@ -73,6 +73,10 @@
#define UCB_ADC_DATA 0x68
#define UCB_ADC_DAT_VALID (1 << 15)
+
+#define UCB_FCSR 0x6c
+#define UCB_FCSR_AVE (1 << 12)
+
#define UCB_ADC_DAT_MASK 0x3ff
#define UCB_ID 0x7e
diff --git a/include/linux/wait.h b/include/linux/wait.h
index 6788e1a4d4c..cf3c2f5dba5 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -77,7 +77,14 @@ struct task_struct;
#define __WAIT_BIT_KEY_INITIALIZER(word, bit) \
{ .flags = word, .bit_nr = bit, }
-extern void init_waitqueue_head(wait_queue_head_t *q);
+extern void __init_waitqueue_head(wait_queue_head_t *q, struct lock_class_key *);
+
+#define init_waitqueue_head(q) \
+ do { \
+ static struct lock_class_key __key; \
+ \
+ __init_waitqueue_head((q), &__key); \
+ } while (0)
#ifdef CONFIG_LOCKDEP
# define __WAIT_QUEUE_HEAD_INIT_ONSTACK(name) \
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index 13e1adf55c4..6273fa97b52 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -240,6 +240,21 @@ static inline int cancel_delayed_work(struct delayed_work *work)
return ret;
}
+/*
+ * Like above, but uses del_timer() instead of del_timer_sync(). This means,
+ * if it returns 0 the timer function may be running and the queueing is in
+ * progress.
+ */
+static inline int __cancel_delayed_work(struct delayed_work *work)
+{
+ int ret;
+
+ ret = del_timer(&work->timer);
+ if (ret)
+ work_clear_pending(&work->work);
+ return ret;
+}
+
extern int cancel_delayed_work_sync(struct delayed_work *work);
/* Obsolete. use cancel_delayed_work_sync() */
diff --git a/include/net/act_api.h b/include/net/act_api.h
index 565eed8fe49..c05fd717c58 100644
--- a/include/net/act_api.h
+++ b/include/net/act_api.h
@@ -16,7 +16,7 @@ struct tcf_common {
u32 tcfc_capab;
int tcfc_action;
struct tcf_t tcfc_tm;
- struct gnet_stats_basic tcfc_bstats;
+ struct gnet_stats_basic_packed tcfc_bstats;
struct gnet_stats_queue tcfc_qstats;
struct gnet_stats_rate_est tcfc_rate_est;
spinlock_t tcfc_lock;
diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h
index 80072611d26..c274993234e 100644
--- a/include/net/bluetooth/rfcomm.h
+++ b/include/net/bluetooth/rfcomm.h
@@ -355,7 +355,17 @@ struct rfcomm_dev_list_req {
};
int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
+
+#ifdef CONFIG_BT_RFCOMM_TTY
int rfcomm_init_ttys(void);
void rfcomm_cleanup_ttys(void);
-
+#else
+static inline int rfcomm_init_ttys(void)
+{
+ return 0;
+}
+static inline void rfcomm_cleanup_ttys(void)
+{
+}
+#endif
#endif /* __RFCOMM_H */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 1a21895b732..d1892d66701 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -979,6 +979,10 @@ struct cfg80211_ops {
* channels at a later time. This can be used for devices which do not
* have calibration information gauranteed for frequencies or settings
* outside of its regulatory domain.
+ * @disable_beacon_hints: enable this if your driver needs to ensure that
+ * passive scan flags and beaconing flags may not be lifted by cfg80211
+ * due to regulatory beacon hints. For more information on beacon
+ * hints read the documenation for regulatory_hint_found_beacon()
* @reg_notifier: the driver's regulatory notification callback
* @regd: the driver's regulatory domain, if one was requested via
* the regulatory_hint() API. This can be used by the driver
@@ -1004,6 +1008,7 @@ struct wiphy {
bool custom_regulatory;
bool strict_regulatory;
+ bool disable_beacon_hints;
enum cfg80211_signal_type signal_type;
diff --git a/include/net/gen_stats.h b/include/net/gen_stats.h
index d136b5240ef..c1488553e34 100644
--- a/include/net/gen_stats.h
+++ b/include/net/gen_stats.h
@@ -28,7 +28,7 @@ extern int gnet_stats_start_copy_compat(struct sk_buff *skb, int type,
spinlock_t *lock, struct gnet_dump *d);
extern int gnet_stats_copy_basic(struct gnet_dump *d,
- struct gnet_stats_basic *b);
+ struct gnet_stats_basic_packed *b);
extern int gnet_stats_copy_rate_est(struct gnet_dump *d,
struct gnet_stats_rate_est *r);
extern int gnet_stats_copy_queue(struct gnet_dump *d,
@@ -37,14 +37,14 @@ extern int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len);
extern int gnet_stats_finish_copy(struct gnet_dump *d);
-extern int gen_new_estimator(struct gnet_stats_basic *bstats,
+extern int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
struct gnet_stats_rate_est *rate_est,
spinlock_t *stats_lock, struct nlattr *opt);
-extern void gen_kill_estimator(struct gnet_stats_basic *bstats,
+extern void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,
struct gnet_stats_rate_est *rate_est);
-extern int gen_replace_estimator(struct gnet_stats_basic *bstats,
+extern int gen_replace_estimator(struct gnet_stats_basic_packed *bstats,
struct gnet_stats_rate_est *rate_est,
spinlock_t *stats_lock, struct nlattr *opt);
-extern bool gen_estimator_active(const struct gnet_stats_basic *bstats,
+extern bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats,
const struct gnet_stats_rate_est *rate_est);
#endif
diff --git a/include/net/netfilter/xt_rateest.h b/include/net/netfilter/xt_rateest.h
index 65d594dffbf..ddbf37e1961 100644
--- a/include/net/netfilter/xt_rateest.h
+++ b/include/net/netfilter/xt_rateest.h
@@ -8,7 +8,7 @@ struct xt_rateest {
spinlock_t lock;
struct gnet_estimator params;
struct gnet_stats_rate_est rstats;
- struct gnet_stats_basic bstats;
+ struct gnet_stats_basic_packed bstats;
};
extern struct xt_rateest *xt_rateest_lookup(const char *name);
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index 964ffa0d881..5482e9582f5 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -72,7 +72,7 @@ struct Qdisc
*/
unsigned long state;
struct sk_buff_head q;
- struct gnet_stats_basic bstats;
+ struct gnet_stats_basic_packed bstats;
struct gnet_stats_queue qstats;
};
diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h
index 251fc1cd500..3dae3f799b9 100644
--- a/include/sound/ac97_codec.h
+++ b/include/sound/ac97_codec.h
@@ -32,6 +32,9 @@
#include "control.h"
#include "info.h"
+/* maximum number of devices on the AC97 bus */
+#define AC97_BUS_MAX_DEVICES 4
+
/*
* AC'97 codec registers
*/
@@ -642,4 +645,10 @@ int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime);
/* ad hoc AC97 device driver access */
extern struct bus_type ac97_bus_type;
+/* AC97 platform_data adding function */
+static inline void snd_ac97_dev_add_pdata(struct snd_ac97 *ac97, void *data)
+{
+ ac97->dev.platform_data = data;
+}
+
#endif /* __SOUND_AC97_CODEC_H */
diff --git a/include/sound/asound.h b/include/sound/asound.h
index 82aed3f4753..1f57bb92eb5 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -138,7 +138,7 @@ struct snd_hwdep_dsp_image {
* *
*****************************************************************************/
-#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 9)
+#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 10)
typedef unsigned long snd_pcm_uframes_t;
typedef signed long snd_pcm_sframes_t;
diff --git a/include/sound/core.h b/include/sound/core.h
index 309cb9659a0..a61499c22b0 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -93,15 +93,6 @@ struct snd_device {
#define snd_device(n) list_entry(n, struct snd_device, list)
-/* monitor files for graceful shutdown (hotplug) */
-
-struct snd_monitor_file {
- struct file *file;
- const struct file_operations *disconnected_f_op;
- struct list_head shutdown_list; /* still need to shutdown */
- struct list_head list; /* link of monitor files */
-};
-
/* main structure for soundcard */
struct snd_card {
@@ -311,9 +302,7 @@ int snd_component_add(struct snd_card *card, const char *component);
int snd_card_file_add(struct snd_card *card, struct file *file);
int snd_card_file_remove(struct snd_card *card, struct file *file);
-#ifndef snd_card_set_dev
#define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
-#endif
/* device.c */
@@ -340,18 +329,17 @@ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size);
struct resource;
void release_and_free_resource(struct resource *res);
-#ifdef CONFIG_SND_VERBOSE_PRINTK
-void snd_verbose_printk(const char *file, int line, const char *format, ...)
- __attribute__ ((format (printf, 3, 4)));
-#endif
-#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
-void snd_verbose_printd(const char *file, int line, const char *format, ...)
- __attribute__ ((format (printf, 3, 4)));
-#endif
-
/* --- */
-#ifdef CONFIG_SND_VERBOSE_PRINTK
+#if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK)
+void __snd_printk(unsigned int level, const char *file, int line,
+ const char *format, ...)
+ __attribute__ ((format (printf, 4, 5)));
+#else
+#define __snd_printk(level, file, line, format, args...) \
+ printk(format, ##args)
+#endif
+
/**
* snd_printk - printk wrapper
* @fmt: format string
@@ -360,15 +348,9 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
* when configured with CONFIG_SND_VERBOSE_PRINTK.
*/
#define snd_printk(fmt, args...) \
- snd_verbose_printk(__FILE__, __LINE__, fmt ,##args)
-#else
-#define snd_printk(fmt, args...) \
- printk(fmt ,##args)
-#endif
+ __snd_printk(0, __FILE__, __LINE__, fmt, ##args)
#ifdef CONFIG_SND_DEBUG
-
-#ifdef CONFIG_SND_VERBOSE_PRINTK
/**
* snd_printd - debug printk
* @fmt: format string
@@ -377,11 +359,7 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
* Ignored when CONFIG_SND_DEBUG is not set.
*/
#define snd_printd(fmt, args...) \
- snd_verbose_printd(__FILE__, __LINE__, fmt ,##args)
-#else
-#define snd_printd(fmt, args...) \
- printk(fmt ,##args)
-#endif
+ __snd_printk(1, __FILE__, __LINE__, fmt, ##args)
/**
* snd_BUG - give a BUG warning message and stack trace
@@ -428,9 +406,10 @@ static inline int __snd_bug_on(int cond)
* Works like snd_printk() for debugging purposes.
* Ignored when CONFIG_SND_DEBUG_VERBOSE is not set.
*/
-#define snd_printdd(format, args...) snd_printk(format, ##args)
+#define snd_printdd(format, args...) \
+ __snd_printk(2, __FILE__, __LINE__, format, ##args)
#else
-#define snd_printdd(format, args...) /* nothing */
+#define snd_printdd(format, args...) do { } while (0)
#endif
@@ -438,12 +417,10 @@ static inline int __snd_bug_on(int cond)
/* for easier backward-porting */
#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE)
-#ifndef gameport_set_dev_parent
#define gameport_set_dev_parent(gp,xdev) ((gp)->dev.parent = (xdev))
#define gameport_set_port_data(gp,r) ((gp)->port_data = (r))
#define gameport_get_port_data(gp) (gp)->port_data
#endif
-#endif
/* PCI quirk list helper */
struct snd_pci_quirk {
diff --git a/include/sound/info.h b/include/sound/info.h
index 7c2ee1a21b0..112e8949e1a 100644
--- a/include/sound/info.h
+++ b/include/sound/info.h
@@ -110,13 +110,13 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer);
static inline void snd_card_info_read_oss(struct snd_info_buffer *buffer) {}
#endif
-int snd_iprintf(struct snd_info_buffer *buffer, char *fmt, ...) \
+int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...) \
__attribute__ ((format (printf, 2, 3)));
int snd_info_init(void);
int snd_info_done(void);
int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len);
-char *snd_info_get_str(char *dest, char *src, int len);
+const char *snd_info_get_str(char *dest, const char *src, int len);
struct snd_info_entry *snd_info_create_module_entry(struct module *module,
const char *name,
struct snd_info_entry *parent);
diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h
index 7ccce94a525..c4250621264 100644
--- a/include/sound/memalloc.h
+++ b/include/sound/memalloc.h
@@ -47,7 +47,11 @@ struct snd_dma_device {
#define SNDRV_DMA_TYPE_UNKNOWN 0 /* not defined */
#define SNDRV_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */
#define SNDRV_DMA_TYPE_DEV 2 /* generic device continuous */
+#ifdef CONFIG_SND_DMA_SGBUF
#define SNDRV_DMA_TYPE_DEV_SG 3 /* generic device SG-buffer */
+#else
+#define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_DEV /* no SG-buf support */
+#endif
/*
* info for buffer allocation
@@ -60,6 +64,7 @@ struct snd_dma_buffer {
void *private_data; /* private for allocator; don't touch */
};
+#ifdef CONFIG_SND_DMA_SGBUF
/*
* Scatter-Gather generic device pages
*/
@@ -107,6 +112,7 @@ static inline void *snd_sgbuf_get_ptr(struct snd_sg_buf *sgbuf, size_t offset)
{
return sgbuf->table[offset >> PAGE_SHIFT].buf + offset % PAGE_SIZE;
}
+#endif /* CONFIG_SND_DMA_SGBUF */
/* allocate/release a buffer */
int snd_dma_alloc_pages(int type, struct device *dev, size_t size,
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 23893523dc8..de6d981de5d 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -902,6 +902,7 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size);
int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream);
+#ifdef CONFIG_SND_DMA_SGBUF
/*
* SG-buffer handling
*/
@@ -927,6 +928,28 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream,
unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
unsigned int ofs, unsigned int size);
+#else /* !SND_DMA_SGBUF */
+/*
+ * fake using a continuous buffer
+ */
+static inline dma_addr_t
+snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs)
+{
+ return substream->runtime->dma_addr + ofs;
+}
+
+static inline void *
+snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
+{
+ return substream->runtime->dma_area + ofs;
+}
+
+#define snd_pcm_sgbuf_ops_page NULL
+
+#define snd_pcm_sgbuf_get_chunk_size(subs, ofs, size) (size)
+
+#endif /* SND_DMA_SGBUF */
+
/* handle mmap counter - PCM mmap callback should handle this counter properly */
static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area)
{
@@ -965,4 +988,6 @@ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max)
#define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
+const char *snd_pcm_format_name(snd_pcm_format_t format);
+
#endif /* __SOUND_PCM_H */
diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h
new file mode 100644
index 00000000000..c0227361a87
--- /dev/null
+++ b/include/sound/sh_fsi.h
@@ -0,0 +1,83 @@
+#ifndef __SOUND_FSI_H
+#define __SOUND_FSI_H
+
+/*
+ * Fifo-attached Serial Interface (FSI) support for SH7724
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * 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.
+ */
+
+/* flags format
+
+ * 0xABCDEEFF
+ *
+ * A: channel size for TDM (input)
+ * B: channel size for TDM (ooutput)
+ * C: inversion
+ * D: mode
+ * E: input format
+ * F: output format
+ */
+
+#include <linux/clk.h>
+#include <sound/soc.h>
+
+/* TDM channel */
+#define SH_FSI_SET_CH_I(x) ((x & 0xF) << 28)
+#define SH_FSI_SET_CH_O(x) ((x & 0xF) << 24)
+
+#define SH_FSI_CH_IMASK 0xF0000000
+#define SH_FSI_CH_OMASK 0x0F000000
+#define SH_FSI_GET_CH_I(x) ((x & SH_FSI_CH_IMASK) >> 28)
+#define SH_FSI_GET_CH_O(x) ((x & SH_FSI_CH_OMASK) >> 24)
+
+/* clock inversion */
+#define SH_FSI_INVERSION_MASK 0x00F00000
+#define SH_FSI_LRM_INV (1 << 20)
+#define SH_FSI_BRM_INV (1 << 21)
+#define SH_FSI_LRS_INV (1 << 22)
+#define SH_FSI_BRS_INV (1 << 23)
+
+/* mode */
+#define SH_FSI_MODE_MASK 0x000F0000
+#define SH_FSI_IN_SLAVE_MODE (1 << 16) /* default master mode */
+#define SH_FSI_OUT_SLAVE_MODE (1 << 17) /* default master mode */
+
+/* DI format */
+#define SH_FSI_FMT_MASK 0x000000FF
+#define SH_FSI_IFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8)
+#define SH_FSI_OFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 0)
+#define SH_FSI_GET_IFMT(x) ((x >> 8) & SH_FSI_FMT_MASK)
+#define SH_FSI_GET_OFMT(x) ((x >> 0) & SH_FSI_FMT_MASK)
+
+#define SH_FSI_FMT_MONO (1 << 0)
+#define SH_FSI_FMT_MONO_DELAY (1 << 1)
+#define SH_FSI_FMT_PCM (1 << 2)
+#define SH_FSI_FMT_I2S (1 << 3)
+#define SH_FSI_FMT_TDM (1 << 4)
+#define SH_FSI_FMT_TDM_DELAY (1 << 5)
+
+#define SH_FSI_IFMT_TDM_CH(x) \
+ (SH_FSI_IFMT(TDM) | SH_FSI_SET_CH_I(x))
+#define SH_FSI_IFMT_TDM_DELAY_CH(x) \
+ (SH_FSI_IFMT(TDM_DELAY) | SH_FSI_SET_CH_I(x))
+
+#define SH_FSI_OFMT_TDM_CH(x) \
+ (SH_FSI_OFMT(TDM) | SH_FSI_SET_CH_O(x))
+#define SH_FSI_OFMT_TDM_DELAY_CH(x) \
+ (SH_FSI_OFMT(TDM_DELAY) | SH_FSI_SET_CH_O(x))
+
+struct sh_fsi_platform_info {
+ unsigned long porta_flags;
+ unsigned long portb_flags;
+};
+
+extern struct snd_soc_dai fsi_soc_dai[2];
+extern struct snd_soc_platform fsi_soc_platform;
+
+#endif /* __SOUND_FSI_H */
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 352d7eee9b6..97ca9af414d 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -27,8 +27,8 @@ struct snd_pcm_substream;
#define SND_SOC_DAIFMT_I2S 0 /* I2S mode */
#define SND_SOC_DAIFMT_RIGHT_J 1 /* Right Justified mode */
#define SND_SOC_DAIFMT_LEFT_J 2 /* Left Justified mode */
-#define SND_SOC_DAIFMT_DSP_A 3 /* L data msb after FRM LRC */
-#define SND_SOC_DAIFMT_DSP_B 4 /* L data msb during FRM LRC */
+#define SND_SOC_DAIFMT_DSP_A 3 /* L data MSB after FRM LRC */
+#define SND_SOC_DAIFMT_DSP_B 4 /* L data MSB during FRM LRC */
#define SND_SOC_DAIFMT_AC97 5 /* AC97 */
/* left and right justified also known as MSB and LSB respectively */
@@ -38,7 +38,7 @@ struct snd_pcm_substream;
/*
* DAI Clock gating.
*
- * DAI bit clocks can be be gated (disabled) when not the DAI is not
+ * DAI bit clocks can be be gated (disabled) when the DAI is not
* sending or receiving PCM data in a frame. This can be used to save power.
*/
#define SND_SOC_DAIFMT_CONT (0 << 4) /* continuous clock */
@@ -51,21 +51,21 @@ struct snd_pcm_substream;
* format.
*/
#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bit clock + frame */
-#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal bclk + inv frm */
-#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert bclk + nor frm */
-#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert bclk + frm */
+#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal BCLK + inv FRM */
+#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert BCLK + nor FRM */
+#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert BCLK + FRM */
/*
* DAI hardware clock masters.
*
* This is wrt the codec, the inverse is true for the interface
- * i.e. if the codec is clk and frm master then the interface is
+ * i.e. if the codec is clk and FRM master then the interface is
* clk and frame slave.
*/
-#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & frm master */
-#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & frm master */
+#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & FRM master */
+#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & FRM master */
#define SND_SOC_DAIFMT_CBM_CFS (2 << 12) /* codec clk master & frame slave */
-#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & frm slave */
+#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & FRM slave */
#define SND_SOC_DAIFMT_FORMAT_MASK 0x000f
#define SND_SOC_DAIFMT_CLOCK_MASK 0x00f0
@@ -78,7 +78,13 @@ struct snd_pcm_substream;
#define SND_SOC_CLOCK_IN 0
#define SND_SOC_CLOCK_OUT 1
-#define SND_SOC_STD_AC97_FMTS (SNDRV_PCM_FMTBIT_S16_LE |\
+#define SND_SOC_STD_AC97_FMTS (SNDRV_PCM_FMTBIT_S8 |\
+ SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S16_BE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S20_3BE |\
+ SNDRV_PCM_FMTBIT_S24_3LE |\
+ SNDRV_PCM_FMTBIT_S24_3BE |\
SNDRV_PCM_FMTBIT_S32_LE |\
SNDRV_PCM_FMTBIT_S32_BE)
@@ -106,7 +112,7 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
- unsigned int mask, int slots);
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
@@ -116,12 +122,12 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
/*
* Digital Audio Interface.
*
- * Describes the Digital Audio Interface in terms of it's ALSA, DAI and AC97
- * operations an capabilities. Codec and platfom drivers will register a this
+ * Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97
+ * operations and capabilities. Codec and platform drivers will register this
* structure for every DAI they have.
*
* This structure covers the clocking, formating and ALSA operations for each
- * interface a
+ * interface.
*/
struct snd_soc_dai_ops {
/*
@@ -140,7 +146,8 @@ struct snd_soc_dai_ops {
*/
int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
int (*set_tdm_slot)(struct snd_soc_dai *dai,
- unsigned int mask, int slots);
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width);
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
/*
@@ -179,6 +186,7 @@ struct snd_soc_dai {
int ac97_control;
struct device *dev;
+ void *ac97_pdata; /* platform_data for the ac97 codec */
/* DAI callbacks */
int (*probe)(struct platform_device *pdev,
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index ec8a45f9a06..c1410e3191e 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -137,6 +137,12 @@
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
/* stream domain */
+#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \
+{ .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
+ .reg = wreg, .shift = wshift, .invert = winvert }
+#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \
+{ .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
+ .reg = wreg, .shift = wshift, .invert = winvert }
#define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
.shift = wshift, .invert = winvert}
@@ -279,9 +285,11 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
/* dapm events */
int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
int event);
+void snd_soc_dapm_shutdown(struct snd_soc_device *socdev);
/* dapm sys fs - used by the core */
int snd_soc_dapm_sys_add(struct device *dev);
+void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec);
/* dapm audio pin control and status */
int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin);
@@ -311,6 +319,8 @@ enum snd_soc_dapm_type {
snd_soc_dapm_pre, /* machine specific pre widget - exec first */
snd_soc_dapm_post, /* machine specific post widget - exec last */
snd_soc_dapm_supply, /* power/clock supply */
+ snd_soc_dapm_aif_in, /* audio interface input */
+ snd_soc_dapm_aif_out, /* audio interface output */
};
/*
diff --git a/include/sound/soc.h b/include/sound/soc.h
index cf6111d72b1..475cb7ed6be 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -135,6 +135,28 @@
.info = snd_soc_info_volsw, \
.get = xhandler_get, .put = xhandler_put, \
.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
+#define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\
+ xhandler_get, xhandler_put, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct soc_mixer_control) \
+ {.reg = xreg, .shift = shift_left, .rshift = shift_right, \
+ .max = xmax, .invert = xinvert} }
+#define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\
+ xhandler_get, xhandler_put, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw_2r, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct soc_mixer_control) \
+ {.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+ .max = xmax, .invert = xinvert} }
#define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_bool_ext, \
@@ -183,14 +205,28 @@ struct snd_soc_jack_gpio;
#endif
typedef int (*hw_write_t)(void *,const char* ,int);
-typedef int (*hw_read_t)(void *,char* ,int);
extern struct snd_ac97_bus_ops soc_ac97_ops;
+enum snd_soc_control_type {
+ SND_SOC_CUSTOM,
+ SND_SOC_I2C,
+ SND_SOC_SPI,
+};
+
int snd_soc_register_platform(struct snd_soc_platform *platform);
void snd_soc_unregister_platform(struct snd_soc_platform *platform);
int snd_soc_register_codec(struct snd_soc_codec *codec);
void snd_soc_unregister_codec(struct snd_soc_codec *codec);
+int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg);
+int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
+ int addr_bits, int data_bits,
+ enum snd_soc_control_type control);
+
+#ifdef CONFIG_PM
+int snd_soc_suspend_device(struct device *dev);
+int snd_soc_resume_device(struct device *dev);
+#endif
/* pcm <-> DAI connect */
void snd_soc_free_pcms(struct snd_soc_device *socdev);
@@ -216,9 +252,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
/* codec register bit access */
int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
- unsigned short mask, unsigned short value);
+ unsigned int mask, unsigned int value);
int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
- unsigned short mask, unsigned short value);
+ unsigned int mask, unsigned int value);
int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
struct snd_ac97_bus_ops *ops, int num);
@@ -356,8 +392,10 @@ struct snd_soc_codec {
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
int (*display_register)(struct snd_soc_codec *, char *,
size_t, unsigned int);
+ int (*volatile_register)(unsigned int);
+ int (*readable_register)(unsigned int);
hw_write_t hw_write;
- hw_read_t hw_read;
+ unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
void *reg_cache;
short reg_cache_size;
short reg_cache_step;
@@ -369,8 +407,6 @@ struct snd_soc_codec {
enum snd_soc_bias_level bias_level;
enum snd_soc_bias_level suspend_bias_level;
struct delayed_work delayed_work;
- struct list_head up_list;
- struct list_head down_list;
/* codec DAI's */
struct snd_soc_dai *dai;
@@ -379,6 +415,7 @@ struct snd_soc_codec {
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_reg;
struct dentry *debugfs_pop_time;
+ struct dentry *debugfs_dapm;
#endif
};
diff --git a/include/sound/tlv.h b/include/sound/tlv.h
index d136ea2181e..9fd5b19ccf5 100644
--- a/include/sound/tlv.h
+++ b/include/sound/tlv.h
@@ -35,6 +35,8 @@
#define SNDRV_CTL_TLVT_DB_SCALE 1 /* dB scale */
#define SNDRV_CTL_TLVT_DB_LINEAR 2 /* linear volume */
#define SNDRV_CTL_TLVT_DB_RANGE 3 /* dB range container */
+#define SNDRV_CTL_TLVT_DB_MINMAX 4 /* dB scale with min/max */
+#define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5 /* dB scale with min/max with mute */
#define TLV_DB_SCALE_ITEM(min, step, mute) \
SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int), \
@@ -42,6 +44,18 @@
#define DECLARE_TLV_DB_SCALE(name, min, step, mute) \
unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) }
+/* dB scale specified with min/max values instead of step */
+#define TLV_DB_MINMAX_ITEM(min_dB, max_dB) \
+ SNDRV_CTL_TLVT_DB_MINMAX, 2 * sizeof(unsigned int), \
+ (min_dB), (max_dB)
+#define TLV_DB_MINMAX_MUTE_ITEM(min_dB, max_dB) \
+ SNDRV_CTL_TLVT_DB_MINMAX_MUTE, 2 * sizeof(unsigned int), \
+ (min_dB), (max_dB)
+#define DECLARE_TLV_DB_MINMAX(name, min_dB, max_dB) \
+ unsigned int name[] = { TLV_DB_MINMAX_ITEM(min_dB, max_dB) }
+#define DECLARE_TLV_DB_MINMAX_MUTE(name, min_dB, max_dB) \
+ unsigned int name[] = { TLV_DB_MINMAX_MUTE_ITEM(min_dB, max_dB) }
+
/* linear volume between min_dB and max_dB (.01dB unit) */
#define TLV_DB_LINEAR_ITEM(min_dB, max_dB) \
SNDRV_CTL_TLVT_DB_LINEAR, 2 * sizeof(unsigned int), \
diff --git a/include/sound/uda1380.h b/include/sound/uda1380.h
new file mode 100644
index 00000000000..381319c7000
--- /dev/null
+++ b/include/sound/uda1380.h
@@ -0,0 +1,22 @@
+/*
+ * UDA1380 ALSA SoC Codec driver
+ *
+ * Copyright 2009 Philipp Zabel
+ *
+ * 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 __UDA1380_H
+#define __UDA1380_H
+
+struct uda1380_platform_data {
+ int gpio_power;
+ int gpio_reset;
+ int dac_clk;
+#define UDA1380_DAC_CLK_SYSCLK 0
+#define UDA1380_DAC_CLK_WSPLL 1
+};
+
+#endif /* __UDA1380_H */
diff --git a/include/sound/version.h b/include/sound/version.h
index 456f1359e1c..22939142dd2 100644
--- a/include/sound/version.h
+++ b/include/sound/version.h
@@ -1,3 +1,3 @@
/* include/version.h */
-#define CONFIG_SND_VERSION "1.0.20"
+#define CONFIG_SND_VERSION "1.0.21"
#define CONFIG_SND_DATE ""
diff --git a/include/sound/wm8993.h b/include/sound/wm8993.h
new file mode 100644
index 00000000000..9c661f2f8cd
--- /dev/null
+++ b/include/sound/wm8993.h
@@ -0,0 +1,44 @@
+/*
+ * linux/sound/wm8993.h -- Platform data for WM8993
+ *
+ * Copyright 2009 Wolfson Microelectronics. PLC.
+ *
+ * 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 __LINUX_SND_WM8993_H
+#define __LINUX_SND_WM8993_H
+
+/* Note that EQ1 only contains the enable/disable bit so will be
+ ignored but is included for simplicity.
+ */
+struct wm8993_retune_mobile_setting {
+ const char *name;
+ unsigned int rate;
+ u16 config[24];
+};
+
+struct wm8993_platform_data {
+ struct wm8993_retune_mobile_setting *retune_configs;
+ int num_retune_configs;
+
+ /* LINEOUT can be differential or single ended */
+ unsigned int lineout1_diff:1;
+ unsigned int lineout2_diff:1;
+
+ /* Common mode feedback */
+ unsigned int lineout1fb:1;
+ unsigned int lineout2fb:1;
+
+ /* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
+ unsigned int micbias1_lvl:1;
+ unsigned int micbias2_lvl:1;
+
+ /* Jack detect threashold levels, see datasheet for values */
+ unsigned int jd_scthr:2;
+ unsigned int jd_thr:2;
+};
+
+#endif
diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h
index 1867553c61e..f64fbaae781 100644
--- a/include/trace/ftrace.h
+++ b/include/trace/ftrace.h
@@ -144,6 +144,9 @@
#undef TP_fast_assign
#define TP_fast_assign(args...) args
+#undef TP_perf_assign
+#define TP_perf_assign(args...)
+
#undef TRACE_EVENT
#define TRACE_EVENT(call, proto, args, tstruct, func, print) \
static int \
@@ -345,6 +348,56 @@ static inline int ftrace_get_offsets_##call( \
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
+#ifdef CONFIG_EVENT_PROFILE
+
+/*
+ * Generate the functions needed for tracepoint perf_counter support.
+ *
+ * NOTE: The insertion profile callback (ftrace_profile_<call>) is defined later
+ *
+ * static int ftrace_profile_enable_<call>(struct ftrace_event_call *event_call)
+ * {
+ * int ret = 0;
+ *
+ * if (!atomic_inc_return(&event_call->profile_count))
+ * ret = register_trace_<call>(ftrace_profile_<call>);
+ *
+ * return ret;
+ * }
+ *
+ * static void ftrace_profile_disable_<call>(struct ftrace_event_call *event_call)
+ * {
+ * if (atomic_add_negative(-1, &event->call->profile_count))
+ * unregister_trace_<call>(ftrace_profile_<call>);
+ * }
+ *
+ */
+
+#undef TRACE_EVENT
+#define TRACE_EVENT(call, proto, args, tstruct, assign, print) \
+ \
+static void ftrace_profile_##call(proto); \
+ \
+static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \
+{ \
+ int ret = 0; \
+ \
+ if (!atomic_inc_return(&event_call->profile_count)) \
+ ret = register_trace_##call(ftrace_profile_##call); \
+ \
+ return ret; \
+} \
+ \
+static void ftrace_profile_disable_##call(struct ftrace_event_call *event_call)\
+{ \
+ if (atomic_add_negative(-1, &event_call->profile_count)) \
+ unregister_trace_##call(ftrace_profile_##call); \
+}
+
+#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
+
+#endif
+
/*
* Stage 4 of the trace events.
*
@@ -447,28 +500,6 @@ static inline int ftrace_get_offsets_##call( \
#define TP_FMT(fmt, args...) fmt "\n", ##args
#ifdef CONFIG_EVENT_PROFILE
-#define _TRACE_PROFILE(call, proto, args) \
-static void ftrace_profile_##call(proto) \
-{ \
- extern void perf_tpcounter_event(int); \
- perf_tpcounter_event(event_##call.id); \
-} \
- \
-static int ftrace_profile_enable_##call(struct ftrace_event_call *event_call) \
-{ \
- int ret = 0; \
- \
- if (!atomic_inc_return(&event_call->profile_count)) \
- ret = register_trace_##call(ftrace_profile_##call); \
- \
- return ret; \
-} \
- \
-static void ftrace_profile_disable_##call(struct ftrace_event_call *event_call)\
-{ \
- if (atomic_add_negative(-1, &event_call->profile_count)) \
- unregister_trace_##call(ftrace_profile_##call); \
-}
#define _TRACE_PROFILE_INIT(call) \
.profile_count = ATOMIC_INIT(-1), \
@@ -476,7 +507,6 @@ static void ftrace_profile_disable_##call(struct ftrace_event_call *event_call)\
.profile_disable = ftrace_profile_disable_##call,
#else
-#define _TRACE_PROFILE(call, proto, args)
#define _TRACE_PROFILE_INIT(call)
#endif
@@ -502,7 +532,6 @@ static void ftrace_profile_disable_##call(struct ftrace_event_call *event_call)\
#undef TRACE_EVENT
#define TRACE_EVENT(call, proto, args, tstruct, assign, print) \
-_TRACE_PROFILE(call, PARAMS(proto), PARAMS(args)) \
\
static struct ftrace_event_call event_##call; \
\
@@ -586,6 +615,110 @@ __attribute__((section("_ftrace_events"))) event_##call = { \
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
-#undef _TRACE_PROFILE
+/*
+ * Define the insertion callback to profile events
+ *
+ * The job is very similar to ftrace_raw_event_<call> except that we don't
+ * insert in the ring buffer but in a perf counter.
+ *
+ * static void ftrace_profile_<call>(proto)
+ * {
+ * struct ftrace_data_offsets_<call> __maybe_unused __data_offsets;
+ * struct ftrace_event_call *event_call = &event_<call>;
+ * extern void perf_tpcounter_event(int, u64, u64, void *, int);
+ * struct ftrace_raw_##call *entry;
+ * u64 __addr = 0, __count = 1;
+ * unsigned long irq_flags;
+ * int __entry_size;
+ * int __data_size;
+ * int pc;
+ *
+ * local_save_flags(irq_flags);
+ * pc = preempt_count();
+ *
+ * __data_size = ftrace_get_offsets_<call>(&__data_offsets, args);
+ *
+ * // Below we want to get the aligned size by taking into account
+ * // the u32 field that will later store the buffer size
+ * __entry_size = ALIGN(__data_size + sizeof(*entry) + sizeof(u32),
+ * sizeof(u64));
+ * __entry_size -= sizeof(u32);
+ *
+ * do {
+ * char raw_data[__entry_size]; <- allocate our sample in the stack
+ * struct trace_entry *ent;
+ *
+ * zero dead bytes from alignment to avoid stack leak to userspace:
+ *
+ * *(u64 *)(&raw_data[__entry_size - sizeof(u64)]) = 0ULL;
+ * entry = (struct ftrace_raw_<call> *)raw_data;
+ * ent = &entry->ent;
+ * tracing_generic_entry_update(ent, irq_flags, pc);
+ * ent->type = event_call->id;
+ *
+ * <tstruct> <- do some jobs with dynamic arrays
+ *
+ * <assign> <- affect our values
+ *
+ * perf_tpcounter_event(event_call->id, __addr, __count, entry,
+ * __entry_size); <- submit them to perf counter
+ * } while (0);
+ *
+ * }
+ */
+
+#ifdef CONFIG_EVENT_PROFILE
+
+#undef __perf_addr
+#define __perf_addr(a) __addr = (a)
+
+#undef __perf_count
+#define __perf_count(c) __count = (c)
+
+#undef TRACE_EVENT
+#define TRACE_EVENT(call, proto, args, tstruct, assign, print) \
+static void ftrace_profile_##call(proto) \
+{ \
+ struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\
+ struct ftrace_event_call *event_call = &event_##call; \
+ extern void perf_tpcounter_event(int, u64, u64, void *, int); \
+ struct ftrace_raw_##call *entry; \
+ u64 __addr = 0, __count = 1; \
+ unsigned long irq_flags; \
+ int __entry_size; \
+ int __data_size; \
+ int pc; \
+ \
+ local_save_flags(irq_flags); \
+ pc = preempt_count(); \
+ \
+ __data_size = ftrace_get_offsets_##call(&__data_offsets, args); \
+ __entry_size = ALIGN(__data_size + sizeof(*entry) + sizeof(u32),\
+ sizeof(u64)); \
+ __entry_size -= sizeof(u32); \
+ \
+ do { \
+ char raw_data[__entry_size]; \
+ struct trace_entry *ent; \
+ \
+ *(u64 *)(&raw_data[__entry_size - sizeof(u64)]) = 0ULL; \
+ entry = (struct ftrace_raw_##call *)raw_data; \
+ ent = &entry->ent; \
+ tracing_generic_entry_update(ent, irq_flags, pc); \
+ ent->type = event_call->id; \
+ \
+ tstruct \
+ \
+ { assign; } \
+ \
+ perf_tpcounter_event(event_call->id, __addr, __count, entry,\
+ __entry_size); \
+ } while (0); \
+ \
+}
+
+#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
+#endif /* CONFIG_EVENT_PROFILE */
+
#undef _TRACE_PROFILE_INIT
diff --git a/init/Kconfig b/init/Kconfig
index cb2c0927022..3f7e60995c8 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -940,6 +940,7 @@ menu "Performance Counters"
config PERF_COUNTERS
bool "Kernel Performance Counters"
+ default y if PROFILING
depends on HAVE_PERF_COUNTERS
select ANON_INODES
help
@@ -961,9 +962,17 @@ config PERF_COUNTERS
Say Y if unsure.
config EVENT_PROFILE
- bool "Tracepoint profile sources"
+ bool "Tracepoint profiling sources"
depends on PERF_COUNTERS && EVENT_TRACING
default y
+ help
+ Allow the use of tracepoints as software performance counters.
+
+ When this is enabled, you can create perf counters based on
+ tracepoints using PERF_TYPE_TRACEPOINT and the tracepoint ID
+ found in debugfs://tracing/events/*/*/id. (The -e/--events
+ option to the perf tool can parse and interpret symbolic
+ tracepoints, in the subsystem:tracepoint_name format.)
endmenu
diff --git a/init/main.c b/init/main.c
index 2c5ade79eb8..11f4f145be3 100644
--- a/init/main.c
+++ b/init/main.c
@@ -584,8 +584,8 @@ asmlinkage void __init start_kernel(void)
setup_arch(&command_line);
mm_init_owner(&init_mm, &init_task);
setup_command_line(command_line);
- setup_per_cpu_areas();
setup_nr_cpu_ids();
+ setup_per_cpu_areas();
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
build_all_zonelists();
@@ -733,13 +733,14 @@ static void __init do_ctors(void)
int initcall_debug;
core_param(initcall_debug, initcall_debug, bool, 0644);
+static char msgbuf[64];
+static struct boot_trace_call call;
+static struct boot_trace_ret ret;
+
int do_one_initcall(initcall_t fn)
{
int count = preempt_count();
ktime_t calltime, delta, rettime;
- char msgbuf[64];
- struct boot_trace_call call;
- struct boot_trace_ret ret;
if (initcall_debug) {
call.caller = task_pid_nr(current);
diff --git a/ipc/shm.c b/ipc/shm.c
index 15dd238e533..1bc4701ef4f 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -174,7 +174,7 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
shm_unlock(shp);
if (!is_file_hugepages(shp->shm_file))
shmem_lock(shp->shm_file, 0, shp->mlock_user);
- else
+ else if (shp->mlock_user)
user_shm_unlock(shp->shm_file->f_path.dentry->d_inode->i_size,
shp->mlock_user);
fput (shp->shm_file);
@@ -369,8 +369,8 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
/* hugetlb_file_setup applies strict accounting */
if (shmflg & SHM_NORESERVE)
acctflag = VM_NORESERVE;
- file = hugetlb_file_setup(name, size, acctflag);
- shp->mlock_user = current_user();
+ file = hugetlb_file_setup(name, size, acctflag,
+ &shp->mlock_user);
} else {
/*
* Do not allow no accounting for OVERCOMMIT_NEVER, even
@@ -410,6 +410,8 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
return error;
no_id:
+ if (shp->mlock_user) /* shmflg & SHM_HUGETLB case */
+ user_shm_unlock(size, shp->mlock_user);
fput(file);
no_file:
security_shm_free(shp);
diff --git a/kernel/fork.c b/kernel/fork.c
index 29b532e718f..e6c04d462ab 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -426,7 +426,6 @@ static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p)
init_rwsem(&mm->mmap_sem);
INIT_LIST_HEAD(&mm->mmlist);
mm->flags = (current->mm) ? current->mm->flags : default_dump_filter;
- mm->oom_adj = (current->mm) ? current->mm->oom_adj : 0;
mm->core_state = NULL;
mm->nr_ptes = 0;
set_mm_counter(mm, file_rss, 0);
@@ -568,18 +567,18 @@ void mm_release(struct task_struct *tsk, struct mm_struct *mm)
* the value intact in a core dump, and to save the unnecessary
* trouble otherwise. Userland only wants this done for a sys_exit.
*/
- if (tsk->clear_child_tid
- && !(tsk->flags & PF_SIGNALED)
- && atomic_read(&mm->mm_users) > 1) {
- u32 __user * tidptr = tsk->clear_child_tid;
+ if (tsk->clear_child_tid) {
+ if (!(tsk->flags & PF_SIGNALED) &&
+ atomic_read(&mm->mm_users) > 1) {
+ /*
+ * We don't check the error code - if userspace has
+ * not set up a proper pointer then tough luck.
+ */
+ put_user(0, tsk->clear_child_tid);
+ sys_futex(tsk->clear_child_tid, FUTEX_WAKE,
+ 1, NULL, NULL, 0);
+ }
tsk->clear_child_tid = NULL;
-
- /*
- * We don't check the error code - if userspace has
- * not set up a proper pointer then tough luck.
- */
- put_user(0, tidptr);
- sys_futex(tidptr, FUTEX_WAKE, 1, NULL, NULL, 0);
}
}
@@ -816,11 +815,8 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
{
struct signal_struct *sig;
- if (clone_flags & CLONE_THREAD) {
- atomic_inc(&current->signal->count);
- atomic_inc(&current->signal->live);
+ if (clone_flags & CLONE_THREAD)
return 0;
- }
sig = kmem_cache_alloc(signal_cachep, GFP_KERNEL);
tsk->signal = sig;
@@ -878,16 +874,6 @@ void __cleanup_signal(struct signal_struct *sig)
kmem_cache_free(signal_cachep, sig);
}
-static void cleanup_signal(struct task_struct *tsk)
-{
- struct signal_struct *sig = tsk->signal;
-
- atomic_dec(&sig->live);
-
- if (atomic_dec_and_test(&sig->count))
- __cleanup_signal(sig);
-}
-
static void copy_flags(unsigned long clone_flags, struct task_struct *p)
{
unsigned long new_flags = p->flags;
@@ -1240,6 +1226,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
}
if (clone_flags & CLONE_THREAD) {
+ atomic_inc(&current->signal->count);
+ atomic_inc(&current->signal->live);
p->group_leader = current->group_leader;
list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group);
}
@@ -1269,6 +1257,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
write_unlock_irq(&tasklist_lock);
proc_fork_connector(p);
cgroup_post_fork(p);
+ perf_counter_fork(p);
return p;
bad_fork_free_pid:
@@ -1282,7 +1271,8 @@ bad_fork_cleanup_mm:
if (p->mm)
mmput(p->mm);
bad_fork_cleanup_signal:
- cleanup_signal(p);
+ if (!(clone_flags & CLONE_THREAD))
+ __cleanup_signal(p->signal);
bad_fork_cleanup_sighand:
__cleanup_sighand(p->sighand);
bad_fork_cleanup_fs:
@@ -1410,9 +1400,6 @@ long do_fork(unsigned long clone_flags,
init_completion(&vfork);
}
- if (!(clone_flags & CLONE_THREAD))
- perf_counter_fork(p);
-
audit_finish_fork(p);
tracehook_report_clone(regs, clone_flags, nr, p);
diff --git a/kernel/futex.c b/kernel/futex.c
index 0672ff88f15..e18cfbdc719 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -1010,15 +1010,19 @@ void requeue_futex(struct futex_q *q, struct futex_hash_bucket *hb1,
* requeue_pi_wake_futex() - Wake a task that acquired the lock during requeue
* q: the futex_q
* key: the key of the requeue target futex
+ * hb: the hash_bucket of the requeue target futex
*
* During futex_requeue, with requeue_pi=1, it is possible to acquire the
* target futex if it is uncontended or via a lock steal. Set the futex_q key
* to the requeue target futex so the waiter can detect the wakeup on the right
* futex, but remove it from the hb and NULL the rt_waiter so it can detect
- * atomic lock acquisition. Must be called with the q->lock_ptr held.
+ * atomic lock acquisition. Set the q->lock_ptr to the requeue target hb->lock
+ * to protect access to the pi_state to fixup the owner later. Must be called
+ * with both q->lock_ptr and hb->lock held.
*/
static inline
-void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key)
+void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key,
+ struct futex_hash_bucket *hb)
{
drop_futex_key_refs(&q->key);
get_futex_key_refs(key);
@@ -1030,6 +1034,11 @@ void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key)
WARN_ON(!q->rt_waiter);
q->rt_waiter = NULL;
+ q->lock_ptr = &hb->lock;
+#ifdef CONFIG_DEBUG_PI_LIST
+ q->list.plist.lock = &hb->lock;
+#endif
+
wake_up_state(q->task, TASK_NORMAL);
}
@@ -1088,7 +1097,7 @@ static int futex_proxy_trylock_atomic(u32 __user *pifutex,
ret = futex_lock_pi_atomic(pifutex, hb2, key2, ps, top_waiter->task,
set_waiters);
if (ret == 1)
- requeue_pi_wake_futex(top_waiter, key2);
+ requeue_pi_wake_futex(top_waiter, key2, hb2);
return ret;
}
@@ -1247,8 +1256,15 @@ retry_private:
if (!match_futex(&this->key, &key1))
continue;
- WARN_ON(!requeue_pi && this->rt_waiter);
- WARN_ON(requeue_pi && !this->rt_waiter);
+ /*
+ * FUTEX_WAIT_REQEUE_PI and FUTEX_CMP_REQUEUE_PI should always
+ * be paired with each other and no other futex ops.
+ */
+ if ((requeue_pi && !this->rt_waiter) ||
+ (!requeue_pi && this->rt_waiter)) {
+ ret = -EINVAL;
+ break;
+ }
/*
* Wake nr_wake waiters. For requeue_pi, if we acquired the
@@ -1273,7 +1289,7 @@ retry_private:
this->task, 1);
if (ret == 1) {
/* We got the lock. */
- requeue_pi_wake_futex(this, &key2);
+ requeue_pi_wake_futex(this, &key2, hb2);
continue;
} else if (ret) {
/* -EDEADLK */
diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c
index d607a5b9ee2..235716556bf 100644
--- a/kernel/futex_compat.c
+++ b/kernel/futex_compat.c
@@ -180,7 +180,8 @@ asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, u32 val,
int cmd = op & FUTEX_CMD_MASK;
if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
- cmd == FUTEX_WAIT_BITSET)) {
+ cmd == FUTEX_WAIT_BITSET ||
+ cmd == FUTEX_WAIT_REQUEUE_PI)) {
if (get_compat_timespec(&ts, utime))
return -EFAULT;
if (!timespec_valid(&ts))
@@ -191,7 +192,8 @@ asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, u32 val,
t = ktime_add_safe(ktime_get(), t);
tp = &t;
}
- if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE)
+ if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
+ cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
val2 = (int) (unsigned long) utime;
return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 61c679db468..0ec9ed83173 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -607,7 +607,6 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
*/
get_task_struct(t);
new->thread = t;
- wake_up_process(t);
}
/*
@@ -690,6 +689,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
(int)(new->flags & IRQF_TRIGGER_MASK));
}
+ new->irq = irq;
*old_ptr = new;
/* Reset broken irq detection when installing new handler */
@@ -707,7 +707,13 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
spin_unlock_irqrestore(&desc->lock, flags);
- new->irq = irq;
+ /*
+ * Strictly no need to wake it up, but hung_task complains
+ * when no hard interrupt wakes the thread up.
+ */
+ if (new->thread)
+ wake_up_process(new->thread);
+
register_irq_proc(irq, desc);
new->dir = NULL;
register_handler_proc(irq, new);
@@ -761,7 +767,6 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
{
struct irq_desc *desc = irq_to_desc(irq);
struct irqaction *action, **action_ptr;
- struct task_struct *irqthread;
unsigned long flags;
WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
@@ -809,9 +814,6 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
desc->chip->disable(irq);
}
- irqthread = action->thread;
- action->thread = NULL;
-
spin_unlock_irqrestore(&desc->lock, flags);
unregister_handler_proc(irq, action);
@@ -819,12 +821,6 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
/* Make sure it's not being used on another CPU: */
synchronize_irq(irq);
- if (irqthread) {
- if (!test_bit(IRQTF_DIED, &action->thread_flags))
- kthread_stop(irqthread);
- put_task_struct(irqthread);
- }
-
#ifdef CONFIG_DEBUG_SHIRQ
/*
* It's a shared IRQ -- the driver ought to be prepared for an IRQ
@@ -840,6 +836,13 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
local_irq_restore(flags);
}
#endif
+
+ if (action->thread) {
+ if (!test_bit(IRQTF_DIED, &action->thread_flags))
+ kthread_stop(action->thread);
+ put_task_struct(action->thread);
+ }
+
return action;
}
diff --git a/kernel/irq/numa_migrate.c b/kernel/irq/numa_migrate.c
index 2f69bee57bf..3fd30197da2 100644
--- a/kernel/irq/numa_migrate.c
+++ b/kernel/irq/numa_migrate.c
@@ -107,8 +107,8 @@ out_unlock:
struct irq_desc *move_irq_desc(struct irq_desc *desc, int node)
{
- /* those all static, do move them */
- if (desc->irq < NR_IRQS_LEGACY)
+ /* those static or target node is -1, do not move them */
+ if (desc->irq < NR_IRQS_LEGACY || node == -1)
return desc;
if (desc->node != node)
diff --git a/kernel/lockdep_proc.c b/kernel/lockdep_proc.c
index d7135aa2d2c..e94caa666db 100644
--- a/kernel/lockdep_proc.c
+++ b/kernel/lockdep_proc.c
@@ -758,7 +758,8 @@ static int __init lockdep_proc_init(void)
&proc_lockdep_stats_operations);
#ifdef CONFIG_LOCK_STAT
- proc_create("lock_stat", S_IRUSR, NULL, &proc_lock_stat_operations);
+ proc_create("lock_stat", S_IRUSR | S_IWUSR, NULL,
+ &proc_lock_stat_operations);
#endif
return 0;
diff --git a/kernel/module.c b/kernel/module.c
index fd141140355..2d537186191 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -909,16 +909,18 @@ void __symbol_put(const char *symbol)
}
EXPORT_SYMBOL(__symbol_put);
+/* Note this assumes addr is a function, which it currently always is. */
void symbol_put_addr(void *addr)
{
struct module *modaddr;
+ unsigned long a = (unsigned long)dereference_function_descriptor(addr);
- if (core_kernel_text((unsigned long)addr))
+ if (core_kernel_text(a))
return;
/* module_text_address is safe here: we're supposed to have reference
* to module from symbol_get, so it can't go away. */
- modaddr = __module_text_address((unsigned long)addr);
+ modaddr = __module_text_address(a);
BUG_ON(!modaddr);
module_put(modaddr);
}
@@ -1272,6 +1274,10 @@ static void add_notes_attrs(struct module *mod, unsigned int nsect,
struct module_notes_attrs *notes_attrs;
struct bin_attribute *nattr;
+ /* failed to create section attributes, so can't create notes */
+ if (!mod->sect_attrs)
+ return;
+
/* Count notes sections and allocate structures. */
notes = 0;
for (i = 0; i < nsect; i++)
diff --git a/kernel/panic.c b/kernel/panic.c
index 984b3ecbd72..512ab73b0ca 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -301,6 +301,7 @@ int oops_may_print(void)
*/
void oops_enter(void)
{
+ tracing_off();
/* can't trust the integrity of the kernel anymore: */
debug_locks_off();
do_oops_enter_exit();
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c
index 95093104195..d7cbc579fc8 100644
--- a/kernel/perf_counter.c
+++ b/kernel/perf_counter.c
@@ -42,6 +42,7 @@ static int perf_overcommit __read_mostly = 1;
static atomic_t nr_counters __read_mostly;
static atomic_t nr_mmap_counters __read_mostly;
static atomic_t nr_comm_counters __read_mostly;
+static atomic_t nr_task_counters __read_mostly;
/*
* perf counter paranoia level:
@@ -49,7 +50,7 @@ static atomic_t nr_comm_counters __read_mostly;
* 1 - disallow cpu counters to unpriv
* 2 - disallow kernel profiling to unpriv
*/
-int sysctl_perf_counter_paranoid __read_mostly;
+int sysctl_perf_counter_paranoid __read_mostly = 1;
static inline bool perf_paranoid_cpu(void)
{
@@ -87,6 +88,7 @@ void __weak hw_perf_disable(void) { barrier(); }
void __weak hw_perf_enable(void) { barrier(); }
void __weak hw_perf_counter_setup(int cpu) { barrier(); }
+void __weak hw_perf_counter_setup_online(int cpu) { barrier(); }
int __weak
hw_perf_group_sched_in(struct perf_counter *group_leader,
@@ -305,6 +307,10 @@ counter_sched_out(struct perf_counter *counter,
return;
counter->state = PERF_COUNTER_STATE_INACTIVE;
+ if (counter->pending_disable) {
+ counter->pending_disable = 0;
+ counter->state = PERF_COUNTER_STATE_OFF;
+ }
counter->tstamp_stopped = ctx->time;
counter->pmu->disable(counter);
counter->oncpu = -1;
@@ -1103,7 +1109,7 @@ static void perf_counter_sync_stat(struct perf_counter_context *ctx,
__perf_counter_sync_stat(counter, next_counter);
counter = list_next_entry(counter, event_entry);
- next_counter = list_next_entry(counter, event_entry);
+ next_counter = list_next_entry(next_counter, event_entry);
}
}
@@ -1497,10 +1503,21 @@ static void perf_counter_enable_on_exec(struct task_struct *task)
*/
static void __perf_counter_read(void *info)
{
+ struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
struct perf_counter *counter = info;
struct perf_counter_context *ctx = counter->ctx;
unsigned long flags;
+ /*
+ * If this is a task context, we need to check whether it is
+ * the current task context of this cpu. If not it has been
+ * scheduled out before the smp call arrived. In that case
+ * counter->count would have been updated to a recent sample
+ * when the counter was scheduled out.
+ */
+ if (ctx->task && cpuctx->task_ctx != ctx)
+ return;
+
local_irq_save(flags);
if (ctx->is_active)
update_context_time(ctx);
@@ -1654,6 +1671,8 @@ static void free_counter(struct perf_counter *counter)
atomic_dec(&nr_mmap_counters);
if (counter->attr.comm)
atomic_dec(&nr_comm_counters);
+ if (counter->attr.task)
+ atomic_dec(&nr_task_counters);
}
if (counter->destroy)
@@ -1688,14 +1707,133 @@ static int perf_release(struct inode *inode, struct file *file)
return 0;
}
+static int perf_counter_read_size(struct perf_counter *counter)
+{
+ int entry = sizeof(u64); /* value */
+ int size = 0;
+ int nr = 1;
+
+ if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+ size += sizeof(u64);
+
+ if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+ size += sizeof(u64);
+
+ if (counter->attr.read_format & PERF_FORMAT_ID)
+ entry += sizeof(u64);
+
+ if (counter->attr.read_format & PERF_FORMAT_GROUP) {
+ nr += counter->group_leader->nr_siblings;
+ size += sizeof(u64);
+ }
+
+ size += entry * nr;
+
+ return size;
+}
+
+static u64 perf_counter_read_value(struct perf_counter *counter)
+{
+ struct perf_counter *child;
+ u64 total = 0;
+
+ total += perf_counter_read(counter);
+ list_for_each_entry(child, &counter->child_list, child_list)
+ total += perf_counter_read(child);
+
+ return total;
+}
+
+static int perf_counter_read_entry(struct perf_counter *counter,
+ u64 read_format, char __user *buf)
+{
+ int n = 0, count = 0;
+ u64 values[2];
+
+ values[n++] = perf_counter_read_value(counter);
+ if (read_format & PERF_FORMAT_ID)
+ values[n++] = primary_counter_id(counter);
+
+ count = n * sizeof(u64);
+
+ if (copy_to_user(buf, values, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static int perf_counter_read_group(struct perf_counter *counter,
+ u64 read_format, char __user *buf)
+{
+ struct perf_counter *leader = counter->group_leader, *sub;
+ int n = 0, size = 0, err = -EFAULT;
+ u64 values[3];
+
+ values[n++] = 1 + leader->nr_siblings;
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
+ values[n++] = leader->total_time_enabled +
+ atomic64_read(&leader->child_total_time_enabled);
+ }
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
+ values[n++] = leader->total_time_running +
+ atomic64_read(&leader->child_total_time_running);
+ }
+
+ size = n * sizeof(u64);
+
+ if (copy_to_user(buf, values, size))
+ return -EFAULT;
+
+ err = perf_counter_read_entry(leader, read_format, buf + size);
+ if (err < 0)
+ return err;
+
+ size += err;
+
+ list_for_each_entry(sub, &leader->sibling_list, list_entry) {
+ err = perf_counter_read_entry(sub, read_format,
+ buf + size);
+ if (err < 0)
+ return err;
+
+ size += err;
+ }
+
+ return size;
+}
+
+static int perf_counter_read_one(struct perf_counter *counter,
+ u64 read_format, char __user *buf)
+{
+ u64 values[4];
+ int n = 0;
+
+ values[n++] = perf_counter_read_value(counter);
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
+ values[n++] = counter->total_time_enabled +
+ atomic64_read(&counter->child_total_time_enabled);
+ }
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
+ values[n++] = counter->total_time_running +
+ atomic64_read(&counter->child_total_time_running);
+ }
+ if (read_format & PERF_FORMAT_ID)
+ values[n++] = primary_counter_id(counter);
+
+ if (copy_to_user(buf, values, n * sizeof(u64)))
+ return -EFAULT;
+
+ return n * sizeof(u64);
+}
+
/*
* Read the performance counter - simple non blocking version for now
*/
static ssize_t
perf_read_hw(struct perf_counter *counter, char __user *buf, size_t count)
{
- u64 values[4];
- int n;
+ u64 read_format = counter->attr.read_format;
+ int ret;
/*
* Return end-of-file for a read on a counter that is in
@@ -1705,28 +1843,18 @@ perf_read_hw(struct perf_counter *counter, char __user *buf, size_t count)
if (counter->state == PERF_COUNTER_STATE_ERROR)
return 0;
+ if (count < perf_counter_read_size(counter))
+ return -ENOSPC;
+
WARN_ON_ONCE(counter->ctx->parent_ctx);
mutex_lock(&counter->child_mutex);
- values[0] = perf_counter_read(counter);
- n = 1;
- if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
- values[n++] = counter->total_time_enabled +
- atomic64_read(&counter->child_total_time_enabled);
- if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
- values[n++] = counter->total_time_running +
- atomic64_read(&counter->child_total_time_running);
- if (counter->attr.read_format & PERF_FORMAT_ID)
- values[n++] = primary_counter_id(counter);
+ if (read_format & PERF_FORMAT_GROUP)
+ ret = perf_counter_read_group(counter, read_format, buf);
+ else
+ ret = perf_counter_read_one(counter, read_format, buf);
mutex_unlock(&counter->child_mutex);
- if (count < n * sizeof(u64))
- return -EINVAL;
- count = n * sizeof(u64);
-
- if (copy_to_user(buf, values, count))
- return -EFAULT;
-
- return count;
+ return ret;
}
static ssize_t
@@ -1891,6 +2019,10 @@ int perf_counter_task_disable(void)
return 0;
}
+#ifndef PERF_COUNTER_INDEX_OFFSET
+# define PERF_COUNTER_INDEX_OFFSET 0
+#endif
+
static int perf_counter_index(struct perf_counter *counter)
{
if (counter->state != PERF_COUNTER_STATE_ACTIVE)
@@ -2230,7 +2362,7 @@ static void perf_pending_counter(struct perf_pending_entry *entry)
if (counter->pending_disable) {
counter->pending_disable = 0;
- perf_counter_disable(counter);
+ __perf_counter_disable(counter);
}
if (counter->pending_wakeup) {
@@ -2615,7 +2747,80 @@ static u32 perf_counter_tid(struct perf_counter *counter, struct task_struct *p)
return task_pid_nr_ns(p, counter->ns);
}
-static void perf_counter_output(struct perf_counter *counter, int nmi,
+static void perf_output_read_one(struct perf_output_handle *handle,
+ struct perf_counter *counter)
+{
+ u64 read_format = counter->attr.read_format;
+ u64 values[4];
+ int n = 0;
+
+ values[n++] = atomic64_read(&counter->count);
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
+ values[n++] = counter->total_time_enabled +
+ atomic64_read(&counter->child_total_time_enabled);
+ }
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
+ values[n++] = counter->total_time_running +
+ atomic64_read(&counter->child_total_time_running);
+ }
+ if (read_format & PERF_FORMAT_ID)
+ values[n++] = primary_counter_id(counter);
+
+ perf_output_copy(handle, values, n * sizeof(u64));
+}
+
+/*
+ * XXX PERF_FORMAT_GROUP vs inherited counters seems difficult.
+ */
+static void perf_output_read_group(struct perf_output_handle *handle,
+ struct perf_counter *counter)
+{
+ struct perf_counter *leader = counter->group_leader, *sub;
+ u64 read_format = counter->attr.read_format;
+ u64 values[5];
+ int n = 0;
+
+ values[n++] = 1 + leader->nr_siblings;
+
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+ values[n++] = leader->total_time_enabled;
+
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+ values[n++] = leader->total_time_running;
+
+ if (leader != counter)
+ leader->pmu->read(leader);
+
+ values[n++] = atomic64_read(&leader->count);
+ if (read_format & PERF_FORMAT_ID)
+ values[n++] = primary_counter_id(leader);
+
+ perf_output_copy(handle, values, n * sizeof(u64));
+
+ list_for_each_entry(sub, &leader->sibling_list, list_entry) {
+ n = 0;
+
+ if (sub != counter)
+ sub->pmu->read(sub);
+
+ values[n++] = atomic64_read(&sub->count);
+ if (read_format & PERF_FORMAT_ID)
+ values[n++] = primary_counter_id(sub);
+
+ perf_output_copy(handle, values, n * sizeof(u64));
+ }
+}
+
+static void perf_output_read(struct perf_output_handle *handle,
+ struct perf_counter *counter)
+{
+ if (counter->attr.read_format & PERF_FORMAT_GROUP)
+ perf_output_read_group(handle, counter);
+ else
+ perf_output_read_one(handle, counter);
+}
+
+void perf_counter_output(struct perf_counter *counter, int nmi,
struct perf_sample_data *data)
{
int ret;
@@ -2626,10 +2831,6 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
struct {
u32 pid, tid;
} tid_entry;
- struct {
- u64 id;
- u64 counter;
- } group_entry;
struct perf_callchain_entry *callchain = NULL;
int callchain_size = 0;
u64 time;
@@ -2684,10 +2885,8 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
if (sample_type & PERF_SAMPLE_PERIOD)
header.size += sizeof(u64);
- if (sample_type & PERF_SAMPLE_GROUP) {
- header.size += sizeof(u64) +
- counter->nr_siblings * sizeof(group_entry);
- }
+ if (sample_type & PERF_SAMPLE_READ)
+ header.size += perf_counter_read_size(counter);
if (sample_type & PERF_SAMPLE_CALLCHAIN) {
callchain = perf_callchain(data->regs);
@@ -2699,6 +2898,18 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
header.size += sizeof(u64);
}
+ if (sample_type & PERF_SAMPLE_RAW) {
+ int size = sizeof(u32);
+
+ if (data->raw)
+ size += data->raw->size;
+ else
+ size += sizeof(u32);
+
+ WARN_ON_ONCE(size & (sizeof(u64)-1));
+ header.size += size;
+ }
+
ret = perf_output_begin(&handle, counter, header.size, nmi, 1);
if (ret)
return;
@@ -2732,26 +2943,8 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
if (sample_type & PERF_SAMPLE_PERIOD)
perf_output_put(&handle, data->period);
- /*
- * XXX PERF_SAMPLE_GROUP vs inherited counters seems difficult.
- */
- if (sample_type & PERF_SAMPLE_GROUP) {
- struct perf_counter *leader, *sub;
- u64 nr = counter->nr_siblings;
-
- perf_output_put(&handle, nr);
-
- leader = counter->group_leader;
- list_for_each_entry(sub, &leader->sibling_list, list_entry) {
- if (sub != counter)
- sub->pmu->read(sub);
-
- group_entry.id = primary_counter_id(sub);
- group_entry.counter = atomic64_read(&sub->count);
-
- perf_output_put(&handle, group_entry);
- }
- }
+ if (sample_type & PERF_SAMPLE_READ)
+ perf_output_read(&handle, counter);
if (sample_type & PERF_SAMPLE_CALLCHAIN) {
if (callchain)
@@ -2762,6 +2955,22 @@ static void perf_counter_output(struct perf_counter *counter, int nmi,
}
}
+ if (sample_type & PERF_SAMPLE_RAW) {
+ if (data->raw) {
+ perf_output_put(&handle, data->raw->size);
+ perf_output_copy(&handle, data->raw->data, data->raw->size);
+ } else {
+ struct {
+ u32 size;
+ u32 data;
+ } raw = {
+ .size = sizeof(u32),
+ .data = 0,
+ };
+ perf_output_put(&handle, raw);
+ }
+ }
+
perf_output_end(&handle);
}
@@ -2774,8 +2983,6 @@ struct perf_read_event {
u32 pid;
u32 tid;
- u64 value;
- u64 format[3];
};
static void
@@ -2787,80 +2994,74 @@ perf_counter_read_event(struct perf_counter *counter,
.header = {
.type = PERF_EVENT_READ,
.misc = 0,
- .size = sizeof(event) - sizeof(event.format),
+ .size = sizeof(event) + perf_counter_read_size(counter),
},
.pid = perf_counter_pid(counter, task),
.tid = perf_counter_tid(counter, task),
- .value = atomic64_read(&counter->count),
};
- int ret, i = 0;
-
- if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
- event.header.size += sizeof(u64);
- event.format[i++] = counter->total_time_enabled;
- }
-
- if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
- event.header.size += sizeof(u64);
- event.format[i++] = counter->total_time_running;
- }
-
- if (counter->attr.read_format & PERF_FORMAT_ID) {
- event.header.size += sizeof(u64);
- event.format[i++] = primary_counter_id(counter);
- }
+ int ret;
ret = perf_output_begin(&handle, counter, event.header.size, 0, 0);
if (ret)
return;
- perf_output_copy(&handle, &event, event.header.size);
+ perf_output_put(&handle, event);
+ perf_output_read(&handle, counter);
+
perf_output_end(&handle);
}
/*
- * fork tracking
+ * task tracking -- fork/exit
+ *
+ * enabled by: attr.comm | attr.mmap | attr.task
*/
-struct perf_fork_event {
- struct task_struct *task;
+struct perf_task_event {
+ struct task_struct *task;
+ struct perf_counter_context *task_ctx;
struct {
struct perf_event_header header;
u32 pid;
u32 ppid;
+ u32 tid;
+ u32 ptid;
} event;
};
-static void perf_counter_fork_output(struct perf_counter *counter,
- struct perf_fork_event *fork_event)
+static void perf_counter_task_output(struct perf_counter *counter,
+ struct perf_task_event *task_event)
{
struct perf_output_handle handle;
- int size = fork_event->event.header.size;
- struct task_struct *task = fork_event->task;
+ int size = task_event->event.header.size;
+ struct task_struct *task = task_event->task;
int ret = perf_output_begin(&handle, counter, size, 0, 0);
if (ret)
return;
- fork_event->event.pid = perf_counter_pid(counter, task);
- fork_event->event.ppid = perf_counter_pid(counter, task->real_parent);
+ task_event->event.pid = perf_counter_pid(counter, task);
+ task_event->event.ppid = perf_counter_pid(counter, current);
- perf_output_put(&handle, fork_event->event);
+ task_event->event.tid = perf_counter_tid(counter, task);
+ task_event->event.ptid = perf_counter_tid(counter, current);
+
+ perf_output_put(&handle, task_event->event);
perf_output_end(&handle);
}
-static int perf_counter_fork_match(struct perf_counter *counter)
+static int perf_counter_task_match(struct perf_counter *counter)
{
- if (counter->attr.comm || counter->attr.mmap)
+ if (counter->attr.comm || counter->attr.mmap || counter->attr.task)
return 1;
return 0;
}
-static void perf_counter_fork_ctx(struct perf_counter_context *ctx,
- struct perf_fork_event *fork_event)
+static void perf_counter_task_ctx(struct perf_counter_context *ctx,
+ struct perf_task_event *task_event)
{
struct perf_counter *counter;
@@ -2869,54 +3070,62 @@ static void perf_counter_fork_ctx(struct perf_counter_context *ctx,
rcu_read_lock();
list_for_each_entry_rcu(counter, &ctx->event_list, event_entry) {
- if (perf_counter_fork_match(counter))
- perf_counter_fork_output(counter, fork_event);
+ if (perf_counter_task_match(counter))
+ perf_counter_task_output(counter, task_event);
}
rcu_read_unlock();
}
-static void perf_counter_fork_event(struct perf_fork_event *fork_event)
+static void perf_counter_task_event(struct perf_task_event *task_event)
{
struct perf_cpu_context *cpuctx;
- struct perf_counter_context *ctx;
+ struct perf_counter_context *ctx = task_event->task_ctx;
cpuctx = &get_cpu_var(perf_cpu_context);
- perf_counter_fork_ctx(&cpuctx->ctx, fork_event);
+ perf_counter_task_ctx(&cpuctx->ctx, task_event);
put_cpu_var(perf_cpu_context);
rcu_read_lock();
- /*
- * doesn't really matter which of the child contexts the
- * events ends up in.
- */
- ctx = rcu_dereference(current->perf_counter_ctxp);
+ if (!ctx)
+ ctx = rcu_dereference(task_event->task->perf_counter_ctxp);
if (ctx)
- perf_counter_fork_ctx(ctx, fork_event);
+ perf_counter_task_ctx(ctx, task_event);
rcu_read_unlock();
}
-void perf_counter_fork(struct task_struct *task)
+static void perf_counter_task(struct task_struct *task,
+ struct perf_counter_context *task_ctx,
+ int new)
{
- struct perf_fork_event fork_event;
+ struct perf_task_event task_event;
if (!atomic_read(&nr_comm_counters) &&
- !atomic_read(&nr_mmap_counters))
+ !atomic_read(&nr_mmap_counters) &&
+ !atomic_read(&nr_task_counters))
return;
- fork_event = (struct perf_fork_event){
- .task = task,
- .event = {
+ task_event = (struct perf_task_event){
+ .task = task,
+ .task_ctx = task_ctx,
+ .event = {
.header = {
- .type = PERF_EVENT_FORK,
+ .type = new ? PERF_EVENT_FORK : PERF_EVENT_EXIT,
.misc = 0,
- .size = sizeof(fork_event.event),
+ .size = sizeof(task_event.event),
},
/* .pid */
/* .ppid */
+ /* .tid */
+ /* .ptid */
},
};
- perf_counter_fork_event(&fork_event);
+ perf_counter_task_event(&task_event);
+}
+
+void perf_counter_fork(struct task_struct *task)
+{
+ perf_counter_task(task, NULL, 1);
}
/*
@@ -3305,125 +3514,111 @@ int perf_counter_overflow(struct perf_counter *counter, int nmi,
* Generic software counter infrastructure
*/
-static void perf_swcounter_update(struct perf_counter *counter)
+/*
+ * We directly increment counter->count and keep a second value in
+ * counter->hw.period_left to count intervals. This period counter
+ * is kept in the range [-sample_period, 0] so that we can use the
+ * sign as trigger.
+ */
+
+static u64 perf_swcounter_set_period(struct perf_counter *counter)
{
struct hw_perf_counter *hwc = &counter->hw;
- u64 prev, now;
- s64 delta;
+ u64 period = hwc->last_period;
+ u64 nr, offset;
+ s64 old, val;
+
+ hwc->last_period = hwc->sample_period;
again:
- prev = atomic64_read(&hwc->prev_count);
- now = atomic64_read(&hwc->count);
- if (atomic64_cmpxchg(&hwc->prev_count, prev, now) != prev)
- goto again;
+ old = val = atomic64_read(&hwc->period_left);
+ if (val < 0)
+ return 0;
- delta = now - prev;
+ nr = div64_u64(period + val, period);
+ offset = nr * period;
+ val -= offset;
+ if (atomic64_cmpxchg(&hwc->period_left, old, val) != old)
+ goto again;
- atomic64_add(delta, &counter->count);
- atomic64_sub(delta, &hwc->period_left);
+ return nr;
}
-static void perf_swcounter_set_period(struct perf_counter *counter)
+static void perf_swcounter_overflow(struct perf_counter *counter,
+ int nmi, struct perf_sample_data *data)
{
struct hw_perf_counter *hwc = &counter->hw;
- s64 left = atomic64_read(&hwc->period_left);
- s64 period = hwc->sample_period;
+ u64 overflow;
- if (unlikely(left <= -period)) {
- left = period;
- atomic64_set(&hwc->period_left, left);
- hwc->last_period = period;
- }
+ data->period = counter->hw.last_period;
+ overflow = perf_swcounter_set_period(counter);
- if (unlikely(left <= 0)) {
- left += period;
- atomic64_add(period, &hwc->period_left);
- hwc->last_period = period;
- }
+ if (hwc->interrupts == MAX_INTERRUPTS)
+ return;
- atomic64_set(&hwc->prev_count, -left);
- atomic64_set(&hwc->count, -left);
+ for (; overflow; overflow--) {
+ if (perf_counter_overflow(counter, nmi, data)) {
+ /*
+ * We inhibit the overflow from happening when
+ * hwc->interrupts == MAX_INTERRUPTS.
+ */
+ break;
+ }
+ }
}
-static enum hrtimer_restart perf_swcounter_hrtimer(struct hrtimer *hrtimer)
+static void perf_swcounter_unthrottle(struct perf_counter *counter)
{
- enum hrtimer_restart ret = HRTIMER_RESTART;
- struct perf_sample_data data;
- struct perf_counter *counter;
- u64 period;
-
- counter = container_of(hrtimer, struct perf_counter, hw.hrtimer);
- counter->pmu->read(counter);
-
- data.addr = 0;
- data.regs = get_irq_regs();
/*
- * In case we exclude kernel IPs or are somehow not in interrupt
- * context, provide the next best thing, the user IP.
+ * Nothing to do, we already reset hwc->interrupts.
*/
- if ((counter->attr.exclude_kernel || !data.regs) &&
- !counter->attr.exclude_user)
- data.regs = task_pt_regs(current);
+}
- if (data.regs) {
- if (perf_counter_overflow(counter, 0, &data))
- ret = HRTIMER_NORESTART;
- }
+static void perf_swcounter_add(struct perf_counter *counter, u64 nr,
+ int nmi, struct perf_sample_data *data)
+{
+ struct hw_perf_counter *hwc = &counter->hw;
- period = max_t(u64, 10000, counter->hw.sample_period);
- hrtimer_forward_now(hrtimer, ns_to_ktime(period));
+ atomic64_add(nr, &counter->count);
- return ret;
-}
+ if (!hwc->sample_period)
+ return;
-static void perf_swcounter_overflow(struct perf_counter *counter,
- int nmi, struct perf_sample_data *data)
-{
- data->period = counter->hw.last_period;
+ if (!data->regs)
+ return;
- perf_swcounter_update(counter);
- perf_swcounter_set_period(counter);
- if (perf_counter_overflow(counter, nmi, data))
- /* soft-disable the counter */
- ;
+ if (!atomic64_add_negative(nr, &hwc->period_left))
+ perf_swcounter_overflow(counter, nmi, data);
}
static int perf_swcounter_is_counting(struct perf_counter *counter)
{
- struct perf_counter_context *ctx;
- unsigned long flags;
- int count;
-
+ /*
+ * The counter is active, we're good!
+ */
if (counter->state == PERF_COUNTER_STATE_ACTIVE)
return 1;
+ /*
+ * The counter is off/error, not counting.
+ */
if (counter->state != PERF_COUNTER_STATE_INACTIVE)
return 0;
/*
- * If the counter is inactive, it could be just because
- * its task is scheduled out, or because it's in a group
- * which could not go on the PMU. We want to count in
- * the first case but not the second. If the context is
- * currently active then an inactive software counter must
- * be the second case. If it's not currently active then
- * we need to know whether the counter was active when the
- * context was last active, which we can determine by
- * comparing counter->tstamp_stopped with ctx->time.
- *
- * We are within an RCU read-side critical section,
- * which protects the existence of *ctx.
+ * The counter is inactive, if the context is active
+ * we're part of a group that didn't make it on the 'pmu',
+ * not counting.
*/
- ctx = counter->ctx;
- spin_lock_irqsave(&ctx->lock, flags);
- count = 1;
- /* Re-check state now we have the lock */
- if (counter->state < PERF_COUNTER_STATE_INACTIVE ||
- counter->ctx->is_active ||
- counter->tstamp_stopped < ctx->time)
- count = 0;
- spin_unlock_irqrestore(&ctx->lock, flags);
- return count;
+ if (counter->ctx->is_active)
+ return 0;
+
+ /*
+ * We're inactive and the context is too, this means the
+ * task is scheduled out, we're counting events that happen
+ * to us, like migration events.
+ */
+ return 1;
}
static int perf_swcounter_match(struct perf_counter *counter,
@@ -3449,15 +3644,6 @@ static int perf_swcounter_match(struct perf_counter *counter,
return 1;
}
-static void perf_swcounter_add(struct perf_counter *counter, u64 nr,
- int nmi, struct perf_sample_data *data)
-{
- int neg = atomic64_add_negative(nr, &counter->hw.count);
-
- if (counter->hw.sample_period && !neg && data->regs)
- perf_swcounter_overflow(counter, nmi, data);
-}
-
static void perf_swcounter_ctx_event(struct perf_counter_context *ctx,
enum perf_type_id type,
u32 event, u64 nr, int nmi,
@@ -3536,27 +3722,66 @@ void __perf_swcounter_event(u32 event, u64 nr, int nmi,
static void perf_swcounter_read(struct perf_counter *counter)
{
- perf_swcounter_update(counter);
}
static int perf_swcounter_enable(struct perf_counter *counter)
{
- perf_swcounter_set_period(counter);
+ struct hw_perf_counter *hwc = &counter->hw;
+
+ if (hwc->sample_period) {
+ hwc->last_period = hwc->sample_period;
+ perf_swcounter_set_period(counter);
+ }
return 0;
}
static void perf_swcounter_disable(struct perf_counter *counter)
{
- perf_swcounter_update(counter);
}
static const struct pmu perf_ops_generic = {
.enable = perf_swcounter_enable,
.disable = perf_swcounter_disable,
.read = perf_swcounter_read,
+ .unthrottle = perf_swcounter_unthrottle,
};
/*
+ * hrtimer based swcounter callback
+ */
+
+static enum hrtimer_restart perf_swcounter_hrtimer(struct hrtimer *hrtimer)
+{
+ enum hrtimer_restart ret = HRTIMER_RESTART;
+ struct perf_sample_data data;
+ struct perf_counter *counter;
+ u64 period;
+
+ counter = container_of(hrtimer, struct perf_counter, hw.hrtimer);
+ counter->pmu->read(counter);
+
+ data.addr = 0;
+ data.regs = get_irq_regs();
+ /*
+ * In case we exclude kernel IPs or are somehow not in interrupt
+ * context, provide the next best thing, the user IP.
+ */
+ if ((counter->attr.exclude_kernel || !data.regs) &&
+ !counter->attr.exclude_user)
+ data.regs = task_pt_regs(current);
+
+ if (data.regs) {
+ if (perf_counter_overflow(counter, 0, &data))
+ ret = HRTIMER_NORESTART;
+ }
+
+ period = max_t(u64, 10000, counter->hw.sample_period);
+ hrtimer_forward_now(hrtimer, ns_to_ktime(period));
+
+ return ret;
+}
+
+/*
* Software counter: cpu wall time clock
*/
@@ -3673,17 +3898,24 @@ static const struct pmu perf_ops_task_clock = {
};
#ifdef CONFIG_EVENT_PROFILE
-void perf_tpcounter_event(int event_id)
+void perf_tpcounter_event(int event_id, u64 addr, u64 count, void *record,
+ int entry_size)
{
+ struct perf_raw_record raw = {
+ .size = entry_size,
+ .data = record,
+ };
+
struct perf_sample_data data = {
.regs = get_irq_regs(),
- .addr = 0,
+ .addr = addr,
+ .raw = &raw,
};
if (!data.regs)
data.regs = task_pt_regs(current);
- do_perf_swcounter_event(PERF_TYPE_TRACEPOINT, event_id, 1, 1, &data);
+ do_perf_swcounter_event(PERF_TYPE_TRACEPOINT, event_id, count, 1, &data);
}
EXPORT_SYMBOL_GPL(perf_tpcounter_event);
@@ -3697,6 +3929,14 @@ static void tp_perf_counter_destroy(struct perf_counter *counter)
static const struct pmu *tp_perf_counter_init(struct perf_counter *counter)
{
+ /*
+ * Raw tracepoint data is a severe data leak, only allow root to
+ * have these.
+ */
+ if ((counter->attr.sample_type & PERF_SAMPLE_RAW) &&
+ !capable(CAP_SYS_ADMIN))
+ return ERR_PTR(-EPERM);
+
if (ftrace_profile_enable(counter->attr.config))
return NULL;
@@ -3826,13 +4066,14 @@ perf_counter_alloc(struct perf_counter_attr *attr,
hwc->sample_period = attr->sample_period;
if (attr->freq && attr->sample_freq)
hwc->sample_period = 1;
+ hwc->last_period = hwc->sample_period;
atomic64_set(&hwc->period_left, hwc->sample_period);
/*
- * we currently do not support PERF_SAMPLE_GROUP on inherited counters
+ * we currently do not support PERF_FORMAT_GROUP on inherited counters
*/
- if (attr->inherit && (attr->sample_type & PERF_SAMPLE_GROUP))
+ if (attr->inherit && (attr->read_format & PERF_FORMAT_GROUP))
goto done;
switch (attr->type) {
@@ -3875,6 +4116,8 @@ done:
atomic_inc(&nr_mmap_counters);
if (counter->attr.comm)
atomic_inc(&nr_comm_counters);
+ if (counter->attr.task)
+ atomic_inc(&nr_task_counters);
}
return counter;
@@ -4236,8 +4479,10 @@ void perf_counter_exit_task(struct task_struct *child)
struct perf_counter_context *child_ctx;
unsigned long flags;
- if (likely(!child->perf_counter_ctxp))
+ if (likely(!child->perf_counter_ctxp)) {
+ perf_counter_task(child, NULL, 0);
return;
+ }
local_irq_save(flags);
/*
@@ -4262,8 +4507,14 @@ void perf_counter_exit_task(struct task_struct *child)
* the counters from it.
*/
unclone_ctx(child_ctx);
- spin_unlock(&child_ctx->lock);
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&child_ctx->lock, flags);
+
+ /*
+ * Report the task dead after unscheduling the counters so that we
+ * won't get any samples after PERF_EVENT_EXIT. We can however still
+ * get a few PERF_EVENT_READ events.
+ */
+ perf_counter_task(child, child_ctx, 0);
/*
* We can recurse on the same lock type through:
@@ -4484,6 +4735,11 @@ perf_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
perf_counter_init_cpu(cpu);
break;
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ hw_perf_counter_setup_online(cpu);
+ break;
+
case CPU_DOWN_PREPARE:
case CPU_DOWN_PREPARE_FROZEN:
perf_counter_exit_cpu(cpu);
@@ -4508,6 +4764,8 @@ void __init perf_counter_init(void)
{
perf_cpu_notify(&perf_cpu_nb, (unsigned long)CPU_UP_PREPARE,
(void *)(long)smp_processor_id());
+ perf_cpu_notify(&perf_cpu_nb, (unsigned long)CPU_ONLINE,
+ (void *)(long)smp_processor_id());
register_cpu_notifier(&perf_cpu_nb);
}
diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c
index bece7c0b67b..e33a21cb940 100644
--- a/kernel/posix-cpu-timers.c
+++ b/kernel/posix-cpu-timers.c
@@ -521,11 +521,12 @@ void posix_cpu_timers_exit(struct task_struct *tsk)
}
void posix_cpu_timers_exit_group(struct task_struct *tsk)
{
- struct task_cputime cputime;
+ struct signal_struct *const sig = tsk->signal;
- thread_group_cputimer(tsk, &cputime);
cleanup_timers(tsk->signal->cpu_timers,
- cputime.utime, cputime.stime, cputime.sum_exec_runtime);
+ cputime_add(tsk->utime, sig->utime),
+ cputime_add(tsk->stime, sig->stime),
+ tsk->se.sum_exec_runtime + sig->sum_sched_runtime);
}
static void clear_dead_task(struct k_itimer *timer, union cpu_time_count now)
diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
index 052ec4d195c..d089d052c4a 100644
--- a/kernel/posix-timers.c
+++ b/kernel/posix-timers.c
@@ -202,6 +202,12 @@ static int no_timer_create(struct k_itimer *new_timer)
return -EOPNOTSUPP;
}
+static int no_nsleep(const clockid_t which_clock, int flags,
+ struct timespec *tsave, struct timespec __user *rmtp)
+{
+ return -EOPNOTSUPP;
+}
+
/*
* Return nonzero if we know a priori this clockid_t value is bogus.
*/
@@ -254,6 +260,7 @@ static __init int init_posix_timers(void)
.clock_get = posix_get_monotonic_raw,
.clock_set = do_posix_clock_nosettime,
.timer_create = no_timer_create,
+ .nsleep = no_nsleep,
};
register_posix_clock(CLOCK_REALTIME, &clock_realtime);
diff --git a/kernel/rtmutex.c b/kernel/rtmutex.c
index fcd107a78c5..29bd4baf9e7 100644
--- a/kernel/rtmutex.c
+++ b/kernel/rtmutex.c
@@ -1039,16 +1039,14 @@ int rt_mutex_start_proxy_lock(struct rt_mutex *lock,
if (!rt_mutex_owner(lock) || try_to_steal_lock(lock, task)) {
/* We got the lock for task. */
debug_rt_mutex_lock(lock);
-
rt_mutex_set_owner(lock, task, 0);
-
+ spin_unlock(&lock->wait_lock);
rt_mutex_deadlock_account_lock(lock, task);
return 1;
}
ret = task_blocks_on_rt_mutex(lock, waiter, task, detect_deadlock);
-
if (ret && !waiter->task) {
/*
* Reset the return value. We might have
diff --git a/kernel/sched_cpupri.c b/kernel/sched_cpupri.c
index e6c251790dd..d014efbf947 100644
--- a/kernel/sched_cpupri.c
+++ b/kernel/sched_cpupri.c
@@ -81,8 +81,21 @@ int cpupri_find(struct cpupri *cp, struct task_struct *p,
if (cpumask_any_and(&p->cpus_allowed, vec->mask) >= nr_cpu_ids)
continue;
- if (lowest_mask)
+ if (lowest_mask) {
cpumask_and(lowest_mask, &p->cpus_allowed, vec->mask);
+
+ /*
+ * We have to ensure that we have at least one bit
+ * still set in the array, since the map could have
+ * been concurrently emptied between the first and
+ * second reads of vec->mask. If we hit this
+ * condition, simply act as though we never hit this
+ * priority level and continue on.
+ */
+ if (cpumask_any(lowest_mask) >= nr_cpu_ids)
+ continue;
+ }
+
return 1;
}
diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c
index 9ffb2b2ceba..652e8bdef9a 100644
--- a/kernel/sched_fair.c
+++ b/kernel/sched_fair.c
@@ -611,9 +611,13 @@ account_entity_dequeue(struct cfs_rq *cfs_rq, struct sched_entity *se)
static void enqueue_sleeper(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
#ifdef CONFIG_SCHEDSTATS
+ struct task_struct *tsk = NULL;
+
+ if (entity_is_task(se))
+ tsk = task_of(se);
+
if (se->sleep_start) {
u64 delta = rq_of(cfs_rq)->clock - se->sleep_start;
- struct task_struct *tsk = task_of(se);
if ((s64)delta < 0)
delta = 0;
@@ -624,11 +628,11 @@ static void enqueue_sleeper(struct cfs_rq *cfs_rq, struct sched_entity *se)
se->sleep_start = 0;
se->sum_sleep_runtime += delta;
- account_scheduler_latency(tsk, delta >> 10, 1);
+ if (tsk)
+ account_scheduler_latency(tsk, delta >> 10, 1);
}
if (se->block_start) {
u64 delta = rq_of(cfs_rq)->clock - se->block_start;
- struct task_struct *tsk = task_of(se);
if ((s64)delta < 0)
delta = 0;
@@ -639,17 +643,19 @@ static void enqueue_sleeper(struct cfs_rq *cfs_rq, struct sched_entity *se)
se->block_start = 0;
se->sum_sleep_runtime += delta;
- /*
- * Blocking time is in units of nanosecs, so shift by 20 to
- * get a milliseconds-range estimation of the amount of
- * time that the task spent sleeping:
- */
- if (unlikely(prof_on == SLEEP_PROFILING)) {
-
- profile_hits(SLEEP_PROFILING, (void *)get_wchan(tsk),
- delta >> 20);
+ if (tsk) {
+ /*
+ * Blocking time is in units of nanosecs, so shift by
+ * 20 to get a milliseconds-range estimation of the
+ * amount of time that the task spent sleeping:
+ */
+ if (unlikely(prof_on == SLEEP_PROFILING)) {
+ profile_hits(SLEEP_PROFILING,
+ (void *)get_wchan(tsk),
+ delta >> 20);
+ }
+ account_scheduler_latency(tsk, delta >> 10, 0);
}
- account_scheduler_latency(tsk, delta >> 10, 0);
}
#endif
}
diff --git a/kernel/signal.c b/kernel/signal.c
index ccf1ceedaeb..64c5deeaca5 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2454,11 +2454,9 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s
stack_t oss;
int error;
- if (uoss) {
- oss.ss_sp = (void __user *) current->sas_ss_sp;
- oss.ss_size = current->sas_ss_size;
- oss.ss_flags = sas_ss_flags(sp);
- }
+ oss.ss_sp = (void __user *) current->sas_ss_sp;
+ oss.ss_size = current->sas_ss_size;
+ oss.ss_flags = sas_ss_flags(sp);
if (uss) {
void __user *ss_sp;
@@ -2466,10 +2464,12 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s
int ss_flags;
error = -EFAULT;
- if (!access_ok(VERIFY_READ, uss, sizeof(*uss))
- || __get_user(ss_sp, &uss->ss_sp)
- || __get_user(ss_flags, &uss->ss_flags)
- || __get_user(ss_size, &uss->ss_size))
+ if (!access_ok(VERIFY_READ, uss, sizeof(*uss)))
+ goto out;
+ error = __get_user(ss_sp, &uss->ss_sp) |
+ __get_user(ss_flags, &uss->ss_flags) |
+ __get_user(ss_size, &uss->ss_size);
+ if (error)
goto out;
error = -EPERM;
@@ -2501,13 +2501,16 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s
current->sas_ss_size = ss_size;
}
+ error = 0;
if (uoss) {
error = -EFAULT;
- if (copy_to_user(uoss, &oss, sizeof(oss)))
+ if (!access_ok(VERIFY_WRITE, uoss, sizeof(*uoss)))
goto out;
+ error = __put_user(oss.ss_sp, &uoss->ss_sp) |
+ __put_user(oss.ss_size, &uoss->ss_size) |
+ __put_user(oss.ss_flags, &uoss->ss_flags);
}
- error = 0;
out:
return error;
}
diff --git a/kernel/smp.c b/kernel/smp.c
index ad63d850120..94188b8ecc3 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -57,7 +57,7 @@ hotplug_cfd(struct notifier_block *nfb, unsigned long action, void *hcpu)
return NOTIFY_BAD;
break;
-#ifdef CONFIG_CPU_HOTPLUG
+#ifdef CONFIG_HOTPLUG_CPU
case CPU_UP_CANCELED:
case CPU_UP_CANCELED_FROZEN:
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 98e02328c67..58be76017fd 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -49,6 +49,7 @@
#include <linux/acpi.h>
#include <linux/reboot.h>
#include <linux/ftrace.h>
+#include <linux/security.h>
#include <linux/slow-work.h>
#include <linux/perf_counter.h>
@@ -1306,10 +1307,10 @@ static struct ctl_table vm_table[] = {
{
.ctl_name = CTL_UNNUMBERED,
.procname = "mmap_min_addr",
- .data = &mmap_min_addr,
- .maxlen = sizeof(unsigned long),
+ .data = &dac_mmap_min_addr,
+ .maxlen = sizeof(unsigned long),
.mode = 0644,
- .proc_handler = &proc_doulongvec_minmax,
+ .proc_handler = &mmap_min_addr_handler,
},
#ifdef CONFIG_NUMA
{
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index a6dcd67b041..620b58abdc3 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -137,11 +137,12 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,
*/
int clockevents_register_notifier(struct notifier_block *nb)
{
+ unsigned long flags;
int ret;
- spin_lock(&clockevents_lock);
+ spin_lock_irqsave(&clockevents_lock, flags);
ret = raw_notifier_chain_register(&clockevents_chain, nb);
- spin_unlock(&clockevents_lock);
+ spin_unlock_irqrestore(&clockevents_lock, flags);
return ret;
}
@@ -178,16 +179,18 @@ static void clockevents_notify_released(void)
*/
void clockevents_register_device(struct clock_event_device *dev)
{
+ unsigned long flags;
+
BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED);
BUG_ON(!dev->cpumask);
- spin_lock(&clockevents_lock);
+ spin_lock_irqsave(&clockevents_lock, flags);
list_add(&dev->list, &clockevent_devices);
clockevents_do_notify(CLOCK_EVT_NOTIFY_ADD, dev);
clockevents_notify_released();
- spin_unlock(&clockevents_lock);
+ spin_unlock_irqrestore(&clockevents_lock, flags);
}
EXPORT_SYMBOL_GPL(clockevents_register_device);
@@ -235,8 +238,9 @@ void clockevents_exchange_device(struct clock_event_device *old,
void clockevents_notify(unsigned long reason, void *arg)
{
struct list_head *node, *tmp;
+ unsigned long flags;
- spin_lock(&clockevents_lock);
+ spin_lock_irqsave(&clockevents_lock, flags);
clockevents_do_notify(reason, arg);
switch (reason) {
@@ -251,7 +255,7 @@ void clockevents_notify(unsigned long reason, void *arg)
default:
break;
}
- spin_unlock(&clockevents_lock);
+ spin_unlock_irqrestore(&clockevents_lock, flags);
}
EXPORT_SYMBOL_GPL(clockevents_notify);
#endif
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index 877dbedc311..c2ec25087a3 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -205,11 +205,11 @@ static void tick_handle_periodic_broadcast(struct clock_event_device *dev)
* Powerstate information: The system enters/leaves a state, where
* affected devices might stop
*/
-static void tick_do_broadcast_on_off(void *why)
+static void tick_do_broadcast_on_off(unsigned long *reason)
{
struct clock_event_device *bc, *dev;
struct tick_device *td;
- unsigned long flags, *reason = why;
+ unsigned long flags;
int cpu, bc_stopped;
spin_lock_irqsave(&tick_broadcast_lock, flags);
@@ -276,8 +276,7 @@ void tick_broadcast_on_off(unsigned long reason, int *oncpu)
printk(KERN_ERR "tick-broadcast: ignoring broadcast for "
"offline CPU #%d\n", *oncpu);
else
- smp_call_function_single(*oncpu, tick_do_broadcast_on_off,
- &reason, 1);
+ tick_do_broadcast_on_off(&reason);
}
/*
diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c
index a999b92a127..fddd69d16e0 100644
--- a/kernel/time/timer_list.c
+++ b/kernel/time/timer_list.c
@@ -286,7 +286,7 @@ static int __init init_timer_list_procfs(void)
{
struct proc_dir_entry *pe;
- pe = proc_create("timer_list", 0644, NULL, &timer_list_fops);
+ pe = proc_create("timer_list", 0444, NULL, &timer_list_fops);
if (!pe)
return -ENOMEM;
return 0;
diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c
index 1090b0aed9b..7a34cb563fe 100644
--- a/kernel/trace/blktrace.c
+++ b/kernel/trace/blktrace.c
@@ -267,8 +267,8 @@ static void blk_trace_free(struct blk_trace *bt)
{
debugfs_remove(bt->msg_file);
debugfs_remove(bt->dropped_file);
- debugfs_remove(bt->dir);
relay_close(bt->rchan);
+ debugfs_remove(bt->dir);
free_percpu(bt->sequence);
free_percpu(bt->msg_data);
kfree(bt);
@@ -378,18 +378,8 @@ static int blk_subbuf_start_callback(struct rchan_buf *buf, void *subbuf,
static int blk_remove_buf_file_callback(struct dentry *dentry)
{
- struct dentry *parent = dentry->d_parent;
debugfs_remove(dentry);
- /*
- * this will fail for all but the last file, but that is ok. what we
- * care about is the top level buts->name directory going away, when
- * the last trace file is gone. Then we don't have to rmdir() that
- * manually on trace stop, so it nicely solves the issue with
- * force killing of running traces.
- */
-
- debugfs_remove(parent);
return 0;
}
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 1f3ec2afa51..25edd5cc593 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1662,7 +1662,7 @@ ftrace_regex_open(struct inode *inode, struct file *file, int enable)
mutex_lock(&ftrace_regex_lock);
if ((file->f_mode & FMODE_WRITE) &&
- !(file->f_flags & O_APPEND))
+ (file->f_flags & O_TRUNC))
ftrace_filter_reset(enable);
if (file->f_mode & FMODE_READ) {
@@ -2278,7 +2278,11 @@ ftrace_regex_write(struct file *file, const char __user *ubuf,
read++;
cnt--;
- if (!(iter->flags & ~FTRACE_ITER_CONT)) {
+ /*
+ * If the parser haven't finished with the last write,
+ * continue reading the user input without skipping spaces.
+ */
+ if (!(iter->flags & FTRACE_ITER_CONT)) {
/* skip white space */
while (cnt && isspace(ch)) {
ret = get_user(ch, ubuf++);
@@ -2288,8 +2292,9 @@ ftrace_regex_write(struct file *file, const char __user *ubuf,
cnt--;
}
+ /* only spaces were written */
if (isspace(ch)) {
- file->f_pos += read;
+ *ppos += read;
ret = read;
goto out;
}
@@ -2319,12 +2324,12 @@ ftrace_regex_write(struct file *file, const char __user *ubuf,
if (ret)
goto out;
iter->buffer_idx = 0;
- } else
+ } else {
iter->flags |= FTRACE_ITER_CONT;
+ iter->buffer[iter->buffer_idx++] = ch;
+ }
-
- file->f_pos += read;
-
+ *ppos += read;
ret = read;
out:
mutex_unlock(&ftrace_regex_lock);
@@ -2577,7 +2582,7 @@ ftrace_graph_open(struct inode *inode, struct file *file)
mutex_lock(&graph_lock);
if ((file->f_mode & FMODE_WRITE) &&
- !(file->f_flags & O_APPEND)) {
+ (file->f_flags & O_TRUNC)) {
ftrace_graph_count = 0;
memset(ftrace_graph_funcs, 0, sizeof(ftrace_graph_funcs));
}
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index bf27bb7a63e..a330513d96c 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -735,6 +735,7 @@ ring_buffer_free(struct ring_buffer *buffer)
put_online_cpus();
+ kfree(buffer->buffers);
free_cpumask_var(buffer->cpumask);
kfree(buffer);
@@ -1785,7 +1786,7 @@ void ring_buffer_discard_commit(struct ring_buffer *buffer,
*/
RB_WARN_ON(buffer, !local_read(&cpu_buffer->committing));
- if (!rb_try_to_discard(cpu_buffer, event))
+ if (rb_try_to_discard(cpu_buffer, event))
goto out;
/*
@@ -2383,7 +2384,6 @@ rb_buffer_peek(struct ring_buffer *buffer, int cpu, u64 *ts)
* the box. Return the padding, and we will release
* the current locks, and try again.
*/
- rb_advance_reader(cpu_buffer);
return event;
case RINGBUF_TYPE_TIME_EXTEND:
@@ -2486,7 +2486,7 @@ static inline int rb_ok_to_lock(void)
* buffer too. A one time deal is all you get from reading
* the ring buffer from an NMI.
*/
- if (likely(!in_nmi() && !oops_in_progress))
+ if (likely(!in_nmi()))
return 1;
tracing_off_permanent();
@@ -2519,6 +2519,8 @@ ring_buffer_peek(struct ring_buffer *buffer, int cpu, u64 *ts)
if (dolock)
spin_lock(&cpu_buffer->reader_lock);
event = rb_buffer_peek(buffer, cpu, ts);
+ if (event && event->type_len == RINGBUF_TYPE_PADDING)
+ rb_advance_reader(cpu_buffer);
if (dolock)
spin_unlock(&cpu_buffer->reader_lock);
local_irq_restore(flags);
@@ -2590,12 +2592,9 @@ ring_buffer_consume(struct ring_buffer *buffer, int cpu, u64 *ts)
spin_lock(&cpu_buffer->reader_lock);
event = rb_buffer_peek(buffer, cpu, ts);
- if (!event)
- goto out_unlock;
-
- rb_advance_reader(cpu_buffer);
+ if (event)
+ rb_advance_reader(cpu_buffer);
- out_unlock:
if (dolock)
spin_unlock(&cpu_buffer->reader_lock);
local_irq_restore(flags);
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 8bc8d8afea6..8c358395d33 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -848,6 +848,7 @@ tracing_generic_entry_update(struct trace_entry *entry, unsigned long flags,
((pc & SOFTIRQ_MASK) ? TRACE_FLAG_SOFTIRQ : 0) |
(need_resched() ? TRACE_FLAG_NEED_RESCHED : 0);
}
+EXPORT_SYMBOL_GPL(tracing_generic_entry_update);
struct ring_buffer_event *trace_buffer_lock_reserve(struct trace_array *tr,
int type,
@@ -2031,7 +2032,7 @@ static int tracing_open(struct inode *inode, struct file *file)
/* If this file was open for write, then erase contents */
if ((file->f_mode & FMODE_WRITE) &&
- !(file->f_flags & O_APPEND)) {
+ (file->f_flags & O_TRUNC)) {
long cpu = (long) inode->i_private;
if (cpu == TRACE_PIPE_ALL_CPU)
@@ -3085,7 +3086,8 @@ tracing_fill_pipe_page(size_t rem, struct trace_iterator *iter)
break;
}
- trace_consume(iter);
+ if (ret != TRACE_TYPE_NO_CONSUME)
+ trace_consume(iter);
rem -= count;
if (!find_next_entry_inc(iter)) {
rem = 0;
@@ -3894,17 +3896,9 @@ trace_options_core_write(struct file *filp, const char __user *ubuf, size_t cnt,
if (ret < 0)
return ret;
- switch (val) {
- case 0:
- trace_flags &= ~(1 << index);
- break;
- case 1:
- trace_flags |= 1 << index;
- break;
-
- default:
+ if (val != 0 && val != 1)
return -EINVAL;
- }
+ set_tracer_flags(1 << index, val);
*ppos += cnt;
@@ -4233,8 +4227,11 @@ static void __ftrace_dump(bool disable_tracing)
iter.pos = -1;
if (find_next_entry_inc(&iter) != NULL) {
- print_trace_line(&iter);
- trace_consume(&iter);
+ int ret;
+
+ ret = print_trace_line(&iter);
+ if (ret != TRACE_TYPE_NO_CONSUME)
+ trace_consume(&iter);
}
trace_printk_seq(&iter.seq);
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 3548ae5cc78..8b9f4f6e955 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -438,10 +438,6 @@ struct trace_entry *tracing_get_trace_entry(struct trace_array *tr,
struct trace_entry *trace_find_next_entry(struct trace_iterator *iter,
int *ent_cpu, u64 *ent_ts);
-void tracing_generic_entry_update(struct trace_entry *entry,
- unsigned long flags,
- int pc);
-
void default_wait_pipe(struct trace_iterator *iter);
void poll_wait_pipe(struct trace_iterator *iter);
diff --git a/kernel/trace/trace_event_profile.c b/kernel/trace/trace_event_profile.c
index 5b5895afecf..11ba5bb4ed0 100644
--- a/kernel/trace/trace_event_profile.c
+++ b/kernel/trace/trace_event_profile.c
@@ -14,7 +14,7 @@ int ftrace_profile_enable(int event_id)
mutex_lock(&event_mutex);
list_for_each_entry(event, &ftrace_events, list) {
- if (event->id == event_id) {
+ if (event->id == event_id && event->profile_enable) {
ret = event->profile_enable(event);
break;
}
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 53c8fd376a8..e75276a49cf 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -376,7 +376,7 @@ ftrace_event_seq_open(struct inode *inode, struct file *file)
const struct seq_operations *seq_ops;
if ((file->f_mode & FMODE_WRITE) &&
- !(file->f_flags & O_APPEND))
+ (file->f_flags & O_TRUNC))
ftrace_clear_events();
seq_ops = inode->i_private;
@@ -940,7 +940,7 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events,
entry = trace_create_file("enable", 0644, call->dir, call,
enable);
- if (call->id)
+ if (call->id && call->profile_enable)
entry = trace_create_file("id", 0444, call->dir, call,
id);
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 936c621bbf4..f32dc9d1ea7 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -624,9 +624,6 @@ static int filter_add_subsystem_pred(struct filter_parse_state *ps,
return -ENOSPC;
}
- filter->preds[filter->n_preds] = pred;
- filter->n_preds++;
-
list_for_each_entry(call, &ftrace_events, list) {
if (!call->define_fields)
@@ -643,6 +640,9 @@ static int filter_add_subsystem_pred(struct filter_parse_state *ps,
}
replace_filter_string(call->filter, filter_string);
}
+
+ filter->preds[filter->n_preds] = pred;
+ filter->n_preds++;
out:
return err;
}
@@ -1029,12 +1029,17 @@ static int replace_preds(struct event_subsystem *system,
if (elt->op == OP_AND || elt->op == OP_OR) {
pred = create_logical_pred(elt->op);
+ if (!pred)
+ return -ENOMEM;
if (call) {
err = filter_add_pred(ps, call, pred);
filter_free_pred(pred);
- } else
+ } else {
err = filter_add_subsystem_pred(ps, system,
pred, filter_string);
+ if (err)
+ filter_free_pred(pred);
+ }
if (err)
return err;
@@ -1048,12 +1053,17 @@ static int replace_preds(struct event_subsystem *system,
}
pred = create_pred(elt->op, operand1, operand2);
+ if (!pred)
+ return -ENOMEM;
if (call) {
err = filter_add_pred(ps, call, pred);
filter_free_pred(pred);
- } else
+ } else {
err = filter_add_subsystem_pred(ps, system, pred,
filter_string);
+ if (err)
+ filter_free_pred(pred);
+ }
if (err)
return err;
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index d2249abafb5..420ec348757 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -843,9 +843,16 @@ print_graph_function(struct trace_iterator *iter)
switch (entry->type) {
case TRACE_GRAPH_ENT: {
- struct ftrace_graph_ent_entry *field;
+ /*
+ * print_graph_entry() may consume the current event,
+ * thus @field may become invalid, so we need to save it.
+ * sizeof(struct ftrace_graph_ent_entry) is very small,
+ * it can be safely saved at the stack.
+ */
+ struct ftrace_graph_ent_entry *field, saved;
trace_assign_type(field, entry);
- return print_graph_entry(field, s, iter);
+ saved = *field;
+ return print_graph_entry(&saved, s, iter);
}
case TRACE_GRAPH_RET: {
struct ftrace_graph_ret_entry *field;
diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c
index 7b627811082..687699d365a 100644
--- a/kernel/trace/trace_printk.c
+++ b/kernel/trace/trace_printk.c
@@ -176,7 +176,7 @@ static int t_show(struct seq_file *m, void *v)
const char *str = *fmt;
int i;
- seq_printf(m, "0x%lx : \"", (unsigned long)fmt);
+ seq_printf(m, "0x%lx : \"", *(unsigned long *)fmt);
/*
* Tabs and new lines need to be converted.
diff --git a/kernel/wait.c b/kernel/wait.c
index ea7c3b4275c..c4bd3d825f3 100644
--- a/kernel/wait.c
+++ b/kernel/wait.c
@@ -10,13 +10,14 @@
#include <linux/wait.h>
#include <linux/hash.h>
-void init_waitqueue_head(wait_queue_head_t *q)
+void __init_waitqueue_head(wait_queue_head_t *q, struct lock_class_key *key)
{
spin_lock_init(&q->lock);
+ lockdep_set_class(&q->lock, key);
INIT_LIST_HEAD(&q->task_list);
}
-EXPORT_SYMBOL(init_waitqueue_head);
+EXPORT_SYMBOL(__init_waitqueue_head);
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
diff --git a/lib/bitmap.c b/lib/bitmap.c
index 35a1f7ff414..702565821c9 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -179,14 +179,16 @@ void __bitmap_shift_left(unsigned long *dst,
}
EXPORT_SYMBOL(__bitmap_shift_left);
-void __bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
+int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k;
int nr = BITS_TO_LONGS(bits);
+ unsigned long result = 0;
for (k = 0; k < nr; k++)
- dst[k] = bitmap1[k] & bitmap2[k];
+ result |= (dst[k] = bitmap1[k] & bitmap2[k]);
+ return result != 0;
}
EXPORT_SYMBOL(__bitmap_and);
@@ -212,14 +214,16 @@ void __bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
}
EXPORT_SYMBOL(__bitmap_xor);
-void __bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
+int __bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k;
int nr = BITS_TO_LONGS(bits);
+ unsigned long result = 0;
for (k = 0; k < nr; k++)
- dst[k] = bitmap1[k] & ~bitmap2[k];
+ result |= (dst[k] = bitmap1[k] & ~bitmap2[k]);
+ return result != 0;
}
EXPORT_SYMBOL(__bitmap_andnot);
diff --git a/lib/decompress_bunzip2.c b/lib/decompress_bunzip2.c
index 708e2a86d87..600f473a561 100644
--- a/lib/decompress_bunzip2.c
+++ b/lib/decompress_bunzip2.c
@@ -45,12 +45,14 @@
*/
-#ifndef STATIC
+#ifdef STATIC
+#define PREBOOT
+#else
#include <linux/decompress/bunzip2.h>
-#endif /* !STATIC */
+#include <linux/slab.h>
+#endif /* STATIC */
#include <linux/decompress/mm.h>
-#include <linux/slab.h>
#ifndef INT_MAX
#define INT_MAX 0x7fffffff
@@ -681,9 +683,7 @@ STATIC int INIT bunzip2(unsigned char *buf, int len,
set_error_fn(error_fn);
if (flush)
outbuf = malloc(BZIP2_IOBUF_SIZE);
- else
- len -= 4; /* Uncompressed size hack active in pre-boot
- environment */
+
if (!outbuf) {
error("Could not allocate output bufer");
return -1;
@@ -733,4 +733,14 @@ exit_0:
return i;
}
-#define decompress bunzip2
+#ifdef PREBOOT
+STATIC int INIT decompress(unsigned char *buf, int len,
+ int(*fill)(void*, unsigned int),
+ int(*flush)(void*, unsigned int),
+ unsigned char *outbuf,
+ int *pos,
+ void(*error_fn)(char *x))
+{
+ return bunzip2(buf, len - 4, fill, flush, outbuf, pos, error_fn);
+}
+#endif
diff --git a/lib/decompress_inflate.c b/lib/decompress_inflate.c
index e36b296fc9f..68dfce59c1b 100644
--- a/lib/decompress_inflate.c
+++ b/lib/decompress_inflate.c
@@ -19,13 +19,13 @@
#include "zlib_inflate/inflate.h"
#include "zlib_inflate/infutil.h"
+#include <linux/slab.h>
#endif /* STATIC */
#include <linux/decompress/mm.h>
-#include <linux/slab.h>
-#define INBUF_LEN (16*1024)
+#define GZIP_IOBUF_SIZE (16*1024)
/* Included from initramfs et al code */
STATIC int INIT gunzip(unsigned char *buf, int len,
@@ -55,7 +55,7 @@ STATIC int INIT gunzip(unsigned char *buf, int len,
if (buf)
zbuf = buf;
else {
- zbuf = malloc(INBUF_LEN);
+ zbuf = malloc(GZIP_IOBUF_SIZE);
len = 0;
}
if (!zbuf) {
@@ -77,7 +77,7 @@ STATIC int INIT gunzip(unsigned char *buf, int len,
}
if (len == 0)
- len = fill(zbuf, INBUF_LEN);
+ len = fill(zbuf, GZIP_IOBUF_SIZE);
/* verify the gzip header */
if (len < 10 ||
@@ -113,7 +113,7 @@ STATIC int INIT gunzip(unsigned char *buf, int len,
while (rc == Z_OK) {
if (strm->avail_in == 0) {
/* TODO: handle case where both pos and fill are set */
- len = fill(zbuf, INBUF_LEN);
+ len = fill(zbuf, GZIP_IOBUF_SIZE);
if (len < 0) {
rc = -1;
error("read error");
diff --git a/lib/decompress_unlzma.c b/lib/decompress_unlzma.c
index 32123a1340e..0b954e04bd3 100644
--- a/lib/decompress_unlzma.c
+++ b/lib/decompress_unlzma.c
@@ -29,12 +29,14 @@
*Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef STATIC
+#ifdef STATIC
+#define PREBOOT
+#else
#include <linux/decompress/unlzma.h>
+#include <linux/slab.h>
#endif /* STATIC */
#include <linux/decompress/mm.h>
-#include <linux/slab.h>
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
@@ -543,9 +545,7 @@ STATIC inline int INIT unlzma(unsigned char *buf, int in_len,
int ret = -1;
set_error_fn(error_fn);
- if (!flush)
- in_len -= 4; /* Uncompressed size hack active in pre-boot
- environment */
+
if (buf)
inbuf = buf;
else
@@ -645,4 +645,15 @@ exit_0:
return ret;
}
-#define decompress unlzma
+#ifdef PREBOOT
+STATIC int INIT decompress(unsigned char *buf, int in_len,
+ int(*fill)(void*, unsigned int),
+ int(*flush)(void*, unsigned int),
+ unsigned char *output,
+ int *posp,
+ void(*error_fn)(char *x)
+ )
+{
+ return unlzma(buf, in_len - 4, fill, flush, output, posp, error_fn);
+}
+#endif
diff --git a/lib/dma-debug.c b/lib/dma-debug.c
index 65b0d99b6d0..58a9f9fc609 100644
--- a/lib/dma-debug.c
+++ b/lib/dma-debug.c
@@ -156,9 +156,13 @@ static bool driver_filter(struct device *dev)
return true;
/* driver filter on and initialized */
- if (current_driver && dev->driver == current_driver)
+ if (current_driver && dev && dev->driver == current_driver)
return true;
+ /* driver filter on, but we can't filter on a NULL device... */
+ if (!dev)
+ return false;
+
if (current_driver || !current_driver_name[0])
return false;
@@ -183,17 +187,17 @@ static bool driver_filter(struct device *dev)
return ret;
}
-#define err_printk(dev, entry, format, arg...) do { \
- error_count += 1; \
- if (driver_filter(dev) && \
- (show_all_errors || show_num_errors > 0)) { \
- WARN(1, "%s %s: " format, \
- dev_driver_string(dev), \
- dev_name(dev) , ## arg); \
- dump_entry_trace(entry); \
- } \
- if (!show_all_errors && show_num_errors > 0) \
- show_num_errors -= 1; \
+#define err_printk(dev, entry, format, arg...) do { \
+ error_count += 1; \
+ if (driver_filter(dev) && \
+ (show_all_errors || show_num_errors > 0)) { \
+ WARN(1, "%s %s: " format, \
+ dev ? dev_driver_string(dev) : "NULL", \
+ dev ? dev_name(dev) : "NULL", ## arg); \
+ dump_entry_trace(entry); \
+ } \
+ if (!show_all_errors && show_num_errors > 0) \
+ show_num_errors -= 1; \
} while (0);
/*
diff --git a/lib/flex_array.c b/lib/flex_array.c
index 0e7894ce888..7baed2fc3bc 100644
--- a/lib/flex_array.c
+++ b/lib/flex_array.c
@@ -99,7 +99,8 @@ static inline int elements_fit_in_base(struct flex_array *fa)
* capacity in the base structure. Also note that no effort is made
* to efficiently pack objects across page boundaries.
*/
-struct flex_array *flex_array_alloc(int element_size, int total, gfp_t flags)
+struct flex_array *flex_array_alloc(int element_size, unsigned int total,
+ gfp_t flags)
{
struct flex_array *ret;
int max_size = nr_base_part_ptrs() * __elements_per_part(element_size);
@@ -115,16 +116,14 @@ struct flex_array *flex_array_alloc(int element_size, int total, gfp_t flags)
return ret;
}
-static int fa_element_to_part_nr(struct flex_array *fa, int element_nr)
+static int fa_element_to_part_nr(struct flex_array *fa,
+ unsigned int element_nr)
{
return element_nr / __elements_per_part(fa->element_size);
}
/**
* flex_array_free_parts - just free the second-level pages
- * @src: address of data to copy into the array
- * @element_nr: index of the position in which to insert
- * the new element.
*
* This is to be used in cases where the base 'struct flex_array'
* has been statically allocated and should not be free.
@@ -146,14 +145,12 @@ void flex_array_free(struct flex_array *fa)
kfree(fa);
}
-static int fa_index_inside_part(struct flex_array *fa, int element_nr)
+static unsigned int index_inside_part(struct flex_array *fa,
+ unsigned int element_nr)
{
- return element_nr % __elements_per_part(fa->element_size);
-}
+ unsigned int part_offset;
-static int index_inside_part(struct flex_array *fa, int element_nr)
-{
- int part_offset = fa_index_inside_part(fa, element_nr);
+ part_offset = element_nr % __elements_per_part(fa->element_size);
return part_offset * fa->element_size;
}
@@ -188,7 +185,8 @@ __fa_get_part(struct flex_array *fa, int part_nr, gfp_t flags)
*
* Locking must be provided by the caller.
*/
-int flex_array_put(struct flex_array *fa, int element_nr, void *src, gfp_t flags)
+int flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src,
+ gfp_t flags)
{
int part_nr = fa_element_to_part_nr(fa, element_nr);
struct flex_array_part *part;
@@ -198,10 +196,11 @@ int flex_array_put(struct flex_array *fa, int element_nr, void *src, gfp_t flags
return -ENOSPC;
if (elements_fit_in_base(fa))
part = (struct flex_array_part *)&fa->parts[0];
- else
+ else {
part = __fa_get_part(fa, part_nr, flags);
- if (!part)
- return -ENOMEM;
+ if (!part)
+ return -ENOMEM;
+ }
dst = &part->elements[index_inside_part(fa, element_nr)];
memcpy(dst, src, fa->element_size);
return 0;
@@ -219,7 +218,8 @@ int flex_array_put(struct flex_array *fa, int element_nr, void *src, gfp_t flags
*
* Locking must be provided by the caller.
*/
-int flex_array_prealloc(struct flex_array *fa, int start, int end, gfp_t flags)
+int flex_array_prealloc(struct flex_array *fa, unsigned int start,
+ unsigned int end, gfp_t flags)
{
int start_part;
int end_part;
@@ -250,20 +250,19 @@ int flex_array_prealloc(struct flex_array *fa, int start, int end, gfp_t flags)
*
* Locking must be provided by the caller.
*/
-void *flex_array_get(struct flex_array *fa, int element_nr)
+void *flex_array_get(struct flex_array *fa, unsigned int element_nr)
{
int part_nr = fa_element_to_part_nr(fa, element_nr);
struct flex_array_part *part;
- int index;
if (element_nr >= fa->total_nr_elements)
return NULL;
- if (!fa->parts[part_nr])
- return NULL;
if (elements_fit_in_base(fa))
part = (struct flex_array_part *)&fa->parts[0];
- else
+ else {
part = fa->parts[part_nr];
- index = index_inside_part(fa, element_nr);
+ if (!part)
+ return NULL;
+ }
return &part->elements[index_inside_part(fa, element_nr)];
}
diff --git a/lib/lmb.c b/lib/lmb.c
index e4a6482d8b2..0343c05609f 100644
--- a/lib/lmb.c
+++ b/lib/lmb.c
@@ -429,7 +429,7 @@ u64 __init lmb_phys_mem_size(void)
return lmb.memory.size;
}
-u64 __init lmb_end_of_DRAM(void)
+u64 lmb_end_of_DRAM(void)
{
int idx = lmb.memory.cnt - 1;
diff --git a/mm/Kconfig b/mm/Kconfig
index c948d4ca8bd..fe5f674d7a7 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -225,9 +225,9 @@ config DEFAULT_MMAP_MIN_ADDR
For most ia64, ppc64 and x86 users with lots of address space
a value of 65536 is reasonable and should cause no problems.
On arm and other archs it should not be higher than 32768.
- Programs which use vm86 functionality would either need additional
- permissions from either the LSM or the capabilities module or have
- this protection disabled.
+ Programs which use vm86 functionality or have some need to map
+ this low address space will need CAP_SYS_RAWIO or disable this
+ protection by setting the value to 0.
This value can be changed after boot using the
/proc/sys/vm/mmap_min_addr tunable.
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index e08e2c4da63..7dd9d9f8069 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -191,25 +191,27 @@ static int mpol_new_bind(struct mempolicy *pol, const nodemask_t *nodes)
* Must be called holding task's alloc_lock to protect task's mems_allowed
* and mempolicy. May also be called holding the mmap_semaphore for write.
*/
-static int mpol_set_nodemask(struct mempolicy *pol, const nodemask_t *nodes)
+static int mpol_set_nodemask(struct mempolicy *pol,
+ const nodemask_t *nodes, struct nodemask_scratch *nsc)
{
- nodemask_t cpuset_context_nmask;
int ret;
/* if mode is MPOL_DEFAULT, pol is NULL. This is right. */
if (pol == NULL)
return 0;
+ /* Check N_HIGH_MEMORY */
+ nodes_and(nsc->mask1,
+ cpuset_current_mems_allowed, node_states[N_HIGH_MEMORY]);
VM_BUG_ON(!nodes);
if (pol->mode == MPOL_PREFERRED && nodes_empty(*nodes))
nodes = NULL; /* explicit local allocation */
else {
if (pol->flags & MPOL_F_RELATIVE_NODES)
- mpol_relative_nodemask(&cpuset_context_nmask, nodes,
- &cpuset_current_mems_allowed);
+ mpol_relative_nodemask(&nsc->mask2, nodes,&nsc->mask1);
else
- nodes_and(cpuset_context_nmask, *nodes,
- cpuset_current_mems_allowed);
+ nodes_and(nsc->mask2, *nodes, nsc->mask1);
+
if (mpol_store_user_nodemask(pol))
pol->w.user_nodemask = *nodes;
else
@@ -217,8 +219,10 @@ static int mpol_set_nodemask(struct mempolicy *pol, const nodemask_t *nodes)
cpuset_current_mems_allowed;
}
- ret = mpol_ops[pol->mode].create(pol,
- nodes ? &cpuset_context_nmask : NULL);
+ if (nodes)
+ ret = mpol_ops[pol->mode].create(pol, &nsc->mask2);
+ else
+ ret = mpol_ops[pol->mode].create(pol, NULL);
return ret;
}
@@ -620,12 +624,17 @@ static long do_set_mempolicy(unsigned short mode, unsigned short flags,
{
struct mempolicy *new, *old;
struct mm_struct *mm = current->mm;
+ NODEMASK_SCRATCH(scratch);
int ret;
- new = mpol_new(mode, flags, nodes);
- if (IS_ERR(new))
- return PTR_ERR(new);
+ if (!scratch)
+ return -ENOMEM;
+ new = mpol_new(mode, flags, nodes);
+ if (IS_ERR(new)) {
+ ret = PTR_ERR(new);
+ goto out;
+ }
/*
* prevent changing our mempolicy while show_numa_maps()
* is using it.
@@ -635,13 +644,13 @@ static long do_set_mempolicy(unsigned short mode, unsigned short flags,
if (mm)
down_write(&mm->mmap_sem);
task_lock(current);
- ret = mpol_set_nodemask(new, nodes);
+ ret = mpol_set_nodemask(new, nodes, scratch);
if (ret) {
task_unlock(current);
if (mm)
up_write(&mm->mmap_sem);
mpol_put(new);
- return ret;
+ goto out;
}
old = current->mempolicy;
current->mempolicy = new;
@@ -654,7 +663,10 @@ static long do_set_mempolicy(unsigned short mode, unsigned short flags,
up_write(&mm->mmap_sem);
mpol_put(old);
- return 0;
+ ret = 0;
+out:
+ NODEMASK_SCRATCH_FREE(scratch);
+ return ret;
}
/*
@@ -1014,12 +1026,20 @@ static long do_mbind(unsigned long start, unsigned long len,
if (err)
return err;
}
- down_write(&mm->mmap_sem);
- task_lock(current);
- err = mpol_set_nodemask(new, nmask);
- task_unlock(current);
+ {
+ NODEMASK_SCRATCH(scratch);
+ if (scratch) {
+ down_write(&mm->mmap_sem);
+ task_lock(current);
+ err = mpol_set_nodemask(new, nmask, scratch);
+ task_unlock(current);
+ if (err)
+ up_write(&mm->mmap_sem);
+ } else
+ err = -ENOMEM;
+ NODEMASK_SCRATCH_FREE(scratch);
+ }
if (err) {
- up_write(&mm->mmap_sem);
mpol_put(new);
return err;
}
@@ -1891,6 +1911,7 @@ restart:
* Install non-NULL @mpol in inode's shared policy rb-tree.
* On entry, the current task has a reference on a non-NULL @mpol.
* This must be released on exit.
+ * This is called at get_inode() calls and we can use GFP_KERNEL.
*/
void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol)
{
@@ -1902,19 +1923,24 @@ void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol)
if (mpol) {
struct vm_area_struct pvma;
struct mempolicy *new;
+ NODEMASK_SCRATCH(scratch);
+ if (!scratch)
+ return;
/* contextualize the tmpfs mount point mempolicy */
new = mpol_new(mpol->mode, mpol->flags, &mpol->w.user_nodemask);
if (IS_ERR(new)) {
mpol_put(mpol); /* drop our ref on sb mpol */
+ NODEMASK_SCRATCH_FREE(scratch);
return; /* no valid nodemask intersection */
}
task_lock(current);
- ret = mpol_set_nodemask(new, &mpol->w.user_nodemask);
+ ret = mpol_set_nodemask(new, &mpol->w.user_nodemask, scratch);
task_unlock(current);
mpol_put(mpol); /* drop our ref on sb mpol */
if (ret) {
+ NODEMASK_SCRATCH_FREE(scratch);
mpol_put(new);
return;
}
@@ -1924,6 +1950,7 @@ void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol)
pvma.vm_end = TASK_SIZE; /* policy covers entire file */
mpol_set_shared_policy(sp, &pvma, new); /* adds ref */
mpol_put(new); /* drop initial ref */
+ NODEMASK_SCRATCH_FREE(scratch);
}
}
@@ -2140,13 +2167,18 @@ int mpol_parse_str(char *str, struct mempolicy **mpol, int no_context)
err = 1;
else {
int ret;
-
- task_lock(current);
- ret = mpol_set_nodemask(new, &nodes);
- task_unlock(current);
- if (ret)
+ NODEMASK_SCRATCH(scratch);
+ if (scratch) {
+ task_lock(current);
+ ret = mpol_set_nodemask(new, &nodes, scratch);
+ task_unlock(current);
+ } else
+ ret = -ENOMEM;
+ NODEMASK_SCRATCH_FREE(scratch);
+ if (ret) {
err = 1;
- else if (no_context) {
+ mpol_put(new);
+ } else if (no_context) {
/* save for contextualization */
new->w.user_nodemask = nodes;
}
diff --git a/mm/mempool.c b/mm/mempool.c
index a46eb1b4bb6..32e75d40050 100644
--- a/mm/mempool.c
+++ b/mm/mempool.c
@@ -303,14 +303,14 @@ EXPORT_SYMBOL(mempool_free_slab);
*/
void *mempool_kmalloc(gfp_t gfp_mask, void *pool_data)
{
- size_t size = (size_t)(long)pool_data;
+ size_t size = (size_t)pool_data;
return kmalloc(size, gfp_mask);
}
EXPORT_SYMBOL(mempool_kmalloc);
void *mempool_kzalloc(gfp_t gfp_mask, void *pool_data)
{
- size_t size = (size_t) pool_data;
+ size_t size = (size_t)pool_data;
return kzalloc(size, gfp_mask);
}
EXPORT_SYMBOL(mempool_kzalloc);
diff --git a/mm/mmap.c b/mm/mmap.c
index 34579b23ebd..8101de490c7 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -88,9 +88,6 @@ int sysctl_overcommit_ratio = 50; /* default is 50% */
int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT;
struct percpu_counter vm_committed_as;
-/* amount of vm to protect from userspace access */
-unsigned long mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
-
/*
* Check that a process has enough memory to allocate a new virtual
* mapping. 0 means there is enough memory for the allocation to
diff --git a/mm/nommu.c b/mm/nommu.c
index 53cab10fece..66e81e7e9fe 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -69,9 +69,6 @@ int sysctl_max_map_count = DEFAULT_MAX_MAP_COUNT;
int sysctl_nr_trim_pages = CONFIG_NOMMU_INITIAL_TRIM_EXCESS;
int heap_stack_gap = 0;
-/* amount of vm to protect from userspace access */
-unsigned long mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
-
atomic_long_t mmap_pages_allocated;
EXPORT_SYMBOL(mem_map);
@@ -922,6 +919,10 @@ static int validate_mmap_request(struct file *file,
if (!file->f_op->read)
capabilities &= ~BDI_CAP_MAP_COPY;
+ /* The file shall have been opened with read permission. */
+ if (!(file->f_mode & FMODE_READ))
+ return -EACCES;
+
if (flags & MAP_SHARED) {
/* do checks for writing, appending and locking */
if ((prot & PROT_WRITE) &&
@@ -1351,6 +1352,7 @@ unsigned long do_mmap_pgoff(struct file *file,
}
vma->vm_region = region;
+ add_nommu_region(region);
/* set up the mapping */
if (file && vma->vm_flags & VM_SHARED)
@@ -1360,8 +1362,6 @@ unsigned long do_mmap_pgoff(struct file *file,
if (ret < 0)
goto error_put_region;
- add_nommu_region(region);
-
/* okay... we have a mapping; now we have to register it */
result = vma->vm_start;
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index 175a67a78a9..a7b2460e922 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -58,7 +58,6 @@ unsigned long badness(struct task_struct *p, unsigned long uptime)
unsigned long points, cpu_time, run_time;
struct mm_struct *mm;
struct task_struct *child;
- int oom_adj;
task_lock(p);
mm = p->mm;
@@ -66,11 +65,6 @@ unsigned long badness(struct task_struct *p, unsigned long uptime)
task_unlock(p);
return 0;
}
- oom_adj = mm->oom_adj;
- if (oom_adj == OOM_DISABLE) {
- task_unlock(p);
- return 0;
- }
/*
* The memory size of the process is the basis for the badness.
@@ -154,15 +148,15 @@ unsigned long badness(struct task_struct *p, unsigned long uptime)
points /= 8;
/*
- * Adjust the score by oom_adj.
+ * Adjust the score by oomkilladj.
*/
- if (oom_adj) {
- if (oom_adj > 0) {
+ if (p->oomkilladj) {
+ if (p->oomkilladj > 0) {
if (!points)
points = 1;
- points <<= oom_adj;
+ points <<= p->oomkilladj;
} else
- points >>= -(oom_adj);
+ points >>= -(p->oomkilladj);
}
#ifdef DEBUG
@@ -257,8 +251,11 @@ static struct task_struct *select_bad_process(unsigned long *ppoints,
*ppoints = ULONG_MAX;
}
+ if (p->oomkilladj == OOM_DISABLE)
+ continue;
+
points = badness(p, uptime.tv_sec);
- if (points > *ppoints) {
+ if (points > *ppoints || !chosen) {
chosen = p;
*ppoints = points;
}
@@ -307,7 +304,8 @@ static void dump_tasks(const struct mem_cgroup *mem)
}
printk(KERN_INFO "[%5d] %5d %5d %8lu %8lu %3d %3d %s\n",
p->pid, __task_cred(p)->uid, p->tgid, mm->total_vm,
- get_mm_rss(mm), (int)task_cpu(p), mm->oom_adj, p->comm);
+ get_mm_rss(mm), (int)task_cpu(p), p->oomkilladj,
+ p->comm);
task_unlock(p);
} while_each_thread(g, p);
}
@@ -325,8 +323,11 @@ static void __oom_kill_task(struct task_struct *p, int verbose)
return;
}
- if (!p->mm)
+ if (!p->mm) {
+ WARN_ON(1);
+ printk(KERN_WARNING "tried to kill an mm-less task!\n");
return;
+ }
if (verbose)
printk(KERN_ERR "Killed process %d (%s)\n",
@@ -348,13 +349,28 @@ static int oom_kill_task(struct task_struct *p)
struct mm_struct *mm;
struct task_struct *g, *q;
- task_lock(p);
mm = p->mm;
- if (!mm || mm->oom_adj == OOM_DISABLE) {
- task_unlock(p);
+
+ /* WARNING: mm may not be dereferenced since we did not obtain its
+ * value from get_task_mm(p). This is OK since all we need to do is
+ * compare mm to q->mm below.
+ *
+ * Furthermore, even if mm contains a non-NULL value, p->mm may
+ * change to NULL at any time since we do not hold task_lock(p).
+ * However, this is of no concern to us.
+ */
+
+ if (mm == NULL)
return 1;
- }
- task_unlock(p);
+
+ /*
+ * Don't kill the process if any threads are set to OOM_DISABLE
+ */
+ do_each_thread(g, q) {
+ if (q->mm == mm && q->oomkilladj == OOM_DISABLE)
+ return 1;
+ } while_each_thread(g, q);
+
__oom_kill_task(p, 1);
/*
@@ -377,11 +393,10 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
struct task_struct *c;
if (printk_ratelimit()) {
- task_lock(current);
printk(KERN_WARNING "%s invoked oom-killer: "
- "gfp_mask=0x%x, order=%d, oom_adj=%d\n",
- current->comm, gfp_mask, order,
- current->mm ? current->mm->oom_adj : OOM_DISABLE);
+ "gfp_mask=0x%x, order=%d, oomkilladj=%d\n",
+ current->comm, gfp_mask, order, current->oomkilladj);
+ task_lock(current);
cpuset_print_task_mems_allowed(current);
task_unlock(current);
dump_stack();
@@ -394,9 +409,8 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
/*
* If the task is already exiting, don't alarm the sysadmin or kill
* its children or threads, just set TIF_MEMDIE so it can die quickly
- * if its mm is still attached.
*/
- if (p->mm && (p->flags & PF_EXITING)) {
+ if (p->flags & PF_EXITING) {
__oom_kill_task(p, 0);
return 0;
}
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index d052abbe306..a0de15f4698 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -817,13 +817,15 @@ __rmqueue_fallback(struct zone *zone, int order, int start_migratetype)
* agressive about taking ownership of free pages
*/
if (unlikely(current_order >= (pageblock_order >> 1)) ||
- start_migratetype == MIGRATE_RECLAIMABLE) {
+ start_migratetype == MIGRATE_RECLAIMABLE ||
+ page_group_by_mobility_disabled) {
unsigned long pages;
pages = move_freepages_block(zone, page,
start_migratetype);
/* Claim the whole block if over half of it is free */
- if (pages >= (1 << (pageblock_order-1)))
+ if (pages >= (1 << (pageblock_order-1)) ||
+ page_group_by_mobility_disabled)
set_pageblock_migratetype(page,
start_migratetype);
@@ -2544,7 +2546,6 @@ static void build_zonelists(pg_data_t *pgdat)
prev_node = local_node;
nodes_clear(used_mask);
- memset(node_load, 0, sizeof(node_load));
memset(node_order, 0, sizeof(node_order));
j = 0;
@@ -2653,6 +2654,9 @@ static int __build_all_zonelists(void *dummy)
{
int nid;
+#ifdef CONFIG_NUMA
+ memset(node_load, 0, sizeof(node_load));
+#endif
for_each_online_node(nid) {
pg_data_t *pgdat = NODE_DATA(nid);
diff --git a/mm/percpu.c b/mm/percpu.c
index b70f2acd885..3311c8919f3 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -8,12 +8,12 @@
*
* This is percpu allocator which can handle both static and dynamic
* areas. Percpu areas are allocated in chunks in vmalloc area. Each
- * chunk is consisted of num_possible_cpus() units and the first chunk
- * is used for static percpu variables in the kernel image (special
- * boot time alloc/init handling necessary as these areas need to be
- * brought up before allocation services are running). Unit grows as
- * necessary and all units grow or shrink in unison. When a chunk is
- * filled up, another chunk is allocated. ie. in vmalloc area
+ * chunk is consisted of nr_cpu_ids units and the first chunk is used
+ * for static percpu variables in the kernel image (special boot time
+ * alloc/init handling necessary as these areas need to be brought up
+ * before allocation services are running). Unit grows as necessary
+ * and all units grow or shrink in unison. When a chunk is filled up,
+ * another chunk is allocated. ie. in vmalloc area
*
* c0 c1 c2
* ------------------- ------------------- ------------
@@ -197,7 +197,12 @@ static unsigned long pcpu_chunk_addr(struct pcpu_chunk *chunk,
static bool pcpu_chunk_page_occupied(struct pcpu_chunk *chunk,
int page_idx)
{
- return *pcpu_chunk_pagep(chunk, 0, page_idx) != NULL;
+ /*
+ * Any possible cpu id can be used here, so there's no need to
+ * worry about preemption or cpu hotplug.
+ */
+ return *pcpu_chunk_pagep(chunk, raw_smp_processor_id(),
+ page_idx) != NULL;
}
/* set the pointer to a chunk in a page struct */
@@ -297,6 +302,14 @@ static struct pcpu_chunk *pcpu_chunk_addr_search(void *addr)
return pcpu_first_chunk;
}
+ /*
+ * The address is relative to unit0 which might be unused and
+ * thus unmapped. Offset the address to the unit space of the
+ * current processor before looking it up in the vmalloc
+ * space. Note that any possible cpu id can be used here, so
+ * there's no need to worry about preemption or cpu hotplug.
+ */
+ addr += raw_smp_processor_id() * pcpu_unit_size;
return pcpu_get_page_chunk(vmalloc_to_page(addr));
}
@@ -558,7 +571,7 @@ static void pcpu_free_area(struct pcpu_chunk *chunk, int freeme)
static void pcpu_unmap(struct pcpu_chunk *chunk, int page_start, int page_end,
bool flush_tlb)
{
- unsigned int last = num_possible_cpus() - 1;
+ unsigned int last = nr_cpu_ids - 1;
unsigned int cpu;
/* unmap must not be done on immutable chunk */
@@ -643,7 +656,7 @@ static void pcpu_depopulate_chunk(struct pcpu_chunk *chunk, int off, int size,
*/
static int pcpu_map(struct pcpu_chunk *chunk, int page_start, int page_end)
{
- unsigned int last = num_possible_cpus() - 1;
+ unsigned int last = nr_cpu_ids - 1;
unsigned int cpu;
int err;
@@ -749,7 +762,7 @@ static struct pcpu_chunk *alloc_pcpu_chunk(void)
chunk->map[chunk->map_used++] = pcpu_unit_size;
chunk->page = chunk->page_ar;
- chunk->vm = get_vm_area(pcpu_chunk_size, GFP_KERNEL);
+ chunk->vm = get_vm_area(pcpu_chunk_size, VM_ALLOC);
if (!chunk->vm) {
free_pcpu_chunk(chunk);
return NULL;
@@ -1067,9 +1080,9 @@ size_t __init pcpu_setup_first_chunk(pcpu_get_page_fn_t get_page_fn,
PFN_UP(size_sum));
pcpu_unit_size = pcpu_unit_pages << PAGE_SHIFT;
- pcpu_chunk_size = num_possible_cpus() * pcpu_unit_size;
+ pcpu_chunk_size = nr_cpu_ids * pcpu_unit_size;
pcpu_chunk_struct_size = sizeof(struct pcpu_chunk)
- + num_possible_cpus() * pcpu_unit_pages * sizeof(struct page *);
+ + nr_cpu_ids * pcpu_unit_pages * sizeof(struct page *);
if (dyn_size < 0)
dyn_size = pcpu_unit_size - static_size - reserved_size;
@@ -1248,7 +1261,7 @@ ssize_t __init pcpu_embed_first_chunk(size_t static_size, size_t reserved_size,
} else
pcpue_unit_size = max_t(size_t, pcpue_size, PCPU_MIN_UNIT_SIZE);
- chunk_size = pcpue_unit_size * num_possible_cpus();
+ chunk_size = pcpue_unit_size * nr_cpu_ids;
pcpue_ptr = __alloc_bootmem_nopanic(chunk_size, PAGE_SIZE,
__pa(MAX_DMA_ADDRESS));
@@ -1259,12 +1272,15 @@ ssize_t __init pcpu_embed_first_chunk(size_t static_size, size_t reserved_size,
}
/* return the leftover and copy */
- for_each_possible_cpu(cpu) {
+ for (cpu = 0; cpu < nr_cpu_ids; cpu++) {
void *ptr = pcpue_ptr + cpu * pcpue_unit_size;
- free_bootmem(__pa(ptr + pcpue_size),
- pcpue_unit_size - pcpue_size);
- memcpy(ptr, __per_cpu_load, static_size);
+ if (cpu_possible(cpu)) {
+ free_bootmem(__pa(ptr + pcpue_size),
+ pcpue_unit_size - pcpue_size);
+ memcpy(ptr, __per_cpu_load, static_size);
+ } else
+ free_bootmem(__pa(ptr), pcpue_unit_size);
}
/* we're ready, commit */
diff --git a/mm/rmap.c b/mm/rmap.c
index 836c6c63e1f..0895b5c7cbf 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -358,6 +358,7 @@ static int page_referenced_one(struct page *page,
*/
if (vma->vm_flags & VM_LOCKED) {
*mapcount = 1; /* break early from loop */
+ *vm_flags |= VM_LOCKED;
goto out_unmap;
}
diff --git a/mm/slub.c b/mm/slub.c
index b9f1491a58a..b6276753626 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -2594,8 +2594,6 @@ static inline int kmem_cache_close(struct kmem_cache *s)
*/
void kmem_cache_destroy(struct kmem_cache *s)
{
- if (s->flags & SLAB_DESTROY_BY_RCU)
- rcu_barrier();
down_write(&slub_lock);
s->refcount--;
if (!s->refcount) {
@@ -2606,6 +2604,8 @@ void kmem_cache_destroy(struct kmem_cache *s)
"still has objects.\n", s->name, __func__);
dump_stack();
}
+ if (s->flags & SLAB_DESTROY_BY_RCU)
+ rcu_barrier();
sysfs_slab_remove(s);
} else
up_write(&slub_lock);
diff --git a/mm/vmscan.c b/mm/vmscan.c
index dea7abd3109..94e86dd6954 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -630,9 +630,14 @@ static unsigned long shrink_page_list(struct list_head *page_list,
referenced = page_referenced(page, 1,
sc->mem_cgroup, &vm_flags);
- /* In active use or really unfreeable? Activate it. */
+ /*
+ * In active use or really unfreeable? Activate it.
+ * If page which have PG_mlocked lost isoltation race,
+ * try_to_unmap moves it to unevictable list
+ */
if (sc->order <= PAGE_ALLOC_COSTLY_ORDER &&
- referenced && page_mapping_inuse(page))
+ referenced && page_mapping_inuse(page)
+ && !(vm_flags & VM_LOCKED))
goto activate_locked;
/*
diff --git a/net/9p/client.c b/net/9p/client.c
index 787ccddb85e..5bf5f227dbe 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -60,9 +60,9 @@ static struct p9_req_t *
p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...);
/**
- * v9fs_parse_options - parse mount options into session structure
- * @options: options string passed from mount
- * @v9ses: existing v9fs session information
+ * parse_options - parse mount options into client structure
+ * @opts: options string passed from mount
+ * @clnt: existing v9fs client information
*
* Return 0 upon success, -ERRNO upon failure
*/
@@ -232,7 +232,7 @@ EXPORT_SYMBOL(p9_tag_lookup);
/**
* p9_tag_init - setup tags structure and contents
- * @tags: tags structure from the client struct
+ * @c: v9fs client struct
*
* This initializes the tags structure for each client instance.
*
@@ -258,7 +258,7 @@ error:
/**
* p9_tag_cleanup - cleans up tags structure and reclaims resources
- * @tags: tags structure from the client struct
+ * @c: v9fs client struct
*
* This frees resources associated with the tags structure
*
@@ -411,14 +411,9 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
if (c->dotu)
err = -ecode;
- if (!err) {
+ if (!err || !IS_ERR_VALUE(err))
err = p9_errstr2errno(ename, strlen(ename));
- /* string match failed */
- if (!err)
- err = -ESERVERFAULT;
- }
-
P9_DPRINTK(P9_DEBUG_9P, "<<< RERROR (%d) %s\n", -ecode, ename);
kfree(ename);
@@ -430,8 +425,8 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
/**
* p9_client_flush - flush (cancel) a request
- * c: client state
- * req: request to cancel
+ * @c: client state
+ * @oldreq: request to cancel
*
* This sents a flush for a particular requests and links
* the flush request to the original request. The current
diff --git a/net/9p/error.c b/net/9p/error.c
index fdebe431406..52518512a93 100644
--- a/net/9p/error.c
+++ b/net/9p/error.c
@@ -239,7 +239,7 @@ int p9_errstr2errno(char *errstr, int len)
errstr[len] = 0;
printk(KERN_ERR "%s: server reported unknown error %s\n",
__func__, errstr);
- errno = 1;
+ errno = ESERVERFAULT;
}
return -errno;
diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c
index 8c2588e4edc..8d934dd7fd5 100644
--- a/net/9p/trans_fd.c
+++ b/net/9p/trans_fd.c
@@ -119,8 +119,8 @@ struct p9_poll_wait {
* @wpos: write position for current frame
* @wsize: amount of data to write for current frame
* @wbuf: current write buffer
+ * @poll_pending_link: pending links to be polled per conn
* @poll_wait: array of wait_q's for various worker threads
- * @poll_waddr: ????
* @pt: poll state
* @rq: current read work
* @wq: current write work
@@ -700,9 +700,9 @@ static int p9_fd_cancel(struct p9_client *client, struct p9_req_t *req)
}
/**
- * parse_options - parse mount options into session structure
- * @options: options string passed from mount
- * @opts: transport-specific structure to parse options into
+ * parse_opts - parse mount options into p9_fd_opts structure
+ * @params: options string passed from mount
+ * @opts: fd transport-specific structure to parse options into
*
* Returns 0 upon success, -ERRNO upon failure
*/
diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c
index ac4990041eb..65cb29db03f 100644
--- a/net/9p/trans_rdma.c
+++ b/net/9p/trans_rdma.c
@@ -67,14 +67,15 @@
* @pd: Protection Domain pointer
* @qp: Queue Pair pointer
* @cq: Completion Queue pointer
+ * @dm_mr: DMA Memory Region pointer
* @lkey: The local access only memory region key
* @timeout: Number of uSecs to wait for connection management events
* @sq_depth: The depth of the Send Queue
* @sq_sem: Semaphore for the SQ
* @rq_depth: The depth of the Receive Queue.
+ * @rq_count: Count of requests in the Receive Queue.
* @addr: The remote peer's address
* @req_lock: Protects the active request list
- * @send_wait: Wait list when the SQ fills up
* @cm_done: Completion event for connection management tracking
*/
struct p9_trans_rdma {
@@ -154,9 +155,9 @@ static match_table_t tokens = {
};
/**
- * parse_options - parse mount options into session structure
- * @options: options string passed from mount
- * @opts: transport-specific structure to parse options into
+ * parse_opts - parse mount options into rdma options structure
+ * @params: options string passed from mount
+ * @opts: rdma transport-specific structure to parse options into
*
* Returns 0 upon success, -ERRNO upon failure
*/
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c
index a49484e67e1..9bf0b737aa5 100644
--- a/net/9p/trans_virtio.c
+++ b/net/9p/trans_virtio.c
@@ -57,11 +57,9 @@ static int chan_index;
* @initialized: whether the channel is initialized
* @inuse: whether the channel is in use
* @lock: protects multiple elements within this structure
+ * @client: client instance
* @vdev: virtio dev associated with this channel
* @vq: virtio queue associated with this channel
- * @tagpool: accounting for tag ids (and request slots)
- * @reqs: array of request slots
- * @max_tag: current number of request_slots allocated
* @sg: scatter gather list which is used to pack a request (protected?)
*
* We keep all per-channel information in a structure.
@@ -92,7 +90,7 @@ static unsigned int rest_of_page(void *data)
/**
* p9_virtio_close - reclaim resources of a channel
- * @trans: transport state
+ * @client: client instance
*
* This reclaims a channel by freeing its resources and
* reseting its inuse flag.
@@ -181,9 +179,8 @@ static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req)
/**
* p9_virtio_request - issue a request
- * @t: transport state
- * @tc: &p9_fcall request to transmit
- * @rc: &p9_fcall to put reponse into
+ * @client: client instance issuing the request
+ * @req: request to be issued
*
*/
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index bfbe13786bb..875eda5dbad 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -1238,6 +1238,7 @@ static int atalk_getname(struct socket *sock, struct sockaddr *uaddr,
return -ENOBUFS;
*uaddr_len = sizeof(struct sockaddr_at);
+ memset(&sat.sat_zero, 0, sizeof(sat.sat_zero));
if (peer) {
if (sk->sk_state != TCP_ESTABLISHED)
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index e50566ebf9f..94b3388c188 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -2080,28 +2080,41 @@ static CLASS_ATTR(rfcomm_dlc, S_IRUGO, rfcomm_dlc_sysfs_show, NULL);
/* ---- Initialization ---- */
static int __init rfcomm_init(void)
{
+ int ret;
+
l2cap_load();
hci_register_cb(&rfcomm_cb);
rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd");
if (IS_ERR(rfcomm_thread)) {
- hci_unregister_cb(&rfcomm_cb);
- return PTR_ERR(rfcomm_thread);
+ ret = PTR_ERR(rfcomm_thread);
+ goto out_thread;
}
if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0)
BT_ERR("Failed to create RFCOMM info file");
- rfcomm_init_sockets();
+ ret = rfcomm_init_ttys();
+ if (ret)
+ goto out_tty;
-#ifdef CONFIG_BT_RFCOMM_TTY
- rfcomm_init_ttys();
-#endif
+ ret = rfcomm_init_sockets();
+ if (ret)
+ goto out_sock;
BT_INFO("RFCOMM ver %s", VERSION);
return 0;
+
+out_sock:
+ rfcomm_cleanup_ttys();
+out_tty:
+ kthread_stop(rfcomm_thread);
+out_thread:
+ hci_unregister_cb(&rfcomm_cb);
+
+ return ret;
}
static void __exit rfcomm_exit(void)
@@ -2112,9 +2125,7 @@ static void __exit rfcomm_exit(void)
kthread_stop(rfcomm_thread);
-#ifdef CONFIG_BT_RFCOMM_TTY
rfcomm_cleanup_ttys();
-#endif
rfcomm_cleanup_sockets();
}
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 7f482784e9f..0b85e811685 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -1132,7 +1132,7 @@ error:
return err;
}
-void __exit rfcomm_cleanup_sockets(void)
+void rfcomm_cleanup_sockets(void)
{
class_remove_file(bt_class, &class_attr_rfcomm);
diff --git a/net/can/raw.c b/net/can/raw.c
index f4cc44548bd..db3152df7d2 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -401,6 +401,7 @@ static int raw_getname(struct socket *sock, struct sockaddr *uaddr,
if (peer)
return -EOPNOTSUPP;
+ memset(addr, 0, sizeof(*addr));
addr->can_family = AF_CAN;
addr->can_ifindex = ro->ifindex;
diff --git a/net/core/dev.c b/net/core/dev.c
index 70c27e0c7c3..6a94475aee8 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3865,10 +3865,12 @@ int dev_unicast_delete(struct net_device *dev, void *addr)
ASSERT_RTNL();
+ netif_addr_lock_bh(dev);
err = __hw_addr_del(&dev->uc, addr, dev->addr_len,
NETDEV_HW_ADDR_T_UNICAST);
if (!err)
__dev_set_rx_mode(dev);
+ netif_addr_unlock_bh(dev);
return err;
}
EXPORT_SYMBOL(dev_unicast_delete);
@@ -3889,10 +3891,12 @@ int dev_unicast_add(struct net_device *dev, void *addr)
ASSERT_RTNL();
+ netif_addr_lock_bh(dev);
err = __hw_addr_add(&dev->uc, addr, dev->addr_len,
NETDEV_HW_ADDR_T_UNICAST);
if (!err)
__dev_set_rx_mode(dev);
+ netif_addr_unlock_bh(dev);
return err;
}
EXPORT_SYMBOL(dev_unicast_add);
@@ -3949,7 +3953,8 @@ void __dev_addr_unsync(struct dev_addr_list **to, int *to_count,
* @from: source device
*
* Add newly added addresses to the destination device and release
- * addresses that have no users left.
+ * addresses that have no users left. The source device must be
+ * locked by netif_tx_lock_bh.
*
* This function is intended to be called from the dev->set_rx_mode
* function of layered software devices.
@@ -3958,14 +3963,14 @@ int dev_unicast_sync(struct net_device *to, struct net_device *from)
{
int err = 0;
- ASSERT_RTNL();
-
if (to->addr_len != from->addr_len)
return -EINVAL;
+ netif_addr_lock_bh(to);
err = __hw_addr_sync(&to->uc, &from->uc, to->addr_len);
if (!err)
__dev_set_rx_mode(to);
+ netif_addr_unlock_bh(to);
return err;
}
EXPORT_SYMBOL(dev_unicast_sync);
@@ -3981,27 +3986,27 @@ EXPORT_SYMBOL(dev_unicast_sync);
*/
void dev_unicast_unsync(struct net_device *to, struct net_device *from)
{
- ASSERT_RTNL();
-
if (to->addr_len != from->addr_len)
return;
+ netif_addr_lock_bh(from);
+ netif_addr_lock(to);
__hw_addr_unsync(&to->uc, &from->uc, to->addr_len);
__dev_set_rx_mode(to);
+ netif_addr_unlock(to);
+ netif_addr_unlock_bh(from);
}
EXPORT_SYMBOL(dev_unicast_unsync);
static void dev_unicast_flush(struct net_device *dev)
{
- /* rtnl_mutex must be held here */
-
+ netif_addr_lock_bh(dev);
__hw_addr_flush(&dev->uc);
+ netif_addr_unlock_bh(dev);
}
static void dev_unicast_init(struct net_device *dev)
{
- /* rtnl_mutex must be held here */
-
__hw_addr_init(&dev->uc);
}
diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c
index 78e5bfc454a..493775f4f2f 100644
--- a/net/core/gen_estimator.c
+++ b/net/core/gen_estimator.c
@@ -81,7 +81,7 @@
struct gen_estimator
{
struct list_head list;
- struct gnet_stats_basic *bstats;
+ struct gnet_stats_basic_packed *bstats;
struct gnet_stats_rate_est *rate_est;
spinlock_t *stats_lock;
int ewma_log;
@@ -165,7 +165,7 @@ static void gen_add_node(struct gen_estimator *est)
}
static
-struct gen_estimator *gen_find_node(const struct gnet_stats_basic *bstats,
+struct gen_estimator *gen_find_node(const struct gnet_stats_basic_packed *bstats,
const struct gnet_stats_rate_est *rate_est)
{
struct rb_node *p = est_root.rb_node;
@@ -202,7 +202,7 @@ struct gen_estimator *gen_find_node(const struct gnet_stats_basic *bstats,
*
* NOTE: Called under rtnl_mutex
*/
-int gen_new_estimator(struct gnet_stats_basic *bstats,
+int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
struct gnet_stats_rate_est *rate_est,
spinlock_t *stats_lock,
struct nlattr *opt)
@@ -262,7 +262,7 @@ static void __gen_kill_estimator(struct rcu_head *head)
*
* NOTE: Called under rtnl_mutex
*/
-void gen_kill_estimator(struct gnet_stats_basic *bstats,
+void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,
struct gnet_stats_rate_est *rate_est)
{
struct gen_estimator *e;
@@ -292,7 +292,7 @@ EXPORT_SYMBOL(gen_kill_estimator);
*
* Returns 0 on success or a negative error code.
*/
-int gen_replace_estimator(struct gnet_stats_basic *bstats,
+int gen_replace_estimator(struct gnet_stats_basic_packed *bstats,
struct gnet_stats_rate_est *rate_est,
spinlock_t *stats_lock, struct nlattr *opt)
{
@@ -308,7 +308,7 @@ EXPORT_SYMBOL(gen_replace_estimator);
*
* Returns true if estimator is active, and false if not.
*/
-bool gen_estimator_active(const struct gnet_stats_basic *bstats,
+bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats,
const struct gnet_stats_rate_est *rate_est)
{
ASSERT_RTNL();
diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c
index c3d0ffeac24..8569310268a 100644
--- a/net/core/gen_stats.c
+++ b/net/core/gen_stats.c
@@ -106,16 +106,21 @@ gnet_stats_start_copy(struct sk_buff *skb, int type, spinlock_t *lock,
* if the room in the socket buffer was not sufficient.
*/
int
-gnet_stats_copy_basic(struct gnet_dump *d, struct gnet_stats_basic *b)
+gnet_stats_copy_basic(struct gnet_dump *d, struct gnet_stats_basic_packed *b)
{
if (d->compat_tc_stats) {
d->tc_stats.bytes = b->bytes;
d->tc_stats.packets = b->packets;
}
- if (d->tail)
- return gnet_stats_copy(d, TCA_STATS_BASIC, b, sizeof(*b));
+ if (d->tail) {
+ struct gnet_stats_basic sb;
+ memset(&sb, 0, sizeof(sb));
+ sb.bytes = b->bytes;
+ sb.packets = b->packets;
+ return gnet_stats_copy(d, TCA_STATS_BASIC, &sb, sizeof(sb));
+ }
return 0;
}
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index b7292a2719d..197283072cc 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -488,7 +488,7 @@ int net_assign_generic(struct net *net, int id, void *data)
*/
ng->len = id;
- memcpy(&ng->ptr, &old_ng->ptr, old_ng->len);
+ memcpy(&ng->ptr, &old_ng->ptr, old_ng->len * sizeof(void*));
rcu_assign_pointer(net->gen, ng);
call_rcu(&old_ng->rcu, net_generic_release);
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index df30feb2fc7..1b76eb11deb 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -319,6 +319,11 @@ static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
udelay(USEC_PER_POLL);
}
+
+ WARN_ONCE(!irqs_disabled(),
+ "netpoll_send_skb(): %s enabled interrupts in poll (%pF)\n",
+ dev->name, ops->ndo_start_xmit);
+
local_irq_restore(flags);
}
diff --git a/net/core/sock.c b/net/core/sock.c
index bbb25be7ddf..76334228ed1 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1025,6 +1025,7 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
sk->sk_prot = sk->sk_prot_creator = prot;
sock_lock_init(sk);
sock_net_set(sk, get_net(net));
+ atomic_set(&sk->sk_wmem_alloc, 1);
}
return sk;
@@ -1872,7 +1873,6 @@ void sock_init_data(struct socket *sock, struct sock *sk)
*/
smp_wmb();
atomic_set(&sk->sk_refcnt, 1);
- atomic_set(&sk->sk_wmem_alloc, 1);
atomic_set(&sk->sk_drops, 0);
}
EXPORT_SYMBOL(sock_init_data);
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 3281013ce03..1bca9205104 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -1159,6 +1159,7 @@ static void __exit dccp_fini(void)
kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
dccp_ackvec_exit();
dccp_sysctl_exit();
+ percpu_counter_destroy(&dccp_orphan_count);
}
module_init(dccp_init);
diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c
index 2e1f836d424..f0bbc57926c 100644
--- a/net/econet/af_econet.c
+++ b/net/econet/af_econet.c
@@ -520,6 +520,7 @@ static int econet_getname(struct socket *sock, struct sockaddr *uaddr,
if (peer)
return -EOPNOTSUPP;
+ memset(sec, 0, sizeof(*sec));
mutex_lock(&econet_mutex);
sk = sock->sk;
diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c
index 3bb6bdb1dac..af661805b9f 100644
--- a/net/ieee802154/af_ieee802154.c
+++ b/net/ieee802154/af_ieee802154.c
@@ -136,7 +136,7 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
unsigned int cmd)
{
struct ifreq ifr;
- int ret = -EINVAL;
+ int ret = -ENOIOCTLCMD;
struct net_device *dev;
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
@@ -146,8 +146,10 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
dev_load(sock_net(sk), ifr.ifr_name);
dev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
- if (dev->type == ARPHRD_IEEE802154 ||
- dev->type == ARPHRD_IEEE802154_PHY)
+
+ if ((dev->type == ARPHRD_IEEE802154 ||
+ dev->type == ARPHRD_IEEE802154_PHY) &&
+ dev->netdev_ops->ndo_do_ioctl)
ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd);
if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c
index 14d39840dd6..ba8b214dda8 100644
--- a/net/ieee802154/dgram.c
+++ b/net/ieee802154/dgram.c
@@ -377,6 +377,18 @@ int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb)
return ret;
}
+static int dgram_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ return -EOPNOTSUPP;
+}
+
+static int dgram_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user optlen)
+{
+ return -EOPNOTSUPP;
+}
+
struct proto ieee802154_dgram_prot = {
.name = "IEEE-802.15.4-MAC",
.owner = THIS_MODULE,
@@ -391,5 +403,7 @@ struct proto ieee802154_dgram_prot = {
.connect = dgram_connect,
.disconnect = dgram_disconnect,
.ioctl = dgram_ioctl,
+ .getsockopt = dgram_getsockopt,
+ .setsockopt = dgram_setsockopt,
};
diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c
index fca44d59f97..9315977c4c6 100644
--- a/net/ieee802154/raw.c
+++ b/net/ieee802154/raw.c
@@ -238,6 +238,18 @@ void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb)
read_unlock(&raw_lock);
}
+static int raw_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ return -EOPNOTSUPP;
+}
+
+static int raw_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user optlen)
+{
+ return -EOPNOTSUPP;
+}
+
struct proto ieee802154_raw_prot = {
.name = "IEEE-802.15.4-RAW",
.owner = THIS_MODULE,
@@ -250,5 +262,7 @@ struct proto ieee802154_raw_prot = {
.unhash = raw_unhash,
.connect = raw_connect,
.disconnect = raw_disconnect,
+ .getsockopt = raw_getsockopt,
+ .setsockopt = raw_setsockopt,
};
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index c29d75d8f1b..090e9991ac2 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -1304,7 +1304,9 @@ static void arp_format_neigh_entry(struct seq_file *seq,
hbuffer[k++] = hex_asc_lo(n->ha[j]);
hbuffer[k++] = ':';
}
- hbuffer[--k] = 0;
+ if (k != 0)
+ --k;
+ hbuffer[k] = 0;
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
}
#endif
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index cb4a0f4bd5e..82c11dd10a6 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -951,7 +951,7 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev)
addend += 4;
}
dev->needed_headroom = addend + hlen;
- mtu -= dev->hard_header_len - addend;
+ mtu -= dev->hard_header_len + addend;
if (mtu < 68)
mtu = 68;
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 7d082105472..7ffcd96fe59 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -813,6 +813,8 @@ int ip_append_data(struct sock *sk,
inet->cork.addr = ipc->addr;
}
rt = *rtp;
+ if (unlikely(!rt))
+ return -EFAULT;
/*
* We steal reference to this route, caller should not release it
*/
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index caa0278d30a..45f9a2a42d5 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -306,8 +306,10 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
v4addr != htonl(INADDR_ANY) &&
chk_addr_ret != RTN_LOCAL &&
chk_addr_ret != RTN_MULTICAST &&
- chk_addr_ret != RTN_BROADCAST)
+ chk_addr_ret != RTN_BROADCAST) {
+ err = -EADDRNOTAVAIL;
goto out;
+ }
} else {
if (addr_type != IPV6_ADDR_ANY) {
struct net_device *dev = NULL;
diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c
index 80cf29aae09..50b43c57d5d 100644
--- a/net/irda/af_irda.c
+++ b/net/irda/af_irda.c
@@ -715,6 +715,7 @@ static int irda_getname(struct socket *sock, struct sockaddr *uaddr,
struct sock *sk = sock->sk;
struct irda_sock *self = irda_sk(sk);
+ memset(&saddr, 0, sizeof(saddr));
if (peer) {
if (sk->sk_state != TCP_ESTABLISHED)
return -ENOTCONN;
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index 9208cf5f2bd..c45eee1c0e8 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -914,6 +914,7 @@ static int llc_ui_getname(struct socket *sock, struct sockaddr *uaddr,
struct llc_sock *llc = llc_sk(sk);
int rc = 0;
+ memset(&sllc, 0, sizeof(sllc));
lock_sock(sk);
if (sock_flag(sk, SOCK_ZAPPED))
goto out;
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 9e5762ad307..a24e59816b9 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -381,6 +381,14 @@ static void ieee80211_agg_splice_packets(struct ieee80211_local *local,
&local->hw, queue,
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+ if (!(sta->ampdu_mlme.tid_state_tx[tid] & HT_ADDBA_REQUESTED_MSK))
+ return;
+
+ if (WARN(!sta->ampdu_mlme.tid_tx[tid],
+ "TID %d gone but expected when splicing aggregates from"
+ "the pending queue\n", tid))
+ return;
+
if (!skb_queue_empty(&sta->ampdu_mlme.tid_tx[tid]->pending)) {
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
/* mark queue as pending, it is stopped already */
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index ce267565e18..659a42d529e 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -67,6 +67,8 @@ static DECLARE_WORK(todo_work, key_todo);
*
* @key: key to add to do item for
* @flag: todo flag(s)
+ *
+ * Must be called with IRQs or softirqs disabled.
*/
static void add_todo(struct ieee80211_key *key, u32 flag)
{
@@ -140,9 +142,9 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
ret = drv_set_key(key->local, SET_KEY, &sdata->vif, sta, &key->conf);
if (!ret) {
- spin_lock(&todo_lock);
+ spin_lock_bh(&todo_lock);
key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
- spin_unlock(&todo_lock);
+ spin_unlock_bh(&todo_lock);
}
if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP)
@@ -164,12 +166,12 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
if (!key || !key->local->ops->set_key)
return;
- spin_lock(&todo_lock);
+ spin_lock_bh(&todo_lock);
if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
- spin_unlock(&todo_lock);
+ spin_unlock_bh(&todo_lock);
return;
}
- spin_unlock(&todo_lock);
+ spin_unlock_bh(&todo_lock);
sta = get_sta_for_key(key);
sdata = key->sdata;
@@ -188,9 +190,9 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
wiphy_name(key->local->hw.wiphy),
key->conf.keyidx, sta ? sta->addr : bcast_addr, ret);
- spin_lock(&todo_lock);
+ spin_lock_bh(&todo_lock);
key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
- spin_unlock(&todo_lock);
+ spin_unlock_bh(&todo_lock);
}
static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
@@ -437,14 +439,14 @@ void ieee80211_key_link(struct ieee80211_key *key,
__ieee80211_key_replace(sdata, sta, old_key, key);
- spin_unlock_irqrestore(&sdata->local->key_lock, flags);
-
/* free old key later */
add_todo(old_key, KEY_FLAG_TODO_DELETE);
add_todo(key, KEY_FLAG_TODO_ADD_DEBUGFS);
if (netif_running(sdata->dev))
add_todo(key, KEY_FLAG_TODO_HWACCEL_ADD);
+
+ spin_unlock_irqrestore(&sdata->local->key_lock, flags);
}
static void __ieee80211_key_free(struct ieee80211_key *key)
@@ -547,7 +549,7 @@ static void __ieee80211_key_todo(void)
*/
synchronize_rcu();
- spin_lock(&todo_lock);
+ spin_lock_bh(&todo_lock);
while (!list_empty(&todo_list)) {
key = list_first_entry(&todo_list, struct ieee80211_key, todo);
list_del_init(&key->todo);
@@ -558,7 +560,7 @@ static void __ieee80211_key_todo(void)
KEY_FLAG_TODO_HWACCEL_REMOVE |
KEY_FLAG_TODO_DELETE);
key->flags &= ~todoflags;
- spin_unlock(&todo_lock);
+ spin_unlock_bh(&todo_lock);
work_done = false;
@@ -591,9 +593,9 @@ static void __ieee80211_key_todo(void)
WARN_ON(!work_done);
- spin_lock(&todo_lock);
+ spin_lock_bh(&todo_lock);
}
- spin_unlock(&todo_lock);
+ spin_unlock_bh(&todo_lock);
}
void ieee80211_key_todo(void)
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index aca22b00b6a..07e7e41816b 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -721,7 +721,7 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
{
struct ieee80211_local *local = (void *) data;
- if (local->quiescing)
+ if (local->quiescing || local->suspended)
return;
queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 7a549f9deb9..5e3d476972f 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -55,15 +55,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
rcu_read_unlock();
- /* flush again, in case driver queued work */
- flush_workqueue(local->hw.workqueue);
-
- /* stop hardware - this must stop RX */
- if (local->open_count) {
- ieee80211_led_radio(local, false);
- drv_stop(local);
- }
-
/* remove STAs */
spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry(sta, &local->sta_list, list) {
@@ -111,7 +102,22 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
drv_remove_interface(local, &conf);
}
+ /* stop hardware - this must stop RX */
+ if (local->open_count) {
+ ieee80211_led_radio(local, false);
+ drv_stop(local);
+ }
+
+ /*
+ * flush again, in case driver queued work -- it
+ * shouldn't be doing (or cancel everything in the
+ * stop callback) that but better safe than sorry.
+ */
+ flush_workqueue(local->hw.workqueue);
+
local->suspended = true;
+ /* need suspended to be visible before quiescing is false */
+ barrier();
local->quiescing = false;
return 0;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index de5bba7f910..0936fc24942 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2453,6 +2453,18 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
return;
}
+ /*
+ * If we're suspending, it is possible although not too likely
+ * that we'd be receiving frames after having already partially
+ * quiesced the stack. We can't process such frames then since
+ * that might, for example, cause stations to be added or other
+ * driver callbacks be invoked.
+ */
+ if (unlikely(local->quiescing || local->suspended)) {
+ kfree_skb(skb);
+ return;
+ }
+
if (status->flag & RX_FLAG_HT) {
/* rate_idx is MCS index */
if (WARN_ON(status->rate_idx < 0 ||
diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c
index 43f5676b1af..d80b8192e0d 100644
--- a/net/netfilter/xt_RATEEST.c
+++ b/net/netfilter/xt_RATEEST.c
@@ -74,7 +74,7 @@ static unsigned int
xt_rateest_tg(struct sk_buff *skb, const struct xt_target_param *par)
{
const struct xt_rateest_target_info *info = par->targinfo;
- struct gnet_stats_basic *stats = &info->est->bstats;
+ struct gnet_stats_basic_packed *stats = &info->est->bstats;
spin_lock_bh(&info->est->lock);
stats->bytes += skb->len;
diff --git a/net/netfilter/xt_quota.c b/net/netfilter/xt_quota.c
index 98fc190e8f0..390b7d09fe5 100644
--- a/net/netfilter/xt_quota.c
+++ b/net/netfilter/xt_quota.c
@@ -52,7 +52,7 @@ static bool quota_mt_check(const struct xt_mtchk_param *par)
q->master = kmalloc(sizeof(*q->master), GFP_KERNEL);
if (q->master == NULL)
- return -ENOMEM;
+ return false;
q->master->quota = q->quota;
return true;
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index b0e582f2d37..16e6c4378ff 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -151,7 +151,7 @@ int netlbl_cfg_unlbl_map_add(const char *domain,
addr6 = addr;
mask6 = mask;
map6 = kzalloc(sizeof(*map6), GFP_ATOMIC);
- if (map4 == NULL)
+ if (map6 == NULL)
goto cfg_unlbl_map_add_failure;
map6->type = NETLBL_NLTYPE_UNLABELED;
ipv6_addr_copy(&map6->list.addr, addr6);
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index ce51ce012cd..ce1a34b99c2 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -847,6 +847,7 @@ static int nr_getname(struct socket *sock, struct sockaddr *uaddr,
sax->fsa_ax25.sax25_family = AF_NETROM;
sax->fsa_ax25.sax25_ndigis = 1;
sax->fsa_ax25.sax25_call = nr->user_addr;
+ memset(sax->fsa_digipeater, 0, sizeof(sax->fsa_digipeater));
sax->fsa_digipeater[0] = nr->dest_addr;
*uaddr_len = sizeof(struct full_sockaddr_ax25);
} else {
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index e943c16552a..4eb1ac9a767 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -630,23 +630,23 @@ out:
return dev;
}
-static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters)
+static ax25_digi *nr_call_to_digi(ax25_digi *digi, int ndigis,
+ ax25_address *digipeaters)
{
- static ax25_digi ax25_digi;
int i;
if (ndigis == 0)
return NULL;
for (i = 0; i < ndigis; i++) {
- ax25_digi.calls[i] = digipeaters[i];
- ax25_digi.repeated[i] = 0;
+ digi->calls[i] = digipeaters[i];
+ digi->repeated[i] = 0;
}
- ax25_digi.ndigi = ndigis;
- ax25_digi.lastrepeat = -1;
+ digi->ndigi = ndigis;
+ digi->lastrepeat = -1;
- return &ax25_digi;
+ return digi;
}
/*
@@ -656,6 +656,7 @@ int nr_rt_ioctl(unsigned int cmd, void __user *arg)
{
struct nr_route_struct nr_route;
struct net_device *dev;
+ ax25_digi digi;
int ret;
switch (cmd) {
@@ -673,13 +674,15 @@ int nr_rt_ioctl(unsigned int cmd, void __user *arg)
ret = nr_add_node(&nr_route.callsign,
nr_route.mnemonic,
&nr_route.neighbour,
- nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
+ nr_call_to_digi(&digi, nr_route.ndigis,
+ nr_route.digipeaters),
dev, nr_route.quality,
nr_route.obs_count);
break;
case NETROM_NEIGH:
ret = nr_add_neigh(&nr_route.callsign,
- nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
+ nr_call_to_digi(&digi, nr_route.ndigis,
+ nr_route.digipeaters),
dev, nr_route.quality);
break;
default:
diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c
index b0d6ddd82a9..c2b77a69869 100644
--- a/net/phonet/pn_dev.c
+++ b/net/phonet/pn_dev.c
@@ -96,7 +96,7 @@ struct net_device *phonet_device_get(struct net *net)
{
struct phonet_device_list *pndevs = phonet_device_list(net);
struct phonet_device *pnd;
- struct net_device *dev;
+ struct net_device *dev = NULL;
spin_lock_bh(&pndevs->lock);
list_for_each_entry(pnd, &pndevs->list, list) {
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c
index f0a76f6bca7..e5f478ca3d6 100644
--- a/net/rose/af_rose.c
+++ b/net/rose/af_rose.c
@@ -954,6 +954,7 @@ static int rose_getname(struct socket *sock, struct sockaddr *uaddr,
struct rose_sock *rose = rose_sk(sk);
int n;
+ memset(srose, 0, sizeof(*srose));
if (peer != 0) {
if (sk->sk_state != TCP_ESTABLISHED)
return -ENOTCONN;
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 24d17ce9c29..fdb694e9f75 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1456,6 +1456,8 @@ static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q,
nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags);
tcm = NLMSG_DATA(nlh);
tcm->tcm_family = AF_UNSPEC;
+ tcm->tcm__pad1 = 0;
+ tcm->tcm__pad2 = 0;
tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
tcm->tcm_parent = q->handle;
tcm->tcm_handle = q->handle;
diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c
index 2a8b83af7c4..ab82f145f68 100644
--- a/net/sched/sch_atm.c
+++ b/net/sched/sch_atm.c
@@ -49,7 +49,7 @@ struct atm_flow_data {
struct socket *sock; /* for closing */
u32 classid; /* x:y type ID */
int ref; /* reference count */
- struct gnet_stats_basic bstats;
+ struct gnet_stats_basic_packed bstats;
struct gnet_stats_queue qstats;
struct atm_flow_data *next;
struct atm_flow_data *excess; /* flow for excess traffic;
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index 23a167670fd..d5798e17a83 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -128,7 +128,7 @@ struct cbq_class
long avgidle;
long deficit; /* Saved deficit for WRR */
psched_time_t penalized;
- struct gnet_stats_basic bstats;
+ struct gnet_stats_basic_packed bstats;
struct gnet_stats_queue qstats;
struct gnet_stats_rate_est rate_est;
struct tc_cbq_xstats xstats;
diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c
index 7597fe14686..12b2fb04b29 100644
--- a/net/sched/sch_drr.c
+++ b/net/sched/sch_drr.c
@@ -22,7 +22,7 @@ struct drr_class {
unsigned int refcnt;
unsigned int filter_cnt;
- struct gnet_stats_basic bstats;
+ struct gnet_stats_basic_packed bstats;
struct gnet_stats_queue qstats;
struct gnet_stats_rate_est rate_est;
struct list_head alist;
diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
index 362c2811b2d..dad0144423d 100644
--- a/net/sched/sch_hfsc.c
+++ b/net/sched/sch_hfsc.c
@@ -116,7 +116,7 @@ struct hfsc_class
struct Qdisc_class_common cl_common;
unsigned int refcnt; /* usage count */
- struct gnet_stats_basic bstats;
+ struct gnet_stats_basic_packed bstats;
struct gnet_stats_queue qstats;
struct gnet_stats_rate_est rate_est;
unsigned int level; /* class level in hierarchy */
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index 88cd0262662..ec4d46399d5 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -74,7 +74,7 @@ enum htb_cmode {
struct htb_class {
struct Qdisc_class_common common;
/* general class parameters */
- struct gnet_stats_basic bstats;
+ struct gnet_stats_basic_packed bstats;
struct gnet_stats_queue qstats;
struct gnet_stats_rate_est rate_est;
struct tc_htb_xstats xstats; /* our special stats */
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 79cbd47f4df..a76da657244 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -160,6 +160,7 @@ static void sctp_proc_exit(void)
remove_proc_entry("sctp", init_net.proc_net);
}
#endif
+ percpu_counter_destroy(&sctp_sockets_allocated);
}
/* Private helper to extract ipv4 address and stash them in
diff --git a/net/socket.c b/net/socket.c
index 791d71a36a9..6d471655904 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -736,7 +736,7 @@ static ssize_t sock_sendpage(struct file *file, struct page *page,
if (more)
flags |= MSG_MORE;
- return sock->ops->sendpage(sock, page, offset, size, flags);
+ return kernel_sendpage(sock, page, offset, size, flags);
}
static ssize_t sock_splice_read(struct file *file, loff_t *ppos,
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index ebfcf9b8990..df1039f077c 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -937,6 +937,7 @@ static inline void
rpc_task_force_reencode(struct rpc_task *task)
{
task->tk_rqstp->rq_snd_buf.len = 0;
+ task->tk_rqstp->rq_bytes_sent = 0;
}
static inline void
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 5e14371cda7..75a406d3361 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -1089,17 +1089,18 @@ static void handle_reg_beacon(struct wiphy *wiphy,
chan->beacon_found = true;
+ if (wiphy->disable_beacon_hints)
+ return;
+
chan_before.center_freq = chan->center_freq;
chan_before.flags = chan->flags;
- if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
- !(chan->orig_flags & IEEE80211_CHAN_PASSIVE_SCAN)) {
+ if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
channel_changed = true;
}
- if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
- !(chan->orig_flags & IEEE80211_CHAN_NO_IBSS)) {
+ if (chan->flags & IEEE80211_CHAN_NO_IBSS) {
chan->flags &= ~IEEE80211_CHAN_NO_IBSS;
channel_changed = true;
}
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index e37829a49dc..4e167a8e11b 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -30,7 +30,8 @@ int set_regdom(const struct ieee80211_regdomain *rd);
* non-radar 5 GHz channels.
*
* Drivers do not need to call this, cfg80211 will do it for after a scan
- * on a newly found BSS.
+ * on a newly found BSS. If you cannot make use of this feature you can
+ * set the wiphy->disable_beacon_hints to true.
*/
int regulatory_hint_found_beacon(struct wiphy *wiphy,
struct ieee80211_channel *beacon_chan,
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 9271118e1fc..7e595ce24ee 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -118,7 +118,7 @@ static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
if (!ie1 && !ie2)
return 0;
- if (!ie1)
+ if (!ie1 || !ie2)
return -1;
r = memcmp(ie1 + 2, ie2 + 2, min(ie1[1], ie2[1]));
@@ -171,6 +171,8 @@ static bool is_mesh(struct cfg80211_bss *a,
ie = find_ie(WLAN_EID_MESH_CONFIG,
a->information_elements,
a->len_information_elements);
+ if (!ie)
+ return false;
if (ie[1] != IEEE80211_MESH_CONFIG_LEN)
return false;
diff --git a/net/xfrm/xfrm_hash.h b/net/xfrm/xfrm_hash.h
index d401dc8f05e..e5195c99f71 100644
--- a/net/xfrm/xfrm_hash.h
+++ b/net/xfrm/xfrm_hash.h
@@ -16,7 +16,7 @@ static inline unsigned int __xfrm6_addr_hash(xfrm_address_t *addr)
static inline unsigned int __xfrm4_daddr_saddr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr)
{
- return ntohl(daddr->a4 ^ saddr->a4);
+ return ntohl(daddr->a4 + saddr->a4);
}
static inline unsigned int __xfrm6_daddr_saddr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr)
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
index 7109e2b5bc0..911ba7ffab8 100755
--- a/scripts/recordmcount.pl
+++ b/scripts/recordmcount.pl
@@ -393,7 +393,7 @@ while (<IN>) {
$read_function = 0;
}
# print out any recorded offsets
- update_funcs() if ($text_found);
+ update_funcs() if (defined($ref_func));
# reset all markers and arrays
$text_found = 0;
@@ -403,7 +403,6 @@ while (<IN>) {
# section found, now is this a start of a function?
} elsif ($read_function && /$function_regex/) {
$text_found = 1;
- $offset = hex $1;
$text = $2;
# if this is either a local function or a weak function
@@ -412,10 +411,15 @@ while (<IN>) {
if (!defined($locals{$text}) && !defined($weak{$text})) {
$ref_func = $text;
$read_function = 0;
+ $offset = hex $1;
} else {
# if we already have a function, and this is weak, skip it
- if (!defined($ref_func) || !defined($weak{$text})) {
+ if (!defined($ref_func) && !defined($weak{$text}) &&
+ # PPC64 can have symbols that start with .L and
+ # gcc considers these special. Don't use them!
+ $text !~ /^\.L/) {
$ref_func = $text;
+ $offset = hex $1;
}
}
} elsif ($read_headers && /$mcount_section/) {
@@ -440,7 +444,7 @@ while (<IN>) {
}
# dump out anymore offsets that may have been found
-update_funcs() if ($text_found);
+update_funcs() if (defined($ref_func));
# If we did not find any mcount callers, we are done (do nothing).
if (!$opened) {
diff --git a/security/Kconfig b/security/Kconfig
index d23c839038f..4c865345caa 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -113,6 +113,22 @@ config SECURITY_ROOTPLUG
If you are unsure how to answer this question, answer N.
+config LSM_MMAP_MIN_ADDR
+ int "Low address space for LSM to protect from user allocation"
+ depends on SECURITY && SECURITY_SELINUX
+ default 65536
+ help
+ This is the portion of low virtual memory which should be protected
+ from userspace allocation. Keeping a user from writing to low pages
+ can help reduce the impact of kernel NULL pointer bugs.
+
+ For most ia64, ppc64 and x86 users with lots of address space
+ a value of 65536 is reasonable and should cause no problems.
+ On arm and other archs it should not be higher than 32768.
+ Programs which use vm86 functionality or have some need to map
+ this low address space will need the permission specific to the
+ systems running LSM.
+
source security/selinux/Kconfig
source security/smack/Kconfig
source security/tomoyo/Kconfig
diff --git a/security/Makefile b/security/Makefile
index c67557cdaa8..b56e7f9ecbc 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -8,7 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack
subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
# always enable default capabilities
-obj-y += commoncap.o
+obj-y += commoncap.o min_addr.o
# Object file lists
obj-$(CONFIG_SECURITY) += security.o capability.o
diff --git a/security/capability.c b/security/capability.c
index 21b6cead6a8..88f752e8152 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -330,15 +330,6 @@ static int cap_file_ioctl(struct file *file, unsigned int command,
return 0;
}
-static int cap_file_mmap(struct file *file, unsigned long reqprot,
- unsigned long prot, unsigned long flags,
- unsigned long addr, unsigned long addr_only)
-{
- if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO))
- return -EACCES;
- return 0;
-}
-
static int cap_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
unsigned long prot)
{
diff --git a/security/commoncap.c b/security/commoncap.c
index 48b7e0228fa..e3097c0a131 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -984,3 +984,33 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
cap_sys_admin = 1;
return __vm_enough_memory(mm, pages, cap_sys_admin);
}
+
+/*
+ * cap_file_mmap - check if able to map given addr
+ * @file: unused
+ * @reqprot: unused
+ * @prot: unused
+ * @flags: unused
+ * @addr: address attempting to be mapped
+ * @addr_only: unused
+ *
+ * If the process is attempting to map memory below mmap_min_addr they need
+ * CAP_SYS_RAWIO. The other parameters to this function are unused by the
+ * capability security module. Returns 0 if this mapping should be allowed
+ * -EPERM if not.
+ */
+int cap_file_mmap(struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags,
+ unsigned long addr, unsigned long addr_only)
+{
+ int ret = 0;
+
+ if (addr < dac_mmap_min_addr) {
+ ret = cap_capable(current, current_cred(), CAP_SYS_RAWIO,
+ SECURITY_CAP_AUDIT);
+ /* set PF_SUPERPRIV if it turns out we allow the low mmap */
+ if (ret == 0)
+ current->flags |= PF_SUPERPRIV;
+ }
+ return ret;
+}
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index 63003a63aae..46642a19bc7 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -45,9 +45,9 @@ int ima_calc_hash(struct file *file, char *digest)
{
struct hash_desc desc;
struct scatterlist sg[1];
- loff_t i_size;
+ loff_t i_size, offset = 0;
char *rbuf;
- int rc, offset = 0;
+ int rc;
rc = init_desc(&desc);
if (rc != 0)
@@ -67,6 +67,8 @@ int ima_calc_hash(struct file *file, char *digest)
rc = rbuf_len;
break;
}
+ if (rbuf_len == 0)
+ break;
offset += rbuf_len;
sg_init_one(sg, rbuf, rbuf_len);
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 101c512564e..b85e61bcf24 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -249,7 +249,11 @@ void ima_counts_put(struct path *path, int mask)
struct inode *inode = path->dentry->d_inode;
struct ima_iint_cache *iint;
- if (!ima_initialized || !S_ISREG(inode->i_mode))
+ /* The inode may already have been freed, freeing the iint
+ * with it. Verify the inode is not NULL before dereferencing
+ * it.
+ */
+ if (!ima_initialized || !inode || !S_ISREG(inode->i_mode))
return;
iint = ima_iint_find_insert_get(inode);
if (!iint)
@@ -262,6 +266,8 @@ void ima_counts_put(struct path *path, int mask)
else if (mask & (MAY_READ | MAY_EXEC))
iint->readcount--;
mutex_unlock(&iint->mutex);
+
+ kref_put(&iint->refcount, iint_free);
}
/*
@@ -291,6 +297,8 @@ void ima_counts_get(struct file *file)
if (file->f_mode & FMODE_WRITE)
iint->writecount++;
mutex_unlock(&iint->mutex);
+
+ kref_put(&iint->refcount, iint_free);
}
EXPORT_SYMBOL_GPL(ima_counts_get);
diff --git a/security/min_addr.c b/security/min_addr.c
new file mode 100644
index 00000000000..14cc7b3b8d0
--- /dev/null
+++ b/security/min_addr.c
@@ -0,0 +1,49 @@
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/security.h>
+#include <linux/sysctl.h>
+
+/* amount of vm to protect from userspace access by both DAC and the LSM*/
+unsigned long mmap_min_addr;
+/* amount of vm to protect from userspace using CAP_SYS_RAWIO (DAC) */
+unsigned long dac_mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
+/* amount of vm to protect from userspace using the LSM = CONFIG_LSM_MMAP_MIN_ADDR */
+
+/*
+ * Update mmap_min_addr = max(dac_mmap_min_addr, CONFIG_LSM_MMAP_MIN_ADDR)
+ */
+static void update_mmap_min_addr(void)
+{
+#ifdef CONFIG_LSM_MMAP_MIN_ADDR
+ if (dac_mmap_min_addr > CONFIG_LSM_MMAP_MIN_ADDR)
+ mmap_min_addr = dac_mmap_min_addr;
+ else
+ mmap_min_addr = CONFIG_LSM_MMAP_MIN_ADDR;
+#else
+ mmap_min_addr = dac_mmap_min_addr;
+#endif
+}
+
+/*
+ * sysctl handler which just sets dac_mmap_min_addr = the new value and then
+ * calls update_mmap_min_addr() so non MAP_FIXED hints get rounded properly
+ */
+int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int ret;
+
+ ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos);
+
+ update_mmap_min_addr();
+
+ return ret;
+}
+
+int __init init_mmap_min_addr(void)
+{
+ update_mmap_min_addr();
+
+ return 0;
+}
+pure_initcall(init_mmap_min_addr);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 15c2a08a66f..8d8b69c5664 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1285,6 +1285,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
context, len);
if (rc == -ERANGE) {
+ kfree(context);
+
/* Need a larger buffer. Query for the right size. */
rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
NULL, 0);
@@ -1292,7 +1294,6 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
dput(dentry);
goto out_unlock;
}
- kfree(context);
len = rc;
context = kmalloc(len+1, GFP_NOFS);
if (!context) {
@@ -3029,9 +3030,21 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,
int rc = 0;
u32 sid = current_sid();
- if (addr < mmap_min_addr)
+ /*
+ * notice that we are intentionally putting the SELinux check before
+ * the secondary cap_file_mmap check. This is such a likely attempt
+ * at bad behaviour/exploit that we always want to get the AVC, even
+ * if DAC would have also denied the operation.
+ */
+ if (addr < CONFIG_LSM_MMAP_MIN_ADDR) {
rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
MEMPROTECT__MMAP_ZERO, NULL);
+ if (rc)
+ return rc;
+ }
+
+ /* do DAC check on address space usage */
+ rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only);
if (rc || addr_only)
return rc;
diff --git a/sound/Kconfig b/sound/Kconfig
index 1eceb85287c..439e15c8faa 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -32,6 +32,34 @@ config SOUND_OSS_CORE
bool
default n
+config SOUND_OSS_CORE_PRECLAIM
+ bool "Preclaim OSS device numbers"
+ depends on SOUND_OSS_CORE
+ default y
+ help
+ With this option enabled, the kernel will claim all OSS device
+ numbers if any OSS support (native or emulation) is enabled
+ whether the respective module is loaded or not and try to load the
+ appropriate module using sound-slot/service-* and char-major-*
+ module aliases when one of the device numbers is opened. With
+ this option disabled, kernel will only claim actually in-use
+ device numbers and opening a missing device will generate only the
+ standard char-major-* aliases.
+
+ The only visible difference is use of additional module aliases
+ and whether OSS sound devices appear multiple times in
+ /proc/devices. sound-slot/service-* module aliases are scheduled
+ to be removed (ie. PRECLAIM won't be available) and this option is
+ to make the transition easier. This option can be overridden
+ during boot using the kernel parameter soundcore.preclaim_oss.
+
+ Disabling this allows alternative OSS implementations.
+
+ Please read Documentation/feature-removal-schedule.txt for
+ details.
+
+ If unusre, say Y.
+
source "sound/oss/dmasound/Kconfig"
if !M68K
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index c570ebd9d17..4e34d19ddbc 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -170,6 +170,13 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
struct snd_ac97_bus *ac97_bus;
struct snd_ac97_template ac97_template;
int ret;
+ pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
+
+ if (dev->id >= 0) {
+ dev_err(&dev->dev, "PXA2xx has only one AC97 port.\n");
+ ret = -ENXIO;
+ goto err_dev;
+ }
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
THIS_MODULE, 0, &card);
@@ -200,6 +207,8 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
snprintf(card->longname, sizeof(card->longname),
"%s (%s)", dev->dev.driver->name, card->mixername);
+ if (pdata && pdata->codec_pdata[0])
+ snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);
snd_card_set_dev(card, &dev->dev);
ret = snd_card_register(card);
if (ret == 0) {
@@ -212,6 +221,7 @@ err_remove:
err:
if (card)
snd_card_free(card);
+err_dev:
return ret;
}
diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c
index 6205f37d547..743ac6a2906 100644
--- a/sound/arm/pxa2xx-pcm-lib.c
+++ b/sound/arm/pxa2xx-pcm-lib.c
@@ -136,6 +136,9 @@ int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
{
struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
+ if (!prtd || !prtd->params)
+ return 0;
+
DCSR(prtd->dma_ch) &= ~DCSR_RUN;
DCSR(prtd->dma_ch) = 0;
DCMD(prtd->dma_ch) = 0;
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 6061fb5f4e1..c15682a2f9d 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -206,4 +206,8 @@ config SND_PCM_XRUN_DEBUG
config SND_VMASTER
bool
+config SND_DMA_SGBUF
+ def_bool y
+ depends on X86
+
source "sound/core/seq/Kconfig"
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 4229052e7b9..350a08d277f 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -13,7 +13,7 @@ snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
pcm_memory.o
snd-page-alloc-y := memalloc.o
-snd-page-alloc-$(CONFIG_HAS_DMA) += sgbuf.o
+snd-page-alloc-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
snd-rawmidi-objs := rawmidi.o
snd-timer-objs := timer.o
diff --git a/sound/core/control.c b/sound/core/control.c
index 17b8d47a5cd..a8b7fabe645 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -414,7 +414,7 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
EXPORT_SYMBOL(snd_ctl_remove_id);
/**
- * snd_ctl_remove_unlocked_id - remove the unlocked control of the given id and release it
+ * snd_ctl_remove_user_ctl - remove and release the unlocked user control
* @file: active control handle
* @id: the control id to remove
*
@@ -423,8 +423,8 @@ EXPORT_SYMBOL(snd_ctl_remove_id);
*
* Returns 0 if successful, or a negative error code on failure.
*/
-static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file,
- struct snd_ctl_elem_id *id)
+static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
+ struct snd_ctl_elem_id *id)
{
struct snd_card *card = file->card;
struct snd_kcontrol *kctl;
@@ -433,15 +433,23 @@ static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file,
down_write(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, id);
if (kctl == NULL) {
- up_write(&card->controls_rwsem);
- return -ENOENT;
+ ret = -ENOENT;
+ goto error;
+ }
+ if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) {
+ ret = -EINVAL;
+ goto error;
}
for (idx = 0; idx < kctl->count; idx++)
if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
- up_write(&card->controls_rwsem);
- return -EBUSY;
+ ret = -EBUSY;
+ goto error;
}
ret = snd_ctl_remove(card, kctl);
+ if (ret < 0)
+ goto error;
+ card->user_ctl_count--;
+error:
up_write(&card->controls_rwsem);
return ret;
}
@@ -951,7 +959,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
if (card->user_ctl_count >= MAX_USER_CONTROLS)
return -ENOMEM;
- if (info->count > 1024)
+ if (info->count < 1)
return -EINVAL;
access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
@@ -1052,18 +1060,10 @@ static int snd_ctl_elem_remove(struct snd_ctl_file *file,
struct snd_ctl_elem_id __user *_id)
{
struct snd_ctl_elem_id id;
- int err;
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
- err = snd_ctl_remove_unlocked_id(file, &id);
- if (! err) {
- struct snd_card *card = file->card;
- down_write(&card->controls_rwsem);
- card->user_ctl_count--;
- up_write(&card->controls_rwsem);
- }
- return err;
+ return snd_ctl_remove_user_ctl(file, &id);
}
static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
diff --git a/sound/core/info.c b/sound/core/info.c
index 35df614f6c5..d749a0d394a 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -88,12 +88,10 @@ static int resize_info_buffer(struct snd_info_buffer *buffer,
char *nbuf;
nsize = PAGE_ALIGN(nsize);
- nbuf = kmalloc(nsize, GFP_KERNEL);
+ nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL);
if (! nbuf)
return -ENOMEM;
- memcpy(nbuf, buffer->buffer, buffer->len);
- kfree(buffer->buffer);
buffer->buffer = nbuf;
buffer->len = nsize;
return 0;
@@ -108,7 +106,7 @@ static int resize_info_buffer(struct snd_info_buffer *buffer,
*
* Returns the size of output string.
*/
-int snd_iprintf(struct snd_info_buffer *buffer, char *fmt,...)
+int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...)
{
va_list args;
int len, res;
@@ -727,7 +725,7 @@ EXPORT_SYMBOL(snd_info_get_line);
* Returns the updated pointer of the original string so that
* it can be used for the next call.
*/
-char *snd_info_get_str(char *dest, char *src, int len)
+const char *snd_info_get_str(char *dest, const char *src, int len)
{
int c;
diff --git a/sound/core/init.c b/sound/core/init.c
index d5d40d78c40..ec4a50ce565 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -31,6 +31,14 @@
#include <sound/control.h>
#include <sound/info.h>
+/* monitor files for graceful shutdown (hotplug) */
+struct snd_monitor_file {
+ struct file *file;
+ const struct file_operations *disconnected_f_op;
+ struct list_head shutdown_list; /* still need to shutdown */
+ struct list_head list; /* link of monitor files */
+};
+
static DEFINE_SPINLOCK(shutdown_lock);
static LIST_HEAD(shutdown_files);
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 1b3534d6768..9e92441f9b7 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -199,6 +199,8 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
case SNDRV_DMA_TYPE_DEV:
dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
break;
+#endif
+#ifdef CONFIG_SND_DMA_SGBUF
case SNDRV_DMA_TYPE_DEV_SG:
snd_malloc_sgbuf_pages(device, size, dmab, NULL);
break;
@@ -269,6 +271,8 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab)
case SNDRV_DMA_TYPE_DEV:
snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
break;
+#endif
+#ifdef CONFIG_SND_DMA_SGBUF
case SNDRV_DMA_TYPE_DEV_SG:
snd_free_sgbuf_pages(dmab);
break;
diff --git a/sound/core/misc.c b/sound/core/misc.c
index a9710e0c97a..23a032c6d48 100644
--- a/sound/core/misc.c
+++ b/sound/core/misc.c
@@ -24,6 +24,20 @@
#include <linux/ioport.h>
#include <sound/core.h>
+#ifdef CONFIG_SND_DEBUG
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+#define DEFAULT_DEBUG_LEVEL 2
+#else
+#define DEFAULT_DEBUG_LEVEL 1
+#endif
+
+static int debug = DEFAULT_DEBUG_LEVEL;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0 = disable)");
+
+#endif /* CONFIG_SND_DEBUG */
+
void release_and_free_resource(struct resource *res)
{
if (res) {
@@ -35,46 +49,53 @@ void release_and_free_resource(struct resource *res)
EXPORT_SYMBOL(release_and_free_resource);
#ifdef CONFIG_SND_VERBOSE_PRINTK
-void snd_verbose_printk(const char *file, int line, const char *format, ...)
+/* strip the leading path if the given path is absolute */
+static const char *sanity_file_name(const char *path)
{
- va_list args;
-
- if (format[0] == '<' && format[1] >= '0' && format[1] <= '7' && format[2] == '>') {
- char tmp[] = "<0>";
+ if (*path == '/')
+ return strrchr(path, '/') + 1;
+ else
+ return path;
+}
+
+/* print file and line with a certain printk prefix */
+static int print_snd_pfx(unsigned int level, const char *path, int line,
+ const char *format)
+{
+ const char *file = sanity_file_name(path);
+ char tmp[] = "<0>";
+ const char *pfx = level ? KERN_DEBUG : KERN_DEFAULT;
+ int ret = 0;
+
+ if (format[0] == '<' && format[2] == '>') {
tmp[1] = format[1];
- printk("%sALSA %s:%d: ", tmp, file, line);
- format += 3;
- } else {
- printk("ALSA %s:%d: ", file, line);
+ pfx = tmp;
+ ret = 1;
}
- va_start(args, format);
- vprintk(format, args);
- va_end(args);
+ printk("%sALSA %s:%d: ", pfx, file, line);
+ return ret;
}
-
-EXPORT_SYMBOL(snd_verbose_printk);
+#else
+#define print_snd_pfx(level, path, line, format) 0
#endif
-#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
-void snd_verbose_printd(const char *file, int line, const char *format, ...)
+#if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK)
+void __snd_printk(unsigned int level, const char *path, int line,
+ const char *format, ...)
{
va_list args;
- if (format[0] == '<' && format[1] >= '0' && format[1] <= '7' && format[2] == '>') {
- char tmp[] = "<0>";
- tmp[1] = format[1];
- printk("%sALSA %s:%d: ", tmp, file, line);
- format += 3;
- } else {
- printk(KERN_DEBUG "ALSA %s:%d: ", file, line);
- }
+#ifdef CONFIG_SND_DEBUG
+ if (debug < level)
+ return;
+#endif
va_start(args, format);
+ if (print_snd_pfx(level, path, line, format))
+ format += 3; /* skip the printk level-prefix */
vprintk(format, args);
va_end(args);
-
}
-
-EXPORT_SYMBOL(snd_verbose_printd);
+EXPORT_SYMBOL_GPL(__snd_printk);
#endif
#ifdef CONFIG_PCI
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 5dcd8a52697..772423889eb 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -1154,7 +1154,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_mixer_oss *mixer = entry->private_data;
- char line[128], str[32], idxstr[16], *cptr;
+ char line[128], str[32], idxstr[16];
+ const char *cptr;
int ch, idx;
struct snd_mixer_oss_assign_table *tbl;
struct slot *slot;
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index dbe406b8259..d9c96353121 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -1043,10 +1043,15 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
runtime->oss.channels = params_channels(params);
runtime->oss.rate = params_rate(params);
- runtime->oss.params = 0;
- runtime->oss.prepare = 1;
vfree(runtime->oss.buffer);
runtime->oss.buffer = vmalloc(runtime->oss.period_bytes);
+ if (!runtime->oss.buffer) {
+ err = -ENOMEM;
+ goto failure;
+ }
+
+ runtime->oss.params = 0;
+ runtime->oss.prepare = 1;
runtime->oss.buffer_used = 0;
if (runtime->dma_area)
snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));
@@ -2836,7 +2841,8 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_pcm_str *pstr = entry->private_data;
- char line[128], str[32], task_name[32], *ptr;
+ char line[128], str[32], task_name[32];
+ const char *ptr;
int idx1;
struct snd_pcm_oss_setup *setup, *setup1, template;
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 145931a9ff3..0c1440121c2 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -162,18 +162,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
return -ENOIOCTLCMD;
}
-#ifdef CONFIG_SND_VERBOSE_PROCFS
-
-#define STATE(v) [SNDRV_PCM_STATE_##v] = #v
-#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v
-#define READY(v) [SNDRV_PCM_READY_##v] = #v
-#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v
-#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v
-#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v
-#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v
-#define START(v) [SNDRV_PCM_START_##v] = #v
#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v
-#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v
static char *snd_pcm_format_names[] = {
FORMAT(S8),
@@ -216,10 +205,23 @@ static char *snd_pcm_format_names[] = {
FORMAT(U18_3BE),
};
-static const char *snd_pcm_format_name(snd_pcm_format_t format)
+const char *snd_pcm_format_name(snd_pcm_format_t format)
{
return snd_pcm_format_names[format];
}
+EXPORT_SYMBOL_GPL(snd_pcm_format_name);
+
+#ifdef CONFIG_SND_VERBOSE_PROCFS
+
+#define STATE(v) [SNDRV_PCM_STATE_##v] = #v
+#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v
+#define READY(v) [SNDRV_PCM_READY_##v] = #v
+#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v
+#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v
+#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v
+#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v
+#define START(v) [SNDRV_PCM_START_##v] = #v
+#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v
static char *snd_pcm_stream_names[] = {
STREAM(PLAYBACK),
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 72cfd47af6b..30f410832a2 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -197,12 +197,16 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
avail = snd_pcm_capture_avail(runtime);
if (avail > runtime->avail_max)
runtime->avail_max = avail;
- if (avail >= runtime->stop_threshold) {
- if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+ if (avail >= runtime->buffer_size) {
snd_pcm_drain_done(substream);
- else
+ return -EPIPE;
+ }
+ } else {
+ if (avail >= runtime->stop_threshold) {
xrun(substream);
- return -EPIPE;
+ return -EPIPE;
+ }
}
if (avail >= runtime->control->avail_min)
wake_up(&runtime->sleep);
@@ -943,47 +947,24 @@ static int snd_interval_ratden(struct snd_interval *i,
int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask)
{
unsigned int k;
- int changed = 0;
+ struct snd_interval list_range;
if (!count) {
i->empty = 1;
return -EINVAL;
}
+ snd_interval_any(&list_range);
+ list_range.min = UINT_MAX;
+ list_range.max = 0;
for (k = 0; k < count; k++) {
if (mask && !(mask & (1 << k)))
continue;
- if (i->min == list[k] && !i->openmin)
- goto _l1;
- if (i->min < list[k]) {
- i->min = list[k];
- i->openmin = 0;
- changed = 1;
- goto _l1;
- }
- }
- i->empty = 1;
- return -EINVAL;
- _l1:
- for (k = count; k-- > 0;) {
- if (mask && !(mask & (1 << k)))
+ if (!snd_interval_test(i, list[k]))
continue;
- if (i->max == list[k] && !i->openmax)
- goto _l2;
- if (i->max > list[k]) {
- i->max = list[k];
- i->openmax = 0;
- changed = 1;
- goto _l2;
- }
+ list_range.min = min(list_range.min, list[k]);
+ list_range.max = max(list_range.max, list[k]);
}
- i->empty = 1;
- return -EINVAL;
- _l2:
- if (snd_interval_checkempty(i)) {
- i->empty = 1;
- return -EINVAL;
- }
- return changed;
+ return snd_interval_refine(i, &list_range);
}
EXPORT_SYMBOL(snd_interval_list);
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index a6d42808828..caa7796bc2f 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -304,6 +304,7 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
+#ifdef CONFIG_SND_DMA_SGBUF
/**
* snd_pcm_sgbuf_ops_page - get the page struct at the given offset
* @substream: the pcm substream instance
@@ -349,6 +350,7 @@ unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
return size;
}
EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
+#endif /* CONFIG_SND_DMA_SGBUF */
/**
* snd_pcm_lib_malloc_pages - allocate the DMA buffer
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index ac2150e0670..59e5fbe6af5 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1343,8 +1343,6 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream,
static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state)
{
- if (substream->f_flags & O_NONBLOCK)
- return -EAGAIN;
substream->runtime->trigger_master = substream;
return 0;
}
@@ -1392,7 +1390,6 @@ static struct action_ops snd_pcm_action_drain_init = {
struct drain_rec {
struct snd_pcm_substream *substream;
wait_queue_t wait;
- snd_pcm_uframes_t stop_threshold;
};
static int snd_pcm_drop(struct snd_pcm_substream *substream);
@@ -1404,13 +1401,15 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream);
* After this call, all streams are supposed to be either SETUP or DRAINING
* (capture only) state.
*/
-static int snd_pcm_drain(struct snd_pcm_substream *substream)
+static int snd_pcm_drain(struct snd_pcm_substream *substream,
+ struct file *file)
{
struct snd_card *card;
struct snd_pcm_runtime *runtime;
struct snd_pcm_substream *s;
int result = 0;
int i, num_drecs;
+ int nonblock = 0;
struct drain_rec *drec, drec_tmp, *d;
card = substream->pcm->card;
@@ -1428,6 +1427,15 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
}
}
+ if (file) {
+ if (file->f_flags & O_NONBLOCK)
+ nonblock = 1;
+ } else if (substream->f_flags & O_NONBLOCK)
+ nonblock = 1;
+
+ if (nonblock)
+ goto lock; /* no need to allocate waitqueues */
+
/* allocate temporary record for drain sync */
down_read(&snd_pcm_link_rwsem);
if (snd_pcm_stream_linked(substream)) {
@@ -1449,16 +1457,11 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
d->substream = s;
init_waitqueue_entry(&d->wait, current);
add_wait_queue(&runtime->sleep, &d->wait);
- /* stop_threshold fixup to avoid endless loop when
- * stop_threshold > buffer_size
- */
- d->stop_threshold = runtime->stop_threshold;
- if (runtime->stop_threshold > runtime->buffer_size)
- runtime->stop_threshold = runtime->buffer_size;
}
}
up_read(&snd_pcm_link_rwsem);
+ lock:
snd_pcm_stream_lock_irq(substream);
/* resume pause */
if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED)
@@ -1466,9 +1469,12 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
/* pre-start/stop - all running streams are changed to DRAINING state */
result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
- if (result < 0) {
- snd_pcm_stream_unlock_irq(substream);
- goto _error;
+ if (result < 0)
+ goto unlock;
+ /* in non-blocking, we don't wait in ioctl but let caller poll */
+ if (nonblock) {
+ result = -EAGAIN;
+ goto unlock;
}
for (;;) {
@@ -1504,18 +1510,18 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
}
}
+ unlock:
snd_pcm_stream_unlock_irq(substream);
- _error:
- for (i = 0; i < num_drecs; i++) {
- d = &drec[i];
- runtime = d->substream->runtime;
- remove_wait_queue(&runtime->sleep, &d->wait);
- runtime->stop_threshold = d->stop_threshold;
+ if (!nonblock) {
+ for (i = 0; i < num_drecs; i++) {
+ d = &drec[i];
+ runtime = d->substream->runtime;
+ remove_wait_queue(&runtime->sleep, &d->wait);
+ }
+ if (drec != &drec_tmp)
+ kfree(drec);
}
-
- if (drec != &drec_tmp)
- kfree(drec);
snd_power_unlock(card);
return result;
@@ -2208,6 +2214,9 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst
case SNDRV_PCM_STATE_XRUN:
ret = -EPIPE;
goto __end;
+ case SNDRV_PCM_STATE_SUSPENDED:
+ ret = -ESTRPIPE;
+ goto __end;
default:
ret = -EBADFD;
goto __end;
@@ -2253,6 +2262,9 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr
case SNDRV_PCM_STATE_XRUN:
ret = -EPIPE;
goto __end;
+ case SNDRV_PCM_STATE_SUSPENDED:
+ ret = -ESTRPIPE;
+ goto __end;
default:
ret = -EBADFD;
goto __end;
@@ -2299,6 +2311,9 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs
case SNDRV_PCM_STATE_XRUN:
ret = -EPIPE;
goto __end;
+ case SNDRV_PCM_STATE_SUSPENDED:
+ ret = -ESTRPIPE;
+ goto __end;
default:
ret = -EBADFD;
goto __end;
@@ -2345,6 +2360,9 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst
case SNDRV_PCM_STATE_XRUN:
ret = -EPIPE;
goto __end;
+ case SNDRV_PCM_STATE_SUSPENDED:
+ ret = -ESTRPIPE;
+ goto __end;
default:
ret = -EBADFD;
goto __end;
@@ -2544,7 +2562,7 @@ static int snd_pcm_common_ioctl1(struct file *file,
return snd_pcm_hw_params_old_user(substream, arg);
#endif
case SNDRV_PCM_IOCTL_DRAIN:
- return snd_pcm_drain(substream);
+ return snd_pcm_drain(substream, file);
case SNDRV_PCM_IOCTL_DROP:
return snd_pcm_drop(substream);
case SNDRV_PCM_IOCTL_PAUSE:
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 473247c8e6d..c0adc14c91f 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -274,7 +274,7 @@ static int open_substream(struct snd_rawmidi *rmidi,
return err;
substream->opened = 1;
if (substream->use_count++ == 0)
- substream->active_sensing = 1;
+ substream->active_sensing = 0;
if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
substream->append = 1;
rmidi->streams[substream->stream].substream_opened++;
diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
index 0a711d2d04f..9dfb2f77be6 100644
--- a/sound/core/seq/oss/seq_oss_midi.c
+++ b/sound/core/seq/oss/seq_oss_midi.c
@@ -20,6 +20,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <sound/asoundef.h>
#include "seq_oss_midi.h"
#include "seq_oss_readq.h"
#include "seq_oss_timer.h"
@@ -476,19 +477,20 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
ev.source.port = dp->port;
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {
ev.type = SNDRV_SEQ_EVENT_SENSING;
- snd_seq_oss_dispatch(dp, &ev, 0, 0); /* active sensing */
+ snd_seq_oss_dispatch(dp, &ev, 0, 0);
}
for (c = 0; c < 16; c++) {
ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
ev.data.control.channel = c;
- ev.data.control.param = 123;
- snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */
+ ev.data.control.param = MIDI_CTL_ALL_NOTES_OFF;
+ snd_seq_oss_dispatch(dp, &ev, 0, 0);
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
- ev.data.control.param = 121;
- snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */
+ ev.data.control.param =
+ MIDI_CTL_RESET_CONTROLLERS;
+ snd_seq_oss_dispatch(dp, &ev, 0, 0);
ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
ev.data.control.value = 0;
- snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */
+ snd_seq_oss_dispatch(dp, &ev, 0, 0);
}
}
}
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index 4d26146a62c..ebaf1b541dc 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -120,7 +120,8 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i
return -EINVAL;
runtime = substream->runtime;
if ((tmp = runtime->avail) < count) {
- snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp);
+ if (printk_ratelimit())
+ snd_printk(KERN_ERR "MIDI output buffer overrun\n");
return -ENOMEM;
}
if (snd_rawmidi_kernel_write(substream, buf, count) < count)
@@ -236,6 +237,7 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
memset(&params, 0, sizeof(params));
params.avail_min = 1;
params.buffer_size = output_buffer_size;
+ params.no_active_sensing = 1;
if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, &params)) < 0) {
snd_rawmidi_kernel_release(&msynth->output_rfile);
return err;
@@ -248,12 +250,9 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
{
struct seq_midisynth *msynth = private_data;
- unsigned char buf = 0xff; /* MIDI reset */
if (snd_BUG_ON(!msynth->output_rfile.output))
return -EINVAL;
- /* sending single MIDI reset message to shut the device up */
- snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1);
snd_rawmidi_drain_output(msynth->output_rfile.output);
return snd_rawmidi_kernel_release(&msynth->output_rfile);
}
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
index 257624bd199..3b9b550109c 100644
--- a/sound/core/vmaster.c
+++ b/sound/core/vmaster.c
@@ -353,7 +353,8 @@ static void master_free(struct snd_kcontrol *kcontrol)
*
* The optional argument @tlv can be used to specify the TLV information
* for dB scale of the master control. It should be a single element
- * with #SNDRV_CTL_TLVT_DB_SCALE type, and should be the max 0dB.
+ * with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or
+ * #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB.
*/
struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
const unsigned int *tlv)
@@ -384,7 +385,10 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
kctl->private_free = master_free;
/* additional (constant) TLV read */
- if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) {
+ if (tlv &&
+ (tlv[0] == SNDRV_CTL_TLVT_DB_SCALE ||
+ tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX ||
+ tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX_MUTE)) {
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
memcpy(master->tlv, tlv, sizeof(master->tlv));
kctl->tlv.p = master->tlv;
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index 54239d2e099..6ba066c41d2 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -25,12 +25,15 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
+#include <linux/hrtimer.h>
+#include <linux/math64.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/rawmidi.h>
+#include <sound/info.h>
#include <sound/initval.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -39,7 +42,7 @@ MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");
#define MAX_PCM_DEVICES 4
-#define MAX_PCM_SUBSTREAMS 16
+#define MAX_PCM_SUBSTREAMS 128
#define MAX_MIDI_DEVICES 2
#if 0 /* emu10k1 emulation */
@@ -148,6 +151,10 @@ static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+#ifdef CONFIG_HIGH_RES_TIMERS
+static int hrtimer = 1;
+#endif
+static int fake_buffer = 1;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for dummy soundcard.");
@@ -161,6 +168,12 @@ module_param_array(pcm_substreams, int, NULL, 0444);
MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver.");
//module_param_array(midi_devs, int, NULL, 0444);
//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
+module_param(fake_buffer, bool, 0444);
+MODULE_PARM_DESC(fake_buffer, "Fake buffer allocations.");
+#ifdef CONFIG_HIGH_RES_TIMERS
+module_param(hrtimer, bool, 0644);
+MODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source.");
+#endif
static struct platform_device *devices[SNDRV_CARDS];
@@ -171,137 +184,324 @@ static struct platform_device *devices[SNDRV_CARDS];
#define MIXER_ADDR_CD 4
#define MIXER_ADDR_LAST 4
+struct dummy_timer_ops {
+ int (*create)(struct snd_pcm_substream *);
+ void (*free)(struct snd_pcm_substream *);
+ int (*prepare)(struct snd_pcm_substream *);
+ int (*start)(struct snd_pcm_substream *);
+ int (*stop)(struct snd_pcm_substream *);
+ snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *);
+};
+
struct snd_dummy {
struct snd_card *card;
struct snd_pcm *pcm;
spinlock_t mixer_lock;
int mixer_volume[MIXER_ADDR_LAST+1][2];
int capture_source[MIXER_ADDR_LAST+1][2];
+ const struct dummy_timer_ops *timer_ops;
};
-struct snd_dummy_pcm {
- struct snd_dummy *dummy;
+/*
+ * system timer interface
+ */
+
+struct dummy_systimer_pcm {
spinlock_t lock;
struct timer_list timer;
- unsigned int pcm_buffer_size;
- unsigned int pcm_period_size;
- unsigned int pcm_bps; /* bytes per second */
- unsigned int pcm_hz; /* HZ */
- unsigned int pcm_irq_pos; /* IRQ position */
- unsigned int pcm_buf_pos; /* position in buffer */
+ unsigned long base_time;
+ unsigned int frac_pos; /* fractional sample position (based HZ) */
+ unsigned int frac_period_rest;
+ unsigned int frac_buffer_size; /* buffer_size * HZ */
+ unsigned int frac_period_size; /* period_size * HZ */
+ unsigned int rate;
+ int elapsed;
struct snd_pcm_substream *substream;
};
-
-static inline void snd_card_dummy_pcm_timer_start(struct snd_dummy_pcm *dpcm)
+static void dummy_systimer_rearm(struct dummy_systimer_pcm *dpcm)
{
- dpcm->timer.expires = 1 + jiffies;
+ dpcm->timer.expires = jiffies +
+ (dpcm->frac_period_rest + dpcm->rate - 1) / dpcm->rate;
add_timer(&dpcm->timer);
}
-static inline void snd_card_dummy_pcm_timer_stop(struct snd_dummy_pcm *dpcm)
+static void dummy_systimer_update(struct dummy_systimer_pcm *dpcm)
{
- del_timer(&dpcm->timer);
+ unsigned long delta;
+
+ delta = jiffies - dpcm->base_time;
+ if (!delta)
+ return;
+ dpcm->base_time += delta;
+ delta *= dpcm->rate;
+ dpcm->frac_pos += delta;
+ while (dpcm->frac_pos >= dpcm->frac_buffer_size)
+ dpcm->frac_pos -= dpcm->frac_buffer_size;
+ while (dpcm->frac_period_rest <= delta) {
+ dpcm->elapsed++;
+ dpcm->frac_period_rest += dpcm->frac_period_size;
+ }
+ dpcm->frac_period_rest -= delta;
}
-static int snd_card_dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int dummy_systimer_start(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dummy_pcm *dpcm = runtime->private_data;
- int err = 0;
+ struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
+ spin_lock(&dpcm->lock);
+ dpcm->base_time = jiffies;
+ dummy_systimer_rearm(dpcm);
+ spin_unlock(&dpcm->lock);
+ return 0;
+}
+static int dummy_systimer_stop(struct snd_pcm_substream *substream)
+{
+ struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
spin_lock(&dpcm->lock);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- snd_card_dummy_pcm_timer_start(dpcm);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- snd_card_dummy_pcm_timer_stop(dpcm);
- break;
- default:
- err = -EINVAL;
- break;
- }
+ del_timer(&dpcm->timer);
spin_unlock(&dpcm->lock);
return 0;
}
-static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream)
+static int dummy_systimer_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dummy_pcm *dpcm = runtime->private_data;
- int bps;
-
- bps = snd_pcm_format_width(runtime->format) * runtime->rate *
- runtime->channels / 8;
-
- if (bps <= 0)
- return -EINVAL;
-
- dpcm->pcm_bps = bps;
- dpcm->pcm_hz = HZ;
- dpcm->pcm_buffer_size = snd_pcm_lib_buffer_bytes(substream);
- dpcm->pcm_period_size = snd_pcm_lib_period_bytes(substream);
- dpcm->pcm_irq_pos = 0;
- dpcm->pcm_buf_pos = 0;
+ struct dummy_systimer_pcm *dpcm = runtime->private_data;
- snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
- bytes_to_samples(runtime, runtime->dma_bytes));
+ dpcm->frac_pos = 0;
+ dpcm->rate = runtime->rate;
+ dpcm->frac_buffer_size = runtime->buffer_size * HZ;
+ dpcm->frac_period_size = runtime->period_size * HZ;
+ dpcm->frac_period_rest = dpcm->frac_period_size;
+ dpcm->elapsed = 0;
return 0;
}
-static void snd_card_dummy_pcm_timer_function(unsigned long data)
+static void dummy_systimer_callback(unsigned long data)
{
- struct snd_dummy_pcm *dpcm = (struct snd_dummy_pcm *)data;
+ struct dummy_systimer_pcm *dpcm = (struct dummy_systimer_pcm *)data;
unsigned long flags;
+ int elapsed = 0;
spin_lock_irqsave(&dpcm->lock, flags);
- dpcm->timer.expires = 1 + jiffies;
- add_timer(&dpcm->timer);
- dpcm->pcm_irq_pos += dpcm->pcm_bps;
- dpcm->pcm_buf_pos += dpcm->pcm_bps;
- dpcm->pcm_buf_pos %= dpcm->pcm_buffer_size * dpcm->pcm_hz;
- if (dpcm->pcm_irq_pos >= dpcm->pcm_period_size * dpcm->pcm_hz) {
- dpcm->pcm_irq_pos %= dpcm->pcm_period_size * dpcm->pcm_hz;
- spin_unlock_irqrestore(&dpcm->lock, flags);
+ dummy_systimer_update(dpcm);
+ dummy_systimer_rearm(dpcm);
+ elapsed = dpcm->elapsed;
+ dpcm->elapsed = 0;
+ spin_unlock_irqrestore(&dpcm->lock, flags);
+ if (elapsed)
+ snd_pcm_period_elapsed(dpcm->substream);
+}
+
+static snd_pcm_uframes_t
+dummy_systimer_pointer(struct snd_pcm_substream *substream)
+{
+ struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
+ snd_pcm_uframes_t pos;
+
+ spin_lock(&dpcm->lock);
+ dummy_systimer_update(dpcm);
+ pos = dpcm->frac_pos / HZ;
+ spin_unlock(&dpcm->lock);
+ return pos;
+}
+
+static int dummy_systimer_create(struct snd_pcm_substream *substream)
+{
+ struct dummy_systimer_pcm *dpcm;
+
+ dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
+ if (!dpcm)
+ return -ENOMEM;
+ substream->runtime->private_data = dpcm;
+ init_timer(&dpcm->timer);
+ dpcm->timer.data = (unsigned long) dpcm;
+ dpcm->timer.function = dummy_systimer_callback;
+ spin_lock_init(&dpcm->lock);
+ dpcm->substream = substream;
+ return 0;
+}
+
+static void dummy_systimer_free(struct snd_pcm_substream *substream)
+{
+ kfree(substream->runtime->private_data);
+}
+
+static struct dummy_timer_ops dummy_systimer_ops = {
+ .create = dummy_systimer_create,
+ .free = dummy_systimer_free,
+ .prepare = dummy_systimer_prepare,
+ .start = dummy_systimer_start,
+ .stop = dummy_systimer_stop,
+ .pointer = dummy_systimer_pointer,
+};
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+/*
+ * hrtimer interface
+ */
+
+struct dummy_hrtimer_pcm {
+ ktime_t base_time;
+ ktime_t period_time;
+ atomic_t running;
+ struct hrtimer timer;
+ struct tasklet_struct tasklet;
+ struct snd_pcm_substream *substream;
+};
+
+static void dummy_hrtimer_pcm_elapsed(unsigned long priv)
+{
+ struct dummy_hrtimer_pcm *dpcm = (struct dummy_hrtimer_pcm *)priv;
+ if (atomic_read(&dpcm->running))
snd_pcm_period_elapsed(dpcm->substream);
- } else
- spin_unlock_irqrestore(&dpcm->lock, flags);
}
-static snd_pcm_uframes_t snd_card_dummy_pcm_pointer(struct snd_pcm_substream *substream)
+static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer)
+{
+ struct dummy_hrtimer_pcm *dpcm;
+
+ dpcm = container_of(timer, struct dummy_hrtimer_pcm, timer);
+ if (!atomic_read(&dpcm->running))
+ return HRTIMER_NORESTART;
+ tasklet_schedule(&dpcm->tasklet);
+ hrtimer_forward_now(timer, dpcm->period_time);
+ return HRTIMER_RESTART;
+}
+
+static int dummy_hrtimer_start(struct snd_pcm_substream *substream)
+{
+ struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
+
+ dpcm->base_time = hrtimer_cb_get_time(&dpcm->timer);
+ hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL);
+ atomic_set(&dpcm->running, 1);
+ return 0;
+}
+
+static int dummy_hrtimer_stop(struct snd_pcm_substream *substream)
+{
+ struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
+
+ atomic_set(&dpcm->running, 0);
+ hrtimer_cancel(&dpcm->timer);
+ return 0;
+}
+
+static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm)
+{
+ tasklet_kill(&dpcm->tasklet);
+}
+
+static snd_pcm_uframes_t
+dummy_hrtimer_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dummy_pcm *dpcm = runtime->private_data;
+ struct dummy_hrtimer_pcm *dpcm = runtime->private_data;
+ u64 delta;
+ u32 pos;
+
+ delta = ktime_us_delta(hrtimer_cb_get_time(&dpcm->timer),
+ dpcm->base_time);
+ delta = div_u64(delta * runtime->rate + 999999, 1000000);
+ div_u64_rem(delta, runtime->buffer_size, &pos);
+ return pos;
+}
- return bytes_to_frames(runtime, dpcm->pcm_buf_pos / dpcm->pcm_hz);
+static int dummy_hrtimer_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dummy_hrtimer_pcm *dpcm = runtime->private_data;
+ unsigned int period, rate;
+ long sec;
+ unsigned long nsecs;
+
+ dummy_hrtimer_sync(dpcm);
+ period = runtime->period_size;
+ rate = runtime->rate;
+ sec = period / rate;
+ period %= rate;
+ nsecs = div_u64((u64)period * 1000000000UL + rate - 1, rate);
+ dpcm->period_time = ktime_set(sec, nsecs);
+
+ return 0;
}
-static struct snd_pcm_hardware snd_card_dummy_playback =
+static int dummy_hrtimer_create(struct snd_pcm_substream *substream)
{
- .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
- .formats = USE_FORMATS,
- .rates = USE_RATE,
- .rate_min = USE_RATE_MIN,
- .rate_max = USE_RATE_MAX,
- .channels_min = USE_CHANNELS_MIN,
- .channels_max = USE_CHANNELS_MAX,
- .buffer_bytes_max = MAX_BUFFER_SIZE,
- .period_bytes_min = 64,
- .period_bytes_max = MAX_PERIOD_SIZE,
- .periods_min = USE_PERIODS_MIN,
- .periods_max = USE_PERIODS_MAX,
- .fifo_size = 0,
+ struct dummy_hrtimer_pcm *dpcm;
+
+ dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
+ if (!dpcm)
+ return -ENOMEM;
+ substream->runtime->private_data = dpcm;
+ hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ dpcm->timer.function = dummy_hrtimer_callback;
+ dpcm->substream = substream;
+ atomic_set(&dpcm->running, 0);
+ tasklet_init(&dpcm->tasklet, dummy_hrtimer_pcm_elapsed,
+ (unsigned long)dpcm);
+ return 0;
+}
+
+static void dummy_hrtimer_free(struct snd_pcm_substream *substream)
+{
+ struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
+ dummy_hrtimer_sync(dpcm);
+ kfree(dpcm);
+}
+
+static struct dummy_timer_ops dummy_hrtimer_ops = {
+ .create = dummy_hrtimer_create,
+ .free = dummy_hrtimer_free,
+ .prepare = dummy_hrtimer_prepare,
+ .start = dummy_hrtimer_start,
+ .stop = dummy_hrtimer_stop,
+ .pointer = dummy_hrtimer_pointer,
};
-static struct snd_pcm_hardware snd_card_dummy_capture =
+#endif /* CONFIG_HIGH_RES_TIMERS */
+
+/*
+ * PCM interface
+ */
+
+static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
+ struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ return dummy->timer_ops->start(substream);
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ return dummy->timer_ops->stop(substream);
+ }
+ return -EINVAL;
+}
+
+static int dummy_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
+
+ return dummy->timer_ops->prepare(substream);
+}
+
+static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
+
+ return dummy->timer_ops->pointer(substream);
+}
+
+static struct snd_pcm_hardware dummy_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_MMAP_VALID),
.formats = USE_FORMATS,
.rates = USE_RATE,
.rate_min = USE_RATE_MIN,
@@ -316,123 +516,152 @@ static struct snd_pcm_hardware snd_card_dummy_capture =
.fifo_size = 0,
};
-static void snd_card_dummy_runtime_free(struct snd_pcm_runtime *runtime)
-{
- kfree(runtime->private_data);
-}
-
-static int snd_card_dummy_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+static int dummy_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (fake_buffer) {
+ /* runtime->dma_bytes has to be set manually to allow mmap */
+ substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
+ return 0;
+ }
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
}
-static int snd_card_dummy_hw_free(struct snd_pcm_substream *substream)
+static int dummy_pcm_hw_free(struct snd_pcm_substream *substream)
{
+ if (fake_buffer)
+ return 0;
return snd_pcm_lib_free_pages(substream);
}
-static struct snd_dummy_pcm *new_pcm_stream(struct snd_pcm_substream *substream)
-{
- struct snd_dummy_pcm *dpcm;
-
- dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
- if (! dpcm)
- return dpcm;
- init_timer(&dpcm->timer);
- dpcm->timer.data = (unsigned long) dpcm;
- dpcm->timer.function = snd_card_dummy_pcm_timer_function;
- spin_lock_init(&dpcm->lock);
- dpcm->substream = substream;
- return dpcm;
-}
-
-static int snd_card_dummy_playback_open(struct snd_pcm_substream *substream)
+static int dummy_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dummy_pcm *dpcm;
int err;
- if ((dpcm = new_pcm_stream(substream)) == NULL)
- return -ENOMEM;
- runtime->private_data = dpcm;
- /* makes the infrastructure responsible for freeing dpcm */
- runtime->private_free = snd_card_dummy_runtime_free;
- runtime->hw = snd_card_dummy_playback;
+ dummy->timer_ops = &dummy_systimer_ops;
+#ifdef CONFIG_HIGH_RES_TIMERS
+ if (hrtimer)
+ dummy->timer_ops = &dummy_hrtimer_ops;
+#endif
+
+ err = dummy->timer_ops->create(substream);
+ if (err < 0)
+ return err;
+
+ runtime->hw = dummy_pcm_hardware;
if (substream->pcm->device & 1) {
runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
}
if (substream->pcm->device & 2)
- runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
- err = add_playback_constraints(runtime);
- if (err < 0)
+ runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ err = add_playback_constraints(substream->runtime);
+ else
+ err = add_capture_constraints(substream->runtime);
+ if (err < 0) {
+ dummy->timer_ops->free(substream);
return err;
-
+ }
return 0;
}
-static int snd_card_dummy_capture_open(struct snd_pcm_substream *substream)
+static int dummy_pcm_close(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dummy_pcm *dpcm;
- int err;
+ struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
+ dummy->timer_ops->free(substream);
+ return 0;
+}
- if ((dpcm = new_pcm_stream(substream)) == NULL)
- return -ENOMEM;
- runtime->private_data = dpcm;
- /* makes the infrastructure responsible for freeing dpcm */
- runtime->private_free = snd_card_dummy_runtime_free;
- runtime->hw = snd_card_dummy_capture;
- if (substream->pcm->device == 1) {
- runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
- runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
+/*
+ * dummy buffer handling
+ */
+
+static void *dummy_page[2];
+
+static void free_fake_buffer(void)
+{
+ if (fake_buffer) {
+ int i;
+ for (i = 0; i < 2; i++)
+ if (dummy_page[i]) {
+ free_page((unsigned long)dummy_page[i]);
+ dummy_page[i] = NULL;
+ }
}
- if (substream->pcm->device & 2)
- runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
- err = add_capture_constraints(runtime);
- if (err < 0)
- return err;
+}
+static int alloc_fake_buffer(void)
+{
+ int i;
+
+ if (!fake_buffer)
+ return 0;
+ for (i = 0; i < 2; i++) {
+ dummy_page[i] = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!dummy_page[i]) {
+ free_fake_buffer();
+ return -ENOMEM;
+ }
+ }
return 0;
}
-static int snd_card_dummy_playback_close(struct snd_pcm_substream *substream)
+static int dummy_pcm_copy(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t pos,
+ void __user *dst, snd_pcm_uframes_t count)
{
- return 0;
+ return 0; /* do nothing */
}
-static int snd_card_dummy_capture_close(struct snd_pcm_substream *substream)
+static int dummy_pcm_silence(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t pos,
+ snd_pcm_uframes_t count)
{
- return 0;
+ return 0; /* do nothing */
+}
+
+static struct page *dummy_pcm_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ return virt_to_page(dummy_page[substream->stream]); /* the same page */
}
-static struct snd_pcm_ops snd_card_dummy_playback_ops = {
- .open = snd_card_dummy_playback_open,
- .close = snd_card_dummy_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_card_dummy_hw_params,
- .hw_free = snd_card_dummy_hw_free,
- .prepare = snd_card_dummy_pcm_prepare,
- .trigger = snd_card_dummy_pcm_trigger,
- .pointer = snd_card_dummy_pcm_pointer,
+static struct snd_pcm_ops dummy_pcm_ops = {
+ .open = dummy_pcm_open,
+ .close = dummy_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = dummy_pcm_hw_params,
+ .hw_free = dummy_pcm_hw_free,
+ .prepare = dummy_pcm_prepare,
+ .trigger = dummy_pcm_trigger,
+ .pointer = dummy_pcm_pointer,
};
-static struct snd_pcm_ops snd_card_dummy_capture_ops = {
- .open = snd_card_dummy_capture_open,
- .close = snd_card_dummy_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_card_dummy_hw_params,
- .hw_free = snd_card_dummy_hw_free,
- .prepare = snd_card_dummy_pcm_prepare,
- .trigger = snd_card_dummy_pcm_trigger,
- .pointer = snd_card_dummy_pcm_pointer,
+static struct snd_pcm_ops dummy_pcm_ops_no_buf = {
+ .open = dummy_pcm_open,
+ .close = dummy_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = dummy_pcm_hw_params,
+ .hw_free = dummy_pcm_hw_free,
+ .prepare = dummy_pcm_prepare,
+ .trigger = dummy_pcm_trigger,
+ .pointer = dummy_pcm_pointer,
+ .copy = dummy_pcm_copy,
+ .silence = dummy_pcm_silence,
+ .page = dummy_pcm_page,
};
static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
int substreams)
{
struct snd_pcm *pcm;
+ struct snd_pcm_ops *ops;
int err;
err = snd_pcm_new(dummy->card, "Dummy PCM", device,
@@ -440,17 +669,28 @@ static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
if (err < 0)
return err;
dummy->pcm = pcm;
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops);
+ if (fake_buffer)
+ ops = &dummy_pcm_ops_no_buf;
+ else
+ ops = &dummy_pcm_ops;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops);
pcm->private_data = dummy;
pcm->info_flags = 0;
strcpy(pcm->name, "Dummy PCM");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- 0, 64*1024);
+ if (!fake_buffer) {
+ snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
+ 0, 64*1024);
+ }
return 0;
}
+/*
+ * mixer interface
+ */
+
#define DUMMY_VOLUME(xname, xindex, addr) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
@@ -581,6 +821,131 @@ static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy)
return 0;
}
+#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS)
+/*
+ * proc interface
+ */
+static void print_formats(struct snd_info_buffer *buffer)
+{
+ int i;
+
+ for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
+ if (dummy_pcm_hardware.formats & (1ULL << i))
+ snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
+ }
+}
+
+static void print_rates(struct snd_info_buffer *buffer)
+{
+ static int rates[] = {
+ 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
+ 64000, 88200, 96000, 176400, 192000,
+ };
+ int i;
+
+ if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_CONTINUOUS)
+ snd_iprintf(buffer, " continuous");
+ if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_KNOT)
+ snd_iprintf(buffer, " knot");
+ for (i = 0; i < ARRAY_SIZE(rates); i++)
+ if (dummy_pcm_hardware.rates & (1 << i))
+ snd_iprintf(buffer, " %d", rates[i]);
+}
+
+#define get_dummy_int_ptr(ofs) \
+ (unsigned int *)((char *)&dummy_pcm_hardware + (ofs))
+#define get_dummy_ll_ptr(ofs) \
+ (unsigned long long *)((char *)&dummy_pcm_hardware + (ofs))
+
+struct dummy_hw_field {
+ const char *name;
+ const char *format;
+ unsigned int offset;
+ unsigned int size;
+};
+#define FIELD_ENTRY(item, fmt) { \
+ .name = #item, \
+ .format = fmt, \
+ .offset = offsetof(struct snd_pcm_hardware, item), \
+ .size = sizeof(dummy_pcm_hardware.item) }
+
+static struct dummy_hw_field fields[] = {
+ FIELD_ENTRY(formats, "%#llx"),
+ FIELD_ENTRY(rates, "%#x"),
+ FIELD_ENTRY(rate_min, "%d"),
+ FIELD_ENTRY(rate_max, "%d"),
+ FIELD_ENTRY(channels_min, "%d"),
+ FIELD_ENTRY(channels_max, "%d"),
+ FIELD_ENTRY(buffer_bytes_max, "%ld"),
+ FIELD_ENTRY(period_bytes_min, "%ld"),
+ FIELD_ENTRY(period_bytes_max, "%ld"),
+ FIELD_ENTRY(periods_min, "%d"),
+ FIELD_ENTRY(periods_max, "%d"),
+};
+
+static void dummy_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fields); i++) {
+ snd_iprintf(buffer, "%s ", fields[i].name);
+ if (fields[i].size == sizeof(int))
+ snd_iprintf(buffer, fields[i].format,
+ *get_dummy_int_ptr(fields[i].offset));
+ else
+ snd_iprintf(buffer, fields[i].format,
+ *get_dummy_ll_ptr(fields[i].offset));
+ if (!strcmp(fields[i].name, "formats"))
+ print_formats(buffer);
+ else if (!strcmp(fields[i].name, "rates"))
+ print_rates(buffer);
+ snd_iprintf(buffer, "\n");
+ }
+}
+
+static void dummy_proc_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ char line[64];
+
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ char item[20];
+ const char *ptr;
+ unsigned long long val;
+ int i;
+
+ ptr = snd_info_get_str(item, line, sizeof(item));
+ for (i = 0; i < ARRAY_SIZE(fields); i++) {
+ if (!strcmp(item, fields[i].name))
+ break;
+ }
+ if (i >= ARRAY_SIZE(fields))
+ continue;
+ snd_info_get_str(item, ptr, sizeof(item));
+ if (strict_strtoull(item, 0, &val))
+ continue;
+ if (fields[i].size == sizeof(int))
+ *get_dummy_int_ptr(fields[i].offset) = val;
+ else
+ *get_dummy_ll_ptr(fields[i].offset) = val;
+ }
+}
+
+static void __devinit dummy_proc_init(struct snd_dummy *chip)
+{
+ struct snd_info_entry *entry;
+
+ if (!snd_card_proc_new(chip->card, "dummy_pcm", &entry)) {
+ snd_info_set_text_ops(entry, chip, dummy_proc_read);
+ entry->c.text.write = dummy_proc_write;
+ entry->mode |= S_IWUSR;
+ }
+}
+#else
+#define dummy_proc_init(x)
+#endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */
+
static int __devinit snd_dummy_probe(struct platform_device *devptr)
{
struct snd_card *card;
@@ -610,6 +975,8 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
strcpy(card->shortname, "Dummy");
sprintf(card->longname, "Dummy %i", dev + 1);
+ dummy_proc_init(dummy);
+
snd_card_set_dev(card, &devptr->dev);
err = snd_card_register(card);
@@ -670,6 +1037,7 @@ static void snd_dummy_unregister_all(void)
for (i = 0; i < ARRAY_SIZE(devices); ++i)
platform_device_unregister(devices[i]);
platform_driver_unregister(&snd_dummy_driver);
+ free_fake_buffer();
}
static int __init alsa_card_dummy_init(void)
@@ -680,6 +1048,12 @@ static int __init alsa_card_dummy_init(void)
if (err < 0)
return err;
+ err = alloc_fake_buffer();
+ if (err < 0) {
+ platform_driver_unregister(&snd_dummy_driver);
+ return err;
+ }
+
cards = 0;
for (i = 0; i < SNDRV_CARDS; i++) {
struct platform_device *device;
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
index 3ee0269e5bd..02f79d25271 100644
--- a/sound/isa/cmi8330.c
+++ b/sound/isa/cmi8330.c
@@ -1,5 +1,5 @@
/*
- * Driver for C-Media's CMI8330 soundcards.
+ * Driver for C-Media's CMI8330 and CMI8329 soundcards.
* Copyright (c) by George Talusan <gstalusan@uwaterloo.ca>
* http://www.undergrad.math.uwaterloo.ca/~gstalusa
*
@@ -35,7 +35,7 @@
*
* This card has two mixers and two PCM devices. I've cheesed it such
* that recording and playback can be done through the same device.
- * The driver "magically" routes the capturing to the CMI8330 codec,
+ * The driver "magically" routes the capturing to the AD1848 codec,
* and playback to the SB16 codec. This allows for full-duplex mode
* to some extent.
* The utilities in alsa-utils are aware of both devices, so passing
@@ -64,7 +64,7 @@
/*
*/
MODULE_AUTHOR("George Talusan <gstalusan@uwaterloo.ca>");
-MODULE_DESCRIPTION("C-Media CMI8330");
+MODULE_DESCRIPTION("C-Media CMI8330/CMI8329");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}");
@@ -86,38 +86,38 @@ static long mpuport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
static int mpuirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for CMI8330 soundcard.");
+MODULE_PARM_DESC(index, "Index value for CMI8330/CMI8329 soundcard.");
module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for CMI8330 soundcard.");
+MODULE_PARM_DESC(id, "ID string for CMI8330/CMI8329 soundcard.");
module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable CMI8330 soundcard.");
+MODULE_PARM_DESC(enable, "Enable CMI8330/CMI8329 soundcard.");
#ifdef CONFIG_PNP
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
module_param_array(sbport, long, NULL, 0444);
-MODULE_PARM_DESC(sbport, "Port # for CMI8330 SB driver.");
+MODULE_PARM_DESC(sbport, "Port # for CMI8330/CMI8329 SB driver.");
module_param_array(sbirq, int, NULL, 0444);
-MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330 SB driver.");
+MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330/CMI8329 SB driver.");
module_param_array(sbdma8, int, NULL, 0444);
-MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330 SB driver.");
+MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330/CMI8329 SB driver.");
module_param_array(sbdma16, int, NULL, 0444);
-MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330 SB driver.");
+MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330/CMI8329 SB driver.");
module_param_array(wssport, long, NULL, 0444);
-MODULE_PARM_DESC(wssport, "Port # for CMI8330 WSS driver.");
+MODULE_PARM_DESC(wssport, "Port # for CMI8330/CMI8329 WSS driver.");
module_param_array(wssirq, int, NULL, 0444);
-MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330 WSS driver.");
+MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330/CMI8329 WSS driver.");
module_param_array(wssdma, int, NULL, 0444);
-MODULE_PARM_DESC(wssdma, "DMA for CMI8330 WSS driver.");
+MODULE_PARM_DESC(wssdma, "DMA for CMI8330/CMI8329 WSS driver.");
module_param_array(fmport, long, NULL, 0444);
-MODULE_PARM_DESC(fmport, "FM port # for CMI8330 driver.");
+MODULE_PARM_DESC(fmport, "FM port # for CMI8330/CMI8329 driver.");
module_param_array(mpuport, long, NULL, 0444);
-MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330 driver.");
+MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330/CMI8329 driver.");
module_param_array(mpuirq, int, NULL, 0444);
-MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330 MPU-401 port.");
+MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330/CMI8329 MPU-401 port.");
#ifdef CONFIG_PNP
static int isa_registered;
static int pnp_registered;
@@ -156,6 +156,11 @@ static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] =
typedef int (*snd_pcm_open_callback_t)(struct snd_pcm_substream *);
+enum card_type {
+ CMI8330,
+ CMI8329
+};
+
struct snd_cmi8330 {
#ifdef CONFIG_PNP
struct pnp_dev *cap;
@@ -172,11 +177,14 @@ struct snd_cmi8330 {
snd_pcm_open_callback_t open;
void *private_data; /* sb or wss */
} streams[2];
+
+ enum card_type type;
};
#ifdef CONFIG_PNP
static struct pnp_card_device_id snd_cmi8330_pnpids[] = {
+ { .id = "CMI0001", .devs = { { "@X@0001" }, { "@@@0001" }, { "@H@0001" }, { "A@@0001" } } },
{ .id = "CMI0001", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } } },
{ .id = "" }
};
@@ -304,7 +312,7 @@ static int __devinit snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330
unsigned int idx;
int err;
- strcpy(card->mixername, "CMI8330/C3D");
+ strcpy(card->mixername, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) {
err = snd_ctl_add(card,
@@ -329,6 +337,9 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
struct pnp_dev *pdev;
int err;
+ /* CMI8329 has a device with ID A@@0001, CMI8330 does not */
+ acard->type = (id->devs[3].id[0]) ? CMI8329 : CMI8330;
+
acard->cap = pnp_request_card_device(card, id->devs[0].id, NULL);
if (acard->cap == NULL)
return -EBUSY;
@@ -345,38 +356,45 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "CMI8330/C3D PnP configure failure\n");
+ snd_printk(KERN_ERR "AD1848 PnP configure failure\n");
return -EBUSY;
}
wssport[dev] = pnp_port_start(pdev, 0);
wssdma[dev] = pnp_dma(pdev, 0);
wssirq[dev] = pnp_irq(pdev, 0);
- fmport[dev] = pnp_port_start(pdev, 1);
+ if (pnp_port_start(pdev, 1))
+ fmport[dev] = pnp_port_start(pdev, 1);
/* allocate SB16 resources */
pdev = acard->play;
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "CMI8330/C3D (SB16) PnP configure failure\n");
+ snd_printk(KERN_ERR "SB16 PnP configure failure\n");
return -EBUSY;
}
sbport[dev] = pnp_port_start(pdev, 0);
sbdma8[dev] = pnp_dma(pdev, 0);
sbdma16[dev] = pnp_dma(pdev, 1);
sbirq[dev] = pnp_irq(pdev, 0);
+ /* On CMI8239, the OPL3 port might be present in SB16 PnP resources */
+ if (fmport[dev] == SNDRV_AUTO_PORT) {
+ if (pnp_port_start(pdev, 1))
+ fmport[dev] = pnp_port_start(pdev, 1);
+ else
+ fmport[dev] = 0x388; /* Or hardwired */
+ }
/* allocate MPU-401 resources */
pdev = acard->mpu;
err = pnp_activate_dev(pdev);
- if (err < 0) {
- snd_printk(KERN_ERR
- "CMI8330/C3D (MPU-401) PnP configure failure\n");
- return -EBUSY;
+ if (err < 0)
+ snd_printk(KERN_ERR "MPU-401 PnP configure failure: will be disabled\n");
+ else {
+ mpuport[dev] = pnp_port_start(pdev, 0);
+ mpuirq[dev] = pnp_irq(pdev, 0);
}
- mpuport[dev] = pnp_port_start(pdev, 0);
- mpuirq[dev] = pnp_irq(pdev, 0);
return 0;
}
#endif
@@ -430,9 +448,9 @@ static int __devinit snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *
snd_cmi8330_capture_open
};
- if ((err = snd_pcm_new(card, "CMI8330", 0, 1, 1, &pcm)) < 0)
+ if ((err = snd_pcm_new(card, (chip->type == CMI8329) ? "CMI8329" : "CMI8330", 0, 1, 1, &pcm)) < 0)
return err;
- strcpy(pcm->name, "CMI8330");
+ strcpy(pcm->name, (chip->type == CMI8329) ? "CMI8329" : "CMI8330");
pcm->private_data = chip;
/* SB16 */
@@ -527,11 +545,11 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
wssdma[dev], -1,
WSS_HW_DETECT, 0, &acard->wss);
if (err < 0) {
- snd_printk(KERN_ERR PFX "(CMI8330) device busy??\n");
+ snd_printk(KERN_ERR PFX "AD1848 device busy??\n");
return err;
}
if (acard->wss->hardware != WSS_HW_CMI8330) {
- snd_printk(KERN_ERR PFX "(CMI8330) not found during probe\n");
+ snd_printk(KERN_ERR PFX "AD1848 not found during probe\n");
return -ENODEV;
}
@@ -541,11 +559,11 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
sbdma8[dev],
sbdma16[dev],
SB_HW_AUTO, &acard->sb)) < 0) {
- snd_printk(KERN_ERR PFX "(SB16) device busy??\n");
+ snd_printk(KERN_ERR PFX "SB16 device busy??\n");
return err;
}
if (acard->sb->hardware != SB_HW_16) {
- snd_printk(KERN_ERR PFX "(SB16) not found during probe\n");
+ snd_printk(KERN_ERR PFX "SB16 not found during probe\n");
return err;
}
@@ -585,8 +603,8 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
mpuport[dev]);
}
- strcpy(card->driver, "CMI8330/C3D");
- strcpy(card->shortname, "C-Media CMI8330/C3D");
+ strcpy(card->driver, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
+ strcpy(card->shortname, (acard->type == CMI8329) ? "C-Media CMI8329" : "C-Media CMI8330/C3D");
sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
card->shortname,
acard->wss->port,
diff --git a/sound/oss/midibuf.c b/sound/oss/midibuf.c
index a40be0cf1d9..782b3b84dac 100644
--- a/sound/oss/midibuf.c
+++ b/sound/oss/midibuf.c
@@ -127,15 +127,16 @@ static void midi_poll(unsigned long dummy)
for (dev = 0; dev < num_midis; dev++)
if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL)
{
- int ok = 1;
-
- while (DATA_AVAIL(midi_out_buf[dev]) && ok)
+ while (DATA_AVAIL(midi_out_buf[dev]))
{
+ int ok;
int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
spin_unlock_irqrestore(&lock,flags);/* Give some time to others */
ok = midi_devs[dev]->outputc(dev, c);
spin_lock_irqsave(&lock, flags);
+ if (!ok)
+ break;
midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
midi_out_buf[dev]->len--;
}
diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c
index 187f72750e8..6713110bdc7 100644
--- a/sound/oss/vwsnd.c
+++ b/sound/oss/vwsnd.c
@@ -628,7 +628,7 @@ static void li_setup_dma(dma_chan_t *chan,
ASSERT(!(buffer_paddr & 0xFF));
chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8);
- chan->cfgval = (!LI_CCFG_LOCK |
+ chan->cfgval = ((chan->cfgval & ~LI_CCFG_LOCK) |
SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) |
desc->direction |
mode |
@@ -638,9 +638,9 @@ static void li_setup_dma(dma_chan_t *chan,
tmask = 13 - fragshift; /* See Lithium DMA Notes above. */
ASSERT(size >= 2 && size <= 7);
ASSERT(tmask >= 1 && tmask <= 7);
- chan->ctlval = (!LI_CCTL_RESET |
+ chan->ctlval = ((chan->ctlval & ~LI_CCTL_RESET) |
SHIFT_FIELD(size, LI_CCTL_SIZE) |
- !LI_CCTL_DMA_ENABLE |
+ (chan->ctlval & ~LI_CCTL_DMA_ENABLE) |
SHIFT_FIELD(tmask, LI_CCTL_TMASK) |
SHIFT_FIELD(0, LI_CCTL_TPTR));
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 748f6b7d90b..fb5ee3cc396 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -135,11 +135,11 @@ config SND_AW2
config SND_AZT3328
- tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+ tristate "Aztech AZF3328 / PCI168"
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
+ select SND_RAWMIDI
help
Say Y here to include support for Aztech AZF3328 (PCI168)
soundcards.
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index c551006e292..b458d208720 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -310,12 +310,16 @@ static int snd_ali_codec_ready(struct snd_ali *codec,
unsigned int res;
end_time = jiffies + msecs_to_jiffies(250);
- do {
+
+ for (;;) {
res = snd_ali_5451_peek(codec,port);
if (!(res & 0x8000))
return 0;
+ if (!time_after_eq(end_time, jiffies))
+ break;
schedule_timeout_uninterruptible(1);
- } while (time_after_eq(end_time, jiffies));
+ }
+
snd_ali_5451_poke(codec, port, res & ~0x8000);
snd_printdd("ali_codec_ready: codec is not ready.\n ");
return -EIO;
@@ -327,15 +331,17 @@ static int snd_ali_stimer_ready(struct snd_ali *codec)
unsigned long dwChk1,dwChk2;
dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER);
- dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER);
-
end_time = jiffies + msecs_to_jiffies(250);
- do {
+
+ for (;;) {
dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER);
if (dwChk2 != dwChk1)
return 0;
+ if (!time_after_eq(end_time, jiffies))
+ break;
schedule_timeout_uninterruptible(1);
- } while (time_after_eq(end_time, jiffies));
+ }
+
snd_printk(KERN_ERR "ali_stimer_read: stimer is not ready.\n");
return -EIO;
}
@@ -472,45 +478,6 @@ static int snd_ali_reset_5451(struct snd_ali *codec)
return 0;
}
-#ifdef CODEC_RESET
-
-static int snd_ali_reset_codec(struct snd_ali *codec)
-{
- struct pci_dev *pci_dev;
- unsigned char bVal;
- unsigned int dwVal;
- unsigned short wCount, wReg;
-
- pci_dev = codec->pci_m1533;
-
- pci_read_config_dword(pci_dev, 0x7c, &dwVal);
- pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
- udelay(5000);
- pci_read_config_dword(pci_dev, 0x7c, &dwVal);
- pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
- udelay(5000);
-
- bVal = inb(ALI_REG(codec,ALI_SCTRL));
- bVal |= 0x02;
- outb(ALI_REG(codec,ALI_SCTRL),bVal);
- udelay(5000);
- bVal = inb(ALI_REG(codec,ALI_SCTRL));
- bVal &= 0xfd;
- outb(ALI_REG(codec,ALI_SCTRL),bVal);
- udelay(15000);
-
- wCount = 200;
- while (wCount--) {
- wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN);
- if ((wReg & 0x000f) == 0x000f)
- return 0;
- udelay(5000);
- }
- return -1;
-}
-
-#endif
-
/*
* ALI 5451 Controller
*/
@@ -555,22 +522,6 @@ static void snd_ali_disable_address_interrupt(struct snd_ali *codec)
outl(gc, ALI_REG(codec, ALI_GC_CIR));
}
-#if 0 /* not used */
-static void snd_ali_enable_voice_irq(struct snd_ali *codec,
- unsigned int channel)
-{
- unsigned int mask;
- struct snd_ali_channel_control *pchregs = &(codec->chregs);
-
- snd_ali_printk("enable_voice_irq channel=%d\n",channel);
-
- mask = 1 << (channel & 0x1f);
- pchregs->data.ainten = inl(ALI_REG(codec, pchregs->regs.ainten));
- pchregs->data.ainten |= mask;
- outl(pchregs->data.ainten, ALI_REG(codec, pchregs->regs.ainten));
-}
-#endif
-
static void snd_ali_disable_voice_irq(struct snd_ali *codec,
unsigned int channel)
{
@@ -671,16 +622,6 @@ static void snd_ali_free_channel_pcm(struct snd_ali *codec, int channel)
}
}
-#if 0 /* not used */
-static void snd_ali_start_voice(struct snd_ali *codec, unsigned int channel)
-{
- unsigned int mask = 1 << (channel & 0x1f);
-
- snd_ali_printk("start_voice: channel=%d\n",channel);
- outl(mask, ALI_REG(codec,codec->chregs.regs.start));
-}
-#endif
-
static void snd_ali_stop_voice(struct snd_ali *codec, unsigned int channel)
{
unsigned int mask = 1 << (channel & 0x1f);
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index f290bc56178..8451a0169f3 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -1,6 +1,6 @@
/*
* azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
- * Copyright (C) 2002, 2005 - 2008 by Andreas Mohr <andi AT lisas.de>
+ * Copyright (C) 2002, 2005 - 2009 by Andreas Mohr <andi AT lisas.de>
*
* Framework borrowed from Bart Hartgers's als4000.c.
* Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
@@ -10,6 +10,13 @@
* PCI168 A/AP, sub ID 8000
* Please give me feedback in case you try my driver with one of these!!
*
+ * Keywords: Windows XP Vista 168nt4-125.zip 168win95-125.zip PCI 168 download
+ * (XP/Vista do not support this card at all but every Linux distribution
+ * has very good support out of the box;
+ * just to make sure that the right people hit this and get to know that,
+ * despite the high level of Internet ignorance - as usual :-P -
+ * about very good support for this card - on Linux!)
+ *
* GPL LICENSE
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -71,10 +78,11 @@
* - built-in General DirectX timer having a 20 bits counter
* with 1us resolution (see below!)
* - I2S serial output port for external DAC
+ * [FIXME: 3.3V or 5V level? maximum rate is 66.2kHz right?]
* - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI
* - supports hardware volume control
* - single chip low cost solution (128 pin QFP)
- * - supports programmable Sub-vendor and Sub-system ID
+ * - supports programmable Sub-vendor and Sub-system ID [24C02 SEEPROM chip]
* required for Microsoft's logo compliance (FIXME: where?)
* At least the Trident 4D Wave DX has one bit somewhere
* to enable writes to PCI subsystem VID registers, that should be it.
@@ -82,6 +90,7 @@
* some custom data starting at 0x80. What kind of config settings
* are located in our extended PCI space anyway??
* - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms
+ * [TDA1517P chip]
*
* Note that this driver now is actually *better* than the Windows driver,
* since it additionally supports the card's 1MHz DirectX timer - just try
@@ -146,10 +155,15 @@
* to read the Digital Enhanced Game Port. Not sure whether it is fixable.
*
* TODO
+ * - use PCI_VDEVICE
+ * - verify driver status on x86_64
+ * - test multi-card driver operation
+ * - (ab)use 1MHz DirectX timer as kernel clocksource
* - test MPU401 MIDI playback etc.
* - add more power micro-management (disable various units of the card
- * as long as they're unused). However this requires more I/O ports which I
- * haven't figured out yet and which thus might not even exist...
+ * as long as they're unused, to improve audio quality and save power).
+ * However this requires more I/O ports which I haven't figured out yet
+ * and which thus might not even exist...
* The standard suspend/resume functionality could probably make use of
* some improvement, too...
* - figure out what all unknown port bits are responsible for
@@ -185,25 +199,46 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
#define SUPPORT_GAMEPORT 1
#endif
+/* === Debug settings ===
+ Further diagnostic functionality than the settings below
+ does not need to be provided, since one can easily write a bash script
+ to dump the card's I/O ports (those listed in lspci -v -v):
+ function dump()
+ {
+ local descr=$1; local addr=$2; local count=$3
+
+ echo "${descr}: ${count} @ ${addr}:"
+ dd if=/dev/port skip=$[${addr}] count=${count} bs=1 2>/dev/null| hexdump -C
+ }
+ and then use something like
+ "dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
+ "dump codec00 0xa800 32", "dump mixer 0xb800 64", "dump synth 0xbc00 8",
+ possibly within a "while true; do ... sleep 1; done" loop.
+ Tweaking ports could be done using
+ VALSTRING="`printf "%02x" $value`"
+ printf "\x""$VALSTRING"|dd of=/dev/port seek=$[${addr}] bs=1 2>/dev/null
+*/
+
#define DEBUG_MISC 0
#define DEBUG_CALLS 0
#define DEBUG_MIXER 0
-#define DEBUG_PLAY_REC 0
+#define DEBUG_CODEC 0
#define DEBUG_IO 0
#define DEBUG_TIMER 0
#define DEBUG_GAME 0
+#define DEBUG_PM 0
#define MIXER_TESTING 0
#if DEBUG_MISC
-#define snd_azf3328_dbgmisc(format, args...) printk(KERN_ERR format, ##args)
+#define snd_azf3328_dbgmisc(format, args...) printk(KERN_DEBUG format, ##args)
#else
#define snd_azf3328_dbgmisc(format, args...)
#endif
#if DEBUG_CALLS
#define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
-#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__)
-#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__)
+#define snd_azf3328_dbgcallenter() printk(KERN_DEBUG "--> %s\n", __func__)
+#define snd_azf3328_dbgcallleave() printk(KERN_DEBUG "<-- %s\n", __func__)
#else
#define snd_azf3328_dbgcalls(format, args...)
#define snd_azf3328_dbgcallenter()
@@ -216,10 +251,10 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
#define snd_azf3328_dbgmixer(format, args...)
#endif
-#if DEBUG_PLAY_REC
-#define snd_azf3328_dbgplay(format, args...) printk(KERN_DEBUG format, ##args)
+#if DEBUG_CODEC
+#define snd_azf3328_dbgcodec(format, args...) printk(KERN_DEBUG format, ##args)
#else
-#define snd_azf3328_dbgplay(format, args...)
+#define snd_azf3328_dbgcodec(format, args...)
#endif
#if DEBUG_MISC
@@ -234,6 +269,12 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
#define snd_azf3328_dbggame(format, args...)
#endif
+#if DEBUG_PM
+#define snd_azf3328_dbgpm(format, args...) printk(KERN_DEBUG format, ##args)
+#else
+#define snd_azf3328_dbgpm(format, args...)
+#endif
+
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");
@@ -250,22 +291,23 @@ static int seqtimer_scaling = 128;
module_param(seqtimer_scaling, int, 0444);
MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
-struct snd_azf3328_audio_stream {
+struct snd_azf3328_codec_data {
+ unsigned long io_base;
struct snd_pcm_substream *substream;
- int enabled;
- int running;
- unsigned long portbase;
+ bool running;
+ const char *name;
};
-enum snd_azf3328_stream_index {
- AZF_PLAYBACK = 0,
- AZF_CAPTURE = 1,
+enum snd_azf3328_codec_type {
+ AZF_CODEC_PLAYBACK = 0,
+ AZF_CODEC_CAPTURE = 1,
+ AZF_CODEC_I2S_OUT = 2,
};
struct snd_azf3328 {
/* often-used fields towards beginning, then grouped */
- unsigned long codec_io; /* usually 0xb000, size 128 */
+ unsigned long ctrl_io; /* usually 0xb000, size 128 */
unsigned long game_io; /* usually 0xb400, size 8 */
unsigned long mpu_io; /* usually 0xb800, size 4 */
unsigned long opl3_io; /* usually 0xbc00, size 8 */
@@ -275,15 +317,17 @@ struct snd_azf3328 {
struct snd_timer *timer;
- struct snd_pcm *pcm;
- struct snd_azf3328_audio_stream audio_stream[2];
+ struct snd_pcm *pcm[3];
+
+ /* playback, recording and I2S out codecs */
+ struct snd_azf3328_codec_data codecs[3];
struct snd_card *card;
struct snd_rawmidi *rmidi;
#ifdef SUPPORT_GAMEPORT
struct gameport *gameport;
- int axes[4];
+ u16 axes[4];
#endif
struct pci_dev *pci;
@@ -293,16 +337,16 @@ struct snd_azf3328 {
* If we need to add more registers here, then we might try to fold this
* into some transparent combined shadow register handling with
* CONFIG_PM register storage below, but that's slightly difficult. */
- u16 shadow_reg_codec_6AH;
+ u16 shadow_reg_ctrl_6AH;
#ifdef CONFIG_PM
/* register value containers for power management
- * Note: not always full I/O range preserved (just like Win driver!) */
- u16 saved_regs_codec[AZF_IO_SIZE_CODEC_PM / 2];
- u16 saved_regs_game [AZF_IO_SIZE_GAME_PM / 2];
- u16 saved_regs_mpu [AZF_IO_SIZE_MPU_PM / 2];
- u16 saved_regs_opl3 [AZF_IO_SIZE_OPL3_PM / 2];
- u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2];
+ * Note: not always full I/O range preserved (similar to Win driver!) */
+ u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4];
+ u32 saved_regs_game[AZF_ALIGN(AZF_IO_SIZE_GAME_PM) / 4];
+ u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4];
+ u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4];
+ u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4];
#endif
};
@@ -316,7 +360,7 @@ MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
static int
-snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set)
+snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
{
u8 prev = inb(reg), new;
@@ -331,39 +375,72 @@ snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set)
}
static inline void
-snd_azf3328_codec_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
+snd_azf3328_codec_outb(const struct snd_azf3328_codec_data *codec,
+ unsigned reg,
+ u8 value
+)
{
- outb(value, chip->codec_io + reg);
+ outb(value, codec->io_base + reg);
}
static inline u8
-snd_azf3328_codec_inb(const struct snd_azf3328 *chip, unsigned reg)
+snd_azf3328_codec_inb(const struct snd_azf3328_codec_data *codec, unsigned reg)
{
- return inb(chip->codec_io + reg);
+ return inb(codec->io_base + reg);
}
static inline void
-snd_azf3328_codec_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
+snd_azf3328_codec_outw(const struct snd_azf3328_codec_data *codec,
+ unsigned reg,
+ u16 value
+)
{
- outw(value, chip->codec_io + reg);
+ outw(value, codec->io_base + reg);
}
static inline u16
-snd_azf3328_codec_inw(const struct snd_azf3328 *chip, unsigned reg)
+snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
{
- return inw(chip->codec_io + reg);
+ return inw(codec->io_base + reg);
}
static inline void
-snd_azf3328_codec_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
+snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
+ unsigned reg,
+ u32 value
+)
{
- outl(value, chip->codec_io + reg);
+ outl(value, codec->io_base + reg);
}
static inline u32
-snd_azf3328_codec_inl(const struct snd_azf3328 *chip, unsigned reg)
+snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg)
+{
+ return inl(codec->io_base + reg);
+}
+
+static inline void
+snd_azf3328_ctrl_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
+{
+ outb(value, chip->ctrl_io + reg);
+}
+
+static inline u8
+snd_azf3328_ctrl_inb(const struct snd_azf3328 *chip, unsigned reg)
+{
+ return inb(chip->ctrl_io + reg);
+}
+
+static inline void
+snd_azf3328_ctrl_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
+{
+ outw(value, chip->ctrl_io + reg);
+}
+
+static inline void
+snd_azf3328_ctrl_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
{
- return inl(chip->codec_io + reg);
+ outl(value, chip->ctrl_io + reg);
}
static inline void
@@ -404,13 +481,13 @@ snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
#define AZF_MUTE_BIT 0x80
-static int
+static bool
snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
- unsigned reg, int do_mute
+ unsigned reg, bool do_mute
)
{
unsigned long portbase = chip->mixer_io + reg + 1;
- int updated;
+ bool updated;
/* the mute bit is on the *second* (i.e. right) register of a
* left/right channel setting */
@@ -569,7 +646,7 @@ snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
{
struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
struct azf3328_mixer_reg reg;
- unsigned int oreg, val;
+ u16 oreg, val;
snd_azf3328_dbgcallenter();
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
@@ -600,7 +677,7 @@ snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
{
struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
struct azf3328_mixer_reg reg;
- unsigned int oreg, nreg, val;
+ u16 oreg, nreg, val;
snd_azf3328_dbgcallenter();
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
@@ -709,7 +786,7 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
{
struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
struct azf3328_mixer_reg reg;
- unsigned int oreg, nreg, val;
+ u16 oreg, nreg, val;
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
oreg = snd_azf3328_mixer_inw(chip, reg.reg);
@@ -867,14 +944,15 @@ snd_azf3328_hw_free(struct snd_pcm_substream *substream)
static void
snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
- unsigned reg,
+ enum snd_azf3328_codec_type codec_type,
enum azf_freq_t bitrate,
unsigned int format_width,
unsigned int channels
)
{
- u16 val = 0xff00;
unsigned long flags;
+ const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
+ u16 val = 0xff00;
snd_azf3328_dbgcallenter();
switch (bitrate) {
@@ -917,7 +995,7 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
spin_lock_irqsave(&chip->reg_lock, flags);
/* set bitrate/format */
- snd_azf3328_codec_outw(chip, reg, val);
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
/* changing the bitrate/format settings switches off the
* audio output with an annoying click in case of 8/16bit format change
@@ -926,11 +1004,11 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
* (FIXME: yes, it works, but what exactly am I doing here?? :)
* FIXME: does this have some side effects for full-duplex
* or other dramatic side effects? */
- if (reg == IDX_IO_PLAY_SOUNDFORMAT) /* only do it for playback */
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
- snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) |
- DMA_PLAY_SOMETHING1 |
- DMA_PLAY_SOMETHING2 |
+ if (codec_type == AZF_CODEC_PLAYBACK) /* only do it for playback */
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
+ DMA_RUN_SOMETHING1 |
+ DMA_RUN_SOMETHING2 |
SOMETHING_ALMOST_ALWAYS_SET |
DMA_EPILOGUE_SOMETHING |
DMA_SOMETHING_ELSE
@@ -942,112 +1020,134 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
static inline void
snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip,
- unsigned reg
+ enum snd_azf3328_codec_type codec_type
)
{
/* choose lowest frequency for low power consumption.
* While this will cause louder noise due to rather coarse frequency,
* it should never matter since output should always
* get disabled properly when idle anyway. */
- snd_azf3328_codec_setfmt(chip, reg, AZF_FREQ_4000, 8, 1);
+ snd_azf3328_codec_setfmt(chip, codec_type, AZF_FREQ_4000, 8, 1);
}
static void
-snd_azf3328_codec_reg_6AH_update(struct snd_azf3328 *chip,
+snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip,
unsigned bitmask,
- int enable
+ bool enable
)
{
- if (enable)
- chip->shadow_reg_codec_6AH &= ~bitmask;
+ bool do_mask = !enable;
+ if (do_mask)
+ chip->shadow_reg_ctrl_6AH |= bitmask;
else
- chip->shadow_reg_codec_6AH |= bitmask;
- snd_azf3328_dbgplay("6AH_update mask 0x%04x enable %d: val 0x%04x\n",
- bitmask, enable, chip->shadow_reg_codec_6AH);
- snd_azf3328_codec_outw(chip, IDX_IO_6AH, chip->shadow_reg_codec_6AH);
+ chip->shadow_reg_ctrl_6AH &= ~bitmask;
+ snd_azf3328_dbgcodec("6AH_update mask 0x%04x do_mask %d: val 0x%04x\n",
+ bitmask, do_mask, chip->shadow_reg_ctrl_6AH);
+ snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH);
}
static inline void
-snd_azf3328_codec_enable(struct snd_azf3328 *chip, int enable)
+snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable)
{
- snd_azf3328_dbgplay("codec_enable %d\n", enable);
+ snd_azf3328_dbgcodec("codec_enable %d\n", enable);
/* no idea what exactly is being done here, but I strongly assume it's
* PM related */
- snd_azf3328_codec_reg_6AH_update(
+ snd_azf3328_ctrl_reg_6AH_update(
chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
);
}
static void
-snd_azf3328_codec_activity(struct snd_azf3328 *chip,
- enum snd_azf3328_stream_index stream_type,
- int enable
+snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
+ enum snd_azf3328_codec_type codec_type,
+ bool enable
)
{
- int need_change = (chip->audio_stream[stream_type].running != enable);
+ struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
+ bool need_change = (codec->running != enable);
- snd_azf3328_dbgplay(
- "codec_activity: type %d, enable %d, need_change %d\n",
- stream_type, enable, need_change
+ snd_azf3328_dbgcodec(
+ "codec_activity: %s codec, enable %d, need_change %d\n",
+ codec->name, enable, need_change
);
if (need_change) {
- enum snd_azf3328_stream_index other =
- (stream_type == AZF_PLAYBACK) ?
- AZF_CAPTURE : AZF_PLAYBACK;
- /* small check to prevent shutting down the other party
- * in case it's active */
- if ((enable) || !(chip->audio_stream[other].running))
- snd_azf3328_codec_enable(chip, enable);
+ static const struct {
+ enum snd_azf3328_codec_type other1;
+ enum snd_azf3328_codec_type other2;
+ } peer_codecs[3] =
+ { { AZF_CODEC_CAPTURE, AZF_CODEC_I2S_OUT },
+ { AZF_CODEC_PLAYBACK, AZF_CODEC_I2S_OUT },
+ { AZF_CODEC_PLAYBACK, AZF_CODEC_CAPTURE } };
+ bool call_function;
+
+ if (enable)
+ /* if enable codec, call enable_codecs func
+ to enable codec supply... */
+ call_function = 1;
+ else {
+ /* ...otherwise call enable_codecs func
+ (which globally shuts down operation of codecs)
+ only in case the other codecs are currently
+ not active either! */
+ call_function =
+ ((!chip->codecs[peer_codecs[codec_type].other1]
+ .running)
+ && (!chip->codecs[peer_codecs[codec_type].other2]
+ .running));
+ }
+ if (call_function)
+ snd_azf3328_ctrl_enable_codecs(chip, enable);
/* ...and adjust clock, too
* (reduce noise and power consumption) */
if (!enable)
snd_azf3328_codec_setfmt_lowpower(
chip,
- chip->audio_stream[stream_type].portbase
- + IDX_IO_PLAY_SOUNDFORMAT
+ codec_type
);
+ codec->running = enable;
}
- chip->audio_stream[stream_type].running = enable;
}
static void
-snd_azf3328_setdmaa(struct snd_azf3328 *chip,
- long unsigned int addr,
- unsigned int count,
- unsigned int size,
- enum snd_azf3328_stream_index stream_type
+snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
+ enum snd_azf3328_codec_type codec_type,
+ unsigned long addr,
+ unsigned int count,
+ unsigned int size
)
{
+ const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
snd_azf3328_dbgcallenter();
- if (!chip->audio_stream[stream_type].running) {
- /* AZF3328 uses a two buffer pointer DMA playback approach */
+ if (!codec->running) {
+ /* AZF3328 uses a two buffer pointer DMA transfer approach */
- unsigned long flags, portbase, addr_area2;
+ unsigned long flags, addr_area2;
/* width 32bit (prevent overflow): */
- unsigned long count_areas, count_tmp;
+ u32 count_areas, lengths;
- portbase = chip->audio_stream[stream_type].portbase;
count_areas = size/2;
addr_area2 = addr+count_areas;
count_areas--; /* max. index */
- snd_azf3328_dbgplay("set DMA: buf1 %08lx[%lu], buf2 %08lx[%lu]\n", addr, count_areas, addr_area2, count_areas);
+ snd_azf3328_dbgcodec("setdma: buffers %08lx[%u] / %08lx[%u]\n",
+ addr, count_areas, addr_area2, count_areas);
/* build combined I/O buffer length word */
- count_tmp = count_areas;
- count_areas |= (count_tmp << 16);
+ lengths = (count_areas << 16) | (count_areas);
spin_lock_irqsave(&chip->reg_lock, flags);
- outl(addr, portbase + IDX_IO_PLAY_DMA_START_1);
- outl(addr_area2, portbase + IDX_IO_PLAY_DMA_START_2);
- outl(count_areas, portbase + IDX_IO_PLAY_DMA_LEN_1);
+ snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr);
+ snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2,
+ addr_area2);
+ snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS,
+ lengths);
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
snd_azf3328_dbgcallleave();
}
static int
-snd_azf3328_playback_prepare(struct snd_pcm_substream *substream)
+snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
{
#if 0
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
@@ -1058,157 +1158,161 @@ snd_azf3328_playback_prepare(struct snd_pcm_substream *substream)
snd_azf3328_dbgcallenter();
#if 0
- snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
+ snd_azf3328_codec_setfmt(chip, AZF_CODEC_...,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
- snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_PLAYBACK);
+ snd_azf3328_codec_setdmaa(chip, AZF_CODEC_...,
+ runtime->dma_addr, count, size);
#endif
snd_azf3328_dbgcallleave();
return 0;
}
static int
-snd_azf3328_capture_prepare(struct snd_pcm_substream *substream)
-{
-#if 0
- struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int size = snd_pcm_lib_buffer_bytes(substream);
- unsigned int count = snd_pcm_lib_period_bytes(substream);
-#endif
-
- snd_azf3328_dbgcallenter();
-#if 0
- snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
- runtime->rate,
- snd_pcm_format_width(runtime->format),
- runtime->channels);
- snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_CAPTURE);
-#endif
- snd_azf3328_dbgcallleave();
- return 0;
-}
-
-static int
-snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
+ struct snd_pcm_substream *substream, int cmd)
{
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
+ const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
struct snd_pcm_runtime *runtime = substream->runtime;
int result = 0;
- unsigned int status1;
- int previously_muted;
+ u16 flags1;
+ bool previously_muted = 0;
+ bool is_playback_codec = (AZF_CODEC_PLAYBACK == codec_type);
- snd_azf3328_dbgcalls("snd_azf3328_playback_trigger cmd %d\n", cmd);
+ snd_azf3328_dbgcalls("snd_azf3328_codec_trigger cmd %d\n", cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- snd_azf3328_dbgplay("START PLAYBACK\n");
-
- /* mute WaveOut (avoid clicking during setup) */
- previously_muted =
- snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+ snd_azf3328_dbgcodec("START %s\n", codec->name);
+
+ if (is_playback_codec) {
+ /* mute WaveOut (avoid clicking during setup) */
+ previously_muted =
+ snd_azf3328_mixer_set_mute(
+ chip, IDX_MIXER_WAVEOUT, 1
+ );
+ }
- snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
+ snd_azf3328_codec_setfmt(chip, codec_type,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
spin_lock(&chip->reg_lock);
/* first, remember current value: */
- status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS);
+ flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
- /* stop playback */
- status1 &= ~DMA_RESUME;
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
+ /* stop transfer */
+ flags1 &= ~DMA_RESUME;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
/* FIXME: clear interrupts or what??? */
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_IRQTYPE, 0xffff);
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
spin_unlock(&chip->reg_lock);
- snd_azf3328_setdmaa(chip, runtime->dma_addr,
+ snd_azf3328_codec_setdmaa(chip, codec_type, runtime->dma_addr,
snd_pcm_lib_period_bytes(substream),
- snd_pcm_lib_buffer_bytes(substream),
- AZF_PLAYBACK);
+ snd_pcm_lib_buffer_bytes(substream)
+ );
spin_lock(&chip->reg_lock);
#ifdef WIN9X
/* FIXME: enable playback/recording??? */
- status1 |= DMA_PLAY_SOMETHING1 | DMA_PLAY_SOMETHING2;
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
+ flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
- /* start playback again */
+ /* start transfer again */
/* FIXME: what is this value (0x0010)??? */
- status1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
+ flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
#else /* NT4 */
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
0x0000);
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
- DMA_PLAY_SOMETHING1);
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
- DMA_PLAY_SOMETHING1 |
- DMA_PLAY_SOMETHING2);
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ DMA_RUN_SOMETHING1);
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ DMA_RUN_SOMETHING1 |
+ DMA_RUN_SOMETHING2);
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
DMA_RESUME |
SOMETHING_ALMOST_ALWAYS_SET |
DMA_EPILOGUE_SOMETHING |
DMA_SOMETHING_ELSE);
#endif
spin_unlock(&chip->reg_lock);
- snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 1);
-
- /* now unmute WaveOut */
- if (!previously_muted)
- snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
+ snd_azf3328_ctrl_codec_activity(chip, codec_type, 1);
+
+ if (is_playback_codec) {
+ /* now unmute WaveOut */
+ if (!previously_muted)
+ snd_azf3328_mixer_set_mute(
+ chip, IDX_MIXER_WAVEOUT, 0
+ );
+ }
- snd_azf3328_dbgplay("STARTED PLAYBACK\n");
+ snd_azf3328_dbgcodec("STARTED %s\n", codec->name);
break;
case SNDRV_PCM_TRIGGER_RESUME:
- snd_azf3328_dbgplay("RESUME PLAYBACK\n");
- /* resume playback if we were active */
+ snd_azf3328_dbgcodec("RESUME %s\n", codec->name);
+ /* resume codec if we were active */
spin_lock(&chip->reg_lock);
- if (chip->audio_stream[AZF_PLAYBACK].running)
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
- snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | DMA_RESUME);
+ if (codec->running)
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ snd_azf3328_codec_inw(
+ codec, IDX_IO_CODEC_DMA_FLAGS
+ ) | DMA_RESUME
+ );
spin_unlock(&chip->reg_lock);
break;
case SNDRV_PCM_TRIGGER_STOP:
- snd_azf3328_dbgplay("STOP PLAYBACK\n");
-
- /* mute WaveOut (avoid clicking during setup) */
- previously_muted =
- snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+ snd_azf3328_dbgcodec("STOP %s\n", codec->name);
+
+ if (is_playback_codec) {
+ /* mute WaveOut (avoid clicking during setup) */
+ previously_muted =
+ snd_azf3328_mixer_set_mute(
+ chip, IDX_MIXER_WAVEOUT, 1
+ );
+ }
spin_lock(&chip->reg_lock);
/* first, remember current value: */
- status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS);
+ flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
- /* stop playback */
- status1 &= ~DMA_RESUME;
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
+ /* stop transfer */
+ flags1 &= ~DMA_RESUME;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
/* hmm, is this really required? we're resetting the same bit
* immediately thereafter... */
- status1 |= DMA_PLAY_SOMETHING1;
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
+ flags1 |= DMA_RUN_SOMETHING1;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
- status1 &= ~DMA_PLAY_SOMETHING1;
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
+ flags1 &= ~DMA_RUN_SOMETHING1;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
spin_unlock(&chip->reg_lock);
- snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0);
-
- /* now unmute WaveOut */
- if (!previously_muted)
- snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
+ snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
+
+ if (is_playback_codec) {
+ /* now unmute WaveOut */
+ if (!previously_muted)
+ snd_azf3328_mixer_set_mute(
+ chip, IDX_MIXER_WAVEOUT, 0
+ );
+ }
- snd_azf3328_dbgplay("STOPPED PLAYBACK\n");
+ snd_azf3328_dbgcodec("STOPPED %s\n", codec->name);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
- snd_azf3328_dbgplay("SUSPEND PLAYBACK\n");
- /* make sure playback is stopped */
- snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
- snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) & ~DMA_RESUME);
+ snd_azf3328_dbgcodec("SUSPEND %s\n", codec->name);
+ /* make sure codec is stopped */
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ snd_azf3328_codec_inw(
+ codec, IDX_IO_CODEC_DMA_FLAGS
+ ) & ~DMA_RESUME
+ );
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
@@ -1217,7 +1321,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
break;
default:
- printk(KERN_ERR "FIXME: unknown trigger mode!\n");
+ snd_printk(KERN_ERR "FIXME: unknown trigger mode!\n");
return -EINVAL;
}
@@ -1225,172 +1329,74 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
return result;
}
-/* this is just analogous to playback; I'm not quite sure whether recording
- * should actually be triggered like that */
static int
-snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+snd_azf3328_codec_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
- struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int result = 0;
- unsigned int status1;
-
- snd_azf3328_dbgcalls("snd_azf3328_capture_trigger cmd %d\n", cmd);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
-
- snd_azf3328_dbgplay("START CAPTURE\n");
-
- snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
- runtime->rate,
- snd_pcm_format_width(runtime->format),
- runtime->channels);
-
- spin_lock(&chip->reg_lock);
- /* first, remember current value: */
- status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS);
-
- /* stop recording */
- status1 &= ~DMA_RESUME;
- snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-
- /* FIXME: clear interrupts or what??? */
- snd_azf3328_codec_outw(chip, IDX_IO_REC_IRQTYPE, 0xffff);
- spin_unlock(&chip->reg_lock);
-
- snd_azf3328_setdmaa(chip, runtime->dma_addr,
- snd_pcm_lib_period_bytes(substream),
- snd_pcm_lib_buffer_bytes(substream),
- AZF_CAPTURE);
-
- spin_lock(&chip->reg_lock);
-#ifdef WIN9X
- /* FIXME: enable playback/recording??? */
- status1 |= DMA_PLAY_SOMETHING1 | DMA_PLAY_SOMETHING2;
- snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-
- /* start capture again */
- /* FIXME: what is this value (0x0010)??? */
- status1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
- snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-#else
- snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
- 0x0000);
- snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
- DMA_PLAY_SOMETHING1);
- snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
- DMA_PLAY_SOMETHING1 |
- DMA_PLAY_SOMETHING2);
- snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
- DMA_RESUME |
- SOMETHING_ALMOST_ALWAYS_SET |
- DMA_EPILOGUE_SOMETHING |
- DMA_SOMETHING_ELSE);
-#endif
- spin_unlock(&chip->reg_lock);
- snd_azf3328_codec_activity(chip, AZF_CAPTURE, 1);
-
- snd_azf3328_dbgplay("STARTED CAPTURE\n");
- break;
- case SNDRV_PCM_TRIGGER_RESUME:
- snd_azf3328_dbgplay("RESUME CAPTURE\n");
- /* resume recording if we were active */
- spin_lock(&chip->reg_lock);
- if (chip->audio_stream[AZF_CAPTURE].running)
- snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
- snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) | DMA_RESUME);
- spin_unlock(&chip->reg_lock);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- snd_azf3328_dbgplay("STOP CAPTURE\n");
-
- spin_lock(&chip->reg_lock);
- /* first, remember current value: */
- status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS);
-
- /* stop recording */
- status1 &= ~DMA_RESUME;
- snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-
- status1 |= DMA_PLAY_SOMETHING1;
- snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-
- status1 &= ~DMA_PLAY_SOMETHING1;
- snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
- spin_unlock(&chip->reg_lock);
- snd_azf3328_codec_activity(chip, AZF_CAPTURE, 0);
+ return snd_azf3328_codec_trigger(AZF_CODEC_PLAYBACK, substream, cmd);
+}
- snd_azf3328_dbgplay("STOPPED CAPTURE\n");
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- snd_azf3328_dbgplay("SUSPEND CAPTURE\n");
- /* make sure recording is stopped */
- snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
- snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) & ~DMA_RESUME);
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
- break;
- default:
- printk(KERN_ERR "FIXME: unknown trigger mode!\n");
- return -EINVAL;
- }
+static int
+snd_azf3328_codec_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ return snd_azf3328_codec_trigger(AZF_CODEC_CAPTURE, substream, cmd);
+}
- snd_azf3328_dbgcallleave();
- return result;
+static int
+snd_azf3328_codec_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ return snd_azf3328_codec_trigger(AZF_CODEC_I2S_OUT, substream, cmd);
}
static snd_pcm_uframes_t
-snd_azf3328_playback_pointer(struct snd_pcm_substream *substream)
+snd_azf3328_codec_pointer(struct snd_pcm_substream *substream,
+ enum snd_azf3328_codec_type codec_type
+)
{
- struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
+ const struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
+ const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
unsigned long bufptr, result;
snd_pcm_uframes_t frmres;
#ifdef QUERY_HARDWARE
- bufptr = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_START_1);
+ bufptr = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
#else
bufptr = substream->runtime->dma_addr;
#endif
- result = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_CURRPOS);
+ result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
/* calculate offset */
result -= bufptr;
frmres = bytes_to_frames( substream->runtime, result);
- snd_azf3328_dbgplay("PLAY @ 0x%8lx, frames %8ld\n", result, frmres);
+ snd_azf3328_dbgcodec("%s @ 0x%8lx, frames %8ld\n",
+ codec->name, result, frmres);
return frmres;
}
static snd_pcm_uframes_t
-snd_azf3328_capture_pointer(struct snd_pcm_substream *substream)
+snd_azf3328_codec_playback_pointer(struct snd_pcm_substream *substream)
{
- struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
- unsigned long bufptr, result;
- snd_pcm_uframes_t frmres;
+ return snd_azf3328_codec_pointer(substream, AZF_CODEC_PLAYBACK);
+}
-#ifdef QUERY_HARDWARE
- bufptr = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_START_1);
-#else
- bufptr = substream->runtime->dma_addr;
-#endif
- result = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_CURRPOS);
+static snd_pcm_uframes_t
+snd_azf3328_codec_capture_pointer(struct snd_pcm_substream *substream)
+{
+ return snd_azf3328_codec_pointer(substream, AZF_CODEC_CAPTURE);
+}
- /* calculate offset */
- result -= bufptr;
- frmres = bytes_to_frames( substream->runtime, result);
- snd_azf3328_dbgplay("REC @ 0x%8lx, frames %8ld\n", result, frmres);
- return frmres;
+static snd_pcm_uframes_t
+snd_azf3328_codec_i2s_out_pointer(struct snd_pcm_substream *substream)
+{
+ return snd_azf3328_codec_pointer(substream, AZF_CODEC_I2S_OUT);
}
/******************************************************************/
#ifdef SUPPORT_GAMEPORT
static inline void
-snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, int enable)
+snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip,
+ bool enable
+)
{
snd_azf3328_io_reg_setb(
chip->game_io+IDX_GAME_HWCONFIG,
@@ -1400,7 +1406,9 @@ snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, int enable)
}
static inline void
-snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, int enable)
+snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip,
+ bool enable
+)
{
snd_azf3328_io_reg_setb(
chip->game_io+IDX_GAME_HWCONFIG,
@@ -1409,10 +1417,27 @@ snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, int enable)
);
}
+static void
+snd_azf3328_gameport_set_counter_frequency(struct snd_azf3328 *chip,
+ unsigned int freq_cfg
+)
+{
+ snd_azf3328_io_reg_setb(
+ chip->game_io+IDX_GAME_HWCONFIG,
+ 0x02,
+ (freq_cfg & 1) != 0
+ );
+ snd_azf3328_io_reg_setb(
+ chip->game_io+IDX_GAME_HWCONFIG,
+ 0x04,
+ (freq_cfg & 2) != 0
+ );
+}
+
static inline void
-snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, int enable)
+snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, bool enable)
{
- snd_azf3328_codec_reg_6AH_update(
+ snd_azf3328_ctrl_reg_6AH_update(
chip, IO_6A_SOMETHING2_GAMEPORT, enable
);
}
@@ -1447,6 +1472,8 @@ snd_azf3328_gameport_open(struct gameport *gameport, int mode)
break;
}
+ snd_azf3328_gameport_set_counter_frequency(chip,
+ GAME_HWCFG_ADC_COUNTER_FREQ_STD);
snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
return res;
@@ -1458,6 +1485,8 @@ snd_azf3328_gameport_close(struct gameport *gameport)
struct snd_azf3328 *chip = gameport_get_port_data(gameport);
snd_azf3328_dbggame("gameport_close\n");
+ snd_azf3328_gameport_set_counter_frequency(chip,
+ GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
snd_azf3328_gameport_axis_circuit_enable(chip, 0);
}
@@ -1491,7 +1520,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
if (val & GAME_AXES_SAMPLING_READY) {
- for (i = 0; i < 4; ++i) {
+ for (i = 0; i < ARRAY_SIZE(chip->axes); ++i) {
/* configure the axis to read */
val = (i << 4) | 0x0f;
snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
@@ -1514,7 +1543,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
spin_unlock_irqrestore(&chip->reg_lock, flags);
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < ARRAY_SIZE(chip->axes); i++) {
axes[i] = chip->axes[i];
if (axes[i] == 0xffff)
axes[i] = -1;
@@ -1552,6 +1581,8 @@ snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
/* DISABLE legacy address: we don't need it! */
snd_azf3328_gameport_legacy_address_enable(chip, 0);
+ snd_azf3328_gameport_set_counter_frequency(chip,
+ GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
snd_azf3328_gameport_axis_circuit_enable(chip, 0);
gameport_register_port(chip->gameport);
@@ -1585,40 +1616,77 @@ snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
static inline void
snd_azf3328_irq_log_unknown_type(u8 which)
{
- snd_azf3328_dbgplay(
+ snd_azf3328_dbgcodec(
"azt3328: unknown IRQ type (%x) occurred, please report!\n",
which
);
}
+static inline void
+snd_azf3328_codec_interrupt(struct snd_azf3328 *chip, u8 status)
+{
+ u8 which;
+ enum snd_azf3328_codec_type codec_type;
+ const struct snd_azf3328_codec_data *codec;
+
+ for (codec_type = AZF_CODEC_PLAYBACK;
+ codec_type <= AZF_CODEC_I2S_OUT;
+ ++codec_type) {
+
+ /* skip codec if there's no interrupt for it */
+ if (!(status & (1 << codec_type)))
+ continue;
+
+ codec = &chip->codecs[codec_type];
+
+ spin_lock(&chip->reg_lock);
+ which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
+ /* ack all IRQ types immediately */
+ snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
+ spin_unlock(&chip->reg_lock);
+
+ if ((chip->pcm[codec_type]) && (codec->substream)) {
+ snd_pcm_period_elapsed(codec->substream);
+ snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n",
+ codec->name,
+ which,
+ snd_azf3328_codec_inl(
+ codec, IDX_IO_CODEC_DMA_CURRPOS
+ )
+ );
+ } else
+ printk(KERN_WARNING "azt3328: irq handler problem!\n");
+ if (which & IRQ_SOMETHING)
+ snd_azf3328_irq_log_unknown_type(which);
+ }
+}
+
static irqreturn_t
snd_azf3328_interrupt(int irq, void *dev_id)
{
struct snd_azf3328 *chip = dev_id;
- u8 status, which;
-#if DEBUG_PLAY_REC
+ u8 status;
+#if DEBUG_CODEC
static unsigned long irq_count;
#endif
- status = snd_azf3328_codec_inb(chip, IDX_IO_IRQSTATUS);
+ status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS);
/* fast path out, to ease interrupt sharing */
if (!(status &
- (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
+ (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT
+ |IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
))
return IRQ_NONE; /* must be interrupt for another device */
- snd_azf3328_dbgplay(
- "irq_count %ld! IDX_IO_PLAY_FLAGS %04x, "
- "IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n",
+ snd_azf3328_dbgcodec(
+ "irq_count %ld! IDX_IO_IRQSTATUS %04x\n",
irq_count++ /* debug-only */,
- snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS),
- snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE),
status
);
if (status & IRQ_TIMER) {
- /* snd_azf3328_dbgplay("timer %ld\n",
+ /* snd_azf3328_dbgcodec("timer %ld\n",
snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
& TIMER_VALUE_MASK
); */
@@ -1626,71 +1694,36 @@ snd_azf3328_interrupt(int irq, void *dev_id)
snd_timer_interrupt(chip->timer, chip->timer->sticks);
/* ACK timer */
spin_lock(&chip->reg_lock);
- snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
+ snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
spin_unlock(&chip->reg_lock);
- snd_azf3328_dbgplay("azt3328: timer IRQ\n");
+ snd_azf3328_dbgcodec("azt3328: timer IRQ\n");
}
- if (status & IRQ_PLAYBACK) {
- spin_lock(&chip->reg_lock);
- which = snd_azf3328_codec_inb(chip, IDX_IO_PLAY_IRQTYPE);
- /* ack all IRQ types immediately */
- snd_azf3328_codec_outb(chip, IDX_IO_PLAY_IRQTYPE, which);
- spin_unlock(&chip->reg_lock);
- if (chip->pcm && chip->audio_stream[AZF_PLAYBACK].substream) {
- snd_pcm_period_elapsed(
- chip->audio_stream[AZF_PLAYBACK].substream
- );
- snd_azf3328_dbgplay("PLAY period done (#%x), @ %x\n",
- which,
- snd_azf3328_codec_inl(
- chip, IDX_IO_PLAY_DMA_CURRPOS
- )
- );
- } else
- printk(KERN_WARNING "azt3328: irq handler problem!\n");
- if (which & IRQ_PLAY_SOMETHING)
- snd_azf3328_irq_log_unknown_type(which);
- }
- if (status & IRQ_RECORDING) {
- spin_lock(&chip->reg_lock);
- which = snd_azf3328_codec_inb(chip, IDX_IO_REC_IRQTYPE);
- /* ack all IRQ types immediately */
- snd_azf3328_codec_outb(chip, IDX_IO_REC_IRQTYPE, which);
- spin_unlock(&chip->reg_lock);
+ if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
+ snd_azf3328_codec_interrupt(chip, status);
- if (chip->pcm && chip->audio_stream[AZF_CAPTURE].substream) {
- snd_pcm_period_elapsed(
- chip->audio_stream[AZF_CAPTURE].substream
- );
- snd_azf3328_dbgplay("REC period done (#%x), @ %x\n",
- which,
- snd_azf3328_codec_inl(
- chip, IDX_IO_REC_DMA_CURRPOS
- )
- );
- } else
- printk(KERN_WARNING "azt3328: irq handler problem!\n");
- if (which & IRQ_REC_SOMETHING)
- snd_azf3328_irq_log_unknown_type(which);
- }
if (status & IRQ_GAMEPORT)
snd_azf3328_gameport_interrupt(chip);
+
/* MPU401 has less critical IRQ requirements
* than timer and playback/recording, right? */
if (status & IRQ_MPU401) {
snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
/* hmm, do we have to ack the IRQ here somehow?
- * If so, then I don't know how... */
- snd_azf3328_dbgplay("azt3328: MPU401 IRQ\n");
+ * If so, then I don't know how yet... */
+ snd_azf3328_dbgcodec("azt3328: MPU401 IRQ\n");
}
return IRQ_HANDLED;
}
/*****************************************************************/
-static const struct snd_pcm_hardware snd_azf3328_playback =
+/* as long as we think we have identical snd_pcm_hardware parameters
+ for playback, capture and i2s out, we can use the same physical struct
+ since the struct is simply being copied into a member.
+*/
+static const struct snd_pcm_hardware snd_azf3328_hardware =
{
/* FIXME!! Correct? */
.info = SNDRV_PCM_INFO_MMAP |
@@ -1718,31 +1751,6 @@ static const struct snd_pcm_hardware snd_azf3328_playback =
.fifo_size = 0,
};
-static const struct snd_pcm_hardware snd_azf3328_capture =
-{
- /* FIXME */
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID,
- .formats = SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_U8 |
- SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_U16_LE,
- .rates = SNDRV_PCM_RATE_5512 |
- SNDRV_PCM_RATE_8000_48000 |
- SNDRV_PCM_RATE_KNOT,
- .rate_min = AZF_FREQ_4000,
- .rate_max = AZF_FREQ_66200,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = 65536,
- .period_bytes_min = 64,
- .period_bytes_max = 65536,
- .periods_min = 1,
- .periods_max = 1024,
- .fifo_size = 0,
-};
-
static unsigned int snd_azf3328_fixed_rates[] = {
AZF_FREQ_4000,
@@ -1770,14 +1778,19 @@ static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
/*****************************************************************/
static int
-snd_azf3328_playback_open(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
+ enum snd_azf3328_codec_type codec_type
+)
{
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
snd_azf3328_dbgcallenter();
- chip->audio_stream[AZF_PLAYBACK].substream = substream;
- runtime->hw = snd_azf3328_playback;
+ chip->codecs[codec_type].substream = substream;
+
+ /* same parameters for all our codecs - at least we think so... */
+ runtime->hw = snd_azf3328_hardware;
+
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_azf3328_hw_constraints_rates);
snd_azf3328_dbgcallleave();
@@ -1785,40 +1798,52 @@ snd_azf3328_playback_open(struct snd_pcm_substream *substream)
}
static int
+snd_azf3328_playback_open(struct snd_pcm_substream *substream)
+{
+ return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
+}
+
+static int
snd_azf3328_capture_open(struct snd_pcm_substream *substream)
{
- struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
+ return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
+}
- snd_azf3328_dbgcallenter();
- chip->audio_stream[AZF_CAPTURE].substream = substream;
- runtime->hw = snd_azf3328_capture;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
- &snd_azf3328_hw_constraints_rates);
- snd_azf3328_dbgcallleave();
- return 0;
+static int
+snd_azf3328_i2s_out_open(struct snd_pcm_substream *substream)
+{
+ return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
}
static int
-snd_azf3328_playback_close(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_close(struct snd_pcm_substream *substream,
+ enum snd_azf3328_codec_type codec_type
+)
{
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
snd_azf3328_dbgcallenter();
- chip->audio_stream[AZF_PLAYBACK].substream = NULL;
+ chip->codecs[codec_type].substream = NULL;
snd_azf3328_dbgcallleave();
return 0;
}
static int
+snd_azf3328_playback_close(struct snd_pcm_substream *substream)
+{
+ return snd_azf3328_pcm_close(substream, AZF_CODEC_PLAYBACK);
+}
+
+static int
snd_azf3328_capture_close(struct snd_pcm_substream *substream)
{
- struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
+ return snd_azf3328_pcm_close(substream, AZF_CODEC_CAPTURE);
+}
- snd_azf3328_dbgcallenter();
- chip->audio_stream[AZF_CAPTURE].substream = NULL;
- snd_azf3328_dbgcallleave();
- return 0;
+static int
+snd_azf3328_i2s_out_close(struct snd_pcm_substream *substream)
+{
+ return snd_azf3328_pcm_close(substream, AZF_CODEC_I2S_OUT);
}
/******************************************************************/
@@ -1829,9 +1854,9 @@ static struct snd_pcm_ops snd_azf3328_playback_ops = {
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_azf3328_hw_params,
.hw_free = snd_azf3328_hw_free,
- .prepare = snd_azf3328_playback_prepare,
- .trigger = snd_azf3328_playback_trigger,
- .pointer = snd_azf3328_playback_pointer
+ .prepare = snd_azf3328_codec_prepare,
+ .trigger = snd_azf3328_codec_playback_trigger,
+ .pointer = snd_azf3328_codec_playback_pointer
};
static struct snd_pcm_ops snd_azf3328_capture_ops = {
@@ -1840,30 +1865,67 @@ static struct snd_pcm_ops snd_azf3328_capture_ops = {
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_azf3328_hw_params,
.hw_free = snd_azf3328_hw_free,
- .prepare = snd_azf3328_capture_prepare,
- .trigger = snd_azf3328_capture_trigger,
- .pointer = snd_azf3328_capture_pointer
+ .prepare = snd_azf3328_codec_prepare,
+ .trigger = snd_azf3328_codec_capture_trigger,
+ .pointer = snd_azf3328_codec_capture_pointer
+};
+
+static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
+ .open = snd_azf3328_i2s_out_open,
+ .close = snd_azf3328_i2s_out_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_azf3328_hw_params,
+ .hw_free = snd_azf3328_hw_free,
+ .prepare = snd_azf3328_codec_prepare,
+ .trigger = snd_azf3328_codec_i2s_out_trigger,
+ .pointer = snd_azf3328_codec_i2s_out_pointer
};
static int __devinit
-snd_azf3328_pcm(struct snd_azf3328 *chip, int device)
+snd_azf3328_pcm(struct snd_azf3328 *chip)
{
+enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
+
struct snd_pcm *pcm;
int err;
snd_azf3328_dbgcallenter();
- if ((err = snd_pcm_new(chip->card, "AZF3328 DSP", device, 1, 1, &pcm)) < 0)
+
+ err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD,
+ 1, 1, &pcm);
+ if (err < 0)
return err;
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_azf3328_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_azf3328_capture_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_azf3328_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_azf3328_capture_ops);
pcm->private_data = chip;
pcm->info_flags = 0;
strcpy(pcm->name, chip->card->shortname);
- chip->pcm = pcm;
+ /* same pcm object for playback/capture (see snd_pcm_new() above) */
+ chip->pcm[AZF_CODEC_PLAYBACK] = pcm;
+ chip->pcm[AZF_CODEC_CAPTURE] = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
+ snd_dma_pci_data(chip->pci),
+ 64*1024, 64*1024);
+
+ err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT,
+ 1, 0, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_azf3328_i2s_out_ops);
+
+ pcm->private_data = chip;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, chip->card->shortname);
+ chip->pcm[AZF_CODEC_I2S_OUT] = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_pci_data(chip->pci),
+ 64*1024, 64*1024);
snd_azf3328_dbgcallleave();
return 0;
@@ -1902,7 +1964,7 @@ snd_azf3328_timer_start(struct snd_timer *timer)
snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay);
delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
spin_lock_irqsave(&chip->reg_lock, flags);
- snd_azf3328_codec_outl(chip, IDX_IO_TIMER_VALUE, delay);
+ snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_azf3328_dbgcallleave();
return 0;
@@ -1919,7 +1981,7 @@ snd_azf3328_timer_stop(struct snd_timer *timer)
spin_lock_irqsave(&chip->reg_lock, flags);
/* disable timer countdown and interrupt */
/* FIXME: should we write TIMER_IRQ_ACK here? */
- snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0);
+ snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0);
spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_azf3328_dbgcallleave();
return 0;
@@ -2035,7 +2097,7 @@ snd_azf3328_test_bit(unsigned unsigned reg, int bit)
outb(val, reg);
- printk(KERN_ERR "reg %04x bit %d: %02x %02x %02x\n",
+ printk(KERN_DEBUG "reg %04x bit %d: %02x %02x %02x\n",
reg, bit, val, valoff, valon
);
}
@@ -2048,9 +2110,9 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
u16 tmp;
snd_azf3328_dbgmisc(
- "codec_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
+ "ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
"opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
- chip->codec_io, chip->game_io, chip->mpu_io,
+ chip->ctrl_io, chip->game_io, chip->mpu_io,
chip->opl3_io, chip->mixer_io, chip->irq
);
@@ -2083,9 +2145,9 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
inb(0x38c + tmp)
);
- for (tmp = 0; tmp < AZF_IO_SIZE_CODEC; tmp += 2)
- snd_azf3328_dbgmisc("codec 0x%02x: 0x%04x\n",
- tmp, snd_azf3328_codec_inw(chip, tmp)
+ for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2)
+ snd_azf3328_dbgmisc("ctrl 0x%02x: 0x%04x\n",
+ tmp, snd_azf3328_ctrl_inw(chip, tmp)
);
for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
@@ -2106,7 +2168,8 @@ snd_azf3328_create(struct snd_card *card,
static struct snd_device_ops ops = {
.dev_free = snd_azf3328_dev_free,
};
- u16 tmp;
+ u8 dma_init;
+ enum snd_azf3328_codec_type codec_type;
*rchip = NULL;
@@ -2138,14 +2201,21 @@ snd_azf3328_create(struct snd_card *card,
if (err < 0)
goto out_err;
- chip->codec_io = pci_resource_start(pci, 0);
+ chip->ctrl_io = pci_resource_start(pci, 0);
chip->game_io = pci_resource_start(pci, 1);
chip->mpu_io = pci_resource_start(pci, 2);
- chip->opl3_io = pci_resource_start(pci, 3);
+ chip->opl3_io = pci_resource_start(pci, 3);
chip->mixer_io = pci_resource_start(pci, 4);
- chip->audio_stream[AZF_PLAYBACK].portbase = chip->codec_io + 0x00;
- chip->audio_stream[AZF_CAPTURE].portbase = chip->codec_io + 0x20;
+ chip->codecs[AZF_CODEC_PLAYBACK].io_base =
+ chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
+ chip->codecs[AZF_CODEC_PLAYBACK].name = "PLAYBACK";
+ chip->codecs[AZF_CODEC_CAPTURE].io_base =
+ chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
+ chip->codecs[AZF_CODEC_CAPTURE].name = "CAPTURE";
+ chip->codecs[AZF_CODEC_I2S_OUT].io_base =
+ chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
+ chip->codecs[AZF_CODEC_I2S_OUT].name = "I2S_OUT";
if (request_irq(pci->irq, snd_azf3328_interrupt,
IRQF_SHARED, card->shortname, chip)) {
@@ -2168,20 +2238,25 @@ snd_azf3328_create(struct snd_card *card,
if (err < 0)
goto out_err;
- /* shutdown codecs to save power */
- /* have snd_azf3328_codec_activity() act properly */
- chip->audio_stream[AZF_PLAYBACK].running = 1;
- snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0);
+ /* standard codec init stuff */
+ /* default DMA init value */
+ dma_init = DMA_RUN_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
- /* standard chip init stuff */
- /* default IRQ init value */
- tmp = DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
+ for (codec_type = AZF_CODEC_PLAYBACK;
+ codec_type <= AZF_CODEC_I2S_OUT; ++codec_type) {
+ struct snd_azf3328_codec_data *codec =
+ &chip->codecs[codec_type];
- spin_lock_irq(&chip->reg_lock);
- snd_azf3328_codec_outb(chip, IDX_IO_PLAY_FLAGS, tmp);
- snd_azf3328_codec_outb(chip, IDX_IO_REC_FLAGS, tmp);
- snd_azf3328_codec_outb(chip, IDX_IO_SOMETHING_FLAGS, tmp);
- spin_unlock_irq(&chip->reg_lock);
+ /* shutdown codecs to save power */
+ /* have ...ctrl_codec_activity() act properly */
+ codec->running = 1;
+ snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
+
+ spin_lock_irq(&chip->reg_lock);
+ snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
+ dma_init);
+ spin_unlock_irq(&chip->reg_lock);
+ }
snd_card_set_dev(card, &pci->dev);
@@ -2229,8 +2304,11 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
card->private_data = chip;
+ /* chose to use MPU401_HW_AZT2320 ID instead of MPU401_HW_MPU401,
+ since our hardware ought to be similar, thus use same ID. */
err = snd_mpu401_uart_new(
- card, 0, MPU401_HW_MPU401, chip->mpu_io, MPU401_INFO_INTEGRATED,
+ card, 0,
+ MPU401_HW_AZT2320, chip->mpu_io, MPU401_INFO_INTEGRATED,
pci->irq, 0, &chip->rmidi
);
if (err < 0) {
@@ -2244,7 +2322,7 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
if (err < 0)
goto out_err;
- err = snd_azf3328_pcm(chip, 0);
+ err = snd_azf3328_pcm(chip);
if (err < 0)
goto out_err;
@@ -2266,14 +2344,14 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
opl3->private_data = chip;
sprintf(card->longname, "%s at 0x%lx, irq %i",
- card->shortname, chip->codec_io, chip->irq);
+ card->shortname, chip->ctrl_io, chip->irq);
err = snd_card_register(card);
if (err < 0)
goto out_err;
#ifdef MODULE
- printk(
+ printk(KERN_INFO
"azt3328: Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n"
"azt3328: Hardware was completely undocumented, unfortunately.\n"
"azt3328: Feel free to contact andi AT lisas.de for bug reports etc.!\n"
@@ -2308,36 +2386,52 @@ snd_azf3328_remove(struct pci_dev *pci)
}
#ifdef CONFIG_PM
+static inline void
+snd_azf3328_suspend_regs(unsigned long io_addr, unsigned count, u32 *saved_regs)
+{
+ unsigned reg;
+
+ for (reg = 0; reg < count; ++reg) {
+ *saved_regs = inl(io_addr);
+ snd_azf3328_dbgpm("suspend: io 0x%04lx: 0x%08x\n",
+ io_addr, *saved_regs);
+ ++saved_regs;
+ io_addr += sizeof(*saved_regs);
+ }
+}
+
static int
snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_azf3328 *chip = card->private_data;
- unsigned reg;
+ u16 *saved_regs_ctrl_u16;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
+ snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
+ snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
- for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
- chip->saved_regs_mixer[reg] = inw(chip->mixer_io + reg * 2);
+ snd_azf3328_suspend_regs(chip->mixer_io,
+ ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
/* make sure to disable master volume etc. to prevent looping sound */
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
- for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg)
- chip->saved_regs_codec[reg] = inw(chip->codec_io + reg * 2);
+ snd_azf3328_suspend_regs(chip->ctrl_io,
+ ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
/* manually store the one currently relevant write-only reg, too */
- chip->saved_regs_codec[IDX_IO_6AH / 2] = chip->shadow_reg_codec_6AH;
+ saved_regs_ctrl_u16 = (u16 *)chip->saved_regs_ctrl;
+ saved_regs_ctrl_u16[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH;
- for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg)
- chip->saved_regs_game[reg] = inw(chip->game_io + reg * 2);
- for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
- chip->saved_regs_mpu[reg] = inw(chip->mpu_io + reg * 2);
- for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
- chip->saved_regs_opl3[reg] = inw(chip->opl3_io + reg * 2);
+ snd_azf3328_suspend_regs(chip->game_io,
+ ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game);
+ snd_azf3328_suspend_regs(chip->mpu_io,
+ ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu);
+ snd_azf3328_suspend_regs(chip->opl3_io,
+ ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3);
pci_disable_device(pci);
pci_save_state(pci);
@@ -2345,12 +2439,28 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
return 0;
}
+static inline void
+snd_azf3328_resume_regs(const u32 *saved_regs,
+ unsigned long io_addr,
+ unsigned count
+)
+{
+ unsigned reg;
+
+ for (reg = 0; reg < count; ++reg) {
+ outl(*saved_regs, io_addr);
+ snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
+ io_addr, *saved_regs, inl(io_addr));
+ ++saved_regs;
+ io_addr += sizeof(*saved_regs);
+ }
+}
+
static int
snd_azf3328_resume(struct pci_dev *pci)
{
struct snd_card *card = pci_get_drvdata(pci);
- struct snd_azf3328 *chip = card->private_data;
- unsigned reg;
+ const struct snd_azf3328 *chip = card->private_data;
pci_set_power_state(pci, PCI_D0);
pci_restore_state(pci);
@@ -2362,16 +2472,24 @@ snd_azf3328_resume(struct pci_dev *pci)
}
pci_set_master(pci);
- for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg)
- outw(chip->saved_regs_game[reg], chip->game_io + reg * 2);
- for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
- outw(chip->saved_regs_mpu[reg], chip->mpu_io + reg * 2);
- for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
- outw(chip->saved_regs_opl3[reg], chip->opl3_io + reg * 2);
- for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
- outw(chip->saved_regs_mixer[reg], chip->mixer_io + reg * 2);
- for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg)
- outw(chip->saved_regs_codec[reg], chip->codec_io + reg * 2);
+ snd_azf3328_resume_regs(chip->saved_regs_game, chip->game_io,
+ ARRAY_SIZE(chip->saved_regs_game));
+ snd_azf3328_resume_regs(chip->saved_regs_mpu, chip->mpu_io,
+ ARRAY_SIZE(chip->saved_regs_mpu));
+ snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io,
+ ARRAY_SIZE(chip->saved_regs_opl3));
+
+ snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io,
+ ARRAY_SIZE(chip->saved_regs_mixer));
+
+ /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
+ and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
+ resulting in a mixer reset condition persisting until _after_
+ master vol was restored. Thus master vol needs an extra restore. */
+ outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
+
+ snd_azf3328_resume_regs(chip->saved_regs_ctrl, chip->ctrl_io,
+ ARRAY_SIZE(chip->saved_regs_ctrl));
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
diff --git a/sound/pci/azt3328.h b/sound/pci/azt3328.h
index 974e05122f0..6f46b97650c 100644
--- a/sound/pci/azt3328.h
+++ b/sound/pci/azt3328.h
@@ -6,50 +6,59 @@
/*** main I/O area port indices ***/
/* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */
-#define AZF_IO_SIZE_CODEC 0x80
-#define AZF_IO_SIZE_CODEC_PM 0x70
+#define AZF_IO_SIZE_CTRL 0x80
+#define AZF_IO_SIZE_CTRL_PM 0x70
-/* the driver initialisation suggests a layout of 4 main areas:
- * from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe MPU401??).
+/* the driver initialisation suggests a layout of 4 areas
+ * within the main card control I/O:
+ * from 0x00 (playback codec), from 0x20 (recording codec)
+ * and from 0x40 (most certainly I2S out codec).
* And another area from 0x60 to 0x6f (DirectX timer, IRQ management,
* power management etc.???). */
-/** playback area **/
-#define IDX_IO_PLAY_FLAGS 0x00 /* PU:0x0000 */
+#define AZF_IO_OFFS_CODEC_PLAYBACK 0x00
+#define AZF_IO_OFFS_CODEC_CAPTURE 0x20
+#define AZF_IO_OFFS_CODEC_I2S_OUT 0x40
+
+#define IDX_IO_CODEC_DMA_FLAGS 0x00 /* PU:0x0000 */
/* able to reactivate output after output muting due to 8/16bit
* output change, just like 0x0002.
* 0x0001 is the only bit that's able to start the DMA counter */
- #define DMA_RESUME 0x0001 /* paused if cleared ? */
+ #define DMA_RESUME 0x0001 /* paused if cleared? */
/* 0x0002 *temporarily* set during DMA stopping. hmm
* both 0x0002 and 0x0004 set in playback setup. */
/* able to reactivate output after output muting due to 8/16bit
* output change, just like 0x0001. */
- #define DMA_PLAY_SOMETHING1 0x0002 /* \ alternated (toggled) */
+ #define DMA_RUN_SOMETHING1 0x0002 /* \ alternated (toggled) */
/* 0x0004: NOT able to reactivate output */
- #define DMA_PLAY_SOMETHING2 0x0004 /* / bits */
+ #define DMA_RUN_SOMETHING2 0x0004 /* / bits */
#define SOMETHING_ALMOST_ALWAYS_SET 0x0008 /* ???; can be modified */
#define DMA_EPILOGUE_SOMETHING 0x0010
#define DMA_SOMETHING_ELSE 0x0020 /* ??? */
- #define SOMETHING_UNMODIFIABLE 0xffc0 /* unused ? not modifiable */
-#define IDX_IO_PLAY_IRQTYPE 0x02 /* PU:0x0001 */
+ #define SOMETHING_UNMODIFIABLE 0xffc0 /* unused? not modifiable */
+#define IDX_IO_CODEC_IRQTYPE 0x02 /* PU:0x0001 */
/* write back to flags in case flags are set, in order to ACK IRQ in handler
* (bit 1 of port 0x64 indicates interrupt for one of these three types)
* sometimes in this case it just writes 0xffff to globally ACK all IRQs
* settings written are not reflected when reading back, though.
- * seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows ? */
- #define IRQ_PLAY_SOMETHING 0x0001 /* something & ACK */
- #define IRQ_FINISHED_PLAYBUF_1 0x0002 /* 1st dmabuf finished & ACK */
- #define IRQ_FINISHED_PLAYBUF_2 0x0004 /* 2nd dmabuf finished & ACK */
+ * seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows? */
+ #define IRQ_SOMETHING 0x0001 /* something & ACK */
+ #define IRQ_FINISHED_DMABUF_1 0x0002 /* 1st dmabuf finished & ACK */
+ #define IRQ_FINISHED_DMABUF_2 0x0004 /* 2nd dmabuf finished & ACK */
#define IRQMASK_SOME_STATUS_1 0x0008 /* \ related bits */
#define IRQMASK_SOME_STATUS_2 0x0010 /* / (checked together in loop) */
- #define IRQMASK_UNMODIFIABLE 0xffe0 /* unused ? not modifiable */
-#define IDX_IO_PLAY_DMA_START_1 0x04 /* start address of 1st DMA play area, PU:0x00000000 */
-#define IDX_IO_PLAY_DMA_START_2 0x08 /* start address of 2nd DMA play area, PU:0x00000000 */
-#define IDX_IO_PLAY_DMA_LEN_1 0x0c /* length of 1st DMA play area, PU:0x0000 */
-#define IDX_IO_PLAY_DMA_LEN_2 0x0e /* length of 2nd DMA play area, PU:0x0000 */
-#define IDX_IO_PLAY_DMA_CURRPOS 0x10 /* current DMA position, PU:0x00000000 */
-#define IDX_IO_PLAY_DMA_CURROFS 0x14 /* offset within current DMA play area, PU:0x0000 */
-#define IDX_IO_PLAY_SOUNDFORMAT 0x16 /* PU:0x0010 */
+ #define IRQMASK_UNMODIFIABLE 0xffe0 /* unused? not modifiable */
+ /* start address of 1st DMA transfer area, PU:0x00000000 */
+#define IDX_IO_CODEC_DMA_START_1 0x04
+ /* start address of 2nd DMA transfer area, PU:0x00000000 */
+#define IDX_IO_CODEC_DMA_START_2 0x08
+ /* both lengths of DMA transfer areas, PU:0x00000000
+ length1: offset 0x0c, length2: offset 0x0e */
+#define IDX_IO_CODEC_DMA_LENGTHS 0x0c
+#define IDX_IO_CODEC_DMA_CURRPOS 0x10 /* current DMA position, PU:0x00000000 */
+ /* offset within current DMA transfer area, PU:0x0000 */
+#define IDX_IO_CODEC_DMA_CURROFS 0x14
+#define IDX_IO_CODEC_SOUNDFORMAT 0x16 /* PU:0x0010 */
/* all unspecified bits can't be modified */
#define SOUNDFORMAT_FREQUENCY_MASK 0x000f
#define SOUNDFORMAT_XTAL1 0x00
@@ -76,6 +85,7 @@
#define SOUNDFORMAT_FLAG_16BIT 0x0010
#define SOUNDFORMAT_FLAG_2CHANNELS 0x0020
+
/* define frequency helpers, for maximum value safety */
enum azf_freq_t {
#define AZF_FREQ(rate) AZF_FREQ_##rate = rate
@@ -96,29 +106,6 @@ enum azf_freq_t {
#undef AZF_FREQ
};
-/** recording area (see also: playback bit flag definitions) **/
-#define IDX_IO_REC_FLAGS 0x20 /* ??, PU:0x0000 */
-#define IDX_IO_REC_IRQTYPE 0x22 /* ??, PU:0x0000 */
- #define IRQ_REC_SOMETHING 0x0001 /* something & ACK */
- #define IRQ_FINISHED_RECBUF_1 0x0002 /* 1st dmabuf finished & ACK */
- #define IRQ_FINISHED_RECBUF_2 0x0004 /* 2nd dmabuf finished & ACK */
- /* hmm, maybe these are just the corresponding *recording* flags ?
- * but OTOH they are most likely at port 0x22 instead */
- #define IRQMASK_SOME_STATUS_1 0x0008 /* \ related bits */
- #define IRQMASK_SOME_STATUS_2 0x0010 /* / (checked together in loop) */
-#define IDX_IO_REC_DMA_START_1 0x24 /* PU:0x00000000 */
-#define IDX_IO_REC_DMA_START_2 0x28 /* PU:0x00000000 */
-#define IDX_IO_REC_DMA_LEN_1 0x2c /* PU:0x0000 */
-#define IDX_IO_REC_DMA_LEN_2 0x2e /* PU:0x0000 */
-#define IDX_IO_REC_DMA_CURRPOS 0x30 /* PU:0x00000000 */
-#define IDX_IO_REC_DMA_CURROFS 0x34 /* PU:0x00000000 */
-#define IDX_IO_REC_SOUNDFORMAT 0x36 /* PU:0x0000 */
-
-/** hmm, what is this I/O area for? MPU401?? or external DAC via I2S?? (after playback, recording, ???, timer) **/
-#define IDX_IO_SOMETHING_FLAGS 0x40 /* gets set to 0x34 just like port 0x0 and 0x20 on card init, PU:0x0000 */
-/* general */
-#define IDX_IO_42H 0x42 /* PU:0x0001 */
-
/** DirectX timer, main interrupt area (FIXME: and something else?) **/
#define IDX_IO_TIMER_VALUE 0x60 /* found this timer area by pure luck :-) */
/* timer countdown value; triggers IRQ when timer is finished */
@@ -133,17 +120,19 @@ enum azf_freq_t {
#define IDX_IO_IRQSTATUS 0x64
/* some IRQ bit in here might also be used to signal a power-management timer
* timeout, to request shutdown of the chip (e.g. AD1815JS has such a thing).
- * Some OPL3 hardware (e.g. in LM4560) has some special timer hardware which
- * can trigger an OPL3 timer IRQ, so maybe there's such a thing as well... */
+ * OPL3 hardware contains several timers which confusingly in most cases
+ * are NOT routed to an IRQ, but some designs (e.g. LM4560) DO support that,
+ * so I wouldn't be surprised at all to discover that AZF3328
+ * supports that thing as well... */
#define IRQ_PLAYBACK 0x0001
#define IRQ_RECORDING 0x0002
- #define IRQ_UNKNOWN1 0x0004 /* most probably I2S port */
+ #define IRQ_I2S_OUT 0x0004 /* this IS I2S, right!? (untested) */
#define IRQ_GAMEPORT 0x0008 /* Interrupt of Digital(ly) Enhanced Game Port */
#define IRQ_MPU401 0x0010
#define IRQ_TIMER 0x0020 /* DirectX timer */
- #define IRQ_UNKNOWN2 0x0040 /* probably unused, or possibly I2S port? */
- #define IRQ_UNKNOWN3 0x0080 /* probably unused, or possibly I2S port? */
+ #define IRQ_UNKNOWN2 0x0040 /* probably unused, or possibly OPL3 timer? */
+ #define IRQ_UNKNOWN3 0x0080 /* probably unused, or possibly OPL3 timer? */
#define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */
/* this is set to e.g. 0x3ff or 0x300, and writable;
* maybe some buffer limit, but I couldn't find out more, PU:0x00ff: */
@@ -206,7 +195,7 @@ enum azf_freq_t {
/*** Gameport area port indices ***/
/* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */
#define AZF_IO_SIZE_GAME 0x08
-#define AZF_IO_SIZE_GAME_PM 0x06
+#define AZF_IO_SIZE_GAME_PM 0x06
enum {
AZF_GAME_LEGACY_IO_PORT = 0x200
@@ -272,6 +261,12 @@ enum {
* 11 --> 1/200: */
#define GAME_HWCFG_ADC_COUNTER_FREQ_MASK 0x06
+ /* FIXME: these values might be reversed... */
+ #define GAME_HWCFG_ADC_COUNTER_FREQ_STD 0
+ #define GAME_HWCFG_ADC_COUNTER_FREQ_1_2 1
+ #define GAME_HWCFG_ADC_COUNTER_FREQ_1_20 2
+ #define GAME_HWCFG_ADC_COUNTER_FREQ_1_200 3
+
/* enable gameport legacy I/O address (0x200)
* I was unable to locate any configurability for a different address: */
#define GAME_HWCFG_LEGACY_ADDRESS_ENABLE 0x08
@@ -281,6 +276,7 @@ enum {
#define AZF_IO_SIZE_MPU_PM 0x04
/*** OPL3 synth ***/
+/* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */
#define AZF_IO_SIZE_OPL3 0x08
#define AZF_IO_SIZE_OPL3_PM 0x06
/* hmm, given that a standard OPL3 has 4 registers only,
@@ -340,4 +336,7 @@ enum {
#define SET_CHAN_LEFT 1
#define SET_CHAN_RIGHT 2
+/* helper macro to align I/O port ranges to 32bit I/O width */
+#define AZF_ALIGN(x) (((x) + 3) & (~3))
+
#endif /* __SOUND_AZT3328_H */
diff --git a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h
index 4eb55aa3361..b5189495d58 100644
--- a/sound/pci/cs46xx/cs46xx_lib.h
+++ b/sound/pci/cs46xx/cs46xx_lib.h
@@ -35,7 +35,7 @@
#ifdef CONFIG_SND_CS46XX_NEW_DSP
-#define CS46XX_MIN_PERIOD_SIZE 1
+#define CS46XX_MIN_PERIOD_SIZE 64
#define CS46XX_MAX_PERIOD_SIZE 1024*1024
#else
#define CS46XX_MIN_PERIOD_SIZE 2048
diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h
index 2d07986f57c..e0394e3996e 100644
--- a/sound/pci/ctxfi/ct20k2reg.h
+++ b/sound/pci/ctxfi/ct20k2reg.h
@@ -11,9 +11,12 @@
/* Timer Registers */
-#define TIMER_TIMR 0x1B7004
-#define INTERRUPT_GIP 0x1B7010
-#define INTERRUPT_GIE 0x1B7014
+#define WC 0x1b7000
+#define TIMR 0x1b7004
+# define TIMR_IE (1<<15)
+# define TIMR_IP (1<<14)
+#define GIP 0x1b7010
+#define GIE 0x1b7014
/* I2C Registers */
#define I2C_IF_ADDRESS 0x1B9000
diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c
index a7f4a671f7b..fee35cfc0c7 100644
--- a/sound/pci/ctxfi/ctamixer.c
+++ b/sound/pci/ctxfi/ctamixer.c
@@ -63,7 +63,7 @@ static int amixer_set_input(struct amixer *amixer, struct rsc *rsc)
hw = amixer->rsc.hw;
hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE);
amixer->input = rsc;
- if (NULL == rsc)
+ if (!rsc)
hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT);
else
hw->amixer_set_x(amixer->rsc.ctrl_blk,
@@ -99,7 +99,7 @@ static int amixer_set_sum(struct amixer *amixer, struct sum *sum)
hw = amixer->rsc.hw;
amixer->sum = sum;
- if (NULL == sum) {
+ if (!sum) {
hw->amixer_set_se(amixer->rsc.ctrl_blk, 0);
} else {
hw->amixer_set_se(amixer->rsc.ctrl_blk, 1);
@@ -124,20 +124,20 @@ static int amixer_commit_write(struct amixer *amixer)
/* Program master and conjugate resources */
amixer->rsc.ops->master(&amixer->rsc);
- if (NULL != input)
+ if (input)
input->ops->master(input);
- if (NULL != sum)
+ if (sum)
sum->rsc.ops->master(&sum->rsc);
for (i = 0; i < amixer->rsc.msr; i++) {
hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk);
- if (NULL != input) {
+ if (input) {
hw->amixer_set_x(amixer->rsc.ctrl_blk,
input->ops->output_slot(input));
input->ops->next_conj(input);
}
- if (NULL != sum) {
+ if (sum) {
hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
sum->rsc.ops->index(&sum->rsc));
sum->rsc.ops->next_conj(&sum->rsc);
@@ -147,10 +147,10 @@ static int amixer_commit_write(struct amixer *amixer)
amixer->rsc.ops->next_conj(&amixer->rsc);
}
amixer->rsc.ops->master(&amixer->rsc);
- if (NULL != input)
+ if (input)
input->ops->master(input);
- if (NULL != sum)
+ if (sum)
sum->rsc.ops->master(&sum->rsc);
return 0;
@@ -303,7 +303,7 @@ int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
*ramixer_mgr = NULL;
amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL);
- if (NULL == amixer_mgr)
+ if (!amixer_mgr)
return -ENOMEM;
err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw);
@@ -456,7 +456,7 @@ int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
*rsum_mgr = NULL;
sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL);
- if (NULL == sum_mgr)
+ if (!sum_mgr)
return -ENOMEM;
err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw);
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index a49c7664730..b1b3a644f73 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -136,7 +136,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
struct snd_pcm_runtime *runtime;
struct ct_vm *vm;
- if (NULL == apcm->substream)
+ if (!apcm->substream)
return 0;
runtime = apcm->substream->runtime;
@@ -144,7 +144,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
apcm->vm_block = vm->map(vm, apcm->substream, runtime->dma_bytes);
- if (NULL == apcm->vm_block)
+ if (!apcm->vm_block)
return -ENOENT;
return 0;
@@ -154,7 +154,7 @@ static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
{
struct ct_vm *vm;
- if (NULL == apcm->vm_block)
+ if (!apcm->vm_block)
return;
vm = atc->vm;
@@ -231,16 +231,16 @@ atc_get_pitch(unsigned int input_rate, unsigned int output_rate)
static int select_rom(unsigned int pitch)
{
- if ((pitch > 0x00428f5c) && (pitch < 0x01b851ec)) {
+ if (pitch > 0x00428f5c && pitch < 0x01b851ec) {
/* 0.26 <= pitch <= 1.72 */
return 1;
- } else if ((0x01d66666 == pitch) || (0x01d66667 == pitch)) {
+ } else if (pitch == 0x01d66666 || pitch == 0x01d66667) {
/* pitch == 1.8375 */
return 2;
- } else if (0x02000000 == pitch) {
+ } else if (pitch == 0x02000000) {
/* pitch == 2 */
return 3;
- } else if ((pitch >= 0x0) && (pitch <= 0x08000000)) {
+ } else if (pitch >= 0x0 && pitch <= 0x08000000) {
/* 0 <= pitch <= 8 */
return 0;
} else {
@@ -283,7 +283,7 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
/* Get AMIXER resource */
n_amixer = (n_amixer < 2) ? 2 : n_amixer;
apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
- if (NULL == apcm->amixers) {
+ if (!apcm->amixers) {
err = -ENOMEM;
goto error1;
}
@@ -311,7 +311,7 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
INIT_VOL, atc->pcm[i+device*2]);
mutex_unlock(&atc->atc_mutex);
src = src->ops->next_interleave(src);
- if (NULL == src)
+ if (!src)
src = apcm->src;
}
@@ -334,7 +334,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
struct srcimp *srcimp;
int i;
- if (NULL != apcm->srcimps) {
+ if (apcm->srcimps) {
for (i = 0; i < apcm->n_srcimp; i++) {
srcimp = apcm->srcimps[i];
srcimp->ops->unmap(srcimp);
@@ -345,7 +345,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
apcm->srcimps = NULL;
}
- if (NULL != apcm->srccs) {
+ if (apcm->srccs) {
for (i = 0; i < apcm->n_srcc; i++) {
src_mgr->put_src(src_mgr, apcm->srccs[i]);
apcm->srccs[i] = NULL;
@@ -354,7 +354,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
apcm->srccs = NULL;
}
- if (NULL != apcm->amixers) {
+ if (apcm->amixers) {
for (i = 0; i < apcm->n_amixer; i++) {
amixer_mgr->put_amixer(amixer_mgr, apcm->amixers[i]);
apcm->amixers[i] = NULL;
@@ -363,17 +363,17 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
apcm->amixers = NULL;
}
- if (NULL != apcm->mono) {
+ if (apcm->mono) {
sum_mgr->put_sum(sum_mgr, apcm->mono);
apcm->mono = NULL;
}
- if (NULL != apcm->src) {
+ if (apcm->src) {
src_mgr->put_src(src_mgr, apcm->src);
apcm->src = NULL;
}
- if (NULL != apcm->vm_block) {
+ if (apcm->vm_block) {
/* Undo device virtual mem map */
ct_unmap_audio_buffer(atc, apcm);
apcm->vm_block = NULL;
@@ -419,7 +419,7 @@ static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm)
src->ops->set_state(src, SRC_STATE_OFF);
src->ops->commit_write(src);
- if (NULL != apcm->srccs) {
+ if (apcm->srccs) {
for (i = 0; i < apcm->n_srcc; i++) {
src = apcm->srccs[i];
src->ops->set_bm(src, 0);
@@ -544,18 +544,18 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
if (n_srcc) {
apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL);
- if (NULL == apcm->srccs)
+ if (!apcm->srccs)
return -ENOMEM;
}
if (n_amixer) {
apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
- if (NULL == apcm->amixers) {
+ if (!apcm->amixers) {
err = -ENOMEM;
goto error1;
}
}
apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL);
- if (NULL == apcm->srcimps) {
+ if (!apcm->srcimps) {
err = -ENOMEM;
goto error1;
}
@@ -818,7 +818,7 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc,
/* Get AMIXER resource */
n_amixer = (n_amixer < 2) ? 2 : n_amixer;
apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
- if (NULL == apcm->amixers) {
+ if (!apcm->amixers) {
err = -ENOMEM;
goto error1;
}
@@ -919,7 +919,7 @@ spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
amixer = apcm->amixers[i];
amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL);
src = src->ops->next_interleave(src);
- if (NULL == src)
+ if (!src)
src = apcm->src;
}
/* Connect to SPDIFOO */
@@ -1121,7 +1121,7 @@ static int atc_release_resources(struct ct_atc *atc)
struct ct_mixer *mixer = NULL;
/* disconnect internal mixer objects */
- if (NULL != atc->mixer) {
+ if (atc->mixer) {
mixer = atc->mixer;
mixer->set_input_left(mixer, MIX_LINE_IN, NULL);
mixer->set_input_right(mixer, MIX_LINE_IN, NULL);
@@ -1131,7 +1131,7 @@ static int atc_release_resources(struct ct_atc *atc)
mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL);
}
- if (NULL != atc->daios) {
+ if (atc->daios) {
daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
for (i = 0; i < atc->n_daio; i++) {
daio = atc->daios[i];
@@ -1149,7 +1149,7 @@ static int atc_release_resources(struct ct_atc *atc)
atc->daios = NULL;
}
- if (NULL != atc->pcm) {
+ if (atc->pcm) {
sum_mgr = atc->rsc_mgrs[SUM];
for (i = 0; i < atc->n_pcm; i++)
sum_mgr->put_sum(sum_mgr, atc->pcm[i]);
@@ -1158,7 +1158,7 @@ static int atc_release_resources(struct ct_atc *atc)
atc->pcm = NULL;
}
- if (NULL != atc->srcs) {
+ if (atc->srcs) {
src_mgr = atc->rsc_mgrs[SRC];
for (i = 0; i < atc->n_src; i++)
src_mgr->put_src(src_mgr, atc->srcs[i]);
@@ -1167,7 +1167,7 @@ static int atc_release_resources(struct ct_atc *atc)
atc->srcs = NULL;
}
- if (NULL != atc->srcimps) {
+ if (atc->srcimps) {
srcimp_mgr = atc->rsc_mgrs[SRCIMP];
for (i = 0; i < atc->n_srcimp; i++) {
srcimp = atc->srcimps[i];
@@ -1185,7 +1185,7 @@ static int ct_atc_destroy(struct ct_atc *atc)
{
int i = 0;
- if (NULL == atc)
+ if (!atc)
return 0;
if (atc->timer) {
@@ -1196,21 +1196,20 @@ static int ct_atc_destroy(struct ct_atc *atc)
atc_release_resources(atc);
/* Destroy internal mixer objects */
- if (NULL != atc->mixer)
+ if (atc->mixer)
ct_mixer_destroy(atc->mixer);
for (i = 0; i < NUM_RSCTYP; i++) {
- if ((NULL != rsc_mgr_funcs[i].destroy) &&
- (NULL != atc->rsc_mgrs[i]))
+ if (rsc_mgr_funcs[i].destroy && atc->rsc_mgrs[i])
rsc_mgr_funcs[i].destroy(atc->rsc_mgrs[i]);
}
- if (NULL != atc->hw)
+ if (atc->hw)
destroy_hw_obj((struct hw *)atc->hw);
/* Destroy device virtual memory manager object */
- if (NULL != atc->vm) {
+ if (atc->vm) {
ct_vm_destroy(atc->vm);
atc->vm = NULL;
}
@@ -1275,7 +1274,7 @@ int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc)
alsa_dev_funcs[MIXER].public_name = atc->chip_name;
for (i = 0; i < NUM_CTALSADEVS; i++) {
- if (NULL == alsa_dev_funcs[i].create)
+ if (!alsa_dev_funcs[i].create)
continue;
err = alsa_dev_funcs[i].create(atc, i,
@@ -1312,7 +1311,7 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc)
return err;
for (i = 0; i < NUM_RSCTYP; i++) {
- if (NULL == rsc_mgr_funcs[i].create)
+ if (!rsc_mgr_funcs[i].create)
continue;
err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]);
@@ -1339,19 +1338,19 @@ static int atc_get_resources(struct ct_atc *atc)
int err, i;
atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
- if (NULL == atc->daios)
+ if (!atc->daios)
return -ENOMEM;
atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
- if (NULL == atc->srcs)
+ if (!atc->srcs)
return -ENOMEM;
atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
- if (NULL == atc->srcimps)
+ if (!atc->srcimps)
return -ENOMEM;
atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL);
- if (NULL == atc->pcm)
+ if (!atc->pcm)
return -ENOMEM;
daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
@@ -1648,7 +1647,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
*ratc = NULL;
atc = kzalloc(sizeof(*atc), GFP_KERNEL);
- if (NULL == atc)
+ if (!atc)
return -ENOMEM;
/* Set operations */
diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c
index deb6cfa7360..af56eb949bd 100644
--- a/sound/pci/ctxfi/ctdaio.c
+++ b/sound/pci/ctxfi/ctdaio.c
@@ -173,7 +173,7 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input)
int i;
entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL);
- if (NULL == entry)
+ if (!entry)
return -ENOMEM;
/* Program master and conjugate resources */
@@ -201,7 +201,7 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input)
int i;
entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL);
- if (NULL == entry)
+ if (!entry)
return -ENOMEM;
/* Program master and conjugate resources */
@@ -228,7 +228,7 @@ static int dao_clear_left_input(struct dao *dao)
struct daio *daio = &dao->daio;
int i;
- if (NULL == dao->imappers[0])
+ if (!dao->imappers[0])
return 0;
entry = dao->imappers[0];
@@ -252,7 +252,7 @@ static int dao_clear_right_input(struct dao *dao)
struct daio *daio = &dao->daio;
int i;
- if (NULL == dao->imappers[daio->rscl.msr])
+ if (!dao->imappers[daio->rscl.msr])
return 0;
entry = dao->imappers[daio->rscl.msr];
@@ -408,7 +408,7 @@ static int dao_rsc_init(struct dao *dao,
return err;
dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL);
- if (NULL == dao->imappers) {
+ if (!dao->imappers) {
err = -ENOMEM;
goto error1;
}
@@ -442,11 +442,11 @@ error1:
static int dao_rsc_uninit(struct dao *dao)
{
- if (NULL != dao->imappers) {
- if (NULL != dao->imappers[0])
+ if (dao->imappers) {
+ if (dao->imappers[0])
dao_clear_left_input(dao);
- if (NULL != dao->imappers[dao->daio.rscl.msr])
+ if (dao->imappers[dao->daio.rscl.msr])
dao_clear_right_input(dao);
kfree(dao->imappers);
@@ -555,7 +555,7 @@ static int get_daio_rsc(struct daio_mgr *mgr,
/* Allocate mem for daio resource */
if (desc->type <= DAIO_OUT_MAX) {
dao = kzalloc(sizeof(*dao), GFP_KERNEL);
- if (NULL == dao) {
+ if (!dao) {
err = -ENOMEM;
goto error;
}
@@ -566,7 +566,7 @@ static int get_daio_rsc(struct daio_mgr *mgr,
*rdaio = &dao->daio;
} else {
dai = kzalloc(sizeof(*dai), GFP_KERNEL);
- if (NULL == dai) {
+ if (!dai) {
err = -ENOMEM;
goto error;
}
@@ -583,9 +583,9 @@ static int get_daio_rsc(struct daio_mgr *mgr,
return 0;
error:
- if (NULL != dao)
+ if (dao)
kfree(dao);
- else if (NULL != dai)
+ else if (dai)
kfree(dai);
spin_lock_irqsave(&mgr->mgr_lock, flags);
@@ -663,7 +663,7 @@ static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry)
int err;
spin_lock_irqsave(&mgr->imap_lock, flags);
- if ((0 == entry->addr) && (mgr->init_imap_added)) {
+ if (!entry->addr && mgr->init_imap_added) {
input_mapper_delete(&mgr->imappers, mgr->init_imap,
daio_map_op, mgr);
mgr->init_imap_added = 0;
@@ -707,7 +707,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
*rdaio_mgr = NULL;
daio_mgr = kzalloc(sizeof(*daio_mgr), GFP_KERNEL);
- if (NULL == daio_mgr)
+ if (!daio_mgr)
return -ENOMEM;
err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw);
@@ -718,7 +718,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
spin_lock_init(&daio_mgr->imap_lock);
INIT_LIST_HEAD(&daio_mgr->imappers);
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
- if (NULL == entry) {
+ if (!entry) {
err = -ENOMEM;
goto error2;
}
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index ad3e1d14446..0cf400f879f 100644
--- a/sound/pci/ctxfi/cthw20k1.c
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -168,7 +168,7 @@ static int src_get_rsc_ctrl_blk(void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
*rblk = blk;
@@ -494,7 +494,7 @@ static int src_mgr_get_ctrl_blk(void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
*rblk = blk;
@@ -515,7 +515,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
*rblk = blk;
@@ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
*rblk = blk;
@@ -723,7 +723,7 @@ static int amixer_mgr_get_ctrl_blk(void **rblk)
*rblk = NULL;
/*blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
*rblk = blk;*/
@@ -909,7 +909,7 @@ static int dai_get_ctrl_blk(void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
*rblk = blk;
@@ -958,7 +958,7 @@ static int dao_get_ctrl_blk(void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
*rblk = blk;
@@ -1152,7 +1152,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
blk->i2sctl = hw_read_20kx(hw, I2SCTL);
@@ -1808,7 +1808,7 @@ static int uaa_to_xfi(struct pci_dev *pci)
/* By default, Hendrix card UAA Bar0 should be using memory... */
io_base = pci_resource_start(pci, 0);
mem_base = ioremap(io_base, pci_resource_len(pci, 0));
- if (NULL == mem_base)
+ if (!mem_base)
return -ENOENT;
/* Read current mode from Mode Change Register */
@@ -1977,7 +1977,7 @@ static int hw_card_shutdown(struct hw *hw)
hw->irq = -1;
- if (NULL != ((void *)hw->mem_base))
+ if (hw->mem_base)
iounmap((void *)hw->mem_base);
hw->mem_base = (unsigned long)NULL;
@@ -2274,7 +2274,7 @@ int __devinit create_20k1_hw_obj(struct hw **rhw)
*rhw = NULL;
hw20k1 = kzalloc(sizeof(*hw20k1), GFP_KERNEL);
- if (NULL == hw20k1)
+ if (!hw20k1)
return -ENOMEM;
spin_lock_init(&hw20k1->reg_20k1_lock);
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
index dec46d04b04..b6b11bfe757 100644
--- a/sound/pci/ctxfi/cthw20k2.c
+++ b/sound/pci/ctxfi/cthw20k2.c
@@ -166,7 +166,7 @@ static int src_get_rsc_ctrl_blk(void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
*rblk = blk;
@@ -492,7 +492,7 @@ static int src_mgr_get_ctrl_blk(void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
*rblk = blk;
@@ -513,7 +513,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
*rblk = blk;
@@ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
*rblk = blk;
@@ -891,7 +891,7 @@ static int dai_get_ctrl_blk(void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
*rblk = blk;
@@ -941,7 +941,7 @@ static int dao_get_ctrl_blk(void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
*rblk = blk;
@@ -1092,7 +1092,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
*rblk = NULL;
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
- if (NULL == blk)
+ if (!blk)
return -ENOMEM;
for (i = 0; i < 8; i++) {
@@ -1112,6 +1112,26 @@ static int daio_mgr_put_ctrl_blk(void *blk)
return 0;
}
+/* Timer interrupt */
+static int set_timer_irq(struct hw *hw, int enable)
+{
+ hw_write_20kx(hw, GIE, enable ? IT_INT : 0);
+ return 0;
+}
+
+static int set_timer_tick(struct hw *hw, unsigned int ticks)
+{
+ if (ticks)
+ ticks |= TIMR_IE | TIMR_IP;
+ hw_write_20kx(hw, TIMR, ticks);
+ return 0;
+}
+
+static unsigned int get_wc(struct hw *hw)
+{
+ return hw_read_20kx(hw, WC);
+}
+
/* Card hardware initialization block */
struct dac_conf {
unsigned int msr; /* master sample rate in rsrs */
@@ -1841,6 +1861,22 @@ static int hw_have_digit_io_switch(struct hw *hw)
return 0;
}
+static irqreturn_t ct_20k2_interrupt(int irq, void *dev_id)
+{
+ struct hw *hw = dev_id;
+ unsigned int status;
+
+ status = hw_read_20kx(hw, GIP);
+ if (!status)
+ return IRQ_NONE;
+
+ if (hw->irq_callback)
+ hw->irq_callback(hw->irq_callback_data, status);
+
+ hw_write_20kx(hw, GIP, status);
+ return IRQ_HANDLED;
+}
+
static int hw_card_start(struct hw *hw)
{
int err = 0;
@@ -1868,7 +1904,7 @@ static int hw_card_start(struct hw *hw)
hw->io_base = pci_resource_start(hw->pci, 2);
hw->mem_base = (unsigned long)ioremap(hw->io_base,
pci_resource_len(hw->pci, 2));
- if (NULL == (void *)hw->mem_base) {
+ if (!hw->mem_base) {
err = -ENOENT;
goto error2;
}
@@ -1879,12 +1915,15 @@ static int hw_card_start(struct hw *hw)
set_field(&gctl, GCTL_UAA, 0);
hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
- /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED,
- atc->chip_details->nm_card, hw))) {
- goto error3;
+ if (hw->irq < 0) {
+ err = request_irq(pci->irq, ct_20k2_interrupt, IRQF_SHARED,
+ "ctxfi", hw);
+ if (err < 0) {
+ printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
+ goto error2;
+ }
+ hw->irq = pci->irq;
}
- hw->irq = pci->irq;
- */
pci_set_master(pci);
@@ -1923,7 +1962,7 @@ static int hw_card_shutdown(struct hw *hw)
hw->irq = -1;
- if (NULL != ((void *)hw->mem_base))
+ if (hw->mem_base)
iounmap((void *)hw->mem_base);
hw->mem_base = (unsigned long)NULL;
@@ -1972,7 +2011,7 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
/* Reset all global pending interrupts */
- hw_write_20kx(hw, INTERRUPT_GIE, 0);
+ hw_write_20kx(hw, GIE, 0);
/* Reset all SRC pending interrupts */
hw_write_20kx(hw, SRC_IP, 0);
@@ -2149,6 +2188,10 @@ static struct hw ct20k2_preset __devinitdata = {
.daio_mgr_set_imapnxt = daio_mgr_set_imapnxt,
.daio_mgr_set_imapaddr = daio_mgr_set_imapaddr,
.daio_mgr_commit_write = daio_mgr_commit_write,
+
+ .set_timer_irq = set_timer_irq,
+ .set_timer_tick = set_timer_tick,
+ .get_wc = get_wc,
};
int __devinit create_20k2_hw_obj(struct hw **rhw)
diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c
index f26d7cd9db9..15c1e7271ea 100644
--- a/sound/pci/ctxfi/ctmixer.c
+++ b/sound/pci/ctxfi/ctmixer.c
@@ -654,7 +654,7 @@ ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new)
int err;
kctl = snd_ctl_new1(new, mixer->atc);
- if (NULL == kctl)
+ if (!kctl)
return -ENOMEM;
if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface)
@@ -837,17 +837,17 @@ static int ct_mixer_get_mem(struct ct_mixer **rmixer)
*rmixer = NULL;
/* Allocate mem for mixer obj */
mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
- if (NULL == mixer)
+ if (!mixer)
return -ENOMEM;
mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM),
GFP_KERNEL);
- if (NULL == mixer->amixers) {
+ if (!mixer->amixers) {
err = -ENOMEM;
goto error1;
}
mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL);
- if (NULL == mixer->sums) {
+ if (!mixer->sums) {
err = -ENOMEM;
goto error2;
}
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c
index 60ea23180ac..d0dc227fbdd 100644
--- a/sound/pci/ctxfi/ctpcm.c
+++ b/sound/pci/ctxfi/ctpcm.c
@@ -97,7 +97,7 @@ static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)
{
struct ct_atc_pcm *apcm = atc_pcm;
- if (NULL == apcm->substream)
+ if (!apcm->substream)
return;
snd_pcm_period_elapsed(apcm->substream);
@@ -123,7 +123,7 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
int err;
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
- if (NULL == apcm)
+ if (!apcm)
return -ENOMEM;
apcm->substream = substream;
@@ -271,7 +271,7 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
int err;
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
- if (NULL == apcm)
+ if (!apcm)
return -ENOMEM;
apcm->started = 0;
diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c
index 889c495bb7d..7dfaf67344d 100644
--- a/sound/pci/ctxfi/ctresource.c
+++ b/sound/pci/ctxfi/ctresource.c
@@ -144,7 +144,7 @@ int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
rsc->msr = msr;
rsc->hw = hw;
rsc->ops = &rsc_generic_ops;
- if (NULL == hw) {
+ if (!hw) {
rsc->ctrl_blk = NULL;
return 0;
}
@@ -216,7 +216,7 @@ int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
mgr->type = NUM_RSCTYP;
mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL);
- if (NULL == mgr->rscs)
+ if (!mgr->rscs)
return -ENOMEM;
switch (type) {
diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c
index df43a5cd393..c749fa72088 100644
--- a/sound/pci/ctxfi/ctsrc.c
+++ b/sound/pci/ctxfi/ctsrc.c
@@ -441,7 +441,7 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
else
src = kzalloc(sizeof(*src), GFP_KERNEL);
- if (NULL == src) {
+ if (!src) {
err = -ENOMEM;
goto error1;
}
@@ -550,7 +550,7 @@ int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
*rsrc_mgr = NULL;
src_mgr = kzalloc(sizeof(*src_mgr), GFP_KERNEL);
- if (NULL == src_mgr)
+ if (!src_mgr)
return -ENOMEM;
err = rsc_mgr_init(&src_mgr->mgr, SRC, SRC_RESOURCE_NUM, hw);
@@ -679,7 +679,7 @@ static int srcimp_rsc_init(struct srcimp *srcimp,
/* Reserve memory for imapper nodes */
srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr,
GFP_KERNEL);
- if (NULL == srcimp->imappers) {
+ if (!srcimp->imappers) {
err = -ENOMEM;
goto error1;
}
@@ -833,7 +833,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
*rsrcimp_mgr = NULL;
srcimp_mgr = kzalloc(sizeof(*srcimp_mgr), GFP_KERNEL);
- if (NULL == srcimp_mgr)
+ if (!srcimp_mgr)
return -ENOMEM;
err = rsc_mgr_init(&srcimp_mgr->mgr, SRCIMP, SRCIMP_RESOURCE_NUM, hw);
@@ -844,7 +844,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
spin_lock_init(&srcimp_mgr->imap_lock);
INIT_LIST_HEAD(&srcimp_mgr->imappers);
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
- if (NULL == entry) {
+ if (!entry) {
err = -ENOMEM;
goto error2;
}
diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c
index 67665a7e43c..6b78752e950 100644
--- a/sound/pci/ctxfi/ctvmem.c
+++ b/sound/pci/ctxfi/ctvmem.c
@@ -60,7 +60,7 @@ get_vm_block(struct ct_vm *vm, unsigned int size)
}
block = kzalloc(sizeof(*block), GFP_KERNEL);
- if (NULL == block)
+ if (!block)
goto out;
block->addr = entry->addr;
@@ -181,7 +181,7 @@ int ct_vm_create(struct ct_vm **rvm)
*rvm = NULL;
vm = kzalloc(sizeof(*vm), GFP_KERNEL);
- if (NULL == vm)
+ if (!vm)
return -ENOMEM;
mutex_init(&vm->lock);
@@ -189,7 +189,7 @@ int ct_vm_create(struct ct_vm **rvm)
/* Allocate page table pages */
for (i = 0; i < CT_PTP_NUM; i++) {
vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (NULL == vm->ptp[i])
+ if (!vm->ptp[i])
break;
}
if (!i) {
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 04438f1d682..55545e0818b 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -46,6 +46,20 @@ config SND_HDA_INPUT_JACK
Say Y here to enable the jack plugging notification via
input layer.
+config SND_HDA_PATCH_LOADER
+ bool "Support initialization patch loading for HD-audio"
+ depends on EXPERIMENTAL
+ select FW_LOADER
+ select SND_HDA_HWDEP
+ select SND_HDA_RECONFIG
+ help
+ Say Y here to allow the HD-audio driver to load a pseudo
+ firmware file ("patch") for overriding the BIOS setup at
+ start up. The "patch" file can be specified via patch module
+ option, such as patch=hda-init.
+
+ This option turns on hwdep and reconfig features automatically.
+
config SND_HDA_CODEC_REALTEK
bool "Build Realtek HD-audio codec support"
default y
@@ -134,6 +148,19 @@ config SND_HDA_ELD
def_bool y
depends on SND_HDA_CODEC_INTELHDMI
+config SND_HDA_CODEC_CIRRUS
+ bool "Build Cirrus Logic codec support"
+ depends on SND_HDA_INTEL
+ default y
+ help
+ Say Y here to include Cirrus Logic codec support in
+ snd-hda-intel driver, such as CS4206.
+
+ When the HD-audio driver is built as a module, the codec
+ support code is also built as another module,
+ snd-hda-codec-cirrus.
+ This module is automatically loaded at probing.
+
config SND_HDA_CODEC_CONEXANT
bool "Build Conexant HD-audio codec support"
default y
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index e3081d4586c..315a1c4f899 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -13,6 +13,7 @@ snd-hda-codec-analog-objs := patch_analog.o
snd-hda-codec-idt-objs := patch_sigmatel.o
snd-hda-codec-si3054-objs := patch_si3054.o
snd-hda-codec-atihdmi-objs := patch_atihdmi.o
+snd-hda-codec-cirrus-objs := patch_cirrus.o
snd-hda-codec-ca0110-objs := patch_ca0110.o
snd-hda-codec-conexant-objs := patch_conexant.o
snd-hda-codec-via-objs := patch_via.o
@@ -41,6 +42,9 @@ endif
ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
endif
+ifdef CONFIG_SND_HDA_CODEC_CIRRUS
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o
+endif
ifdef CONFIG_SND_HDA_CODEC_CA0110
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
endif
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index b0275a05087..3f51a981e60 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -24,6 +24,7 @@
#include <linux/workqueue.h>
#include <sound/core.h>
#include "hda_beep.h"
+#include "hda_local.h"
enum {
DIGBEEP_HZ_STEP = 46875, /* 46.875 Hz */
@@ -118,6 +119,9 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
struct hda_beep *beep;
int err;
+ if (!snd_hda_get_bool_hint(codec, "beep"))
+ return 0; /* disabled explicitly */
+
beep = kzalloc(sizeof(*beep), GFP_KERNEL);
if (beep == NULL)
return -ENOMEM;
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 88480c0c58a..af989f660cc 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -44,6 +44,7 @@ struct hda_vendor_id {
/* codec vendor labels */
static struct hda_vendor_id hda_vendor_ids[] = {
{ 0x1002, "ATI" },
+ { 0x1013, "Cirrus Logic" },
{ 0x1057, "Motorola" },
{ 0x1095, "Silicon Image" },
{ 0x10de, "Nvidia" },
@@ -150,7 +151,14 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
{
u32 val;
- val = (u32)(codec->addr & 0x0f) << 28;
+ if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) ||
+ (verb & ~0xfff) || (parm & ~0xffff)) {
+ printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n",
+ codec->addr, direct, nid, verb, parm);
+ return ~0;
+ }
+
+ val = (u32)codec->addr << 28;
val |= (u32)direct << 27;
val |= (u32)nid << 20;
val |= verb << 8;
@@ -167,6 +175,9 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
struct hda_bus *bus = codec->bus;
int err;
+ if (cmd == ~0)
+ return -1;
+
if (res)
*res = -1;
again:
@@ -174,7 +185,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
mutex_lock(&bus->cmd_mutex);
err = bus->ops.command(bus, cmd);
if (!err && res)
- *res = bus->ops.get_response(bus);
+ *res = bus->ops.get_response(bus, codec->addr);
mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
if (res && *res == -1 && bus->rirb_error) {
@@ -291,11 +302,20 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
unsigned int parm;
int i, conn_len, conns;
unsigned int shift, num_elems, mask;
+ unsigned int wcaps;
hda_nid_t prev_nid;
if (snd_BUG_ON(!conn_list || max_conns <= 0))
return -EINVAL;
+ wcaps = get_wcaps(codec, nid);
+ if (!(wcaps & AC_WCAP_CONN_LIST) &&
+ get_wcaps_type(wcaps) != AC_WID_VOL_KNB) {
+ snd_printk(KERN_WARNING "hda_codec: "
+ "connection list not available for 0x%x\n", nid);
+ return -EINVAL;
+ }
+
parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
if (parm & AC_CLIST_LONG) {
/* long form */
@@ -316,6 +336,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
/* single connection */
parm = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONNECT_LIST, 0);
+ if (parm == -1 && codec->bus->rirb_error)
+ return -EIO;
conn_list[0] = parm & mask;
return 1;
}
@@ -327,9 +349,12 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
int range_val;
hda_nid_t val, n;
- if (i % num_elems == 0)
+ if (i % num_elems == 0) {
parm = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONNECT_LIST, i);
+ if (parm == -1 && codec->bus->rirb_error)
+ return -EIO;
+ }
range_val = !!(parm & (1 << (shift-1))); /* ranges */
val = parm & mask;
if (val == 0) {
@@ -727,8 +752,7 @@ static int read_pin_defaults(struct hda_codec *codec)
for (i = 0; i < codec->num_nodes; i++, nid++) {
struct hda_pincfg *pin;
unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
- AC_WCAP_TYPE_SHIFT;
+ unsigned int wid_type = get_wcaps_type(wcaps);
if (wid_type != AC_WID_PIN)
continue;
pin = snd_array_new(&codec->init_pins);
@@ -891,7 +915,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
* Returns 0 if successful, or a negative error code.
*/
int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
- int do_init, struct hda_codec **codecp)
+ struct hda_codec **codecp)
{
struct hda_codec *codec;
char component[31];
@@ -984,11 +1008,6 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
codec->afg ? codec->afg : codec->mfg,
AC_PWRST_D0);
- if (do_init) {
- err = snd_hda_codec_configure(codec);
- if (err < 0)
- goto error;
- }
snd_hda_codec_proc_new(codec);
snd_hda_create_hwdep(codec);
@@ -1042,6 +1061,7 @@ int snd_hda_codec_configure(struct hda_codec *codec)
err = init_unsol_queue(codec->bus);
return err;
}
+EXPORT_SYMBOL_HDA(snd_hda_codec_configure);
/**
* snd_hda_codec_setup_stream - set up the codec for streaming
@@ -2356,16 +2376,20 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
hda_nid_t nid;
int i;
- snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+ /* this delay seems necessary to avoid click noise at power-down */
+ if (power_state == AC_PWRST_D3)
+ msleep(100);
+ snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
power_state);
- msleep(10); /* partial workaround for "azx_get_response timeout" */
+ /* partial workaround for "azx_get_response timeout" */
+ if (power_state == AC_PWRST_D0)
+ msleep(10);
nid = codec->start_nid;
for (i = 0; i < codec->num_nodes; i++, nid++) {
unsigned int wcaps = get_wcaps(codec, nid);
if (wcaps & AC_WCAP_POWER) {
- unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
- AC_WCAP_TYPE_SHIFT;
+ unsigned int wid_type = get_wcaps_type(wcaps);
if (power_state == AC_PWRST_D3 &&
wid_type == AC_WID_PIN) {
unsigned int pincap;
@@ -2573,7 +2597,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
case 20:
case 24:
case 32:
- if (maxbps >= 32)
+ if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
val |= 0x40;
else if (maxbps >= 24)
val |= 0x30;
@@ -2700,11 +2724,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
bps = 20;
}
}
- else if (streams == AC_SUPFMT_FLOAT32) {
- /* should be exclusive */
+ if (streams & AC_SUPFMT_FLOAT32) {
formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
- bps = 32;
- } else if (streams == AC_SUPFMT_AC3) {
+ if (!bps)
+ bps = 32;
+ }
+ if (streams == AC_SUPFMT_AC3) {
/* should be exclusive */
/* temporary hack: we have still no proper support
* for the direct AC3 stream...
@@ -3102,7 +3127,7 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
tbl = q;
if (tbl->value >= 0 && tbl->value < num_configs) {
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
char tmp[10];
const char *model = NULL;
if (models)
@@ -3655,8 +3680,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
end_nid = codec->start_nid + codec->num_nodes;
for (nid = codec->start_nid; nid < end_nid; nid++) {
unsigned int wid_caps = get_wcaps(codec, nid);
- unsigned int wid_type =
- (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ unsigned int wid_type = get_wcaps_type(wid_caps);
unsigned int def_conf;
short assoc, loc;
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index cad79efaabc..99552fb5f75 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -568,7 +568,7 @@ struct hda_bus_ops {
/* send a single command */
int (*command)(struct hda_bus *bus, unsigned int cmd);
/* get a response from the last command */
- unsigned int (*get_response)(struct hda_bus *bus);
+ unsigned int (*get_response)(struct hda_bus *bus, unsigned int addr);
/* free the private data */
void (*private_free)(struct hda_bus *);
/* attach a PCM stream */
@@ -830,7 +830,8 @@ enum {
int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
struct hda_bus **busp);
int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
- int do_init, struct hda_codec **codecp);
+ struct hda_codec **codecp);
+int snd_hda_codec_configure(struct hda_codec *codec);
/*
* low level functions
@@ -938,6 +939,13 @@ static inline void snd_hda_power_down(struct hda_codec *codec) {}
#define snd_hda_codec_needs_resume(codec) 1
#endif
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+/*
+ * patch firmware
+ */
+int snd_hda_load_patch(struct hda_bus *bus, const char *patch);
+#endif
+
/*
* Codec modularization
*/
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 1d5797a9668..b36f6c5a92d 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -121,11 +121,17 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid
if (node == NULL)
return -ENOMEM;
node->nid = nid;
- nconns = snd_hda_get_connections(codec, nid, conn_list,
- HDA_MAX_CONNECTIONS);
- if (nconns < 0) {
- kfree(node);
- return nconns;
+ node->wid_caps = get_wcaps(codec, nid);
+ node->type = get_wcaps_type(node->wid_caps);
+ if (node->wid_caps & AC_WCAP_CONN_LIST) {
+ nconns = snd_hda_get_connections(codec, nid, conn_list,
+ HDA_MAX_CONNECTIONS);
+ if (nconns < 0) {
+ kfree(node);
+ return nconns;
+ }
+ } else {
+ nconns = 0;
}
if (nconns <= ARRAY_SIZE(node->slist))
node->conn_list = node->slist;
@@ -140,8 +146,6 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid
}
memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t));
node->nconns = nconns;
- node->wid_caps = get_wcaps(codec, nid);
- node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
if (node->type == AC_WID_PIN) {
node->pin_caps = snd_hda_query_pin_caps(codec, node->nid);
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index 6812fbe80fa..cc24e6721d7 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -24,6 +24,7 @@
#include <linux/compat.h>
#include <linux/mutex.h>
#include <linux/ctype.h>
+#include <linux/firmware.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
@@ -312,12 +313,8 @@ static ssize_t init_verbs_show(struct device *dev,
return len;
}
-static ssize_t init_verbs_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static int parse_init_verbs(struct hda_codec *codec, const char *buf)
{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
struct hda_verb *v;
int nid, verb, param;
@@ -331,6 +328,18 @@ static ssize_t init_verbs_store(struct device *dev,
v->nid = nid;
v->verb = verb;
v->param = param;
+ return 0;
+}
+
+static ssize_t init_verbs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ int err = parse_init_verbs(codec, buf);
+ if (err < 0)
+ return err;
return count;
}
@@ -376,19 +385,15 @@ static void remove_trail_spaces(char *str)
#define MAX_HINTS 1024
-static ssize_t hints_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static int parse_hints(struct hda_codec *codec, const char *buf)
{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
char *key, *val;
struct hda_hint *hint;
while (isspace(*buf))
buf++;
if (!*buf || *buf == '#' || *buf == '\n')
- return count;
+ return 0;
if (*buf == '=')
return -EINVAL;
key = kstrndup_noeol(buf, 1024);
@@ -411,7 +416,7 @@ static ssize_t hints_store(struct device *dev,
kfree(hint->key);
hint->key = key;
hint->val = val;
- return count;
+ return 0;
}
/* allocate a new hint entry */
if (codec->hints.used >= MAX_HINTS)
@@ -424,6 +429,18 @@ static ssize_t hints_store(struct device *dev,
}
hint->key = key;
hint->val = val;
+ return 0;
+}
+
+static ssize_t hints_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ int err = parse_hints(codec, buf);
+ if (err < 0)
+ return err;
return count;
}
@@ -469,20 +486,24 @@ static ssize_t driver_pin_configs_show(struct device *dev,
#define MAX_PIN_CONFIGS 32
-static ssize_t user_pin_configs_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
int nid, cfg;
- int err;
if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
return -EINVAL;
if (!nid)
return -EINVAL;
- err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
+ return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
+}
+
+static ssize_t user_pin_configs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ int err = parse_user_pin_configs(codec, buf);
if (err < 0)
return err;
return count;
@@ -553,3 +574,180 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);
#endif /* CONFIG_SND_HDA_RECONFIG */
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+
+/* parser mode */
+enum {
+ LINE_MODE_NONE,
+ LINE_MODE_CODEC,
+ LINE_MODE_MODEL,
+ LINE_MODE_PINCFG,
+ LINE_MODE_VERB,
+ LINE_MODE_HINT,
+ NUM_LINE_MODES,
+};
+
+static inline int strmatch(const char *a, const char *b)
+{
+ return strnicmp(a, b, strlen(b)) == 0;
+}
+
+/* parse the contents after the line "[codec]"
+ * accept only the line with three numbers, and assign the current codec
+ */
+static void parse_codec_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ unsigned int vendorid, subid, caddr;
+ struct hda_codec *codec;
+
+ *codecp = NULL;
+ if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
+ list_for_each_entry(codec, &bus->codec_list, list) {
+ if (codec->addr == caddr) {
+ *codecp = codec;
+ break;
+ }
+ }
+ }
+}
+
+/* parse the contents after the other command tags, [pincfg], [verb],
+ * [hint] and [model]
+ * just pass to the sysfs helper (only when any codec was specified)
+ */
+static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ if (!*codecp)
+ return;
+ parse_user_pin_configs(*codecp, buf);
+}
+
+static void parse_verb_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ if (!*codecp)
+ return;
+ parse_init_verbs(*codecp, buf);
+}
+
+static void parse_hint_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ if (!*codecp)
+ return;
+ parse_hints(*codecp, buf);
+}
+
+static void parse_model_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ if (!*codecp)
+ return;
+ kfree((*codecp)->modelname);
+ (*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
+}
+
+struct hda_patch_item {
+ const char *tag;
+ void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
+};
+
+static struct hda_patch_item patch_items[NUM_LINE_MODES] = {
+ [LINE_MODE_CODEC] = { "[codec]", parse_codec_mode },
+ [LINE_MODE_MODEL] = { "[model]", parse_model_mode },
+ [LINE_MODE_VERB] = { "[verb]", parse_verb_mode },
+ [LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode },
+ [LINE_MODE_HINT] = { "[hint]", parse_hint_mode },
+};
+
+/* check the line starting with '[' -- change the parser mode accodingly */
+static int parse_line_mode(char *buf, struct hda_bus *bus)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
+ if (!patch_items[i].tag)
+ continue;
+ if (strmatch(buf, patch_items[i].tag))
+ return i;
+ }
+ return LINE_MODE_NONE;
+}
+
+/* copy one line from the buffer in fw, and update the fields in fw
+ * return zero if it reaches to the end of the buffer, or non-zero
+ * if successfully copied a line
+ *
+ * the spaces at the beginning and the end of the line are stripped
+ */
+static int get_line_from_fw(char *buf, int size, struct firmware *fw)
+{
+ int len;
+ const char *p = fw->data;
+ while (isspace(*p) && fw->size) {
+ p++;
+ fw->size--;
+ }
+ if (!fw->size)
+ return 0;
+ if (size < fw->size)
+ size = fw->size;
+
+ for (len = 0; len < fw->size; len++) {
+ if (!*p)
+ break;
+ if (*p == '\n') {
+ p++;
+ len++;
+ break;
+ }
+ if (len < size)
+ *buf++ = *p++;
+ }
+ *buf = 0;
+ fw->size -= len;
+ fw->data = p;
+ remove_trail_spaces(buf);
+ return 1;
+}
+
+/*
+ * load a "patch" firmware file and parse it
+ */
+int snd_hda_load_patch(struct hda_bus *bus, const char *patch)
+{
+ int err;
+ const struct firmware *fw;
+ struct firmware tmp;
+ char buf[128];
+ struct hda_codec *codec;
+ int line_mode;
+ struct device *dev = bus->card->dev;
+
+ if (snd_BUG_ON(!dev))
+ return -ENODEV;
+ err = request_firmware(&fw, patch, dev);
+ if (err < 0) {
+ printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n",
+ patch);
+ return err;
+ }
+
+ tmp = *fw;
+ line_mode = LINE_MODE_NONE;
+ codec = NULL;
+ while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) {
+ if (!*buf || *buf == '#' || *buf == '\n')
+ continue;
+ if (*buf == '[')
+ line_mode = parse_line_mode(buf, bus);
+ else if (patch_items[line_mode].parser)
+ patch_items[line_mode].parser(buf, bus, &codec);
+ }
+ release_firmware(fw);
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_load_patch);
+#endif /* CONFIG_SND_HDA_PATCH_LOADER */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 77c1b840ca8..20a66f85f0a 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -61,6 +61,9 @@ static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_only[SNDRV_CARDS];
static int single_cmd;
static int enable_msi;
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+static char *patch[SNDRV_CARDS];
+#endif
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@@ -84,6 +87,10 @@ MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
"(for debugging only).");
module_param(enable_msi, int, 0444);
MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+module_param_array(patch, charp, NULL, 0444);
+MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
+#endif
#ifdef CONFIG_SND_HDA_POWER_SAVE
static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
@@ -253,7 +260,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
/* STATESTS int mask: S3,SD2,SD1,SD0 */
#define AZX_MAX_CODECS 4
-#define STATESTS_INT_MASK 0x0f
+#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
/* SD_CTL bits */
#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
@@ -361,8 +368,8 @@ struct azx_rb {
dma_addr_t addr; /* physical address of CORB/RIRB buffer */
/* for RIRB */
unsigned short rp, wp; /* read/write pointers */
- int cmds; /* number of pending requests */
- u32 res; /* last read value */
+ int cmds[AZX_MAX_CODECS]; /* number of pending requests */
+ u32 res[AZX_MAX_CODECS]; /* last read value */
};
struct azx {
@@ -418,7 +425,7 @@ struct azx {
unsigned int probing :1; /* codec probing phase */
/* for debugging */
- unsigned int last_cmd; /* last issued command (to sync) */
+ unsigned int last_cmd[AZX_MAX_CODECS];
/* for pending irqs */
struct work_struct irq_pending_work;
@@ -513,6 +520,7 @@ static int azx_alloc_cmd_io(struct azx *chip)
static void azx_init_cmd_io(struct azx *chip)
{
+ spin_lock_irq(&chip->reg_lock);
/* CORB set up */
chip->corb.addr = chip->rb.addr;
chip->corb.buf = (u32 *)chip->rb.area;
@@ -531,7 +539,8 @@ static void azx_init_cmd_io(struct azx *chip)
/* RIRB set up */
chip->rirb.addr = chip->rb.addr + 2048;
chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
- chip->rirb.wp = chip->rirb.rp = chip->rirb.cmds = 0;
+ chip->rirb.wp = chip->rirb.rp = 0;
+ memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds));
azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
@@ -543,30 +552,60 @@ static void azx_init_cmd_io(struct azx *chip)
azx_writew(chip, RINTCNT, 1);
/* enable rirb dma and response irq */
azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
+ spin_unlock_irq(&chip->reg_lock);
}
static void azx_free_cmd_io(struct azx *chip)
{
+ spin_lock_irq(&chip->reg_lock);
/* disable ringbuffer DMAs */
azx_writeb(chip, RIRBCTL, 0);
azx_writeb(chip, CORBCTL, 0);
+ spin_unlock_irq(&chip->reg_lock);
+}
+
+static unsigned int azx_command_addr(u32 cmd)
+{
+ unsigned int addr = cmd >> 28;
+
+ if (addr >= AZX_MAX_CODECS) {
+ snd_BUG();
+ addr = 0;
+ }
+
+ return addr;
+}
+
+static unsigned int azx_response_addr(u32 res)
+{
+ unsigned int addr = res & 0xf;
+
+ if (addr >= AZX_MAX_CODECS) {
+ snd_BUG();
+ addr = 0;
+ }
+
+ return addr;
}
/* send a command */
static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
{
struct azx *chip = bus->private_data;
+ unsigned int addr = azx_command_addr(val);
unsigned int wp;
+ spin_lock_irq(&chip->reg_lock);
+
/* add command to corb */
wp = azx_readb(chip, CORBWP);
wp++;
wp %= ICH6_MAX_CORB_ENTRIES;
- spin_lock_irq(&chip->reg_lock);
- chip->rirb.cmds++;
+ chip->rirb.cmds[addr]++;
chip->corb.buf[wp] = cpu_to_le32(val);
azx_writel(chip, CORBWP, wp);
+
spin_unlock_irq(&chip->reg_lock);
return 0;
@@ -578,13 +617,14 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
static void azx_update_rirb(struct azx *chip)
{
unsigned int rp, wp;
+ unsigned int addr;
u32 res, res_ex;
wp = azx_readb(chip, RIRBWP);
if (wp == chip->rirb.wp)
return;
chip->rirb.wp = wp;
-
+
while (chip->rirb.rp != wp) {
chip->rirb.rp++;
chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES;
@@ -592,18 +632,24 @@ static void azx_update_rirb(struct azx *chip)
rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
res = le32_to_cpu(chip->rirb.buf[rp]);
+ addr = azx_response_addr(res_ex);
if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
snd_hda_queue_unsol_event(chip->bus, res, res_ex);
- else if (chip->rirb.cmds) {
- chip->rirb.res = res;
+ else if (chip->rirb.cmds[addr]) {
+ chip->rirb.res[addr] = res;
smp_wmb();
- chip->rirb.cmds--;
- }
+ chip->rirb.cmds[addr]--;
+ } else
+ snd_printk(KERN_ERR SFX "spurious response %#x:%#x, "
+ "last cmd=%#08x\n",
+ res, res_ex,
+ chip->last_cmd[addr]);
}
}
/* receive a response */
-static unsigned int azx_rirb_get_response(struct hda_bus *bus)
+static unsigned int azx_rirb_get_response(struct hda_bus *bus,
+ unsigned int addr)
{
struct azx *chip = bus->private_data;
unsigned long timeout;
@@ -616,10 +662,10 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
azx_update_rirb(chip);
spin_unlock_irq(&chip->reg_lock);
}
- if (!chip->rirb.cmds) {
+ if (!chip->rirb.cmds[addr]) {
smp_rmb();
bus->rirb_error = 0;
- return chip->rirb.res; /* the last value */
+ return chip->rirb.res[addr]; /* the last value */
}
if (time_after(jiffies, timeout))
break;
@@ -633,7 +679,8 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
if (chip->msi) {
snd_printk(KERN_WARNING SFX "No response from codec, "
- "disabling MSI: last cmd=0x%08x\n", chip->last_cmd);
+ "disabling MSI: last cmd=0x%08x\n",
+ chip->last_cmd[addr]);
free_irq(chip->irq, chip);
chip->irq = -1;
pci_disable_msi(chip->pci);
@@ -648,7 +695,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
if (!chip->polling_mode) {
snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
"switching to polling mode: last cmd=0x%08x\n",
- chip->last_cmd);
+ chip->last_cmd[addr]);
chip->polling_mode = 1;
goto again;
}
@@ -672,7 +719,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
"switching to single_cmd mode: last cmd=0x%08x\n",
- chip->last_cmd);
+ chip->last_cmd[addr]);
chip->single_cmd = 1;
bus->response_reset = 0;
/* re-initialize CORB/RIRB */
@@ -692,7 +739,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
*/
/* receive a response */
-static int azx_single_wait_for_response(struct azx *chip)
+static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
{
int timeout = 50;
@@ -700,7 +747,7 @@ static int azx_single_wait_for_response(struct azx *chip)
/* check IRV busy bit */
if (azx_readw(chip, IRS) & ICH6_IRS_VALID) {
/* reuse rirb.res as the response return value */
- chip->rirb.res = azx_readl(chip, IR);
+ chip->rirb.res[addr] = azx_readl(chip, IR);
return 0;
}
udelay(1);
@@ -708,7 +755,7 @@ static int azx_single_wait_for_response(struct azx *chip)
if (printk_ratelimit())
snd_printd(SFX "get_response timeout: IRS=0x%x\n",
azx_readw(chip, IRS));
- chip->rirb.res = -1;
+ chip->rirb.res[addr] = -1;
return -EIO;
}
@@ -716,6 +763,7 @@ static int azx_single_wait_for_response(struct azx *chip)
static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
{
struct azx *chip = bus->private_data;
+ unsigned int addr = azx_command_addr(val);
int timeout = 50;
bus->rirb_error = 0;
@@ -728,7 +776,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
azx_writel(chip, IC, val);
azx_writew(chip, IRS, azx_readw(chip, IRS) |
ICH6_IRS_BUSY);
- return azx_single_wait_for_response(chip);
+ return azx_single_wait_for_response(chip, addr);
}
udelay(1);
}
@@ -739,10 +787,11 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
}
/* receive a response */
-static unsigned int azx_single_get_response(struct hda_bus *bus)
+static unsigned int azx_single_get_response(struct hda_bus *bus,
+ unsigned int addr)
{
struct azx *chip = bus->private_data;
- return chip->rirb.res;
+ return chip->rirb.res[addr];
}
/*
@@ -757,7 +806,7 @@ static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
{
struct azx *chip = bus->private_data;
- chip->last_cmd = val;
+ chip->last_cmd[azx_command_addr(val)] = val;
if (chip->single_cmd)
return azx_single_send_cmd(bus, val);
else
@@ -765,13 +814,14 @@ static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
}
/* get a response */
-static unsigned int azx_get_response(struct hda_bus *bus)
+static unsigned int azx_get_response(struct hda_bus *bus,
+ unsigned int addr)
{
struct azx *chip = bus->private_data;
if (chip->single_cmd)
- return azx_single_get_response(bus);
+ return azx_single_get_response(bus, addr);
else
- return azx_rirb_get_response(bus);
+ return azx_rirb_get_response(bus, addr);
}
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -1243,10 +1293,12 @@ static int probe_codec(struct azx *chip, int addr)
(AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
unsigned int res;
+ mutex_lock(&chip->bus->cmd_mutex);
chip->probing = 1;
azx_send_cmd(chip->bus, cmd);
- res = azx_get_response(chip->bus);
+ res = azx_get_response(chip->bus, addr);
chip->probing = 0;
+ mutex_unlock(&chip->bus->cmd_mutex);
if (res == -1)
return -EIO;
snd_printdd(SFX "codec #%d probed OK\n", addr);
@@ -1286,8 +1338,7 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
[AZX_DRIVER_TERA] = 1,
};
-static int __devinit azx_codec_create(struct azx *chip, const char *model,
- int no_init)
+static int __devinit azx_codec_create(struct azx *chip, const char *model)
{
struct hda_bus_template bus_temp;
int c, codecs, err;
@@ -1346,7 +1397,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
for (c = 0; c < max_slots; c++) {
if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
struct hda_codec *codec;
- err = snd_hda_codec_new(chip->bus, c, !no_init, &codec);
+ err = snd_hda_codec_new(chip->bus, c, &codec);
if (err < 0)
continue;
codecs++;
@@ -1356,7 +1407,16 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
snd_printk(KERN_ERR SFX "no codecs initialized\n");
return -ENXIO;
}
+ return 0;
+}
+/* configure each codec instance */
+static int __devinit azx_codec_configure(struct azx *chip)
+{
+ struct hda_codec *codec;
+ list_for_each_entry(codec, &chip->bus->codec_list, list) {
+ snd_hda_codec_configure(codec);
+ }
return 0;
}
@@ -2239,6 +2299,30 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
}
}
+/*
+ * white-list for enable_msi
+ */
+static struct snd_pci_quirk msi_white_list[] __devinitdata = {
+ SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1),
+ {}
+};
+
+static void __devinit check_msi(struct azx *chip)
+{
+ const struct snd_pci_quirk *q;
+
+ chip->msi = enable_msi;
+ if (chip->msi)
+ return;
+ q = snd_pci_quirk_lookup(chip->pci, msi_white_list);
+ if (q) {
+ printk(KERN_INFO
+ "hda_intel: msi for device %04x:%04x set to %d\n",
+ q->subvendor, q->subdevice, q->value);
+ chip->msi = q->value;
+ }
+}
+
/*
* constructor
@@ -2273,7 +2357,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->pci = pci;
chip->irq = -1;
chip->driver_type = driver_type;
- chip->msi = enable_msi;
+ check_msi(chip);
chip->dev_index = dev;
INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
@@ -2481,15 +2565,32 @@ static int __devinit azx_probe(struct pci_dev *pci,
return err;
}
+ /* set this here since it's referred in snd_hda_load_patch() */
+ snd_card_set_dev(card, &pci->dev);
+
err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
if (err < 0)
goto out_free;
card->private_data = chip;
/* create codec instances */
- err = azx_codec_create(chip, model[dev], probe_only[dev]);
+ err = azx_codec_create(chip, model[dev]);
if (err < 0)
goto out_free;
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ if (patch[dev]) {
+ snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n",
+ patch[dev]);
+ err = snd_hda_load_patch(chip->bus, patch[dev]);
+ if (err < 0)
+ goto out_free;
+ }
+#endif
+ if (!probe_only[dev]) {
+ err = azx_codec_configure(chip);
+ if (err < 0)
+ goto out_free;
+ }
/* create PCM streams */
err = snd_hda_build_pcms(chip->bus);
@@ -2501,8 +2602,6 @@ static int __devinit azx_probe(struct pci_dev *pci,
if (err < 0)
goto out_free;
- snd_card_set_dev(card, &pci->dev);
-
err = snd_card_register(card);
if (err < 0)
goto out_free;
@@ -2604,11 +2703,15 @@ static struct pci_device_id azx_ids[] = {
/* this entry seems still valid -- i.e. without emu20kx chip */
{ PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC },
#endif
- /* AMD Generic, PCI class code and Vendor ID for HD Audio */
+ /* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
.driver_data = AZX_DRIVER_GENERIC },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_ANY_ID),
+ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+ .class_mask = 0xffffff,
+ .driver_data = AZX_DRIVER_GENERIC },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, azx_ids);
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 83349013b4d..5f1dcc59002 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -99,7 +99,6 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
unsigned int *tlv, const char **slaves);
int snd_hda_codec_reset(struct hda_codec *codec);
-int snd_hda_codec_configure(struct hda_codec *codec);
/* amp value bits */
#define HDA_AMP_MUTE 0x80
@@ -408,6 +407,19 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
return codec->wcaps[nid - codec->start_nid];
}
+/* get the widget type from widget capability bits */
+#define get_wcaps_type(wcaps) (((wcaps) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT)
+
+static inline unsigned int get_wcaps_channels(u32 wcaps)
+{
+ unsigned int chans;
+
+ chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13;
+ chans = ((chans << 1) | 1) + 1;
+
+ return chans;
+}
+
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 418c5d1bada..95f24e4729f 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -508,17 +508,14 @@ static void print_codec_info(struct snd_info_entry *entry,
unsigned int wid_caps =
snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP);
- unsigned int wid_type =
- (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ unsigned int wid_type = get_wcaps_type(wid_caps);
hda_nid_t conn[HDA_MAX_CONNECTIONS];
int conn_len = 0;
snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
get_wid_type_name(wid_type), wid_caps);
if (wid_caps & AC_WCAP_STEREO) {
- unsigned int chans;
- chans = (wid_caps & AC_WCAP_CHAN_CNT_EXT) >> 13;
- chans = ((chans << 1) | 1) + 1;
+ unsigned int chans = get_wcaps_channels(wid_caps);
if (chans == 2)
snd_iprintf(buffer, " Stereo");
else
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 3da85caf8af..215e72a8711 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -2982,7 +2982,8 @@ static int patch_ad1988(struct hda_codec *codec)
board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
ad1988_models, ad1988_cfg_tbl);
if (board_config < 0) {
- printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
+ printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
board_config = AD1988_AUTO;
}
@@ -3702,19 +3703,29 @@ static struct hda_amp_list ad1884a_loopbacks[] = {
* Port F: Internal speakers
*/
-static struct hda_input_mux ad1884a_laptop_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 }, /* port-B */
- { "Internal Mic", 0x1 }, /* port-C */
- { "Dock Mic", 0x4 }, /* port-E */
- { "Mix", 0x3 },
- },
-};
+static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ int mute = (!ucontrol->value.integer.value[0] &&
+ !ucontrol->value.integer.value[1]);
+ /* toggle GPIO1 according to the mute state */
+ snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ mute ? 0x02 : 0x0);
+ return ret;
+}
static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = ad1884a_mobile_master_sw_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
+ },
HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
@@ -3729,36 +3740,9 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
{ } /* end */
};
-static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- int mute = (!ucontrol->value.integer.value[0] &&
- !ucontrol->value.integer.value[1]);
- /* toggle GPIO1 according to the mute state */
- snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- mute ? 0x02 : 0x0);
- return ret;
-}
-
static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
/*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
@@ -3828,6 +3812,63 @@ static int ad1884a_hp_init(struct hda_codec *codec)
return 0;
}
+/* mute internal speaker if HP or docking HP is plugged */
+static void ad1884a_laptop_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0);
+ present &= AC_PINSENSE_PRESENCE;
+ if (!present) {
+ present = snd_hda_codec_read(codec, 0x12, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ present &= AC_PINSENSE_PRESENCE;
+ }
+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ present ? 0x00 : 0x02);
+}
+
+/* switch to external mic if plugged */
+static void ad1884a_laptop_automic(struct hda_codec *codec)
+{
+ unsigned int idx;
+
+ if (snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) &
+ AC_PINSENSE_PRESENCE)
+ idx = 0;
+ else if (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) &
+ AC_PINSENSE_PRESENCE)
+ idx = 4;
+ else
+ idx = 1;
+ snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
+}
+
+/* unsolicited event for HP jack sensing */
+static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case AD1884A_HP_EVENT:
+ ad1884a_laptop_automute(codec);
+ break;
+ case AD1884A_MIC_EVENT:
+ ad1884a_laptop_automic(codec);
+ break;
+ }
+}
+
+/* initialize jack-sensing, too */
+static int ad1884a_laptop_init(struct hda_codec *codec)
+{
+ ad198x_init(codec);
+ ad1884a_laptop_automute(codec);
+ ad1884a_laptop_automic(codec);
+ return 0;
+}
+
/* additional verbs for laptop model */
static struct hda_verb ad1884a_laptop_verbs[] = {
/* Port-A (HP) pin - always unmuted */
@@ -3835,18 +3876,28 @@ static struct hda_verb ad1884a_laptop_verbs[] = {
/* Port-F (int speaker) mixer - route only from analog mixer */
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-F pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ /* Port-F (int speaker) pin */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* required for compaq 6530s/6531s speaker output */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
/* Port-C pin - internal mic-in */
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
+ /* Port-D (docking line-out) pin - default unmuted */
+ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
/* analog mix */
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
/* unsolicited event for pin-sense */
{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
+ {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
+ {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
+ /* allow to touch GPIO1 (for mute control) */
+ {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
+ {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
{ } /* end */
};
@@ -4006,6 +4057,7 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
+ SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
{}
};
@@ -4055,9 +4107,8 @@ static int patch_ad1884a(struct hda_codec *codec)
spec->mixers[0] = ad1884a_laptop_mixers;
spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1884a_laptop_capture_source;
- codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
- codec->patch_ops.init = ad1884a_hp_init;
+ codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
+ codec->patch_ops.init = ad1884a_laptop_init;
/* set the upper-limit for mixer amp to 0dB for avoiding the
* possible damage by overloading
*/
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
index 233e4778bba..fb684f00156 100644
--- a/sound/pci/hda/patch_atihdmi.c
+++ b/sound/pci/hda/patch_atihdmi.c
@@ -141,8 +141,7 @@ static int atihdmi_build_pcms(struct hda_codec *codec)
/* FIXME: we must check ELD and change the PCM parameters dynamically
*/
chans = get_wcaps(codec, CVT_NID);
- chans = (chans & AC_WCAP_CHAN_CNT_EXT) >> 13;
- chans = ((chans << 1) | 1) + 1;
+ chans = get_wcaps_channels(chans);
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
return 0;
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index 019ca7cb56d..d08353d3bb7 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -459,8 +459,7 @@ static void parse_input(struct hda_codec *codec)
nid = codec->start_nid;
for (i = 0; i < codec->num_nodes; i++, nid++) {
unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int type = (wcaps & AC_WCAP_TYPE) >>
- AC_WCAP_TYPE_SHIFT;
+ unsigned int type = get_wcaps_type(wcaps);
if (type != AC_WID_AUD_IN)
continue;
if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
new file mode 100644
index 00000000000..8ba306856d3
--- /dev/null
+++ b/sound/pci/hda/patch_cirrus.c
@@ -0,0 +1,1194 @@
+/*
+ * HD audio interface patch for Cirrus Logic CS420x chip
+ *
+ * Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+/*
+ */
+
+struct cs_spec {
+ int board_config;
+ struct auto_pin_cfg autocfg;
+ struct hda_multi_out multiout;
+ struct snd_kcontrol *vmaster_sw;
+ struct snd_kcontrol *vmaster_vol;
+
+ hda_nid_t dac_nid[AUTO_CFG_MAX_OUTS];
+ hda_nid_t slave_dig_outs[2];
+
+ unsigned int input_idx[AUTO_PIN_LAST];
+ unsigned int capsrc_idx[AUTO_PIN_LAST];
+ hda_nid_t adc_nid[AUTO_PIN_LAST];
+ unsigned int adc_idx[AUTO_PIN_LAST];
+ unsigned int num_inputs;
+ unsigned int cur_input;
+ unsigned int automic_idx;
+ hda_nid_t cur_adc;
+ unsigned int cur_adc_stream_tag;
+ unsigned int cur_adc_format;
+ hda_nid_t dig_in;
+
+ struct hda_bind_ctls *capture_bind[2];
+
+ unsigned int gpio_mask;
+ unsigned int gpio_dir;
+ unsigned int gpio_data;
+
+ struct hda_pcm pcm_rec[2]; /* PCM information */
+
+ unsigned int hp_detect:1;
+ unsigned int mic_detect:1;
+};
+
+/* available models */
+enum {
+ CS420X_MBP55,
+ CS420X_AUTO,
+ CS420X_MODELS
+};
+
+/* Vendor-specific processing widget */
+#define CS420X_VENDOR_NID 0x11
+#define CS_DIG_OUT1_PIN_NID 0x10
+#define CS_DIG_OUT2_PIN_NID 0x15
+#define CS_DMIC1_PIN_NID 0x12
+#define CS_DMIC2_PIN_NID 0x0e
+
+/* coef indices */
+#define IDX_SPDIF_STAT 0x0000
+#define IDX_SPDIF_CTL 0x0001
+#define IDX_ADC_CFG 0x0002
+/* SZC bitmask, 4 modes below:
+ * 0 = immediate,
+ * 1 = digital immediate, analog zero-cross
+ * 2 = digtail & analog soft-ramp
+ * 3 = digital soft-ramp, analog zero-cross
+ */
+#define CS_COEF_ADC_SZC_MASK (3 << 0)
+#define CS_COEF_ADC_MIC_SZC_MODE (3 << 0) /* SZC setup for mic */
+#define CS_COEF_ADC_LI_SZC_MODE (3 << 0) /* SZC setup for line-in */
+/* PGA mode: 0 = differential, 1 = signle-ended */
+#define CS_COEF_ADC_MIC_PGA_MODE (1 << 5) /* PGA setup for mic */
+#define CS_COEF_ADC_LI_PGA_MODE (1 << 6) /* PGA setup for line-in */
+#define IDX_DAC_CFG 0x0003
+/* SZC bitmask, 4 modes below:
+ * 0 = Immediate
+ * 1 = zero-cross
+ * 2 = soft-ramp
+ * 3 = soft-ramp on zero-cross
+ */
+#define CS_COEF_DAC_HP_SZC_MODE (3 << 0) /* nid 0x02 */
+#define CS_COEF_DAC_LO_SZC_MODE (3 << 2) /* nid 0x03 */
+#define CS_COEF_DAC_SPK_SZC_MODE (3 << 4) /* nid 0x04 */
+
+#define IDX_BEEP_CFG 0x0004
+/* 0x0008 - test reg key */
+/* 0x0009 - 0x0014 -> 12 test regs */
+/* 0x0015 - visibility reg */
+
+
+static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
+{
+ snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+ AC_VERB_SET_COEF_INDEX, idx);
+ return snd_hda_codec_read(codec, CS420X_VENDOR_NID, 0,
+ AC_VERB_GET_PROC_COEF, 0);
+}
+
+static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
+ unsigned int coef)
+{
+ snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+ AC_VERB_SET_COEF_INDEX, idx);
+ snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+ AC_VERB_SET_PROC_COEF, coef);
+}
+
+
+#define HP_EVENT 1
+#define MIC_EVENT 2
+
+/*
+ * PCM callbacks
+ */
+static int cs_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
+}
+
+static int cs_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int cs_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int cs_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int cs_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int cs_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+ format, substream);
+}
+
+static int cs_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int cs_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ spec->cur_adc = spec->adc_nid[spec->cur_input];
+ spec->cur_adc_stream_tag = stream_tag;
+ spec->cur_adc_format = format;
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+ return 0;
+}
+
+static int cs_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+ spec->cur_adc = 0;
+ return 0;
+}
+
+/*
+ */
+static struct hda_pcm_stream cs_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = cs_playback_pcm_open,
+ .prepare = cs_playback_pcm_prepare,
+ .cleanup = cs_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream cs_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .prepare = cs_capture_pcm_prepare,
+ .cleanup = cs_capture_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream cs_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = cs_dig_playback_pcm_open,
+ .close = cs_dig_playback_pcm_close,
+ .prepare = cs_dig_playback_pcm_prepare,
+ .cleanup = cs_dig_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream cs_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+static int cs_build_pcms(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+
+ codec->pcm_info = info;
+ codec->num_pcms = 0;
+
+ info->name = "Cirrus Analog";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cs_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dac_nid[0];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = cs_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+ spec->adc_nid[spec->cur_input];
+ codec->num_pcms++;
+
+ if (!spec->multiout.dig_out_nid && !spec->dig_in)
+ return 0;
+
+ info++;
+ info->name = "Cirrus Digital";
+ info->pcm_type = spec->autocfg.dig_out_type[0];
+ if (!info->pcm_type)
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->multiout.dig_out_nid) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ cs_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->multiout.dig_out_nid;
+ }
+ if (spec->dig_in) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ cs_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+ }
+ codec->num_pcms++;
+
+ return 0;
+}
+
+/*
+ * parse codec topology
+ */
+
+static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin)
+{
+ hda_nid_t dac;
+ if (!pin)
+ return 0;
+ if (snd_hda_get_connections(codec, pin, &dac, 1) != 1)
+ return 0;
+ return dac;
+}
+
+static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t pin = cfg->input_pins[idx];
+ unsigned int val = snd_hda_query_pin_caps(codec, pin);
+ if (!(val & AC_PINCAP_PRES_DETECT))
+ return 0;
+ val = snd_hda_codec_get_pincfg(codec, pin);
+ return (get_defcfg_connect(val) == AC_JACK_PORT_COMPLEX);
+}
+
+static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int *idxp)
+{
+ int i;
+ hda_nid_t nid;
+
+ nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ hda_nid_t pins[2];
+ unsigned int type;
+ int j, nums;
+ type = (get_wcaps(codec, nid) & AC_WCAP_TYPE)
+ >> AC_WCAP_TYPE_SHIFT;
+ if (type != AC_WID_AUD_IN)
+ continue;
+ nums = snd_hda_get_connections(codec, nid, pins,
+ ARRAY_SIZE(pins));
+ if (nums <= 0)
+ continue;
+ for (j = 0; j < nums; j++) {
+ if (pins[j] == pin) {
+ *idxp = j;
+ return nid;
+ }
+ }
+ }
+ return 0;
+}
+
+static int is_active_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int val;
+ val = snd_hda_codec_get_pincfg(codec, nid);
+ return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
+}
+
+static int parse_output(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, extra_nids;
+ hda_nid_t dac;
+
+ for (i = 0; i < cfg->line_outs; i++) {
+ dac = get_dac(codec, cfg->line_out_pins[i]);
+ if (!dac)
+ break;
+ spec->dac_nid[i] = dac;
+ }
+ spec->multiout.num_dacs = i;
+ spec->multiout.dac_nids = spec->dac_nid;
+ spec->multiout.max_channels = i * 2;
+
+ /* add HP and speakers */
+ extra_nids = 0;
+ for (i = 0; i < cfg->hp_outs; i++) {
+ dac = get_dac(codec, cfg->hp_pins[i]);
+ if (!dac)
+ break;
+ if (!i)
+ spec->multiout.hp_nid = dac;
+ else
+ spec->multiout.extra_out_nid[extra_nids++] = dac;
+ }
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ dac = get_dac(codec, cfg->speaker_pins[i]);
+ if (!dac)
+ break;
+ spec->multiout.extra_out_nid[extra_nids++] = dac;
+ }
+
+ if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ cfg->speaker_outs = cfg->line_outs;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->line_outs = 0;
+ }
+
+ return 0;
+}
+
+static int parse_input(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ hda_nid_t pin = cfg->input_pins[i];
+ if (!pin)
+ continue;
+ spec->input_idx[spec->num_inputs] = i;
+ spec->capsrc_idx[i] = spec->num_inputs++;
+ spec->cur_input = i;
+ spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]);
+ }
+ if (!spec->num_inputs)
+ return 0;
+
+ /* check whether the automatic mic switch is available */
+ if (spec->num_inputs == 2 &&
+ spec->adc_nid[AUTO_PIN_MIC] && spec->adc_nid[AUTO_PIN_FRONT_MIC]) {
+ if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_FRONT_MIC])) {
+ if (!is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+ spec->mic_detect = 1;
+ spec->automic_idx = AUTO_PIN_FRONT_MIC;
+ }
+ } else {
+ if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+ spec->mic_detect = 1;
+ spec->automic_idx = AUTO_PIN_MIC;
+ }
+ }
+ }
+ return 0;
+}
+
+
+static int parse_digital_output(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
+
+ if (!cfg->dig_outs)
+ return 0;
+ if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1)
+ return 0;
+ spec->multiout.dig_out_nid = nid;
+ spec->multiout.share_spdif = 1;
+ if (cfg->dig_outs > 1 &&
+ snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) {
+ spec->slave_dig_outs[0] = nid;
+ codec->slave_dig_outs = spec->slave_dig_outs;
+ }
+ return 0;
+}
+
+static int parse_digital_input(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int idx;
+
+ if (cfg->dig_in_pin)
+ spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx);
+ return 0;
+}
+
+/*
+ * create mixer controls
+ */
+
+static const char *dir_sfx[2] = { "Playback", "Capture" };
+
+static int add_mute(struct hda_codec *codec, const char *name, int index,
+ unsigned int pval, int dir, struct snd_kcontrol **kctlp)
+{
+ char tmp[44];
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_IDX(tmp, index, 0, 0, HDA_OUTPUT);
+ knew.private_value = pval;
+ snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]);
+ *kctlp = snd_ctl_new1(&knew, codec);
+ return snd_hda_ctl_add(codec, *kctlp);
+}
+
+static int add_volume(struct hda_codec *codec, const char *name,
+ int index, unsigned int pval, int dir,
+ struct snd_kcontrol **kctlp)
+{
+ char tmp[32];
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_IDX(tmp, index, 0, 0, HDA_OUTPUT);
+ knew.private_value = pval;
+ snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]);
+ *kctlp = snd_ctl_new1(&knew, codec);
+ return snd_hda_ctl_add(codec, *kctlp);
+}
+
+static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
+{
+ unsigned int caps;
+
+ /* set the upper-limit for mixer amp to 0dB */
+ caps = query_amp_caps(codec, dac, HDA_OUTPUT);
+ caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT);
+ caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f)
+ << AC_AMPCAP_NUM_STEPS_SHIFT;
+ snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps);
+}
+
+static int add_vmaster(struct hda_codec *codec, hda_nid_t dac)
+{
+ struct cs_spec *spec = codec->spec;
+ unsigned int tlv[4];
+ int err;
+
+ spec->vmaster_sw =
+ snd_ctl_make_virtual_master("Master Playback Switch", NULL);
+ err = snd_hda_ctl_add(codec, spec->vmaster_sw);
+ if (err < 0)
+ return err;
+
+ snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv);
+ spec->vmaster_vol =
+ snd_ctl_make_virtual_master("Master Playback Volume", tlv);
+ err = snd_hda_ctl_add(codec, spec->vmaster_vol);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx,
+ int num_ctls, int type)
+{
+ struct cs_spec *spec = codec->spec;
+ const char *name;
+ int err, index;
+ struct snd_kcontrol *kctl;
+ static char *speakers[] = {
+ "Front Speaker", "Surround Speaker", "Bass Speaker"
+ };
+ static char *line_outs[] = {
+ "Front Line-Out", "Surround Line-Out", "Bass Line-Out"
+ };
+
+ fix_volume_caps(codec, dac);
+ if (!spec->vmaster_sw) {
+ err = add_vmaster(codec, dac);
+ if (err < 0)
+ return err;
+ }
+
+ index = 0;
+ switch (type) {
+ case AUTO_PIN_HP_OUT:
+ name = "Headphone";
+ index = idx;
+ break;
+ case AUTO_PIN_SPEAKER_OUT:
+ if (num_ctls > 1)
+ name = speakers[idx];
+ else
+ name = "Speaker";
+ break;
+ default:
+ if (num_ctls > 1)
+ name = line_outs[idx];
+ else
+ name = "Line-Out";
+ break;
+ }
+
+ err = add_mute(codec, name, index,
+ HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
+ if (err < 0)
+ return err;
+ err = snd_ctl_add_slave(spec->vmaster_sw, kctl);
+ if (err < 0)
+ return err;
+
+ err = add_volume(codec, name, index,
+ HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
+ if (err < 0)
+ return err;
+ err = snd_ctl_add_slave(spec->vmaster_vol, kctl);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int build_output(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, err;
+
+ for (i = 0; i < cfg->line_outs; i++) {
+ err = add_output(codec, get_dac(codec, cfg->line_out_pins[i]),
+ i, cfg->line_outs, cfg->line_out_type);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < cfg->hp_outs; i++) {
+ err = add_output(codec, get_dac(codec, cfg->hp_pins[i]),
+ i, cfg->hp_outs, AUTO_PIN_HP_OUT);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ err = add_output(codec, get_dac(codec, cfg->speaker_pins[i]),
+ i, cfg->speaker_outs, AUTO_PIN_SPEAKER_OUT);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/*
+ */
+
+static struct snd_kcontrol_new cs_capture_ctls[] = {
+ HDA_BIND_SW("Capture Switch", 0),
+ HDA_BIND_VOL("Capture Volume", 0),
+};
+
+static int change_cur_input(struct hda_codec *codec, unsigned int idx,
+ int force)
+{
+ struct cs_spec *spec = codec->spec;
+
+ if (spec->cur_input == idx && !force)
+ return 0;
+ if (spec->cur_adc && spec->cur_adc != spec->adc_nid[idx]) {
+ /* stream is running, let's swap the current ADC */
+ snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+ spec->cur_adc = spec->adc_nid[idx];
+ snd_hda_codec_setup_stream(codec, spec->cur_adc,
+ spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+ }
+ snd_hda_codec_write(codec, spec->cur_adc, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ spec->adc_idx[idx]);
+ spec->cur_input = idx;
+ return 1;
+}
+
+static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs_spec *spec = codec->spec;
+ unsigned int idx;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = spec->num_inputs;
+ if (uinfo->value.enumerated.item >= spec->num_inputs)
+ uinfo->value.enumerated.item = spec->num_inputs - 1;
+ idx = spec->input_idx[uinfo->value.enumerated.item];
+ strcpy(uinfo->value.enumerated.name, auto_pin_cfg_labels[idx]);
+ return 0;
+}
+
+static int cs_capture_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->capsrc_idx[spec->cur_input];
+ return 0;
+}
+
+static int cs_capture_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs_spec *spec = codec->spec;
+ unsigned int idx = ucontrol->value.enumerated.item[0];
+
+ if (idx >= spec->num_inputs)
+ return -EINVAL;
+ idx = spec->input_idx[idx];
+ return change_cur_input(codec, idx, 0);
+}
+
+static struct snd_kcontrol_new cs_capture_source = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = cs_capture_source_info,
+ .get = cs_capture_source_get,
+ .put = cs_capture_source_put,
+};
+
+static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
+ struct hda_ctl_ops *ops)
+{
+ struct cs_spec *spec = codec->spec;
+ struct hda_bind_ctls *bind;
+ int i, n;
+
+ bind = kzalloc(sizeof(*bind) + sizeof(long) * (spec->num_inputs + 1),
+ GFP_KERNEL);
+ if (!bind)
+ return NULL;
+ bind->ops = ops;
+ n = 0;
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (!spec->adc_nid[i])
+ continue;
+ bind->values[n++] =
+ HDA_COMPOSE_AMP_VAL(spec->adc_nid[i], 3,
+ spec->adc_idx[i], HDA_INPUT);
+ }
+ return bind;
+}
+
+static int build_input(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ int i, err;
+
+ if (!spec->num_inputs)
+ return 0;
+
+ /* make bind-capture */
+ spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw);
+ spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol);
+ for (i = 0; i < 2; i++) {
+ struct snd_kcontrol *kctl;
+ if (!spec->capture_bind[i])
+ return -ENOMEM;
+ kctl = snd_ctl_new1(&cs_capture_ctls[i], codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = (long)spec->capture_bind[i];
+ err = snd_hda_ctl_add(codec, kctl);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->num_inputs > 1 && !spec->mic_detect) {
+ err = snd_hda_ctl_add(codec,
+ snd_ctl_new1(&cs_capture_source, codec));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ */
+
+static int build_digital_output(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ int err;
+
+ if (!spec->multiout.dig_out_nid)
+ return 0;
+
+ err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+ if (err < 0)
+ return err;
+ err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int build_digital_input(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ if (spec->dig_in)
+ return snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+ return 0;
+}
+
+/*
+ * auto-mute and auto-mic switching
+ */
+
+static void cs_automute(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int caps, present, hp_present;
+ hda_nid_t nid;
+ int i;
+
+ hp_present = 0;
+ for (i = 0; i < cfg->hp_outs; i++) {
+ nid = cfg->hp_pins[i];
+ caps = snd_hda_query_pin_caps(codec, nid);
+ if (!(caps & AC_PINCAP_PRES_DETECT))
+ continue;
+ if (caps & AC_PINCAP_TRIG_REQ)
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_SET_PIN_SENSE, 0);
+ present = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ hp_present |= (present & AC_PINSENSE_PRESENCE) != 0;
+ if (hp_present)
+ break;
+ }
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ nid = cfg->speaker_pins[i];
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ hp_present ? 0 : PIN_OUT);
+ }
+ if (spec->board_config == CS420X_MBP55) {
+ unsigned int gpio = hp_present ? 0x02 : 0x08;
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, gpio);
+ }
+}
+
+static void cs_automic(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
+ unsigned int caps, present;
+
+ nid = cfg->input_pins[spec->automic_idx];
+ caps = snd_hda_query_pin_caps(codec, nid);
+ if (caps & AC_PINCAP_TRIG_REQ)
+ snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
+ present = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ if (present & AC_PINSENSE_PRESENCE)
+ change_cur_input(codec, spec->automic_idx, 0);
+ else {
+ unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ?
+ AUTO_PIN_FRONT_MIC : AUTO_PIN_MIC;
+ change_cur_input(codec, imic, 0);
+ }
+}
+
+/*
+ */
+
+static void init_output(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ /* mute first */
+ for (i = 0; i < spec->multiout.num_dacs; i++)
+ snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ if (spec->multiout.hp_nid)
+ snd_hda_codec_write(codec, spec->multiout.hp_nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
+ if (!spec->multiout.extra_out_nid[i])
+ break;
+ snd_hda_codec_write(codec, spec->multiout.extra_out_nid[i], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ }
+
+ /* set appropriate pin controls */
+ for (i = 0; i < cfg->line_outs; i++)
+ snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ for (i = 0; i < cfg->hp_outs; i++) {
+ hda_nid_t nid = cfg->hp_pins[i];
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+ if (!cfg->speaker_outs)
+ continue;
+ if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | HP_EVENT);
+ spec->hp_detect = 1;
+ }
+ }
+ for (i = 0; i < cfg->speaker_outs; i++)
+ snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ if (spec->hp_detect)
+ cs_automute(codec);
+}
+
+static void init_input(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int coef;
+ int i;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ unsigned int ctl;
+ hda_nid_t pin = cfg->input_pins[i];
+ if (!pin || !spec->adc_nid[i])
+ continue;
+ /* set appropriate pin control and mute first */
+ ctl = PIN_IN;
+ if (i <= AUTO_PIN_FRONT_MIC) {
+ unsigned int caps = snd_hda_query_pin_caps(codec, pin);
+ caps >>= AC_PINCAP_VREF_SHIFT;
+ if (caps & AC_PINCAP_VREF_80)
+ ctl = PIN_VREF80;
+ }
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
+ snd_hda_codec_write(codec, spec->adc_nid[i], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_MUTE(spec->adc_idx[i]));
+ if (spec->mic_detect && spec->automic_idx == i)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | MIC_EVENT);
+ }
+ change_cur_input(codec, spec->cur_input, 1);
+ if (spec->mic_detect)
+ cs_automic(codec);
+
+ coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */
+ if (is_active_pin(codec, CS_DMIC2_PIN_NID))
+ coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */
+ if (is_active_pin(codec, CS_DMIC1_PIN_NID))
+ coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0
+ * No effect if SPDIF_OUT2 is slected in
+ * IDX_SPDIF_CTL.
+ */
+ cs_vendor_coef_set(codec, IDX_ADC_CFG, coef);
+}
+
+static struct hda_verb cs_coef_init_verbs[] = {
+ {0x11, AC_VERB_SET_PROC_STATE, 1},
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF,
+ (0x002a /* DAC1/2/3 SZCMode Soft Ramp */
+ | 0x0040 /* Mute DACs on FIFO error */
+ | 0x1000 /* Enable DACs High Pass Filter */
+ | 0x0400 /* Disable Coefficient Auto increment */
+ )},
+ /* Beep */
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */
+
+ {} /* terminator */
+};
+
+/* SPDIF setup */
+static void init_digital(struct hda_codec *codec)
+{
+ unsigned int coef;
+
+ coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */
+ coef |= 0x0008; /* Replace with mute on error */
+ if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID))
+ coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2
+ * SPDIF_OUT2 is shared with GPIO1 and
+ * DMIC_SDA2.
+ */
+ cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef);
+}
+
+static int cs_init(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+
+ snd_hda_sequence_write(codec, cs_coef_init_verbs);
+
+ if (spec->gpio_mask) {
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
+ spec->gpio_mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
+ spec->gpio_dir);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
+ }
+
+ init_output(codec);
+ init_input(codec);
+ init_digital(codec);
+ return 0;
+}
+
+static int cs_build_controls(struct hda_codec *codec)
+{
+ int err;
+
+ err = build_output(codec);
+ if (err < 0)
+ return err;
+ err = build_input(codec);
+ if (err < 0)
+ return err;
+ err = build_digital_output(codec);
+ if (err < 0)
+ return err;
+ err = build_digital_input(codec);
+ if (err < 0)
+ return err;
+ return cs_init(codec);
+}
+
+static void cs_free(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ kfree(spec->capture_bind[0]);
+ kfree(spec->capture_bind[1]);
+ kfree(codec->spec);
+}
+
+static void cs_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ switch ((res >> 26) & 0x7f) {
+ case HP_EVENT:
+ cs_automute(codec);
+ break;
+ case MIC_EVENT:
+ cs_automic(codec);
+ break;
+ }
+}
+
+static struct hda_codec_ops cs_patch_ops = {
+ .build_controls = cs_build_controls,
+ .build_pcms = cs_build_pcms,
+ .init = cs_init,
+ .free = cs_free,
+ .unsol_event = cs_unsol_event,
+};
+
+static int cs_parse_auto_config(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ if (err < 0)
+ return err;
+
+ err = parse_output(codec);
+ if (err < 0)
+ return err;
+ err = parse_input(codec);
+ if (err < 0)
+ return err;
+ err = parse_digital_output(codec);
+ if (err < 0)
+ return err;
+ err = parse_digital_input(codec);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static const char *cs420x_models[CS420X_MODELS] = {
+ [CS420X_MBP55] = "mbp55",
+ [CS420X_AUTO] = "auto",
+};
+
+
+static struct snd_pci_quirk cs420x_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
+ {} /* terminator */
+};
+
+struct cs_pincfg {
+ hda_nid_t nid;
+ u32 val;
+};
+
+static struct cs_pincfg mbp55_pincfgs[] = {
+ { 0x09, 0x012b4030 },
+ { 0x0a, 0x90100121 },
+ { 0x0b, 0x90100120 },
+ { 0x0c, 0x400000f0 },
+ { 0x0d, 0x90a00110 },
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x400000f0 },
+ { 0x10, 0x014be040 },
+ { 0x12, 0x400000f0 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
+
+static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
+ [CS420X_MBP55] = mbp55_pincfgs,
+};
+
+static void fix_pincfg(struct hda_codec *codec, int model)
+{
+ const struct cs_pincfg *cfg = cs_pincfgs[model];
+ if (!cfg)
+ return;
+ for (; cfg->nid; cfg++)
+ snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
+}
+
+
+static int patch_cs420x(struct hda_codec *codec)
+{
+ struct cs_spec *spec;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+
+ spec->board_config =
+ snd_hda_check_board_config(codec, CS420X_MODELS,
+ cs420x_models, cs420x_cfg_tbl);
+ if (spec->board_config >= 0)
+ fix_pincfg(codec, spec->board_config);
+
+ switch (spec->board_config) {
+ case CS420X_MBP55:
+ /* GPIO1 = headphones */
+ /* GPIO3 = speakers */
+ spec->gpio_mask = 0x0a;
+ spec->gpio_dir = 0x0a;
+ break;
+ }
+
+ err = cs_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ codec->patch_ops = cs_patch_ops;
+
+ return 0;
+
+ error:
+ kfree(codec->spec);
+ codec->spec = NULL;
+ return err;
+}
+
+
+/*
+ * patch entries
+ */
+static struct hda_codec_preset snd_hda_preset_cirrus[] = {
+ { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
+ { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
+ {} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:10134206");
+MODULE_ALIAS("snd-hda-codec-id:10134207");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
+
+static struct hda_codec_preset_list cirrus_list = {
+ .preset = snd_hda_preset_cirrus,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_cirrus_init(void)
+{
+ return snd_hda_add_codec_preset(&cirrus_list);
+}
+
+static void __exit patch_cirrus_exit(void)
+{
+ snd_hda_delete_codec_preset(&cirrus_list);
+}
+
+module_init(patch_cirrus_init)
+module_exit(patch_cirrus_exit)
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index c921264bbd7..780e1a72114 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -635,7 +635,8 @@ static int patch_cmi9880(struct hda_codec *codec)
cmi9880_models,
cmi9880_cfg_tbl);
if (spec->board_config < 0) {
- snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n");
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
spec->board_config = CMI_AUTO; /* try everything */
}
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index ac868c59f9e..9d899eda44d 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -108,6 +108,8 @@ struct conexant_spec {
struct hda_input_mux private_imux;
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+ unsigned int dell_automute;
+ unsigned int port_d_mode;
};
static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@ -1908,6 +1910,480 @@ static int patch_cxt5051(struct hda_codec *codec)
return 0;
}
+/* Conexant 5066 specific */
+
+static hda_nid_t cxt5066_dac_nids[1] = { 0x10 };
+static hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 };
+static hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 };
+#define CXT5066_SPDIF_OUT 0x21
+
+static struct hda_channel_mode cxt5066_modes[1] = {
+ { 2, NULL },
+};
+
+static void cxt5066_update_speaker(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ unsigned int pinctl;
+
+ snd_printdd("CXT5066: update speaker, hp_present=%d\n",
+ spec->hp_present);
+
+ /* Port A (HP) */
+ pinctl = ((spec->hp_present & 1) && spec->cur_eapd) ? PIN_HP : 0;
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl);
+
+ /* Port D (HP/LO) */
+ pinctl = ((spec->hp_present & 2) && spec->cur_eapd)
+ ? spec->port_d_mode : 0;
+ snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl);
+
+ /* CLASS_D AMP */
+ pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
+ snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl);
+
+ if (spec->dell_automute) {
+ /* DELL AIO Port Rule: PortA > PortD > IntSpk */
+ pinctl = (!(spec->hp_present & 1) && spec->cur_eapd)
+ ? PIN_OUT : 0;
+ snd_hda_codec_write(codec, 0x1c, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+ }
+}
+
+/* turn on/off EAPD (+ mute HP) as a master switch */
+static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (!cxt_eapd_put(kcontrol, ucontrol))
+ return 0;
+
+ cxt5066_update_speaker(codec);
+ return 1;
+}
+
+/* toggle input of built-in and mic jack appropriately */
+static void cxt5066_automic(struct hda_codec *codec)
+{
+ static struct hda_verb ext_mic_present[] = {
+ /* enable external mic, port B */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+
+ /* switch to external mic input */
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0},
+
+ /* disable internal mic, port C */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {}
+ };
+ static struct hda_verb ext_mic_absent[] = {
+ /* enable internal mic, port C */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+
+ /* switch to internal mic input */
+ {0x17, AC_VERB_SET_CONNECT_SEL, 1},
+
+ /* disable external mic, port B */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {}
+ };
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x1a, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ if (present) {
+ snd_printdd("CXT5066: external microphone detected\n");
+ snd_hda_sequence_write(codec, ext_mic_present);
+ } else {
+ snd_printdd("CXT5066: external microphone absent\n");
+ snd_hda_sequence_write(codec, ext_mic_absent);
+ }
+}
+
+/* mute internal speaker if HP is plugged */
+static void cxt5066_hp_automute(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ unsigned int portA, portD;
+
+ /* Port A */
+ portA = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE;
+
+ /* Port D */
+ portD = (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0)
+ & AC_PINSENSE_PRESENCE) << 1;
+
+ spec->hp_present = !!(portA | portD);
+ snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
+ portA, portD, spec->hp_present);
+ cxt5066_update_speaker(codec);
+}
+
+/* unsolicited event for jack sensing */
+static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
+ switch (res >> 26) {
+ case CONEXANT_HP_EVENT:
+ cxt5066_hp_automute(codec);
+ break;
+ case CONEXANT_MIC_EVENT:
+ cxt5066_automic(codec);
+ break;
+ }
+}
+
+static const struct hda_input_mux cxt5066_analog_mic_boost = {
+ .num_items = 5,
+ .items = {
+ { "0dB", 0 },
+ { "10dB", 1 },
+ { "20dB", 2 },
+ { "30dB", 3 },
+ { "40dB", 4 },
+ },
+};
+
+static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ return snd_hda_input_mux_info(&cxt5066_analog_mic_boost, uinfo);
+}
+
+static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ int val;
+
+ val = snd_hda_codec_read(codec, 0x17, 0,
+ AC_VERB_GET_AMP_GAIN_MUTE, AC_AMP_GET_OUTPUT);
+
+ ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN;
+ return 0;
+}
+
+static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
+ unsigned int idx;
+
+ if (!imux->num_items)
+ return 0;
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+
+ snd_hda_codec_write_cache(codec, 0x17, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
+ imux->items[idx].index);
+
+ return 1;
+}
+
+static struct hda_input_mux cxt5066_capture_source = {
+ .num_items = 4,
+ .items = {
+ { "Mic B", 0 },
+ { "Mic C", 1 },
+ { "Mic E", 2 },
+ { "Mic F", 3 },
+ },
+};
+
+static struct hda_bind_ctls cxt5066_bind_capture_vol_others = {
+ .ops = &snd_hda_bind_vol,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT),
+ 0
+ },
+};
+
+static struct hda_bind_ctls cxt5066_bind_capture_sw_others = {
+ .ops = &snd_hda_bind_sw,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT),
+ 0
+ },
+};
+
+static struct snd_kcontrol_new cxt5066_mixer_master[] = {
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+ {}
+};
+
+static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+ .info = snd_hda_mixer_amp_volume_info,
+ .get = snd_hda_mixer_amp_volume_get,
+ .put = snd_hda_mixer_amp_volume_put,
+ .tlv = { .c = snd_hda_mixer_amp_tlv },
+ /* offset by 28 volume steps to limit minimum gain to -46dB */
+ .private_value =
+ HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28),
+ },
+ {}
+};
+
+static struct snd_kcontrol_new cxt5066_mixers[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = cxt_eapd_info,
+ .get = cxt_eapd_get,
+ .put = cxt5066_hp_master_sw_put,
+ .private_value = 0x1d,
+ },
+
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Mic Boost Capture Enum",
+ .info = cxt5066_mic_boost_mux_enum_info,
+ .get = cxt5066_mic_boost_mux_enum_get,
+ .put = cxt5066_mic_boost_mux_enum_put,
+ },
+
+ HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others),
+ HDA_BIND_SW("Capture Switch", &cxt5066_bind_capture_sw_others),
+ {}
+};
+
+static struct hda_verb cxt5066_init_verbs[] = {
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
+ {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
+
+ /* Speakers */
+ {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+ /* HP, Amp */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+ /* DAC1 */
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+ /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
+ /* no digital microphone support yet */
+ {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* Audio input selector */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
+
+ /* SPDIF route: PCM */
+ {0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
+ {0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
+
+ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+ /* EAPD */
+ {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+
+ /* not handling these yet */
+ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+ {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+ {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+ {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+ {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+ {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+ {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+ { } /* end */
+};
+
+static struct hda_verb cxt5066_init_verbs_olpc[] = {
+ /* Port A: headphones */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+ /* Port B: external microphone */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+
+ /* Port C: internal microphone */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+
+ /* Port D: unused */
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* Port E: unused, but has primary EAPD */
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+
+ /* Port F: unused */
+ {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* Port G: internal speakers */
+ {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+ /* DAC1 */
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+ /* DAC2: unused */
+ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+
+ /* Disable digital microphone port */
+ {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* Audio input selectors */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+
+ /* Disable SPDIF */
+ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+ /* enable unsolicited events for Port A and B */
+ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+ {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+ { } /* end */
+};
+
+static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
+ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ { } /* end */
+};
+
+/* initialize jack-sensing, too */
+static int cxt5066_init(struct hda_codec *codec)
+{
+ snd_printdd("CXT5066: init\n");
+ conexant_init(codec);
+ if (codec->patch_ops.unsol_event) {
+ cxt5066_hp_automute(codec);
+ cxt5066_automic(codec);
+ }
+ return 0;
+}
+
+enum {
+ CXT5066_LAPTOP, /* Laptops w/ EAPD support */
+ CXT5066_DELL_LAPTOP, /* Dell Laptop */
+ CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
+ CXT5066_MODELS
+};
+
+static const char *cxt5066_models[CXT5066_MODELS] = {
+ [CXT5066_LAPTOP] = "laptop",
+ [CXT5066_DELL_LAPTOP] = "dell-laptop",
+ [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
+};
+
+static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
+ CXT5066_LAPTOP),
+ SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
+ CXT5066_DELL_LAPTOP),
+ {}
+};
+
+static int patch_cxt5066(struct hda_codec *codec)
+{
+ struct conexant_spec *spec;
+ int board_config;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+
+ codec->patch_ops = conexant_patch_ops;
+ codec->patch_ops.init = cxt5066_init;
+
+ spec->dell_automute = 0;
+ spec->multiout.max_channels = 2;
+ spec->multiout.num_dacs = ARRAY_SIZE(cxt5066_dac_nids);
+ spec->multiout.dac_nids = cxt5066_dac_nids;
+ spec->multiout.dig_out_nid = CXT5066_SPDIF_OUT;
+ spec->num_adc_nids = 1;
+ spec->adc_nids = cxt5066_adc_nids;
+ spec->capsrc_nids = cxt5066_capsrc_nids;
+ spec->input_mux = &cxt5066_capture_source;
+
+ spec->port_d_mode = PIN_HP;
+
+ spec->num_init_verbs = 1;
+ spec->init_verbs[0] = cxt5066_init_verbs;
+ spec->num_channel_mode = ARRAY_SIZE(cxt5066_modes);
+ spec->channel_mode = cxt5066_modes;
+ spec->cur_adc = 0;
+ spec->cur_adc_idx = 0;
+
+ board_config = snd_hda_check_board_config(codec, CXT5066_MODELS,
+ cxt5066_models, cxt5066_cfg_tbl);
+ switch (board_config) {
+ default:
+ case CXT5066_LAPTOP:
+ spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
+ spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+ break;
+ case CXT5066_DELL_LAPTOP:
+ spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
+ spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+
+ spec->port_d_mode = PIN_OUT;
+ spec->init_verbs[spec->num_init_verbs] = cxt5066_init_verbs_portd_lo;
+ spec->num_init_verbs++;
+ spec->dell_automute = 1;
+ break;
+ case CXT5066_OLPC_XO_1_5:
+ codec->patch_ops.unsol_event = cxt5066_unsol_event;
+ spec->init_verbs[0] = cxt5066_init_verbs_olpc;
+ spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
+ spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+ spec->port_d_mode = 0;
+
+ /* no S/PDIF out */
+ spec->multiout.dig_out_nid = 0;
+
+ /* input source automatically selected */
+ spec->input_mux = NULL;
+ break;
+ }
+
+ return 0;
+}
/*
*/
@@ -1919,12 +2395,15 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
.patch = patch_cxt5047 },
{ .id = 0x14f15051, .name = "CX20561 (Hermosa)",
.patch = patch_cxt5051 },
+ { .id = 0x14f15066, .name = "CX20582 (Pebble)",
+ .patch = patch_cxt5066 },
{} /* terminator */
};
MODULE_ALIAS("snd-hda-codec-id:14f15045");
MODULE_ALIAS("snd-hda-codec-id:14f15047");
MODULE_ALIAS("snd-hda-codec-id:14f15051");
+MODULE_ALIAS("snd-hda-codec-id:14f15066");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c
index fcc77fec448..01a18ed475a 100644
--- a/sound/pci/hda/patch_intelhdmi.c
+++ b/sound/pci/hda/patch_intelhdmi.c
@@ -33,8 +33,8 @@
#include "hda_codec.h"
#include "hda_local.h"
-#define CVT_NID 0x02 /* audio converter */
-#define PIN_NID 0x03 /* HDMI output pin */
+static hda_nid_t cvt_nid; /* audio converter */
+static hda_nid_t pin_nid; /* HDMI output pin */
#define INTEL_HDMI_EVENT_TAG 0x08
@@ -44,30 +44,6 @@ struct intel_hdmi_spec {
struct hdmi_eld sink_eld;
};
-static struct hda_verb pinout_enable_verb[] = {
- {PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {} /* terminator */
-};
-
-static struct hda_verb unsolicited_response_verb[] = {
- {PIN_NID, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN |
- INTEL_HDMI_EVENT_TAG},
- {}
-};
-
-static struct hda_verb def_chan_map[] = {
- {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x00},
- {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x11},
- {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x22},
- {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x33},
- {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x44},
- {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x55},
- {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x66},
- {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x77},
- {}
-};
-
-
struct hdmi_audio_infoframe {
u8 type; /* 0x84 */
u8 ver; /* 0x01 */
@@ -244,11 +220,12 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
static void hdmi_enable_output(struct hda_codec *codec)
{
/* Unmute */
- if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, PIN_NID, 0,
+ if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
/* Enable pin out */
- snd_hda_sequence_write(codec, pinout_enable_verb);
+ snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
}
/*
@@ -256,8 +233,8 @@ static void hdmi_enable_output(struct hda_codec *codec)
*/
static void hdmi_start_infoframe_trans(struct hda_codec *codec)
{
- hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
- snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+ hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
AC_DIPXMIT_BEST);
}
@@ -266,20 +243,20 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec)
*/
static void hdmi_stop_infoframe_trans(struct hda_codec *codec)
{
- hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
- snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+ hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
AC_DIPXMIT_DISABLE);
}
static int hdmi_get_channel_count(struct hda_codec *codec)
{
- return 1 + snd_hda_codec_read(codec, CVT_NID, 0,
+ return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
AC_VERB_GET_CVT_CHAN_COUNT, 0);
}
static void hdmi_set_channel_count(struct hda_codec *codec, int chs)
{
- snd_hda_codec_write(codec, CVT_NID, 0,
+ snd_hda_codec_write(codec, cvt_nid, 0,
AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
if (chs != hdmi_get_channel_count(codec))
@@ -294,7 +271,7 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec)
int slot;
for (i = 0; i < 8; i++) {
- slot = snd_hda_codec_read(codec, CVT_NID, 0,
+ slot = snd_hda_codec_read(codec, cvt_nid, 0,
AC_VERB_GET_HDMI_CHAN_SLOT, i);
printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
slot >> 4, slot & 0x7);
@@ -307,7 +284,7 @@ static void hdmi_parse_eld(struct hda_codec *codec)
struct intel_hdmi_spec *spec = codec->spec;
struct hdmi_eld *eld = &spec->sink_eld;
- if (!snd_hdmi_get_eld(eld, codec, PIN_NID))
+ if (!snd_hdmi_get_eld(eld, codec, pin_nid))
snd_hdmi_show_eld(eld);
}
@@ -322,11 +299,11 @@ static void hdmi_debug_dip_size(struct hda_codec *codec)
int i;
int size;
- size = snd_hdmi_get_eld_size(codec, PIN_NID);
+ size = snd_hdmi_get_eld_size(codec, pin_nid);
printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
for (i = 0; i < 8; i++) {
- size = snd_hda_codec_read(codec, PIN_NID, 0,
+ size = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_HDMI_DIP_SIZE, i);
printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
}
@@ -340,15 +317,15 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec)
int size;
int pi, bi;
for (i = 0; i < 8; i++) {
- size = snd_hda_codec_read(codec, PIN_NID, 0,
+ size = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_HDMI_DIP_SIZE, i);
if (size == 0)
continue;
- hdmi_set_dip_index(codec, PIN_NID, i, 0x0);
+ hdmi_set_dip_index(codec, pin_nid, i, 0x0);
for (j = 1; j < 1000; j++) {
- hdmi_write_dip_byte(codec, PIN_NID, 0x0);
- hdmi_get_dip_index(codec, PIN_NID, &pi, &bi);
+ hdmi_write_dip_byte(codec, pin_nid, 0x0);
+ hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
if (pi != i)
snd_printd(KERN_INFO "dip index %d: %d != %d\n",
bi, pi, i);
@@ -376,9 +353,9 @@ static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
sum += params[i];
ai->checksum = - sum;
- hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
+ hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
for (i = 0; i < sizeof(ai); i++)
- hdmi_write_dip_byte(codec, PIN_NID, params[i]);
+ hdmi_write_dip_byte(codec, pin_nid, params[i]);
}
/*
@@ -465,6 +442,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
static void hdmi_setup_channel_mapping(struct hda_codec *codec,
struct hdmi_audio_infoframe *ai)
{
+ int i;
+
if (!ai->CA)
return;
@@ -473,7 +452,11 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec,
* ALSA sequence is front/surr/clfe/side?
*/
- snd_hda_sequence_write(codec, def_chan_map);
+ for (i = 0; i < 8; i++)
+ snd_hda_codec_write(codec, cvt_nid, 0,
+ AC_VERB_SET_HDMI_CHAN_SLOT,
+ (i << 4) | i);
+
hdmi_debug_channel_mapping(codec);
}
@@ -597,7 +580,6 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 8,
- .nid = CVT_NID, /* NID to query formats and rates and setup streams */
.ops = {
.open = intel_hdmi_playback_pcm_open,
.close = intel_hdmi_playback_pcm_close,
@@ -613,6 +595,9 @@ static int intel_hdmi_build_pcms(struct hda_codec *codec)
codec->num_pcms = 1;
codec->pcm_info = info;
+ /* NID to query formats and rates and setup streams */
+ intel_hdmi_pcm_playback.nid = cvt_nid;
+
info->name = "INTEL HDMI";
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
@@ -636,8 +621,9 @@ static int intel_hdmi_init(struct hda_codec *codec)
{
hdmi_enable_output(codec);
- snd_hda_sequence_write(codec, unsolicited_response_verb);
-
+ snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | INTEL_HDMI_EVENT_TAG);
return 0;
}
@@ -657,7 +643,7 @@ static struct hda_codec_ops intel_hdmi_patch_ops = {
.unsol_event = intel_hdmi_unsol_event,
};
-static int patch_intel_hdmi(struct hda_codec *codec)
+static int do_patch_intel_hdmi(struct hda_codec *codec)
{
struct intel_hdmi_spec *spec;
@@ -667,7 +653,7 @@ static int patch_intel_hdmi(struct hda_codec *codec)
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 8;
- spec->multiout.dig_out_nid = CVT_NID;
+ spec->multiout.dig_out_nid = cvt_nid;
codec->spec = spec;
codec->patch_ops = intel_hdmi_patch_ops;
@@ -679,12 +665,27 @@ static int patch_intel_hdmi(struct hda_codec *codec)
return 0;
}
+static int patch_intel_hdmi(struct hda_codec *codec)
+{
+ cvt_nid = 0x02;
+ pin_nid = 0x03;
+ return do_patch_intel_hdmi(codec);
+}
+
+static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec)
+{
+ cvt_nid = 0x02;
+ pin_nid = 0x04;
+ return do_patch_intel_hdmi(codec);
+}
+
static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
{ .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi },
{ .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
{ .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
{ .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
{ .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi },
+ { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak },
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
{} /* terminator */
};
@@ -694,6 +695,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862801");
MODULE_ALIAS("snd-hda-codec-id:80862802");
MODULE_ALIAS("snd-hda-codec-id:80862803");
MODULE_ALIAS("snd-hda-codec-id:80862804");
+MODULE_ALIAS("snd-hda-codec-id:80860054");
MODULE_ALIAS("snd-hda-codec-id:10951392");
MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
index f5792e2eea8..c8435c9a97f 100644
--- a/sound/pci/hda/patch_nvhdmi.c
+++ b/sound/pci/hda/patch_nvhdmi.c
@@ -377,6 +377,7 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
*/
static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
+ { .id = 0x10de0003, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
@@ -385,6 +386,7 @@ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
};
MODULE_ALIAS("snd-hda-codec-id:10de0002");
+MODULE_ALIAS("snd-hda-codec-id:10de0003");
MODULE_ALIAS("snd-hda-codec-id:10de0006");
MODULE_ALIAS("snd-hda-codec-id:10de0007");
MODULE_ALIAS("snd-hda-codec-id:10de0067");
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index b95df5d5dcc..7ed47f66ddd 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -208,12 +208,6 @@ enum {
ALC885_MBP3,
ALC885_MB5,
ALC885_IMAC24,
- ALC882_AUTO,
- ALC882_MODEL_LAST,
-};
-
-/* ALC883 models */
-enum {
ALC883_3ST_2ch_DIG,
ALC883_3ST_6ch_DIG,
ALC883_3ST_6ch,
@@ -226,6 +220,7 @@ enum {
ALC888_ACER_ASPIRE_4930G,
ALC888_ACER_ASPIRE_6530G,
ALC888_ACER_ASPIRE_8930G,
+ ALC888_ACER_ASPIRE_7730G,
ALC883_MEDION,
ALC883_MEDION_MD2,
ALC883_LAPTOP_EAPD,
@@ -237,17 +232,20 @@ enum {
ALC888_3ST_HP,
ALC888_6ST_DELL,
ALC883_MITAC,
+ ALC883_CLEVO_M540R,
ALC883_CLEVO_M720,
ALC883_FUJITSU_PI2515,
ALC888_FUJITSU_XA3530,
ALC883_3ST_6ch_INTEL,
+ ALC889A_INTEL,
+ ALC889_INTEL,
ALC888_ASUS_M90V,
ALC888_ASUS_EEE1601,
ALC889A_MB31,
ALC1200_ASUS_P5Q,
ALC883_SONY_VAIO_TT,
- ALC883_AUTO,
- ALC883_MODEL_LAST,
+ ALC882_AUTO,
+ ALC882_MODEL_LAST,
};
/* for GPIO Poll */
@@ -262,6 +260,14 @@ enum {
ALC_INIT_GPIO3,
};
+struct alc_mic_route {
+ hda_nid_t pin;
+ unsigned char mux_idx;
+ unsigned char amix_idx;
+};
+
+#define MUX_IDX_UNDEF ((unsigned char)-1)
+
struct alc_spec {
/* codec parameterization */
struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
@@ -304,6 +310,8 @@ struct alc_spec {
unsigned int num_mux_defs;
const struct hda_input_mux *input_mux;
unsigned int cur_mux[3];
+ struct alc_mic_route ext_mic;
+ struct alc_mic_route int_mic;
/* channel model */
const struct hda_channel_mode *channel_mode;
@@ -320,6 +328,8 @@ struct alc_spec {
struct snd_array kctls;
struct hda_input_mux private_imux[3];
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+ hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS];
+ hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS];
/* hooks */
void (*init_hook)(struct hda_codec *codec);
@@ -329,6 +339,7 @@ struct alc_spec {
unsigned int sense_updated: 1;
unsigned int jack_present: 1;
unsigned int master_sw: 1;
+ unsigned int auto_mic:1;
/* other flags */
unsigned int no_analog :1; /* digital I/O only */
@@ -370,6 +381,7 @@ struct alc_config_preset {
unsigned int num_mux_defs;
const struct hda_input_mux *input_mux;
void (*unsol_event)(struct hda_codec *, unsigned int);
+ void (*setup)(struct hda_codec *);
void (*init_hook)(struct hda_codec *);
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_amp_list *loopbacks;
@@ -417,7 +429,7 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
imux = &spec->input_mux[mux_idx];
- type = (get_wcaps(codec, nid) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ type = get_wcaps_type(get_wcaps(codec, nid));
if (type == AC_WID_AUD_MIX) {
/* Matrix-mixer style (e.g. ALC882) */
unsigned int *cur_val = &spec->cur_mux[adc_idx];
@@ -559,7 +571,7 @@ static int alc_pin_mode_get(struct snd_kcontrol *kcontrol,
/* Find enumerated value for current pinctl setting */
i = alc_pin_mode_min(dir);
- while (alc_pin_mode_values[i] != pinctl && i <= alc_pin_mode_max(dir))
+ while (i <= alc_pin_mode_max(dir) && alc_pin_mode_values[i] != pinctl)
i++;
*valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir);
return 0;
@@ -842,9 +854,10 @@ static void print_realtek_coef(struct snd_info_buffer *buffer,
/*
* set up from the preset table
*/
-static void setup_preset(struct alc_spec *spec,
+static void setup_preset(struct hda_codec *codec,
const struct alc_config_preset *preset)
{
+ struct alc_spec *spec = codec->spec;
int i;
for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
@@ -886,6 +899,9 @@ static void setup_preset(struct alc_spec *spec,
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = preset->loopbacks;
#endif
+
+ if (preset->setup)
+ preset->setup(codec);
}
/* Enable GPIO mask and set output */
@@ -965,30 +981,64 @@ static void alc_automute_pin(struct hda_codec *codec)
}
}
-#if 0 /* it's broken in some cases -- temporarily disabled */
+static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
+ hda_nid_t nid)
+{
+ hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+ int i, nums;
+
+ nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+ for (i = 0; i < nums; i++)
+ if (conn[i] == nid)
+ return i;
+ return -1;
+}
+
static void alc_mic_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int present;
- unsigned int mic_nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
- unsigned int fmic_nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
- unsigned int mix_nid = spec->capsrc_nids[0];
- unsigned int capsrc_idx_mic, capsrc_idx_fmic;
-
- capsrc_idx_mic = mic_nid - 0x18;
- capsrc_idx_fmic = fmic_nid - 0x18;
- present = snd_hda_codec_read(codec, mic_nid, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (capsrc_idx_mic << 8) | (present ? 0 : 0x80));
- snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (capsrc_idx_fmic << 8) | (present ? 0x80 : 0));
- snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, capsrc_idx_fmic,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ struct alc_mic_route *dead, *alive;
+ unsigned int present, type;
+ hda_nid_t cap_nid;
+
+ if (!spec->auto_mic)
+ return;
+ if (!spec->int_mic.pin || !spec->ext_mic.pin)
+ return;
+ if (snd_BUG_ON(!spec->adc_nids))
+ return;
+
+ cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
+
+ present = snd_hda_codec_read(codec, spec->ext_mic.pin, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ present &= AC_PINSENSE_PRESENCE;
+ if (present) {
+ alive = &spec->ext_mic;
+ dead = &spec->int_mic;
+ } else {
+ alive = &spec->int_mic;
+ dead = &spec->ext_mic;
+ }
+
+ type = get_wcaps_type(get_wcaps(codec, cap_nid));
+ if (type == AC_WID_AUD_MIX) {
+ /* Matrix-mixer style (e.g. ALC882) */
+ snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
+ alive->mux_idx,
+ HDA_AMP_MUTE, 0);
+ snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
+ dead->mux_idx,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ } else {
+ /* MUX style (e.g. ALC880) */
+ snd_hda_codec_write_cache(codec, cap_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ alive->mux_idx);
+ }
+
+ /* FIXME: analog mixer */
}
-#else
-#define alc_mic_automute(codec) do {} while(0) /* NOP */
-#endif /* disabled */
/* unsolicited event for HP jack sensing */
static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
@@ -1031,6 +1081,16 @@ static void alc888_coef_init(struct hda_codec *codec)
AC_VERB_SET_PROC_COEF, 0x3030);
}
+static void alc889_coef_init(struct hda_codec *codec)
+{
+ unsigned int tmp;
+
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
+ tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, tmp|0x2010);
+}
+
static void alc_auto_init_amp(struct hda_codec *codec, int type)
{
unsigned int tmp;
@@ -1088,15 +1148,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
case 0x10ec0885:
case 0x10ec0887:
case 0x10ec0889:
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 7);
- tmp = snd_hda_codec_read(codec, 0x20, 0,
- AC_VERB_GET_PROC_COEF, 0);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 7);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF,
- tmp | 0x2010);
+ alc889_coef_init(codec);
break;
case 0x10ec0888:
alc888_coef_init(codec);
@@ -1142,6 +1194,55 @@ static void alc_init_auto_hp(struct hda_codec *codec)
spec->unsol_event = alc_sku_unsol_event;
}
+static void alc_init_auto_mic(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t fixed, ext;
+ int i;
+
+ /* there must be only two mic inputs exclusively */
+ for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++)
+ if (cfg->input_pins[i])
+ return;
+
+ fixed = ext = 0;
+ for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) {
+ hda_nid_t nid = cfg->input_pins[i];
+ unsigned int defcfg;
+ if (!nid)
+ return;
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ switch (get_defcfg_connect(defcfg)) {
+ case AC_JACK_PORT_FIXED:
+ if (fixed)
+ return; /* already occupied */
+ fixed = nid;
+ break;
+ case AC_JACK_PORT_COMPLEX:
+ if (ext)
+ return; /* already occupied */
+ ext = nid;
+ break;
+ default:
+ return; /* invalid entry */
+ }
+ }
+ if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
+ return; /* no unsol support */
+ snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x\n",
+ ext, fixed);
+ spec->ext_mic.pin = ext;
+ spec->int_mic.pin = fixed;
+ spec->ext_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
+ spec->int_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
+ spec->auto_mic = 1;
+ snd_hda_codec_write_cache(codec, spec->ext_mic.pin, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | ALC880_MIC_EVENT);
+ spec->unsol_event = alc_sku_unsol_event;
+}
+
/* check subsystem ID and set up device-specific initialization;
* return 1 if initialized, 0 if invalid SSID
*/
@@ -1243,6 +1344,7 @@ do_sku:
}
alc_init_auto_hp(codec);
+ alc_init_auto_mic(codec);
return 1;
}
@@ -1255,6 +1357,7 @@ static void alc_ssid_check(struct hda_codec *codec,
"Enable default setup for auto mode as fallback\n");
spec->init_amp = ALC_INIT_DEFAULT;
alc_init_auto_hp(codec);
+ alc_init_auto_mic(codec);
}
}
@@ -1436,7 +1539,25 @@ static void alc_automute_amp_unsol_event(struct hda_codec *codec,
alc_automute_amp(codec);
}
-static void alc888_fujitsu_xa3530_init_hook(struct hda_codec *codec)
+static void alc889_automute_setup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ spec->autocfg.hp_pins[0] = 0x15;
+ spec->autocfg.speaker_pins[0] = 0x14;
+ spec->autocfg.speaker_pins[1] = 0x16;
+ spec->autocfg.speaker_pins[2] = 0x17;
+ spec->autocfg.speaker_pins[3] = 0x19;
+ spec->autocfg.speaker_pins[4] = 0x1a;
+}
+
+static void alc889_intel_init_hook(struct hda_codec *codec)
+{
+ alc889_coef_init(codec);
+ alc_automute_amp(codec);
+}
+
+static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -1444,7 +1565,6 @@ static void alc888_fujitsu_xa3530_init_hook(struct hda_codec *codec)
spec->autocfg.hp_pins[1] = 0x1b; /* hp */
spec->autocfg.speaker_pins[0] = 0x14; /* speaker */
spec->autocfg.speaker_pins[1] = 0x15; /* bass */
- alc_automute_amp(codec);
}
/*
@@ -1643,16 +1763,15 @@ static struct snd_kcontrol_new alc888_base_mixer[] = {
{ } /* end */
};
-static void alc888_acer_aspire_4930g_init_hook(struct hda_codec *codec)
+static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
- alc_automute_amp(codec);
}
-static void alc888_acer_aspire_6530g_init_hook(struct hda_codec *codec)
+static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -1660,10 +1779,9 @@ static void alc888_acer_aspire_6530g_init_hook(struct hda_codec *codec)
spec->autocfg.speaker_pins[0] = 0x14;
spec->autocfg.speaker_pins[1] = 0x16;
spec->autocfg.speaker_pins[2] = 0x17;
- alc_automute_amp(codec);
}
-static void alc889_acer_aspire_8930g_init_hook(struct hda_codec *codec)
+static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -1671,7 +1789,6 @@ static void alc889_acer_aspire_8930g_init_hook(struct hda_codec *codec)
spec->autocfg.speaker_pins[0] = 0x14;
spec->autocfg.speaker_pins[1] = 0x16;
spec->autocfg.speaker_pins[2] = 0x1b;
- alc_automute_amp(codec);
}
/*
@@ -2651,13 +2768,17 @@ static void alc880_uniwill_mic_automute(struct hda_codec *codec)
snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
}
-static void alc880_uniwill_init_hook(struct hda_codec *codec)
+static void alc880_uniwill_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x16;
+}
+
+static void alc880_uniwill_init_hook(struct hda_codec *codec)
+{
alc_automute_amp(codec);
alc880_uniwill_mic_automute(codec);
}
@@ -2678,13 +2799,12 @@ static void alc880_uniwill_unsol_event(struct hda_codec *codec,
}
}
-static void alc880_uniwill_p53_init_hook(struct hda_codec *codec)
+static void alc880_uniwill_p53_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x15;
- alc_automute_amp(codec);
}
static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
@@ -2947,13 +3067,12 @@ static struct hda_verb alc880_lg_init_verbs[] = {
};
/* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_init_hook(struct hda_codec *codec)
+static void alc880_lg_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x1b;
spec->autocfg.speaker_pins[0] = 0x17;
- alc_automute_amp(codec);
}
/*
@@ -3032,13 +3151,12 @@ static struct hda_verb alc880_lg_lw_init_verbs[] = {
};
/* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_lw_init_hook(struct hda_codec *codec)
+static void alc880_lg_lw_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x1b;
spec->autocfg.speaker_pins[0] = 0x14;
- alc_automute_amp(codec);
}
static struct snd_kcontrol_new alc880_medion_rim_mixer[] = {
@@ -3104,13 +3222,12 @@ static void alc880_medion_rim_unsol_event(struct hda_codec *codec,
alc880_medion_rim_automute(codec);
}
-static void alc880_medion_rim_init_hook(struct hda_codec *codec)
+static void alc880_medion_rim_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x1b;
- alc880_medion_rim_automute(codec);
}
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -3977,7 +4094,8 @@ static struct alc_config_preset alc880_presets[] = {
.channel_mode = alc880_2_jack_modes,
.input_mux = &alc880_f1734_capture_source,
.unsol_event = alc880_uniwill_p53_unsol_event,
- .init_hook = alc880_uniwill_p53_init_hook,
+ .setup = alc880_uniwill_p53_setup,
+ .init_hook = alc_automute_amp,
},
[ALC880_ASUS] = {
.mixers = { alc880_asus_mixer },
@@ -4054,6 +4172,7 @@ static struct alc_config_preset alc880_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc880_capture_source,
.unsol_event = alc880_uniwill_unsol_event,
+ .setup = alc880_uniwill_setup,
.init_hook = alc880_uniwill_init_hook,
},
[ALC880_UNIWILL_P53] = {
@@ -4066,7 +4185,8 @@ static struct alc_config_preset alc880_presets[] = {
.channel_mode = alc880_threestack_modes,
.input_mux = &alc880_capture_source,
.unsol_event = alc880_uniwill_p53_unsol_event,
- .init_hook = alc880_uniwill_p53_init_hook,
+ .setup = alc880_uniwill_p53_setup,
+ .init_hook = alc_automute_amp,
},
[ALC880_FUJITSU] = {
.mixers = { alc880_fujitsu_mixer },
@@ -4080,7 +4200,8 @@ static struct alc_config_preset alc880_presets[] = {
.channel_mode = alc880_2_jack_modes,
.input_mux = &alc880_capture_source,
.unsol_event = alc880_uniwill_p53_unsol_event,
- .init_hook = alc880_uniwill_p53_init_hook,
+ .setup = alc880_uniwill_p53_setup,
+ .init_hook = alc_automute_amp,
},
[ALC880_CLEVO] = {
.mixers = { alc880_three_stack_mixer },
@@ -4106,7 +4227,8 @@ static struct alc_config_preset alc880_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc880_lg_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc880_lg_init_hook,
+ .setup = alc880_lg_setup,
+ .init_hook = alc_automute_amp,
#ifdef CONFIG_SND_HDA_POWER_SAVE
.loopbacks = alc880_lg_loopbacks,
#endif
@@ -4122,7 +4244,8 @@ static struct alc_config_preset alc880_presets[] = {
.channel_mode = alc880_lg_lw_modes,
.input_mux = &alc880_lg_lw_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc880_lg_lw_init_hook,
+ .setup = alc880_lg_lw_setup,
+ .init_hook = alc_automute_amp,
},
[ALC880_MEDION_RIM] = {
.mixers = { alc880_medion_rim_mixer },
@@ -4136,7 +4259,8 @@ static struct alc_config_preset alc880_presets[] = {
.channel_mode = alc880_2_jack_modes,
.input_mux = &alc880_medion_rim_capture_source,
.unsol_event = alc880_medion_rim_unsol_event,
- .init_hook = alc880_medion_rim_init_hook,
+ .setup = alc880_medion_rim_setup,
+ .init_hook = alc880_medion_rim_automute,
},
#ifdef CONFIG_SND_DEBUG
[ALC880_TEST] = {
@@ -4189,8 +4313,6 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
#define alc880_fixed_pin_idx(nid) ((nid) - 0x14)
#define alc880_is_multi_pin(nid) ((nid) >= 0x18)
#define alc880_multi_pin_idx(nid) ((nid) - 0x18)
-#define alc880_is_input_pin(nid) ((nid) >= 0x18)
-#define alc880_input_pin_idx(nid) ((nid) - 0x18)
#define alc880_idx_to_dac(nid) ((nid) + 0x02)
#define alc880_dac_to_idx(nid) ((nid) - 0x02)
#define alc880_idx_to_mixer(nid) ((nid) + 0x0c)
@@ -4278,13 +4400,19 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
if (err < 0)
return err;
} else {
- sprintf(name, "%s Playback Volume", chname[i]);
+ const char *pfx;
+ if (cfg->line_outs == 1 &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ pfx = "Speaker";
+ else
+ pfx = chname[i];
+ sprintf(name, "%s Playback Volume", pfx);
err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
HDA_COMPOSE_AMP_VAL(nid, 3, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- sprintf(name, "%s Playback Switch", chname[i]);
+ sprintf(name, "%s Playback Switch", pfx);
err = add_control(spec, ALC_CTL_BIND_MUTE, name,
HDA_COMPOSE_AMP_VAL(nid, 3, 2,
HDA_INPUT));
@@ -4358,31 +4486,61 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
return 0;
}
+static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
+ return (pincap & AC_PINCAP_IN) != 0;
+}
+
/* create playback/capture controls for input pins */
-static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
+static int alc_auto_create_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ hda_nid_t mixer,
+ hda_nid_t cap1, hda_nid_t cap2)
{
+ struct alc_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
int i, err, idx;
for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (alc880_is_input_pin(cfg->input_pins[i])) {
- idx = alc880_input_pin_idx(cfg->input_pins[i]);
- err = new_analog_input(spec, cfg->input_pins[i],
- auto_pin_cfg_labels[i],
- idx, 0x0b);
- if (err < 0)
- return err;
+ hda_nid_t pin;
+
+ pin = cfg->input_pins[i];
+ if (!alc_is_input_pin(codec, pin))
+ continue;
+
+ if (mixer) {
+ idx = get_connection_index(codec, mixer, pin);
+ if (idx >= 0) {
+ err = new_analog_input(spec, pin,
+ auto_pin_cfg_labels[i],
+ idx, mixer);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ if (!cap1)
+ continue;
+ idx = get_connection_index(codec, cap1, pin);
+ if (idx < 0 && cap2)
+ idx = get_connection_index(codec, cap2, pin);
+ if (idx >= 0) {
imux->items[imux->num_items].label =
auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index =
- alc880_input_pin_idx(cfg->input_pins[i]);
+ imux->items[imux->num_items].index = idx;
imux->num_items++;
}
}
return 0;
}
+static int alc880_auto_create_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x08, 0x09);
+}
+
static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
unsigned int pin_type)
{
@@ -4448,7 +4606,7 @@ static void alc880_auto_init_analog_input(struct hda_codec *codec)
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i];
- if (alc880_is_input_pin(nid)) {
+ if (alc_is_input_pin(codec, nid)) {
alc_set_input_pin(codec, nid, i);
if (nid != ALC880_PIN_CD_NID &&
(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
@@ -4491,7 +4649,7 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
"Headphone");
if (err < 0)
return err;
- err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = alc880_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -4505,12 +4663,6 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
&dig_nid, 1);
if (err < 0)
continue;
- if (dig_nid > 0x7f) {
- printk(KERN_ERR "alc880_auto: invalid dig_nid "
- "connection 0x%x for NID 0x%x\n", dig_nid,
- spec->autocfg.dig_out_pins[i]);
- continue;
- }
if (!i)
spec->multiout.dig_out_nid = dig_nid;
else {
@@ -4547,8 +4699,42 @@ static void alc880_auto_init(struct hda_codec *codec)
alc_inithook(codec);
}
-static void set_capture_mixer(struct alc_spec *spec)
+/* check the ADC/MUX contains all input pins; some ADC/MUX contains only
+ * one of two digital mic pins, e.g. on ALC272
+ */
+static void fixup_automic_adc(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ hda_nid_t cap = spec->capsrc_nids ?
+ spec->capsrc_nids[i] : spec->adc_nids[i];
+ int iidx, eidx;
+
+ iidx = get_connection_index(codec, cap, spec->int_mic.pin);
+ if (iidx < 0)
+ continue;
+ eidx = get_connection_index(codec, cap, spec->ext_mic.pin);
+ if (eidx < 0)
+ continue;
+ spec->int_mic.mux_idx = iidx;
+ spec->ext_mic.mux_idx = eidx;
+ if (spec->capsrc_nids)
+ spec->capsrc_nids += i;
+ spec->adc_nids += i;
+ spec->num_adc_nids = 1;
+ return;
+ }
+ snd_printd(KERN_INFO "hda_codec: %s: "
+ "No ADC/MUX containing both 0x%x and 0x%x pins\n",
+ codec->chip_name, spec->int_mic.pin, spec->ext_mic.pin);
+ spec->auto_mic = 0; /* disable auto-mic to be sure */
+}
+
+static void set_capture_mixer(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
static struct snd_kcontrol_new *caps[2][3] = {
{ alc_capture_mixer_nosrc1,
alc_capture_mixer_nosrc2,
@@ -4559,7 +4745,10 @@ static void set_capture_mixer(struct alc_spec *spec)
};
if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
int mux;
- if (spec->input_mux && spec->input_mux->num_items > 1)
+ if (spec->auto_mic) {
+ mux = 0;
+ fixup_automic_adc(codec);
+ } else if (spec->input_mux && spec->input_mux->num_items > 1)
mux = 1;
else
mux = 0;
@@ -4590,8 +4779,8 @@ static int patch_alc880(struct hda_codec *codec)
alc880_models,
alc880_cfg_tbl);
if (board_config < 0) {
- printk(KERN_INFO "hda_codec: Unknown model for %s, "
- "trying auto-probe from BIOS...\n", codec->chip_name);
+ printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
board_config = ALC880_AUTO;
}
@@ -4616,7 +4805,7 @@ static int patch_alc880(struct hda_codec *codec)
}
if (board_config != ALC880_AUTO)
- setup_preset(spec, &alc880_presets[board_config]);
+ setup_preset(codec, &alc880_presets[board_config]);
spec->stream_analog_playback = &alc880_pcm_analog_playback;
spec->stream_analog_capture = &alc880_pcm_analog_capture;
@@ -4629,7 +4818,7 @@ static int patch_alc880(struct hda_codec *codec)
/* check whether NID 0x07 is valid */
unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]);
/* get type */
- wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ wcap = get_wcaps_type(wcap);
if (wcap != AC_WID_AUD_IN) {
spec->adc_nids = alc880_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
@@ -4638,7 +4827,7 @@ static int patch_alc880(struct hda_codec *codec)
spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
}
}
- set_capture_mixer(spec);
+ set_capture_mixer(codec);
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
spec->vmaster_nid = 0x0c;
@@ -5830,7 +6019,14 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
nid = cfg->line_out_pins[0];
if (nid) {
- err = alc260_add_playback_controls(spec, nid, "Front", &vols);
+ const char *pfx;
+ if (!cfg->speaker_pins[0] && !cfg->hp_pins[0])
+ pfx = "Master";
+ else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ pfx = "Speaker";
+ else
+ pfx = "Front";
+ err = alc260_add_playback_controls(spec, nid, pfx, &vols);
if (err < 0)
return err;
}
@@ -5853,39 +6049,10 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
}
/* create playback/capture controls for input pins */
-static int alc260_auto_create_analog_input_ctls(struct alc_spec *spec,
+static int alc260_auto_create_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (cfg->input_pins[i] >= 0x12) {
- idx = cfg->input_pins[i] - 0x12;
- err = new_analog_input(spec, cfg->input_pins[i],
- auto_pin_cfg_labels[i], idx,
- 0x07);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label =
- auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- if (cfg->input_pins[i] >= 0x0f && cfg->input_pins[i] <= 0x10){
- idx = cfg->input_pins[i] - 0x09;
- err = new_analog_input(spec, cfg->input_pins[i],
- auto_pin_cfg_labels[i], idx,
- 0x07);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label =
- auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- }
- return 0;
+ return alc_auto_create_input_ctls(codec, cfg, 0x07, 0x04, 0x05);
}
static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
@@ -5999,7 +6166,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
return err;
if (!spec->kctls.list)
return 0; /* can't find valid BIOS pin config */
- err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = alc260_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -6234,8 +6401,7 @@ static int patch_alc260(struct hda_codec *codec)
alc260_models,
alc260_cfg_tbl);
if (board_config < 0) {
- snd_printd(KERN_INFO "hda_codec: Unknown model for %s, "
- "trying auto-probe from BIOS...\n",
+ snd_printd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
codec->chip_name);
board_config = ALC260_AUTO;
}
@@ -6261,7 +6427,7 @@ static int patch_alc260(struct hda_codec *codec)
}
if (board_config != ALC260_AUTO)
- setup_preset(spec, &alc260_presets[board_config]);
+ setup_preset(codec, &alc260_presets[board_config]);
spec->stream_analog_playback = &alc260_pcm_analog_playback;
spec->stream_analog_capture = &alc260_pcm_analog_capture;
@@ -6272,7 +6438,7 @@ static int patch_alc260(struct hda_codec *codec)
if (!spec->adc_nids && spec->input_mux) {
/* check whether NID 0x04 is valid */
unsigned int wcap = get_wcaps(codec, 0x04);
- wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ wcap = get_wcaps_type(wcap);
/* get type */
if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
spec->adc_nids = alc260_adc_nids_alt;
@@ -6282,7 +6448,7 @@ static int patch_alc260(struct hda_codec *codec)
spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
}
}
- set_capture_mixer(spec);
+ set_capture_mixer(codec);
set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
spec->vmaster_nid = 0x08;
@@ -6301,7 +6467,7 @@ static int patch_alc260(struct hda_codec *codec)
/*
- * ALC882 support
+ * ALC882/883/885/888/889 support
*
* ALC882 is almost identical with ALC880 but has cleaner and more flexible
* configuration. Each pin widget can choose any input DACs and a mixer.
@@ -6313,22 +6479,35 @@ static int patch_alc260(struct hda_codec *codec)
*/
#define ALC882_DIGOUT_NID 0x06
#define ALC882_DIGIN_NID 0x0a
+#define ALC883_DIGOUT_NID ALC882_DIGOUT_NID
+#define ALC883_DIGIN_NID ALC882_DIGIN_NID
+#define ALC1200_DIGOUT_NID 0x10
+
static struct hda_channel_mode alc882_ch_modes[1] = {
{ 8, NULL }
};
+/* DACs */
static hda_nid_t alc882_dac_nids[4] = {
/* front, rear, clfe, rear_surr */
0x02, 0x03, 0x04, 0x05
};
+#define alc883_dac_nids alc882_dac_nids
-/* identical with ALC880 */
+/* ADCs */
#define alc882_adc_nids alc880_adc_nids
#define alc882_adc_nids_alt alc880_adc_nids_alt
+#define alc883_adc_nids alc882_adc_nids_alt
+static hda_nid_t alc883_adc_nids_alt[1] = { 0x08 };
+static hda_nid_t alc883_adc_nids_rev[2] = { 0x09, 0x08 };
+#define alc889_adc_nids alc880_adc_nids
static hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 };
static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 };
+#define alc883_capsrc_nids alc882_capsrc_nids_alt
+static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 };
+#define alc889_capsrc_nids alc882_capsrc_nids
/* input MUX */
/* FIXME: should be a matrix-type input source selection */
@@ -6343,6 +6522,17 @@ static struct hda_input_mux alc882_capture_source = {
},
};
+#define alc883_capture_source alc882_capture_source
+
+static struct hda_input_mux alc889_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Front Mic", 0x0 },
+ { "Mic", 0x3 },
+ { "Line", 0x2 },
+ },
+};
+
static struct hda_input_mux mb5_capture_source = {
.num_items = 3,
.items = {
@@ -6352,6 +6542,77 @@ static struct hda_input_mux mb5_capture_source = {
},
};
+static struct hda_input_mux alc883_3stack_6ch_intel = {
+ .num_items = 4,
+ .items = {
+ { "Mic", 0x1 },
+ { "Front Mic", 0x0 },
+ { "Line", 0x2 },
+ { "CD", 0x4 },
+ },
+};
+
+static struct hda_input_mux alc883_lenovo_101e_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x1 },
+ { "Line", 0x2 },
+ },
+};
+
+static struct hda_input_mux alc883_lenovo_nb0763_capture_source = {
+ .num_items = 4,
+ .items = {
+ { "Mic", 0x0 },
+ { "iMic", 0x1 },
+ { "Line", 0x2 },
+ { "CD", 0x4 },
+ },
+};
+
+static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x0 },
+ { "Int Mic", 0x1 },
+ },
+};
+
+static struct hda_input_mux alc883_lenovo_sky_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Mic", 0x0 },
+ { "Front Mic", 0x1 },
+ { "Line", 0x4 },
+ },
+};
+
+static struct hda_input_mux alc883_asus_eee1601_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x0 },
+ { "Line", 0x2 },
+ },
+};
+
+static struct hda_input_mux alc889A_mb31_capture_source = {
+ .num_items = 2,
+ .items = {
+ { "Mic", 0x0 },
+ /* Front Mic (0x01) unused */
+ { "Line", 0x2 },
+ /* Line 2 (0x03) unused */
+ /* CD (0x04) unsused? */
+ },
+};
+
+/*
+ * 2ch mode
+ */
+static struct hda_channel_mode alc883_3ST_2ch_modes[1] = {
+ { 2, NULL }
+};
+
/*
* 2ch mode
*/
@@ -6364,6 +6625,18 @@ static struct hda_verb alc882_3ST_ch2_init[] = {
};
/*
+ * 4ch mode
+ */
+static struct hda_verb alc882_3ST_ch4_init[] = {
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+/*
* 6ch mode
*/
static struct hda_verb alc882_3ST_ch6_init[] = {
@@ -6376,11 +6649,60 @@ static struct hda_verb alc882_3ST_ch6_init[] = {
{ } /* end */
};
-static struct hda_channel_mode alc882_3ST_6ch_modes[2] = {
+static struct hda_channel_mode alc882_3ST_6ch_modes[3] = {
{ 2, alc882_3ST_ch2_init },
+ { 4, alc882_3ST_ch4_init },
{ 6, alc882_3ST_ch6_init },
};
+#define alc883_3ST_6ch_modes alc882_3ST_6ch_modes
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_3ST_ch2_clevo_init[] = {
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc883_3ST_ch4_clevo_init[] = {
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_3ST_ch6_clevo_init[] = {
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+static struct hda_channel_mode alc883_3ST_6ch_clevo_modes[3] = {
+ { 2, alc883_3ST_ch2_clevo_init },
+ { 4, alc883_3ST_ch4_clevo_init },
+ { 6, alc883_3ST_ch6_clevo_init },
+};
+
+
/*
* 6ch mode
*/
@@ -6423,9 +6745,9 @@ static struct hda_verb alc885_mbp_ch2_init[] = {
};
/*
- * 6ch mode
+ * 4ch mode
*/
-static struct hda_verb alc885_mbp_ch6_init[] = {
+static struct hda_verb alc885_mbp_ch4_init[] = {
{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
@@ -6434,9 +6756,9 @@ static struct hda_verb alc885_mbp_ch6_init[] = {
{ } /* end */
};
-static struct hda_channel_mode alc885_mbp_6ch_modes[2] = {
+static struct hda_channel_mode alc885_mbp_4ch_modes[2] = {
{ 2, alc885_mbp_ch2_init },
- { 6, alc885_mbp_ch6_init },
+ { 4, alc885_mbp_ch4_init },
};
/*
@@ -6468,6 +6790,189 @@ static struct hda_channel_mode alc885_mb5_6ch_modes[2] = {
{ 6, alc885_mb5_ch6_init },
};
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_4ST_ch2_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc883_4ST_ch4_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_4ST_ch6_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc883_4ST_ch8_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+static struct hda_channel_mode alc883_4ST_8ch_modes[4] = {
+ { 2, alc883_4ST_ch2_init },
+ { 4, alc883_4ST_ch4_init },
+ { 6, alc883_4ST_ch6_init },
+ { 8, alc883_4ST_ch8_init },
+};
+
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_3ST_ch2_intel_init[] = {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc883_3ST_ch4_intel_init[] = {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_3ST_ch6_intel_init[] = {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { } /* end */
+};
+
+static struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = {
+ { 2, alc883_3ST_ch2_intel_init },
+ { 4, alc883_3ST_ch4_intel_init },
+ { 6, alc883_3ST_ch6_intel_init },
+};
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc889_ch2_intel_init[] = {
+ { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ { 0x19, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ { 0x16, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc889_ch6_intel_init[] = {
+ { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 },
+ { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ { } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc889_ch8_intel_init[] = {
+ { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
+ { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 },
+ { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 },
+ { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
+ { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x03 },
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+ { } /* end */
+};
+
+static struct hda_channel_mode alc889_8ch_intel_modes[3] = {
+ { 2, alc889_ch2_intel_init },
+ { 6, alc889_ch6_intel_init },
+ { 8, alc889_ch8_intel_init },
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_sixstack_ch6_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc883_sixstack_ch8_init[] = {
+ { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { } /* end */
+};
+
+static struct hda_channel_mode alc883_sixstack_modes[2] = {
+ { 6, alc883_sixstack_ch6_init },
+ { 8, alc883_sixstack_ch8_init },
+};
+
+
/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
* Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
*/
@@ -6497,10 +7002,11 @@ static struct snd_kcontrol_new alc882_base_mixer[] = {
};
static struct snd_kcontrol_new alc885_mbp3_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE ("Speaker Playback Switch", 0x14, 0x00, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+ HDA_BIND_MUTE ("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0e, 0x00, HDA_OUTPUT),
+ HDA_BIND_MUTE ("Headphone Playback Switch", 0x0e, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT),
@@ -6603,7 +7109,7 @@ static struct snd_kcontrol_new alc882_chmode_mixer[] = {
{ } /* end */
};
-static struct hda_verb alc882_init_verbs[] = {
+static struct hda_verb alc882_base_init_verbs[] = {
/* Front mixer: unmute input/output amp left and right (volume = 0) */
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
@@ -6621,6 +7127,13 @@ static struct hda_verb alc882_init_verbs[] = {
{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* mute analog input loopbacks */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
/* Front Pin: output 0 (0x0c) */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
@@ -6655,11 +7168,6 @@ static struct hda_verb alc882_init_verbs[] = {
/* FIXME: use matrix-type input source selection */
/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
/* Input mixer2 */
{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
@@ -6670,9 +7178,6 @@ static struct hda_verb alc882_init_verbs[] = {
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* ADC1: mute amp left and right */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
/* ADC2: mute amp left and right */
{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -6683,6 +7188,18 @@ static struct hda_verb alc882_init_verbs[] = {
{ }
};
+static struct hda_verb alc882_adc1_init_verbs[] = {
+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ /* ADC1: mute amp left and right */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+ { }
+};
+
static struct hda_verb alc882_eapd_verbs[] = {
/* change to EAPD mode */
{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
@@ -6690,6 +7207,110 @@ static struct hda_verb alc882_eapd_verbs[] = {
{ }
};
+static struct hda_verb alc889_eapd_verbs[] = {
+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+ { }
+};
+
+static struct hda_verb alc_hp15_unsol_verbs[] = {
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {}
+};
+
+static struct hda_verb alc885_init_verbs[] = {
+ /* Front mixer: unmute input/output amp left and right (volume = 0) */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* Rear mixer */
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* CLFE mixer */
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* Side mixer */
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+
+ /* mute analog input loopbacks */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+
+ /* Front HP Pin: output 0 (0x0c) */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* Front Pin: output 0 (0x0c) */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* Rear Pin: output 1 (0x0d) */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x19, AC_VERB_SET_CONNECT_SEL, 0x01},
+ /* CLFE Pin: output 2 (0x0e) */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
+ /* Side Pin: output 3 (0x0f) */
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+ /* Mic (rear) pin: input vref at 80% */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Front Mic pin: input vref at 80% */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ /* Line In pin: input */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+ /* Mixer elements: 0x18, , 0x1a, 0x1b */
+ /* Input mixer1 */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ /* Input mixer2 */
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ /* Input mixer3 */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ /* ADC2: mute amp left and right */
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ /* ADC3: mute amp left and right */
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+
+ { }
+};
+
+static struct hda_verb alc885_init_input_verbs[] = {
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ { }
+};
+
+
+/* Unmute Selector 24h and set the default input to front mic */
+static struct hda_verb alc889_init_input_verbs[] = {
+ {0x24, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ { }
+};
+
+
+#define alc883_init_verbs alc882_base_init_verbs
+
/* Mac Pro test */
static struct snd_kcontrol_new alc882_macpro_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -6814,14 +7435,18 @@ static struct hda_verb alc885_mbp3_init_verbs[] = {
{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ /* HP mixer */
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
/* Front Pin: output 0 (0x0c) */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP Pin: output 0 (0x0d) */
+ /* HP Pin: output 0 (0x0e) */
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x02},
{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
/* Mic (rear) pin: input vref at 80% */
{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
@@ -6893,23 +7518,21 @@ static struct hda_verb alc885_imac24_init_verbs[] = {
};
/* Toggle speaker-output according to the hp-jack state */
-static void alc885_imac24_automute_init_hook(struct hda_codec *codec)
+static void alc885_imac24_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x18;
spec->autocfg.speaker_pins[1] = 0x1a;
- alc_automute_amp(codec);
}
-static void alc885_mbp3_init_hook(struct hda_codec *codec)
+static void alc885_mbp3_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
- alc_automute_amp(codec);
}
@@ -6937,13 +7560,12 @@ static void alc882_targa_automute(struct hda_codec *codec)
spec->jack_present ? 1 : 3);
}
-static void alc882_targa_init_hook(struct hda_codec *codec)
+static void alc882_targa_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x1b;
- alc882_targa_automute(codec);
}
static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res)
@@ -7031,18 +7653,16 @@ static void alc885_macpro_init_hook(struct hda_codec *codec)
static void alc885_imac24_init_hook(struct hda_codec *codec)
{
alc885_macpro_init_hook(codec);
- alc885_imac24_automute_init_hook(codec);
+ alc_automute_amp(codec);
}
/*
* generic initialization of ADC, input mixers and output mixers
*/
-static struct hda_verb alc882_auto_init_verbs[] = {
+static struct hda_verb alc883_auto_init_verbs[] = {
/*
* Unmute ADC0-2 and set the default input to mic-in
*/
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -7083,11 +7703,6 @@ static struct hda_verb alc882_auto_init_verbs[] = {
/* FIXME: use matrix-type input source selection */
/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
/* Input mixer2 */
{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
@@ -7102,820 +7717,6 @@ static struct hda_verb alc882_auto_init_verbs[] = {
{ }
};
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc882_loopbacks alc880_loopbacks
-#endif
-
-/* pcm configuration: identical with ALC880 */
-#define alc882_pcm_analog_playback alc880_pcm_analog_playback
-#define alc882_pcm_analog_capture alc880_pcm_analog_capture
-#define alc882_pcm_digital_playback alc880_pcm_digital_playback
-#define alc882_pcm_digital_capture alc880_pcm_digital_capture
-
-/*
- * configuration and preset
- */
-static const char *alc882_models[ALC882_MODEL_LAST] = {
- [ALC882_3ST_DIG] = "3stack-dig",
- [ALC882_6ST_DIG] = "6stack-dig",
- [ALC882_ARIMA] = "arima",
- [ALC882_W2JC] = "w2jc",
- [ALC882_TARGA] = "targa",
- [ALC882_ASUS_A7J] = "asus-a7j",
- [ALC882_ASUS_A7M] = "asus-a7m",
- [ALC885_MACPRO] = "macpro",
- [ALC885_MB5] = "mb5",
- [ALC885_MBP3] = "mbp3",
- [ALC885_IMAC24] = "imac24",
- [ALC882_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc882_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
- SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J),
- SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M),
- SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
- SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */
- SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
- {}
-};
-
-static struct alc_config_preset alc882_presets[] = {
- [ALC882_3ST_DIG] = {
- .mixers = { alc882_base_mixer },
- .init_verbs = { alc882_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
- .channel_mode = alc882_ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_6ST_DIG] = {
- .mixers = { alc882_base_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
- .channel_mode = alc882_sixstack_modes,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_ARIMA] = {
- .mixers = { alc882_base_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_init_verbs, alc882_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
- .channel_mode = alc882_sixstack_modes,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_W2JC] = {
- .mixers = { alc882_w2jc_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_init_verbs, alc882_eapd_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- },
- [ALC885_MBP3] = {
- .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer },
- .init_verbs = { alc885_mbp3_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_mbp_6ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mbp_6ch_modes),
- .input_mux = &alc882_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc885_mbp3_init_hook,
- },
- [ALC885_MB5] = {
- .mixers = { alc885_mb5_mixer, alc882_chmode_mixer },
- .init_verbs = { alc885_mb5_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_mb5_6ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes),
- .input_mux = &mb5_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- },
- [ALC885_MACPRO] = {
- .mixers = { alc882_macpro_mixer },
- .init_verbs = { alc882_macpro_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
- .channel_mode = alc882_ch_modes,
- .input_mux = &alc882_capture_source,
- .init_hook = alc885_macpro_init_hook,
- },
- [ALC885_IMAC24] = {
- .mixers = { alc885_imac24_mixer },
- .init_verbs = { alc885_imac24_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
- .channel_mode = alc882_ch_modes,
- .input_mux = &alc882_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc885_imac24_init_hook,
- },
- [ALC882_TARGA] = {
- .mixers = { alc882_targa_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_init_verbs, alc880_gpio3_init_verbs,
- alc882_targa_verbs},
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
- .adc_nids = alc882_adc_nids,
- .capsrc_nids = alc882_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
- .channel_mode = alc882_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- .unsol_event = alc882_targa_unsol_event,
- .init_hook = alc882_targa_init_hook,
- },
- [ALC882_ASUS_A7J] = {
- .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_init_verbs, alc882_asus_a7j_verbs},
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
- .adc_nids = alc882_adc_nids,
- .capsrc_nids = alc882_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
- .channel_mode = alc882_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_ASUS_A7M] = {
- .mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_init_verbs, alc882_eapd_verbs,
- alc880_gpio1_init_verbs,
- alc882_asus_a7m_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- },
-};
-
-
-/*
- * Pin config fixes
- */
-enum {
- PINFIX_ABIT_AW9D_MAX
-};
-
-static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
- { 0x15, 0x01080104 }, /* side */
- { 0x16, 0x01011012 }, /* rear */
- { 0x17, 0x01016011 }, /* clfe */
- { }
-};
-
-static const struct alc_pincfg *alc882_pin_fixes[] = {
- [PINFIX_ABIT_AW9D_MAX] = alc882_abit_aw9d_pinfix,
-};
-
-static struct snd_pci_quirk alc882_pinfix_tbl[] = {
- SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
- {}
-};
-
-/*
- * BIOS auto configuration
- */
-static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int dac_idx)
-{
- /* set as output */
- struct alc_spec *spec = codec->spec;
- int idx;
-
- alc_set_pin_output(codec, nid, pin_type);
- if (spec->multiout.dac_nids[dac_idx] == 0x25)
- idx = 4;
- else
- idx = spec->multiout.dac_nids[dac_idx] - 2;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
-
-}
-
-static void alc882_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i <= HDA_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- if (nid)
- alc882_auto_set_output_and_unmute(codec, nid, pin_type,
- i);
- }
-}
-
-static void alc882_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front */
- /* use dac 0 */
- alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
- pin = spec->autocfg.speaker_pins[0];
- if (pin)
- alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
-}
-
-#define alc882_is_input_pin(nid) alc880_is_input_pin(nid)
-#define ALC882_PIN_CD_NID ALC880_PIN_CD_NID
-
-static void alc882_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
- if (!nid)
- continue;
- alc_set_input_pin(codec, nid, AUTO_PIN_FRONT_MIC /*i*/);
- if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
-}
-
-static void alc882_auto_init_input_src(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int c;
-
- for (c = 0; c < spec->num_adc_nids; c++) {
- hda_nid_t conn_list[HDA_MAX_NUM_INPUTS];
- hda_nid_t nid = spec->capsrc_nids[c];
- unsigned int mux_idx;
- const struct hda_input_mux *imux;
- int conns, mute, idx, item;
-
- conns = snd_hda_get_connections(codec, nid, conn_list,
- ARRAY_SIZE(conn_list));
- if (conns < 0)
- continue;
- mux_idx = c >= spec->num_mux_defs ? 0 : c;
- imux = &spec->input_mux[mux_idx];
- for (idx = 0; idx < conns; idx++) {
- /* if the current connection is the selected one,
- * unmute it as default - otherwise mute it
- */
- mute = AMP_IN_MUTE(idx);
- for (item = 0; item < imux->num_items; item++) {
- if (imux->items[item].index == idx) {
- if (spec->cur_mux[c] == item)
- mute = AMP_IN_UNMUTE(idx);
- break;
- }
- }
- /* check if we have a selector or mixer
- * we could check for the widget type instead, but
- * just check for Amp-In presence (in case of mixer
- * without amp-in there is something wrong, this
- * function shouldn't be used or capsrc nid is wrong)
- */
- if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- mute);
- else if (mute != AMP_IN_MUTE(idx))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- idx);
- }
- }
-}
-
-/* add mic boosts if needed */
-static int alc_auto_add_mic_boost(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- hda_nid_t nid;
-
- nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
- if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Mic Boost",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- }
- nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
- if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Front Mic Boost",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/* almost identical with ALC880 parser... */
-static int alc882_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err = alc880_parse_auto_config(codec);
-
- if (err < 0)
- return err;
- else if (!err)
- return 0; /* no config found */
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- /* hack - override the init verbs */
- spec->init_verbs[0] = alc882_auto_init_verbs;
-
- return 1; /* config found */
-}
-
-/* additional initialization for auto-configuration model */
-static void alc882_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc882_auto_init_multi_out(codec);
- alc882_auto_init_hp_out(codec);
- alc882_auto_init_analog_input(codec);
- alc882_auto_init_input_src(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-static int patch_alc883(struct hda_codec *codec); /* called in patch_alc882() */
-
-static int patch_alc882(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST,
- alc882_models,
- alc882_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
- /* Pick up systems that don't supply PCI SSID */
- switch (codec->subsystem_id) {
- case 0x106b0c00: /* Mac Pro */
- board_config = ALC885_MACPRO;
- break;
- case 0x106b1000: /* iMac 24 */
- case 0x106b2800: /* AppleTV */
- case 0x106b3e00: /* iMac 24 Aluminium */
- board_config = ALC885_IMAC24;
- break;
- case 0x106b00a0: /* MacBookPro3,1 - Another revision */
- case 0x106b00a1: /* Macbook (might be wrong - PCI SSID?) */
- case 0x106b00a4: /* MacbookPro4,1 */
- case 0x106b2c00: /* Macbook Pro rev3 */
- /* Macbook 3.1 (0x106b3600) is handled by patch_alc883() */
- case 0x106b3800: /* MacbookPro4,1 - latter revision */
- board_config = ALC885_MBP3;
- break;
- case 0x106b3f00: /* Macbook 5,1 */
- case 0x106b4000: /* Macbook Pro 5,1 - FIXME: HP jack sense
- * seems not working, so apparently
- * no perfect solution yet
- */
- board_config = ALC885_MB5;
- break;
- default:
- /* ALC889A is handled better as ALC888-compatible */
- if (codec->revision_id == 0x100101 ||
- codec->revision_id == 0x100103) {
- alc_free(codec);
- return patch_alc883(codec);
- }
- printk(KERN_INFO "hda_codec: Unknown model for %s, "
- "trying auto-probe from BIOS...\n",
- codec->chip_name);
- board_config = ALC882_AUTO;
- }
- }
-
- alc_fix_pincfg(codec, alc882_pinfix_tbl, alc882_pin_fixes);
-
- if (board_config == ALC882_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc882_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC882_3ST_DIG;
- }
- }
-
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
-
- if (board_config != ALC882_AUTO)
- setup_preset(spec, &alc882_presets[board_config]);
-
- spec->stream_analog_playback = &alc882_pcm_analog_playback;
- spec->stream_analog_capture = &alc882_pcm_analog_capture;
- /* FIXME: setup DAC5 */
- /*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/
- spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
-
- spec->stream_digital_playback = &alc882_pcm_digital_playback;
- spec->stream_digital_capture = &alc882_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- /* check whether NID 0x07 is valid */
- unsigned int wcap = get_wcaps(codec, 0x07);
- /* get type */
- wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
- if (wcap != AC_WID_AUD_IN) {
- spec->adc_nids = alc882_adc_nids_alt;
- spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt);
- spec->capsrc_nids = alc882_capsrc_nids_alt;
- } else {
- spec->adc_nids = alc882_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
- spec->capsrc_nids = alc882_capsrc_nids;
- }
- }
- set_capture_mixer(spec);
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- spec->vmaster_nid = 0x0c;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC882_AUTO)
- spec->init_hook = alc882_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc882_loopbacks;
-#endif
- codec->proc_widget_hook = print_realtek_coef;
-
- return 0;
-}
-
-/*
- * ALC883 support
- *
- * ALC883 is almost identical with ALC880 but has cleaner and more flexible
- * configuration. Each pin widget can choose any input DACs and a mixer.
- * Each ADC is connected from a mixer of all inputs. This makes possible
- * 6-channel independent captures.
- *
- * In addition, an independent DAC for the multi-playback (not used in this
- * driver yet).
- */
-#define ALC883_DIGOUT_NID 0x06
-#define ALC883_DIGIN_NID 0x0a
-
-#define ALC1200_DIGOUT_NID 0x10
-
-static hda_nid_t alc883_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x03, 0x04, 0x05
-};
-
-static hda_nid_t alc883_adc_nids[2] = {
- /* ADC1-2 */
- 0x08, 0x09,
-};
-
-static hda_nid_t alc883_adc_nids_alt[1] = {
- /* ADC1 */
- 0x08,
-};
-
-static hda_nid_t alc883_adc_nids_rev[2] = {
- /* ADC2-1 */
- 0x09, 0x08
-};
-
-#define alc889_adc_nids alc880_adc_nids
-
-static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 };
-
-static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 };
-
-#define alc889_capsrc_nids alc882_capsrc_nids
-
-/* input MUX */
-/* FIXME: should be a matrix-type input source selection */
-
-static struct hda_input_mux alc883_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc883_3stack_6ch_intel = {
- .num_items = 4,
- .items = {
- { "Mic", 0x1 },
- { "Front Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc883_lenovo_101e_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux alc883_lenovo_nb0763_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "iMic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Int Mic", 0x1 },
- },
-};
-
-static struct hda_input_mux alc883_lenovo_sky_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x4 },
- },
-};
-
-static struct hda_input_mux alc883_asus_eee1601_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux alc889A_mb31_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- /* Front Mic (0x01) unused */
- { "Line", 0x2 },
- /* Line 2 (0x03) unused */
- /* CD (0x04) unsused? */
- },
-};
-
-/*
- * 2ch mode
- */
-static struct hda_channel_mode alc883_3ST_2ch_modes[1] = {
- { 2, NULL }
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_3ST_ch2_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_3ST_ch4_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_3ST_ch6_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc883_3ST_6ch_modes[3] = {
- { 2, alc883_3ST_ch2_init },
- { 4, alc883_3ST_ch4_init },
- { 6, alc883_3ST_ch6_init },
-};
-
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_4ST_ch2_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_4ST_ch4_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_4ST_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc883_4ST_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc883_4ST_8ch_modes[4] = {
- { 2, alc883_4ST_ch2_init },
- { 4, alc883_4ST_ch4_init },
- { 6, alc883_4ST_ch6_init },
- { 8, alc883_4ST_ch8_init },
-};
-
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_3ST_ch2_intel_init[] = {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_3ST_ch4_intel_init[] = {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_3ST_ch6_intel_init[] = {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = {
- { 2, alc883_3ST_ch2_intel_init },
- { 4, alc883_3ST_ch4_intel_init },
- { 6, alc883_3ST_ch6_intel_init },
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_sixstack_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc883_sixstack_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-static struct hda_channel_mode alc883_sixstack_modes[2] = {
- { 6, alc883_sixstack_ch6_init },
- { 8, alc883_sixstack_ch8_init },
-};
-
/* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */
static struct hda_verb alc889A_mb31_ch2_init[] = {
{0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */
@@ -7966,34 +7767,7 @@ static struct hda_verb alc883_medion_eapd_verbs[] = {
{ }
};
-/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
- * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
- */
-
-static struct snd_kcontrol_new alc883_base_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
+#define alc883_base_mixer alc882_base_mixer
static struct snd_kcontrol_new alc883_mitac_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -8104,6 +7878,30 @@ static struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc885_8ch_intel_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
+ HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0f, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x1b, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -8344,93 +8142,14 @@ static struct snd_kcontrol_new alc883_chmode_mixer[] = {
{ } /* end */
};
-static struct hda_verb alc883_init_verbs[] = {
- /* ADC1: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC2: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Front mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Rear mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* CLFE mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Side mixer */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- /* mute analog input loopbacks */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* Front Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Rear Pin: output 1 (0x0d) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* CLFE Pin: output 2 (0x0e) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* Side Pin: output 3 (0x0f) */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line-2 In: Headphone output (output 0 - 0x0c) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- { }
-};
-
/* toggle speaker-output according to the hp-jack state */
-static void alc883_mitac_init_hook(struct hda_codec *codec)
+static void alc883_mitac_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
spec->autocfg.speaker_pins[1] = 0x17;
- alc_automute_amp(codec);
}
/* auto-toggle front mic */
@@ -8462,6 +8181,22 @@ static struct hda_verb alc883_mitac_verbs[] = {
{ } /* end */
};
+static struct hda_verb alc883_clevo_m540r_verbs[] = {
+ /* HP */
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ /* Int speaker */
+ /*{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},*/
+
+ /* enable unsolicited event */
+ /*
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
+ */
+
+ { } /* end */
+};
+
static struct hda_verb alc883_clevo_m720_verbs[] = {
/* HP */
{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -8585,7 +8320,7 @@ static struct hda_verb alc883_vaiott_verbs[] = {
{ } /* end */
};
-static void alc888_3st_hp_init_hook(struct hda_codec *codec)
+static void alc888_3st_hp_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -8593,7 +8328,6 @@ static void alc888_3st_hp_init_hook(struct hda_codec *codec)
spec->autocfg.speaker_pins[0] = 0x14;
spec->autocfg.speaker_pins[1] = 0x16;
spec->autocfg.speaker_pins[2] = 0x18;
- alc_automute_amp(codec);
}
static struct hda_verb alc888_3st_hp_verbs[] = {
@@ -8690,13 +8424,12 @@ static struct hda_verb alc883_medion_md2_verbs[] = {
};
/* toggle speaker-output according to the hp-jack state */
-static void alc883_medion_md2_init_hook(struct hda_codec *codec)
+static void alc883_medion_md2_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x15;
- alc_automute_amp(codec);
}
/* toggle speaker-output according to the hp-jack state */
@@ -8713,12 +8446,16 @@ static void alc883_clevo_m720_mic_automute(struct hda_codec *codec)
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
}
-static void alc883_clevo_m720_init_hook(struct hda_codec *codec)
+static void alc883_clevo_m720_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
+}
+
+static void alc883_clevo_m720_init_hook(struct hda_codec *codec)
+{
alc_automute_amp(codec);
alc883_clevo_m720_mic_automute(codec);
}
@@ -8737,22 +8474,20 @@ static void alc883_clevo_m720_unsol_event(struct hda_codec *codec,
}
/* toggle speaker-output according to the hp-jack state */
-static void alc883_2ch_fujitsu_pi2515_init_hook(struct hda_codec *codec)
+static void alc883_2ch_fujitsu_pi2515_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x15;
- alc_automute_amp(codec);
}
-static void alc883_haier_w66_init_hook(struct hda_codec *codec)
+static void alc883_haier_w66_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x1b;
spec->autocfg.speaker_pins[0] = 0x14;
- alc_automute_amp(codec);
}
static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
@@ -8791,14 +8526,13 @@ static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec,
}
/* toggle speaker-output according to the hp-jack state */
-static void alc883_acer_aspire_init_hook(struct hda_codec *codec)
+static void alc883_acer_aspire_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x15;
spec->autocfg.speaker_pins[1] = 0x16;
- alc_automute_amp(codec);
}
static struct hda_verb alc883_acer_eapd_verbs[] = {
@@ -8819,7 +8553,14 @@ static struct hda_verb alc883_acer_eapd_verbs[] = {
{ }
};
-static void alc888_6st_dell_init_hook(struct hda_codec *codec)
+static struct hda_verb alc888_acer_aspire_7730G_verbs[] = {
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ { } /* end */
+};
+
+static void alc888_6st_dell_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -8828,10 +8569,9 @@ static void alc888_6st_dell_init_hook(struct hda_codec *codec)
spec->autocfg.speaker_pins[1] = 0x15;
spec->autocfg.speaker_pins[2] = 0x16;
spec->autocfg.speaker_pins[3] = 0x17;
- alc_automute_amp(codec);
}
-static void alc888_lenovo_sky_init_hook(struct hda_codec *codec)
+static void alc888_lenovo_sky_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -8841,82 +8581,17 @@ static void alc888_lenovo_sky_init_hook(struct hda_codec *codec)
spec->autocfg.speaker_pins[2] = 0x16;
spec->autocfg.speaker_pins[3] = 0x17;
spec->autocfg.speaker_pins[4] = 0x1a;
- alc_automute_amp(codec);
}
-static void alc883_vaiott_init_hook(struct hda_codec *codec)
+static void alc883_vaiott_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
spec->autocfg.speaker_pins[1] = 0x17;
- alc_automute_amp(codec);
}
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc883_auto_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for
- * front panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /*
- * Set up output mixers (0x0c - 0x0f)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- /* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
- /* Input mixer2 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- { }
-};
-
static struct hda_verb alc888_asus_m90v_verbs[] = {
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
@@ -8927,19 +8602,7 @@ static struct hda_verb alc888_asus_m90v_verbs[] = {
{ } /* end */
};
-static void alc883_nb_mic_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
- snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
-}
-
-static void alc883_M90V_init_hook(struct hda_codec *codec)
+static void alc883_mode2_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -8947,26 +8610,11 @@ static void alc883_M90V_init_hook(struct hda_codec *codec)
spec->autocfg.speaker_pins[0] = 0x14;
spec->autocfg.speaker_pins[1] = 0x15;
spec->autocfg.speaker_pins[2] = 0x16;
- alc_automute_pin(codec);
-}
-
-static void alc883_mode2_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_MIC_EVENT:
- alc883_nb_mic_automute(codec);
- break;
- default:
- alc_sku_unsol_event(codec, res);
- break;
- }
-}
-
-static void alc883_mode2_inithook(struct hda_codec *codec)
-{
- alc883_M90V_init_hook(codec);
- alc883_nb_mic_automute(codec);
+ spec->ext_mic.pin = 0x18;
+ spec->int_mic.pin = 0x19;
+ spec->ext_mic.mux_idx = 0;
+ spec->int_mic.mux_idx = 1;
+ spec->auto_mic = 1;
}
static struct hda_verb alc888_asus_eee1601_verbs[] = {
@@ -9027,25 +8675,44 @@ static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res)
alc889A_mb31_automute(codec);
}
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc883_loopbacks alc880_loopbacks
+#define alc882_loopbacks alc880_loopbacks
#endif
/* pcm configuration: identical with ALC880 */
-#define alc883_pcm_analog_playback alc880_pcm_analog_playback
-#define alc883_pcm_analog_capture alc880_pcm_analog_capture
-#define alc883_pcm_analog_alt_capture alc880_pcm_analog_alt_capture
-#define alc883_pcm_digital_playback alc880_pcm_digital_playback
-#define alc883_pcm_digital_capture alc880_pcm_digital_capture
+#define alc882_pcm_analog_playback alc880_pcm_analog_playback
+#define alc882_pcm_analog_capture alc880_pcm_analog_capture
+#define alc882_pcm_digital_playback alc880_pcm_digital_playback
+#define alc882_pcm_digital_capture alc880_pcm_digital_capture
+
+static hda_nid_t alc883_slave_dig_outs[] = {
+ ALC1200_DIGOUT_NID, 0,
+};
+
+static hda_nid_t alc1200_slave_dig_outs[] = {
+ ALC883_DIGOUT_NID, 0,
+};
/*
* configuration and preset
*/
-static const char *alc883_models[ALC883_MODEL_LAST] = {
- [ALC883_3ST_2ch_DIG] = "3stack-dig",
+static const char *alc882_models[ALC882_MODEL_LAST] = {
+ [ALC882_3ST_DIG] = "3stack-dig",
+ [ALC882_6ST_DIG] = "6stack-dig",
+ [ALC882_ARIMA] = "arima",
+ [ALC882_W2JC] = "w2jc",
+ [ALC882_TARGA] = "targa",
+ [ALC882_ASUS_A7J] = "asus-a7j",
+ [ALC882_ASUS_A7M] = "asus-a7m",
+ [ALC885_MACPRO] = "macpro",
+ [ALC885_MB5] = "mb5",
+ [ALC885_MBP3] = "mbp3",
+ [ALC885_IMAC24] = "imac24",
+ [ALC883_3ST_2ch_DIG] = "3stack-2ch-dig",
[ALC883_3ST_6ch_DIG] = "3stack-6ch-dig",
[ALC883_3ST_6ch] = "3stack-6ch",
- [ALC883_6ST_DIG] = "6stack-dig",
+ [ALC883_6ST_DIG] = "alc883-6stack-dig",
[ALC883_TARGA_DIG] = "targa-dig",
[ALC883_TARGA_2ch_DIG] = "targa-2ch-dig",
[ALC883_TARGA_8ch_DIG] = "targa-8ch-dig",
@@ -9054,6 +8721,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
[ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g",
[ALC888_ACER_ASPIRE_6530G] = "acer-aspire-6530g",
[ALC888_ACER_ASPIRE_8930G] = "acer-aspire-8930g",
+ [ALC888_ACER_ASPIRE_7730G] = "acer-aspire-7730g",
[ALC883_MEDION] = "medion",
[ALC883_MEDION_MD2] = "medion-md2",
[ALC883_LAPTOP_EAPD] = "laptop-eapd",
@@ -9065,18 +8733,22 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
[ALC888_3ST_HP] = "3stack-hp",
[ALC888_6ST_DELL] = "6stack-dell",
[ALC883_MITAC] = "mitac",
+ [ALC883_CLEVO_M540R] = "clevo-m540r",
[ALC883_CLEVO_M720] = "clevo-m720",
[ALC883_FUJITSU_PI2515] = "fujitsu-pi2515",
[ALC888_FUJITSU_XA3530] = "fujitsu-xa3530",
[ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel",
+ [ALC889A_INTEL] = "intel-alc889a",
+ [ALC889_INTEL] = "intel-x58",
[ALC1200_ASUS_P5Q] = "asus-p5q",
[ALC889A_MB31] = "mb31",
[ALC883_SONY_VAIO_TT] = "sony-vaio-tt",
- [ALC883_AUTO] = "auto",
+ [ALC882_AUTO] = "auto",
};
-static struct snd_pci_quirk alc883_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG),
+static struct snd_pci_quirk alc882_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG),
+
SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE),
SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_ACER_ASPIRE),
@@ -9091,40 +8763,56 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
ALC888_ACER_ASPIRE_8930G),
SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G",
ALC888_ACER_ASPIRE_8930G),
- SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC883_AUTO),
- SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC883_AUTO),
+ SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC882_AUTO),
+ SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC882_AUTO),
SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G",
ALC888_ACER_ASPIRE_6530G),
SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G",
ALC888_ACER_ASPIRE_6530G),
+ SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G",
+ ALC888_ACER_ASPIRE_7730G),
/* default Acer -- disabled as it causes more problems.
* model=auto should work fine now
*/
/* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), */
+
SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
+
SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP),
SND_PCI_QUIRK(0x103c, 0x2a72, "HP Educ.ar", ALC888_3ST_HP),
+
+ SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
+ SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J),
+ SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M),
SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
+ SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
+ SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
+ SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
SND_PCI_QUIRK(0x1043, 0x8284, "Asus Z37E", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q),
SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601),
+
+ SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT),
SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
SND_PCI_QUIRK(0x1071, 0x8227, "Mitac 82801H", ALC883_MITAC),
SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC),
SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL),
SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
- SND_PCI_QUIRK(0x1458, 0xa002, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG),
+
SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
+ SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */
SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC883_TARGA_2ch_DIG),
+ SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG),
@@ -9133,6 +8821,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x42cd, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
@@ -9146,11 +8835,15 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x7350, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
+ SND_PCI_QUIRK(0x1462, 0xaa08, "MSI", ALC883_TARGA_2ch_DIG),
+
SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720),
SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720),
+ SND_PCI_QUIRK(0x1558, 0x5409, "Clevo laptop M540R", ALC883_CLEVO_M540R),
SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC883_LAPTOP_EAPD),
SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
+ /* SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), */
SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1100, "FSC AMILO Xi/Pi25xx",
ALC883_FUJITSU_PI2515),
@@ -9165,24 +8858,186 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
SND_PCI_QUIRK(0x17c0, 0x4085, "MEDION MD96630", ALC888_LENOVO_MS7195_DIG),
SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66),
+
SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL),
SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL),
SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC),
- SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC883_3ST_6ch_INTEL),
+ SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_INTEL),
+ SND_PCI_QUIRK(0x8086, 0x0021, "Intel IbexPeak", ALC889A_INTEL),
+ SND_PCI_QUIRK(0x8086, 0x3b56, "Intel IbexPeak", ALC889A_INTEL),
SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
- SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT),
- {}
-};
-static hda_nid_t alc883_slave_dig_outs[] = {
- ALC1200_DIGOUT_NID, 0,
+ {}
};
-static hda_nid_t alc1200_slave_dig_outs[] = {
- ALC883_DIGOUT_NID, 0,
+/* codec SSID table for Intel Mac */
+static struct snd_pci_quirk alc882_ssid_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC885_MBP3),
+ SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC885_MBP3),
+ SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC885_MBP3),
+ SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC885_MACPRO),
+ SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_IMAC24),
+ SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_IMAC24),
+ SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC885_MBP3),
+ SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889A_MB31),
+ SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3),
+ SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24),
+ SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC885_MB5),
+ /* FIXME: HP jack sense seems not working for MBP 5,1, so apparently
+ * no perfect solution yet
+ */
+ SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC885_MB5),
+ {} /* terminator */
};
-static struct alc_config_preset alc883_presets[] = {
+static struct alc_config_preset alc882_presets[] = {
+ [ALC882_3ST_DIG] = {
+ .mixers = { alc882_base_mixer },
+ .init_verbs = { alc882_base_init_verbs,
+ alc882_adc1_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ .dig_in_nid = ALC882_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
+ .channel_mode = alc882_ch_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc882_capture_source,
+ },
+ [ALC882_6ST_DIG] = {
+ .mixers = { alc882_base_mixer, alc882_chmode_mixer },
+ .init_verbs = { alc882_base_init_verbs,
+ alc882_adc1_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ .dig_in_nid = ALC882_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
+ .channel_mode = alc882_sixstack_modes,
+ .input_mux = &alc882_capture_source,
+ },
+ [ALC882_ARIMA] = {
+ .mixers = { alc882_base_mixer, alc882_chmode_mixer },
+ .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
+ alc882_eapd_verbs },
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
+ .channel_mode = alc882_sixstack_modes,
+ .input_mux = &alc882_capture_source,
+ },
+ [ALC882_W2JC] = {
+ .mixers = { alc882_w2jc_mixer, alc882_chmode_mixer },
+ .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
+ alc882_eapd_verbs, alc880_gpio1_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
+ .channel_mode = alc880_threestack_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc882_capture_source,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ },
+ [ALC885_MBP3] = {
+ .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer },
+ .init_verbs = { alc885_mbp3_init_verbs,
+ alc880_gpio1_init_verbs },
+ .num_dacs = 2,
+ .dac_nids = alc882_dac_nids,
+ .hp_nid = 0x04,
+ .channel_mode = alc885_mbp_4ch_modes,
+ .num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes),
+ .input_mux = &alc882_capture_source,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ .dig_in_nid = ALC882_DIGIN_NID,
+ .unsol_event = alc_automute_amp_unsol_event,
+ .setup = alc885_mbp3_setup,
+ .init_hook = alc_automute_amp,
+ },
+ [ALC885_MB5] = {
+ .mixers = { alc885_mb5_mixer, alc882_chmode_mixer },
+ .init_verbs = { alc885_mb5_init_verbs,
+ alc880_gpio1_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .channel_mode = alc885_mb5_6ch_modes,
+ .num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes),
+ .input_mux = &mb5_capture_source,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ .dig_in_nid = ALC882_DIGIN_NID,
+ },
+ [ALC885_MACPRO] = {
+ .mixers = { alc882_macpro_mixer },
+ .init_verbs = { alc882_macpro_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ .dig_in_nid = ALC882_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
+ .channel_mode = alc882_ch_modes,
+ .input_mux = &alc882_capture_source,
+ .init_hook = alc885_macpro_init_hook,
+ },
+ [ALC885_IMAC24] = {
+ .mixers = { alc885_imac24_mixer },
+ .init_verbs = { alc885_imac24_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ .dig_in_nid = ALC882_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
+ .channel_mode = alc882_ch_modes,
+ .input_mux = &alc882_capture_source,
+ .unsol_event = alc_automute_amp_unsol_event,
+ .setup = alc885_imac24_setup,
+ .init_hook = alc885_imac24_init_hook,
+ },
+ [ALC882_TARGA] = {
+ .mixers = { alc882_targa_mixer, alc882_chmode_mixer },
+ .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
+ alc880_gpio3_init_verbs, alc882_targa_verbs},
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
+ .adc_nids = alc882_adc_nids,
+ .capsrc_nids = alc882_capsrc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
+ .channel_mode = alc882_3ST_6ch_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc882_capture_source,
+ .unsol_event = alc882_targa_unsol_event,
+ .setup = alc882_targa_setup,
+ .init_hook = alc882_targa_automute,
+ },
+ [ALC882_ASUS_A7J] = {
+ .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
+ .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
+ alc882_asus_a7j_verbs},
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
+ .adc_nids = alc882_adc_nids,
+ .capsrc_nids = alc882_capsrc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
+ .channel_mode = alc882_3ST_6ch_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc882_capture_source,
+ },
+ [ALC882_ASUS_A7M] = {
+ .mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer },
+ .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
+ alc882_eapd_verbs, alc880_gpio1_init_verbs,
+ alc882_asus_a7m_verbs },
+ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+ .dac_nids = alc882_dac_nids,
+ .dig_out_nid = ALC882_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
+ .channel_mode = alc880_threestack_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc882_capture_source,
+ },
[ALC883_3ST_2ch_DIG] = {
.mixers = { alc883_3ST_2ch_mixer },
.init_verbs = { alc883_init_verbs },
@@ -9229,6 +9084,46 @@ static struct alc_config_preset alc883_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc883_3stack_6ch_intel,
},
+ [ALC889A_INTEL] = {
+ .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc885_init_verbs, alc885_init_input_verbs,
+ alc_hp15_unsol_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
+ .adc_nids = alc889_adc_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .dig_in_nid = ALC883_DIGIN_NID,
+ .slave_dig_outs = alc883_slave_dig_outs,
+ .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes),
+ .channel_mode = alc889_8ch_intel_modes,
+ .capsrc_nids = alc889_capsrc_nids,
+ .input_mux = &alc889_capture_source,
+ .setup = alc889_automute_setup,
+ .init_hook = alc_automute_amp,
+ .unsol_event = alc_automute_amp_unsol_event,
+ .need_dac_fix = 1,
+ },
+ [ALC889_INTEL] = {
+ .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc885_init_verbs, alc889_init_input_verbs,
+ alc889_eapd_verbs, alc_hp15_unsol_verbs},
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
+ .adc_nids = alc889_adc_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .dig_in_nid = ALC883_DIGIN_NID,
+ .slave_dig_outs = alc883_slave_dig_outs,
+ .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes),
+ .channel_mode = alc889_8ch_intel_modes,
+ .capsrc_nids = alc889_capsrc_nids,
+ .input_mux = &alc889_capture_source,
+ .setup = alc889_automute_setup,
+ .init_hook = alc889_intel_init_hook,
+ .unsol_event = alc_automute_amp_unsol_event,
+ .need_dac_fix = 1,
+ },
[ALC883_6ST_DIG] = {
.mixers = { alc883_base_mixer, alc883_chmode_mixer },
.init_verbs = { alc883_init_verbs },
@@ -9252,7 +9147,8 @@ static struct alc_config_preset alc883_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc883_capture_source,
.unsol_event = alc883_targa_unsol_event,
- .init_hook = alc883_targa_init_hook,
+ .setup = alc882_targa_setup,
+ .init_hook = alc882_targa_automute,
},
[ALC883_TARGA_2ch_DIG] = {
.mixers = { alc883_targa_2ch_mixer},
@@ -9267,7 +9163,8 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_capture_source,
.unsol_event = alc883_targa_unsol_event,
- .init_hook = alc883_targa_init_hook,
+ .setup = alc882_targa_setup,
+ .init_hook = alc882_targa_automute,
},
[ALC883_TARGA_8ch_DIG] = {
.mixers = { alc883_base_mixer, alc883_chmode_mixer },
@@ -9285,7 +9182,8 @@ static struct alc_config_preset alc883_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc883_capture_source,
.unsol_event = alc883_targa_unsol_event,
- .init_hook = alc883_targa_init_hook,
+ .setup = alc882_targa_setup,
+ .init_hook = alc882_targa_automute,
},
[ALC883_ACER] = {
.mixers = { alc883_base_mixer },
@@ -9311,7 +9209,8 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc883_acer_aspire_init_hook,
+ .setup = alc883_acer_aspire_setup,
+ .init_hook = alc_automute_amp,
},
[ALC888_ACER_ASPIRE_4930G] = {
.mixers = { alc888_base_mixer,
@@ -9331,7 +9230,8 @@ static struct alc_config_preset alc883_presets[] = {
ARRAY_SIZE(alc888_2_capture_sources),
.input_mux = alc888_2_capture_sources,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc888_acer_aspire_4930g_init_hook,
+ .setup = alc888_acer_aspire_4930g_setup,
+ .init_hook = alc_automute_amp,
},
[ALC888_ACER_ASPIRE_6530G] = {
.mixers = { alc888_acer_aspire_6530_mixer },
@@ -9349,7 +9249,8 @@ static struct alc_config_preset alc883_presets[] = {
ARRAY_SIZE(alc888_2_capture_sources),
.input_mux = alc888_acer_aspire_6530_sources,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc888_acer_aspire_6530g_init_hook,
+ .setup = alc888_acer_aspire_6530g_setup,
+ .init_hook = alc_automute_amp,
},
[ALC888_ACER_ASPIRE_8930G] = {
.mixers = { alc888_base_mixer,
@@ -9370,7 +9271,28 @@ static struct alc_config_preset alc883_presets[] = {
ARRAY_SIZE(alc889_capture_sources),
.input_mux = alc889_capture_sources,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc889_acer_aspire_8930g_init_hook,
+ .setup = alc889_acer_aspire_8930g_setup,
+ .init_hook = alc_automute_amp,
+ },
+ [ALC888_ACER_ASPIRE_7730G] = {
+ .mixers = { alc883_3ST_6ch_mixer,
+ alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
+ alc888_acer_aspire_7730G_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
+ .adc_nids = alc883_adc_nids_rev,
+ .capsrc_nids = alc883_capsrc_nids_rev,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+ .channel_mode = alc883_3ST_6ch_modes,
+ .need_dac_fix = 1,
+ .const_channel_count = 6,
+ .input_mux = &alc883_capture_source,
+ .unsol_event = alc_automute_amp_unsol_event,
+ .setup = alc888_acer_aspire_6530g_setup,
+ .init_hook = alc_automute_amp,
},
[ALC883_MEDION] = {
.mixers = { alc883_fivestack_mixer,
@@ -9395,7 +9317,8 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc883_medion_md2_init_hook,
+ .setup = alc883_medion_md2_setup,
+ .init_hook = alc_automute_amp,
},
[ALC883_LAPTOP_EAPD] = {
.mixers = { alc883_base_mixer },
@@ -9406,6 +9329,21 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_capture_source,
},
+ [ALC883_CLEVO_M540R] = {
+ .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs, alc883_clevo_m540r_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .dig_in_nid = ALC883_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_clevo_modes),
+ .channel_mode = alc883_3ST_6ch_clevo_modes,
+ .need_dac_fix = 1,
+ .input_mux = &alc883_capture_source,
+ /* This machine has the hardware HP auto-muting, thus
+ * we need no software mute via unsol event
+ */
+ },
[ALC883_CLEVO_M720] = {
.mixers = { alc883_clevo_m720_mixer },
.init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs },
@@ -9416,6 +9354,7 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_capture_source,
.unsol_event = alc883_clevo_m720_unsol_event,
+ .setup = alc883_clevo_m720_setup,
.init_hook = alc883_clevo_m720_init_hook,
},
[ALC883_LENOVO_101E_2ch] = {
@@ -9441,7 +9380,8 @@ static struct alc_config_preset alc883_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc883_lenovo_nb0763_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc883_medion_md2_init_hook,
+ .setup = alc883_medion_md2_setup,
+ .init_hook = alc_automute_amp,
},
[ALC888_LENOVO_MS7195_DIG] = {
.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9466,7 +9406,8 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc883_haier_w66_init_hook,
+ .setup = alc883_haier_w66_setup,
+ .init_hook = alc_automute_amp,
},
[ALC888_3ST_HP] = {
.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9478,7 +9419,8 @@ static struct alc_config_preset alc883_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc883_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc888_3st_hp_init_hook,
+ .setup = alc888_3st_hp_setup,
+ .init_hook = alc_automute_amp,
},
[ALC888_6ST_DELL] = {
.mixers = { alc883_base_mixer, alc883_chmode_mixer },
@@ -9491,7 +9433,8 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_sixstack_modes,
.input_mux = &alc883_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc888_6st_dell_init_hook,
+ .setup = alc888_6st_dell_setup,
+ .init_hook = alc_automute_amp,
},
[ALC883_MITAC] = {
.mixers = { alc883_mitac_mixer },
@@ -9502,7 +9445,8 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc883_mitac_init_hook,
+ .setup = alc883_mitac_setup,
+ .init_hook = alc_automute_amp,
},
[ALC883_FUJITSU_PI2515] = {
.mixers = { alc883_2ch_fujitsu_pi2515_mixer },
@@ -9515,7 +9459,8 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_fujitsu_pi2515_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc883_2ch_fujitsu_pi2515_init_hook,
+ .setup = alc883_2ch_fujitsu_pi2515_setup,
+ .init_hook = alc_automute_amp,
},
[ALC888_FUJITSU_XA3530] = {
.mixers = { alc888_base_mixer, alc883_chmode_mixer },
@@ -9533,7 +9478,8 @@ static struct alc_config_preset alc883_presets[] = {
ARRAY_SIZE(alc888_2_capture_sources),
.input_mux = alc888_2_capture_sources,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc888_fujitsu_xa3530_init_hook,
+ .setup = alc888_fujitsu_xa3530_setup,
+ .init_hook = alc_automute_amp,
},
[ALC888_LENOVO_SKY] = {
.mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
@@ -9546,7 +9492,8 @@ static struct alc_config_preset alc883_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc883_lenovo_sky_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc888_lenovo_sky_init_hook,
+ .setup = alc888_lenovo_sky_setup,
+ .init_hook = alc_automute_amp,
},
[ALC888_ASUS_M90V] = {
.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9559,8 +9506,9 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_3ST_6ch_modes,
.need_dac_fix = 1,
.input_mux = &alc883_fujitsu_pi2515_capture_source,
- .unsol_event = alc883_mode2_unsol_event,
- .init_hook = alc883_mode2_inithook,
+ .unsol_event = alc_sku_unsol_event,
+ .setup = alc883_mode2_setup,
+ .init_hook = alc_inithook,
},
[ALC888_ASUS_EEE1601] = {
.mixers = { alc883_asus_eee1601_mixer },
@@ -9613,15 +9561,45 @@ static struct alc_config_preset alc883_presets[] = {
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc883_vaiott_init_hook,
+ .setup = alc883_vaiott_setup,
+ .init_hook = alc_automute_amp,
},
};
/*
+ * Pin config fixes
+ */
+enum {
+ PINFIX_ABIT_AW9D_MAX
+};
+
+static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
+ { 0x15, 0x01080104 }, /* side */
+ { 0x16, 0x01011012 }, /* rear */
+ { 0x17, 0x01016011 }, /* clfe */
+ { }
+};
+
+static const struct alc_pincfg *alc882_pin_fixes[] = {
+ [PINFIX_ABIT_AW9D_MAX] = alc882_abit_aw9d_pinfix,
+};
+
+static struct snd_pci_quirk alc882_pinfix_tbl[] = {
+ SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
+ {}
+};
+
+/*
* BIOS auto configuration
*/
-static void alc883_auto_set_output_and_unmute(struct hda_codec *codec,
+static int alc882_auto_create_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x23, 0x22);
+}
+
+static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
hda_nid_t nid, int pin_type,
int dac_idx)
{
@@ -9638,7 +9616,7 @@ static void alc883_auto_set_output_and_unmute(struct hda_codec *codec,
}
-static void alc883_auto_init_multi_out(struct hda_codec *codec)
+static void alc882_auto_init_multi_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
int i;
@@ -9647,12 +9625,12 @@ static void alc883_auto_init_multi_out(struct hda_codec *codec)
hda_nid_t nid = spec->autocfg.line_out_pins[i];
int pin_type = get_pin_type(spec->autocfg.line_out_type);
if (nid)
- alc883_auto_set_output_and_unmute(codec, nid, pin_type,
+ alc882_auto_set_output_and_unmute(codec, nid, pin_type,
i);
}
}
-static void alc883_auto_init_hp_out(struct hda_codec *codec)
+static void alc882_auto_init_hp_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
hda_nid_t pin;
@@ -9660,91 +9638,191 @@ static void alc883_auto_init_hp_out(struct hda_codec *codec)
pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front */
/* use dac 0 */
- alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+ alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
pin = spec->autocfg.speaker_pins[0];
if (pin)
- alc883_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
+ alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
}
-#define alc883_is_input_pin(nid) alc880_is_input_pin(nid)
-#define ALC883_PIN_CD_NID ALC880_PIN_CD_NID
-
-static void alc883_auto_init_analog_input(struct hda_codec *codec)
+static void alc882_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
int i;
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i];
- if (alc883_is_input_pin(nid)) {
- alc_set_input_pin(codec, nid, i);
- if (nid != ALC883_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
+ if (!nid)
+ continue;
+ alc_set_input_pin(codec, nid, i);
+ if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE);
+ }
+}
+
+static void alc882_auto_init_input_src(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int c;
+
+ for (c = 0; c < spec->num_adc_nids; c++) {
+ hda_nid_t conn_list[HDA_MAX_NUM_INPUTS];
+ hda_nid_t nid = spec->capsrc_nids[c];
+ unsigned int mux_idx;
+ const struct hda_input_mux *imux;
+ int conns, mute, idx, item;
+
+ conns = snd_hda_get_connections(codec, nid, conn_list,
+ ARRAY_SIZE(conn_list));
+ if (conns < 0)
+ continue;
+ mux_idx = c >= spec->num_mux_defs ? 0 : c;
+ imux = &spec->input_mux[mux_idx];
+ for (idx = 0; idx < conns; idx++) {
+ /* if the current connection is the selected one,
+ * unmute it as default - otherwise mute it
+ */
+ mute = AMP_IN_MUTE(idx);
+ for (item = 0; item < imux->num_items; item++) {
+ if (imux->items[item].index == idx) {
+ if (spec->cur_mux[c] == item)
+ mute = AMP_IN_UNMUTE(idx);
+ break;
+ }
+ }
+ /* check if we have a selector or mixer
+ * we could check for the widget type instead, but
+ * just check for Amp-In presence (in case of mixer
+ * without amp-in there is something wrong, this
+ * function shouldn't be used or capsrc nid is wrong)
+ */
+ if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
+ mute);
+ else if (mute != AMP_IN_MUTE(idx))
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ idx);
}
}
}
-#define alc883_auto_init_input_src alc882_auto_init_input_src
+/* add mic boosts if needed */
+static int alc_auto_add_mic_boost(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err;
+ hda_nid_t nid;
+
+ nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
+ if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
+ err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ "Mic Boost",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
+ if (err < 0)
+ return err;
+ }
+ nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
+ if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
+ err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ "Front Mic Boost",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
/* almost identical with ALC880 parser... */
-static int alc883_parse_auto_config(struct hda_codec *codec)
+static int alc882_parse_auto_config(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- int err = alc880_parse_auto_config(codec);
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
+ static hda_nid_t alc882_ignore[] = { 0x1d, 0 };
+ int i, err;
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+ alc882_ignore);
if (err < 0)
return err;
- else if (!err)
- return 0; /* no config found */
+ if (!spec->autocfg.line_outs)
+ return 0; /* can't find valid BIOS pin config */
- err = alc_auto_add_mic_boost(codec);
+ err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = alc880_auto_create_extra_out(spec,
+ spec->autocfg.speaker_pins[0],
+ "Speaker");
+ if (err < 0)
+ return err;
+ err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
+ "Headphone");
+ if (err < 0)
+ return err;
+ err = alc882_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
- /* hack - override the init verbs */
- spec->init_verbs[0] = alc883_auto_init_verbs;
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- /* setup input_mux for ALC889 */
- if (codec->vendor_id == 0x10ec0889) {
- /* digital-mic input pin is excluded in alc880_auto_create..()
- * because it's under 0x18
- */
- if (cfg->input_pins[AUTO_PIN_MIC] == 0x12 ||
- cfg->input_pins[AUTO_PIN_FRONT_MIC] == 0x12) {
- struct hda_input_mux *imux = &spec->private_imux[0];
- for (i = 1; i < 3; i++)
- memcpy(&spec->private_imux[i],
- &spec->private_imux[0],
- sizeof(spec->private_imux[0]));
- imux->items[imux->num_items].label = "Int DMic";
- imux->items[imux->num_items].index = 0x0b;
- imux->num_items++;
- spec->num_mux_defs = 3;
- spec->input_mux = spec->private_imux;
+ /* check multiple SPDIF-out (for recent codecs) */
+ for (i = 0; i < spec->autocfg.dig_outs; i++) {
+ hda_nid_t dig_nid;
+ err = snd_hda_get_connections(codec,
+ spec->autocfg.dig_out_pins[i],
+ &dig_nid, 1);
+ if (err < 0)
+ continue;
+ if (!i)
+ spec->multiout.dig_out_nid = dig_nid;
+ else {
+ spec->multiout.slave_dig_outs = spec->slave_dig_outs;
+ spec->slave_dig_outs[i - 1] = dig_nid;
+ if (i == ARRAY_SIZE(spec->slave_dig_outs) - 1)
+ break;
}
}
+ if (spec->autocfg.dig_in_pin)
+ spec->dig_in_nid = ALC880_DIGIN_NID;
+
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
+
+ add_verb(spec, alc883_auto_init_verbs);
+ /* if ADC 0x07 is available, initialize it, too */
+ if (get_wcaps_type(get_wcaps(codec, 0x07)) == AC_WID_AUD_IN)
+ add_verb(spec, alc882_adc1_init_verbs);
+
+ spec->num_mux_defs = 1;
+ spec->input_mux = &spec->private_imux[0];
+
+ alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+
+ err = alc_auto_add_mic_boost(codec);
+ if (err < 0)
+ return err;
return 1; /* config found */
}
/* additional initialization for auto-configuration model */
-static void alc883_auto_init(struct hda_codec *codec)
+static void alc882_auto_init(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- alc883_auto_init_multi_out(codec);
- alc883_auto_init_hp_out(codec);
- alc883_auto_init_analog_input(codec);
- alc883_auto_init_input_src(codec);
+ alc882_auto_init_multi_out(codec);
+ alc882_auto_init_hp_out(codec);
+ alc882_auto_init_analog_input(codec);
+ alc882_auto_init_input_src(codec);
if (spec->unsol_event)
alc_inithook(codec);
}
-static int patch_alc883(struct hda_codec *codec)
+static int patch_alc882(struct hda_codec *codec)
{
struct alc_spec *spec;
int err, board_config;
@@ -9755,28 +9833,35 @@ static int patch_alc883(struct hda_codec *codec)
codec->spec = spec;
- alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+ switch (codec->vendor_id) {
+ case 0x10ec0882:
+ case 0x10ec0885:
+ break;
+ default:
+ /* ALC883 and variants */
+ alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+ break;
+ }
- board_config = snd_hda_check_board_config(codec, ALC883_MODEL_LAST,
- alc883_models,
- alc883_cfg_tbl);
- if (board_config < 0 || board_config >= ALC883_MODEL_LAST) {
- /* Pick up systems that don't supply PCI SSID */
- switch (codec->subsystem_id) {
- case 0x106b3600: /* Macbook 3.1 */
- board_config = ALC889A_MB31;
- break;
- default:
- printk(KERN_INFO
- "hda_codec: Unknown model for %s, trying "
- "auto-probe from BIOS...\n", codec->chip_name);
- board_config = ALC883_AUTO;
- }
+ board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST,
+ alc882_models,
+ alc882_cfg_tbl);
+
+ if (board_config < 0 || board_config >= ALC882_MODEL_LAST)
+ board_config = snd_hda_check_board_codec_sid_config(codec,
+ ALC882_MODEL_LAST, alc882_models, alc882_ssid_cfg_tbl);
+
+ if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
+ printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
+ board_config = ALC882_AUTO;
}
- if (board_config == ALC883_AUTO) {
+ alc_fix_pincfg(codec, alc882_pinfix_tbl, alc882_pin_fixes);
+
+ if (board_config == ALC882_AUTO) {
/* automatic parse from the BIOS config */
- err = alc883_parse_auto_config(codec);
+ err = alc882_parse_auto_config(codec);
if (err < 0) {
alc_free(codec);
return err;
@@ -9784,7 +9869,7 @@ static int patch_alc883(struct hda_codec *codec)
printk(KERN_INFO
"hda_codec: Cannot set up configuration "
"from BIOS. Using base mode...\n");
- board_config = ALC883_3ST_2ch_DIG;
+ board_config = ALC882_3ST_DIG;
}
}
@@ -9794,63 +9879,61 @@ static int patch_alc883(struct hda_codec *codec)
return err;
}
- if (board_config != ALC883_AUTO)
- setup_preset(spec, &alc883_presets[board_config]);
+ if (board_config != ALC882_AUTO)
+ setup_preset(codec, &alc882_presets[board_config]);
- switch (codec->vendor_id) {
- case 0x10ec0888:
- if (!spec->num_adc_nids) {
- spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
- spec->adc_nids = alc883_adc_nids;
- }
- if (!spec->capsrc_nids)
- spec->capsrc_nids = alc883_capsrc_nids;
+ spec->stream_analog_playback = &alc882_pcm_analog_playback;
+ spec->stream_analog_capture = &alc882_pcm_analog_capture;
+ /* FIXME: setup DAC5 */
+ /*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/
+ spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
+
+ spec->stream_digital_playback = &alc882_pcm_digital_playback;
+ spec->stream_digital_capture = &alc882_pcm_digital_capture;
+
+ if (codec->vendor_id == 0x10ec0888)
spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */
- break;
- case 0x10ec0889:
- if (!spec->num_adc_nids) {
- spec->num_adc_nids = ARRAY_SIZE(alc889_adc_nids);
- spec->adc_nids = alc889_adc_nids;
- }
- if (!spec->capsrc_nids)
- spec->capsrc_nids = alc889_capsrc_nids;
- break;
- default:
- if (!spec->num_adc_nids) {
- spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
- spec->adc_nids = alc883_adc_nids;
+
+ if (!spec->adc_nids && spec->input_mux) {
+ int i;
+ spec->num_adc_nids = 0;
+ for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) {
+ hda_nid_t cap;
+ hda_nid_t nid = alc882_adc_nids[i];
+ unsigned int wcap = get_wcaps(codec, nid);
+ /* get type */
+ wcap = get_wcaps_type(wcap);
+ if (wcap != AC_WID_AUD_IN)
+ continue;
+ spec->private_adc_nids[spec->num_adc_nids] = nid;
+ err = snd_hda_get_connections(codec, nid, &cap, 1);
+ if (err < 0)
+ continue;
+ spec->private_capsrc_nids[spec->num_adc_nids] = cap;
+ spec->num_adc_nids++;
}
- if (!spec->capsrc_nids)
- spec->capsrc_nids = alc883_capsrc_nids;
- break;
+ spec->adc_nids = spec->private_adc_nids;
+ spec->capsrc_nids = spec->private_capsrc_nids;
}
- spec->stream_analog_playback = &alc883_pcm_analog_playback;
- spec->stream_analog_capture = &alc883_pcm_analog_capture;
- spec->stream_analog_alt_capture = &alc883_pcm_analog_alt_capture;
-
- spec->stream_digital_playback = &alc883_pcm_digital_playback;
- spec->stream_digital_capture = &alc883_pcm_digital_capture;
-
- if (!spec->cap_mixer)
- set_capture_mixer(spec);
+ set_capture_mixer(codec);
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
spec->vmaster_nid = 0x0c;
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC883_AUTO)
- spec->init_hook = alc883_auto_init;
-
+ if (board_config == ALC882_AUTO)
+ spec->init_hook = alc882_auto_init;
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
- spec->loopback.amplist = alc883_loopbacks;
+ spec->loopback.amplist = alc882_loopbacks;
#endif
codec->proc_widget_hook = print_realtek_coef;
return 0;
}
+
/*
* ALC262 support
*/
@@ -10026,13 +10109,12 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
};
/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hp_t5735_init_hook(struct hda_codec *codec)
+static void alc262_hp_t5735_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x0c; /* HACK: not actually a pin */
- alc_automute_amp(codec);
}
static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
@@ -10189,22 +10271,20 @@ static void alc262_hippo_unsol_event(struct hda_codec *codec, unsigned int res)
alc262_hippo_automute(codec);
}
-static void alc262_hippo_init_hook(struct hda_codec *codec)
+static void alc262_hippo_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
- alc262_hippo_automute(codec);
}
-static void alc262_hippo1_init_hook(struct hda_codec *codec)
+static void alc262_hippo1_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x1b;
spec->autocfg.speaker_pins[0] = 0x14;
- alc262_hippo_automute(codec);
}
@@ -10261,13 +10341,12 @@ static struct hda_verb alc262_tyan_verbs[] = {
};
/* unsolicited event for HP jack sensing */
-static void alc262_tyan_init_hook(struct hda_codec *codec)
+static void alc262_tyan_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x1b;
spec->autocfg.speaker_pins[0] = 0x15;
- alc_automute_amp(codec);
}
@@ -10359,12 +10438,6 @@ static struct hda_verb alc262_eapd_verbs[] = {
{ }
};
-static struct hda_verb alc262_hippo_unsol_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
-
static struct hda_verb alc262_hippo1_unsol_verbs[] = {
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -10385,14 +10458,6 @@ static struct hda_verb alc262_sony_unsol_verbs[] = {
{}
};
-static struct hda_input_mux alc262_dmic_capture_source = {
- .num_items = 2,
- .items = {
- { "Int DMic", 0x9 },
- { "Mic", 0x0 },
- },
-};
-
static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = {
HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
@@ -10414,35 +10479,17 @@ static struct hda_verb alc262_toshiba_s06_verbs[] = {
{}
};
-static void alc262_dmic_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- snd_hda_codec_write(codec, 0x22, 0,
- AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x09);
-}
-
-
-/* unsolicited event for HP jack sensing */
-static void alc262_toshiba_s06_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc262_dmic_automute(codec);
- else
- alc_sku_unsol_event(codec, res);
-}
-
-static void alc262_toshiba_s06_init_hook(struct hda_codec *codec)
+static void alc262_toshiba_s06_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
- alc_automute_pin(codec);
- alc262_dmic_automute(codec);
+ spec->ext_mic.pin = 0x18;
+ spec->ext_mic.mux_idx = 0;
+ spec->int_mic.pin = 0x12;
+ spec->int_mic.mux_idx = 9;
+ spec->auto_mic = 1;
}
/*
@@ -10860,104 +10907,111 @@ static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = {
{ } /* end */
};
+/* We use two mixers depending on the output pin; 0x16 is a mono output
+ * and thus it's bound with a different mixer.
+ * This function returns which mixer amp should be used.
+ */
+static int alc262_check_volbit(hda_nid_t nid)
+{
+ if (!nid)
+ return 0;
+ else if (nid == 0x16)
+ return 2;
+ else
+ return 1;
+}
+
+static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
+ const char *pfx, int *vbits)
+{
+ char name[32];
+ unsigned long val;
+ int vbit;
+
+ vbit = alc262_check_volbit(nid);
+ if (!vbit)
+ return 0;
+ if (*vbits & vbit) /* a volume control for this mixer already there */
+ return 0;
+ *vbits |= vbit;
+ snprintf(name, sizeof(name), "%s Playback Volume", pfx);
+ if (vbit == 2)
+ val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
+ else
+ val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
+ return add_control(spec, ALC_CTL_WIDGET_VOL, name, val);
+}
+
+static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
+ const char *pfx)
+{
+ char name[32];
+ unsigned long val;
+
+ if (!nid)
+ return 0;
+ snprintf(name, sizeof(name), "%s Playback Switch", pfx);
+ if (nid == 0x16)
+ val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
+ else
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ return add_control(spec, ALC_CTL_WIDGET_MUTE, name, val);
+}
+
/* add playback controls from the parsed DAC table */
static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
const struct auto_pin_cfg *cfg)
{
- hda_nid_t nid;
+ const char *pfx;
+ int vbits;
int err;
spec->multiout.num_dacs = 1; /* only use one dac */
spec->multiout.dac_nids = spec->private_dac_nids;
spec->multiout.dac_nids[0] = 2;
- nid = cfg->line_out_pins[0];
- if (nid) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_control(spec, ALC_CTL_WIDGET_MUTE,
- "Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
-
- nid = cfg->speaker_pins[0];
- if (nid) {
- if (nid == 0x16) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Speaker Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x0e, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_control(spec, ALC_CTL_WIDGET_MUTE,
- "Speaker Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- err = add_control(spec, ALC_CTL_WIDGET_MUTE,
- "Speaker Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
- nid = cfg->hp_pins[0];
- if (nid) {
- /* spec->multiout.hp_nid = 2; */
- if (nid == 0x16) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x0e, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_control(spec, ALC_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- err = add_control(spec, ALC_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-static int alc262_auto_create_analog_input_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int err;
+ if (!cfg->speaker_pins[0] && !cfg->hp_pins[0])
+ pfx = "Master";
+ else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ pfx = "Speaker";
+ else
+ pfx = "Front";
+ err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[0], pfx);
+ if (err < 0)
+ return err;
+ err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[0], "Speaker");
+ if (err < 0)
+ return err;
+ err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[0], "Headphone");
+ if (err < 0)
+ return err;
- err = alc880_auto_create_analog_input_ctls(spec, cfg);
+ vbits = alc262_check_volbit(cfg->line_out_pins[0]) |
+ alc262_check_volbit(cfg->speaker_pins[0]) |
+ alc262_check_volbit(cfg->hp_pins[0]);
+ if (vbits == 1 || vbits == 2)
+ pfx = "Master"; /* only one mixer is used */
+ else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ pfx = "Speaker";
+ else
+ pfx = "Front";
+ vbits = 0;
+ err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[0], pfx, &vbits);
+ if (err < 0)
+ return err;
+ err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[0], "Speaker",
+ &vbits);
+ if (err < 0)
+ return err;
+ err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[0], "Headphone",
+ &vbits);
if (err < 0)
return err;
- /* digital-mic input pin is excluded in alc880_auto_create..()
- * because it's under 0x18
- */
- if (cfg->input_pins[AUTO_PIN_MIC] == 0x12 ||
- cfg->input_pins[AUTO_PIN_FRONT_MIC] == 0x12) {
- struct hda_input_mux *imux = &spec->private_imux[0];
- imux->items[imux->num_items].label = "Int Mic";
- imux->items[imux->num_items].index = 0x09;
- imux->num_items++;
- }
return 0;
}
+#define alc262_auto_create_input_ctls \
+ alc880_auto_create_input_ctls
/*
* generic initialization of ADC, input mixers and output mixers
@@ -11275,7 +11329,7 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg);
if (err < 0)
return err;
- err = alc262_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = alc262_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -11406,7 +11460,7 @@ static struct alc_config_preset alc262_presets[] = {
},
[ALC262_HIPPO] = {
.mixers = { alc262_hippo_mixer },
- .init_verbs = { alc262_init_verbs, alc262_hippo_unsol_verbs},
+ .init_verbs = { alc262_init_verbs, alc_hp15_unsol_verbs},
.num_dacs = ARRAY_SIZE(alc262_dac_nids),
.dac_nids = alc262_dac_nids,
.hp_nid = 0x03,
@@ -11415,7 +11469,8 @@ static struct alc_config_preset alc262_presets[] = {
.channel_mode = alc262_modes,
.input_mux = &alc262_capture_source,
.unsol_event = alc262_hippo_unsol_event,
- .init_hook = alc262_hippo_init_hook,
+ .setup = alc262_hippo_setup,
+ .init_hook = alc262_hippo_automute,
},
[ALC262_HIPPO_1] = {
.mixers = { alc262_hippo1_mixer },
@@ -11428,7 +11483,8 @@ static struct alc_config_preset alc262_presets[] = {
.channel_mode = alc262_modes,
.input_mux = &alc262_capture_source,
.unsol_event = alc262_hippo_unsol_event,
- .init_hook = alc262_hippo1_init_hook,
+ .setup = alc262_hippo1_setup,
+ .init_hook = alc262_hippo_automute,
},
[ALC262_FUJITSU] = {
.mixers = { alc262_fujitsu_mixer },
@@ -11491,7 +11547,8 @@ static struct alc_config_preset alc262_presets[] = {
.channel_mode = alc262_modes,
.input_mux = &alc262_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc262_hp_t5735_init_hook,
+ .setup = alc262_hp_t5735_setup,
+ .init_hook = alc_automute_amp,
},
[ALC262_HP_RP5700] = {
.mixers = { alc262_hp_rp5700_mixer },
@@ -11522,11 +11579,13 @@ static struct alc_config_preset alc262_presets[] = {
.channel_mode = alc262_modes,
.input_mux = &alc262_capture_source,
.unsol_event = alc262_hippo_unsol_event,
- .init_hook = alc262_hippo_init_hook,
+ .setup = alc262_hippo_setup,
+ .init_hook = alc262_hippo_automute,
},
[ALC262_BENQ_T31] = {
.mixers = { alc262_benq_t31_mixer },
- .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs, alc262_hippo_unsol_verbs },
+ .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs,
+ alc_hp15_unsol_verbs },
.num_dacs = ARRAY_SIZE(alc262_dac_nids),
.dac_nids = alc262_dac_nids,
.hp_nid = 0x03,
@@ -11534,7 +11593,8 @@ static struct alc_config_preset alc262_presets[] = {
.channel_mode = alc262_modes,
.input_mux = &alc262_capture_source,
.unsol_event = alc262_hippo_unsol_event,
- .init_hook = alc262_hippo_init_hook,
+ .setup = alc262_hippo_setup,
+ .init_hook = alc262_hippo_automute,
},
[ALC262_ULTRA] = {
.mixers = { alc262_ultra_mixer },
@@ -11586,9 +11646,9 @@ static struct alc_config_preset alc262_presets[] = {
.dig_out_nid = ALC262_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc262_modes),
.channel_mode = alc262_modes,
- .input_mux = &alc262_dmic_capture_source,
- .unsol_event = alc262_toshiba_s06_unsol_event,
- .init_hook = alc262_toshiba_s06_init_hook,
+ .unsol_event = alc_sku_unsol_event,
+ .setup = alc262_toshiba_s06_setup,
+ .init_hook = alc_inithook,
},
[ALC262_TOSHIBA_RX1] = {
.mixers = { alc262_toshiba_rx1_mixer },
@@ -11600,7 +11660,8 @@ static struct alc_config_preset alc262_presets[] = {
.channel_mode = alc262_modes,
.input_mux = &alc262_capture_source,
.unsol_event = alc262_hippo_unsol_event,
- .init_hook = alc262_hippo_init_hook,
+ .setup = alc262_hippo_setup,
+ .init_hook = alc262_hippo_automute,
},
[ALC262_TYAN] = {
.mixers = { alc262_tyan_mixer },
@@ -11613,7 +11674,8 @@ static struct alc_config_preset alc262_presets[] = {
.channel_mode = alc262_modes,
.input_mux = &alc262_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc262_tyan_init_hook,
+ .setup = alc262_tyan_setup,
+ .init_hook = alc_automute_amp,
},
};
@@ -11648,8 +11710,8 @@ static int patch_alc262(struct hda_codec *codec)
alc262_cfg_tbl);
if (board_config < 0) {
- printk(KERN_INFO "hda_codec: Unknown model for %s, "
- "trying auto-probe from BIOS...\n", codec->chip_name);
+ printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
board_config = ALC262_AUTO;
}
@@ -11676,7 +11738,7 @@ static int patch_alc262(struct hda_codec *codec)
}
if (board_config != ALC262_AUTO)
- setup_preset(spec, &alc262_presets[board_config]);
+ setup_preset(codec, &alc262_presets[board_config]);
spec->stream_analog_playback = &alc262_pcm_analog_playback;
spec->stream_analog_capture = &alc262_pcm_analog_capture;
@@ -11702,7 +11764,7 @@ static int patch_alc262(struct hda_codec *codec)
unsigned int wcap = get_wcaps(codec, 0x07);
/* get type */
- wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ wcap = get_wcaps_type(wcap);
if (wcap != AC_WID_AUD_IN) {
spec->adc_nids = alc262_adc_nids_alt;
spec->num_adc_nids =
@@ -11717,7 +11779,7 @@ static int patch_alc262(struct hda_codec *codec)
}
}
if (!spec->cap_mixer && !spec->no_analog)
- set_capture_mixer(spec);
+ set_capture_mixer(codec);
if (!spec->no_analog)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
@@ -11809,14 +11871,6 @@ static struct hda_verb alc268_toshiba_verbs[] = {
{ } /* end */
};
-static struct hda_input_mux alc268_acer_lc_capture_source = {
- .num_items = 2,
- .items = {
- { "i-Mic", 0x6 },
- { "E-Mic", 0x0 },
- },
-};
-
/* Acer specific */
/* bind volumes of both NID 0x02 and 0x03 */
static struct hda_bind_ctls alc268_acer_bind_master_vol = {
@@ -11935,7 +11989,8 @@ static struct hda_verb alc268_acer_verbs[] = {
/* unsolicited event for HP jack sensing */
#define alc268_toshiba_unsol_event alc262_hippo_unsol_event
-#define alc268_toshiba_init_hook alc262_hippo_init_hook
+#define alc268_toshiba_setup alc262_hippo_setup
+#define alc268_toshiba_automute alc262_hippo_automute
static void alc268_acer_unsol_event(struct hda_codec *codec,
unsigned int res)
@@ -11965,30 +12020,33 @@ static void alc268_aspire_one_speaker_automute(struct hda_codec *codec)
AMP_IN_MUTE(0), bits);
}
-
-static void alc268_acer_mic_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 0x0 : 0x6);
-}
-
static void alc268_acer_lc_unsol_event(struct hda_codec *codec,
unsigned int res)
{
- if ((res >> 26) == ALC880_HP_EVENT)
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
alc268_aspire_one_speaker_automute(codec);
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc268_acer_mic_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc_mic_automute(codec);
+ break;
+ }
+}
+
+static void alc268_acer_lc_setup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ spec->ext_mic.pin = 0x18;
+ spec->ext_mic.mux_idx = 0;
+ spec->int_mic.pin = 0x12;
+ spec->int_mic.mux_idx = 6;
+ spec->auto_mic = 1;
}
static void alc268_acer_lc_init_hook(struct hda_codec *codec)
{
alc268_aspire_one_speaker_automute(codec);
- alc268_acer_mic_automute(codec);
+ alc_mic_automute(codec);
}
static struct snd_kcontrol_new alc268_dell_mixer[] = {
@@ -12006,17 +12064,22 @@ static struct hda_verb alc268_dell_verbs[] = {
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
{ }
};
/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc268_dell_init_hook(struct hda_codec *codec)
+static void alc268_dell_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
- alc_automute_pin(codec);
+ spec->ext_mic.pin = 0x18;
+ spec->ext_mic.mux_idx = 0;
+ spec->int_mic.pin = 0x19;
+ spec->int_mic.mux_idx = 1;
+ spec->auto_mic = 1;
}
static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = {
@@ -12037,38 +12100,16 @@ static struct hda_verb alc267_quanta_il1_verbs[] = {
{ }
};
-static void alc267_quanta_il1_mic_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- snd_hda_codec_write(codec, 0x23, 0,
- AC_VERB_SET_CONNECT_SEL,
- present ? 0x00 : 0x01);
-}
-
-static void alc267_quanta_il1_init_hook(struct hda_codec *codec)
+static void alc267_quanta_il1_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
-
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
- alc_automute_pin(codec);
- alc267_quanta_il1_mic_automute(codec);
-}
-
-static void alc267_quanta_il1_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_MIC_EVENT:
- alc267_quanta_il1_mic_automute(codec);
- break;
- default:
- alc_sku_unsol_event(codec, res);
- break;
- }
+ spec->ext_mic.pin = 0x18;
+ spec->ext_mic.mux_idx = 0;
+ spec->int_mic.pin = 0x19;
+ spec->int_mic.mux_idx = 1;
+ spec->auto_mic = 1;
}
/*
@@ -12148,21 +12189,16 @@ static struct hda_verb alc268_volume_init_verbs[] = {
{ }
};
+static struct snd_kcontrol_new alc268_capture_nosrc_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
+ { } /* end */
+};
+
static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
+ _DEFINE_CAPSRC(1),
{ } /* end */
};
@@ -12171,18 +12207,7 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = {
HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
+ _DEFINE_CAPSRC(2),
{ } /* end */
};
@@ -12269,26 +12294,38 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
const char *ctlname, int idx)
{
char name[32];
+ hda_nid_t dac;
int err;
sprintf(name, "%s Playback Volume", ctlname);
- if (nid == 0x14) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(0x02, 3, idx,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (nid == 0x15) {
+ switch (nid) {
+ case 0x14:
+ case 0x16:
+ dac = 0x02;
+ break;
+ case 0x15:
+ dac = 0x03;
+ break;
+ default:
+ return 0;
+ }
+ if (spec->multiout.dac_nids[0] != dac &&
+ spec->multiout.dac_nids[1] != dac) {
err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(0x03, 3, idx,
+ HDA_COMPOSE_AMP_VAL(dac, 3, idx,
HDA_OUTPUT));
if (err < 0)
return err;
- } else
- return -1;
+ spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
+ }
+
sprintf(name, "%s Playback Switch", ctlname);
- err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ if (nid != 0x16)
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
+ else /* mono */
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT));
if (err < 0)
return err;
return 0;
@@ -12301,14 +12338,19 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
hda_nid_t nid;
int err;
- spec->multiout.num_dacs = 2; /* only use one dac */
spec->multiout.dac_nids = spec->private_dac_nids;
- spec->multiout.dac_nids[0] = 2;
- spec->multiout.dac_nids[1] = 3;
nid = cfg->line_out_pins[0];
- if (nid)
- alc268_new_analog_output(spec, nid, "Front", 0);
+ if (nid) {
+ const char *name;
+ if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ name = "Speaker";
+ else
+ name = "Front";
+ err = alc268_new_analog_output(spec, nid, name, 0);
+ if (err < 0)
+ return err;
+ }
nid = cfg->speaker_pins[0];
if (nid == 0x1d) {
@@ -12317,16 +12359,23 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
if (err < 0)
return err;
+ } else {
+ err = alc268_new_analog_output(spec, nid, "Speaker", 0);
+ if (err < 0)
+ return err;
}
nid = cfg->hp_pins[0];
- if (nid)
- alc268_new_analog_output(spec, nid, "Headphone", 0);
+ if (nid) {
+ err = alc268_new_analog_output(spec, nid, "Headphone", 0);
+ if (err < 0)
+ return err;
+ }
nid = cfg->line_out_pins[1] | cfg->line_out_pins[2];
if (nid == 0x16) {
err = add_control(spec, ALC_CTL_WIDGET_MUTE,
"Mono Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_INPUT));
+ HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT));
if (err < 0)
return err;
}
@@ -12334,38 +12383,46 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
}
/* create playback/capture controls for input pins */
-static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec,
+static int alc268_auto_create_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, idx1;
+ return alc_auto_create_input_ctls(codec, cfg, 0, 0x23, 0x24);
+}
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- switch(cfg->input_pins[i]) {
- case 0x18:
- idx1 = 0; /* Mic 1 */
- break;
- case 0x19:
- idx1 = 1; /* Mic 2 */
- break;
- case 0x1a:
- idx1 = 2; /* Line In */
- break;
- case 0x1c:
- idx1 = 3; /* CD */
- break;
- case 0x12:
- case 0x13:
- idx1 = 6; /* digital mics */
- break;
- default:
- continue;
- }
- imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = idx1;
- imux->num_items++;
+static void alc268_auto_set_output_and_unmute(struct hda_codec *codec,
+ hda_nid_t nid, int pin_type)
+{
+ int idx;
+
+ alc_set_pin_output(codec, nid, pin_type);
+ if (nid == 0x14 || nid == 0x16)
+ idx = 0;
+ else
+ idx = 1;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
+}
+
+static void alc268_auto_init_multi_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t nid = spec->autocfg.line_out_pins[0];
+ if (nid) {
+ int pin_type = get_pin_type(spec->autocfg.line_out_type);
+ alc268_auto_set_output_and_unmute(codec, nid, pin_type);
}
- return 0;
+}
+
+static void alc268_auto_init_hp_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t pin;
+
+ pin = spec->autocfg.hp_pins[0];
+ if (pin)
+ alc268_auto_set_output_and_unmute(codec, pin, PIN_HP);
+ pin = spec->autocfg.speaker_pins[0];
+ if (pin)
+ alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT);
}
static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
@@ -12376,9 +12433,10 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
unsigned int dac_vol1, dac_vol2;
- if (speaker_nid) {
+ if (line_nid == 0x1d || speaker_nid == 0x1d) {
snd_hda_codec_write(codec, speaker_nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ /* mute mixer inputs from 0x1d */
snd_hda_codec_write(codec, 0x0f, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_IN_UNMUTE(1));
@@ -12386,6 +12444,7 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_IN_UNMUTE(1));
} else {
+ /* unmute mixer inputs from 0x1d */
snd_hda_codec_write(codec, 0x0f, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
snd_hda_codec_write(codec, 0x10, 0,
@@ -12442,7 +12501,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg);
if (err < 0)
return err;
- err = alc268_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = alc268_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -12461,7 +12520,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
add_mixer(spec, alc268_beep_mixer);
add_verb(spec, alc268_volume_init_verbs);
- spec->num_mux_defs = 1;
+ spec->num_mux_defs = 2;
spec->input_mux = &spec->private_imux[0];
err = alc_auto_add_mic_boost(codec);
@@ -12473,8 +12532,6 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
return 1;
}
-#define alc268_auto_init_multi_out alc882_auto_init_multi_out
-#define alc268_auto_init_hp_out alc882_auto_init_hp_out
#define alc268_auto_init_analog_input alc882_auto_init_analog_input
/* init callback for auto-configuration model -- overriding the default init */
@@ -12517,12 +12574,13 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = {
ALC268_ACER_ASPIRE_ONE),
SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron Mini9", ALC268_DELL),
+ /* almost compatible with toshiba but with optional digital outs;
+ * auto-probing seems working fine
+ */
SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series",
- ALC268_TOSHIBA),
+ ALC268_AUTO),
SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO),
- SND_PCI_QUIRK_MASK(0x1179, 0xff00, 0xff00, "TOSHIBA A/Lx05",
- ALC268_TOSHIBA),
SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA),
SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1),
@@ -12530,9 +12588,19 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = {
{}
};
+/* Toshiba laptops have no unique PCI SSID but only codec SSID */
+static struct snd_pci_quirk alc268_ssid_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1179, 0xff0a, "TOSHIBA X-200", ALC268_AUTO),
+ SND_PCI_QUIRK(0x1179, 0xff0e, "TOSHIBA X-200 HDMI", ALC268_AUTO),
+ SND_PCI_QUIRK_MASK(0x1179, 0xff00, 0xff00, "TOSHIBA A/Lx05",
+ ALC268_TOSHIBA),
+ {}
+};
+
static struct alc_config_preset alc268_presets[] = {
[ALC267_QUANTA_IL1] = {
- .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer },
+ .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer,
+ alc268_capture_nosrc_mixer },
.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
alc267_quanta_il1_verbs },
.num_dacs = ARRAY_SIZE(alc268_dac_nids),
@@ -12542,9 +12610,9 @@ static struct alc_config_preset alc268_presets[] = {
.hp_nid = 0x03,
.num_channel_mode = ARRAY_SIZE(alc268_modes),
.channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
- .unsol_event = alc267_quanta_il1_unsol_event,
- .init_hook = alc267_quanta_il1_init_hook,
+ .unsol_event = alc_sku_unsol_event,
+ .setup = alc267_quanta_il1_setup,
+ .init_hook = alc_inithook,
},
[ALC268_3ST] = {
.mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
@@ -12576,10 +12644,11 @@ static struct alc_config_preset alc268_presets[] = {
.channel_mode = alc268_modes,
.input_mux = &alc268_capture_source,
.unsol_event = alc268_toshiba_unsol_event,
- .init_hook = alc268_toshiba_init_hook,
+ .setup = alc268_toshiba_setup,
+ .init_hook = alc268_toshiba_automute,
},
[ALC268_ACER] = {
- .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer,
+ .mixers = { alc268_acer_mixer, alc268_capture_nosrc_mixer,
alc268_beep_mixer },
.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
alc268_acer_verbs },
@@ -12615,7 +12684,7 @@ static struct alc_config_preset alc268_presets[] = {
[ALC268_ACER_ASPIRE_ONE] = {
.mixers = { alc268_acer_aspire_one_mixer,
alc268_beep_mixer,
- alc268_capture_alt_mixer },
+ alc268_capture_nosrc_mixer },
.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
alc268_acer_aspire_one_verbs },
.num_dacs = ARRAY_SIZE(alc268_dac_nids),
@@ -12626,22 +12695,26 @@ static struct alc_config_preset alc268_presets[] = {
.hp_nid = 0x03,
.num_channel_mode = ARRAY_SIZE(alc268_modes),
.channel_mode = alc268_modes,
- .input_mux = &alc268_acer_lc_capture_source,
.unsol_event = alc268_acer_lc_unsol_event,
+ .setup = alc268_acer_lc_setup,
.init_hook = alc268_acer_lc_init_hook,
},
[ALC268_DELL] = {
- .mixers = { alc268_dell_mixer, alc268_beep_mixer },
+ .mixers = { alc268_dell_mixer, alc268_beep_mixer,
+ alc268_capture_nosrc_mixer },
.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
alc268_dell_verbs },
.num_dacs = ARRAY_SIZE(alc268_dac_nids),
.dac_nids = alc268_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+ .adc_nids = alc268_adc_nids_alt,
+ .capsrc_nids = alc268_capsrc_nids,
.hp_nid = 0x02,
.num_channel_mode = ARRAY_SIZE(alc268_modes),
.channel_mode = alc268_modes,
.unsol_event = alc_sku_unsol_event,
- .init_hook = alc268_dell_init_hook,
- .input_mux = &alc268_capture_source,
+ .setup = alc268_dell_setup,
+ .init_hook = alc_inithook,
},
[ALC268_ZEPTO] = {
.mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
@@ -12658,8 +12731,8 @@ static struct alc_config_preset alc268_presets[] = {
.num_channel_mode = ARRAY_SIZE(alc268_modes),
.channel_mode = alc268_modes,
.input_mux = &alc268_capture_source,
- .unsol_event = alc268_toshiba_unsol_event,
- .init_hook = alc268_toshiba_init_hook
+ .setup = alc268_toshiba_setup,
+ .init_hook = alc268_toshiba_automute,
},
#ifdef CONFIG_SND_DEBUG
[ALC268_TEST] = {
@@ -12696,9 +12769,13 @@ static int patch_alc268(struct hda_codec *codec)
alc268_models,
alc268_cfg_tbl);
+ if (board_config < 0 || board_config >= ALC268_MODEL_LAST)
+ board_config = snd_hda_check_board_codec_sid_config(codec,
+ ALC882_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl);
+
if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: Unknown model for %s, "
- "trying auto-probe from BIOS...\n", codec->chip_name);
+ printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
board_config = ALC268_AUTO;
}
@@ -12717,7 +12794,7 @@ static int patch_alc268(struct hda_codec *codec)
}
if (board_config != ALC268_AUTO)
- setup_preset(spec, &alc268_presets[board_config]);
+ setup_preset(codec, &alc268_presets[board_config]);
spec->stream_analog_playback = &alc268_pcm_analog_playback;
spec->stream_analog_capture = &alc268_pcm_analog_capture;
@@ -12754,11 +12831,15 @@ static int patch_alc268(struct hda_codec *codec)
int i;
/* get type */
- wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
- if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
+ wcap = get_wcaps_type(wcap);
+ if (spec->auto_mic ||
+ wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
spec->adc_nids = alc268_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
- add_mixer(spec, alc268_capture_alt_mixer);
+ if (spec->auto_mic || spec->input_mux->num_items == 1)
+ add_mixer(spec, alc268_capture_nosrc_mixer);
+ else
+ add_mixer(spec, alc268_capture_alt_mixer);
} else {
spec->adc_nids = alc268_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
@@ -12769,6 +12850,8 @@ static int patch_alc268(struct hda_codec *codec)
for (i = 0; i < spec->num_adc_nids; i++)
snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i],
0, AC_VERB_SET_CONNECT_SEL,
+ i < spec->num_mux_defs ?
+ spec->input_mux[i].items[0].index :
spec->input_mux->items[0].index);
}
@@ -12803,22 +12886,6 @@ static hda_nid_t alc269_capsrc_nids[1] = {
* not a mux!
*/
-static struct hda_input_mux alc269_eeepc_dmic_capture_source = {
- .num_items = 2,
- .items = {
- { "i-Mic", 0x5 },
- { "e-Mic", 0x0 },
- },
-};
-
-static struct hda_input_mux alc269_eeepc_amic_capture_source = {
- .num_items = 2,
- .items = {
- { "i-Mic", 0x1 },
- { "e-Mic", 0x0 },
- },
-};
-
#define alc269_modes alc260_modes
#define alc269_capture_source alc880_lg_lw_capture_source
@@ -12980,16 +13047,6 @@ static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
AC_VERB_SET_PROC_COEF, 0x480);
}
-static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- snd_hda_codec_write(codec, 0x23, 0,
- AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1);
-}
-
static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
{
unsigned int present_laptop;
@@ -13016,10 +13073,14 @@ static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
unsigned int res)
{
- if ((res >> 26) == ALC880_HP_EVENT)
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
alc269_quanta_fl1_speaker_automute(codec);
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc269_quanta_fl1_mic_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc_mic_automute(codec);
+ break;
+ }
}
static void alc269_lifebook_unsol_event(struct hda_codec *codec,
@@ -13031,10 +13092,20 @@ static void alc269_lifebook_unsol_event(struct hda_codec *codec,
alc269_lifebook_mic_autoswitch(codec);
}
+static void alc269_quanta_fl1_setup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ spec->ext_mic.pin = 0x18;
+ spec->ext_mic.mux_idx = 0;
+ spec->int_mic.pin = 0x19;
+ spec->int_mic.mux_idx = 1;
+ spec->auto_mic = 1;
+}
+
static void alc269_quanta_fl1_init_hook(struct hda_codec *codec)
{
alc269_quanta_fl1_speaker_automute(codec);
- alc269_quanta_fl1_mic_automute(codec);
+ alc_mic_automute(codec);
}
static void alc269_lifebook_init_hook(struct hda_codec *codec)
@@ -13079,60 +13150,44 @@ static void alc269_speaker_automute(struct hda_codec *codec)
AMP_IN_MUTE(0), bits);
}
-static void alc269_eeepc_dmic_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- snd_hda_codec_write(codec, 0x23, 0,
- AC_VERB_SET_CONNECT_SEL, (present ? 0 : 5));
-}
-
-static void alc269_eeepc_amic_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
- snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
-}
-
/* unsolicited event for HP jack sensing */
-static void alc269_eeepc_dmic_unsol_event(struct hda_codec *codec,
+static void alc269_eeepc_unsol_event(struct hda_codec *codec,
unsigned int res)
{
- if ((res >> 26) == ALC880_HP_EVENT)
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
alc269_speaker_automute(codec);
-
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc269_eeepc_dmic_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ alc_mic_automute(codec);
+ break;
+ }
}
-static void alc269_eeepc_dmic_inithook(struct hda_codec *codec)
+static void alc269_eeepc_dmic_setup(struct hda_codec *codec)
{
- alc269_speaker_automute(codec);
- alc269_eeepc_dmic_automute(codec);
+ struct alc_spec *spec = codec->spec;
+ spec->ext_mic.pin = 0x18;
+ spec->ext_mic.mux_idx = 0;
+ spec->int_mic.pin = 0x12;
+ spec->int_mic.mux_idx = 5;
+ spec->auto_mic = 1;
}
-/* unsolicited event for HP jack sensing */
-static void alc269_eeepc_amic_unsol_event(struct hda_codec *codec,
- unsigned int res)
+static void alc269_eeepc_amic_setup(struct hda_codec *codec)
{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc269_speaker_automute(codec);
-
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc269_eeepc_amic_automute(codec);
+ struct alc_spec *spec = codec->spec;
+ spec->ext_mic.pin = 0x18;
+ spec->ext_mic.mux_idx = 0;
+ spec->int_mic.pin = 0x19;
+ spec->int_mic.mux_idx = 1;
+ spec->auto_mic = 1;
}
-static void alc269_eeepc_amic_inithook(struct hda_codec *codec)
+static void alc269_eeepc_inithook(struct hda_codec *codec)
{
alc269_speaker_automute(codec);
- alc269_eeepc_amic_automute(codec);
+ alc_mic_automute(codec);
}
/*
@@ -13205,89 +13260,10 @@ static struct hda_verb alc269_init_verbs[] = {
{ }
};
-/* add playback controls from the parsed DAC table */
-static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- hda_nid_t nid;
- int err;
-
- spec->multiout.num_dacs = 1; /* only use one dac */
- spec->multiout.dac_nids = spec->private_dac_nids;
- spec->multiout.dac_nids[0] = 2;
-
- nid = cfg->line_out_pins[0];
- if (nid) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_control(spec, ALC_CTL_WIDGET_MUTE,
- "Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
-
- nid = cfg->speaker_pins[0];
- if (nid) {
- if (!cfg->line_out_pins[0]) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Speaker Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- if (nid == 0x16) {
- err = add_control(spec, ALC_CTL_WIDGET_MUTE,
- "Speaker Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- err = add_control(spec, ALC_CTL_WIDGET_MUTE,
- "Speaker Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
- nid = cfg->hp_pins[0];
- if (nid) {
- /* spec->multiout.hp_nid = 2; */
- if (!cfg->line_out_pins[0] && !cfg->speaker_pins[0]) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- if (nid == 0x16) {
- err = add_control(spec, ALC_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- err = add_control(spec, ALC_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-#define alc269_auto_create_analog_input_ctls \
- alc262_auto_create_analog_input_ctls
+#define alc269_auto_create_multi_out_ctls \
+ alc268_auto_create_multi_out_ctls
+#define alc269_auto_create_input_ctls \
+ alc268_auto_create_input_ctls
#ifdef CONFIG_SND_HDA_POWER_SAVE
#define alc269_loopbacks alc880_loopbacks
@@ -13337,7 +13313,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
if (err < 0)
return err;
- err = alc269_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = alc269_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -13362,15 +13338,15 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
return err;
if (!spec->cap_mixer && !spec->no_analog)
- set_capture_mixer(spec);
+ set_capture_mixer(codec);
alc_ssid_check(codec, 0x15, 0x1b, 0x14);
return 1;
}
-#define alc269_auto_init_multi_out alc882_auto_init_multi_out
-#define alc269_auto_init_hp_out alc882_auto_init_hp_out
+#define alc269_auto_init_multi_out alc268_auto_init_multi_out
+#define alc269_auto_init_hp_out alc268_auto_init_hp_out
#define alc269_auto_init_analog_input alc882_auto_init_analog_input
@@ -13438,6 +13414,7 @@ static struct alc_config_preset alc269_presets[] = {
.channel_mode = alc269_modes,
.input_mux = &alc269_capture_source,
.unsol_event = alc269_quanta_fl1_unsol_event,
+ .setup = alc269_quanta_fl1_setup,
.init_hook = alc269_quanta_fl1_init_hook,
},
[ALC269_ASUS_EEEPC_P703] = {
@@ -13450,9 +13427,9 @@ static struct alc_config_preset alc269_presets[] = {
.hp_nid = 0x03,
.num_channel_mode = ARRAY_SIZE(alc269_modes),
.channel_mode = alc269_modes,
- .input_mux = &alc269_eeepc_amic_capture_source,
- .unsol_event = alc269_eeepc_amic_unsol_event,
- .init_hook = alc269_eeepc_amic_inithook,
+ .unsol_event = alc269_eeepc_unsol_event,
+ .setup = alc269_eeepc_amic_setup,
+ .init_hook = alc269_eeepc_inithook,
},
[ALC269_ASUS_EEEPC_P901] = {
.mixers = { alc269_eeepc_mixer },
@@ -13464,9 +13441,9 @@ static struct alc_config_preset alc269_presets[] = {
.hp_nid = 0x03,
.num_channel_mode = ARRAY_SIZE(alc269_modes),
.channel_mode = alc269_modes,
- .input_mux = &alc269_eeepc_dmic_capture_source,
- .unsol_event = alc269_eeepc_dmic_unsol_event,
- .init_hook = alc269_eeepc_dmic_inithook,
+ .unsol_event = alc269_eeepc_unsol_event,
+ .setup = alc269_eeepc_dmic_setup,
+ .init_hook = alc269_eeepc_inithook,
},
[ALC269_FUJITSU] = {
.mixers = { alc269_fujitsu_mixer },
@@ -13478,9 +13455,9 @@ static struct alc_config_preset alc269_presets[] = {
.hp_nid = 0x03,
.num_channel_mode = ARRAY_SIZE(alc269_modes),
.channel_mode = alc269_modes,
- .input_mux = &alc269_eeepc_dmic_capture_source,
- .unsol_event = alc269_eeepc_dmic_unsol_event,
- .init_hook = alc269_eeepc_dmic_inithook,
+ .unsol_event = alc269_eeepc_unsol_event,
+ .setup = alc269_eeepc_dmic_setup,
+ .init_hook = alc269_eeepc_inithook,
},
[ALC269_LIFEBOOK] = {
.mixers = { alc269_lifebook_mixer },
@@ -13515,8 +13492,8 @@ static int patch_alc269(struct hda_codec *codec)
alc269_cfg_tbl);
if (board_config < 0) {
- printk(KERN_INFO "hda_codec: Unknown model for %s, "
- "trying auto-probe from BIOS...\n", codec->chip_name);
+ printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
board_config = ALC269_AUTO;
}
@@ -13541,7 +13518,7 @@ static int patch_alc269(struct hda_codec *codec)
}
if (board_config != ALC269_AUTO)
- setup_preset(spec, &alc269_presets[board_config]);
+ setup_preset(codec, &alc269_presets[board_config]);
if (codec->subsystem_id == 0x17aa3bf8) {
/* Due to a hardware problem on Lenovo Ideadpad, we need to
@@ -13560,9 +13537,11 @@ static int patch_alc269(struct hda_codec *codec)
spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
spec->capsrc_nids = alc269_capsrc_nids;
if (!spec->cap_mixer)
- set_capture_mixer(spec);
+ set_capture_mixer(codec);
set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
+ spec->vmaster_nid = 0x02;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC269_AUTO)
spec->init_hook = alc269_auto_init;
@@ -14108,23 +14087,23 @@ static struct hda_verb alc861_auto_init_verbs[] = {
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
{0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, /* set Mic 1 */
@@ -14196,64 +14175,96 @@ static struct hda_input_mux alc861_capture_source = {
},
};
+static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t mix, srcs[5];
+ int i, j, num;
+
+ if (snd_hda_get_connections(codec, pin, &mix, 1) != 1)
+ return 0;
+ num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs));
+ if (num < 0)
+ return 0;
+ for (i = 0; i < num; i++) {
+ unsigned int type;
+ type = get_wcaps_type(get_wcaps(codec, srcs[i]));
+ if (type != AC_WID_AUD_OUT)
+ continue;
+ for (j = 0; j < spec->multiout.num_dacs; j++)
+ if (spec->multiout.dac_nids[j] == srcs[i])
+ break;
+ if (j >= spec->multiout.num_dacs)
+ return srcs[i];
+ }
+ return 0;
+}
+
/* fill in the dac_nids table from the parsed pin configuration */
-static int alc861_auto_fill_dac_nids(struct alc_spec *spec,
+static int alc861_auto_fill_dac_nids(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
+ struct alc_spec *spec = codec->spec;
int i;
- hda_nid_t nid;
+ hda_nid_t nid, dac;
spec->multiout.dac_nids = spec->private_dac_nids;
for (i = 0; i < cfg->line_outs; i++) {
nid = cfg->line_out_pins[i];
- if (nid) {
- if (i >= ARRAY_SIZE(alc861_dac_nids))
- continue;
- spec->multiout.dac_nids[i] = alc861_dac_nids[i];
- }
+ dac = alc861_look_for_dac(codec, nid);
+ if (!dac)
+ continue;
+ spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
}
- spec->multiout.num_dacs = cfg->line_outs;
return 0;
}
+static int alc861_create_out_sw(struct hda_codec *codec, const char *pfx,
+ hda_nid_t nid, unsigned int chs)
+{
+ char name[32];
+ snprintf(name, sizeof(name), "%s Playback Switch", pfx);
+ return add_control(codec->spec, ALC_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+}
+
/* add playback controls from the parsed DAC table */
-static int alc861_auto_create_multi_out_ctls(struct alc_spec *spec,
+static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- char name[32];
+ struct alc_spec *spec = codec->spec;
static const char *chname[4] = {
"Front", "Surround", NULL /*CLFE*/, "Side"
};
hda_nid_t nid;
- int i, idx, err;
+ int i, err;
+
+ if (cfg->line_outs == 1) {
+ const char *pfx = NULL;
+ if (!cfg->hp_outs)
+ pfx = "Master";
+ else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ pfx = "Speaker";
+ if (pfx) {
+ nid = spec->multiout.dac_nids[0];
+ return alc861_create_out_sw(codec, pfx, nid, 3);
+ }
+ }
for (i = 0; i < cfg->line_outs; i++) {
nid = spec->multiout.dac_nids[i];
if (!nid)
continue;
- if (nid == 0x05) {
+ if (i == 2) {
/* Center/LFE */
- err = add_control(spec, ALC_CTL_BIND_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0,
- HDA_OUTPUT));
+ err = alc861_create_out_sw(codec, "Center", nid, 1);
if (err < 0)
return err;
- err = add_control(spec, ALC_CTL_BIND_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0,
- HDA_OUTPUT));
+ err = alc861_create_out_sw(codec, "LFE", nid, 2);
if (err < 0)
return err;
} else {
- for (idx = 0; idx < ARRAY_SIZE(alc861_dac_nids) - 1;
- idx++)
- if (nid == alc861_dac_nids[idx])
- break;
- sprintf(name, "%s Playback Switch", chname[idx]);
- err = add_control(spec, ALC_CTL_BIND_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
+ err = alc861_create_out_sw(codec, chname[i], nid, 3);
if (err < 0)
return err;
}
@@ -14261,8 +14272,9 @@ static int alc861_auto_create_multi_out_ctls(struct alc_spec *spec,
return 0;
}
-static int alc861_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin)
+static int alc861_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
{
+ struct alc_spec *spec = codec->spec;
int err;
hda_nid_t nid;
@@ -14270,70 +14282,49 @@ static int alc861_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin)
return 0;
if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) {
- nid = 0x03;
- err = add_control(spec, ALC_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- spec->multiout.hp_nid = nid;
+ nid = alc861_look_for_dac(codec, pin);
+ if (nid) {
+ err = alc861_create_out_sw(codec, "Headphone", nid, 3);
+ if (err < 0)
+ return err;
+ spec->multiout.hp_nid = nid;
+ }
}
return 0;
}
/* create playback/capture controls for input pins */
-static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec,
+static int alc861_auto_create_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx, idx1;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- switch (cfg->input_pins[i]) {
- case 0x0c:
- idx1 = 1;
- idx = 2; /* Line In */
- break;
- case 0x0f:
- idx1 = 2;
- idx = 2; /* Line In */
- break;
- case 0x0d:
- idx1 = 0;
- idx = 1; /* Mic In */
- break;
- case 0x10:
- idx1 = 3;
- idx = 1; /* Mic In */
- break;
- case 0x11:
- idx1 = 4;
- idx = 0; /* CD */
- break;
- default:
- continue;
- }
-
- err = new_analog_input(spec, cfg->input_pins[i],
- auto_pin_cfg_labels[i], idx, 0x15);
- if (err < 0)
- return err;
-
- imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = idx1;
- imux->num_items++;
- }
- return 0;
+ return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x08, 0);
}
static void alc861_auto_set_output_and_unmute(struct hda_codec *codec,
hda_nid_t nid,
- int pin_type, int dac_idx)
+ int pin_type, hda_nid_t dac)
{
+ hda_nid_t mix, srcs[5];
+ int i, num;
+
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
pin_type);
- snd_hda_codec_write(codec, dac_idx, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
+ if (snd_hda_get_connections(codec, nid, &mix, 1) != 1)
+ return;
+ num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs));
+ if (num < 0)
+ return;
+ for (i = 0; i < num; i++) {
+ unsigned int mute;
+ if (srcs[i] == dac || srcs[i] == 0x15)
+ mute = AMP_IN_UNMUTE(i);
+ else
+ mute = AMP_IN_MUTE(i);
+ snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ mute);
+ }
}
static void alc861_auto_init_multi_out(struct hda_codec *codec)
@@ -14356,12 +14347,13 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec)
hda_nid_t pin;
pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front */
+ if (pin)
alc861_auto_set_output_and_unmute(codec, pin, PIN_HP,
- spec->multiout.dac_nids[0]);
+ spec->multiout.hp_nid);
pin = spec->autocfg.speaker_pins[0];
if (pin)
- alc861_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
+ alc861_auto_set_output_and_unmute(codec, pin, PIN_OUT,
+ spec->multiout.dac_nids[0]);
}
static void alc861_auto_init_analog_input(struct hda_codec *codec)
@@ -14393,16 +14385,16 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
if (!spec->autocfg.line_outs)
return 0; /* can't find valid BIOS pin config */
- err = alc861_auto_fill_dac_nids(spec, &spec->autocfg);
+ err = alc861_auto_fill_dac_nids(codec, &spec->autocfg);
if (err < 0)
return err;
- err = alc861_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
- err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+ err = alc861_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = alc861_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = alc861_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -14421,7 +14413,7 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
spec->adc_nids = alc861_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
- set_capture_mixer(spec);
+ set_capture_mixer(codec);
alc_ssid_check(codec, 0x0e, 0x0f, 0x0b);
@@ -14614,8 +14606,8 @@ static int patch_alc861(struct hda_codec *codec)
alc861_cfg_tbl);
if (board_config < 0) {
- printk(KERN_INFO "hda_codec: Unknown model for %s, "
- "trying auto-probe from BIOS...\n", codec->chip_name);
+ printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
board_config = ALC861_AUTO;
}
@@ -14640,7 +14632,7 @@ static int patch_alc861(struct hda_codec *codec)
}
if (board_config != ALC861_AUTO)
- setup_preset(spec, &alc861_presets[board_config]);
+ setup_preset(codec, &alc861_presets[board_config]);
spec->stream_analog_playback = &alc861_pcm_analog_playback;
spec->stream_analog_capture = &alc861_pcm_analog_capture;
@@ -15043,12 +15035,15 @@ static void alc861vd_lenovo_mic_automute(struct hda_codec *codec)
HDA_AMP_MUTE, bits);
}
-static void alc861vd_lenovo_init_hook(struct hda_codec *codec)
+static void alc861vd_lenovo_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
-
spec->autocfg.hp_pins[0] = 0x1b;
spec->autocfg.speaker_pins[0] = 0x14;
+}
+
+static void alc861vd_lenovo_init_hook(struct hda_codec *codec)
+{
alc_automute_amp(codec);
alc861vd_lenovo_mic_automute(codec);
}
@@ -15112,13 +15107,12 @@ static struct hda_verb alc861vd_dallas_verbs[] = {
};
/* toggle speaker-output according to the hp-jack state */
-static void alc861vd_dallas_init_hook(struct hda_codec *codec)
+static void alc861vd_dallas_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x15;
spec->autocfg.speaker_pins[0] = 0x14;
- alc_automute_amp(codec);
}
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -15157,7 +15151,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
/*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/
- SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS),
+ SND_PCI_QUIRK(0x1179, 0xff01, "Toshiba A135", ALC861VD_LENOVO),
SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO),
SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS),
SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG),
@@ -15232,6 +15226,7 @@ static struct alc_config_preset alc861vd_presets[] = {
.channel_mode = alc861vd_3stack_2ch_modes,
.input_mux = &alc861vd_capture_source,
.unsol_event = alc861vd_lenovo_unsol_event,
+ .setup = alc861vd_lenovo_setup,
.init_hook = alc861vd_lenovo_init_hook,
},
[ALC861VD_DALLAS] = {
@@ -15243,7 +15238,8 @@ static struct alc_config_preset alc861vd_presets[] = {
.channel_mode = alc861vd_3stack_2ch_modes,
.input_mux = &alc861vd_dallas_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc861vd_dallas_init_hook,
+ .setup = alc861vd_dallas_setup,
+ .init_hook = alc_automute_amp,
},
[ALC861VD_HP] = {
.mixers = { alc861vd_hp_mixer },
@@ -15255,7 +15251,8 @@ static struct alc_config_preset alc861vd_presets[] = {
.channel_mode = alc861vd_3stack_2ch_modes,
.input_mux = &alc861vd_hp_capture_source,
.unsol_event = alc_automute_amp_unsol_event,
- .init_hook = alc861vd_dallas_init_hook,
+ .setup = alc861vd_dallas_setup,
+ .init_hook = alc_automute_amp,
},
[ALC660VD_ASUS_V1S] = {
.mixers = { alc861vd_lenovo_mixer },
@@ -15270,6 +15267,7 @@ static struct alc_config_preset alc861vd_presets[] = {
.channel_mode = alc861vd_3stack_2ch_modes,
.input_mux = &alc861vd_capture_source,
.unsol_event = alc861vd_lenovo_unsol_event,
+ .setup = alc861vd_lenovo_setup,
.init_hook = alc861vd_lenovo_init_hook,
},
};
@@ -15277,6 +15275,13 @@ static struct alc_config_preset alc861vd_presets[] = {
/*
* BIOS auto configuration
*/
+static int alc861vd_auto_create_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x09, 0);
+}
+
+
static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec,
hda_nid_t nid, int pin_type, int dac_idx)
{
@@ -15311,7 +15316,6 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
}
-#define alc861vd_is_input_pin(nid) alc880_is_input_pin(nid)
#define ALC861VD_PIN_CD_NID ALC880_PIN_CD_NID
static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
@@ -15321,7 +15325,7 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i];
- if (alc861vd_is_input_pin(nid)) {
+ if (alc_is_input_pin(codec, nid)) {
alc_set_input_pin(codec, nid, i);
if (nid != ALC861VD_PIN_CD_NID &&
(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
@@ -15385,13 +15389,25 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
if (err < 0)
return err;
} else {
- sprintf(name, "%s Playback Volume", chname[i]);
+ const char *pfx;
+ if (cfg->line_outs == 1 &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ if (!cfg->hp_pins)
+ pfx = "Speaker";
+ else
+ pfx = "PCM";
+ } else
+ pfx = chname[i];
+ sprintf(name, "%s Playback Volume", pfx);
err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- sprintf(name, "%s Playback Switch", chname[i]);
+ if (cfg->line_outs == 1 &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ pfx = "Speaker";
+ sprintf(name, "%s Playback Switch", pfx);
err = add_control(spec, ALC_CTL_BIND_MUTE, name,
HDA_COMPOSE_AMP_VAL(nid_s, 3, 2,
HDA_INPUT));
@@ -15484,7 +15500,7 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
"Headphone");
if (err < 0)
return err;
- err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = alc861vd_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -15538,8 +15554,8 @@ static int patch_alc861vd(struct hda_codec *codec)
alc861vd_cfg_tbl);
if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: Unknown model for %s, "
- "trying auto-probe from BIOS...\n", codec->chip_name);
+ printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
board_config = ALC861VD_AUTO;
}
@@ -15564,7 +15580,7 @@ static int patch_alc861vd(struct hda_codec *codec)
}
if (board_config != ALC861VD_AUTO)
- setup_preset(spec, &alc861vd_presets[board_config]);
+ setup_preset(codec, &alc861vd_presets[board_config]);
if (codec->vendor_id == 0x10ec0660) {
/* always turn on EAPD */
@@ -15577,11 +15593,14 @@ static int patch_alc861vd(struct hda_codec *codec)
spec->stream_digital_playback = &alc861vd_pcm_digital_playback;
spec->stream_digital_capture = &alc861vd_pcm_digital_capture;
- spec->adc_nids = alc861vd_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
- spec->capsrc_nids = alc861vd_capsrc_nids;
+ if (!spec->adc_nids) {
+ spec->adc_nids = alc861vd_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
+ }
+ if (!spec->capsrc_nids)
+ spec->capsrc_nids = alc861vd_capsrc_nids;
- set_capture_mixer(spec);
+ set_capture_mixer(codec);
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
spec->vmaster_nid = 0x02;
@@ -15622,9 +15641,9 @@ static hda_nid_t alc272_dac_nids[2] = {
0x02, 0x03
};
-static hda_nid_t alc662_adc_nids[1] = {
+static hda_nid_t alc662_adc_nids[2] = {
/* ADC1-2 */
- 0x09,
+ 0x09, 0x08
};
static hda_nid_t alc272_adc_nids[1] = {
@@ -15632,7 +15651,7 @@ static hda_nid_t alc272_adc_nids[1] = {
0x08,
};
-static hda_nid_t alc662_capsrc_nids[1] = { 0x22 };
+static hda_nid_t alc662_capsrc_nids[2] = { 0x22, 0x23 };
static hda_nid_t alc272_capsrc_nids[1] = { 0x23 };
@@ -15656,14 +15675,6 @@ static struct hda_input_mux alc662_lenovo_101e_capture_source = {
},
};
-static struct hda_input_mux alc662_eeepc_capture_source = {
- .num_items = 2,
- .items = {
- { "i-Mic", 0x1 },
- { "e-Mic", 0x0 },
- },
-};
-
static struct hda_input_mux alc663_capture_source = {
.num_items = 3,
.items = {
@@ -15673,23 +15684,7 @@ static struct hda_input_mux alc663_capture_source = {
},
};
-static struct hda_input_mux alc663_m51va_capture_source = {
- .num_items = 2,
- .items = {
- { "Ext-Mic", 0x0 },
- { "D-Mic", 0x9 },
- },
-};
-
-#if 1 /* set to 0 for testing other input sources below */
-static struct hda_input_mux alc272_nc10_capture_source = {
- .num_items = 2,
- .items = {
- { "Autoselect Mic", 0x0 },
- { "Internal Mic", 0x1 },
- },
-};
-#else
+#if 0 /* set to 1 for testing other input sources below */
static struct hda_input_mux alc272_nc10_capture_source = {
.num_items = 16,
.items = {
@@ -16358,47 +16353,44 @@ static void alc662_lenovo_101e_unsol_event(struct hda_codec *codec,
alc662_lenovo_101e_ispeaker_automute(codec);
}
-static void alc662_eeepc_mic_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
- snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
- snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
- snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
-}
-
/* unsolicited event for HP jack sensing */
static void alc662_eeepc_unsol_event(struct hda_codec *codec,
unsigned int res)
{
if ((res >> 26) == ALC880_MIC_EVENT)
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
else
alc262_hippo_unsol_event(codec, res);
}
+static void alc662_eeepc_setup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc262_hippo1_setup(codec);
+ spec->ext_mic.pin = 0x18;
+ spec->ext_mic.mux_idx = 0;
+ spec->int_mic.pin = 0x19;
+ spec->int_mic.mux_idx = 1;
+ spec->auto_mic = 1;
+}
+
static void alc662_eeepc_inithook(struct hda_codec *codec)
{
- alc262_hippo1_init_hook(codec);
- alc662_eeepc_mic_automute(codec);
+ alc262_hippo_automute(codec);
+ alc_mic_automute(codec);
}
-static void alc662_eeepc_ep20_inithook(struct hda_codec *codec)
+static void alc662_eeepc_ep20_setup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
spec->autocfg.hp_pins[0] = 0x14;
spec->autocfg.speaker_pins[0] = 0x1b;
- alc262_hippo_master_update(codec);
}
+#define alc662_eeepc_ep20_inithook alc262_hippo_master_update
+
static void alc663_m51va_speaker_automute(struct hda_codec *codec)
{
unsigned int present;
@@ -16509,23 +16501,6 @@ static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec)
}
}
-static void alc663_m51va_mic_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_codec_read(codec, 0x18, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
- snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
- snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
- snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
- snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- 0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
-}
-
static void alc663_m51va_unsol_event(struct hda_codec *codec,
unsigned int res)
{
@@ -16534,36 +16509,32 @@ static void alc663_m51va_unsol_event(struct hda_codec *codec,
alc663_m51va_speaker_automute(codec);
break;
case ALC880_MIC_EVENT:
- alc663_m51va_mic_automute(codec);
+ alc_mic_automute(codec);
break;
}
}
+static void alc663_m51va_setup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ spec->ext_mic.pin = 0x18;
+ spec->ext_mic.mux_idx = 0;
+ spec->int_mic.pin = 0x12;
+ spec->int_mic.mux_idx = 1;
+ spec->auto_mic = 1;
+}
+
static void alc663_m51va_inithook(struct hda_codec *codec)
{
alc663_m51va_speaker_automute(codec);
- alc663_m51va_mic_automute(codec);
+ alc_mic_automute(codec);
}
/* ***************** Mode1 ******************************/
-static void alc663_mode1_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_m51va_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc662_eeepc_mic_automute(codec);
- break;
- }
-}
+#define alc663_mode1_unsol_event alc663_m51va_unsol_event
+#define alc663_mode1_setup alc663_m51va_setup
+#define alc663_mode1_inithook alc663_m51va_inithook
-static void alc663_mode1_inithook(struct hda_codec *codec)
-{
- alc663_m51va_speaker_automute(codec);
- alc662_eeepc_mic_automute(codec);
-}
/* ***************** Mode2 ******************************/
static void alc662_mode2_unsol_event(struct hda_codec *codec,
unsigned int res)
@@ -16573,15 +16544,17 @@ static void alc662_mode2_unsol_event(struct hda_codec *codec,
alc662_f5z_speaker_automute(codec);
break;
case ALC880_MIC_EVENT:
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
break;
}
}
+#define alc662_mode2_setup alc663_m51va_setup
+
static void alc662_mode2_inithook(struct hda_codec *codec)
{
alc662_f5z_speaker_automute(codec);
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
}
/* ***************** Mode3 ******************************/
static void alc663_mode3_unsol_event(struct hda_codec *codec,
@@ -16592,15 +16565,17 @@ static void alc663_mode3_unsol_event(struct hda_codec *codec,
alc663_two_hp_m1_speaker_automute(codec);
break;
case ALC880_MIC_EVENT:
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
break;
}
}
+#define alc663_mode3_setup alc663_m51va_setup
+
static void alc663_mode3_inithook(struct hda_codec *codec)
{
alc663_two_hp_m1_speaker_automute(codec);
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
}
/* ***************** Mode4 ******************************/
static void alc663_mode4_unsol_event(struct hda_codec *codec,
@@ -16611,15 +16586,17 @@ static void alc663_mode4_unsol_event(struct hda_codec *codec,
alc663_21jd_two_speaker_automute(codec);
break;
case ALC880_MIC_EVENT:
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
break;
}
}
+#define alc663_mode4_setup alc663_m51va_setup
+
static void alc663_mode4_inithook(struct hda_codec *codec)
{
alc663_21jd_two_speaker_automute(codec);
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
}
/* ***************** Mode5 ******************************/
static void alc663_mode5_unsol_event(struct hda_codec *codec,
@@ -16630,15 +16607,17 @@ static void alc663_mode5_unsol_event(struct hda_codec *codec,
alc663_15jd_two_speaker_automute(codec);
break;
case ALC880_MIC_EVENT:
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
break;
}
}
+#define alc663_mode5_setup alc663_m51va_setup
+
static void alc663_mode5_inithook(struct hda_codec *codec)
{
alc663_15jd_two_speaker_automute(codec);
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
}
/* ***************** Mode6 ******************************/
static void alc663_mode6_unsol_event(struct hda_codec *codec,
@@ -16649,15 +16628,17 @@ static void alc663_mode6_unsol_event(struct hda_codec *codec,
alc663_two_hp_m2_speaker_automute(codec);
break;
case ALC880_MIC_EVENT:
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
break;
}
}
+#define alc663_mode6_setup alc663_m51va_setup
+
static void alc663_mode6_inithook(struct hda_codec *codec)
{
alc663_two_hp_m2_speaker_automute(codec);
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
}
static void alc663_g71v_hp_automute(struct hda_codec *codec)
@@ -16699,16 +16680,18 @@ static void alc663_g71v_unsol_event(struct hda_codec *codec,
alc663_g71v_front_automute(codec);
break;
case ALC880_MIC_EVENT:
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
break;
}
}
+#define alc663_g71v_setup alc663_m51va_setup
+
static void alc663_g71v_inithook(struct hda_codec *codec)
{
alc663_g71v_front_automute(codec);
alc663_g71v_hp_automute(codec);
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
}
static void alc663_g50v_unsol_event(struct hda_codec *codec,
@@ -16719,15 +16702,17 @@ static void alc663_g50v_unsol_event(struct hda_codec *codec,
alc663_m51va_speaker_automute(codec);
break;
case ALC880_MIC_EVENT:
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
break;
}
}
+#define alc663_g50v_setup alc663_m51va_setup
+
static void alc663_g50v_inithook(struct hda_codec *codec)
{
alc663_m51va_speaker_automute(codec);
- alc662_eeepc_mic_automute(codec);
+ alc_mic_automute(codec);
}
static struct snd_kcontrol_new alc662_ecs_mixer[] = {
@@ -16931,8 +16916,8 @@ static struct alc_config_preset alc662_presets[] = {
.dac_nids = alc662_dac_nids,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_eeepc_capture_source,
.unsol_event = alc662_eeepc_unsol_event,
+ .setup = alc662_eeepc_setup,
.init_hook = alc662_eeepc_inithook,
},
[ALC662_ASUS_EEEPC_EP20] = {
@@ -16946,6 +16931,7 @@ static struct alc_config_preset alc662_presets[] = {
.channel_mode = alc662_3ST_6ch_modes,
.input_mux = &alc662_lenovo_101e_capture_source,
.unsol_event = alc662_eeepc_unsol_event,
+ .setup = alc662_eeepc_ep20_setup,
.init_hook = alc662_eeepc_ep20_inithook,
},
[ALC662_ECS] = {
@@ -16956,8 +16942,8 @@ static struct alc_config_preset alc662_presets[] = {
.dac_nids = alc662_dac_nids,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_eeepc_capture_source,
.unsol_event = alc662_eeepc_unsol_event,
+ .setup = alc662_eeepc_setup,
.init_hook = alc662_eeepc_inithook,
},
[ALC663_ASUS_M51VA] = {
@@ -16968,8 +16954,8 @@ static struct alc_config_preset alc662_presets[] = {
.dig_out_nid = ALC662_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc663_m51va_capture_source,
.unsol_event = alc663_m51va_unsol_event,
+ .setup = alc663_m51va_setup,
.init_hook = alc663_m51va_inithook,
},
[ALC663_ASUS_G71V] = {
@@ -16980,8 +16966,8 @@ static struct alc_config_preset alc662_presets[] = {
.dig_out_nid = ALC662_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_eeepc_capture_source,
.unsol_event = alc663_g71v_unsol_event,
+ .setup = alc663_g71v_setup,
.init_hook = alc663_g71v_inithook,
},
[ALC663_ASUS_H13] = {
@@ -16991,7 +16977,6 @@ static struct alc_config_preset alc662_presets[] = {
.dac_nids = alc662_dac_nids,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc663_m51va_capture_source,
.unsol_event = alc663_m51va_unsol_event,
.init_hook = alc663_m51va_inithook,
},
@@ -17005,6 +16990,7 @@ static struct alc_config_preset alc662_presets[] = {
.channel_mode = alc662_3ST_6ch_modes,
.input_mux = &alc663_capture_source,
.unsol_event = alc663_g50v_unsol_event,
+ .setup = alc663_g50v_setup,
.init_hook = alc663_g50v_inithook,
},
[ALC663_ASUS_MODE1] = {
@@ -17018,8 +17004,8 @@ static struct alc_config_preset alc662_presets[] = {
.dig_out_nid = ALC662_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_eeepc_capture_source,
.unsol_event = alc663_mode1_unsol_event,
+ .setup = alc663_mode1_setup,
.init_hook = alc663_mode1_inithook,
},
[ALC662_ASUS_MODE2] = {
@@ -17032,8 +17018,8 @@ static struct alc_config_preset alc662_presets[] = {
.dig_out_nid = ALC662_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_eeepc_capture_source,
.unsol_event = alc662_mode2_unsol_event,
+ .setup = alc662_mode2_setup,
.init_hook = alc662_mode2_inithook,
},
[ALC663_ASUS_MODE3] = {
@@ -17047,8 +17033,8 @@ static struct alc_config_preset alc662_presets[] = {
.dig_out_nid = ALC662_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_eeepc_capture_source,
.unsol_event = alc663_mode3_unsol_event,
+ .setup = alc663_mode3_setup,
.init_hook = alc663_mode3_inithook,
},
[ALC663_ASUS_MODE4] = {
@@ -17062,8 +17048,8 @@ static struct alc_config_preset alc662_presets[] = {
.dig_out_nid = ALC662_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_eeepc_capture_source,
.unsol_event = alc663_mode4_unsol_event,
+ .setup = alc663_mode4_setup,
.init_hook = alc663_mode4_inithook,
},
[ALC663_ASUS_MODE5] = {
@@ -17077,8 +17063,8 @@ static struct alc_config_preset alc662_presets[] = {
.dig_out_nid = ALC662_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_eeepc_capture_source,
.unsol_event = alc663_mode5_unsol_event,
+ .setup = alc663_mode5_setup,
.init_hook = alc663_mode5_inithook,
},
[ALC663_ASUS_MODE6] = {
@@ -17092,8 +17078,8 @@ static struct alc_config_preset alc662_presets[] = {
.dig_out_nid = ALC662_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_eeepc_capture_source,
.unsol_event = alc663_mode6_unsol_event,
+ .setup = alc663_mode6_setup,
.init_hook = alc663_mode6_inithook,
},
[ALC272_DELL] = {
@@ -17107,8 +17093,8 @@ static struct alc_config_preset alc662_presets[] = {
.num_adc_nids = ARRAY_SIZE(alc272_adc_nids),
.capsrc_nids = alc272_capsrc_nids,
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc663_m51va_capture_source,
.unsol_event = alc663_m51va_unsol_event,
+ .setup = alc663_m51va_setup,
.init_hook = alc663_m51va_inithook,
},
[ALC272_DELL_ZM1] = {
@@ -17119,11 +17105,11 @@ static struct alc_config_preset alc662_presets[] = {
.dac_nids = alc662_dac_nids,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.adc_nids = alc662_adc_nids,
- .num_adc_nids = ARRAY_SIZE(alc662_adc_nids),
+ .num_adc_nids = 1,
.capsrc_nids = alc662_capsrc_nids,
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc663_m51va_capture_source,
.unsol_event = alc663_m51va_unsol_event,
+ .setup = alc663_m51va_setup,
.init_hook = alc663_m51va_inithook,
},
[ALC272_SAMSUNG_NC10] = {
@@ -17134,8 +17120,9 @@ static struct alc_config_preset alc662_presets[] = {
.dac_nids = alc272_dac_nids,
.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
.channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc272_nc10_capture_source,
+ /*.input_mux = &alc272_nc10_capture_source,*/
.unsol_event = alc663_mode4_unsol_event,
+ .setup = alc663_mode4_setup,
.init_hook = alc663_mode4_inithook,
},
};
@@ -17187,13 +17174,25 @@ static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec,
if (err < 0)
return err;
} else {
- sprintf(name, "%s Playback Volume", chname[i]);
+ const char *pfx;
+ if (cfg->line_outs == 1 &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ if (!cfg->hp_pins)
+ pfx = "Speaker";
+ else
+ pfx = "PCM";
+ } else
+ pfx = chname[i];
+ sprintf(name, "%s Playback Volume", pfx);
err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
HDA_COMPOSE_AMP_VAL(nid, 3, 0,
HDA_OUTPUT));
if (err < 0)
return err;
- sprintf(name, "%s Playback Switch", chname[i]);
+ if (cfg->line_outs == 1 &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ pfx = "Speaker";
+ sprintf(name, "%s Playback Switch", pfx);
err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
HDA_COMPOSE_AMP_VAL(alc880_idx_to_mixer(i),
3, 0, HDA_INPUT));
@@ -17255,62 +17254,9 @@ static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
return 0;
}
-/* return the index of the src widget from the connection list of the nid.
- * return -1 if not found
- */
-static int alc662_input_pin_idx(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t src)
-{
- hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
- int i, conns;
-
- conns = snd_hda_get_connections(codec, nid, conn_list,
- ARRAY_SIZE(conn_list));
- if (conns < 0)
- return -1;
- for (i = 0; i < conns; i++)
- if (conn_list[i] == src)
- return i;
- return -1;
-}
-
-static int alc662_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
- return (pincap & AC_PINCAP_IN) != 0;
-}
-
/* create playback/capture controls for input pins */
-static int alc662_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct alc_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (alc662_is_input_pin(codec, cfg->input_pins[i])) {
- idx = alc662_input_pin_idx(codec, 0x0b,
- cfg->input_pins[i]);
- if (idx >= 0) {
- err = new_analog_input(spec, cfg->input_pins[i],
- auto_pin_cfg_labels[i],
- idx, 0x0b);
- if (err < 0)
- return err;
- }
- idx = alc662_input_pin_idx(codec, 0x22,
- cfg->input_pins[i]);
- if (idx >= 0) {
- imux->items[imux->num_items].label =
- auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- }
- }
- return 0;
-}
+#define alc662_auto_create_input_ctls \
+ alc880_auto_create_input_ctls
static void alc662_auto_set_output_and_unmute(struct hda_codec *codec,
hda_nid_t nid, int pin_type,
@@ -17364,7 +17310,7 @@ static void alc662_auto_init_analog_input(struct hda_codec *codec)
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = spec->autocfg.input_pins[i];
- if (alc662_is_input_pin(codec, nid)) {
+ if (alc_is_input_pin(codec, nid)) {
alc_set_input_pin(codec, nid, i);
if (nid != ALC662_PIN_CD_NID &&
(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
@@ -17405,7 +17351,7 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
"Headphone");
if (err < 0)
return err;
- err = alc662_auto_create_analog_input_ctls(codec, &spec->autocfg);
+ err = alc662_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -17462,8 +17408,8 @@ static int patch_alc662(struct hda_codec *codec)
alc662_models,
alc662_cfg_tbl);
if (board_config < 0) {
- printk(KERN_INFO "hda_codec: Unknown model for %s, "
- "trying auto-probe from BIOS...\n", codec->chip_name);
+ printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
board_config = ALC662_AUTO;
}
@@ -17488,7 +17434,7 @@ static int patch_alc662(struct hda_codec *codec)
}
if (board_config != ALC662_AUTO)
- setup_preset(spec, &alc662_presets[board_config]);
+ setup_preset(codec, &alc662_presets[board_config]);
spec->stream_analog_playback = &alc662_pcm_analog_playback;
spec->stream_analog_capture = &alc662_pcm_analog_capture;
@@ -17496,12 +17442,15 @@ static int patch_alc662(struct hda_codec *codec)
spec->stream_digital_playback = &alc662_pcm_digital_playback;
spec->stream_digital_capture = &alc662_pcm_digital_capture;
- spec->adc_nids = alc662_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
- spec->capsrc_nids = alc662_capsrc_nids;
+ if (!spec->adc_nids) {
+ spec->adc_nids = alc662_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
+ }
+ if (!spec->capsrc_nids)
+ spec->capsrc_nids = alc662_capsrc_nids;
if (!spec->cap_mixer)
- set_capture_mixer(spec);
+ set_capture_mixer(codec);
if (codec->vendor_id == 0x10ec0662)
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
else
@@ -17537,23 +17486,23 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
{ .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd },
{ .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2",
- .patch = patch_alc883 },
+ .patch = patch_alc882 },
{ .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
.patch = patch_alc662 },
{ .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
- { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
+ { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
{ .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A",
- .patch = patch_alc882 }, /* should be patch_alc883() in future */
+ .patch = patch_alc882 },
{ .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
- .patch = patch_alc882 }, /* should be patch_alc883() in future */
+ .patch = patch_alc882 },
{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
- { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc883 },
+ { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 },
{ .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
- .patch = patch_alc883 },
- { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
- { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 },
+ .patch = patch_alc882 },
+ { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 },
+ { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 5383d8cff88..e31e53dc696 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -40,6 +40,8 @@ enum {
STAC_INSERT_EVENT,
STAC_PWR_EVENT,
STAC_HP_EVENT,
+ STAC_LO_EVENT,
+ STAC_MIC_EVENT,
};
enum {
@@ -76,10 +78,12 @@ enum {
STAC_92HD73XX_AUTO,
STAC_92HD73XX_NO_JD, /* no jack-detection */
STAC_92HD73XX_REF,
+ STAC_92HD73XX_INTEL,
STAC_DELL_M6_AMIC,
STAC_DELL_M6_DMIC,
STAC_DELL_M6_BOTH,
STAC_DELL_EQ,
+ STAC_ALIENWARE_M17X,
STAC_92HD73XX_MODELS
};
@@ -176,6 +180,12 @@ struct sigmatel_jack {
struct snd_jack *jack;
};
+struct sigmatel_mic_route {
+ hda_nid_t pin;
+ unsigned char mux_idx;
+ unsigned char dmux_idx;
+};
+
struct sigmatel_spec {
struct snd_kcontrol_new *mixers[4];
unsigned int num_mixers;
@@ -187,6 +197,7 @@ struct sigmatel_spec {
unsigned int hp_detect: 1;
unsigned int spdif_mute: 1;
unsigned int check_volume_offset:1;
+ unsigned int auto_mic:1;
/* gpio lines */
unsigned int eapd_mask;
@@ -218,7 +229,6 @@ struct sigmatel_spec {
/* playback */
struct hda_input_mux *mono_mux;
- struct hda_input_mux *amp_mux;
unsigned int cur_mmux;
struct hda_multi_out multiout;
hda_nid_t dac_nids[5];
@@ -238,6 +248,15 @@ struct sigmatel_spec {
unsigned int num_dmuxes;
hda_nid_t *smux_nids;
unsigned int num_smuxes;
+ unsigned int num_analog_muxes;
+
+ unsigned long *capvols; /* amp-volume attr: HDA_COMPOSE_AMP_VAL() */
+ unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */
+ unsigned int num_caps; /* number of capture volume/switch elements */
+
+ struct sigmatel_mic_route ext_mic;
+ struct sigmatel_mic_route int_mic;
+
const char **spdif_labels;
hda_nid_t dig_in_nid;
@@ -262,7 +281,6 @@ struct sigmatel_spec {
unsigned int cur_smux[2];
unsigned int cur_amux;
hda_nid_t *amp_nids;
- unsigned int num_amps;
unsigned int powerdown_adcs;
/* i/o switches */
@@ -281,7 +299,6 @@ struct sigmatel_spec {
struct hda_input_mux private_dimux;
struct hda_input_mux private_imux;
struct hda_input_mux private_smux;
- struct hda_input_mux private_amp_mux;
struct hda_input_mux private_mono_mux;
};
@@ -310,11 +327,6 @@ static hda_nid_t stac92hd73xx_adc_nids[2] = {
0x1a, 0x1b
};
-#define DELL_M6_AMP 2
-static hda_nid_t stac92hd73xx_amp_nids[3] = {
- 0x0b, 0x0c, 0x0e
-};
-
#define STAC92HD73XX_NUM_DMICS 2
static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
0x13, 0x14, 0
@@ -322,8 +334,8 @@ static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
#define STAC92HD73_DAC_COUNT 5
-static hda_nid_t stac92hd73xx_mux_nids[4] = {
- 0x28, 0x29, 0x2a, 0x2b,
+static hda_nid_t stac92hd73xx_mux_nids[2] = {
+ 0x20, 0x21,
};
static hda_nid_t stac92hd73xx_dmux_nids[2] = {
@@ -334,14 +346,16 @@ static hda_nid_t stac92hd73xx_smux_nids[2] = {
0x22, 0x23,
};
-#define STAC92HD83XXX_NUM_DMICS 2
-static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
- 0x11, 0x12, 0
+#define STAC92HD73XX_NUM_CAPS 2
+static unsigned long stac92hd73xx_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x20, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
};
+#define stac92hd73xx_capsws stac92hd73xx_capvols
#define STAC92HD83_DAC_COUNT 3
-static hda_nid_t stac92hd83xxx_dmux_nids[2] = {
+static hda_nid_t stac92hd83xxx_mux_nids[2] = {
0x17, 0x18,
};
@@ -361,9 +375,12 @@ static unsigned int stac92hd83xxx_pwr_mapping[4] = {
0x03, 0x0c, 0x20, 0x40,
};
-static hda_nid_t stac92hd83xxx_amp_nids[1] = {
- 0xc,
+#define STAC92HD83XXX_NUM_CAPS 2
+static unsigned long stac92hd83xxx_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_OUTPUT),
};
+#define stac92hd83xxx_capsws stac92hd83xxx_capvols
static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
0x0a, 0x0d, 0x0f
@@ -394,6 +411,13 @@ static hda_nid_t stac92hd71bxx_slave_dig_outs[2] = {
0x22, 0
};
+#define STAC92HD71BXX_NUM_CAPS 2
+static unsigned long stac92hd71bxx_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
+};
+#define stac92hd71bxx_capsws stac92hd71bxx_capvols
+
static hda_nid_t stac925x_adc_nids[1] = {
0x03,
};
@@ -415,6 +439,13 @@ static hda_nid_t stac925x_dmux_nids[1] = {
0x14,
};
+static unsigned long stac925x_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT),
+};
+static unsigned long stac925x_capsws[] = {
+ HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+};
+
static hda_nid_t stac922x_adc_nids[2] = {
0x06, 0x07,
};
@@ -423,6 +454,13 @@ static hda_nid_t stac922x_mux_nids[2] = {
0x12, 0x13,
};
+#define STAC922X_NUM_CAPS 2
+static unsigned long stac922x_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT),
+};
+#define stac922x_capsws stac922x_capvols
+
static hda_nid_t stac927x_slave_dig_outs[2] = {
0x1f, 0,
};
@@ -452,6 +490,18 @@ static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = {
0x13, 0x14, 0
};
+#define STAC927X_NUM_CAPS 3
+static unsigned long stac927x_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x19, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_INPUT),
+};
+static unsigned long stac927x_capsws[] = {
+ HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
+};
+
static const char *stac927x_spdif_labels[5] = {
"Digital Playback", "ADAT", "Analog Mux 1",
"Analog Mux 2", "Analog Mux 3"
@@ -478,6 +528,16 @@ static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
0x17, 0x18, 0
};
+#define STAC9205_NUM_CAPS 2
+static unsigned long stac9205_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_INPUT),
+};
+static unsigned long stac9205_capsws[] = {
+ HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x1e, 3, 0, HDA_OUTPUT),
+};
+
static hda_nid_t stac9200_pin_nids[8] = {
0x08, 0x09, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12,
@@ -528,34 +588,6 @@ static hda_nid_t stac9205_pin_nids[12] = {
0x21, 0x22,
};
-#define stac92xx_amp_volume_info snd_hda_mixer_amp_volume_info
-
-static int stac92xx_amp_volume_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = spec->amp_nids[spec->cur_amux];
-
- kcontrol->private_value ^= get_amp_nid(kcontrol);
- kcontrol->private_value |= nid;
-
- return snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);
-}
-
-static int stac92xx_amp_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = spec->amp_nids[spec->cur_amux];
-
- kcontrol->private_value ^= get_amp_nid(kcontrol);
- kcontrol->private_value |= nid;
-
- return snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
-}
-
static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -692,9 +724,35 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]);
+ const struct hda_input_mux *imux = spec->input_mux;
+ unsigned int idx, prev_idx;
+
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ prev_idx = spec->cur_mux[adc_idx];
+ if (prev_idx == idx)
+ return 0;
+ if (idx < spec->num_analog_muxes) {
+ snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ imux->items[idx].index);
+ if (prev_idx >= spec->num_analog_muxes) {
+ imux = spec->dinput_mux;
+ /* 0 = analog */
+ snd_hda_codec_write_cache(codec,
+ spec->dmux_nids[adc_idx], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ imux->items[0].index);
+ }
+ } else {
+ imux = spec->dinput_mux;
+ snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ imux->items[idx - 1].index);
+ }
+ spec->cur_mux[adc_idx] = idx;
+ return 1;
}
static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol,
@@ -725,41 +783,6 @@ static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,
spec->mono_nid, &spec->cur_mmux);
}
-static int stac92xx_amp_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->amp_mux, uinfo);
-}
-
-static int stac92xx_amp_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- ucontrol->value.enumerated.item[0] = spec->cur_amux;
- return 0;
-}
-
-static int stac92xx_amp_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- struct snd_kcontrol *ctl =
- snd_hda_find_mixer_ctl(codec, "Amp Capture Volume");
- if (!ctl)
- return -EINVAL;
-
- snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE |
- SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
-
- return snd_hda_input_mux_put(codec, spec->amp_mux, ucontrol,
- 0, &spec->cur_amux);
-}
-
#define stac92xx_aloopback_info snd_ctl_boolean_mono_info
static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
@@ -827,84 +850,16 @@ static struct hda_verb stac9200_eapd_init[] = {
{}
};
-static struct hda_verb stac92hd73xx_6ch_core_init[] = {
- /* set master volume and direct control */
- { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* setup adcs to point to mixer */
- { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
- { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* setup import muxs */
- { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {}
-};
-
static struct hda_verb dell_eq_core_init[] = {
/* set master volume to max value without distortion
* and direct control */
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
- /* setup adcs to point to mixer */
- { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
- { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
- /* setup import muxs */
- { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {}
-};
-
-static struct hda_verb dell_m6_core_init[] = {
- { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* setup adcs to point to mixer */
- { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
- { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
- /* setup import muxs */
- { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {}
-};
-
-static struct hda_verb stac92hd73xx_8ch_core_init[] = {
- /* set master volume and direct control */
- { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* setup adcs to point to mixer */
- { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
- { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* setup import muxs */
- { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
{}
};
-static struct hda_verb stac92hd73xx_10ch_core_init[] = {
+static struct hda_verb stac92hd73xx_core_init[] = {
/* set master volume and direct control */
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* dac3 is connected to import3 mux */
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f},
- /* setup adcs to point to mixer */
- { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
- { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* setup import muxs */
- { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
{}
};
@@ -924,19 +879,6 @@ static struct hda_verb stac92hd71bxx_core_init[] = {
{}
};
-#define HD_DISABLE_PORTF 1
-static struct hda_verb stac92hd71bxx_analog_core_init[] = {
- /* start of config #1 */
-
- /* connect port 0f to audio mixer */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
- /* start of config #2 */
-
- /* set master volume and direct control */
- { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- {}
-};
-
static struct hda_verb stac92hd71bxx_unmute_core_init[] = {
/* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -995,31 +937,6 @@ static struct hda_verb stac9205_core_init[] = {
.put = stac92xx_mono_mux_enum_put, \
}
-#define STAC_AMP_MUX \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Amp Selector Capture Switch", \
- .count = 1, \
- .info = stac92xx_amp_mux_enum_info, \
- .get = stac92xx_amp_mux_enum_get, \
- .put = stac92xx_amp_mux_enum_put, \
- }
-
-#define STAC_AMP_VOL(xname, nid, chs, idx, dir) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = 0, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
- SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
- .info = stac92xx_amp_volume_info, \
- .get = stac92xx_amp_volume_get, \
- .put = stac92xx_amp_volume_put, \
- .tlv = { .c = snd_hda_mixer_amp_tlv }, \
- .private_value = HDA_COMPOSE_AMP_VAL(nid, chs, idx, dir) \
- }
-
#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
@@ -1050,34 +967,6 @@ static struct snd_kcontrol_new stac9200_mixer[] = {
{ } /* end */
};
-#define DELL_M6_MIXER 6
-static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = {
- /* start of config #1 */
- HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
-
- /* start of config #2 */
- HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
-
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
-
- { } /* end */
-};
-
static struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = {
STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
{}
@@ -1093,134 +982,14 @@ static struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = {
{}
};
-static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = {
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
- { } /* end */
-};
-
-
-static struct snd_kcontrol_new stac92hd83xxx_mixer[] = {
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0x3, HDA_INPUT),
-
- HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x4, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x2, HDA_INPUT),
-
- /*
- HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x1, HDA_INPUT),
- */
- { } /* end */
-};
-
-static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
- /* analog pc-beep replaced with digital beep support */
- /*
- HDA_CODEC_VOLUME("PC Beep Volume", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("PC Beep Switch", 0x17, 0x2, HDA_INPUT),
- */
-
- HDA_CODEC_MUTE("Import0 Mux Capture Switch", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Import0 Mux Capture Volume", 0x17, 0x0, HDA_INPUT),
-
- HDA_CODEC_MUTE("Import1 Mux Capture Switch", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Import1 Mux Capture Volume", 0x17, 0x1, HDA_INPUT),
-
- HDA_CODEC_MUTE("DAC0 Capture Switch", 0x17, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x17, 0x3, HDA_INPUT),
-
- HDA_CODEC_MUTE("DAC1 Capture Switch", 0x17, 0x4, HDA_INPUT),
- HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x17, 0x4, HDA_INPUT),
- { } /* end */
-};
static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2)
};
-static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
static struct snd_kcontrol_new stac925x_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x14, 0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new stac9205_mixer[] = {
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1c, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1e, 0x0, HDA_OUTPUT),
{ } /* end */
};
@@ -1229,29 +998,6 @@ static struct snd_kcontrol_new stac9205_loopback[] = {
{}
};
-/* This needs to be generated dynamically based on sequence */
-static struct snd_kcontrol_new stac922x_mixer[] = {
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-
-static struct snd_kcontrol_new stac927x_mixer[] = {
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x19, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1c, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME_IDX("Capture Volume", 0x2, 0x1A, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 0x2, 0x1d, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
static struct snd_kcontrol_new stac927x_loopback[] = {
STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
{}
@@ -1309,16 +1055,19 @@ static int stac92xx_build_controls(struct hda_codec *codec)
int err;
int i;
- err = snd_hda_add_new_ctls(codec, spec->mixer);
- if (err < 0)
- return err;
+ if (spec->mixer) {
+ err = snd_hda_add_new_ctls(codec, spec->mixer);
+ if (err < 0)
+ return err;
+ }
for (i = 0; i < spec->num_mixers; i++) {
err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
if (err < 0)
return err;
}
- if (spec->num_dmuxes > 0) {
+ if (!spec->auto_mic && spec->num_dmuxes > 0 &&
+ snd_hda_get_bool_hint(codec, "separate_dmux") == 1) {
stac_dmux_mixer.count = spec->num_dmuxes;
err = snd_hda_ctl_add(codec,
snd_ctl_new1(&stac_dmux_mixer, codec));
@@ -1765,22 +1514,32 @@ static unsigned int dell_m6_pin_configs[13] = {
0x4f0000f0,
};
+static unsigned int alienware_m17x_pin_configs[13] = {
+ 0x0321101f, 0x0321101f, 0x03a11020, 0x03014020,
+ 0x90170110, 0x4f0000f0, 0x4f0000f0, 0x4f0000f0,
+ 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
+ 0x904601b0,
+};
+
static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
[STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
[STAC_DELL_M6_AMIC] = dell_m6_pin_configs,
[STAC_DELL_M6_DMIC] = dell_m6_pin_configs,
[STAC_DELL_M6_BOTH] = dell_m6_pin_configs,
[STAC_DELL_EQ] = dell_m6_pin_configs,
+ [STAC_ALIENWARE_M17X] = alienware_m17x_pin_configs,
};
static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
[STAC_92HD73XX_AUTO] = "auto",
[STAC_92HD73XX_NO_JD] = "no-jd",
[STAC_92HD73XX_REF] = "ref",
+ [STAC_92HD73XX_INTEL] = "intel",
[STAC_DELL_M6_AMIC] = "dell-m6-amic",
[STAC_DELL_M6_DMIC] = "dell-m6-dmic",
[STAC_DELL_M6_BOTH] = "dell-m6",
[STAC_DELL_EQ] = "dell-eq",
+ [STAC_ALIENWARE_M17X] = "alienware",
};
static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
@@ -1789,6 +1548,10 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
"DFI LanParty", STAC_92HD73XX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
"DFI LanParty", STAC_92HD73XX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5002,
+ "Intel DG45ID", STAC_92HD73XX_INTEL),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5003,
+ "Intel DG45FC", STAC_92HD73XX_INTEL),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254,
"Dell Studio 1535", STAC_DELL_M6_DMIC),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255,
@@ -1814,6 +1577,12 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
{} /* terminator */
};
+static struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = {
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a1,
+ "Alienware M17x", STAC_ALIENWARE_M17X),
+ {} /* terminator */
+};
+
static unsigned int ref92hd83xxx_pin_configs[10] = {
0x02214030, 0x02211010, 0x02a19020, 0x02170130,
0x01014050, 0x01819040, 0x01014020, 0x90a3014e,
@@ -1921,6 +1690,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
"HP mini 1000", STAC_HP_M4),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b,
"HP HDX", STAC_HP_HDX), /* HDX16 */
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010,
+ "HP", STAC_HP_DV5),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
"unknown Dell", STAC_DELL_M4_1),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
@@ -2266,7 +2037,7 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS),
@@ -2636,8 +2407,7 @@ static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid,
- unsigned char type);
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid);
static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -2651,7 +2421,7 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
/* check to be sure that the ports are upto date with
* switch changes
*/
- stac_issue_unsol_event(codec, nid, STAC_HP_EVENT);
+ stac_issue_unsol_event(codec, nid);
return 1;
}
@@ -2784,7 +2554,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
* appropriately according to the pin direction
*/
if (spec->hp_detect)
- stac_issue_unsol_event(codec, nid, STAC_HP_EVENT);
+ stac_issue_unsol_event(codec, nid);
return 1;
}
@@ -2853,8 +2623,6 @@ enum {
STAC_CTL_WIDGET_VOL,
STAC_CTL_WIDGET_MUTE,
STAC_CTL_WIDGET_MONO_MUX,
- STAC_CTL_WIDGET_AMP_MUX,
- STAC_CTL_WIDGET_AMP_VOL,
STAC_CTL_WIDGET_HP_SWITCH,
STAC_CTL_WIDGET_IO_SWITCH,
STAC_CTL_WIDGET_CLFE_SWITCH,
@@ -2865,8 +2633,6 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {
HDA_CODEC_VOLUME(NULL, 0, 0, 0),
HDA_CODEC_MUTE(NULL, 0, 0, 0),
STAC_MONO_MUX,
- STAC_AMP_MUX,
- STAC_AMP_VOL(NULL, 0, 0, 0, 0),
STAC_CODEC_HP_SWITCH(NULL),
STAC_CODEC_IO_SWITCH(NULL, 0),
STAC_CODEC_CLFE_SWITCH(NULL, 0),
@@ -2967,6 +2733,8 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec)
struct snd_kcontrol_new *knew;
struct hda_input_mux *imux = &spec->private_imux;
+ if (spec->auto_mic)
+ return 0; /* no need for input source */
if (!spec->num_adcs || imux->num_items <= 1)
return 0; /* no need for input source control */
knew = stac_control_new(spec, &stac_input_src_temp,
@@ -3060,7 +2828,7 @@ static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid)
HDA_MAX_CONNECTIONS);
for (j = 0; j < conn_len; j++) {
wcaps = get_wcaps(codec, conn[j]);
- wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ wtype = get_wcaps_type(wcaps);
/* we check only analog outputs */
if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL))
continue;
@@ -3319,6 +3087,21 @@ static int create_multi_out_ctls(struct hda_codec *codec, int num_outs,
return 0;
}
+static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol,
+ unsigned long sw, int idx)
+{
+ int err;
+ err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx,
+ "Capture Volume", vol);
+ if (err < 0)
+ return err;
+ err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_MUTE, idx,
+ "Capture Switch", sw);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
/* add playback controls from the parsed DAC table */
static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
@@ -3392,7 +3175,7 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
spec->mono_nid,
con_lst,
HDA_MAX_NUM_INPUTS);
- if (!num_cons || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
+ if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
return -EINVAL;
for (i = 0; i < num_cons; i++) {
@@ -3406,37 +3189,6 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
"Mono Mux", spec->mono_nid);
}
-/* labels for amp mux outputs */
-static const char *stac92xx_amp_labels[3] = {
- "Front Microphone", "Microphone", "Line In",
-};
-
-/* create amp out controls mux on capable codecs */
-static int stac92xx_auto_create_amp_output_ctls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *amp_mux = &spec->private_amp_mux;
- int i, err;
-
- for (i = 0; i < spec->num_amps; i++) {
- amp_mux->items[amp_mux->num_items].label =
- stac92xx_amp_labels[i];
- amp_mux->items[amp_mux->num_items].index = i;
- amp_mux->num_items++;
- }
-
- if (spec->num_amps > 1) {
- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_MUX,
- "Amp Selector Capture Switch", 0);
- if (err < 0)
- return err;
- }
- return stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_VOL,
- "Amp Capture Volume",
- HDA_COMPOSE_AMP_VAL(spec->amp_nids[0], 3, 0, HDA_INPUT));
-}
-
-
/* create PC beep volume controls */
static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
hda_nid_t nid)
@@ -3505,19 +3257,33 @@ static int stac92xx_beep_switch_ctl(struct hda_codec *codec)
static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- int wcaps, nid, i, err = 0;
+ int i, j, err = 0;
for (i = 0; i < spec->num_muxes; i++) {
+ hda_nid_t nid;
+ unsigned int wcaps;
+ unsigned long val;
+
nid = spec->mux_nids[i];
wcaps = get_wcaps(codec, nid);
+ if (!(wcaps & AC_WCAP_OUT_AMP))
+ continue;
- if (wcaps & AC_WCAP_OUT_AMP) {
- err = stac92xx_add_control_idx(spec,
- STAC_CTL_WIDGET_VOL, i, "Mux Capture Volume",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
+ /* check whether already the same control was created as
+ * normal Capture Volume.
+ */
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ for (j = 0; j < spec->num_caps; j++) {
+ if (spec->capvols[j] == val)
+ break;
}
+ if (j < spec->num_caps)
+ continue;
+
+ err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, i,
+ "Mux Capture Volume", val);
+ if (err < 0)
+ return err;
}
return 0;
};
@@ -3538,7 +3304,7 @@ static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
spec->smux_nids[0],
con_lst,
HDA_MAX_NUM_INPUTS);
- if (!num_cons)
+ if (num_cons <= 0)
return -EINVAL;
if (!labels)
@@ -3559,101 +3325,231 @@ static const char *stac92xx_dmic_labels[5] = {
"Digital Mic 3", "Digital Mic 4"
};
+static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
+ hda_nid_t nid)
+{
+ hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+ int i, nums;
+
+ nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+ for (i = 0; i < nums; i++)
+ if (conn[i] == nid)
+ return i;
+ return -1;
+}
+
+/* create a volume assigned to the given pin (only if supported) */
+/* return 1 if the volume control is created */
+static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
+ const char *label, int direction)
+{
+ unsigned int caps, nums;
+ char name[32];
+ int err;
+
+ if (direction == HDA_OUTPUT)
+ caps = AC_WCAP_OUT_AMP;
+ else
+ caps = AC_WCAP_IN_AMP;
+ if (!(get_wcaps(codec, nid) & caps))
+ return 0;
+ caps = query_amp_caps(codec, nid, direction);
+ nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ if (!nums)
+ return 0;
+ snprintf(name, sizeof(name), "%s Capture Volume", label);
+ err = stac92xx_add_control(codec->spec, STAC_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
+ if (err < 0)
+ return err;
+ return 1;
+}
+
/* create playback/capture controls for input pins on dmic capable codecs */
static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
struct sigmatel_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->private_imux;
struct hda_input_mux *dimux = &spec->private_dimux;
- hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
- int err, i, j;
- char name[32];
+ int err, i, active_mics;
+ unsigned int def_conf;
dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
dimux->items[dimux->num_items].index = 0;
dimux->num_items++;
+ active_mics = 0;
+ for (i = 0; i < spec->num_dmics; i++) {
+ /* check the validity: sometimes it's a dead vendor-spec node */
+ if (get_wcaps_type(get_wcaps(codec, spec->dmic_nids[i]))
+ != AC_WID_PIN)
+ continue;
+ def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]);
+ if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
+ active_mics++;
+ }
+
for (i = 0; i < spec->num_dmics; i++) {
hda_nid_t nid;
int index;
- int num_cons;
- unsigned int wcaps;
- unsigned int def_conf;
+ const char *label;
- def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]);
+ nid = spec->dmic_nids[i];
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ continue;
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
continue;
- nid = spec->dmic_nids[i];
- num_cons = snd_hda_get_connections(codec,
- spec->dmux_nids[0],
- con_lst,
- HDA_MAX_NUM_INPUTS);
- for (j = 0; j < num_cons; j++)
- if (con_lst[j] == nid) {
- index = j;
- goto found;
- }
- continue;
-found:
- wcaps = get_wcaps(codec, nid) &
- (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);
+ index = get_connection_index(codec, spec->dmux_nids[0], nid);
+ if (index < 0)
+ continue;
- if (wcaps) {
- sprintf(name, "%s Capture Volume",
- stac92xx_dmic_labels[dimux->num_items]);
+ if (active_mics == 1)
+ label = "Digital Mic";
+ else
+ label = stac92xx_dmic_labels[dimux->num_items];
- err = stac92xx_add_control(spec,
- STAC_CTL_WIDGET_VOL,
- name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- (wcaps & AC_WCAP_OUT_AMP) ?
- HDA_OUTPUT : HDA_INPUT));
+ err = create_elem_capture_vol(codec, nid, label, HDA_INPUT);
+ if (err < 0)
+ return err;
+ if (!err) {
+ err = create_elem_capture_vol(codec, nid, label,
+ HDA_OUTPUT);
if (err < 0)
return err;
}
- dimux->items[dimux->num_items].label =
- stac92xx_dmic_labels[dimux->num_items];
+ dimux->items[dimux->num_items].label = label;
dimux->items[dimux->num_items].index = index;
dimux->num_items++;
+ if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) {
+ imux->items[imux->num_items].label = label;
+ imux->items[imux->num_items].index = index;
+ imux->num_items++;
+ }
}
return 0;
}
+static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t *fixed, hda_nid_t *ext)
+{
+ unsigned int cfg;
+
+ if (!nid)
+ return 0;
+ cfg = snd_hda_codec_get_pincfg(codec, nid);
+ switch (get_defcfg_connect(cfg)) {
+ case AC_JACK_PORT_FIXED:
+ if (*fixed)
+ return 1; /* already occupied */
+ *fixed = nid;
+ break;
+ case AC_JACK_PORT_COMPLEX:
+ if (*ext)
+ return 1; /* already occupied */
+ *ext = nid;
+ break;
+ }
+ return 0;
+}
+
+static int set_mic_route(struct hda_codec *codec,
+ struct sigmatel_mic_route *mic,
+ hda_nid_t pin)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ mic->pin = pin;
+ for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
+ if (pin == cfg->input_pins[i])
+ break;
+ if (i <= AUTO_PIN_FRONT_MIC) {
+ /* analog pin */
+ mic->dmux_idx = 0;
+ i = get_connection_index(codec, spec->mux_nids[0], pin);
+ if (i < 0)
+ return -1;
+ mic->mux_idx = i;
+ } else if (spec->dmux_nids) {
+ /* digital pin */
+ mic->mux_idx = 0;
+ i = get_connection_index(codec, spec->dmux_nids[0], pin);
+ if (i < 0)
+ return -1;
+ mic->dmux_idx = i;
+ }
+ return 0;
+}
+
+/* return non-zero if the device is for automatic mic switch */
+static int stac_check_auto_mic(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t fixed, ext;
+ int i;
+
+ for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) {
+ if (cfg->input_pins[i])
+ return 0; /* must be exclusively mics */
+ }
+ fixed = ext = 0;
+ for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
+ if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext))
+ return 0;
+ for (i = 0; i < spec->num_dmics; i++)
+ if (check_mic_pin(codec, spec->dmic_nids[i], &fixed, &ext))
+ return 0;
+ if (!fixed || !ext)
+ return 0;
+ if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
+ return 0; /* no unsol support */
+ if (set_mic_route(codec, &spec->ext_mic, ext) ||
+ set_mic_route(codec, &spec->int_mic, fixed))
+ return 0; /* something is wrong */
+ return 1;
+}
+
/* create playback/capture controls for input pins */
static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
{
struct sigmatel_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux;
- hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
- int i, j, k;
+ int i, j;
for (i = 0; i < AUTO_PIN_LAST; i++) {
- int index;
+ hda_nid_t nid = cfg->input_pins[i];
+ int index, err;
- if (!cfg->input_pins[i])
+ if (!nid)
continue;
index = -1;
for (j = 0; j < spec->num_muxes; j++) {
- int num_cons;
- num_cons = snd_hda_get_connections(codec,
- spec->mux_nids[j],
- con_lst,
- HDA_MAX_NUM_INPUTS);
- for (k = 0; k < num_cons; k++)
- if (con_lst[k] == cfg->input_pins[i]) {
- index = k;
- goto found;
- }
+ index = get_connection_index(codec, spec->mux_nids[j],
+ nid);
+ if (index >= 0)
+ break;
}
- continue;
- found:
+ if (index < 0)
+ continue;
+
+ err = create_elem_capture_vol(codec, nid,
+ auto_pin_cfg_labels[i],
+ HDA_INPUT);
+ if (err < 0)
+ return err;
+
imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
imux->items[imux->num_items].index = index;
imux->num_items++;
}
+ spec->num_analog_muxes = imux->num_items;
if (imux->num_items) {
/*
@@ -3705,7 +3601,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
{
struct sigmatel_spec *spec = codec->spec;
int hp_swap = 0;
- int err;
+ int i, err;
if ((err = snd_hda_parse_pin_def_config(codec,
&spec->autocfg,
@@ -3745,11 +3641,10 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
if (snd_hda_get_connections(codec,
spec->autocfg.mono_out_pin, conn_list, 1) &&
snd_hda_get_connections(codec, conn_list[0],
- conn_list, 1)) {
+ conn_list, 1) > 0) {
int wcaps = get_wcaps(codec, conn_list[0]);
- int wid_type = (wcaps & AC_WCAP_TYPE)
- >> AC_WCAP_TYPE_SHIFT;
+ int wid_type = get_wcaps_type(wcaps);
/* LR swap check, some stac925x have a mux that
* changes the DACs output path instead of the
* mono-mux path.
@@ -3840,6 +3735,21 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
spec->autocfg.line_outs = 0;
}
+ if (stac_check_auto_mic(codec)) {
+ spec->auto_mic = 1;
+ /* only one capture for auto-mic */
+ spec->num_adcs = 1;
+ spec->num_caps = 1;
+ spec->num_muxes = 1;
+ }
+
+ for (i = 0; i < spec->num_caps; i++) {
+ err = stac92xx_add_capvol_ctls(codec, spec->capvols[i],
+ spec->capsws[i], i);
+ if (err < 0)
+ return err;
+ }
+
err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -3849,11 +3759,6 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
if (err < 0)
return err;
}
- if (spec->num_amps > 0) {
- err = stac92xx_auto_create_amp_output_ctls(codec);
- if (err < 0)
- return err;
- }
if (spec->num_dmics > 0 && !spec->dinput_mux)
if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
&spec->autocfg)) < 0)
@@ -3890,7 +3795,6 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
spec->dinput_mux = &spec->private_dimux;
spec->sinput_mux = &spec->private_smux;
spec->mono_mux = &spec->private_mono_mux;
- spec->amp_mux = &spec->private_amp_mux;
return 1;
}
@@ -4102,14 +4006,14 @@ static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
}
static struct sigmatel_event *stac_get_event(struct hda_codec *codec,
- hda_nid_t nid, unsigned char type)
+ hda_nid_t nid)
{
struct sigmatel_spec *spec = codec->spec;
struct sigmatel_event *event = spec->events.list;
int i;
for (i = 0; i < spec->events.used; i++, event++) {
- if (event->nid == nid && event->type == type)
+ if (event->nid == nid)
return event;
}
return NULL;
@@ -4129,24 +4033,32 @@ static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec,
return NULL;
}
-static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
- unsigned int type)
+/* check if given nid is a valid pin and no other events are assigned
+ * to it. If OK, assign the event, set the unsol flag, and returns 1.
+ * Otherwise, returns zero.
+ */
+static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int type)
{
struct sigmatel_event *event;
int tag;
if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
- return;
- event = stac_get_event(codec, nid, type);
- if (event)
+ return 0;
+ event = stac_get_event(codec, nid);
+ if (event) {
+ if (event->type != type)
+ return 0;
tag = event->tag;
- else
+ } else {
tag = stac_add_event(codec->spec, nid, type, 0);
- if (tag < 0)
- return;
+ if (tag < 0)
+ return 0;
+ }
snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | tag);
+ return 1;
}
static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
@@ -4239,20 +4151,36 @@ static int stac92xx_init(struct hda_codec *codec)
hda_nid_t nid = cfg->hp_pins[i];
enable_pin_detect(codec, nid, STAC_HP_EVENT);
}
+ if (cfg->line_out_type == AUTO_PIN_LINE_OUT &&
+ cfg->speaker_outs > 0) {
+ /* enable pin-detect for line-outs as well */
+ for (i = 0; i < cfg->line_outs; i++) {
+ hda_nid_t nid = cfg->line_out_pins[i];
+ enable_pin_detect(codec, nid, STAC_LO_EVENT);
+ }
+ }
+
/* force to enable the first line-out; the others are set up
* in unsol_event
*/
stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
AC_PINCTL_OUT_EN);
/* fake event to set up pins */
- stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0],
- STAC_HP_EVENT);
+ stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0]);
} else {
stac92xx_auto_init_multi_out(codec);
stac92xx_auto_init_hp_out(codec);
for (i = 0; i < cfg->hp_outs; i++)
stac_toggle_power_map(codec, cfg->hp_pins[i], 1);
}
+ if (spec->auto_mic) {
+ /* initialize connection to analog input */
+ if (spec->dmux_nids)
+ snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
+ AC_VERB_SET_CONNECT_SEL, 0);
+ if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
+ stac_issue_unsol_event(codec, spec->ext_mic.pin);
+ }
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = cfg->input_pins[i];
if (nid) {
@@ -4279,10 +4207,9 @@ static int stac92xx_init(struct hda_codec *codec)
}
conf = snd_hda_codec_get_pincfg(codec, nid);
if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
- enable_pin_detect(codec, nid,
- STAC_INSERT_EVENT);
- stac_issue_unsol_event(codec, nid,
- STAC_INSERT_EVENT);
+ if (enable_pin_detect(codec, nid,
+ STAC_INSERT_EVENT))
+ stac_issue_unsol_event(codec, nid);
}
}
}
@@ -4327,10 +4254,8 @@ static int stac92xx_init(struct hda_codec *codec)
stac_toggle_power_map(codec, nid, 1);
continue;
}
- if (!stac_get_event(codec, nid, STAC_INSERT_EVENT)) {
- enable_pin_detect(codec, nid, STAC_PWR_EVENT);
- stac_issue_unsol_event(codec, nid, STAC_PWR_EVENT);
- }
+ if (enable_pin_detect(codec, nid, STAC_PWR_EVENT))
+ stac_issue_unsol_event(codec, nid);
}
if (spec->dac_list)
stac92xx_power_down(codec);
@@ -4434,6 +4359,48 @@ static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
return 0;
}
+static void stac92xx_line_out_detect(struct hda_codec *codec,
+ int presence)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < cfg->line_outs; i++) {
+ if (presence)
+ break;
+ presence = get_pin_presence(codec, cfg->line_out_pins[i]);
+ if (presence) {
+ unsigned int pinctl;
+ pinctl = snd_hda_codec_read(codec,
+ cfg->line_out_pins[i], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ if (pinctl & AC_PINCTL_IN_EN)
+ presence = 0; /* mic- or line-input */
+ }
+ }
+
+ if (presence) {
+ /* disable speakers */
+ for (i = 0; i < cfg->speaker_outs; i++)
+ stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
+ AC_PINCTL_OUT_EN);
+ if (spec->eapd_mask && spec->eapd_switch)
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data &
+ ~spec->eapd_mask);
+ } else {
+ /* enable speakers */
+ for (i = 0; i < cfg->speaker_outs; i++)
+ stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
+ AC_PINCTL_OUT_EN);
+ if (spec->eapd_mask && spec->eapd_switch)
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data |
+ spec->eapd_mask);
+ }
+}
+
/* return non-zero if the hp-pin of the given array index isn't
* a jack-detection target
*/
@@ -4486,13 +4453,6 @@ static void stac92xx_hp_detect(struct hda_codec *codec)
for (i = 0; i < cfg->line_outs; i++)
stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
AC_PINCTL_OUT_EN);
- for (i = 0; i < cfg->speaker_outs; i++)
- stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
- AC_PINCTL_OUT_EN);
- if (spec->eapd_mask && spec->eapd_switch)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data &
- ~spec->eapd_mask);
} else {
/* enable lineouts */
if (spec->hp_switch)
@@ -4501,14 +4461,8 @@ static void stac92xx_hp_detect(struct hda_codec *codec)
for (i = 0; i < cfg->line_outs; i++)
stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
AC_PINCTL_OUT_EN);
- for (i = 0; i < cfg->speaker_outs; i++)
- stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
- AC_PINCTL_OUT_EN);
- if (spec->eapd_mask && spec->eapd_switch)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data |
- spec->eapd_mask);
}
+ stac92xx_line_out_detect(codec, presence);
/* toggle hp outs */
for (i = 0; i < cfg->hp_outs; i++) {
unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN;
@@ -4593,10 +4547,28 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
}
}
-static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid,
- unsigned char type)
+static void stac92xx_mic_detect(struct hda_codec *codec)
{
- struct sigmatel_event *event = stac_get_event(codec, nid, type);
+ struct sigmatel_spec *spec = codec->spec;
+ struct sigmatel_mic_route *mic;
+
+ if (get_pin_presence(codec, spec->ext_mic.pin))
+ mic = &spec->ext_mic;
+ else
+ mic = &spec->int_mic;
+ if (mic->dmux_idx)
+ snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mic->dmux_idx);
+ else
+ snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mic->mux_idx);
+}
+
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct sigmatel_event *event = stac_get_event(codec, nid);
if (!event)
return;
codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26);
@@ -4615,8 +4587,18 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
switch (event->type) {
case STAC_HP_EVENT:
+ case STAC_LO_EVENT:
stac92xx_hp_detect(codec);
- /* fallthru */
+ break;
+ case STAC_MIC_EVENT:
+ stac92xx_mic_detect(codec);
+ break;
+ }
+
+ switch (event->type) {
+ case STAC_HP_EVENT:
+ case STAC_LO_EVENT:
+ case STAC_MIC_EVENT:
case STAC_INSERT_EVENT:
case STAC_PWR_EVENT:
if (spec->num_pwrs > 0)
@@ -4707,8 +4689,7 @@ static int stac92xx_resume(struct hda_codec *codec)
snd_hda_codec_resume_cache(codec);
/* fake event to set up pins again to override cached values */
if (spec->hp_detect)
- stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0],
- STAC_HP_EVENT);
+ stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0]);
return 0;
}
@@ -4748,6 +4729,19 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
{
struct sigmatel_spec *spec = codec->spec;
+ int i;
+ hda_nid_t nid;
+
+ /* reset each pin before powering down DAC/ADC to avoid click noise */
+ nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int wid_type = get_wcaps_type(wcaps);
+ if (wid_type == AC_WID_PIN)
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+ }
+
if (spec->eapd_mask)
stac_gpio_set(codec, spec->gpio_mask,
spec->gpio_dir, spec->gpio_data &
@@ -4784,7 +4778,8 @@ static int patch_stac9200(struct hda_codec *codec)
stac9200_models,
stac9200_cfg_tbl);
if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
else
stac92xx_set_config_regs(codec,
stac9200_brd_tbl[spec->board_config]);
@@ -4856,8 +4851,8 @@ static int patch_stac925x(struct hda_codec *codec)
stac925x_cfg_tbl);
again:
if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x,"
- "using BIOS defaults\n");
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
else
stac92xx_set_config_regs(codec,
stac925x_brd_tbl[spec->board_config]);
@@ -4887,6 +4882,9 @@ static int patch_stac925x(struct hda_codec *codec)
spec->init = stac925x_core_init;
spec->mixer = stac925x_mixer;
+ spec->num_caps = 1;
+ spec->capvols = stac925x_capvols;
+ spec->capsws = stac925x_capsws;
err = stac92xx_parse_auto_config(codec, 0x8, 0x7);
if (!err) {
@@ -4908,16 +4906,6 @@ static int patch_stac925x(struct hda_codec *codec)
return 0;
}
-static struct hda_input_mux stac92hd73xx_dmux = {
- .num_items = 4,
- .items = {
- { "Analog Inputs", 0x0b },
- { "Digital Mic 1", 0x09 },
- { "Digital Mic 2", 0x0a },
- { "CD", 0x08 },
- }
-};
-
static int patch_stac92hd73xx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
@@ -4937,10 +4925,16 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
STAC_92HD73XX_MODELS,
stac92hd73xx_models,
stac92hd73xx_cfg_tbl);
+ /* check codec subsystem id if not found */
+ if (spec->board_config < 0)
+ spec->board_config =
+ snd_hda_check_board_codec_sid_config(codec,
+ STAC_92HD73XX_MODELS, stac92hd73xx_models,
+ stac92hd73xx_codec_id_cfg_tbl);
again:
if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: Unknown model for"
- " STAC92HD73XX, using BIOS defaults\n");
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
else
stac92xx_set_config_regs(codec,
stac92hd73xx_brd_tbl[spec->board_config]);
@@ -4953,20 +4947,15 @@ again:
"number of channels defaulting to DAC count\n");
num_dacs = STAC92HD73_DAC_COUNT;
}
+ spec->init = stac92hd73xx_core_init;
switch (num_dacs) {
case 0x3: /* 6 Channel */
- spec->mixer = stac92hd73xx_6ch_mixer;
- spec->init = stac92hd73xx_6ch_core_init;
spec->aloopback_ctl = stac92hd73xx_6ch_loopback;
break;
case 0x4: /* 8 Channel */
- spec->mixer = stac92hd73xx_8ch_mixer;
- spec->init = stac92hd73xx_8ch_core_init;
spec->aloopback_ctl = stac92hd73xx_8ch_loopback;
break;
case 0x5: /* 10 Channel */
- spec->mixer = stac92hd73xx_10ch_mixer;
- spec->init = stac92hd73xx_10ch_core_init;
spec->aloopback_ctl = stac92hd73xx_10ch_loopback;
break;
}
@@ -4981,14 +4970,14 @@ again:
spec->dmic_nids = stac92hd73xx_dmic_nids;
spec->dmux_nids = stac92hd73xx_dmux_nids;
spec->smux_nids = stac92hd73xx_smux_nids;
- spec->amp_nids = stac92hd73xx_amp_nids;
- spec->num_amps = ARRAY_SIZE(stac92hd73xx_amp_nids);
spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
- memcpy(&spec->private_dimux, &stac92hd73xx_dmux,
- sizeof(stac92hd73xx_dmux));
+
+ spec->num_caps = STAC92HD73XX_NUM_CAPS;
+ spec->capvols = stac92hd73xx_capvols;
+ spec->capsws = stac92hd73xx_capsws;
switch (spec->board_config) {
case STAC_DELL_EQ:
@@ -4998,43 +4987,40 @@ again:
case STAC_DELL_M6_DMIC:
case STAC_DELL_M6_BOTH:
spec->num_smuxes = 0;
- spec->mixer = &stac92hd73xx_6ch_mixer[DELL_M6_MIXER];
- spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP];
spec->eapd_switch = 0;
- spec->num_amps = 1;
- if (spec->board_config != STAC_DELL_EQ)
- spec->init = dell_m6_core_init;
switch (spec->board_config) {
case STAC_DELL_M6_AMIC: /* Analog Mics */
snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
spec->num_dmics = 0;
- spec->private_dimux.num_items = 1;
break;
case STAC_DELL_M6_DMIC: /* Digital Mics */
snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
spec->num_dmics = 1;
- spec->private_dimux.num_items = 2;
break;
case STAC_DELL_M6_BOTH: /* Both */
snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
spec->num_dmics = 1;
- spec->private_dimux.num_items = 2;
break;
}
break;
+ case STAC_ALIENWARE_M17X:
+ spec->num_dmics = STAC92HD73XX_NUM_DMICS;
+ spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
+ spec->eapd_switch = 0;
+ break;
default:
spec->num_dmics = STAC92HD73XX_NUM_DMICS;
spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
spec->eapd_switch = 1;
+ break;
}
if (spec->board_config > STAC_92HD73XX_REF) {
/* GPIO0 High = Enable EAPD */
spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
spec->gpio_data = 0x01;
}
- spec->dinput_mux = &spec->private_dimux;
spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
spec->pwr_nids = stac92hd73xx_pwr_nids;
@@ -5066,15 +5052,6 @@ again:
return 0;
}
-static struct hda_input_mux stac92hd83xxx_dmux = {
- .num_items = 3,
- .items = {
- { "Analog Inputs", 0x03 },
- { "Digital Mic 1", 0x04 },
- { "Digital Mic 2", 0x05 },
- }
-};
-
static int patch_stac92hd83xxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
@@ -5091,32 +5068,30 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
spec->mono_nid = 0x19;
spec->digbeep_nid = 0x21;
- spec->dmic_nids = stac92hd83xxx_dmic_nids;
- spec->dmux_nids = stac92hd83xxx_dmux_nids;
+ spec->mux_nids = stac92hd83xxx_mux_nids;
+ spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids);
spec->adc_nids = stac92hd83xxx_adc_nids;
+ spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids);
spec->pwr_nids = stac92hd83xxx_pwr_nids;
- spec->amp_nids = stac92hd83xxx_amp_nids;
spec->pwr_mapping = stac92hd83xxx_pwr_mapping;
spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
spec->multiout.dac_nids = spec->dac_nids;
spec->init = stac92hd83xxx_core_init;
- spec->mixer = stac92hd83xxx_mixer;
spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids);
- spec->num_dmuxes = ARRAY_SIZE(stac92hd83xxx_dmux_nids);
- spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids);
- spec->num_amps = ARRAY_SIZE(stac92hd83xxx_amp_nids);
- spec->num_dmics = STAC92HD83XXX_NUM_DMICS;
- spec->dinput_mux = &stac92hd83xxx_dmux;
spec->pin_nids = stac92hd83xxx_pin_nids;
+ spec->num_caps = STAC92HD83XXX_NUM_CAPS;
+ spec->capvols = stac92hd83xxx_capvols;
+ spec->capsws = stac92hd83xxx_capsws;
+
spec->board_config = snd_hda_check_board_config(codec,
STAC_92HD83XXX_MODELS,
stac92hd83xxx_models,
stac92hd83xxx_cfg_tbl);
again:
if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: Unknown model for"
- " STAC92HD83XXX, using BIOS defaults\n");
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
else
stac92xx_set_config_regs(codec,
stac92hd83xxx_brd_tbl[spec->board_config]);
@@ -5158,6 +5133,8 @@ again:
num_dacs = snd_hda_get_connections(codec, nid,
conn, STAC92HD83_DAC_COUNT + 1) - 1;
+ if (num_dacs < 0)
+ num_dacs = STAC92HD83_DAC_COUNT;
/* set port X to select the last DAC
*/
@@ -5171,25 +5148,6 @@ again:
return 0;
}
-static struct hda_input_mux stac92hd71bxx_dmux_nomixer = {
- .num_items = 3,
- .items = {
- { "Analog Inputs", 0x00 },
- { "Digital Mic 1", 0x02 },
- { "Digital Mic 2", 0x03 },
- }
-};
-
-static struct hda_input_mux stac92hd71bxx_dmux_amixer = {
- .num_items = 4,
- .items = {
- { "Analog Inputs", 0x00 },
- { "Mixer", 0x01 },
- { "Digital Mic 1", 0x02 },
- { "Digital Mic 2", 0x03 },
- }
-};
-
/* get the pin connection (fixed, none, etc) */
static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
{
@@ -5250,7 +5208,6 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
struct sigmatel_spec *spec;
struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
int err = 0;
- unsigned int ndmic_nids = 0;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -5279,8 +5236,8 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
stac92hd71bxx_cfg_tbl);
again:
if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: Unknown model for"
- " STAC92HD71BXX, using BIOS defaults\n");
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
else
stac92xx_set_config_regs(codec,
stac92hd71bxx_brd_tbl[spec->board_config]);
@@ -5295,6 +5252,10 @@ again:
spec->dmic_nids = stac92hd71bxx_dmic_nids;
spec->dmux_nids = stac92hd71bxx_dmux_nids;
+ spec->num_caps = STAC92HD71BXX_NUM_CAPS;
+ spec->capvols = stac92hd71bxx_capvols;
+ spec->capsws = stac92hd71bxx_capsws;
+
switch (codec->vendor_id) {
case 0x111d76b6: /* 4 Port without Analog Mixer */
case 0x111d76b7:
@@ -5302,24 +5263,13 @@ again:
/* fallthru */
case 0x111d76b4: /* 6 Port without Analog Mixer */
case 0x111d76b5:
- memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_nomixer,
- sizeof(stac92hd71bxx_dmux_nomixer));
- spec->mixer = stac92hd71bxx_mixer;
spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
spec->num_dmics = stac92hd71bxx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS);
- if (spec->num_dmics) {
- spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
- spec->dinput_mux = &spec->private_dimux;
- ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1;
- }
break;
case 0x111d7608: /* 5 Port with Analog Mixer */
- memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_amixer,
- sizeof(stac92hd71bxx_dmux_amixer));
- spec->private_dimux.num_items--;
switch (spec->board_config) {
case STAC_HP_M4:
/* Enable VREF power saving on GPIO1 detect */
@@ -5341,11 +5291,8 @@ again:
/* no output amps */
spec->num_pwrs = 0;
- spec->mixer = stac92hd71bxx_analog_mixer;
- spec->dinput_mux = &spec->private_dimux;
-
/* disable VSW */
- spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
+ spec->init = stac92hd71bxx_core_init;
unmute_init++;
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
@@ -5353,8 +5300,6 @@ again:
spec->num_dmics = stac92hd71bxx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS - 1);
- spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
- ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 2;
break;
case 0x111d7603: /* 6 Port with Analog Mixer */
if ((codec->revision_id & 0xf) == 1)
@@ -5364,17 +5309,12 @@ again:
spec->num_pwrs = 0;
/* fallthru */
default:
- memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_amixer,
- sizeof(stac92hd71bxx_dmux_amixer));
- spec->dinput_mux = &spec->private_dimux;
- spec->mixer = stac92hd71bxx_analog_mixer;
- spec->init = stac92hd71bxx_analog_core_init;
+ spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
spec->num_dmics = stac92hd71bxx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS);
- spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
- ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1;
+ break;
}
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
@@ -5402,6 +5342,7 @@ again:
spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
+ spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e);
switch (spec->board_config) {
@@ -5456,8 +5397,6 @@ again:
#endif
spec->multiout.dac_nids = spec->dac_nids;
- if (spec->dinput_mux)
- spec->private_dimux.num_items += spec->num_dmics - ndmic_nids;
err = stac92xx_parse_auto_config(codec, 0x21, 0);
if (!err) {
@@ -5535,8 +5474,8 @@ static int patch_stac922x(struct hda_codec *codec)
again:
if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
- "using BIOS defaults\n");
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
else
stac92xx_set_config_regs(codec,
stac922x_brd_tbl[spec->board_config]);
@@ -5549,7 +5488,10 @@ static int patch_stac922x(struct hda_codec *codec)
spec->num_pwrs = 0;
spec->init = stac922x_core_init;
- spec->mixer = stac922x_mixer;
+
+ spec->num_caps = STAC922X_NUM_CAPS;
+ spec->capvols = stac922x_capvols;
+ spec->capsws = stac922x_capsws;
spec->multiout.dac_nids = spec->dac_nids;
@@ -5598,8 +5540,8 @@ static int patch_stac927x(struct hda_codec *codec)
stac927x_cfg_tbl);
again:
if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: Unknown model for"
- "STAC927x, using BIOS defaults\n");
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
else
stac92xx_set_config_regs(codec,
stac927x_brd_tbl[spec->board_config]);
@@ -5624,7 +5566,6 @@ static int patch_stac927x(struct hda_codec *codec)
spec->num_dmics = 0;
spec->init = d965_core_init;
- spec->mixer = stac927x_mixer;
break;
case STAC_DELL_BIOS:
switch (codec->subsystem_id) {
@@ -5645,11 +5586,17 @@ static int patch_stac927x(struct hda_codec *codec)
/* GPIO2 High = Enable EAPD */
spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x04;
spec->gpio_data = 0x04;
+ switch (codec->subsystem_id) {
+ case 0x1028022f:
+ /* correct EAPD to be GPIO0 */
+ spec->eapd_mask = spec->gpio_mask = 0x01;
+ spec->gpio_dir = spec->gpio_data = 0x01;
+ break;
+ };
spec->dmic_nids = stac927x_dmic_nids;
spec->num_dmics = STAC927X_NUM_DMICS;
spec->init = d965_core_init;
- spec->mixer = stac927x_mixer;
spec->dmux_nids = stac927x_dmux_nids;
spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
break;
@@ -5662,9 +5609,12 @@ static int patch_stac927x(struct hda_codec *codec)
spec->num_dmics = 0;
spec->init = stac927x_core_init;
- spec->mixer = stac927x_mixer;
}
+ spec->num_caps = STAC927X_NUM_CAPS;
+ spec->capvols = stac927x_capvols;
+ spec->capsws = stac927x_capsws;
+
spec->num_pwrs = 0;
spec->aloopback_ctl = stac927x_loopback;
spec->aloopback_mask = 0x40;
@@ -5726,7 +5676,8 @@ static int patch_stac9205(struct hda_codec *codec)
stac9205_cfg_tbl);
again:
if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
else
stac92xx_set_config_regs(codec,
stac9205_brd_tbl[spec->board_config]);
@@ -5745,9 +5696,12 @@ static int patch_stac9205(struct hda_codec *codec)
spec->num_pwrs = 0;
spec->init = stac9205_core_init;
- spec->mixer = stac9205_mixer;
spec->aloopback_ctl = stac9205_loopback;
+ spec->num_caps = STAC9205_NUM_CAPS;
+ spec->capvols = stac9205_capvols;
+ spec->capsws = stac9205_capsws;
+
spec->aloopback_mask = 0x40;
spec->aloopback_shift = 0;
/* Turn on/off EAPD per HP plugging */
@@ -5822,12 +5776,6 @@ static struct hda_verb stac9872_core_init[] = {
{}
};
-static struct snd_kcontrol_new stac9872_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
- { } /* end */
-};
-
static hda_nid_t stac9872_pin_nids[] = {
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x11, 0x13, 0x14,
@@ -5841,6 +5789,11 @@ static hda_nid_t stac9872_mux_nids[] = {
0x15
};
+static unsigned long stac9872_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
+};
+#define stac9872_capsws stac9872_capvols
+
static unsigned int stac9872_vaio_pin_configs[9] = {
0x03211020, 0x411111f0, 0x411111f0, 0x03a15030,
0x411111f0, 0x90170110, 0x411111f0, 0x411111f0,
@@ -5878,8 +5831,8 @@ static int patch_stac9872(struct hda_codec *codec)
stac9872_models,
stac9872_cfg_tbl);
if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9872, "
- "using BIOS defaults\n");
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
else
stac92xx_set_config_regs(codec,
stac9872_brd_tbl[spec->board_config]);
@@ -5889,8 +5842,10 @@ static int patch_stac9872(struct hda_codec *codec)
spec->adc_nids = stac9872_adc_nids;
spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids);
spec->mux_nids = stac9872_mux_nids;
- spec->mixer = stac9872_mixer;
spec->init = stac9872_core_init;
+ spec->num_caps = 1;
+ spec->capvols = stac9872_capvols;
+ spec->capsws = stac9872_capsws;
err = stac92xx_parse_auto_config(codec, 0x10, 0x12);
if (err < 0) {
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 9008b4b013a..ee89db90c9b 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1339,8 +1339,7 @@ static int get_mux_nids(struct hda_codec *codec)
for (i = 0; i < spec->num_adc_nids; i++) {
nid = spec->adc_nids[i];
while (nid) {
- type = (get_wcaps(codec, nid) & AC_WCAP_TYPE)
- >> AC_WCAP_TYPE_SHIFT;
+ type = get_wcaps_type(get_wcaps(codec, nid));
if (type == AC_WID_PIN)
break;
n = snd_hda_get_connections(codec, nid, conn,
@@ -1395,6 +1394,7 @@ static int patch_vt1708(struct hda_codec *codec)
if (!spec->adc_nids && spec->input_mux) {
spec->adc_nids = vt1708_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
+ get_mux_nids(codec);
spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
spec->num_mixers++;
}
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index adc909ec125..9da2dae64c5 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -379,6 +379,15 @@ struct snd_ice1712 {
unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate);
void (*set_spdif_clock)(struct snd_ice1712 *ice);
+#ifdef CONFIG_PM
+ int (*pm_suspend)(struct snd_ice1712 *);
+ int (*pm_resume)(struct snd_ice1712 *);
+ int pm_suspend_enabled:1;
+ int pm_saved_is_spdif_master:1;
+ unsigned int pm_saved_spdif_ctrl;
+ unsigned char pm_saved_spdif_cfg;
+ unsigned int pm_saved_route;
+#endif
};
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index cc84a831eb2..af6e0014862 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -560,6 +560,7 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
spin_lock(&ice->reg_lock);
old = inb(ICEMT1724(ice, DMA_CONTROL));
if (cmd == SNDRV_PCM_TRIGGER_START)
@@ -570,6 +571,10 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
spin_unlock(&ice->reg_lock);
break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /* apps will have to restart stream */
+ break;
+
default:
return -EINVAL;
}
@@ -2262,7 +2267,7 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
-static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice)
+static void snd_vt1724_chip_reset(struct snd_ice1712 *ice)
{
outb(VT1724_RESET , ICEREG1724(ice, CONTROL));
inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
@@ -2272,7 +2277,7 @@ static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice)
msleep(10);
}
-static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice)
+static int snd_vt1724_chip_init(struct snd_ice1712 *ice)
{
outb(ice->eeprom.data[ICE_EEP2_SYSCONF], ICEREG1724(ice, SYS_CFG));
outb(ice->eeprom.data[ICE_EEP2_ACLINK], ICEREG1724(ice, AC97_CFG));
@@ -2287,6 +2292,14 @@ static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice)
outb(0, ICEREG1724(ice, POWERDOWN));
+ /* MPU_RX and TX irq masks are cleared later dynamically */
+ outb(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX , ICEREG1724(ice, IRQMASK));
+
+ /* don't handle FIFO overrun/underruns (just yet),
+ * since they cause machine lockups
+ */
+ outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK));
+
return 0;
}
@@ -2431,6 +2444,8 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
snd_vt1724_proc_init(ice);
synchronize_irq(pci->irq);
+ card->private_data = ice;
+
err = pci_request_regions(pci, "ICE1724");
if (err < 0) {
kfree(ice);
@@ -2459,14 +2474,6 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
return -EIO;
}
- /* MPU_RX and TX irq masks are cleared later dynamically */
- outb(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX , ICEREG1724(ice, IRQMASK));
-
- /* don't handle FIFO overrun/underruns (just yet),
- * since they cause machine lockups
- */
- outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK));
-
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
if (err < 0) {
snd_vt1724_free(ice);
@@ -2650,11 +2657,96 @@ static void __devexit snd_vt1724_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
+#ifdef CONFIG_PM
+static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_ice1712 *ice = card->private_data;
+
+ if (!ice->pm_suspend_enabled)
+ return 0;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+ snd_pcm_suspend_all(ice->pcm);
+ snd_pcm_suspend_all(ice->pcm_pro);
+ snd_pcm_suspend_all(ice->pcm_ds);
+ snd_ac97_suspend(ice->ac97);
+
+ spin_lock_irq(&ice->reg_lock);
+ ice->pm_saved_is_spdif_master = ice->is_spdif_master(ice);
+ ice->pm_saved_spdif_ctrl = inw(ICEMT1724(ice, SPDIF_CTRL));
+ ice->pm_saved_spdif_cfg = inb(ICEREG1724(ice, SPDIF_CFG));
+ ice->pm_saved_route = inl(ICEMT1724(ice, ROUTE_PLAYBACK));
+ spin_unlock_irq(&ice->reg_lock);
+
+ if (ice->pm_suspend)
+ ice->pm_suspend(ice);
+
+ pci_disable_device(pci);
+ pci_save_state(pci);
+ pci_set_power_state(pci, pci_choose_state(pci, state));
+ return 0;
+}
+
+static int snd_vt1724_resume(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_ice1712 *ice = card->private_data;
+
+ if (!ice->pm_suspend_enabled)
+ return 0;
+
+ pci_set_power_state(pci, PCI_D0);
+ pci_restore_state(pci);
+
+ if (pci_enable_device(pci) < 0) {
+ snd_card_disconnect(card);
+ return -EIO;
+ }
+
+ pci_set_master(pci);
+
+ snd_vt1724_chip_reset(ice);
+
+ if (snd_vt1724_chip_init(ice) < 0) {
+ snd_card_disconnect(card);
+ return -EIO;
+ }
+
+ if (ice->pm_resume)
+ ice->pm_resume(ice);
+
+ if (ice->pm_saved_is_spdif_master) {
+ /* switching to external clock via SPDIF */
+ ice->set_spdif_clock(ice);
+ } else {
+ /* internal on-card clock */
+ snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
+ }
+
+ update_spdif_bits(ice, ice->pm_saved_spdif_ctrl);
+
+ outb(ice->pm_saved_spdif_cfg, ICEREG1724(ice, SPDIF_CFG));
+ outl(ice->pm_saved_route, ICEMT1724(ice, ROUTE_PLAYBACK));
+
+ if (ice->ac97)
+ snd_ac97_resume(ice->ac97);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+#endif
+
static struct pci_driver driver = {
.name = "ICE1724",
.id_table = snd_vt1724_ids,
.probe = snd_vt1724_probe,
.remove = __devexit_p(snd_vt1724_remove),
+#ifdef CONFIG_PM
+ .suspend = snd_vt1724_suspend,
+ .resume = snd_vt1724_resume,
+#endif
};
static int __init alsa_card_ice1724_init(void)
diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c
index 043a93879bd..c75515f5be6 100644
--- a/sound/pci/ice1712/prodigy_hifi.c
+++ b/sound/pci/ice1712/prodigy_hifi.c
@@ -1077,7 +1077,7 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
/*
* initialize the chip
*/
-static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
+static void ak4396_init(struct snd_ice1712 *ice)
{
static unsigned short ak4396_inits[] = {
AK4396_CTRL1, 0x87, /* I2S Normal Mode, 24 bit */
@@ -1087,9 +1087,37 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
AK4396_RCH_ATT, 0x00,
};
- struct prodigy_hifi_spec *spec;
unsigned int i;
+ /* initialize ak4396 codec */
+ /* reset codec */
+ ak4396_write(ice, AK4396_CTRL1, 0x86);
+ msleep(100);
+ ak4396_write(ice, AK4396_CTRL1, 0x87);
+
+ for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2)
+ ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]);
+}
+
+#ifdef CONFIG_PM
+static int __devinit prodigy_hd2_resume(struct snd_ice1712 *ice)
+{
+ /* initialize ak4396 codec and restore previous mixer volumes */
+ struct prodigy_hifi_spec *spec = ice->spec;
+ int i;
+ mutex_lock(&ice->gpio_mutex);
+ ak4396_init(ice);
+ for (i = 0; i < 2; i++)
+ ak4396_write(ice, AK4396_LCH_ATT + i, spec->vol[i] & 0xff);
+ mutex_unlock(&ice->gpio_mutex);
+ return 0;
+}
+#endif
+
+static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
+{
+ struct prodigy_hifi_spec *spec;
+
ice->vt1720 = 0;
ice->vt1724 = 1;
@@ -1112,14 +1140,12 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
return -ENOMEM;
ice->spec = spec;
- /* initialize ak4396 codec */
- /* reset codec */
- ak4396_write(ice, AK4396_CTRL1, 0x86);
- msleep(100);
- ak4396_write(ice, AK4396_CTRL1, 0x87);
-
- for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2)
- ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]);
+#ifdef CONFIG_PM
+ ice->pm_resume = &prodigy_hd2_resume;
+ ice->pm_suspend_enabled = 1;
+#endif
+
+ ak4396_init(ice);
return 0;
}
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
index c1eb923f2ac..09b2b2a36df 100644
--- a/sound/pci/oxygen/oxygen_io.c
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -215,17 +215,8 @@ EXPORT_SYMBOL(oxygen_write_spi);
void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data)
{
- unsigned long timeout;
-
/* should not need more than about 300 us */
- timeout = jiffies + msecs_to_jiffies(1);
- do {
- if (!(oxygen_read16(chip, OXYGEN_2WIRE_BUS_STATUS)
- & OXYGEN_2WIRE_BUSY))
- break;
- udelay(1);
- cond_resched();
- } while (time_after_eq(timeout, jiffies));
+ msleep(1);
oxygen_write8(chip, OXYGEN_2WIRE_MAP, map);
oxygen_write8(chip, OXYGEN_2WIRE_DATA, data);
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 312251d3969..9a8936e2074 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -260,6 +260,9 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
* chip didn't if the first EEPROM word was overwritten.
*/
subdevice = oxygen_read_eeprom(chip, 2);
+ /* use default ID if EEPROM is missing */
+ if (subdevice == 0xffff)
+ subdevice = 0x8788;
/*
* We use only the subsystem device ID for searching because it is
* unique even without the subsystem vendor ID, which may have been
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index 3b5ca70c9d4..ef2345d82b8 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -469,9 +469,11 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
oxygen_rate(hw_params) |
chip->model.dac_i2s_format |
+ oxygen_i2s_mclk(hw_params) |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
+ OXYGEN_I2S_MCLK_MASK |
OXYGEN_I2S_BITS_MASK);
oxygen_update_dac_routing(chip);
oxygen_update_spdif_source(chip);
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 3da5c029f93..7bb827c7d80 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -3294,15 +3294,33 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
char *clock_source;
int x;
- if (hdsp_check_for_iobox (hdsp)) {
- snd_iprintf(buffer, "No I/O box connected.\nPlease connect one and upload firmware.\n");
+ status = hdsp_read(hdsp, HDSP_statusRegister);
+ status2 = hdsp_read(hdsp, HDSP_status2Register);
+
+ snd_iprintf(buffer, "%s (Card #%d)\n", hdsp->card_name,
+ hdsp->card->number + 1);
+ snd_iprintf(buffer, "Buffers: capture %p playback %p\n",
+ hdsp->capture_buffer, hdsp->playback_buffer);
+ snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
+ hdsp->irq, hdsp->port, (unsigned long)hdsp->iobase);
+ snd_iprintf(buffer, "Control register: 0x%x\n", hdsp->control_register);
+ snd_iprintf(buffer, "Control2 register: 0x%x\n",
+ hdsp->control2_register);
+ snd_iprintf(buffer, "Status register: 0x%x\n", status);
+ snd_iprintf(buffer, "Status2 register: 0x%x\n", status2);
+
+ if (hdsp_check_for_iobox(hdsp)) {
+ snd_iprintf(buffer, "No I/O box connected.\n"
+ "Please connect one and upload firmware.\n");
return;
- }
+ }
if (hdsp_check_for_firmware(hdsp, 0)) {
if (hdsp->state & HDSP_FirmwareCached) {
if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) {
- snd_iprintf(buffer, "Firmware loading from cache failed, please upload manually.\n");
+ snd_iprintf(buffer, "Firmware loading from "
+ "cache failed, "
+ "please upload manually.\n");
return;
}
} else {
@@ -3319,18 +3337,6 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
}
}
- status = hdsp_read(hdsp, HDSP_statusRegister);
- status2 = hdsp_read(hdsp, HDSP_status2Register);
-
- snd_iprintf(buffer, "%s (Card #%d)\n", hdsp->card_name, hdsp->card->number + 1);
- snd_iprintf(buffer, "Buffers: capture %p playback %p\n",
- hdsp->capture_buffer, hdsp->playback_buffer);
- snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
- hdsp->irq, hdsp->port, (unsigned long)hdsp->iobase);
- snd_iprintf(buffer, "Control register: 0x%x\n", hdsp->control_register);
- snd_iprintf(buffer, "Control2 register: 0x%x\n", hdsp->control2_register);
- snd_iprintf(buffer, "Status register: 0x%x\n", status);
- snd_iprintf(buffer, "Status2 register: 0x%x\n", status2);
snd_iprintf(buffer, "FIFO status: %d\n", hdsp_read(hdsp, HDSP_fifoStatus) & 0xff);
snd_iprintf(buffer, "MIDI1 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut0));
snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0));
@@ -3351,7 +3357,6 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
snd_iprintf(buffer, "\n");
-
switch (hdsp_clock_source(hdsp)) {
case HDSP_CLOCK_SOURCE_AUTOSYNC:
clock_source = "AutoSync";
diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
index 6416d3f0c7b..a69e774d0b1 100644
--- a/sound/pci/vx222/vx222_ops.c
+++ b/sound/pci/vx222/vx222_ops.c
@@ -885,10 +885,10 @@ static int vx_input_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
struct vx_core *_chip = snd_kcontrol_chip(kcontrol);
struct snd_vx222 *chip = (struct snd_vx222 *)_chip;
if (ucontrol->value.integer.value[0] < 0 ||
- ucontrol->value.integer.value[0] < MIC_LEVEL_MAX)
+ ucontrol->value.integer.value[0] > MIC_LEVEL_MAX)
return -EINVAL;
if (ucontrol->value.integer.value[1] < 0 ||
- ucontrol->value.integer.value[1] < MIC_LEVEL_MAX)
+ ucontrol->value.integer.value[1] > MIC_LEVEL_MAX)
return -EINVAL;
mutex_lock(&_chip->mixer_mutex);
if (chip->input_level[0] != ucontrol->value.integer.value[0] ||
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index d3e786a9a0a..b1749bc6797 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -29,6 +29,7 @@ source "sound/soc/au1x/Kconfig"
source "sound/soc/blackfin/Kconfig"
source "sound/soc/davinci/Kconfig"
source "sound/soc/fsl/Kconfig"
+source "sound/soc/imx/Kconfig"
source "sound/soc/omap/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/s3c24xx/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 6f1e28de23c..0c5eac01bf2 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,4 +1,4 @@
-snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o
+snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
@@ -7,6 +7,7 @@ obj-$(CONFIG_SND_SOC) += au1x/
obj-$(CONFIG_SND_SOC) += blackfin/
obj-$(CONFIG_SND_SOC) += davinci/
obj-$(CONFIG_SND_SOC) += fsl/
+obj-$(CONFIG_SND_SOC) += imx/
obj-$(CONFIG_SND_SOC) += omap/
obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += s3c24xx/
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 173a239a541..130b12118d4 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -56,30 +56,14 @@
#define MCLK_RATE 12000000
-static struct clk *mclk;
-
-static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
- struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
- MCLK_RATE, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- clk_disable(mclk);
- return ret;
- }
-
- return 0;
-}
-
-static void at91sam9g20ek_shutdown(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+/*
+ * As shipped the board does not have inputs. However, it is relatively
+ * straightforward to modify the board to hook them up so support is left
+ * in the driver.
+ */
+#undef ENABLE_MIC_INPUT
- dev_dbg(rtd->socdev->dev, "shutdown");
-}
+static struct clk *mclk;
static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
@@ -87,102 +71,17 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- struct atmel_ssc_info *ssc_p = cpu_dai->private_data;
- struct ssc_device *ssc = ssc_p->ssc;
int ret;
- unsigned int rate;
- int cmr_div, period;
-
- if (ssc == NULL) {
- printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n");
- return -EINVAL;
- }
-
/* set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0)
- return ret;
-
- /*
- * The SSC clock dividers depend on the sample rate. The CMR.DIV
- * field divides the system master clock MCK to drive the SSC TK
- * signal which provides the codec BCLK. The TCMR.PERIOD and
- * RCMR.PERIOD fields further divide the BCLK signal to drive
- * the SSC TF and RF signals which provide the codec DACLRC and
- * ADCLRC clocks.
- *
- * The dividers were determined through trial and error, where a
- * CMR.DIV value is chosen such that the resulting BCLK value is
- * divisible, or almost divisible, by (2 * sample rate), and then
- * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
- */
- rate = params_rate(params);
-
- switch (rate) {
- case 8000:
- cmr_div = 55; /* BCLK = 133MHz/(2*55) = 1.209MHz */
- period = 74; /* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */
- break;
- case 11025:
- cmr_div = 67; /* BCLK = 133MHz/(2*60) = 1.108MHz */
- period = 45; /* LRC = BCLK/(2*(49+1)) = 11083,3Hz */
- break;
- case 16000:
- cmr_div = 63; /* BCLK = 133MHz/(2*63) = 1.055MHz */
- period = 32; /* LRC = BCLK/(2*(32+1)) = 15993,2Hz */
- break;
- case 22050:
- cmr_div = 52; /* BCLK = 133MHz/(2*52) = 1.278MHz */
- period = 28; /* LRC = BCLK/(2*(28+1)) = 22049Hz */
- break;
- case 32000:
- cmr_div = 66; /* BCLK = 133MHz/(2*66) = 1.007MHz */
- period = 15; /* LRC = BCLK/(2*(15+1)) = 31486,742Hz */
- break;
- case 44100:
- cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */
- period = 25; /* LRC = BCLK/(2*(25+1)) = 44098Hz */
- break;
- case 48000:
- cmr_div = 33; /* BCLK = 133MHz/(2*33) = 2.015MHz */
- period = 20; /* LRC = BCLK/(2*(20+1)) = 47979,79Hz */
- break;
- case 88200:
- cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */
- period = 12; /* LRC = BCLK/(2*(12+1)) = 88196Hz */
- break;
- case 96000:
- cmr_div = 23; /* BCLK = 133MHz/(2*23) = 2.891MHz */
- period = 14; /* LRC = BCLK/(2*(14+1)) = 96376Hz */
- break;
- default:
- printk(KERN_WARNING "unsupported rate %d"
- " on at91sam9g20ek board\n", rate);
- return -EINVAL;
- }
-
- /* set the MCK divider for BCLK */
- ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div);
- if (ret < 0)
- return ret;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- /* set the BCLK divider for DACLRC */
- ret = snd_soc_dai_set_clkdiv(cpu_dai,
- ATMEL_SSC_TCMR_PERIOD, period);
- } else {
- /* set the BCLK divider for ADCLRC */
- ret = snd_soc_dai_set_clkdiv(cpu_dai,
- ATMEL_SSC_RCMR_PERIOD, period);
- }
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
@@ -190,9 +89,7 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
}
static struct snd_soc_ops at91sam9g20ek_ops = {
- .startup = at91sam9g20ek_startup,
.hw_params = at91sam9g20ek_hw_params,
- .shutdown = at91sam9g20ek_shutdown,
};
static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
@@ -241,10 +138,20 @@ static const struct snd_soc_dapm_route intercon[] = {
*/
static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
{
+ struct snd_soc_dai *codec_dai = &codec->dai[0];
+ int ret;
+
printk(KERN_DEBUG
"at91sam9g20ek_wm8731 "
": at91sam9g20ek_wm8731_init() called\n");
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
+ MCLK_RATE, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret);
+ return ret;
+ }
+
/* Add specific widgets */
snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets,
ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
@@ -255,8 +162,13 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
snd_soc_dapm_nc_pin(codec, "RLINEIN");
snd_soc_dapm_nc_pin(codec, "LLINEIN");
- /* always connected */
+#ifdef ENABLE_MIC_INPUT
snd_soc_dapm_enable_pin(codec, "Int Mic");
+#else
+ snd_soc_dapm_nc_pin(codec, "Int Mic");
+#endif
+
+ /* always connected */
snd_soc_dapm_enable_pin(codec, "Ext Spk");
snd_soc_dapm_sync(codec);
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index 479d7bdf186..a521aa90dde 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -1,8 +1,8 @@
/*
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
- * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
- * Manuel Lauss <mano@roarinelk.homelinux.net>
+ * (c) 2007-2009 MSC Vertriebsges.m.b.H.,
+ * Manuel Lauss <manuel.lauss@gmail.com>
*
* 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
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
+#include <linux/mutex.h>
#include <linux/suspend.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -29,6 +30,9 @@
#include "psc.h"
+/* how often to retry failed codec register reads/writes */
+#define AC97_RW_RETRIES 5
+
#define AC97_DIR \
(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
@@ -45,6 +49,9 @@
#define AC97PCR_CLRFIFO(stype) \
((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
+#define AC97STAT_BUSY(stype) \
+ ((stype) == PCM_TX ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)
+
/* instance data. There can be only one, MacLeod!!!! */
static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
@@ -54,24 +61,33 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
- unsigned short data, tmo;
+ unsigned short data, retry, tmo;
- au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), AC97_CDC(pscdata));
+ au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
- tmo = 1000;
- while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
- udelay(2);
+ retry = AC97_RW_RETRIES;
+ do {
+ mutex_lock(&pscdata->lock);
+
+ au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg),
+ AC97_CDC(pscdata));
+ au_sync();
+
+ tmo = 2000;
+ while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
+ && --tmo)
+ udelay(2);
- if (!tmo)
- data = 0xffff;
- else
data = au_readl(AC97_CDC(pscdata)) & 0xffff;
- au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
- au_sync();
+ au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+ au_sync();
+
+ mutex_unlock(&pscdata->lock);
+ } while (--retry && !tmo);
- return data;
+ return retry ? data : 0xffff;
}
/* AC97 controller writes to codec register */
@@ -80,16 +96,29 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
- unsigned int tmo;
+ unsigned int tmo, retry;
- au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), AC97_CDC(pscdata));
+ au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
- tmo = 1000;
- while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
+
+ retry = AC97_RW_RETRIES;
+ do {
+ mutex_lock(&pscdata->lock);
+
+ au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff),
+ AC97_CDC(pscdata));
au_sync();
- au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
- au_sync();
+ tmo = 2000;
+ while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
+ && --tmo)
+ udelay(2);
+
+ au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+ au_sync();
+
+ mutex_unlock(&pscdata->lock);
+ } while (--retry && !tmo);
}
/* AC97 controller asserts a warm reset */
@@ -129,9 +158,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
au_sync();
/* wait for PSC to indicate it's ready */
- i = 100000;
+ i = 1000;
while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
- au_sync();
+ msleep(1);
if (i == 0) {
printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
@@ -143,9 +172,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
au_sync();
/* wait for AC97 core to become ready */
- i = 100000;
+ i = 1000;
while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
- au_sync();
+ msleep(1);
if (i == 0)
printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
}
@@ -165,12 +194,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
- unsigned long r, stat;
+ unsigned long r, ro, stat;
int chans, stype = SUBSTREAM_TYPE(substream);
chans = params_channels(params);
- r = au_readl(AC97_CFG(pscdata));
+ r = ro = au_readl(AC97_CFG(pscdata));
stat = au_readl(AC97_STAT(pscdata));
/* already active? */
@@ -180,9 +209,6 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
(pscdata->rate != params_rate(params)))
return -EINVAL;
} else {
- /* disable AC97 device controller first */
- au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
- au_sync();
/* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
r &= ~PSC_AC97CFG_LEN_MASK;
@@ -199,14 +225,40 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
r |= PSC_AC97CFG_RXSLOT_ENA(4);
}
- /* finally enable the AC97 controller again */
+ /* do we need to poke the hardware? */
+ if (!(r ^ ro))
+ goto out;
+
+ /* ac97 engine is about to be disabled */
+ mutex_lock(&pscdata->lock);
+
+ /* disable AC97 device controller first... */
+ au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+ au_sync();
+
+ /* ...wait for it... */
+ while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)
+ asm volatile ("nop");
+
+ /* ...write config... */
+ au_writel(r, AC97_CFG(pscdata));
+ au_sync();
+
+ /* ...enable the AC97 controller again... */
au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
au_sync();
+ /* ...and wait for ready bit */
+ while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR))
+ asm volatile ("nop");
+
+ mutex_unlock(&pscdata->lock);
+
pscdata->cfg = r;
pscdata->rate = params_rate(params);
}
+out:
return 0;
}
@@ -222,6 +274,8 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
+ au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
+ au_sync();
au_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
au_sync();
break;
@@ -229,6 +283,13 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_SUSPEND:
au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
au_sync();
+
+ while (au_readl(AC97_STAT(pscdata)) & AC97STAT_BUSY(stype))
+ asm volatile ("nop");
+
+ au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
+ au_sync();
+
break;
default:
ret = -EINVAL;
@@ -251,6 +312,8 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev,
if (!au1xpsc_ac97_workdata)
return -ENOMEM;
+ mutex_init(&au1xpsc_ac97_workdata->lock);
+
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
ret = -ENODEV;
@@ -269,9 +332,9 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev,
goto out1;
/* configuration: max dma trigger threshold, enable ac97 */
- au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
- PSC_AC97CFG_TT_FIFO8 |
- PSC_AC97CFG_DE_ENABLE;
+ au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
+ PSC_AC97CFG_TT_FIFO8 |
+ PSC_AC97CFG_DE_ENABLE;
/* preserve PSC clock source set up by platform (dev.platform_data
* is already occupied by soc layer)
@@ -386,4 +449,4 @@ module_exit(au1xpsc_ac97_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
-MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
+MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>");
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h
index 8fdb1a04a07..3f474e8ed4f 100644
--- a/sound/soc/au1x/psc.h
+++ b/sound/soc/au1x/psc.h
@@ -29,6 +29,7 @@ struct au1xpsc_audio_data {
unsigned long pm[2];
struct resource *ioarea;
+ struct mutex lock;
};
#define PCM_TX 0
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index 811596f4c09..ac927ffdc96 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -7,6 +7,15 @@ config SND_BF5XX_I2S
mode (supports single stereo In/Out).
You will also need to select the audio interfaces to support below.
+config SND_BF5XX_TDM
+ tristate "SoC I2S(TDM mode) Audio for the ADI BF5xx chip"
+ depends on (BLACKFIN && SND_SOC)
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the Blackfin SPORT (synchronous serial ports) interface in TDM
+ mode.
+ You will also need to select the audio interfaces to support below.
+
config SND_BF5XX_SOC_SSM2602
tristate "SoC SSM2602 Audio support for BF52x ezkit"
depends on SND_BF5XX_I2S
@@ -69,12 +78,24 @@ config SND_BF5XX_SOC_I2S
tristate
select SND_BF5XX_SOC_SPORT
+config SND_BF5XX_SOC_TDM
+ tristate
+ select SND_BF5XX_SOC_SPORT
+
config SND_BF5XX_SOC_AC97
tristate
select AC97_BUS
select SND_SOC_AC97_BUS
select SND_BF5XX_SOC_SPORT
+config SND_BF5XX_SOC_AD1836
+ tristate "SoC AD1836 Audio support for BF5xx"
+ depends on SND_BF5XX_TDM
+ select SND_BF5XX_SOC_TDM
+ select SND_SOC_AD1836
+ help
+ Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
+
config SND_BF5XX_SOC_AD1980
tristate "SoC AD1980/1 Audio support for BF5xx"
depends on SND_BF5XX_AC97
@@ -83,9 +104,17 @@ config SND_BF5XX_SOC_AD1980
help
Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
+config SND_BF5XX_SOC_AD1938
+ tristate "SoC AD1938 Audio support for Blackfin"
+ depends on SND_BF5XX_TDM
+ select SND_BF5XX_SOC_TDM
+ select SND_SOC_AD1938
+ help
+ Say Y if you want to add support for AD1938 codec on Blackfin.
+
config SND_BF5XX_SPORT_NUM
int "Set a SPORT for Sound chip"
- depends on (SND_BF5XX_I2S || SND_BF5XX_AC97)
+ depends on (SND_BF5XX_I2S || SND_BF5XX_AC97 || SND_BF5XX_TDM)
range 0 3 if BF54x
range 0 1 if !BF54x
default 0
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
index 97bb37a6359..87e30423912 100644
--- a/sound/soc/blackfin/Makefile
+++ b/sound/soc/blackfin/Makefile
@@ -1,21 +1,29 @@
# Blackfin Platform Support
snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o
snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o
+snd-bf5xx-tdm-objs := bf5xx-tdm-pcm.o
snd-soc-bf5xx-sport-objs := bf5xx-sport.o
snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o
snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o
+snd-soc-bf5xx-tdm-objs := bf5xx-tdm.o
obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o
obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o
+obj-$(CONFIG_SND_BF5XX_TDM) += snd-bf5xx-tdm.o
obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o
obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o
obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o
+obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o
# Blackfin Machine Support
+snd-ad1836-objs := bf5xx-ad1836.o
snd-ad1980-objs := bf5xx-ad1980.o
snd-ssm2602-objs := bf5xx-ssm2602.o
snd-ad73311-objs := bf5xx-ad73311.o
+snd-ad1938-objs := bf5xx-ad1938.o
+obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.o
obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
+obj-$(CONFIG_SND_BF5XX_SOC_AD1938) += snd-ad1938.o
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index b1ed423fabd..2758b9017a7 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -277,28 +277,24 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
if (!dai->active)
return 0;
- ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
+ ret = sport_set_multichannel(sport, 16, 0x1F, 1);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
+ ret = sport_config_rx(sport, IRFS, 0xF, 0, (16*16-1));
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
+ ret = sport_config_tx(sport, ITFS, 0xF, 0, (16*16-1));
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- if (dai->capture.active)
- sport_rx_start(sport);
- if (dai->playback.active)
- sport_tx_start(sport);
return 0;
}
diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c
new file mode 100644
index 00000000000..cd361e304b0
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ad1836.c
@@ -0,0 +1,128 @@
+/*
+ * File: sound/soc/blackfin/bf5xx-ad1836.c
+ * Author: Barry Song <Barry.Song@analog.com>
+ *
+ * Created: Aug 4 2009
+ * Description: Board driver for ad1836 sound chip
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+
+#include <asm/blackfin.h>
+#include <asm/cacheflush.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+
+#include "../codecs/ad1836.h"
+#include "bf5xx-sport.h"
+
+#include "bf5xx-tdm-pcm.h"
+#include "bf5xx-tdm.h"
+
+static struct snd_soc_card bf5xx_ad1836;
+
+static int bf5xx_ad1836_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ cpu_dai->private_data = sport_handle;
+ return 0;
+}
+
+static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ int ret = 0;
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops bf5xx_ad1836_ops = {
+ .startup = bf5xx_ad1836_startup,
+ .hw_params = bf5xx_ad1836_hw_params,
+};
+
+static struct snd_soc_dai_link bf5xx_ad1836_dai = {
+ .name = "ad1836",
+ .stream_name = "AD1836",
+ .cpu_dai = &bf5xx_tdm_dai,
+ .codec_dai = &ad1836_dai,
+ .ops = &bf5xx_ad1836_ops,
+};
+
+static struct snd_soc_card bf5xx_ad1836 = {
+ .name = "bf5xx_ad1836",
+ .platform = &bf5xx_tdm_soc_platform,
+ .dai_link = &bf5xx_ad1836_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_ad1836_snd_devdata = {
+ .card = &bf5xx_ad1836,
+ .codec_dev = &soc_codec_dev_ad1836,
+};
+
+static struct platform_device *bfxx_ad1836_snd_device;
+
+static int __init bf5xx_ad1836_init(void)
+{
+ int ret;
+
+ bfxx_ad1836_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!bfxx_ad1836_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(bfxx_ad1836_snd_device, &bf5xx_ad1836_snd_devdata);
+ bf5xx_ad1836_snd_devdata.dev = &bfxx_ad1836_snd_device->dev;
+ ret = platform_device_add(bfxx_ad1836_snd_device);
+
+ if (ret)
+ platform_device_put(bfxx_ad1836_snd_device);
+
+ return ret;
+}
+
+static void __exit bf5xx_ad1836_exit(void)
+{
+ platform_device_unregister(bfxx_ad1836_snd_device);
+}
+
+module_init(bf5xx_ad1836_init);
+module_exit(bf5xx_ad1836_exit);
+
+/* Module information */
+MODULE_AUTHOR("Barry Song");
+MODULE_DESCRIPTION("ALSA SoC AD1836 board driver");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/blackfin/bf5xx-ad1938.c b/sound/soc/blackfin/bf5xx-ad1938.c
new file mode 100644
index 00000000000..08269e91810
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ad1938.c
@@ -0,0 +1,142 @@
+/*
+ * File: sound/soc/blackfin/bf5xx-ad1938.c
+ * Author: Barry Song <Barry.Song@analog.com>
+ *
+ * Created: Thur June 4 2009
+ * Description: Board driver for ad1938 sound chip
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+
+#include <asm/blackfin.h>
+#include <asm/cacheflush.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+
+#include "../codecs/ad1938.h"
+#include "bf5xx-sport.h"
+
+#include "bf5xx-tdm-pcm.h"
+#include "bf5xx-tdm.h"
+
+static struct snd_soc_card bf5xx_ad1938;
+
+static int bf5xx_ad1938_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ cpu_dai->private_data = sport_handle;
+ return 0;
+}
+
+static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ int ret = 0;
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ /* set codec DAI slots, 8 channels, all channels are enabled */
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 8);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops bf5xx_ad1938_ops = {
+ .startup = bf5xx_ad1938_startup,
+ .hw_params = bf5xx_ad1938_hw_params,
+};
+
+static struct snd_soc_dai_link bf5xx_ad1938_dai = {
+ .name = "ad1938",
+ .stream_name = "AD1938",
+ .cpu_dai = &bf5xx_tdm_dai,
+ .codec_dai = &ad1938_dai,
+ .ops = &bf5xx_ad1938_ops,
+};
+
+static struct snd_soc_card bf5xx_ad1938 = {
+ .name = "bf5xx_ad1938",
+ .platform = &bf5xx_tdm_soc_platform,
+ .dai_link = &bf5xx_ad1938_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_ad1938_snd_devdata = {
+ .card = &bf5xx_ad1938,
+ .codec_dev = &soc_codec_dev_ad1938,
+};
+
+static struct platform_device *bfxx_ad1938_snd_device;
+
+static int __init bf5xx_ad1938_init(void)
+{
+ int ret;
+
+ bfxx_ad1938_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!bfxx_ad1938_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(bfxx_ad1938_snd_device, &bf5xx_ad1938_snd_devdata);
+ bf5xx_ad1938_snd_devdata.dev = &bfxx_ad1938_snd_device->dev;
+ ret = platform_device_add(bfxx_ad1938_snd_device);
+
+ if (ret)
+ platform_device_put(bfxx_ad1938_snd_device);
+
+ return ret;
+}
+
+static void __exit bf5xx_ad1938_exit(void)
+{
+ platform_device_unregister(bfxx_ad1938_snd_device);
+}
+
+module_init(bf5xx_ad1938_init);
+module_exit(bf5xx_ad1938_exit);
+
+/* Module information */
+MODULE_AUTHOR("Barry Song");
+MODULE_DESCRIPTION("ALSA SoC AD1938 board driver");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c
index edfbdc024e6..9825b71d0e2 100644
--- a/sound/soc/blackfin/bf5xx-ad73311.c
+++ b/sound/soc/blackfin/bf5xx-ad73311.c
@@ -203,23 +203,23 @@ static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
.codec_dev = &soc_codec_dev_ad73311,
};
-static struct platform_device *bf52x_ad73311_snd_device;
+static struct platform_device *bf5xx_ad73311_snd_device;
static int __init bf5xx_ad73311_init(void)
{
int ret;
pr_debug("%s enter\n", __func__);
- bf52x_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
- if (!bf52x_ad73311_snd_device)
+ bf5xx_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!bf5xx_ad73311_snd_device)
return -ENOMEM;
- platform_set_drvdata(bf52x_ad73311_snd_device, &bf5xx_ad73311_snd_devdata);
- bf5xx_ad73311_snd_devdata.dev = &bf52x_ad73311_snd_device->dev;
- ret = platform_device_add(bf52x_ad73311_snd_device);
+ platform_set_drvdata(bf5xx_ad73311_snd_device, &bf5xx_ad73311_snd_devdata);
+ bf5xx_ad73311_snd_devdata.dev = &bf5xx_ad73311_snd_device->dev;
+ ret = platform_device_add(bf5xx_ad73311_snd_device);
if (ret)
- platform_device_put(bf52x_ad73311_snd_device);
+ platform_device_put(bf5xx_ad73311_snd_device);
return ret;
}
@@ -227,7 +227,7 @@ static int __init bf5xx_ad73311_init(void)
static void __exit bf5xx_ad73311_exit(void)
{
pr_debug("%s enter\n", __func__);
- platform_device_unregister(bf52x_ad73311_snd_device);
+ platform_device_unregister(bf5xx_ad73311_snd_device);
}
module_init(bf5xx_ad73311_init);
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
index af06904bab0..876abade27e 100644
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -259,22 +259,18 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
if (!dai->active)
return 0;
- ret = sport_config_rx(sport_handle, RFSR | RCKFE, RSFSE|0x1f, 0, 0);
+ ret = sport_config_rx(sport, RFSR | RCKFE, RSFSE|0x1f, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- ret = sport_config_tx(sport_handle, TFSR | TCKFE, TSFSE|0x1f, 0, 0);
+ ret = sport_config_tx(sport, TFSR | TCKFE, TSFSE|0x1f, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- if (dai->capture.active)
- sport_rx_start(sport);
- if (dai->playback.active)
- sport_tx_start(sport);
return 0;
}
diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c
index bc0cdded711..3a00fa4dbe6 100644
--- a/sound/soc/blackfin/bf5xx-ssm2602.c
+++ b/sound/soc/blackfin/bf5xx-ssm2602.c
@@ -148,24 +148,24 @@ static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
.codec_data = &bf5xx_ssm2602_setup,
};
-static struct platform_device *bf52x_ssm2602_snd_device;
+static struct platform_device *bf5xx_ssm2602_snd_device;
static int __init bf5xx_ssm2602_init(void)
{
int ret;
pr_debug("%s enter\n", __func__);
- bf52x_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
- if (!bf52x_ssm2602_snd_device)
+ bf5xx_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!bf5xx_ssm2602_snd_device)
return -ENOMEM;
- platform_set_drvdata(bf52x_ssm2602_snd_device,
+ platform_set_drvdata(bf5xx_ssm2602_snd_device,
&bf5xx_ssm2602_snd_devdata);
- bf5xx_ssm2602_snd_devdata.dev = &bf52x_ssm2602_snd_device->dev;
- ret = platform_device_add(bf52x_ssm2602_snd_device);
+ bf5xx_ssm2602_snd_devdata.dev = &bf5xx_ssm2602_snd_device->dev;
+ ret = platform_device_add(bf5xx_ssm2602_snd_device);
if (ret)
- platform_device_put(bf52x_ssm2602_snd_device);
+ platform_device_put(bf5xx_ssm2602_snd_device);
return ret;
}
@@ -173,7 +173,7 @@ static int __init bf5xx_ssm2602_init(void)
static void __exit bf5xx_ssm2602_exit(void)
{
pr_debug("%s enter\n", __func__);
- platform_device_unregister(bf52x_ssm2602_snd_device);
+ platform_device_unregister(bf5xx_ssm2602_snd_device);
}
module_init(bf5xx_ssm2602_init);
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c
new file mode 100644
index 00000000000..ccb5e823bd1
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c
@@ -0,0 +1,330 @@
+/*
+ * File: sound/soc/blackfin/bf5xx-tdm-pcm.c
+ * Author: Barry Song <Barry.Song@analog.com>
+ *
+ * Created: Tue June 06 2009
+ * Description: DMA driver for tdm codec
+ *
+ * Modified:
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+
+#include "bf5xx-tdm-pcm.h"
+#include "bf5xx-tdm.h"
+#include "bf5xx-sport.h"
+
+#define PCM_BUFFER_MAX 0x10000
+#define FRAGMENT_SIZE_MIN (4*1024)
+#define FRAGMENTS_MIN 2
+#define FRAGMENTS_MAX 32
+
+static void bf5xx_dma_irq(void *data)
+{
+ struct snd_pcm_substream *pcm = data;
+ snd_pcm_period_elapsed(pcm);
+}
+
+static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .channels_min = 2,
+ .channels_max = 8,
+ .buffer_bytes_max = PCM_BUFFER_MAX,
+ .period_bytes_min = FRAGMENT_SIZE_MIN,
+ .period_bytes_max = PCM_BUFFER_MAX/2,
+ .periods_min = FRAGMENTS_MIN,
+ .periods_max = FRAGMENTS_MAX,
+};
+
+static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
+ snd_pcm_lib_malloc_pages(substream, size * 4);
+
+ return 0;
+}
+
+static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ snd_pcm_lib_free_pages(substream);
+
+ return 0;
+}
+
+static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+ int fragsize_bytes = frames_to_bytes(runtime, runtime->period_size);
+
+ fragsize_bytes /= runtime->channels;
+ /* inflate the fragsize to match the dma width of SPORT */
+ fragsize_bytes *= 8;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
+ sport_config_tx_dma(sport, runtime->dma_area,
+ runtime->periods, fragsize_bytes);
+ } else {
+ sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
+ sport_config_rx_dma(sport, runtime->dma_area,
+ runtime->periods, fragsize_bytes);
+ }
+
+ return 0;
+}
+
+static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sport_tx_start(sport);
+ else
+ sport_rx_start(sport);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sport_tx_stop(sport);
+ else
+ sport_rx_stop(sport);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+ unsigned int diff;
+ snd_pcm_uframes_t frames;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ diff = sport_curr_offset_tx(sport);
+ frames = diff / (8*4); /* 32 bytes per frame */
+ } else {
+ diff = sport_curr_offset_rx(sport);
+ frames = diff / (8*4);
+ }
+ return frames;
+}
+
+static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret = 0;
+
+ snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+ if (sport_handle != NULL)
+ runtime->private_data = sport_handle;
+ else {
+ pr_err("sport_handle is NULL\n");
+ ret = -ENODEV;
+ }
+out:
+ return ret;
+}
+
+static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
+ snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
+{
+ unsigned int *src;
+ unsigned int *dst;
+ int i;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ src = buf;
+ dst = (unsigned int *)substream->runtime->dma_area;
+
+ dst += pos * 8;
+ while (count--) {
+ for (i = 0; i < substream->runtime->channels; i++)
+ *(dst + i) = *src++;
+ dst += 8;
+ }
+ } else {
+ src = (unsigned int *)substream->runtime->dma_area;
+ dst = buf;
+
+ src += pos * 8;
+ while (count--) {
+ for (i = 0; i < substream->runtime->channels; i++)
+ *dst++ = *(src+i);
+ src += 8;
+ }
+ }
+
+ return 0;
+}
+
+static int bf5xx_pcm_silence(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
+{
+ unsigned char *buf = substream->runtime->dma_area;
+ buf += pos * 8 * 4;
+ memset(buf, '\0', count * 8 * 4);
+
+ return 0;
+}
+
+
+struct snd_pcm_ops bf5xx_pcm_tdm_ops = {
+ .open = bf5xx_pcm_open,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = bf5xx_pcm_hw_params,
+ .hw_free = bf5xx_pcm_hw_free,
+ .prepare = bf5xx_pcm_prepare,
+ .trigger = bf5xx_pcm_trigger,
+ .pointer = bf5xx_pcm_pointer,
+ .copy = bf5xx_pcm_copy,
+ .silence = bf5xx_pcm_silence,
+};
+
+static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_coherent(pcm->card->dev, size * 4,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area) {
+ pr_err("Failed to allocate dma memory \
+ Please increase uncached DMA memory region\n");
+ return -ENOMEM;
+ }
+ buf->bytes = size;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sport_handle->tx_buf = buf->area;
+ else
+ sport_handle->rx_buf = buf->area;
+
+ return 0;
+}
+
+static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+ dma_free_coherent(NULL, buf->bytes, buf->area, 0);
+ buf->area = NULL;
+ }
+ if (sport_handle)
+ sport_done(sport_handle);
+}
+
+static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
+
+static int bf5xx_pcm_tdm_new(struct snd_card *card, struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &bf5xx_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ if (dai->playback.channels_min) {
+ ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (dai->capture.channels_min) {
+ ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+out:
+ return ret;
+}
+
+struct snd_soc_platform bf5xx_tdm_soc_platform = {
+ .name = "bf5xx-audio",
+ .pcm_ops = &bf5xx_pcm_tdm_ops,
+ .pcm_new = bf5xx_pcm_tdm_new,
+ .pcm_free = bf5xx_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(bf5xx_tdm_soc_platform);
+
+static int __init bfin_pcm_tdm_init(void)
+{
+ return snd_soc_register_platform(&bf5xx_tdm_soc_platform);
+}
+module_init(bfin_pcm_tdm_init);
+
+static void __exit bfin_pcm_tdm_exit(void)
+{
+ snd_soc_unregister_platform(&bf5xx_tdm_soc_platform);
+}
+module_exit(bfin_pcm_tdm_exit);
+
+MODULE_AUTHOR("Barry Song");
+MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.h b/sound/soc/blackfin/bf5xx-tdm-pcm.h
new file mode 100644
index 00000000000..ddc5047df88
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-tdm-pcm.h
@@ -0,0 +1,21 @@
+/*
+ * sound/soc/blackfin/bf5xx-tdm-pcm.h -- ALSA PCM interface for the Blackfin
+ *
+ * Copyright 2009 Analog Device 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.
+ */
+
+#ifndef _BF5XX_TDM_PCM_H
+#define _BF5XX_TDM_PCM_H
+
+struct bf5xx_pcm_dma_params {
+ char *name; /* stream identifier */
+};
+
+/* platform data */
+extern struct snd_soc_platform bf5xx_tdm_soc_platform;
+
+#endif
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c
new file mode 100644
index 00000000000..3096badf09a
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-tdm.c
@@ -0,0 +1,343 @@
+/*
+ * File: sound/soc/blackfin/bf5xx-tdm.c
+ * Author: Barry Song <Barry.Song@analog.com>
+ *
+ * Created: Thurs June 04 2009
+ * Description: Blackfin I2S(TDM) CPU DAI driver
+ * Even though TDM mode can be as part of I2S DAI, but there
+ * are so much difference in configuration and data flow,
+ * it's very ugly to integrate I2S and TDM into a module
+ *
+ * Modified:
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/irq.h>
+#include <asm/portmux.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+
+#include "bf5xx-sport.h"
+#include "bf5xx-tdm.h"
+
+struct bf5xx_tdm_port {
+ u16 tcr1;
+ u16 rcr1;
+ u16 tcr2;
+ u16 rcr2;
+ int configured;
+};
+
+static struct bf5xx_tdm_port bf5xx_tdm;
+static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
+
+static struct sport_param sport_params[2] = {
+ {
+ .dma_rx_chan = CH_SPORT0_RX,
+ .dma_tx_chan = CH_SPORT0_TX,
+ .err_irq = IRQ_SPORT0_ERROR,
+ .regs = (struct sport_register *)SPORT0_TCR1,
+ },
+ {
+ .dma_rx_chan = CH_SPORT1_RX,
+ .dma_tx_chan = CH_SPORT1_TX,
+ .err_irq = IRQ_SPORT1_ERROR,
+ .regs = (struct sport_register *)SPORT1_TCR1,
+ }
+};
+
+/*
+ * Setting the TFS pin selector for SPORT 0 based on whether the selected
+ * port id F or G. If the port is F then no conflict should exist for the
+ * TFS. When Port G is selected and EMAC then there is a conflict between
+ * the PHY interrupt line and TFS. Current settings prevent the conflict
+ * by ignoring the TFS pin when Port G is selected. This allows both
+ * ssm2602 using Port G and EMAC concurrently.
+ */
+#ifdef CONFIG_BF527_SPORT0_PORTF
+#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
+#else
+#define LOCAL_SPORT0_TFS (0)
+#endif
+
+static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
+ P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
+ {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
+ P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
+
+static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ int ret = 0;
+
+ /* interface format:support TDM,slave mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ default:
+ printk(KERN_ERR "%s: Unknown DAI format type\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+ ret = -EINVAL;
+ break;
+ default:
+ printk(KERN_ERR "%s: Unknown DAI master type\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+
+ bf5xx_tdm.tcr2 &= ~0x1f;
+ bf5xx_tdm.rcr2 &= ~0x1f;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bf5xx_tdm.tcr2 |= 31;
+ bf5xx_tdm.rcr2 |= 31;
+ sport_handle->wdsize = 4;
+ break;
+ /* at present, we only support 32bit transfer */
+ default:
+ pr_err("not supported PCM format yet\n");
+ return -EINVAL;
+ break;
+ }
+
+ if (!bf5xx_tdm.configured) {
+ /*
+ * TX and RX are not independent,they are enabled at the
+ * same time, even if only one side is running. So, we
+ * need to configure both of them at the time when the first
+ * stream is opened.
+ *
+ * CPU DAI:slave mode.
+ */
+ ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1,
+ bf5xx_tdm.rcr2, 0, 0);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ return -EBUSY;
+ }
+
+ ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1,
+ bf5xx_tdm.tcr2, 0, 0);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ return -EBUSY;
+ }
+
+ bf5xx_tdm.configured = 1;
+ }
+
+ return 0;
+}
+
+static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ /* No active stream, SPORT is allowed to be configured again. */
+ if (!dai->active)
+ bf5xx_tdm.configured = 0;
+}
+
+#ifdef CONFIG_PM
+static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
+{
+ struct sport_device *sport =
+ (struct sport_device *)dai->private_data;
+
+ if (!dai->active)
+ return 0;
+ if (dai->capture.active)
+ sport_rx_stop(sport);
+ if (dai->playback.active)
+ sport_tx_stop(sport);
+ return 0;
+}
+
+static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
+{
+ int ret;
+ struct sport_device *sport =
+ (struct sport_device *)dai->private_data;
+
+ if (!dai->active)
+ return 0;
+
+ ret = sport_set_multichannel(sport, 8, 0xFF, 1);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ ret = -EBUSY;
+ }
+
+ ret = sport_config_rx(sport, IRFS, 0x1F, 0, 0);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ ret = -EBUSY;
+ }
+
+ ret = sport_config_tx(sport, ITFS, 0x1F, 0, 0);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ ret = -EBUSY;
+ }
+
+ return 0;
+}
+
+#else
+#define bf5xx_tdm_suspend NULL
+#define bf5xx_tdm_resume NULL
+#endif
+
+static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = {
+ .hw_params = bf5xx_tdm_hw_params,
+ .set_fmt = bf5xx_tdm_set_dai_fmt,
+ .shutdown = bf5xx_tdm_shutdown,
+};
+
+struct snd_soc_dai bf5xx_tdm_dai = {
+ .name = "bf5xx-tdm",
+ .id = 0,
+ .suspend = bf5xx_tdm_suspend,
+ .resume = bf5xx_tdm_resume,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,},
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,},
+ .ops = &bf5xx_tdm_dai_ops,
+};
+EXPORT_SYMBOL_GPL(bf5xx_tdm_dai);
+
+static int __devinit bfin_tdm_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
+ pr_err("Requesting Peripherals failed\n");
+ return -EFAULT;
+ }
+
+ /* request DMA for SPORT */
+ sport_handle = sport_init(&sport_params[sport_num], 4, \
+ 8 * sizeof(u32), NULL);
+ if (!sport_handle) {
+ peripheral_free_list(&sport_req[sport_num][0]);
+ return -ENODEV;
+ }
+
+ /* SPORT works in TDM mode */
+ ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ ret = -EBUSY;
+ goto sport_config_err;
+ }
+
+ ret = sport_config_rx(sport_handle, IRFS, 0x1F, 0, 0);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ ret = -EBUSY;
+ goto sport_config_err;
+ }
+
+ ret = sport_config_tx(sport_handle, ITFS, 0x1F, 0, 0);
+ if (ret) {
+ pr_err("SPORT is busy!\n");
+ ret = -EBUSY;
+ goto sport_config_err;
+ }
+
+ ret = snd_soc_register_dai(&bf5xx_tdm_dai);
+ if (ret) {
+ pr_err("Failed to register DAI: %d\n", ret);
+ goto sport_config_err;
+ }
+ return 0;
+
+sport_config_err:
+ peripheral_free_list(&sport_req[sport_num][0]);
+ return ret;
+}
+
+static int __devexit bfin_tdm_remove(struct platform_device *pdev)
+{
+ peripheral_free_list(&sport_req[sport_num][0]);
+ snd_soc_unregister_dai(&bf5xx_tdm_dai);
+
+ return 0;
+}
+
+static struct platform_driver bfin_tdm_driver = {
+ .probe = bfin_tdm_probe,
+ .remove = __devexit_p(bfin_tdm_remove),
+ .driver = {
+ .name = "bfin-tdm",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init bfin_tdm_init(void)
+{
+ return platform_driver_register(&bfin_tdm_driver);
+}
+module_init(bfin_tdm_init);
+
+static void __exit bfin_tdm_exit(void)
+{
+ platform_driver_unregister(&bfin_tdm_driver);
+}
+module_exit(bfin_tdm_exit);
+
+/* Module information */
+MODULE_AUTHOR("Barry Song");
+MODULE_DESCRIPTION("TDM driver for ADI Blackfin");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/blackfin/bf5xx-tdm.h b/sound/soc/blackfin/bf5xx-tdm.h
new file mode 100644
index 00000000000..618ec3d90cd
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-tdm.h
@@ -0,0 +1,14 @@
+/*
+ * sound/soc/blackfin/bf5xx-tdm.h
+ *
+ * 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 _BF5XX_TDM_H
+#define _BF5XX_TDM_H
+
+extern struct snd_soc_dai bf5xx_tdm_dai;
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index bbc97fd7664..0edca93af3b 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -12,11 +12,15 @@ config SND_SOC_ALL_CODECS
tristate "Build all ASoC CODEC drivers"
select SND_SOC_L3
select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
+ select SND_SOC_AD1836 if SPI_MASTER
+ select SND_SOC_AD1938 if SPI_MASTER
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_AD73311 if I2C
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
+ select SND_SOC_AK4642 if I2C
select SND_SOC_CS4270 if I2C
+ select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
select SND_SOC_SPDIF
select SND_SOC_SSM2602 if I2C
@@ -30,18 +34,23 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8350 if MFD_WM8350
select SND_SOC_WM8400 if MFD_WM8400
select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
+ select SND_SOC_WM8523 if I2C
select SND_SOC_WM8580 if I2C
select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI
+ select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C
select SND_SOC_WM8940 if I2C
select SND_SOC_WM8960 if I2C
+ select SND_SOC_WM8961 if I2C
select SND_SOC_WM8971 if I2C
+ select SND_SOC_WM8974 if I2C
select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8990 if I2C
+ select SND_SOC_WM8993 if I2C
select SND_SOC_WM9081 if I2C
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
select SND_SOC_WM9712 if SND_SOC_AC97_BUS
@@ -57,11 +66,21 @@ config SND_SOC_ALL_CODECS
If unsure select "N".
+config SND_SOC_WM_HUBS
+ tristate
+ default y if SND_SOC_WM8993=y
+ default m if SND_SOC_WM8993=m
config SND_SOC_AC97_CODEC
tristate
select SND_AC97_CODEC
+config SND_SOC_AD1836
+ tristate
+
+config SND_SOC_AD1938
+ tristate
+
config SND_SOC_AD1980
tristate
@@ -74,6 +93,9 @@ config SND_SOC_AK4104
config SND_SOC_AK4535
tristate
+config SND_SOC_AK4642
+ tristate
+
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
tristate
@@ -86,6 +108,9 @@ config SND_SOC_CS4270_VD33_ERRATA
bool
depends on SND_SOC_CS4270
+config SND_SOC_CX20442
+ tristate
+
config SND_SOC_L3
tristate
@@ -129,6 +154,9 @@ config SND_SOC_WM8400
config SND_SOC_WM8510
tristate
+config SND_SOC_WM8523
+ tristate
+
config SND_SOC_WM8580
tristate
@@ -144,6 +172,9 @@ config SND_SOC_WM8750
config SND_SOC_WM8753
tristate
+config SND_SOC_WM8776
+ tristate
+
config SND_SOC_WM8900
tristate
@@ -156,15 +187,24 @@ config SND_SOC_WM8940
config SND_SOC_WM8960
tristate
+config SND_SOC_WM8961
+ tristate
+
config SND_SOC_WM8971
tristate
+config SND_SOC_WM8974
+ tristate
+
config SND_SOC_WM8988
tristate
config SND_SOC_WM8990
tristate
+config SND_SOC_WM8993
+ tristate
+
config SND_SOC_WM9081
tristate
@@ -176,3 +216,7 @@ config SND_SOC_WM9712
config SND_SOC_WM9713
tristate
+
+# Amp
+config SND_SOC_MAX9877
+ tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 8b7530546f4..fb4af28486b 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,9 +1,13 @@
snd-soc-ac97-objs := ac97.o
+snd-soc-ad1836-objs := ad1836.o
+snd-soc-ad1938-objs := ad1938.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
+snd-soc-ak4642-objs := ak4642.o
snd-soc-cs4270-objs := cs4270.o
+snd-soc-cx20442-objs := cx20442.o
snd-soc-l3-objs := l3.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-spdif-objs := spdif_transciever.o
@@ -18,29 +22,42 @@ snd-soc-uda1380-objs := uda1380.o
snd-soc-wm8350-objs := wm8350.o
snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
+snd-soc-wm8523-objs := wm8523.o
snd-soc-wm8580-objs := wm8580.o
snd-soc-wm8728-objs := wm8728.o
snd-soc-wm8731-objs := wm8731.o
snd-soc-wm8750-objs := wm8750.o
snd-soc-wm8753-objs := wm8753.o
+snd-soc-wm8776-objs := wm8776.o
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8960-objs := wm8960.o
+snd-soc-wm8961-objs := wm8961.o
snd-soc-wm8971-objs := wm8971.o
+snd-soc-wm8974-objs := wm8974.o
snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o
+snd-soc-wm8993-objs := wm8993.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
+snd-soc-wm-hubs-objs := wm_hubs.o
+
+# Amp
+snd-soc-max9877-objs := max9877.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
+obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
+obj-$(CONFIG_SND_SOC_AD1938) += snd-soc-ad1938.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o
@@ -55,19 +72,28 @@ obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
+obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o
obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
+obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
+obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o
obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o
obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
+obj-$(CONFIG_SND_SOC_WM8961) += snd-soc-wm8961.o
obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
+obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o
obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
+obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
+
+# Amp
+obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
new file mode 100644
index 00000000000..3612bb92df9
--- /dev/null
+++ b/sound/soc/codecs/ad1836.c
@@ -0,0 +1,446 @@
+/*
+ * File: sound/soc/codecs/ad1836.c
+ * Author: Barry Song <Barry.Song@analog.com>
+ *
+ * Created: Aug 04 2009
+ * Description: Driver for AD1836 sound chip
+ *
+ * Modified:
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/soc-dapm.h>
+#include <linux/spi/spi.h>
+#include "ad1836.h"
+
+/* codec private data */
+struct ad1836_priv {
+ struct snd_soc_codec codec;
+ u16 reg_cache[AD1836_NUM_REGS];
+};
+
+static struct snd_soc_codec *ad1836_codec;
+struct snd_soc_codec_device soc_codec_dev_ad1836;
+static int ad1836_register(struct ad1836_priv *ad1836);
+static void ad1836_unregister(struct ad1836_priv *ad1836);
+
+/*
+ * AD1836 volume/mute/de-emphasis etc. controls
+ */
+static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"};
+
+static const struct soc_enum ad1836_deemp_enum =
+ SOC_ENUM_SINGLE(AD1836_DAC_CTRL1, 8, 4, ad1836_deemp);
+
+static const struct snd_kcontrol_new ad1836_snd_controls[] = {
+ /* DAC volume control */
+ SOC_DOUBLE_R("DAC1 Volume", AD1836_DAC_L1_VOL,
+ AD1836_DAC_R1_VOL, 0, 0x3FF, 0),
+ SOC_DOUBLE_R("DAC2 Volume", AD1836_DAC_L2_VOL,
+ AD1836_DAC_R2_VOL, 0, 0x3FF, 0),
+ SOC_DOUBLE_R("DAC3 Volume", AD1836_DAC_L3_VOL,
+ AD1836_DAC_R3_VOL, 0, 0x3FF, 0),
+
+ /* ADC switch control */
+ SOC_DOUBLE("ADC1 Switch", AD1836_ADC_CTRL2, AD1836_ADCL1_MUTE,
+ AD1836_ADCR1_MUTE, 1, 1),
+ SOC_DOUBLE("ADC2 Switch", AD1836_ADC_CTRL2, AD1836_ADCL2_MUTE,
+ AD1836_ADCR2_MUTE, 1, 1),
+
+ /* DAC switch control */
+ SOC_DOUBLE("DAC1 Switch", AD1836_DAC_CTRL2, AD1836_DACL1_MUTE,
+ AD1836_DACR1_MUTE, 1, 1),
+ SOC_DOUBLE("DAC2 Switch", AD1836_DAC_CTRL2, AD1836_DACL2_MUTE,
+ AD1836_DACR2_MUTE, 1, 1),
+ SOC_DOUBLE("DAC3 Switch", AD1836_DAC_CTRL2, AD1836_DACL3_MUTE,
+ AD1836_DACR3_MUTE, 1, 1),
+
+ /* ADC high-pass filter */
+ SOC_SINGLE("ADC High Pass Filter Switch", AD1836_ADC_CTRL1,
+ AD1836_ADC_HIGHPASS_FILTER, 1, 0),
+
+ /* DAC de-emphasis */
+ SOC_ENUM("Playback Deemphasis", ad1836_deemp_enum),
+};
+
+static const struct snd_soc_dapm_widget ad1836_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", AD1836_DAC_CTRL1,
+ AD1836_DAC_POWERDOWN, 1),
+ SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1836_ADC_CTRL1,
+ AD1836_ADC_POWERDOWN, 1, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("DAC1OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC2OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC3OUT"),
+ SND_SOC_DAPM_INPUT("ADC1IN"),
+ SND_SOC_DAPM_INPUT("ADC2IN"),
+};
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+ { "DAC", NULL, "ADC_PWR" },
+ { "ADC", NULL, "ADC_PWR" },
+ { "DAC1OUT", "DAC1 Switch", "DAC" },
+ { "DAC2OUT", "DAC2 Switch", "DAC" },
+ { "DAC3OUT", "DAC3 Switch", "DAC" },
+ { "ADC", "ADC1 Switch", "ADC1IN" },
+ { "ADC", "ADC2 Switch", "ADC2IN" },
+};
+
+/*
+ * DAI ops entries
+ */
+
+static int ad1836_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ /* at present, we support adc aux mode to interface with
+ * blackfin sport tdm mode
+ */
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ /* ALCLK,ABCLK are both output, AD1836 can only be master */
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ad1836_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int word_len = 0;
+
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ word_len = 3;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ word_len = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ word_len = 0;
+ break;
+ }
+
+ snd_soc_update_bits(codec, AD1836_DAC_CTRL1,
+ AD1836_DAC_WORD_LEN_MASK, word_len);
+
+ snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
+ AD1836_ADC_WORD_LEN_MASK, word_len);
+
+ return 0;
+}
+
+
+/*
+ * interface to read/write ad1836 register
+ */
+#define AD1836_SPI_REG_SHFT 12
+#define AD1836_SPI_READ (1 << 11)
+#define AD1836_SPI_VAL_MSK 0x3FF
+
+/*
+ * write to the ad1836 register space
+ */
+
+static int ad1836_write_reg(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u16 *reg_cache = codec->reg_cache;
+ int ret = 0;
+
+ if (value != reg_cache[reg]) {
+ unsigned short buf;
+ struct spi_transfer t = {
+ .tx_buf = &buf,
+ .len = 2,
+ };
+ struct spi_message m;
+
+ buf = (reg << AD1836_SPI_REG_SHFT) |
+ (value & AD1836_SPI_VAL_MSK);
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ ret = spi_sync(codec->control_data, &m);
+ if (ret == 0)
+ reg_cache[reg] = value;
+ }
+
+ return ret;
+}
+
+/*
+ * read from the ad1836 register space cache
+ */
+static unsigned int ad1836_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *reg_cache = codec->reg_cache;
+
+ if (reg >= codec->reg_cache_size)
+ return -EINVAL;
+
+ return reg_cache[reg];
+}
+
+static int __devinit ad1836_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_codec *codec;
+ struct ad1836_priv *ad1836;
+
+ ad1836 = kzalloc(sizeof(struct ad1836_priv), GFP_KERNEL);
+ if (ad1836 == NULL)
+ return -ENOMEM;
+
+ codec = &ad1836->codec;
+ codec->control_data = spi;
+ codec->dev = &spi->dev;
+
+ dev_set_drvdata(&spi->dev, ad1836);
+
+ return ad1836_register(ad1836);
+}
+
+static int __devexit ad1836_spi_remove(struct spi_device *spi)
+{
+ struct ad1836_priv *ad1836 = dev_get_drvdata(&spi->dev);
+
+ ad1836_unregister(ad1836);
+ return 0;
+}
+
+static struct spi_driver ad1836_spi_driver = {
+ .driver = {
+ .name = "ad1836-spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = ad1836_spi_probe,
+ .remove = __devexit_p(ad1836_spi_remove),
+};
+
+static struct snd_soc_dai_ops ad1836_dai_ops = {
+ .hw_params = ad1836_hw_params,
+ .set_fmt = ad1836_set_dai_fmt,
+};
+
+/* codec DAI instance */
+struct snd_soc_dai ad1836_dai = {
+ .name = "AD1836",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 6,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &ad1836_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ad1836_dai);
+
+static int ad1836_register(struct ad1836_priv *ad1836)
+{
+ int ret;
+ struct snd_soc_codec *codec = &ad1836->codec;
+
+ if (ad1836_codec) {
+ dev_err(codec->dev, "Another ad1836 is registered\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ codec->private_data = ad1836;
+ codec->reg_cache = ad1836->reg_cache;
+ codec->reg_cache_size = AD1836_NUM_REGS;
+ codec->name = "AD1836";
+ codec->owner = THIS_MODULE;
+ codec->dai = &ad1836_dai;
+ codec->num_dai = 1;
+ codec->write = ad1836_write_reg;
+ codec->read = ad1836_read_reg_cache;
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ ad1836_dai.dev = codec->dev;
+ ad1836_codec = codec;
+
+ /* default setting for ad1836 */
+ /* de-emphasis: 48kHz, power-on dac */
+ codec->write(codec, AD1836_DAC_CTRL1, 0x300);
+ /* unmute dac channels */
+ codec->write(codec, AD1836_DAC_CTRL2, 0x0);
+ /* high-pass filter enable, power-on adc */
+ codec->write(codec, AD1836_ADC_CTRL1, 0x100);
+ /* unmute adc channles, adc aux mode */
+ codec->write(codec, AD1836_ADC_CTRL2, 0x180);
+ /* left/right diff:PGA/MUX */
+ codec->write(codec, AD1836_ADC_CTRL3, 0x3A);
+ /* volume */
+ codec->write(codec, AD1836_DAC_L1_VOL, 0x3FF);
+ codec->write(codec, AD1836_DAC_R1_VOL, 0x3FF);
+ codec->write(codec, AD1836_DAC_L2_VOL, 0x3FF);
+ codec->write(codec, AD1836_DAC_R2_VOL, 0x3FF);
+ codec->write(codec, AD1836_DAC_L3_VOL, 0x3FF);
+ codec->write(codec, AD1836_DAC_R3_VOL, 0x3FF);
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ kfree(ad1836);
+ return ret;
+ }
+
+ ret = snd_soc_register_dai(&ad1836_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ snd_soc_unregister_codec(codec);
+ kfree(ad1836);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ad1836_unregister(struct ad1836_priv *ad1836)
+{
+ snd_soc_unregister_dai(&ad1836_dai);
+ snd_soc_unregister_codec(&ad1836->codec);
+ kfree(ad1836);
+ ad1836_codec = NULL;
+}
+
+static int ad1836_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (ad1836_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = ad1836_codec;
+ codec = ad1836_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, ad1836_snd_controls,
+ ARRAY_SIZE(ad1836_snd_controls));
+ snd_soc_dapm_new_controls(codec, ad1836_dapm_widgets,
+ ARRAY_SIZE(ad1836_dapm_widgets));
+ snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+ snd_soc_dapm_new_widgets(codec);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to register card: %d\n", ret);
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ return ret;
+}
+
+/* power down chip */
+static int ad1836_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ad1836 = {
+ .probe = ad1836_probe,
+ .remove = ad1836_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ad1836);
+
+static int __init ad1836_init(void)
+{
+ int ret;
+
+ ret = spi_register_driver(&ad1836_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register ad1836 SPI driver: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+module_init(ad1836_init);
+
+static void __exit ad1836_exit(void)
+{
+ spi_unregister_driver(&ad1836_spi_driver);
+}
+module_exit(ad1836_exit);
+
+MODULE_DESCRIPTION("ASoC ad1836 driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad1836.h b/sound/soc/codecs/ad1836.h
new file mode 100644
index 00000000000..7660ee6973c
--- /dev/null
+++ b/sound/soc/codecs/ad1836.h
@@ -0,0 +1,64 @@
+/*
+ * File: sound/soc/codecs/ad1836.h
+ * Based on:
+ * Author: Barry Song <Barry.Song@analog.com>
+ *
+ * Created: Aug 04, 2009
+ * Description: definitions for AD1836 registers
+ *
+ * Modified:
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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 __AD1836_H__
+#define __AD1836_H__
+
+#define AD1836_DAC_CTRL1 0
+#define AD1836_DAC_POWERDOWN 2
+#define AD1836_DAC_SERFMT_MASK 0xE0
+#define AD1836_DAC_SERFMT_PCK256 (0x4 << 5)
+#define AD1836_DAC_SERFMT_PCK128 (0x5 << 5)
+#define AD1836_DAC_WORD_LEN_MASK 0x18
+
+#define AD1836_DAC_CTRL2 1
+#define AD1836_DACL1_MUTE 0
+#define AD1836_DACR1_MUTE 1
+#define AD1836_DACL2_MUTE 2
+#define AD1836_DACR2_MUTE 3
+#define AD1836_DACL3_MUTE 4
+#define AD1836_DACR3_MUTE 5
+
+#define AD1836_DAC_L1_VOL 2
+#define AD1836_DAC_R1_VOL 3
+#define AD1836_DAC_L2_VOL 4
+#define AD1836_DAC_R2_VOL 5
+#define AD1836_DAC_L3_VOL 6
+#define AD1836_DAC_R3_VOL 7
+
+#define AD1836_ADC_CTRL1 12
+#define AD1836_ADC_POWERDOWN 7
+#define AD1836_ADC_HIGHPASS_FILTER 8
+
+#define AD1836_ADC_CTRL2 13
+#define AD1836_ADCL1_MUTE 0
+#define AD1836_ADCR1_MUTE 1
+#define AD1836_ADCL2_MUTE 2
+#define AD1836_ADCR2_MUTE 3
+#define AD1836_ADC_WORD_LEN_MASK 0x30
+#define AD1836_ADC_SERFMT_MASK (7 << 6)
+#define AD1836_ADC_SERFMT_PCK256 (0x4 << 6)
+#define AD1836_ADC_SERFMT_PCK128 (0x5 << 6)
+
+#define AD1836_ADC_CTRL3 14
+
+#define AD1836_NUM_REGS 16
+
+extern struct snd_soc_dai ad1836_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ad1836;
+#endif
diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c
new file mode 100644
index 00000000000..e62b27701a4
--- /dev/null
+++ b/sound/soc/codecs/ad1938.c
@@ -0,0 +1,682 @@
+/*
+ * File: sound/soc/codecs/ad1938.c
+ * Author: Barry Song <Barry.Song@analog.com>
+ *
+ * Created: June 04 2009
+ * Description: Driver for AD1938 sound chip
+ *
+ * Modified:
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/soc-dapm.h>
+#include <linux/spi/spi.h>
+#include "ad1938.h"
+
+/* codec private data */
+struct ad1938_priv {
+ struct snd_soc_codec codec;
+ u8 reg_cache[AD1938_NUM_REGS];
+};
+
+static struct snd_soc_codec *ad1938_codec;
+struct snd_soc_codec_device soc_codec_dev_ad1938;
+static int ad1938_register(struct ad1938_priv *ad1938);
+static void ad1938_unregister(struct ad1938_priv *ad1938);
+
+/*
+ * AD1938 volume/mute/de-emphasis etc. controls
+ */
+static const char *ad1938_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"};
+
+static const struct soc_enum ad1938_deemp_enum =
+ SOC_ENUM_SINGLE(AD1938_DAC_CTRL2, 1, 4, ad1938_deemp);
+
+static const struct snd_kcontrol_new ad1938_snd_controls[] = {
+ /* DAC volume control */
+ SOC_DOUBLE_R("DAC1 Volume", AD1938_DAC_L1_VOL,
+ AD1938_DAC_R1_VOL, 0, 0xFF, 1),
+ SOC_DOUBLE_R("DAC2 Volume", AD1938_DAC_L2_VOL,
+ AD1938_DAC_R2_VOL, 0, 0xFF, 1),
+ SOC_DOUBLE_R("DAC3 Volume", AD1938_DAC_L3_VOL,
+ AD1938_DAC_R3_VOL, 0, 0xFF, 1),
+ SOC_DOUBLE_R("DAC4 Volume", AD1938_DAC_L4_VOL,
+ AD1938_DAC_R4_VOL, 0, 0xFF, 1),
+
+ /* ADC switch control */
+ SOC_DOUBLE("ADC1 Switch", AD1938_ADC_CTRL0, AD1938_ADCL1_MUTE,
+ AD1938_ADCR1_MUTE, 1, 1),
+ SOC_DOUBLE("ADC2 Switch", AD1938_ADC_CTRL0, AD1938_ADCL2_MUTE,
+ AD1938_ADCR2_MUTE, 1, 1),
+
+ /* DAC switch control */
+ SOC_DOUBLE("DAC1 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL1_MUTE,
+ AD1938_DACR1_MUTE, 1, 1),
+ SOC_DOUBLE("DAC2 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL2_MUTE,
+ AD1938_DACR2_MUTE, 1, 1),
+ SOC_DOUBLE("DAC3 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL3_MUTE,
+ AD1938_DACR3_MUTE, 1, 1),
+ SOC_DOUBLE("DAC4 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL4_MUTE,
+ AD1938_DACR4_MUTE, 1, 1),
+
+ /* ADC high-pass filter */
+ SOC_SINGLE("ADC High Pass Filter Switch", AD1938_ADC_CTRL0,
+ AD1938_ADC_HIGHPASS_FILTER, 1, 0),
+
+ /* DAC de-emphasis */
+ SOC_ENUM("Playback Deemphasis", ad1938_deemp_enum),
+};
+
+static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", AD1938_DAC_CTRL0, 0, 1),
+ SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1938_ADC_CTRL0, 0, 1, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("DAC1OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC2OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC3OUT"),
+ SND_SOC_DAPM_OUTPUT("DAC4OUT"),
+ SND_SOC_DAPM_INPUT("ADC1IN"),
+ SND_SOC_DAPM_INPUT("ADC2IN"),
+};
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+ { "DAC", NULL, "ADC_PWR" },
+ { "ADC", NULL, "ADC_PWR" },
+ { "DAC1OUT", "DAC1 Switch", "DAC" },
+ { "DAC2OUT", "DAC2 Switch", "DAC" },
+ { "DAC3OUT", "DAC3 Switch", "DAC" },
+ { "DAC4OUT", "DAC4 Switch", "DAC" },
+ { "ADC", "ADC1 Switch", "ADC1IN" },
+ { "ADC", "ADC2 Switch", "ADC2IN" },
+};
+
+/*
+ * DAI ops entries
+ */
+
+static int ad1938_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int reg;
+
+ reg = codec->read(codec, AD1938_DAC_CTRL2);
+ reg = (mute > 0) ? reg | AD1938_DAC_MASTER_MUTE : reg &
+ (~AD1938_DAC_MASTER_MUTE);
+ codec->write(codec, AD1938_DAC_CTRL2, reg);
+
+ return 0;
+}
+
+static inline int ad1938_pll_powerctrl(struct snd_soc_codec *codec, int cmd)
+{
+ int reg = codec->read(codec, AD1938_PLL_CLK_CTRL0);
+ reg = (cmd > 0) ? reg & (~AD1938_PLL_POWERDOWN) : reg |
+ AD1938_PLL_POWERDOWN;
+ codec->write(codec, AD1938_PLL_CLK_CTRL0, reg);
+
+ return 0;
+}
+
+static int ad1938_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int mask, int slots, int width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
+ int adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
+
+ dac_reg &= ~AD1938_DAC_CHAN_MASK;
+ adc_reg &= ~AD1938_ADC_CHAN_MASK;
+
+ switch (slots) {
+ case 2:
+ dac_reg |= AD1938_DAC_2_CHANNELS << AD1938_DAC_CHAN_SHFT;
+ adc_reg |= AD1938_ADC_2_CHANNELS << AD1938_ADC_CHAN_SHFT;
+ break;
+ case 4:
+ dac_reg |= AD1938_DAC_4_CHANNELS << AD1938_DAC_CHAN_SHFT;
+ adc_reg |= AD1938_ADC_4_CHANNELS << AD1938_ADC_CHAN_SHFT;
+ break;
+ case 8:
+ dac_reg |= AD1938_DAC_8_CHANNELS << AD1938_DAC_CHAN_SHFT;
+ adc_reg |= AD1938_ADC_8_CHANNELS << AD1938_ADC_CHAN_SHFT;
+ break;
+ case 16:
+ dac_reg |= AD1938_DAC_16_CHANNELS << AD1938_DAC_CHAN_SHFT;
+ adc_reg |= AD1938_ADC_16_CHANNELS << AD1938_ADC_CHAN_SHFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
+ codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
+
+ return 0;
+}
+
+static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int adc_reg, dac_reg;
+
+ adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
+ dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
+
+ /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S
+ * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A)
+ */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ adc_reg &= ~AD1938_ADC_SERFMT_MASK;
+ adc_reg |= AD1938_ADC_SERFMT_TDM;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ adc_reg &= ~AD1938_ADC_SERFMT_MASK;
+ adc_reg |= AD1938_ADC_SERFMT_AUX;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
+ adc_reg &= ~AD1938_ADC_LEFT_HIGH;
+ adc_reg &= ~AD1938_ADC_BCLK_INV;
+ dac_reg &= ~AD1938_DAC_LEFT_HIGH;
+ dac_reg &= ~AD1938_DAC_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */
+ adc_reg |= AD1938_ADC_LEFT_HIGH;
+ adc_reg &= ~AD1938_ADC_BCLK_INV;
+ dac_reg |= AD1938_DAC_LEFT_HIGH;
+ dac_reg &= ~AD1938_DAC_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */
+ adc_reg &= ~AD1938_ADC_LEFT_HIGH;
+ adc_reg |= AD1938_ADC_BCLK_INV;
+ dac_reg &= ~AD1938_DAC_LEFT_HIGH;
+ dac_reg |= AD1938_DAC_BCLK_INV;
+ break;
+
+ case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */
+ adc_reg |= AD1938_ADC_LEFT_HIGH;
+ adc_reg |= AD1938_ADC_BCLK_INV;
+ dac_reg |= AD1938_DAC_LEFT_HIGH;
+ dac_reg |= AD1938_DAC_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
+ adc_reg |= AD1938_ADC_LCR_MASTER;
+ adc_reg |= AD1938_ADC_BCLK_MASTER;
+ dac_reg |= AD1938_DAC_LCR_MASTER;
+ dac_reg |= AD1938_DAC_BCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */
+ adc_reg |= AD1938_ADC_LCR_MASTER;
+ adc_reg &= ~AD1938_ADC_BCLK_MASTER;
+ dac_reg |= AD1938_DAC_LCR_MASTER;
+ dac_reg &= ~AD1938_DAC_BCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
+ adc_reg &= ~AD1938_ADC_LCR_MASTER;
+ adc_reg |= AD1938_ADC_BCLK_MASTER;
+ dac_reg &= ~AD1938_DAC_LCR_MASTER;
+ dac_reg |= AD1938_DAC_BCLK_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+ adc_reg &= ~AD1938_ADC_LCR_MASTER;
+ adc_reg &= ~AD1938_ADC_BCLK_MASTER;
+ dac_reg &= ~AD1938_DAC_LCR_MASTER;
+ dac_reg &= ~AD1938_DAC_BCLK_MASTER;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
+ codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
+
+ return 0;
+}
+
+static int ad1938_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int word_len = 0, reg = 0;
+
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ word_len = 3;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ word_len = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ word_len = 0;
+ break;
+ }
+
+ reg = codec->read(codec, AD1938_DAC_CTRL2);
+ reg = (reg & (~AD1938_DAC_WORD_LEN_MASK)) | word_len;
+ codec->write(codec, AD1938_DAC_CTRL2, reg);
+
+ reg = codec->read(codec, AD1938_ADC_CTRL1);
+ reg = (reg & (~AD1938_ADC_WORD_LEN_MASK)) | word_len;
+ codec->write(codec, AD1938_ADC_CTRL1, reg);
+
+ return 0;
+}
+
+static int ad1938_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ad1938_pll_powerctrl(codec, 1);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ case SND_SOC_BIAS_OFF:
+ ad1938_pll_powerctrl(codec, 0);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+/*
+ * interface to read/write ad1938 register
+ */
+
+#define AD1938_SPI_ADDR 0x4
+#define AD1938_SPI_READ 0x1
+#define AD1938_SPI_BUFLEN 3
+
+/*
+ * write to the ad1938 register space
+ */
+
+static int ad1938_write_reg(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 *reg_cache = codec->reg_cache;
+ int ret = 0;
+
+ if (value != reg_cache[reg]) {
+ uint8_t buf[AD1938_SPI_BUFLEN];
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = AD1938_SPI_BUFLEN,
+ };
+ struct spi_message m;
+
+ buf[0] = AD1938_SPI_ADDR << 1;
+ buf[1] = reg;
+ buf[2] = value;
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ ret = spi_sync(codec->control_data, &m);
+ if (ret == 0)
+ reg_cache[reg] = value;
+ }
+
+ return ret;
+}
+
+/*
+ * read from the ad1938 register space cache
+ */
+
+static unsigned int ad1938_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 *reg_cache = codec->reg_cache;
+
+ if (reg >= codec->reg_cache_size)
+ return -EINVAL;
+
+ return reg_cache[reg];
+}
+
+/*
+ * read from the ad1938 register space
+ */
+
+static unsigned int ad1938_read_reg(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ char w_buf[AD1938_SPI_BUFLEN];
+ char r_buf[AD1938_SPI_BUFLEN];
+ int ret;
+
+ struct spi_transfer t = {
+ .tx_buf = w_buf,
+ .rx_buf = r_buf,
+ .len = AD1938_SPI_BUFLEN,
+ };
+ struct spi_message m;
+
+ w_buf[0] = (AD1938_SPI_ADDR << 1) | AD1938_SPI_READ;
+ w_buf[1] = reg;
+ w_buf[2] = 0;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ ret = spi_sync(codec->control_data, &m);
+ if (ret == 0)
+ return r_buf[2];
+ else
+ return -EIO;
+}
+
+static int ad1938_fill_cache(struct snd_soc_codec *codec)
+{
+ int i;
+ u8 *reg_cache = codec->reg_cache;
+ struct spi_device *spi = codec->control_data;
+
+ for (i = 0; i < codec->reg_cache_size; i++) {
+ int ret = ad1938_read_reg(codec, i);
+ if (ret == -EIO) {
+ dev_err(&spi->dev, "AD1938 SPI read failure\n");
+ return ret;
+ }
+ reg_cache[i] = ret;
+ }
+
+ return 0;
+}
+
+static int __devinit ad1938_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_codec *codec;
+ struct ad1938_priv *ad1938;
+
+ ad1938 = kzalloc(sizeof(struct ad1938_priv), GFP_KERNEL);
+ if (ad1938 == NULL)
+ return -ENOMEM;
+
+ codec = &ad1938->codec;
+ codec->control_data = spi;
+ codec->dev = &spi->dev;
+
+ dev_set_drvdata(&spi->dev, ad1938);
+
+ return ad1938_register(ad1938);
+}
+
+static int __devexit ad1938_spi_remove(struct spi_device *spi)
+{
+ struct ad1938_priv *ad1938 = dev_get_drvdata(&spi->dev);
+
+ ad1938_unregister(ad1938);
+ return 0;
+}
+
+static struct spi_driver ad1938_spi_driver = {
+ .driver = {
+ .name = "ad1938",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = ad1938_spi_probe,
+ .remove = __devexit_p(ad1938_spi_remove),
+};
+
+static struct snd_soc_dai_ops ad1938_dai_ops = {
+ .hw_params = ad1938_hw_params,
+ .digital_mute = ad1938_mute,
+ .set_tdm_slot = ad1938_set_tdm_slot,
+ .set_fmt = ad1938_set_dai_fmt,
+};
+
+/* codec DAI instance */
+struct snd_soc_dai ad1938_dai = {
+ .name = "AD1938",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &ad1938_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ad1938_dai);
+
+static int ad1938_register(struct ad1938_priv *ad1938)
+{
+ int ret;
+ struct snd_soc_codec *codec = &ad1938->codec;
+
+ if (ad1938_codec) {
+ dev_err(codec->dev, "Another ad1938 is registered\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ codec->private_data = ad1938;
+ codec->reg_cache = ad1938->reg_cache;
+ codec->reg_cache_size = AD1938_NUM_REGS;
+ codec->name = "AD1938";
+ codec->owner = THIS_MODULE;
+ codec->dai = &ad1938_dai;
+ codec->num_dai = 1;
+ codec->write = ad1938_write_reg;
+ codec->read = ad1938_read_reg_cache;
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ ad1938_dai.dev = codec->dev;
+ ad1938_codec = codec;
+
+ /* default setting for ad1938 */
+
+ /* unmute dac channels */
+ codec->write(codec, AD1938_DAC_CHNL_MUTE, 0x0);
+ /* de-emphasis: 48kHz, powedown dac */
+ codec->write(codec, AD1938_DAC_CTRL2, 0x1A);
+ /* powerdown dac, dac in tdm mode */
+ codec->write(codec, AD1938_DAC_CTRL0, 0x41);
+ /* high-pass filter enable */
+ codec->write(codec, AD1938_ADC_CTRL0, 0x3);
+ /* sata delay=1, adc aux mode */
+ codec->write(codec, AD1938_ADC_CTRL1, 0x43);
+ /* pll input: mclki/xi */
+ codec->write(codec, AD1938_PLL_CLK_CTRL0, 0x9D);
+ codec->write(codec, AD1938_PLL_CLK_CTRL1, 0x04);
+
+ ad1938_fill_cache(codec);
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ kfree(ad1938);
+ return ret;
+ }
+
+ ret = snd_soc_register_dai(&ad1938_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ snd_soc_unregister_codec(codec);
+ kfree(ad1938);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ad1938_unregister(struct ad1938_priv *ad1938)
+{
+ ad1938_set_bias_level(&ad1938->codec, SND_SOC_BIAS_OFF);
+ snd_soc_unregister_dai(&ad1938_dai);
+ snd_soc_unregister_codec(&ad1938->codec);
+ kfree(ad1938);
+ ad1938_codec = NULL;
+}
+
+static int ad1938_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (ad1938_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = ad1938_codec;
+ codec = ad1938_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, ad1938_snd_controls,
+ ARRAY_SIZE(ad1938_snd_controls));
+ snd_soc_dapm_new_controls(codec, ad1938_dapm_widgets,
+ ARRAY_SIZE(ad1938_dapm_widgets));
+ snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+ snd_soc_dapm_new_widgets(codec);
+
+ ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to register card: %d\n", ret);
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ return ret;
+}
+
+/* power down chip */
+static int ad1938_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ad1938_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ ad1938_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int ad1938_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+ ad1938_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+ return 0;
+}
+#else
+#define ad1938_suspend NULL
+#define ad1938_resume NULL
+#endif
+
+struct snd_soc_codec_device soc_codec_dev_ad1938 = {
+ .probe = ad1938_probe,
+ .remove = ad1938_remove,
+ .suspend = ad1938_suspend,
+ .resume = ad1938_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ad1938);
+
+static int __init ad1938_init(void)
+{
+ int ret;
+
+ ret = spi_register_driver(&ad1938_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register ad1938 SPI driver: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+module_init(ad1938_init);
+
+static void __exit ad1938_exit(void)
+{
+ spi_unregister_driver(&ad1938_spi_driver);
+}
+module_exit(ad1938_exit);
+
+MODULE_DESCRIPTION("ASoC ad1938 driver");
+MODULE_AUTHOR("Barry Song ");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad1938.h b/sound/soc/codecs/ad1938.h
new file mode 100644
index 00000000000..fe3c48cd2d5
--- /dev/null
+++ b/sound/soc/codecs/ad1938.h
@@ -0,0 +1,100 @@
+/*
+ * File: sound/soc/codecs/ad1836.h
+ * Based on:
+ * Author: Barry Song <Barry.Song@analog.com>
+ *
+ * Created: May 25, 2009
+ * Description: definitions for AD1938 registers
+ *
+ * Modified:
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __AD1938_H__
+#define __AD1938_H__
+
+#define AD1938_PLL_CLK_CTRL0 0
+#define AD1938_PLL_POWERDOWN 0x01
+#define AD1938_PLL_CLK_CTRL1 1
+#define AD1938_DAC_CTRL0 2
+#define AD1938_DAC_POWERDOWN 0x01
+#define AD1938_DAC_SERFMT_MASK 0xC0
+#define AD1938_DAC_SERFMT_STEREO (0 << 6)
+#define AD1938_DAC_SERFMT_TDM (1 << 6)
+#define AD1938_DAC_CTRL1 3
+#define AD1938_DAC_2_CHANNELS 0
+#define AD1938_DAC_4_CHANNELS 1
+#define AD1938_DAC_8_CHANNELS 2
+#define AD1938_DAC_16_CHANNELS 3
+#define AD1938_DAC_CHAN_SHFT 1
+#define AD1938_DAC_CHAN_MASK (3 << AD1938_DAC_CHAN_SHFT)
+#define AD1938_DAC_LCR_MASTER (1 << 4)
+#define AD1938_DAC_BCLK_MASTER (1 << 5)
+#define AD1938_DAC_LEFT_HIGH (1 << 3)
+#define AD1938_DAC_BCLK_INV (1 << 7)
+#define AD1938_DAC_CTRL2 4
+#define AD1938_DAC_WORD_LEN_MASK 0xC
+#define AD1938_DAC_MASTER_MUTE 1
+#define AD1938_DAC_CHNL_MUTE 5
+#define AD1938_DACL1_MUTE 0
+#define AD1938_DACR1_MUTE 1
+#define AD1938_DACL2_MUTE 2
+#define AD1938_DACR2_MUTE 3
+#define AD1938_DACL3_MUTE 4
+#define AD1938_DACR3_MUTE 5
+#define AD1938_DACL4_MUTE 6
+#define AD1938_DACR4_MUTE 7
+#define AD1938_DAC_L1_VOL 6
+#define AD1938_DAC_R1_VOL 7
+#define AD1938_DAC_L2_VOL 8
+#define AD1938_DAC_R2_VOL 9
+#define AD1938_DAC_L3_VOL 10
+#define AD1938_DAC_R3_VOL 11
+#define AD1938_DAC_L4_VOL 12
+#define AD1938_DAC_R4_VOL 13
+#define AD1938_ADC_CTRL0 14
+#define AD1938_ADC_POWERDOWN 0x01
+#define AD1938_ADC_HIGHPASS_FILTER 1
+#define AD1938_ADCL1_MUTE 2
+#define AD1938_ADCR1_MUTE 3
+#define AD1938_ADCL2_MUTE 4
+#define AD1938_ADCR2_MUTE 5
+#define AD1938_ADC_CTRL1 15
+#define AD1938_ADC_SERFMT_MASK 0x60
+#define AD1938_ADC_SERFMT_STEREO (0 << 5)
+#define AD1938_ADC_SERFMT_TDM (1 << 2)
+#define AD1938_ADC_SERFMT_AUX (2 << 5)
+#define AD1938_ADC_WORD_LEN_MASK 0x3
+#define AD1938_ADC_CTRL2 16
+#define AD1938_ADC_2_CHANNELS 0
+#define AD1938_ADC_4_CHANNELS 1
+#define AD1938_ADC_8_CHANNELS 2
+#define AD1938_ADC_16_CHANNELS 3
+#define AD1938_ADC_CHAN_SHFT 4
+#define AD1938_ADC_CHAN_MASK (3 << AD1938_ADC_CHAN_SHFT)
+#define AD1938_ADC_LCR_MASTER (1 << 3)
+#define AD1938_ADC_BCLK_MASTER (1 << 6)
+#define AD1938_ADC_LEFT_HIGH (1 << 2)
+#define AD1938_ADC_BCLK_INV (1 << 1)
+
+#define AD1938_NUM_REGS 17
+
+extern struct snd_soc_dai ad1938_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ad1938;
+#endif
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index dd338020276..0abec0d29a9 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -59,21 +59,6 @@ static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec,
return cache[reg];
}
-static inline unsigned int ak4535_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u8 data;
- data = reg;
-
- if (codec->hw_write(codec->control_data, &data, 1) != 1)
- return -EIO;
-
- if (codec->hw_read(codec->control_data, &data, 1) != 1)
- return -EIO;
-
- return data;
-};
-
/*
* write ak4535 register cache
*/
@@ -635,7 +620,6 @@ static int ak4535_probe(struct platform_device *pdev)
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
codec->hw_write = (hw_write_t)i2c_master_send;
- codec->hw_read = (hw_read_t)i2c_master_recv;
ret = ak4535_add_i2c_device(pdev, setup);
}
#endif
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
new file mode 100644
index 00000000000..e057c7b578d
--- /dev/null
+++ b/sound/soc/codecs/ak4642.c
@@ -0,0 +1,502 @@
+/*
+ * ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on wm8731.c by Richard Purdie
+ * Based on ak4535.c by Richard Purdie
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * 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.
+ */
+
+/* ** CAUTION **
+ *
+ * This is very simple driver.
+ * It can use headphone output / stereo input only
+ *
+ * AK4642 is not tested.
+ * AK4643 is tested.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "ak4642.h"
+
+#define AK4642_VERSION "0.0.1"
+
+#define PW_MGMT1 0x00
+#define PW_MGMT2 0x01
+#define SG_SL1 0x02
+#define SG_SL2 0x03
+#define MD_CTL1 0x04
+#define MD_CTL2 0x05
+#define TIMER 0x06
+#define ALC_CTL1 0x07
+#define ALC_CTL2 0x08
+#define L_IVC 0x09
+#define L_DVC 0x0a
+#define ALC_CTL3 0x0b
+#define R_IVC 0x0c
+#define R_DVC 0x0d
+#define MD_CTL3 0x0e
+#define MD_CTL4 0x0f
+#define PW_MGMT3 0x10
+#define DF_S 0x11
+#define FIL3_0 0x12
+#define FIL3_1 0x13
+#define FIL3_2 0x14
+#define FIL3_3 0x15
+#define EQ_0 0x16
+#define EQ_1 0x17
+#define EQ_2 0x18
+#define EQ_3 0x19
+#define EQ_4 0x1a
+#define EQ_5 0x1b
+#define FIL1_0 0x1c
+#define FIL1_1 0x1d
+#define FIL1_2 0x1e
+#define FIL1_3 0x1f
+#define PW_MGMT4 0x20
+#define MD_CTL5 0x21
+#define LO_MS 0x22
+#define HP_MS 0x23
+#define SPK_MS 0x24
+
+#define AK4642_CACHEREGNUM 0x25
+
+struct snd_soc_codec_device soc_codec_dev_ak4642;
+
+/* codec private data */
+struct ak4642_priv {
+ struct snd_soc_codec codec;
+ unsigned int sysclk;
+};
+
+static struct snd_soc_codec *ak4642_codec;
+
+/*
+ * ak4642 register cache
+ */
+static const u16 ak4642_reg[AK4642_CACHEREGNUM] = {
+ 0x0000, 0x0000, 0x0001, 0x0000,
+ 0x0002, 0x0000, 0x0000, 0x0000,
+ 0x00e1, 0x00e1, 0x0018, 0x0000,
+ 0x00e1, 0x0018, 0x0011, 0x0008,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000,
+};
+
+/*
+ * read ak4642 register cache
+ */
+static inline unsigned int ak4642_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= AK4642_CACHEREGNUM)
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * write ak4642 register cache
+ */
+static inline void ak4642_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= AK4642_CACHEREGNUM)
+ return;
+
+ cache[reg] = value;
+}
+
+/*
+ * write to the AK4642 register space
+ */
+static int ak4642_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ /* data is
+ * D15..D8 AK4642 register offset
+ * D7...D0 register data
+ */
+ data[0] = reg & 0xff;
+ data[1] = value & 0xff;
+
+ if (codec->hw_write(codec->control_data, data, 2) == 2) {
+ ak4642_write_reg_cache(codec, reg, value);
+ return 0;
+ } else
+ return -EIO;
+}
+
+static int ak4642_sync(struct snd_soc_codec *codec)
+{
+ u16 *cache = codec->reg_cache;
+ int i, r = 0;
+
+ for (i = 0; i < AK4642_CACHEREGNUM; i++)
+ r |= ak4642_write(codec, i, cache[i]);
+
+ return r;
+};
+
+static int ak4642_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (is_play) {
+ /*
+ * start headphone output
+ *
+ * PLL, Master Mode
+ * Audio I/F Format :MSB justified (ADC & DAC)
+ * Sampling Frequency: 44.1kHz
+ * Digital Volume: −8dB
+ * Bass Boost Level : Middle
+ *
+ * This operation came from example code of
+ * "ASAHI KASEI AK4642" (japanese) manual p97.
+ *
+ * Example code use 0x39, 0x79 value for 0x01 address,
+ * But we need MCKO (0x02) bit now
+ */
+ ak4642_write(codec, 0x05, 0x27);
+ ak4642_write(codec, 0x0f, 0x09);
+ ak4642_write(codec, 0x0e, 0x19);
+ ak4642_write(codec, 0x09, 0x91);
+ ak4642_write(codec, 0x0c, 0x91);
+ ak4642_write(codec, 0x0a, 0x28);
+ ak4642_write(codec, 0x0d, 0x28);
+ ak4642_write(codec, 0x00, 0x64);
+ ak4642_write(codec, 0x01, 0x3b); /* + MCKO bit */
+ ak4642_write(codec, 0x01, 0x7b); /* + MCKO bit */
+ } else {
+ /*
+ * start stereo input
+ *
+ * PLL Master Mode
+ * Audio I/F Format:MSB justified (ADC & DAC)
+ * Sampling Frequency:44.1kHz
+ * Pre MIC AMP:+20dB
+ * MIC Power On
+ * ALC setting:Refer to Table 35
+ * ALC bit=“1”
+ *
+ * This operation came from example code of
+ * "ASAHI KASEI AK4642" (japanese) manual p94.
+ */
+ ak4642_write(codec, 0x05, 0x27);
+ ak4642_write(codec, 0x02, 0x05);
+ ak4642_write(codec, 0x06, 0x3c);
+ ak4642_write(codec, 0x08, 0xe1);
+ ak4642_write(codec, 0x0b, 0x00);
+ ak4642_write(codec, 0x07, 0x21);
+ ak4642_write(codec, 0x00, 0x41);
+ ak4642_write(codec, 0x10, 0x01);
+ }
+
+ return 0;
+}
+
+static void ak4642_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (is_play) {
+ /* stop headphone output */
+ ak4642_write(codec, 0x01, 0x3b);
+ ak4642_write(codec, 0x01, 0x0b);
+ ak4642_write(codec, 0x00, 0x40);
+ ak4642_write(codec, 0x0e, 0x11);
+ ak4642_write(codec, 0x0f, 0x08);
+ } else {
+ /* stop stereo input */
+ ak4642_write(codec, 0x00, 0x40);
+ ak4642_write(codec, 0x10, 0x00);
+ ak4642_write(codec, 0x07, 0x01);
+ }
+}
+
+static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct ak4642_priv *ak4642 = codec->private_data;
+
+ ak4642->sysclk = freq;
+ return 0;
+}
+
+static struct snd_soc_dai_ops ak4642_dai_ops = {
+ .startup = ak4642_dai_startup,
+ .shutdown = ak4642_dai_shutdown,
+ .set_sysclk = ak4642_dai_set_sysclk,
+};
+
+struct snd_soc_dai ak4642_dai = {
+ .name = "AK4642",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE },
+ .ops = &ak4642_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ak4642_dai);
+
+static int ak4642_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ ak4642_sync(codec);
+ return 0;
+}
+
+/*
+ * initialise the AK4642 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int ak4642_init(struct ak4642_priv *ak4642)
+{
+ struct snd_soc_codec *codec = &ak4642->codec;
+ int ret = 0;
+
+ if (ak4642_codec) {
+ dev_err(codec->dev, "Another ak4642 is registered\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->private_data = ak4642;
+ codec->name = "AK4642";
+ codec->owner = THIS_MODULE;
+ codec->read = ak4642_read_reg_cache;
+ codec->write = ak4642_write;
+ codec->dai = &ak4642_dai;
+ codec->num_dai = 1;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ codec->reg_cache_size = ARRAY_SIZE(ak4642_reg);
+ codec->reg_cache = kmemdup(ak4642_reg,
+ sizeof(ak4642_reg), GFP_KERNEL);
+
+ if (!codec->reg_cache)
+ return -ENOMEM;
+
+ ak4642_dai.dev = codec->dev;
+ ak4642_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto reg_cache_err;
+ }
+
+ ret = snd_soc_register_dai(&ak4642_dai);
+ if (ret) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ snd_soc_unregister_codec(codec);
+ goto reg_cache_err;
+ }
+
+ /*
+ * clock setting
+ *
+ * Audio I/F Format: MSB justified (ADC & DAC)
+ * BICK frequency at Master Mode: 64fs
+ * Input Master Clock Select at PLL Mode: 11.2896MHz
+ * MCKO: Enable
+ * Sampling Frequency: 44.1kHz
+ *
+ * This operation came from example code of
+ * "ASAHI KASEI AK4642" (japanese) manual p89.
+ *
+ * please fix-me
+ */
+ ak4642_write(codec, 0x01, 0x08);
+ ak4642_write(codec, 0x04, 0x4a);
+ ak4642_write(codec, 0x05, 0x27);
+ ak4642_write(codec, 0x00, 0x40);
+ ak4642_write(codec, 0x01, 0x0b);
+
+ return ret;
+
+reg_cache_err:
+ kfree(codec->reg_cache);
+ codec->reg_cache = NULL;
+
+ return ret;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int ak4642_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ak4642_priv *ak4642;
+ struct snd_soc_codec *codec;
+ int ret;
+
+ ak4642 = kzalloc(sizeof(struct ak4642_priv), GFP_KERNEL);
+ if (!ak4642)
+ return -ENOMEM;
+
+ codec = &ak4642->codec;
+ codec->dev = &i2c->dev;
+
+ i2c_set_clientdata(i2c, ak4642);
+ codec->control_data = i2c;
+
+ ret = ak4642_init(ak4642);
+ if (ret < 0)
+ printk(KERN_ERR "failed to initialise AK4642\n");
+
+ return ret;
+}
+
+static int ak4642_i2c_remove(struct i2c_client *client)
+{
+ struct ak4642_priv *ak4642 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_dai(&ak4642_dai);
+ snd_soc_unregister_codec(&ak4642->codec);
+ kfree(ak4642->codec.reg_cache);
+ kfree(ak4642);
+ ak4642_codec = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id ak4642_i2c_id[] = {
+ { "ak4642", 0 },
+ { "ak4643", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id);
+
+static struct i2c_driver ak4642_i2c_driver = {
+ .driver = {
+ .name = "AK4642 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = ak4642_i2c_probe,
+ .remove = ak4642_i2c_remove,
+ .id_table = ak4642_i2c_id,
+};
+
+#endif
+
+static int ak4642_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ int ret;
+
+ if (!ak4642_codec) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = ak4642_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "ak4642: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "ak4642: failed to register card\n");
+ goto card_err;
+ }
+
+ dev_info(&pdev->dev, "AK4642 Audio Codec %s", AK4642_VERSION);
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ return ret;
+
+}
+
+/* power down chip */
+static int ak4642_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak4642 = {
+ .probe = ak4642_probe,
+ .remove = ak4642_remove,
+ .resume = ak4642_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4642);
+
+static int __init ak4642_modinit(void)
+{
+ int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&ak4642_i2c_driver);
+#endif
+ return ret;
+
+}
+module_init(ak4642_modinit);
+
+static void __exit ak4642_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&ak4642_i2c_driver);
+#endif
+
+}
+module_exit(ak4642_exit);
+
+MODULE_DESCRIPTION("Soc AK4642 driver");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4642.h b/sound/soc/codecs/ak4642.h
new file mode 100644
index 00000000000..e476833d314
--- /dev/null
+++ b/sound/soc/codecs/ak4642.h
@@ -0,0 +1,20 @@
+/*
+ * ak4642.h -- AK4642 Soc Audio driver
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ak4535.c
+ *
+ * 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 _AK4642_H
+#define _AK4642_H
+
+extern struct snd_soc_dai ak4642_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4642;
+
+#endif
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index a32b8226c8a..ca1e24a8f12 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -806,15 +806,30 @@ static int cs4270_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
{
struct cs4270_private *cs4270 = i2c_get_clientdata(client);
struct snd_soc_codec *codec = &cs4270->codec;
- int reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
- return snd_soc_write(codec, CS4270_PWRCTL, reg);
+ return snd_soc_suspend_device(codec->dev);
}
static int cs4270_i2c_resume(struct i2c_client *client)
{
struct cs4270_private *cs4270 = i2c_get_clientdata(client);
struct snd_soc_codec *codec = &cs4270->codec;
+
+ return snd_soc_resume_device(codec->dev);
+}
+
+static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ struct snd_soc_codec *codec = cs4270_codec;
+ int reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
+
+ return snd_soc_write(codec, CS4270_PWRCTL, reg);
+}
+
+static int cs4270_soc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_codec *codec = cs4270_codec;
+ struct i2c_client *i2c_client = codec->control_data;
int reg;
/* In case the device was put to hard reset during sleep, we need to
@@ -825,7 +840,7 @@ static int cs4270_i2c_resume(struct i2c_client *client)
for (reg = CS4270_FIRSTREG; reg <= CS4270_LASTREG; reg++) {
u8 val = snd_soc_read(codec, reg);
- if (i2c_smbus_write_byte_data(client, reg, val)) {
+ if (i2c_smbus_write_byte_data(i2c_client, reg, val)) {
dev_err(codec->dev, "i2c write failed\n");
return -EIO;
}
@@ -840,6 +855,8 @@ static int cs4270_i2c_resume(struct i2c_client *client)
#else
#define cs4270_i2c_suspend NULL
#define cs4270_i2c_resume NULL
+#define cs4270_soc_suspend NULL
+#define cs4270_soc_resume NULL
#endif /* CONFIG_PM */
/*
@@ -868,7 +885,9 @@ static struct i2c_driver cs4270_i2c_driver = {
*/
struct snd_soc_codec_device soc_codec_device_cs4270 = {
.probe = cs4270_probe,
- .remove = cs4270_remove
+ .remove = cs4270_remove,
+ .suspend = cs4270_soc_suspend,
+ .resume = cs4270_soc_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_device_cs4270);
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
new file mode 100644
index 00000000000..38eac9c866e
--- /dev/null
+++ b/sound/soc/codecs/cx20442.c
@@ -0,0 +1,501 @@
+/*
+ * cx20442.c -- CX20442 ALSA Soc Audio driver
+ *
+ * Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Initially based on sound/soc/codecs/wm8400.c
+ * Copyright 2008, 2009 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/tty.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/soc-dapm.h>
+
+#include "cx20442.h"
+
+
+struct cx20442_priv {
+ struct snd_soc_codec codec;
+ u8 reg_cache[1];
+};
+
+#define CX20442_PM 0x0
+
+#define CX20442_TELIN 0
+#define CX20442_TELOUT 1
+#define CX20442_MIC 2
+#define CX20442_SPKOUT 3
+#define CX20442_AGC 4
+
+static const struct snd_soc_dapm_widget cx20442_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("TELOUT"),
+ SND_SOC_DAPM_OUTPUT("SPKOUT"),
+ SND_SOC_DAPM_OUTPUT("AGCOUT"),
+
+ SND_SOC_DAPM_MIXER("SPKOUT Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("TELOUT Amp", CX20442_PM, CX20442_TELOUT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPKOUT Amp", CX20442_PM, CX20442_SPKOUT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPKOUT AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MICBIAS("TELIN Bias", CX20442_PM, CX20442_TELIN, 0),
+ SND_SOC_DAPM_MICBIAS("MIC Bias", CX20442_PM, CX20442_MIC, 0),
+
+ SND_SOC_DAPM_PGA("MIC AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("TELIN"),
+ SND_SOC_DAPM_INPUT("MIC"),
+ SND_SOC_DAPM_INPUT("AGCIN"),
+};
+
+static const struct snd_soc_dapm_route cx20442_audio_map[] = {
+ {"TELOUT", NULL, "TELOUT Amp"},
+
+ {"SPKOUT", NULL, "SPKOUT Mixer"},
+ {"SPKOUT Mixer", NULL, "SPKOUT Amp"},
+
+ {"TELOUT Amp", NULL, "DAC"},
+ {"SPKOUT Amp", NULL, "DAC"},
+
+ {"SPKOUT Mixer", NULL, "SPKOUT AGC"},
+ {"SPKOUT AGC", NULL, "AGCIN"},
+
+ {"AGCOUT", NULL, "MIC AGC"},
+ {"MIC AGC", NULL, "MIC"},
+
+ {"MIC Bias", NULL, "MIC"},
+ {"Input Mixer", NULL, "MIC Bias"},
+
+ {"TELIN Bias", NULL, "TELIN"},
+ {"Input Mixer", NULL, "TELIN Bias"},
+
+ {"ADC", NULL, "Input Mixer"},
+};
+
+static int cx20442_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, cx20442_dapm_widgets,
+ ARRAY_SIZE(cx20442_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, cx20442_audio_map,
+ ARRAY_SIZE(cx20442_audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 *reg_cache = codec->reg_cache;
+
+ if (reg >= codec->reg_cache_size)
+ return -EINVAL;
+
+ return reg_cache[reg];
+}
+
+enum v253_vls {
+ V253_VLS_NONE = 0,
+ V253_VLS_T,
+ V253_VLS_L,
+ V253_VLS_LT,
+ V253_VLS_S,
+ V253_VLS_ST,
+ V253_VLS_M,
+ V253_VLS_MST,
+ V253_VLS_S1,
+ V253_VLS_S1T,
+ V253_VLS_MS1T,
+ V253_VLS_M1,
+ V253_VLS_M1ST,
+ V253_VLS_M1S1T,
+ V253_VLS_H,
+ V253_VLS_HT,
+ V253_VLS_MS,
+ V253_VLS_MS1,
+ V253_VLS_M1S,
+ V253_VLS_M1S1,
+ V253_VLS_TEST,
+};
+
+static int cx20442_pm_to_v253_vls(u8 value)
+{
+ switch (value & ~(1 << CX20442_AGC)) {
+ case 0:
+ return V253_VLS_T;
+ case (1 << CX20442_SPKOUT):
+ case (1 << CX20442_MIC):
+ case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC):
+ return V253_VLS_M1S1;
+ case (1 << CX20442_TELOUT):
+ case (1 << CX20442_TELIN):
+ case (1 << CX20442_TELOUT) | (1 << CX20442_TELIN):
+ return V253_VLS_L;
+ case (1 << CX20442_TELOUT) | (1 << CX20442_MIC):
+ return V253_VLS_NONE;
+ }
+ return -EINVAL;
+}
+static int cx20442_pm_to_v253_vsp(u8 value)
+{
+ switch (value & ~(1 << CX20442_AGC)) {
+ case (1 << CX20442_SPKOUT):
+ case (1 << CX20442_MIC):
+ case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC):
+ return (bool)(value & (1 << CX20442_AGC));
+ }
+ return (value & (1 << CX20442_AGC)) ? -EINVAL : 0;
+}
+
+static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 *reg_cache = codec->reg_cache;
+ int vls, vsp, old, len;
+ char buf[18];
+
+ if (reg >= codec->reg_cache_size)
+ return -EINVAL;
+
+ /* hw_write and control_data pointers required for talking to the modem
+ * are expected to be set by the line discipline initialization code */
+ if (!codec->hw_write || !codec->control_data)
+ return -EIO;
+
+ old = reg_cache[reg];
+ reg_cache[reg] = value;
+
+ vls = cx20442_pm_to_v253_vls(value);
+ if (vls < 0)
+ return vls;
+
+ vsp = cx20442_pm_to_v253_vsp(value);
+ if (vsp < 0)
+ return vsp;
+
+ if ((vls == V253_VLS_T) ||
+ (vls == cx20442_pm_to_v253_vls(old))) {
+ if (vsp == cx20442_pm_to_v253_vsp(old))
+ return 0;
+ len = snprintf(buf, ARRAY_SIZE(buf), "at+vsp=%d\r", vsp);
+ } else if (vsp == cx20442_pm_to_v253_vsp(old))
+ len = snprintf(buf, ARRAY_SIZE(buf), "at+vls=%d\r", vls);
+ else
+ len = snprintf(buf, ARRAY_SIZE(buf),
+ "at+vls=%d;+vsp=%d\r", vls, vsp);
+
+ if (unlikely(len > (ARRAY_SIZE(buf) - 1)))
+ return -ENOMEM;
+
+ dev_dbg(codec->dev, "%s: %s\n", __func__, buf);
+ if (codec->hw_write(codec->control_data, buf, len) != len)
+ return -EIO;
+
+ return 0;
+}
+
+
+/* Moved up here as line discipline referres it during initialization */
+static struct snd_soc_codec *cx20442_codec;
+
+
+/*
+ * Line discpline related code
+ *
+ * Any of the callback functions below can be used in two ways:
+ * 1) registerd by a machine driver as one of line discipline operations,
+ * 2) called from a machine's provided line discipline callback function
+ * in case when extra machine specific code must be run as well.
+ */
+
+/* Modem init: echo off, digital speaker off, quiet off, voice mode */
+static const char *v253_init = "ate0m0q0+fclass=8\r";
+
+/* Line discipline .open() */
+static int v253_open(struct tty_struct *tty)
+{
+ struct snd_soc_codec *codec = cx20442_codec;
+ int ret, len = strlen(v253_init);
+
+ /* Doesn't make sense without write callback */
+ if (!tty->ops->write)
+ return -EINVAL;
+
+ /* Pass the codec structure address for use by other ldisc callbacks */
+ tty->disc_data = codec;
+
+ if (tty->ops->write(tty, v253_init, len) != len) {
+ ret = -EIO;
+ goto err;
+ }
+ /* Actual setup will be performed after the modem responds. */
+ return 0;
+err:
+ tty->disc_data = NULL;
+ return ret;
+}
+
+/* Line discipline .close() */
+static void v253_close(struct tty_struct *tty)
+{
+ struct snd_soc_codec *codec = tty->disc_data;
+
+ tty->disc_data = NULL;
+
+ if (!codec)
+ return;
+
+ /* Prevent the codec driver from further accessing the modem */
+ codec->hw_write = NULL;
+ codec->control_data = NULL;
+ codec->pop_time = 0;
+}
+
+/* Line discipline .hangup() */
+static int v253_hangup(struct tty_struct *tty)
+{
+ v253_close(tty);
+ return 0;
+}
+
+/* Line discipline .receive_buf() */
+static void v253_receive(struct tty_struct *tty,
+ const unsigned char *cp, char *fp, int count)
+{
+ struct snd_soc_codec *codec = tty->disc_data;
+
+ if (!codec)
+ return;
+
+ if (!codec->control_data) {
+ /* First modem response, complete setup procedure */
+
+ /* Set up codec driver access to modem controls */
+ codec->control_data = tty;
+ codec->hw_write = (hw_write_t)tty->ops->write;
+ codec->pop_time = 1;
+ }
+}
+
+/* Line discipline .write_wakeup() */
+static void v253_wakeup(struct tty_struct *tty)
+{
+}
+
+struct tty_ldisc_ops v253_ops = {
+ .magic = TTY_LDISC_MAGIC,
+ .name = "cx20442",
+ .owner = THIS_MODULE,
+ .open = v253_open,
+ .close = v253_close,
+ .hangup = v253_hangup,
+ .receive_buf = v253_receive,
+ .write_wakeup = v253_wakeup,
+};
+EXPORT_SYMBOL_GPL(v253_ops);
+
+
+/*
+ * Codec DAI
+ */
+
+struct snd_soc_dai cx20442_dai = {
+ .name = "CX20442",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+EXPORT_SYMBOL_GPL(cx20442_dai);
+
+static int cx20442_codec_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret;
+
+ if (!cx20442_codec) {
+ dev_err(&pdev->dev, "cx20442 not yet discovered\n");
+ return -ENODEV;
+ }
+ codec = cx20442_codec;
+
+ socdev->card->codec = codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ cx20442_add_widgets(codec);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ return ret;
+}
+
+/* power down chip */
+static int cx20442_codec_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device cx20442_codec_dev = {
+ .probe = cx20442_codec_probe,
+ .remove = cx20442_codec_remove,
+};
+EXPORT_SYMBOL_GPL(cx20442_codec_dev);
+
+static int cx20442_register(struct cx20442_priv *cx20442)
+{
+ struct snd_soc_codec *codec = &cx20442->codec;
+ int ret;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->name = "CX20442";
+ codec->owner = THIS_MODULE;
+ codec->private_data = cx20442;
+
+ codec->dai = &cx20442_dai;
+ codec->num_dai = 1;
+
+ codec->reg_cache = &cx20442->reg_cache;
+ codec->reg_cache_size = ARRAY_SIZE(cx20442->reg_cache);
+ codec->read = cx20442_read_reg_cache;
+ codec->write = cx20442_write;
+
+ codec->bias_level = SND_SOC_BIAS_OFF;
+
+ cx20442_dai.dev = codec->dev;
+
+ cx20442_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dai(&cx20442_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ goto err_codec;
+ }
+
+ return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ cx20442_codec = NULL;
+ kfree(cx20442);
+ return ret;
+}
+
+static void cx20442_unregister(struct cx20442_priv *cx20442)
+{
+ snd_soc_unregister_dai(&cx20442_dai);
+ snd_soc_unregister_codec(&cx20442->codec);
+
+ cx20442_codec = NULL;
+ kfree(cx20442);
+}
+
+static int cx20442_platform_probe(struct platform_device *pdev)
+{
+ struct cx20442_priv *cx20442;
+ struct snd_soc_codec *codec;
+
+ cx20442 = kzalloc(sizeof(struct cx20442_priv), GFP_KERNEL);
+ if (cx20442 == NULL)
+ return -ENOMEM;
+
+ codec = &cx20442->codec;
+
+ codec->control_data = NULL;
+ codec->hw_write = NULL;
+ codec->pop_time = 0;
+
+ codec->dev = &pdev->dev;
+ platform_set_drvdata(pdev, cx20442);
+
+ return cx20442_register(cx20442);
+}
+
+static int __exit cx20442_platform_remove(struct platform_device *pdev)
+{
+ struct cx20442_priv *cx20442 = platform_get_drvdata(pdev);
+
+ cx20442_unregister(cx20442);
+ return 0;
+}
+
+static struct platform_driver cx20442_platform_driver = {
+ .driver = {
+ .name = "cx20442",
+ .owner = THIS_MODULE,
+ },
+ .probe = cx20442_platform_probe,
+ .remove = __exit_p(cx20442_platform_remove),
+};
+
+static int __init cx20442_init(void)
+{
+ return platform_driver_register(&cx20442_platform_driver);
+}
+module_init(cx20442_init);
+
+static void __exit cx20442_exit(void)
+{
+ platform_driver_unregister(&cx20442_platform_driver);
+}
+module_exit(cx20442_exit);
+
+MODULE_DESCRIPTION("ASoC CX20442-11 voice modem codec driver");
+MODULE_AUTHOR("Janusz Krzysztofik");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:cx20442");
diff --git a/sound/soc/codecs/cx20442.h b/sound/soc/codecs/cx20442.h
new file mode 100644
index 00000000000..688a5eb62e1
--- /dev/null
+++ b/sound/soc/codecs/cx20442.h
@@ -0,0 +1,20 @@
+/*
+ * cx20442.h -- audio driver for CX20442
+ *
+ * Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * 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 _CX20442_CODEC_H
+#define _CX20442_CODEC_H
+
+extern struct snd_soc_dai cx20442_dai;
+extern struct snd_soc_codec_device cx20442_codec_dev;
+extern struct tty_ldisc_ops v253_ops;
+
+#endif
diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c
new file mode 100644
index 00000000000..9e7e964a5fa
--- /dev/null
+++ b/sound/soc/codecs/max9877.c
@@ -0,0 +1,308 @@
+/*
+ * max9877.c -- amp driver for max9877
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max9877.h"
+
+static struct i2c_client *i2c;
+
+static u8 max9877_regs[5] = { 0x40, 0x00, 0x00, 0x00, 0x49 };
+
+static void max9877_write_regs(void)
+{
+ unsigned int i;
+ u8 data[6];
+
+ data[0] = MAX9877_INPUT_MODE;
+ for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
+ data[i + 1] = max9877_regs[i];
+
+ if (i2c_master_send(i2c, data, 6) != 6)
+ dev_err(&i2c->dev, "i2c write failed\n");
+}
+
+static int max9877_get_reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = mc->max;
+ unsigned int invert = mc->invert;
+
+ ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
+
+ if (invert)
+ ucontrol->value.integer.value[0] =
+ mask - ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int max9877_set_reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = mc->max;
+ unsigned int invert = mc->invert;
+ unsigned int val = (ucontrol->value.integer.value[0] & mask);
+
+ if (invert)
+ val = mask - val;
+
+ if (((max9877_regs[reg] >> shift) & mask) == val)
+ return 0;
+
+ max9877_regs[reg] &= ~(mask << shift);
+ max9877_regs[reg] |= val << shift;
+ max9877_write_regs();
+
+ return 1;
+}
+
+static int max9877_get_2reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = mc->max;
+
+ ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
+ ucontrol->value.integer.value[1] = (max9877_regs[reg2] >> shift) & mask;
+
+ return 0;
+}
+
+static int max9877_set_2reg(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = mc->max;
+ unsigned int val = (ucontrol->value.integer.value[0] & mask);
+ unsigned int val2 = (ucontrol->value.integer.value[1] & mask);
+ unsigned int change = 1;
+
+ if (((max9877_regs[reg] >> shift) & mask) == val)
+ change = 0;
+
+ if (((max9877_regs[reg2] >> shift) & mask) == val2)
+ change = 0;
+
+ if (change) {
+ max9877_regs[reg] &= ~(mask << shift);
+ max9877_regs[reg] |= val << shift;
+ max9877_regs[reg2] &= ~(mask << shift);
+ max9877_regs[reg2] |= val2 << shift;
+ max9877_write_regs();
+ }
+
+ return change;
+}
+
+static int max9877_get_out_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 value = max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK;
+
+ if (value)
+ value -= 1;
+
+ ucontrol->value.integer.value[0] = value;
+ return 0;
+}
+
+static int max9877_set_out_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 value = ucontrol->value.integer.value[0];
+
+ value += 1;
+
+ if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK) == value)
+ return 0;
+
+ max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OUTMODE_MASK;
+ max9877_regs[MAX9877_OUTPUT_MODE] |= value;
+ max9877_write_regs();
+ return 1;
+}
+
+static int max9877_get_osc_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 value = (max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK);
+
+ value = value >> MAX9877_OSC_OFFSET;
+
+ ucontrol->value.integer.value[0] = value;
+ return 0;
+}
+
+static int max9877_set_osc_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 value = ucontrol->value.integer.value[0];
+
+ value = value << MAX9877_OSC_OFFSET;
+ if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK) == value)
+ return 0;
+
+ max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OSC_MASK;
+ max9877_regs[MAX9877_OUTPUT_MODE] |= value;
+ max9877_write_regs();
+ return 1;
+}
+
+static const unsigned int max9877_pgain_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 1, TLV_DB_SCALE_ITEM(0, 900, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0),
+};
+
+static const unsigned int max9877_output_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
+ 8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
+ 16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
+ 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0),
+};
+
+static const char *max9877_out_mode[] = {
+ "INA -> SPK",
+ "INA -> HP",
+ "INA -> SPK and HP",
+ "INB -> SPK",
+ "INB -> HP",
+ "INB -> SPK and HP",
+ "INA + INB -> SPK",
+ "INA + INB -> HP",
+ "INA + INB -> SPK and HP",
+};
+
+static const char *max9877_osc_mode[] = {
+ "1176KHz",
+ "1100KHz",
+ "700KHz",
+};
+
+static const struct soc_enum max9877_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_out_mode), max9877_out_mode),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode),
+};
+
+static const struct snd_kcontrol_new max9877_controls[] = {
+ SOC_SINGLE_EXT_TLV("MAX9877 PGAINA Playback Volume",
+ MAX9877_INPUT_MODE, 0, 2, 0,
+ max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
+ SOC_SINGLE_EXT_TLV("MAX9877 PGAINB Playback Volume",
+ MAX9877_INPUT_MODE, 2, 2, 0,
+ max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
+ SOC_SINGLE_EXT_TLV("MAX9877 Amp Speaker Playback Volume",
+ MAX9877_SPK_VOLUME, 0, 31, 0,
+ max9877_get_reg, max9877_set_reg, max9877_output_tlv),
+ SOC_DOUBLE_R_EXT_TLV("MAX9877 Amp HP Playback Volume",
+ MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0,
+ max9877_get_2reg, max9877_set_2reg, max9877_output_tlv),
+ SOC_SINGLE_EXT("MAX9877 INB Stereo Switch",
+ MAX9877_INPUT_MODE, 4, 1, 1,
+ max9877_get_reg, max9877_set_reg),
+ SOC_SINGLE_EXT("MAX9877 INA Stereo Switch",
+ MAX9877_INPUT_MODE, 5, 1, 1,
+ max9877_get_reg, max9877_set_reg),
+ SOC_SINGLE_EXT("MAX9877 Zero-crossing detection Switch",
+ MAX9877_INPUT_MODE, 6, 1, 0,
+ max9877_get_reg, max9877_set_reg),
+ SOC_SINGLE_EXT("MAX9877 Bypass Mode Switch",
+ MAX9877_OUTPUT_MODE, 6, 1, 0,
+ max9877_get_reg, max9877_set_reg),
+ SOC_SINGLE_EXT("MAX9877 Shutdown Mode Switch",
+ MAX9877_OUTPUT_MODE, 7, 1, 1,
+ max9877_get_reg, max9877_set_reg),
+ SOC_ENUM_EXT("MAX9877 Output Mode", max9877_enum[0],
+ max9877_get_out_mode, max9877_set_out_mode),
+ SOC_ENUM_EXT("MAX9877 Oscillator Mode", max9877_enum[1],
+ max9877_get_osc_mode, max9877_set_osc_mode),
+};
+
+/* This function is called from ASoC machine driver */
+int max9877_add_controls(struct snd_soc_codec *codec)
+{
+ return snd_soc_add_controls(codec, max9877_controls,
+ ARRAY_SIZE(max9877_controls));
+}
+EXPORT_SYMBOL_GPL(max9877_add_controls);
+
+static int __devinit max9877_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ i2c = client;
+
+ max9877_write_regs();
+
+ return 0;
+}
+
+static __devexit int max9877_i2c_remove(struct i2c_client *client)
+{
+ i2c = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id max9877_i2c_id[] = {
+ { "max9877", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max9877_i2c_id);
+
+static struct i2c_driver max9877_i2c_driver = {
+ .driver = {
+ .name = "max9877",
+ .owner = THIS_MODULE,
+ },
+ .probe = max9877_i2c_probe,
+ .remove = __devexit_p(max9877_i2c_remove),
+ .id_table = max9877_i2c_id,
+};
+
+static int __init max9877_init(void)
+{
+ return i2c_add_driver(&max9877_i2c_driver);
+}
+module_init(max9877_init);
+
+static void __exit max9877_exit(void)
+{
+ i2c_del_driver(&max9877_i2c_driver);
+}
+module_exit(max9877_exit);
+
+MODULE_DESCRIPTION("ASoC MAX9877 amp driver");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max9877.h b/sound/soc/codecs/max9877.h
new file mode 100644
index 00000000000..6da72290ac5
--- /dev/null
+++ b/sound/soc/codecs/max9877.h
@@ -0,0 +1,37 @@
+/*
+ * max9877.h -- amp driver for max9877
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef _MAX9877_H
+#define _MAX9877_H
+
+#define MAX9877_INPUT_MODE 0x00
+#define MAX9877_SPK_VOLUME 0x01
+#define MAX9877_HPL_VOLUME 0x02
+#define MAX9877_HPR_VOLUME 0x03
+#define MAX9877_OUTPUT_MODE 0x04
+
+/* MAX9877_INPUT_MODE */
+#define MAX9877_INB (1 << 4)
+#define MAX9877_INA (1 << 5)
+#define MAX9877_ZCD (1 << 6)
+
+/* MAX9877_OUTPUT_MODE */
+#define MAX9877_OUTMODE_MASK (15 << 0)
+#define MAX9877_OSC_MASK (3 << 4)
+#define MAX9877_OSC_OFFSET 4
+#define MAX9877_BYPASS (1 << 6)
+#define MAX9877_SHDN (1 << 7)
+
+extern int max9877_add_controls(struct snd_soc_codec *codec);
+
+#endif
diff --git a/sound/soc/codecs/spdif_transciever.c b/sound/soc/codecs/spdif_transciever.c
index 218b33adad9..a6319114105 100644
--- a/sound/soc/codecs/spdif_transciever.c
+++ b/sound/soc/codecs/spdif_transciever.c
@@ -21,6 +21,8 @@
#include "spdif_transciever.h"
+MODULE_LICENSE("GPL");
+
#define STUB_RATES SNDRV_PCM_RATE_8000_96000
#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE
@@ -34,6 +36,7 @@ struct snd_soc_dai dit_stub_dai = {
.formats = STUB_FORMATS,
},
};
+EXPORT_SYMBOL_GPL(dit_stub_dai);
static int spdif_dit_probe(struct platform_device *pdev)
{
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 8ad4b7b3e3b..befc6488c39 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -149,7 +149,7 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return 0;
}
- if (reg / 2 > ARRAY_SIZE(stac9766_reg))
+ if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
return -EIO;
soc_ac97_ops.write(codec->ac97, reg, val);
@@ -168,7 +168,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return val;
}
- if (reg / 2 > ARRAY_SIZE(stac9766_reg))
+ if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
return -EIO;
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index cb0d1bf34b5..3395cf945d5 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -53,6 +53,7 @@
/* codec private data */
struct aic3x_priv {
+ struct snd_soc_codec codec;
unsigned int sysclk;
int master;
};
@@ -145,8 +146,8 @@ static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
u8 *value)
{
*value = reg & 0xff;
- if (codec->hw_read(codec->control_data, value, 1) != 1)
- return -EIO;
+
+ value[0] = i2c_smbus_read_byte_data(codec->control_data, value[0]);
aic3x_write_reg_cache(codec, reg, *value);
return 0;
@@ -1156,11 +1157,13 @@ static int aic3x_resume(struct platform_device *pdev)
* initialise the AIC3X driver
* register the mixer and dsp interfaces with the kernel
*/
-static int aic3x_init(struct snd_soc_device *socdev)
+static int aic3x_init(struct snd_soc_codec *codec)
{
- struct snd_soc_codec *codec = socdev->card->codec;
- struct aic3x_setup_data *setup = socdev->codec_data;
- int reg, ret = 0;
+ int reg;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
codec->name = "tlv320aic3x";
codec->owner = THIS_MODULE;
@@ -1177,13 +1180,6 @@ static int aic3x_init(struct snd_soc_device *socdev)
aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);
aic3x_write(codec, AIC3X_RESET, SOFT_RESET);
- /* register pcms */
- ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
- if (ret < 0) {
- printk(KERN_ERR "aic3x: failed to create pcms\n");
- goto pcm_err;
- }
-
/* DAC default volume and mute */
aic3x_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON);
aic3x_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON);
@@ -1250,30 +1246,51 @@ static int aic3x_init(struct snd_soc_device *socdev)
/* off, with power on */
aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- /* setup GPIO functions */
- aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4);
- aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4);
+ return 0;
+}
- snd_soc_add_controls(codec, aic3x_snd_controls,
- ARRAY_SIZE(aic3x_snd_controls));
- aic3x_add_widgets(codec);
- ret = snd_soc_init_card(socdev);
+static struct snd_soc_codec *aic3x_codec;
+
+static int aic3x_register(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ ret = aic3x_init(codec);
if (ret < 0) {
- printk(KERN_ERR "aic3x: failed to register card\n");
- goto card_err;
+ dev_err(codec->dev, "Failed to initialise device\n");
+ return ret;
}
- return ret;
+ aic3x_codec = codec;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
-pcm_err:
- kfree(codec->reg_cache);
- return ret;
+ ret = snd_soc_register_codec(codec);
+ if (ret) {
+ dev_err(codec->dev, "Failed to register codec\n");
+ return ret;
+ }
+
+ ret = snd_soc_register_dai(&aic3x_dai);
+ if (ret) {
+ dev_err(codec->dev, "Failed to register dai\n");
+ snd_soc_unregister_codec(codec);
+ return ret;
+ }
+
+ return 0;
}
-static struct snd_soc_device *aic3x_socdev;
+static int aic3x_unregister(struct aic3x_priv *aic3x)
+{
+ aic3x_set_bias_level(&aic3x->codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_unregister_dai(&aic3x_dai);
+ snd_soc_unregister_codec(&aic3x->codec);
+
+ kfree(aic3x);
+ aic3x_codec = NULL;
+
+ return 0;
+}
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
/*
@@ -1288,28 +1305,36 @@ static struct snd_soc_device *aic3x_socdev;
static int aic3x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct snd_soc_device *socdev = aic3x_socdev;
- struct snd_soc_codec *codec = socdev->card->codec;
- int ret;
+ struct snd_soc_codec *codec;
+ struct aic3x_priv *aic3x;
- i2c_set_clientdata(i2c, codec);
+ aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
+ if (aic3x == NULL) {
+ dev_err(&i2c->dev, "failed to create private data\n");
+ return -ENOMEM;
+ }
+
+ codec = &aic3x->codec;
+ codec->dev = &i2c->dev;
+ codec->private_data = aic3x;
codec->control_data = i2c;
+ codec->hw_write = (hw_write_t) i2c_master_send;
- ret = aic3x_init(socdev);
- if (ret < 0)
- printk(KERN_ERR "aic3x: failed to initialise AIC3X\n");
- return ret;
+ i2c_set_clientdata(i2c, aic3x);
+
+ return aic3x_register(codec);
}
static int aic3x_i2c_remove(struct i2c_client *client)
{
- struct snd_soc_codec *codec = i2c_get_clientdata(client);
- kfree(codec->reg_cache);
- return 0;
+ struct aic3x_priv *aic3x = i2c_get_clientdata(client);
+
+ return aic3x_unregister(aic3x);
}
static const struct i2c_device_id aic3x_i2c_id[] = {
{ "tlv320aic3x", 0 },
+ { "tlv320aic33", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
@@ -1320,56 +1345,28 @@ static struct i2c_driver aic3x_i2c_driver = {
.name = "aic3x I2C Codec",
.owner = THIS_MODULE,
},
- .probe = aic3x_i2c_probe,
+ .probe = aic3x_i2c_probe,
.remove = aic3x_i2c_remove,
.id_table = aic3x_i2c_id,
};
-static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len)
+static inline void aic3x_i2c_init(void)
{
- value[0] = i2c_smbus_read_byte_data(client, value[0]);
- return (len == 1);
-}
-
-static int aic3x_add_i2c_device(struct platform_device *pdev,
- const struct aic3x_setup_data *setup)
-{
- struct i2c_board_info info;
- struct i2c_adapter *adapter;
- struct i2c_client *client;
int ret;
ret = i2c_add_driver(&aic3x_i2c_driver);
- if (ret != 0) {
- dev_err(&pdev->dev, "can't add i2c driver\n");
- return ret;
- }
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = setup->i2c_address;
- strlcpy(info.type, "tlv320aic3x", I2C_NAME_SIZE);
-
- adapter = i2c_get_adapter(setup->i2c_bus);
- if (!adapter) {
- dev_err(&pdev->dev, "can't get i2c adapter %d\n",
- setup->i2c_bus);
- goto err_driver;
- }
-
- client = i2c_new_device(adapter, &info);
- i2c_put_adapter(adapter);
- if (!client) {
- dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
- (unsigned int)info.addr);
- goto err_driver;
- }
-
- return 0;
+ if (ret)
+ printk(KERN_ERR "%s: error regsitering i2c driver, %d\n",
+ __func__, ret);
+}
-err_driver:
+static inline void aic3x_i2c_exit(void)
+{
i2c_del_driver(&aic3x_i2c_driver);
- return -ENODEV;
}
+#else
+static inline void aic3x_i2c_init(void) { }
+static inline void aic3x_i2c_exit(void) { }
#endif
static int aic3x_probe(struct platform_device *pdev)
@@ -1377,43 +1374,51 @@ static int aic3x_probe(struct platform_device *pdev)
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct aic3x_setup_data *setup;
struct snd_soc_codec *codec;
- struct aic3x_priv *aic3x;
int ret = 0;
- printk(KERN_INFO "AIC3X Audio Codec %s\n", AIC3X_VERSION);
+ codec = aic3x_codec;
+ if (!codec) {
+ dev_err(&pdev->dev, "Codec not registered\n");
+ return -ENODEV;
+ }
+ socdev->card->codec = codec;
setup = socdev->codec_data;
- codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (codec == NULL)
- return -ENOMEM;
- aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
- if (aic3x == NULL) {
- kfree(codec);
- return -ENOMEM;
+ if (setup) {
+ /* setup GPIO functions */
+ aic3x_write(codec, AIC3X_GPIO1_REG,
+ (setup->gpio_func[0] & 0xf) << 4);
+ aic3x_write(codec, AIC3X_GPIO2_REG,
+ (setup->gpio_func[1] & 0xf) << 4);
}
- codec->private_data = aic3x;
- socdev->card->codec = codec;
- mutex_init(&codec->mutex);
- INIT_LIST_HEAD(&codec->dapm_widgets);
- INIT_LIST_HEAD(&codec->dapm_paths);
-
- aic3x_socdev = socdev;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- if (setup->i2c_address) {
- codec->hw_write = (hw_write_t) i2c_master_send;
- codec->hw_read = (hw_read_t) aic3x_i2c_read;
- ret = aic3x_add_i2c_device(pdev, setup);
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "aic3x: failed to create pcms\n");
+ goto pcm_err;
}
-#else
- /* Add other interfaces here */
-#endif
- if (ret != 0) {
- kfree(codec->private_data);
- kfree(codec);
+ snd_soc_add_controls(codec, aic3x_snd_controls,
+ ARRAY_SIZE(aic3x_snd_controls));
+
+ aic3x_add_widgets(codec);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "aic3x: failed to register card\n");
+ goto card_err;
}
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+pcm_err:
+ kfree(codec->reg_cache);
return ret;
}
@@ -1428,12 +1433,8 @@ static int aic3x_remove(struct platform_device *pdev)
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_unregister_device(codec->control_data);
- i2c_del_driver(&aic3x_i2c_driver);
-#endif
- kfree(codec->private_data);
- kfree(codec);
+
+ kfree(codec->reg_cache);
return 0;
}
@@ -1448,13 +1449,15 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_aic3x);
static int __init aic3x_modinit(void)
{
- return snd_soc_register_dai(&aic3x_dai);
+ aic3x_i2c_init();
+
+ return 0;
}
module_init(aic3x_modinit);
static void __exit aic3x_exit(void)
{
- snd_soc_unregister_dai(&aic3x_dai);
+ aic3x_i2c_exit();
}
module_exit(aic3x_exit);
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index ac827e578c4..9af1c886213 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -282,8 +282,6 @@ int aic3x_headset_detected(struct snd_soc_codec *codec);
int aic3x_button_pressed(struct snd_soc_codec *codec);
struct aic3x_setup_data {
- int i2c_bus;
- unsigned short i2c_address;
unsigned int gpio_func[2];
};
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 4dbb853eef5..4df7c6c61c7 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -225,55 +225,11 @@ static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute)
return;
if (mute) {
- /* Bypass the reg_cache and mute the volumes
- * Headset mute is done in it's own event handler
- * Things to mute: Earpiece, PreDrivL/R, CarkitL/R
- */
- reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL);
- twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
- reg_val & (~TWL4030_EAR_GAIN),
- TWL4030_REG_EAR_CTL);
-
- reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL);
- twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
- reg_val & (~TWL4030_PREDL_GAIN),
- TWL4030_REG_PREDL_CTL);
- reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL);
- twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
- reg_val & (~TWL4030_PREDR_GAIN),
- TWL4030_REG_PREDL_CTL);
-
- reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL);
- twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
- reg_val & (~TWL4030_PRECKL_GAIN),
- TWL4030_REG_PRECKL_CTL);
- reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL);
- twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
- reg_val & (~TWL4030_PRECKR_GAIN),
- TWL4030_REG_PRECKR_CTL);
-
/* Disable PLL */
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
reg_val &= ~TWL4030_APLL_EN;
twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
} else {
- /* Restore the volumes
- * Headset mute is done in it's own event handler
- * Things to restore: Earpiece, PreDrivL/R, CarkitL/R
- */
- twl4030_write(codec, TWL4030_REG_EAR_CTL,
- twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL));
-
- twl4030_write(codec, TWL4030_REG_PREDL_CTL,
- twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL));
- twl4030_write(codec, TWL4030_REG_PREDR_CTL,
- twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL));
-
- twl4030_write(codec, TWL4030_REG_PRECKL_CTL,
- twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL));
- twl4030_write(codec, TWL4030_REG_PRECKR_CTL,
- twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL));
-
/* Enable PLL */
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
reg_val |= TWL4030_APLL_EN;
@@ -443,16 +399,20 @@ SOC_DAPM_ENUM("Route", twl4030_vibrapath_enum);
/* Left analog microphone selection */
static const struct snd_kcontrol_new twl4030_dapm_analoglmic_controls[] = {
- SOC_DAPM_SINGLE("Main mic", TWL4030_REG_ANAMICL, 0, 1, 0),
- SOC_DAPM_SINGLE("Headset mic", TWL4030_REG_ANAMICL, 1, 1, 0),
- SOC_DAPM_SINGLE("AUXL", TWL4030_REG_ANAMICL, 2, 1, 0),
- SOC_DAPM_SINGLE("Carkit mic", TWL4030_REG_ANAMICL, 3, 1, 0),
+ SOC_DAPM_SINGLE("Main Mic Capture Switch",
+ TWL4030_REG_ANAMICL, 0, 1, 0),
+ SOC_DAPM_SINGLE("Headset Mic Capture Switch",
+ TWL4030_REG_ANAMICL, 1, 1, 0),
+ SOC_DAPM_SINGLE("AUXL Capture Switch",
+ TWL4030_REG_ANAMICL, 2, 1, 0),
+ SOC_DAPM_SINGLE("Carkit Mic Capture Switch",
+ TWL4030_REG_ANAMICL, 3, 1, 0),
};
/* Right analog microphone selection */
static const struct snd_kcontrol_new twl4030_dapm_analogrmic_controls[] = {
- SOC_DAPM_SINGLE("Sub mic", TWL4030_REG_ANAMICR, 0, 1, 0),
- SOC_DAPM_SINGLE("AUXR", TWL4030_REG_ANAMICR, 2, 1, 0),
+ SOC_DAPM_SINGLE("Sub Mic Capture Switch", TWL4030_REG_ANAMICR, 0, 1, 0),
+ SOC_DAPM_SINGLE("AUXR Capture Switch", TWL4030_REG_ANAMICR, 2, 1, 0),
};
/* TX1 L/R Analog/Digital microphone selection */
@@ -560,6 +520,41 @@ static int micpath_event(struct snd_soc_dapm_widget *w,
return 0;
}
+/*
+ * Output PGA builder:
+ * Handle the muting and unmuting of the given output (turning off the
+ * amplifier associated with the output pin)
+ * On mute bypass the reg_cache and mute the volume
+ * On unmute: restore the register content
+ * Outputs handled in this way: Earpiece, PreDrivL/R, CarkitL/R
+ */
+#define TWL4030_OUTPUT_PGA(pin_name, reg, mask) \
+static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \
+ struct snd_kcontrol *kcontrol, int event) \
+{ \
+ u8 reg_val; \
+ \
+ switch (event) { \
+ case SND_SOC_DAPM_POST_PMU: \
+ twl4030_write(w->codec, reg, \
+ twl4030_read_reg_cache(w->codec, reg)); \
+ break; \
+ case SND_SOC_DAPM_POST_PMD: \
+ reg_val = twl4030_read_reg_cache(w->codec, reg); \
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, \
+ reg_val & (~mask), \
+ reg); \
+ break; \
+ } \
+ return 0; \
+}
+
+TWL4030_OUTPUT_PGA(earpiece, TWL4030_REG_EAR_CTL, TWL4030_EAR_GAIN);
+TWL4030_OUTPUT_PGA(predrivel, TWL4030_REG_PREDL_CTL, TWL4030_PREDL_GAIN);
+TWL4030_OUTPUT_PGA(predriver, TWL4030_REG_PREDR_CTL, TWL4030_PREDR_GAIN);
+TWL4030_OUTPUT_PGA(carkitl, TWL4030_REG_PRECKL_CTL, TWL4030_PRECKL_GAIN);
+TWL4030_OUTPUT_PGA(carkitr, TWL4030_REG_PRECKR_CTL, TWL4030_PRECKR_GAIN);
+
static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp)
{
unsigned char hs_ctl;
@@ -620,6 +615,9 @@ static int handsfreerpga_event(struct snd_soc_dapm_widget *w,
static void headset_ramp(struct snd_soc_codec *codec, int ramp)
{
+ struct snd_soc_device *socdev = codec->socdev;
+ struct twl4030_setup_data *setup = socdev->codec_data;
+
unsigned char hs_gain, hs_pop;
struct twl4030_priv *twl4030 = codec->private_data;
/* Base values for ramp delay calculation: 2^19 - 2^26 */
@@ -629,6 +627,17 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET);
hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
+ /* Enable external mute control, this dramatically reduces
+ * the pop-noise */
+ if (setup && setup->hs_extmute) {
+ if (setup->set_hs_extmute) {
+ setup->set_hs_extmute(1);
+ } else {
+ hs_pop |= TWL4030_EXTMUTE;
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ }
+ }
+
if (ramp) {
/* Headset ramp-up according to the TRM */
hs_pop |= TWL4030_VMID_EN;
@@ -636,6 +645,9 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hs_gain);
hs_pop |= TWL4030_RAMP_EN;
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ /* Wait ramp delay time + 1, so the VMID can settle */
+ mdelay((ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] /
+ twl4030->sysclk) + 1);
} else {
/* Headset ramp-down _not_ according to
* the TRM, but in a way that it is working */
@@ -652,6 +664,16 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
hs_pop &= ~TWL4030_VMID_EN;
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
}
+
+ /* Disable external mute */
+ if (setup && setup->hs_extmute) {
+ if (setup->set_hs_extmute) {
+ setup->set_hs_extmute(0);
+ } else {
+ hs_pop &= ~TWL4030_EXTMUTE;
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+ }
+ }
}
static int headsetlpga_event(struct snd_soc_dapm_widget *w,
@@ -712,7 +734,19 @@ static int bypass_event(struct snd_soc_dapm_widget *w,
reg = twl4030_read_reg_cache(w->codec, m->reg);
- if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
+ /*
+ * bypass_state[0:3] - analog HiFi bypass
+ * bypass_state[4] - analog voice bypass
+ * bypass_state[5] - digital voice bypass
+ * bypass_state[6:7] - digital HiFi bypass
+ */
+ if (m->reg == TWL4030_REG_VSTPGA) {
+ /* Voice digital bypass */
+ if (reg)
+ twl4030->bypass_state |= (1 << 5);
+ else
+ twl4030->bypass_state &= ~(1 << 5);
+ } else if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
/* Analog bypass */
if (reg & (1 << m->shift))
twl4030->bypass_state |=
@@ -726,12 +760,6 @@ static int bypass_event(struct snd_soc_dapm_widget *w,
twl4030->bypass_state |= (1 << 4);
else
twl4030->bypass_state &= ~(1 << 4);
- } else if (m->reg == TWL4030_REG_VSTPGA) {
- /* Voice digital bypass */
- if (reg)
- twl4030->bypass_state |= (1 << 5);
- else
- twl4030->bypass_state &= ~(1 << 5);
} else {
/* Digital bypass */
if (reg & (0x7 << m->shift))
@@ -924,7 +952,7 @@ static const struct soc_enum twl4030_op_modes_enum =
ARRAY_SIZE(twl4030_op_modes_texts),
twl4030_op_modes_texts);
-int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol,
+static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -1005,6 +1033,16 @@ static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0);
*/
static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
+/* AVADC clock priority */
+static const char *twl4030_avadc_clk_priority_texts[] = {
+ "Voice high priority", "HiFi high priority"
+};
+
+static const struct soc_enum twl4030_avadc_clk_priority_enum =
+ SOC_ENUM_SINGLE(TWL4030_REG_AVADC_CTL, 2,
+ ARRAY_SIZE(twl4030_avadc_clk_priority_texts),
+ twl4030_avadc_clk_priority_texts);
+
static const char *twl4030_rampdelay_texts[] = {
"27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms",
"437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms",
@@ -1106,6 +1144,8 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = {
SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN,
0, 3, 5, 0, input_gain_tlv),
+ SOC_ENUM("AVADC Clock Priority", twl4030_avadc_clk_priority_enum),
+
SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum),
SOC_ENUM("Vibra H-bridge mode", twl4030_vibradirmode_enum),
@@ -1208,13 +1248,22 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_earpiece_controls[0],
ARRAY_SIZE(twl4030_dapm_earpiece_controls)),
+ SND_SOC_DAPM_PGA_E("Earpiece PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, earpiecepga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
/* PreDrivL/R */
SND_SOC_DAPM_MIXER("PredriveL Mixer", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_predrivel_controls[0],
ARRAY_SIZE(twl4030_dapm_predrivel_controls)),
+ SND_SOC_DAPM_PGA_E("PredriveL PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, predrivelpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MIXER("PredriveR Mixer", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_predriver_controls[0],
ARRAY_SIZE(twl4030_dapm_predriver_controls)),
+ SND_SOC_DAPM_PGA_E("PredriveR PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, predriverpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
/* HeadsetL/R */
SND_SOC_DAPM_MIXER("HeadsetL Mixer", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_hsol_controls[0],
@@ -1232,22 +1281,28 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_carkitl_controls[0],
ARRAY_SIZE(twl4030_dapm_carkitl_controls)),
+ SND_SOC_DAPM_PGA_E("CarkitL PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, carkitlpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MIXER("CarkitR Mixer", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_carkitr_controls[0],
ARRAY_SIZE(twl4030_dapm_carkitr_controls)),
+ SND_SOC_DAPM_PGA_E("CarkitR PGA", SND_SOC_NOPM,
+ 0, 0, NULL, 0, carkitrpga_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
/* Output MUX controls */
/* HandsfreeL/R */
SND_SOC_DAPM_MUX("HandsfreeL Mux", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_handsfreel_control),
- SND_SOC_DAPM_SWITCH("HandsfreeL Switch", SND_SOC_NOPM, 0, 0,
+ SND_SOC_DAPM_SWITCH("HandsfreeL", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_handsfreelmute_control),
SND_SOC_DAPM_PGA_E("HandsfreeL PGA", SND_SOC_NOPM,
0, 0, NULL, 0, handsfreelpga_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MUX("HandsfreeR Mux", SND_SOC_NOPM, 5, 0,
&twl4030_dapm_handsfreer_control),
- SND_SOC_DAPM_SWITCH("HandsfreeR Switch", SND_SOC_NOPM, 0, 0,
+ SND_SOC_DAPM_SWITCH("HandsfreeR", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_handsfreermute_control),
SND_SOC_DAPM_PGA_E("HandsfreeR PGA", SND_SOC_NOPM,
0, 0, NULL, 0, handsfreerpga_event,
@@ -1282,11 +1337,11 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_POST_REG),
/* Analog input mixers for the capture amplifiers */
- SND_SOC_DAPM_MIXER("Analog Left Capture Route",
+ SND_SOC_DAPM_MIXER("Analog Left",
TWL4030_REG_ANAMICL, 4, 0,
&twl4030_dapm_analoglmic_controls[0],
ARRAY_SIZE(twl4030_dapm_analoglmic_controls)),
- SND_SOC_DAPM_MIXER("Analog Right Capture Route",
+ SND_SOC_DAPM_MIXER("Analog Right",
TWL4030_REG_ANAMICR, 4, 0,
&twl4030_dapm_analogrmic_controls[0],
ARRAY_SIZE(twl4030_dapm_analogrmic_controls)),
@@ -1326,16 +1381,19 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Earpiece Mixer", "AudioL1", "Analog L1 Playback Mixer"},
{"Earpiece Mixer", "AudioL2", "Analog L2 Playback Mixer"},
{"Earpiece Mixer", "AudioR1", "Analog R1 Playback Mixer"},
+ {"Earpiece PGA", NULL, "Earpiece Mixer"},
/* PreDrivL */
{"PredriveL Mixer", "Voice", "Analog Voice Playback Mixer"},
{"PredriveL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
{"PredriveL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
{"PredriveL Mixer", "AudioR2", "Analog R2 Playback Mixer"},
+ {"PredriveL PGA", NULL, "PredriveL Mixer"},
/* PreDrivR */
{"PredriveR Mixer", "Voice", "Analog Voice Playback Mixer"},
{"PredriveR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
{"PredriveR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
{"PredriveR Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+ {"PredriveR PGA", NULL, "PredriveR Mixer"},
/* HeadsetL */
{"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"},
{"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
@@ -1350,24 +1408,26 @@ static const struct snd_soc_dapm_route intercon[] = {
{"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"},
{"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
{"CarkitL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+ {"CarkitL PGA", NULL, "CarkitL Mixer"},
/* CarkitR */
{"CarkitR Mixer", "Voice", "Analog Voice Playback Mixer"},
{"CarkitR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
{"CarkitR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
+ {"CarkitR PGA", NULL, "CarkitR Mixer"},
/* HandsfreeL */
{"HandsfreeL Mux", "Voice", "Analog Voice Playback Mixer"},
{"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"},
{"HandsfreeL Mux", "AudioL2", "Analog L2 Playback Mixer"},
{"HandsfreeL Mux", "AudioR2", "Analog R2 Playback Mixer"},
- {"HandsfreeL Switch", "Switch", "HandsfreeL Mux"},
- {"HandsfreeL PGA", NULL, "HandsfreeL Switch"},
+ {"HandsfreeL", "Switch", "HandsfreeL Mux"},
+ {"HandsfreeL PGA", NULL, "HandsfreeL"},
/* HandsfreeR */
{"HandsfreeR Mux", "Voice", "Analog Voice Playback Mixer"},
{"HandsfreeR Mux", "AudioR1", "Analog R1 Playback Mixer"},
{"HandsfreeR Mux", "AudioR2", "Analog R2 Playback Mixer"},
{"HandsfreeR Mux", "AudioL2", "Analog L2 Playback Mixer"},
- {"HandsfreeR Switch", "Switch", "HandsfreeR Mux"},
- {"HandsfreeR PGA", NULL, "HandsfreeR Switch"},
+ {"HandsfreeR", "Switch", "HandsfreeR Mux"},
+ {"HandsfreeR PGA", NULL, "HandsfreeR"},
/* Vibra */
{"Vibra Mux", "AudioL1", "DAC Left1"},
{"Vibra Mux", "AudioR1", "DAC Right1"},
@@ -1377,29 +1437,29 @@ static const struct snd_soc_dapm_route intercon[] = {
/* outputs */
{"OUTL", NULL, "Analog L2 Playback Mixer"},
{"OUTR", NULL, "Analog R2 Playback Mixer"},
- {"EARPIECE", NULL, "Earpiece Mixer"},
- {"PREDRIVEL", NULL, "PredriveL Mixer"},
- {"PREDRIVER", NULL, "PredriveR Mixer"},
+ {"EARPIECE", NULL, "Earpiece PGA"},
+ {"PREDRIVEL", NULL, "PredriveL PGA"},
+ {"PREDRIVER", NULL, "PredriveR PGA"},
{"HSOL", NULL, "HeadsetL PGA"},
{"HSOR", NULL, "HeadsetR PGA"},
- {"CARKITL", NULL, "CarkitL Mixer"},
- {"CARKITR", NULL, "CarkitR Mixer"},
+ {"CARKITL", NULL, "CarkitL PGA"},
+ {"CARKITR", NULL, "CarkitR PGA"},
{"HFL", NULL, "HandsfreeL PGA"},
{"HFR", NULL, "HandsfreeR PGA"},
{"Vibra Route", "Audio", "Vibra Mux"},
{"VIBRA", NULL, "Vibra Route"},
/* Capture path */
- {"Analog Left Capture Route", "Main mic", "MAINMIC"},
- {"Analog Left Capture Route", "Headset mic", "HSMIC"},
- {"Analog Left Capture Route", "AUXL", "AUXL"},
- {"Analog Left Capture Route", "Carkit mic", "CARKITMIC"},
+ {"Analog Left", "Main Mic Capture Switch", "MAINMIC"},
+ {"Analog Left", "Headset Mic Capture Switch", "HSMIC"},
+ {"Analog Left", "AUXL Capture Switch", "AUXL"},
+ {"Analog Left", "Carkit Mic Capture Switch", "CARKITMIC"},
- {"Analog Right Capture Route", "Sub mic", "SUBMIC"},
- {"Analog Right Capture Route", "AUXR", "AUXR"},
+ {"Analog Right", "Sub Mic Capture Switch", "SUBMIC"},
+ {"Analog Right", "AUXR Capture Switch", "AUXR"},
- {"ADC Physical Left", NULL, "Analog Left Capture Route"},
- {"ADC Physical Right", NULL, "Analog Right Capture Route"},
+ {"ADC Physical Left", NULL, "Analog Left"},
+ {"ADC Physical Right", NULL, "Analog Right"},
{"Digimic0 Enable", NULL, "DIGIMIC0"},
{"Digimic1 Enable", NULL, "DIGIMIC1"},
@@ -1423,11 +1483,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ADC Virtual Right2", NULL, "TX2 Capture Route"},
/* Analog bypass routes */
- {"Right1 Analog Loopback", "Switch", "Analog Right Capture Route"},
- {"Left1 Analog Loopback", "Switch", "Analog Left Capture Route"},
- {"Right2 Analog Loopback", "Switch", "Analog Right Capture Route"},
- {"Left2 Analog Loopback", "Switch", "Analog Left Capture Route"},
- {"Voice Analog Loopback", "Switch", "Analog Left Capture Route"},
+ {"Right1 Analog Loopback", "Switch", "Analog Right"},
+ {"Left1 Analog Loopback", "Switch", "Analog Left"},
+ {"Right2 Analog Loopback", "Switch", "Analog Right"},
+ {"Left2 Analog Loopback", "Switch", "Analog Left"},
+ {"Voice Analog Loopback", "Switch", "Analog Left"},
{"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"},
{"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"},
@@ -1609,8 +1669,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
/* If the substream has 4 channel, do the necessary setup */
if (params_channels(params) == 4) {
- u8 format, mode;
-
format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
@@ -1806,6 +1864,19 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
+static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
+
+ if (tristate)
+ reg |= TWL4030_AIF_TRI_EN;
+ else
+ reg &= ~TWL4030_AIF_TRI_EN;
+
+ return twl4030_write(codec, TWL4030_REG_AUDIO_IF, reg);
+}
+
/* In case of voice mode, the RX1 L(VRX) for downlink and the TX2 L/R
* (VTXL, VTXR) for uplink has to be enabled/disabled. */
static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction,
@@ -1948,7 +2019,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFM:
format &= ~(TWL4030_VIF_SLAVE_EN);
break;
case SND_SOC_DAIFMT_CBS_CFS:
@@ -1980,6 +2051,19 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
+static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF);
+
+ if (tristate)
+ reg |= TWL4030_VIF_TRI_EN;
+ else
+ reg &= ~TWL4030_VIF_TRI_EN;
+
+ return twl4030_write(codec, TWL4030_REG_VOICE_IF, reg);
+}
+
#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000)
#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
@@ -1989,6 +2073,7 @@ static struct snd_soc_dai_ops twl4030_dai_ops = {
.hw_params = twl4030_hw_params,
.set_sysclk = twl4030_set_dai_sysclk,
.set_fmt = twl4030_set_dai_fmt,
+ .set_tristate = twl4030_set_tristate,
};
static struct snd_soc_dai_ops twl4030_dai_voice_ops = {
@@ -1997,6 +2082,7 @@ static struct snd_soc_dai_ops twl4030_dai_voice_ops = {
.hw_params = twl4030_voice_hw_params,
.set_sysclk = twl4030_voice_set_dai_sysclk,
.set_fmt = twl4030_voice_set_dai_fmt,
+ .set_tristate = twl4030_voice_set_tristate,
};
struct snd_soc_dai twl4030_dai[] = {
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index fe5f395d9e4..2b4bfa23f98 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -274,6 +274,8 @@ extern struct snd_soc_codec_device soc_codec_dev_twl4030;
struct twl4030_setup_data {
unsigned int ramp_delay_value;
unsigned int sysclk;
+ unsigned int hs_extmute:1;
+ void (*set_hs_extmute)(int mute);
};
#endif /* End of __TWL4030_AUDIO_H__ */
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index 269b108e1de..c33b92edbde 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -163,7 +163,7 @@ static int uda134x_mute(struct snd_soc_dai *dai, int mute)
else
mute_reg &= ~(1<<2);
- uda134x_write(codec, UDA134X_DATA010, mute_reg & ~(1<<2));
+ uda134x_write(codec, UDA134X_DATA010, mute_reg);
return 0;
}
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 5b21594e0e5..92ec0344215 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -5,9 +5,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
- * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com>
- * Improved support for DAPM and audio routing/mixing capabilities,
- * added TLV support.
+ * Copyright (c) 2007-2009 Philipp Zabel <philipp.zabel@gmail.com>
*
* Modified by Richard Purdie <richard@openedhand.com> to fit into SoC
* codec model.
@@ -19,26 +17,32 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
-#include <linux/string.h>
#include <linux/slab.h>
#include <linux/errno.h>
-#include <linux/ioctl.h>
+#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
-#include <sound/info.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
+#include <sound/uda1380.h>
#include "uda1380.h"
-static struct work_struct uda1380_work;
static struct snd_soc_codec *uda1380_codec;
+/* codec private data */
+struct uda1380_priv {
+ struct snd_soc_codec codec;
+ u16 reg_cache[UDA1380_CACHEREGNUM];
+ unsigned int dac_clk;
+ struct work_struct work;
+};
+
/*
* uda1380 register cache
*/
@@ -473,6 +477,7 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
+ struct uda1380_priv *uda1380 = codec->private_data;
int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
switch (cmd) {
@@ -480,13 +485,13 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
uda1380_write_reg_cache(codec, UDA1380_MIXER,
mixer & ~R14_SILENCE);
- schedule_work(&uda1380_work);
+ schedule_work(&uda1380->work);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
uda1380_write_reg_cache(codec, UDA1380_MIXER,
mixer | R14_SILENCE);
- schedule_work(&uda1380_work);
+ schedule_work(&uda1380->work);
break;
}
return 0;
@@ -670,44 +675,33 @@ static int uda1380_resume(struct platform_device *pdev)
return 0;
}
-/*
- * initialise the UDA1380 driver
- * register mixer and dsp interfaces with the kernel
- */
-static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
+static int uda1380_probe(struct platform_device *pdev)
{
- struct snd_soc_codec *codec = socdev->card->codec;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct uda1380_platform_data *pdata;
int ret = 0;
- codec->name = "UDA1380";
- codec->owner = THIS_MODULE;
- codec->read = uda1380_read_reg_cache;
- codec->write = uda1380_write;
- codec->set_bias_level = uda1380_set_bias_level;
- codec->dai = uda1380_dai;
- codec->num_dai = ARRAY_SIZE(uda1380_dai);
- codec->reg_cache = kmemdup(uda1380_reg, sizeof(uda1380_reg),
- GFP_KERNEL);
- if (codec->reg_cache == NULL)
- return -ENOMEM;
- codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
- codec->reg_cache_step = 1;
- uda1380_reset(codec);
+ if (uda1380_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
- uda1380_codec = codec;
- INIT_WORK(&uda1380_work, uda1380_flush_work);
+ socdev->card->codec = uda1380_codec;
+ codec = uda1380_codec;
+ pdata = codec->dev->platform_data;
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
- pr_err("uda1380: failed to create pcms\n");
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
goto pcm_err;
}
/* power on device */
uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* set clock input */
- switch (dac_clk) {
+ switch (pdata->dac_clk) {
case UDA1380_DAC_CLK_SYSCLK:
uda1380_write(codec, UDA1380_CLK, 0);
break;
@@ -716,13 +710,12 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
break;
}
- /* uda1380 init */
snd_soc_add_controls(codec, uda1380_snd_controls,
ARRAY_SIZE(uda1380_snd_controls));
uda1380_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
- pr_err("uda1380: failed to register card\n");
+ dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
@@ -732,165 +725,201 @@ card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
- kfree(codec->reg_cache);
return ret;
}
-static struct snd_soc_device *uda1380_socdev;
-
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-
-static int uda1380_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+/* power down chip */
+static int uda1380_remove(struct platform_device *pdev)
{
- struct snd_soc_device *socdev = uda1380_socdev;
- struct uda1380_setup_data *setup = socdev->codec_data;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
- int ret;
-
- i2c_set_clientdata(i2c, codec);
- codec->control_data = i2c;
- ret = uda1380_init(socdev, setup->dac_clk);
- if (ret < 0)
- pr_err("uda1380: failed to initialise UDA1380\n");
+ if (codec->control_data)
+ uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return ret;
-}
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
-static int uda1380_i2c_remove(struct i2c_client *client)
-{
- struct snd_soc_codec *codec = i2c_get_clientdata(client);
- kfree(codec->reg_cache);
return 0;
}
-static const struct i2c_device_id uda1380_i2c_id[] = {
- { "uda1380", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
-
-static struct i2c_driver uda1380_i2c_driver = {
- .driver = {
- .name = "UDA1380 I2C Codec",
- .owner = THIS_MODULE,
- },
- .probe = uda1380_i2c_probe,
- .remove = uda1380_i2c_remove,
- .id_table = uda1380_i2c_id,
+struct snd_soc_codec_device soc_codec_dev_uda1380 = {
+ .probe = uda1380_probe,
+ .remove = uda1380_remove,
+ .suspend = uda1380_suspend,
+ .resume = uda1380_resume,
};
+EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
-static int uda1380_add_i2c_device(struct platform_device *pdev,
- const struct uda1380_setup_data *setup)
+static int uda1380_register(struct uda1380_priv *uda1380)
{
- struct i2c_board_info info;
- struct i2c_adapter *adapter;
- struct i2c_client *client;
- int ret;
+ int ret, i;
+ struct snd_soc_codec *codec = &uda1380->codec;
+ struct uda1380_platform_data *pdata = codec->dev->platform_data;
- ret = i2c_add_driver(&uda1380_i2c_driver);
- if (ret != 0) {
- dev_err(&pdev->dev, "can't add i2c driver\n");
- return ret;
+ if (uda1380_codec) {
+ dev_err(codec->dev, "Another UDA1380 is registered\n");
+ return -EINVAL;
+ }
+
+ if (!pdata || !pdata->gpio_power || !pdata->gpio_reset)
+ return -EINVAL;
+
+ ret = gpio_request(pdata->gpio_power, "uda1380 power");
+ if (ret)
+ goto err_out;
+ ret = gpio_request(pdata->gpio_reset, "uda1380 reset");
+ if (ret)
+ goto err_gpio;
+
+ gpio_direction_output(pdata->gpio_power, 1);
+
+ /* we may need to have the clock running here - pH5 */
+ gpio_direction_output(pdata->gpio_reset, 1);
+ udelay(5);
+ gpio_set_value(pdata->gpio_reset, 0);
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->private_data = uda1380;
+ codec->name = "UDA1380";
+ codec->owner = THIS_MODULE;
+ codec->read = uda1380_read_reg_cache;
+ codec->write = uda1380_write;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = uda1380_set_bias_level;
+ codec->dai = uda1380_dai;
+ codec->num_dai = ARRAY_SIZE(uda1380_dai);
+ codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
+ codec->reg_cache = &uda1380->reg_cache;
+ codec->reg_cache_step = 1;
+
+ memcpy(codec->reg_cache, uda1380_reg, sizeof(uda1380_reg));
+
+ ret = uda1380_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err_reset;
}
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = setup->i2c_address;
- strlcpy(info.type, "uda1380", I2C_NAME_SIZE);
+ INIT_WORK(&uda1380->work, uda1380_flush_work);
+
+ for (i = 0; i < ARRAY_SIZE(uda1380_dai); i++)
+ uda1380_dai[i].dev = codec->dev;
- adapter = i2c_get_adapter(setup->i2c_bus);
- if (!adapter) {
- dev_err(&pdev->dev, "can't get i2c adapter %d\n",
- setup->i2c_bus);
- goto err_driver;
+ uda1380_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err_reset;
}
- client = i2c_new_device(adapter, &info);
- i2c_put_adapter(adapter);
- if (!client) {
- dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
- (unsigned int)info.addr);
- goto err_driver;
+ ret = snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+ goto err_dai;
}
return 0;
-err_driver:
- i2c_del_driver(&uda1380_i2c_driver);
- return -ENODEV;
+err_dai:
+ snd_soc_unregister_codec(codec);
+err_reset:
+ gpio_set_value(pdata->gpio_power, 0);
+ gpio_free(pdata->gpio_reset);
+err_gpio:
+ gpio_free(pdata->gpio_power);
+err_out:
+ return ret;
}
-#endif
-static int uda1380_probe(struct platform_device *pdev)
+static void uda1380_unregister(struct uda1380_priv *uda1380)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct uda1380_setup_data *setup;
+ struct snd_soc_codec *codec = &uda1380->codec;
+ struct uda1380_platform_data *pdata = codec->dev->platform_data;
+
+ snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+ snd_soc_unregister_codec(&uda1380->codec);
+
+ gpio_set_value(pdata->gpio_power, 0);
+ gpio_free(pdata->gpio_reset);
+ gpio_free(pdata->gpio_power);
+
+ kfree(uda1380);
+ uda1380_codec = NULL;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int uda1380_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct uda1380_priv *uda1380;
struct snd_soc_codec *codec;
int ret;
- setup = socdev->codec_data;
- codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (codec == NULL)
+ uda1380 = kzalloc(sizeof(struct uda1380_priv), GFP_KERNEL);
+ if (uda1380 == NULL)
return -ENOMEM;
- socdev->card->codec = codec;
- mutex_init(&codec->mutex);
- INIT_LIST_HEAD(&codec->dapm_widgets);
- INIT_LIST_HEAD(&codec->dapm_paths);
+ codec = &uda1380->codec;
+ codec->hw_write = (hw_write_t)i2c_master_send;
- uda1380_socdev = socdev;
- ret = -ENODEV;
+ i2c_set_clientdata(i2c, uda1380);
+ codec->control_data = i2c;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- if (setup->i2c_address) {
- codec->hw_write = (hw_write_t)i2c_master_send;
- ret = uda1380_add_i2c_device(pdev, setup);
- }
-#endif
+ codec->dev = &i2c->dev;
+ ret = uda1380_register(uda1380);
if (ret != 0)
- kfree(codec);
+ kfree(uda1380);
+
return ret;
}
-/* power down chip */
-static int uda1380_remove(struct platform_device *pdev)
+static int __devexit uda1380_i2c_remove(struct i2c_client *i2c)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->card->codec;
-
- if (codec->control_data)
- uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_unregister_device(codec->control_data);
- i2c_del_driver(&uda1380_i2c_driver);
-#endif
- kfree(codec);
-
+ struct uda1380_priv *uda1380 = i2c_get_clientdata(i2c);
+ uda1380_unregister(uda1380);
return 0;
}
-struct snd_soc_codec_device soc_codec_dev_uda1380 = {
- .probe = uda1380_probe,
- .remove = uda1380_remove,
- .suspend = uda1380_suspend,
- .resume = uda1380_resume,
+static const struct i2c_device_id uda1380_i2c_id[] = {
+ { "uda1380", 0 },
+ { }
};
-EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
+MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
+
+static struct i2c_driver uda1380_i2c_driver = {
+ .driver = {
+ .name = "UDA1380 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = uda1380_i2c_probe,
+ .remove = __devexit_p(uda1380_i2c_remove),
+ .id_table = uda1380_i2c_id,
+};
+#endif
static int __init uda1380_modinit(void)
{
- return snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+ int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&uda1380_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register UDA1380 I2C driver: %d\n", ret);
+#endif
+ return 0;
}
module_init(uda1380_modinit);
static void __exit uda1380_exit(void)
{
- snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&uda1380_i2c_driver);
+#endif
}
module_exit(uda1380_exit);
diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h
index c55c17a52a1..9cefa8a5477 100644
--- a/sound/soc/codecs/uda1380.h
+++ b/sound/soc/codecs/uda1380.h
@@ -72,14 +72,6 @@
#define R22_SKIP_DCFIL 0x0002
#define R23_AGC_EN 0x0001
-struct uda1380_setup_data {
- int i2c_bus;
- unsigned short i2c_address;
- int dac_clk;
-#define UDA1380_DAC_CLK_SYSCLK 0
-#define UDA1380_DAC_CLK_WSPLL 1
-};
-
#define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */
#define UDA1380_DAI_PLAYBACK 1 /* playback DAI */
#define UDA1380_DAI_CAPTURE 2 /* capture DAI */
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index e7348d341b7..3ff0373dff8 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -63,6 +63,8 @@ struct wm8350_data {
struct wm8350_jack_data hpl;
struct wm8350_jack_data hpr;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+ int fll_freq_out;
+ int fll_freq_in;
};
static unsigned int wm8350_codec_cache_read(struct snd_soc_codec *codec,
@@ -406,7 +408,6 @@ static const char *wm8350_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" };
static const char *wm8350_dacmutem[] = { "Normal", "Soft" };
static const char *wm8350_dacmutes[] = { "Fast", "Slow" };
-static const char *wm8350_dacfilter[] = { "Normal", "Sloping" };
static const char *wm8350_adcfilter[] = { "None", "High Pass" };
static const char *wm8350_adchp[] = { "44.1kHz", "8kHz", "16kHz", "32kHz" };
static const char *wm8350_lr[] = { "Left", "Right" };
@@ -416,7 +417,6 @@ static const struct soc_enum wm8350_enum[] = {
SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 0, 4, wm8350_pol),
SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 14, 2, wm8350_dacmutem),
SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 13, 2, wm8350_dacmutes),
- SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 12, 2, wm8350_dacfilter),
SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 15, 2, wm8350_adcfilter),
SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 8, 4, wm8350_adchp),
SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 0, 4, wm8350_pol),
@@ -444,10 +444,9 @@ static const struct snd_kcontrol_new wm8350_snd_controls[] = {
0, 255, 0, dac_pcm_tlv),
SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]),
SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]),
- SOC_ENUM("Playback PCM Filter", wm8350_enum[4]),
- SOC_ENUM("Capture PCM Filter", wm8350_enum[5]),
- SOC_ENUM("Capture PCM HP Filter", wm8350_enum[6]),
- SOC_ENUM("Capture ADC Inversion", wm8350_enum[7]),
+ SOC_ENUM("Capture PCM Filter", wm8350_enum[4]),
+ SOC_ENUM("Capture PCM HP Filter", wm8350_enum[5]),
+ SOC_ENUM("Capture ADC Inversion", wm8350_enum[6]),
SOC_WM8350_DOUBLE_R_TLV("Capture PCM Volume",
WM8350_ADC_DIGITAL_VOLUME_L,
WM8350_ADC_DIGITAL_VOLUME_R,
@@ -613,7 +612,7 @@ SOC_DAPM_SINGLE("Switch", WM8350_BEEP_VOLUME, 15, 1, 1);
/* Out4 Capture Mux */
static const struct snd_kcontrol_new wm8350_out4_capture_controls =
-SOC_DAPM_ENUM("Route", wm8350_enum[8]);
+SOC_DAPM_ENUM("Route", wm8350_enum[7]);
static const struct snd_soc_dapm_widget wm8350_dapm_widgets[] = {
@@ -993,6 +992,7 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai)
{
struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8350 *wm8350 = codec->control_data;
u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
~WM8350_AIF_WL_MASK;
@@ -1012,6 +1012,19 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
}
wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
+
+ /* The sloping stopband filter is recommended for use with
+ * lower sample rates to improve performance.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (params_rate(params) < 24000)
+ wm8350_set_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
+ WM8350_DAC_SB_FILT);
+ else
+ wm8350_clear_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
+ WM8350_DAC_SB_FILT);
+ }
+
return 0;
}
@@ -1093,10 +1106,14 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
{
struct snd_soc_codec *codec = codec_dai->codec;
struct wm8350 *wm8350 = codec->control_data;
+ struct wm8350_data *priv = codec->private_data;
struct _fll_div fll_div;
int ret = 0;
u16 fll_1, fll_4;
+ if (freq_in == priv->fll_freq_in && freq_out == priv->fll_freq_out)
+ return 0;
+
/* power down FLL - we need to do this for reconfiguration */
wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
WM8350_FLL_ENA | WM8350_FLL_OSC_ENA);
@@ -1131,6 +1148,9 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_OSC_ENA);
wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA);
+ priv->fll_freq_out = freq_out;
+ priv->fll_freq_in = freq_in;
+
return 0;
}
@@ -1660,6 +1680,21 @@ static int __devexit wm8350_codec_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int wm8350_codec_suspend(struct platform_device *pdev, pm_message_t m)
+{
+ return snd_soc_suspend_device(&pdev->dev);
+}
+
+static int wm8350_codec_resume(struct platform_device *pdev)
+{
+ return snd_soc_resume_device(&pdev->dev);
+}
+#else
+#define wm8350_codec_suspend NULL
+#define wm8350_codec_resume NULL
+#endif
+
static struct platform_driver wm8350_codec_driver = {
.driver = {
.name = "wm8350-codec",
@@ -1667,6 +1702,8 @@ static struct platform_driver wm8350_codec_driver = {
},
.probe = wm8350_codec_probe,
.remove = __devexit_p(wm8350_codec_remove),
+ .suspend = wm8350_codec_suspend,
+ .resume = wm8350_codec_resume,
};
static __init int wm8350_init(void)
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index 502eefac1ec..b9ef4d91522 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -1022,10 +1022,15 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
if (freq_in == wm8400->fll_in && freq_out == wm8400->fll_out)
return 0;
- if (freq_out != 0) {
+ if (freq_out) {
ret = fll_factors(wm8400, &factors, freq_in, freq_out);
if (ret != 0)
return ret;
+ } else {
+ /* Bodge GCC 4.4.0 uninitialised variable warning - it
+ * doesn't seem capable of working out that we exit if
+ * freq_out is 0 before any of the uses. */
+ memset(&factors, 0, sizeof(factors));
}
wm8400->fll_out = freq_out;
@@ -1040,7 +1045,7 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
reg &= ~WM8400_FLL_OSC_ENA;
wm8400_write(codec, WM8400_FLL_CONTROL_1, reg);
- if (freq_out == 0)
+ if (!freq_out)
return 0;
reg &= ~(WM8400_FLL_REF_FREQ | WM8400_FLL_FRATIO_MASK);
@@ -1553,6 +1558,21 @@ static int __exit wm8400_codec_remove(struct platform_device *dev)
return 0;
}
+#ifdef CONFIG_PM
+static int wm8400_pdev_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&pdev->dev);
+}
+
+static int wm8400_pdev_resume(struct platform_device *pdev)
+{
+ return snd_soc_resume_device(&pdev->dev);
+}
+#else
+#define wm8400_pdev_suspend NULL
+#define wm8400_pdev_resume NULL
+#endif
+
static struct platform_driver wm8400_codec_driver = {
.driver = {
.name = "wm8400-codec",
@@ -1560,6 +1580,8 @@ static struct platform_driver wm8400_codec_driver = {
},
.probe = wm8400_codec_probe,
.remove = __exit_p(wm8400_codec_remove),
+ .suspend = wm8400_pdev_suspend,
+ .resume = wm8400_pdev_resume,
};
static int __init wm8400_codec_init(void)
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index c8b8dba8589..060d5d06ba9 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -58,55 +58,7 @@ static const u16 wm8510_reg[WM8510_CACHEREGNUM] = {
#define WM8510_POWER1_BIASEN 0x08
#define WM8510_POWER1_BUFIOEN 0x10
-/*
- * read wm8510 register cache
- */
-static inline unsigned int wm8510_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg == WM8510_RESET)
- return 0;
- if (reg >= WM8510_CACHEREGNUM)
- return -1;
- return cache[reg];
-}
-
-/*
- * write wm8510 register cache
- */
-static inline void wm8510_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg >= WM8510_CACHEREGNUM)
- return;
- cache[reg] = value;
-}
-
-/*
- * write to the WM8510 register space
- */
-static int wm8510_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8510 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8510_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define wm8510_reset(c) wm8510_write(c, WM8510_RESET, 0)
+#define wm8510_reset(c) snd_soc_write(c, WM8510_RESET, 0)
static const char *wm8510_companding[] = { "Off", "NC", "u-law", "A-law" };
static const char *wm8510_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
@@ -327,27 +279,27 @@ static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
if (freq_in == 0 || freq_out == 0) {
/* Clock CODEC directly from MCLK */
- reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
- wm8510_write(codec, WM8510_CLOCK, reg & 0x0ff);
+ reg = snd_soc_read(codec, WM8510_CLOCK);
+ snd_soc_write(codec, WM8510_CLOCK, reg & 0x0ff);
/* Turn off PLL */
- reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
- wm8510_write(codec, WM8510_POWER1, reg & 0x1df);
+ reg = snd_soc_read(codec, WM8510_POWER1);
+ snd_soc_write(codec, WM8510_POWER1, reg & 0x1df);
return 0;
}
pll_factors(freq_out*4, freq_in);
- wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
- wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18);
- wm8510_write(codec, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
- wm8510_write(codec, WM8510_PLLK3, pll_div.k & 0x1ff);
- reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
- wm8510_write(codec, WM8510_POWER1, reg | 0x020);
+ snd_soc_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
+ snd_soc_write(codec, WM8510_PLLK1, pll_div.k >> 18);
+ snd_soc_write(codec, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8510_PLLK3, pll_div.k & 0x1ff);
+ reg = snd_soc_read(codec, WM8510_POWER1);
+ snd_soc_write(codec, WM8510_POWER1, reg | 0x020);
/* Run CODEC from PLL instead of MCLK */
- reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
- wm8510_write(codec, WM8510_CLOCK, reg | 0x100);
+ reg = snd_soc_read(codec, WM8510_CLOCK);
+ snd_soc_write(codec, WM8510_CLOCK, reg | 0x100);
return 0;
}
@@ -363,24 +315,24 @@ static int wm8510_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
switch (div_id) {
case WM8510_OPCLKDIV:
- reg = wm8510_read_reg_cache(codec, WM8510_GPIO) & 0x1cf;
- wm8510_write(codec, WM8510_GPIO, reg | div);
+ reg = snd_soc_read(codec, WM8510_GPIO) & 0x1cf;
+ snd_soc_write(codec, WM8510_GPIO, reg | div);
break;
case WM8510_MCLKDIV:
- reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x11f;
- wm8510_write(codec, WM8510_CLOCK, reg | div);
+ reg = snd_soc_read(codec, WM8510_CLOCK) & 0x11f;
+ snd_soc_write(codec, WM8510_CLOCK, reg | div);
break;
case WM8510_ADCCLK:
- reg = wm8510_read_reg_cache(codec, WM8510_ADC) & 0x1f7;
- wm8510_write(codec, WM8510_ADC, reg | div);
+ reg = snd_soc_read(codec, WM8510_ADC) & 0x1f7;
+ snd_soc_write(codec, WM8510_ADC, reg | div);
break;
case WM8510_DACCLK:
- reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0x1f7;
- wm8510_write(codec, WM8510_DAC, reg | div);
+ reg = snd_soc_read(codec, WM8510_DAC) & 0x1f7;
+ snd_soc_write(codec, WM8510_DAC, reg | div);
break;
case WM8510_BCLKDIV:
- reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1e3;
- wm8510_write(codec, WM8510_CLOCK, reg | div);
+ reg = snd_soc_read(codec, WM8510_CLOCK) & 0x1e3;
+ snd_soc_write(codec, WM8510_CLOCK, reg | div);
break;
default:
return -EINVAL;
@@ -394,7 +346,7 @@ static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 iface = 0;
- u16 clk = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1fe;
+ u16 clk = snd_soc_read(codec, WM8510_CLOCK) & 0x1fe;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -441,8 +393,8 @@ static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8510_write(codec, WM8510_IFACE, iface);
- wm8510_write(codec, WM8510_CLOCK, clk);
+ snd_soc_write(codec, WM8510_IFACE, iface);
+ snd_soc_write(codec, WM8510_CLOCK, clk);
return 0;
}
@@ -453,8 +405,8 @@ static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- u16 iface = wm8510_read_reg_cache(codec, WM8510_IFACE) & 0x19f;
- u16 adn = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x1f1;
+ u16 iface = snd_soc_read(codec, WM8510_IFACE) & 0x19f;
+ u16 adn = snd_soc_read(codec, WM8510_ADD) & 0x1f1;
/* bit size */
switch (params_format(params)) {
@@ -493,20 +445,20 @@ static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
break;
}
- wm8510_write(codec, WM8510_IFACE, iface);
- wm8510_write(codec, WM8510_ADD, adn);
+ snd_soc_write(codec, WM8510_IFACE, iface);
+ snd_soc_write(codec, WM8510_ADD, adn);
return 0;
}
static int wm8510_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0xffbf;
+ u16 mute_reg = snd_soc_read(codec, WM8510_DAC) & 0xffbf;
if (mute)
- wm8510_write(codec, WM8510_DAC, mute_reg | 0x40);
+ snd_soc_write(codec, WM8510_DAC, mute_reg | 0x40);
else
- wm8510_write(codec, WM8510_DAC, mute_reg);
+ snd_soc_write(codec, WM8510_DAC, mute_reg);
return 0;
}
@@ -514,13 +466,13 @@ static int wm8510_mute(struct snd_soc_dai *dai, int mute)
static int wm8510_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u16 power1 = wm8510_read_reg_cache(codec, WM8510_POWER1) & ~0x3;
+ u16 power1 = snd_soc_read(codec, WM8510_POWER1) & ~0x3;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
power1 |= 0x1; /* VMID 50k */
- wm8510_write(codec, WM8510_POWER1, power1);
+ snd_soc_write(codec, WM8510_POWER1, power1);
break;
case SND_SOC_BIAS_STANDBY:
@@ -528,18 +480,18 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec,
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Initial cap charge at VMID 5k */
- wm8510_write(codec, WM8510_POWER1, power1 | 0x3);
+ snd_soc_write(codec, WM8510_POWER1, power1 | 0x3);
mdelay(100);
}
power1 |= 0x2; /* VMID 500k */
- wm8510_write(codec, WM8510_POWER1, power1);
+ snd_soc_write(codec, WM8510_POWER1, power1);
break;
case SND_SOC_BIAS_OFF:
- wm8510_write(codec, WM8510_POWER1, 0);
- wm8510_write(codec, WM8510_POWER2, 0);
- wm8510_write(codec, WM8510_POWER3, 0);
+ snd_soc_write(codec, WM8510_POWER1, 0);
+ snd_soc_write(codec, WM8510_POWER2, 0);
+ snd_soc_write(codec, WM8510_POWER3, 0);
break;
}
@@ -577,6 +529,7 @@ struct snd_soc_dai wm8510_dai = {
.rates = WM8510_RATES,
.formats = WM8510_FORMATS,},
.ops = &wm8510_dai_ops,
+ .symmetric_rates = 1,
};
EXPORT_SYMBOL_GPL(wm8510_dai);
@@ -612,15 +565,14 @@ static int wm8510_resume(struct platform_device *pdev)
* initialise the WM8510 driver
* register the mixer and dsp interfaces with the kernel
*/
-static int wm8510_init(struct snd_soc_device *socdev)
+static int wm8510_init(struct snd_soc_device *socdev,
+ enum snd_soc_control_type control)
{
struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
codec->name = "WM8510";
codec->owner = THIS_MODULE;
- codec->read = wm8510_read_reg_cache;
- codec->write = wm8510_write;
codec->set_bias_level = wm8510_set_bias_level;
codec->dai = &wm8510_dai;
codec->num_dai = 1;
@@ -630,13 +582,20 @@ static int wm8510_init(struct snd_soc_device *socdev)
if (codec->reg_cache == NULL)
return -ENOMEM;
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8510: failed to set cache I/O: %d\n",
+ ret);
+ goto err;
+ }
+
wm8510_reset(codec);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "wm8510: failed to create pcms\n");
- goto pcm_err;
+ goto err;
}
/* power on device */
@@ -655,7 +614,7 @@ static int wm8510_init(struct snd_soc_device *socdev)
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-pcm_err:
+err:
kfree(codec->reg_cache);
return ret;
}
@@ -678,7 +637,7 @@ static int wm8510_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, codec);
codec->control_data = i2c;
- ret = wm8510_init(socdev);
+ ret = wm8510_init(socdev, SND_SOC_I2C);
if (ret < 0)
pr_err("failed to initialise WM8510\n");
@@ -758,7 +717,7 @@ static int __devinit wm8510_spi_probe(struct spi_device *spi)
codec->control_data = spi;
- ret = wm8510_init(socdev);
+ ret = wm8510_init(socdev, SND_SOC_SPI);
if (ret < 0)
dev_err(&spi->dev, "failed to initialise WM8510\n");
@@ -779,30 +738,6 @@ static struct spi_driver wm8510_spi_driver = {
.probe = wm8510_spi_probe,
.remove = __devexit_p(wm8510_spi_remove),
};
-
-static int wm8510_spi_write(struct spi_device *spi, const char *data, int len)
-{
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
-
- spi_message_init(&m);
- memset(&t, 0, (sizeof t));
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
-}
#endif /* CONFIG_SPI_MASTER */
static int wm8510_probe(struct platform_device *pdev)
@@ -827,13 +762,11 @@ static int wm8510_probe(struct platform_device *pdev)
wm8510_socdev = socdev;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
- codec->hw_write = (hw_write_t)i2c_master_send;
ret = wm8510_add_i2c_device(pdev, setup);
}
#endif
#if defined(CONFIG_SPI_MASTER)
if (setup->spi) {
- codec->hw_write = (hw_write_t)wm8510_spi_write;
ret = spi_register_driver(&wm8510_spi_driver);
if (ret != 0)
printk(KERN_ERR "can't add spi driver");
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
new file mode 100644
index 00000000000..25870a4652f
--- /dev/null
+++ b/sound/soc/codecs/wm8523.c
@@ -0,0 +1,699 @@
+/*
+ * wm8523.c -- WM8523 ALSA SoC Audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8523.h"
+
+static struct snd_soc_codec *wm8523_codec;
+struct snd_soc_codec_device soc_codec_dev_wm8523;
+
+#define WM8523_NUM_SUPPLIES 2
+static const char *wm8523_supply_names[WM8523_NUM_SUPPLIES] = {
+ "AVDD",
+ "LINEVDD",
+};
+
+#define WM8523_NUM_RATES 7
+
+/* codec private data */
+struct wm8523_priv {
+ struct snd_soc_codec codec;
+ u16 reg_cache[WM8523_REGISTER_COUNT];
+ struct regulator_bulk_data supplies[WM8523_NUM_SUPPLIES];
+ unsigned int sysclk;
+ unsigned int rate_constraint_list[WM8523_NUM_RATES];
+ struct snd_pcm_hw_constraint_list rate_constraint;
+};
+
+static const u16 wm8523_reg[WM8523_REGISTER_COUNT] = {
+ 0x8523, /* R0 - DEVICE_ID */
+ 0x0001, /* R1 - REVISION */
+ 0x0000, /* R2 - PSCTRL1 */
+ 0x1812, /* R3 - AIF_CTRL1 */
+ 0x0000, /* R4 - AIF_CTRL2 */
+ 0x0001, /* R5 - DAC_CTRL3 */
+ 0x0190, /* R6 - DAC_GAINL */
+ 0x0190, /* R7 - DAC_GAINR */
+ 0x0000, /* R8 - ZERO_DETECT */
+};
+
+static int wm8523_volatile_register(unsigned int reg)
+{
+ switch (reg) {
+ case WM8523_DEVICE_ID:
+ case WM8523_REVISION:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int wm8523_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, WM8523_DEVICE_ID, 0);
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -10000, 25, 0);
+
+static const char *wm8523_zd_count_text[] = {
+ "1024",
+ "2048",
+};
+
+static const struct soc_enum wm8523_zc_count =
+ SOC_ENUM_SINGLE(WM8523_ZERO_DETECT, 0, 2, wm8523_zd_count_text);
+
+static const struct snd_kcontrol_new wm8523_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Playback Volume", WM8523_DAC_GAINL, WM8523_DAC_GAINR,
+ 0, 448, 0, dac_tlv),
+SOC_SINGLE("ZC Switch", WM8523_DAC_CTRL3, 4, 1, 0),
+SOC_SINGLE("Playback Deemphasis Switch", WM8523_AIF_CTRL1, 8, 1, 0),
+SOC_DOUBLE("Playback Switch", WM8523_DAC_CTRL3, 2, 3, 1, 1),
+SOC_SINGLE("Volume Ramp Up Switch", WM8523_DAC_CTRL3, 1, 1, 0),
+SOC_SINGLE("Volume Ramp Down Switch", WM8523_DAC_CTRL3, 0, 1, 0),
+SOC_ENUM("Zero Detect Count", wm8523_zc_count),
+};
+
+static const struct snd_soc_dapm_widget wm8523_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("LINEVOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEVOUTR"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ { "LINEVOUTL", NULL, "DAC" },
+ { "LINEVOUTR", NULL, "DAC" },
+};
+
+static int wm8523_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8523_dapm_widgets,
+ ARRAY_SIZE(wm8523_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static struct {
+ int value;
+ int ratio;
+} lrclk_ratios[WM8523_NUM_RATES] = {
+ { 1, 128 },
+ { 2, 192 },
+ { 3, 256 },
+ { 4, 384 },
+ { 5, 512 },
+ { 6, 768 },
+ { 7, 1152 },
+};
+
+static int wm8523_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8523_priv *wm8523 = codec->private_data;
+
+ /* The set of sample rates that can be supported depends on the
+ * MCLK supplied to the CODEC - enforce this.
+ */
+ if (!wm8523->sysclk) {
+ dev_err(codec->dev,
+ "No MCLK configured, call set_sysclk() on init\n");
+ return -EINVAL;
+ }
+
+ return 0;
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &wm8523->rate_constraint);
+
+ return 0;
+}
+
+static int wm8523_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct wm8523_priv *wm8523 = codec->private_data;
+ int i;
+ u16 aifctrl1 = snd_soc_read(codec, WM8523_AIF_CTRL1);
+ u16 aifctrl2 = snd_soc_read(codec, WM8523_AIF_CTRL2);
+
+ /* Find a supported LRCLK ratio */
+ for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
+ if (wm8523->sysclk / params_rate(params) ==
+ lrclk_ratios[i].ratio)
+ break;
+ }
+
+ /* Should never happen, should be handled by constraints */
+ if (i == ARRAY_SIZE(lrclk_ratios)) {
+ dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n",
+ wm8523->sysclk / params_rate(params));
+ return -EINVAL;
+ }
+
+ aifctrl2 &= ~WM8523_SR_MASK;
+ aifctrl2 |= lrclk_ratios[i].value;
+
+ aifctrl1 &= ~WM8523_WL_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ aifctrl1 |= 0x8;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ aifctrl1 |= 0x10;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ aifctrl1 |= 0x18;
+ break;
+ }
+
+ snd_soc_write(codec, WM8523_AIF_CTRL1, aifctrl1);
+ snd_soc_write(codec, WM8523_AIF_CTRL2, aifctrl2);
+
+ return 0;
+}
+
+static int wm8523_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8523_priv *wm8523 = codec->private_data;
+ unsigned int val;
+ int i;
+
+ wm8523->sysclk = freq;
+
+ wm8523->rate_constraint.count = 0;
+ for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
+ val = freq / lrclk_ratios[i].ratio;
+ /* Check that it's a standard rate since core can't
+ * cope with others and having the odd rates confuses
+ * constraint matching.
+ */
+ switch (val) {
+ case 8000:
+ case 11025:
+ case 16000:
+ case 22050:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 64000:
+ case 88200:
+ case 96000:
+ case 176400:
+ case 192000:
+ dev_dbg(codec->dev, "Supported sample rate: %dHz\n",
+ val);
+ wm8523->rate_constraint_list[i] = val;
+ wm8523->rate_constraint.count++;
+ break;
+ default:
+ dev_dbg(codec->dev, "Skipping sample rate: %dHz\n",
+ val);
+ }
+ }
+
+ /* Need at least one supported rate... */
+ if (wm8523->rate_constraint.count == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static int wm8523_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 aifctrl1 = snd_soc_read(codec, WM8523_AIF_CTRL1);
+
+ aifctrl1 &= ~(WM8523_BCLK_INV_MASK | WM8523_LRCLK_INV_MASK |
+ WM8523_FMT_MASK | WM8523_AIF_MSTR_MASK);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aifctrl1 |= WM8523_AIF_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ aifctrl1 |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aifctrl1 |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ aifctrl1 |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ aifctrl1 |= 0x0023;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aifctrl1 |= WM8523_BCLK_INV | WM8523_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aifctrl1 |= WM8523_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aifctrl1 |= WM8523_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8523_AIF_CTRL1, aifctrl1);
+
+ return 0;
+}
+
+static int wm8523_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8523_priv *wm8523 = codec->private_data;
+ int ret, i;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /* Full power on */
+ snd_soc_update_bits(codec, WM8523_PSCTRL1,
+ WM8523_SYS_ENA_MASK, 3);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
+ wm8523->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* Initial power up */
+ snd_soc_update_bits(codec, WM8523_PSCTRL1,
+ WM8523_SYS_ENA_MASK, 1);
+
+ /* Sync back default/cached values */
+ for (i = WM8523_AIF_CTRL1;
+ i < WM8523_MAX_REGISTER; i++)
+ snd_soc_write(codec, i, wm8523->reg_cache[i]);
+
+
+ msleep(100);
+ }
+
+ /* Power up to mute */
+ snd_soc_update_bits(codec, WM8523_PSCTRL1,
+ WM8523_SYS_ENA_MASK, 2);
+
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ /* The chip runs through the power down sequence for us. */
+ snd_soc_update_bits(codec, WM8523_PSCTRL1,
+ WM8523_SYS_ENA_MASK, 0);
+ msleep(100);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies),
+ wm8523->supplies);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8523_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM8523_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops wm8523_dai_ops = {
+ .startup = wm8523_startup,
+ .hw_params = wm8523_hw_params,
+ .set_sysclk = wm8523_set_dai_sysclk,
+ .set_fmt = wm8523_set_dai_fmt,
+};
+
+struct snd_soc_dai wm8523_dai = {
+ .name = "WM8523",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2, /* Mono modes not yet supported */
+ .channels_max = 2,
+ .rates = WM8523_RATES,
+ .formats = WM8523_FORMATS,
+ },
+ .ops = &wm8523_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm8523_dai);
+
+#ifdef CONFIG_PM
+static int wm8523_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ wm8523_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8523_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define wm8523_suspend NULL
+#define wm8523_resume NULL
+#endif
+
+static int wm8523_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (wm8523_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = wm8523_codec;
+ codec = wm8523_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, wm8523_snd_controls,
+ ARRAY_SIZE(wm8523_snd_controls));
+ wm8523_add_widgets(codec);
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to register card: %d\n", ret);
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ return ret;
+}
+
+static int wm8523_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8523 = {
+ .probe = wm8523_probe,
+ .remove = wm8523_remove,
+ .suspend = wm8523_suspend,
+ .resume = wm8523_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8523);
+
+static int wm8523_register(struct wm8523_priv *wm8523,
+ enum snd_soc_control_type control)
+{
+ int ret;
+ struct snd_soc_codec *codec = &wm8523->codec;
+ int i;
+
+ if (wm8523_codec) {
+ dev_err(codec->dev, "Another WM8523 is registered\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->private_data = wm8523;
+ codec->name = "WM8523";
+ codec->owner = THIS_MODULE;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8523_set_bias_level;
+ codec->dai = &wm8523_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = WM8523_REGISTER_COUNT;
+ codec->reg_cache = &wm8523->reg_cache;
+ codec->volatile_register = wm8523_volatile_register;
+
+ wm8523->rate_constraint.list = &wm8523->rate_constraint_list[0];
+ wm8523->rate_constraint.count =
+ ARRAY_SIZE(wm8523->rate_constraint_list);
+
+ memcpy(codec->reg_cache, wm8523_reg, sizeof(wm8523_reg));
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, control);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8523->supplies); i++)
+ wm8523->supplies[i].supply = wm8523_supply_names[i];
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8523->supplies),
+ wm8523->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ goto err;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
+ wm8523->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_get;
+ }
+
+ ret = snd_soc_read(codec, WM8523_DEVICE_ID);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read ID register\n");
+ goto err_enable;
+ }
+ if (ret != wm8523_reg[WM8523_DEVICE_ID]) {
+ dev_err(codec->dev, "Device is not a WM8523, ID is %x\n", ret);
+ ret = -EINVAL;
+ goto err_enable;
+ }
+
+ ret = snd_soc_read(codec, WM8523_REVISION);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read revision register\n");
+ goto err_enable;
+ }
+ dev_info(codec->dev, "revision %c\n",
+ (ret & WM8523_CHIP_REV_MASK) + 'A');
+
+ ret = wm8523_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err_enable;
+ }
+
+ wm8523_dai.dev = codec->dev;
+
+ /* Change some default settings - latch VU and enable ZC */
+ wm8523->reg_cache[WM8523_DAC_GAINR] |= WM8523_DACR_VU;
+ wm8523->reg_cache[WM8523_DAC_CTRL3] |= WM8523_ZC;
+
+ wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Bias level configuration will have done an extra enable */
+ regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+
+ wm8523_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_dai(&wm8523_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ snd_soc_unregister_codec(codec);
+ return ret;
+ }
+
+ return 0;
+
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+err_get:
+ regulator_bulk_free(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+err:
+ kfree(wm8523);
+ return ret;
+}
+
+static void wm8523_unregister(struct wm8523_priv *wm8523)
+{
+ wm8523_set_bias_level(&wm8523->codec, SND_SOC_BIAS_OFF);
+ regulator_bulk_free(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+ snd_soc_unregister_dai(&wm8523_dai);
+ snd_soc_unregister_codec(&wm8523->codec);
+ kfree(wm8523);
+ wm8523_codec = NULL;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8523_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8523_priv *wm8523;
+ struct snd_soc_codec *codec;
+
+ wm8523 = kzalloc(sizeof(struct wm8523_priv), GFP_KERNEL);
+ if (wm8523 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8523->codec;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+
+ i2c_set_clientdata(i2c, wm8523);
+ codec->control_data = i2c;
+
+ codec->dev = &i2c->dev;
+
+ return wm8523_register(wm8523, SND_SOC_I2C);
+}
+
+static __devexit int wm8523_i2c_remove(struct i2c_client *client)
+{
+ struct wm8523_priv *wm8523 = i2c_get_clientdata(client);
+ wm8523_unregister(wm8523);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8523_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&i2c->dev);
+}
+
+static int wm8523_i2c_resume(struct i2c_client *i2c)
+{
+ return snd_soc_resume_device(&i2c->dev);
+}
+#else
+#define wm8523_i2c_suspend NULL
+#define wm8523_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm8523_i2c_id[] = {
+ { "wm8523", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id);
+
+static struct i2c_driver wm8523_i2c_driver = {
+ .driver = {
+ .name = "WM8523",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8523_i2c_probe,
+ .remove = __devexit_p(wm8523_i2c_remove),
+ .suspend = wm8523_i2c_suspend,
+ .resume = wm8523_i2c_resume,
+ .id_table = wm8523_i2c_id,
+};
+#endif
+
+static int __init wm8523_modinit(void)
+{
+ int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&wm8523_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8523 I2C driver: %d\n",
+ ret);
+ }
+#endif
+ return 0;
+}
+module_init(wm8523_modinit);
+
+static void __exit wm8523_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8523_i2c_driver);
+#endif
+}
+module_exit(wm8523_exit);
+
+MODULE_DESCRIPTION("ASoC WM8523 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8523.h b/sound/soc/codecs/wm8523.h
new file mode 100644
index 00000000000..1aa9ce3e135
--- /dev/null
+++ b/sound/soc/codecs/wm8523.h
@@ -0,0 +1,160 @@
+/*
+ * wm8523.h -- WM8423 ASoC driver
+ *
+ * Copyright 2009 Wolfson Microelectronics, plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * Based on wm8753.h
+ *
+ * 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 _WM8523_H
+#define _WM8523_H
+
+/*
+ * Register values.
+ */
+#define WM8523_DEVICE_ID 0x00
+#define WM8523_REVISION 0x01
+#define WM8523_PSCTRL1 0x02
+#define WM8523_AIF_CTRL1 0x03
+#define WM8523_AIF_CTRL2 0x04
+#define WM8523_DAC_CTRL3 0x05
+#define WM8523_DAC_GAINL 0x06
+#define WM8523_DAC_GAINR 0x07
+#define WM8523_ZERO_DETECT 0x08
+
+#define WM8523_REGISTER_COUNT 9
+#define WM8523_MAX_REGISTER 0x08
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - DEVICE_ID
+ */
+#define WM8523_CHIP_ID_MASK 0xFFFF /* CHIP_ID - [15:0] */
+#define WM8523_CHIP_ID_SHIFT 0 /* CHIP_ID - [15:0] */
+#define WM8523_CHIP_ID_WIDTH 16 /* CHIP_ID - [15:0] */
+
+/*
+ * R1 (0x01) - REVISION
+ */
+#define WM8523_CHIP_REV_MASK 0x0007 /* CHIP_REV - [2:0] */
+#define WM8523_CHIP_REV_SHIFT 0 /* CHIP_REV - [2:0] */
+#define WM8523_CHIP_REV_WIDTH 3 /* CHIP_REV - [2:0] */
+
+/*
+ * R2 (0x02) - PSCTRL1
+ */
+#define WM8523_SYS_ENA_MASK 0x0003 /* SYS_ENA - [1:0] */
+#define WM8523_SYS_ENA_SHIFT 0 /* SYS_ENA - [1:0] */
+#define WM8523_SYS_ENA_WIDTH 2 /* SYS_ENA - [1:0] */
+
+/*
+ * R3 (0x03) - AIF_CTRL1
+ */
+#define WM8523_TDM_MODE_MASK 0x1800 /* TDM_MODE - [12:11] */
+#define WM8523_TDM_MODE_SHIFT 11 /* TDM_MODE - [12:11] */
+#define WM8523_TDM_MODE_WIDTH 2 /* TDM_MODE - [12:11] */
+#define WM8523_TDM_SLOT_MASK 0x0600 /* TDM_SLOT - [10:9] */
+#define WM8523_TDM_SLOT_SHIFT 9 /* TDM_SLOT - [10:9] */
+#define WM8523_TDM_SLOT_WIDTH 2 /* TDM_SLOT - [10:9] */
+#define WM8523_DEEMPH 0x0100 /* DEEMPH */
+#define WM8523_DEEMPH_MASK 0x0100 /* DEEMPH */
+#define WM8523_DEEMPH_SHIFT 8 /* DEEMPH */
+#define WM8523_DEEMPH_WIDTH 1 /* DEEMPH */
+#define WM8523_AIF_MSTR 0x0080 /* AIF_MSTR */
+#define WM8523_AIF_MSTR_MASK 0x0080 /* AIF_MSTR */
+#define WM8523_AIF_MSTR_SHIFT 7 /* AIF_MSTR */
+#define WM8523_AIF_MSTR_WIDTH 1 /* AIF_MSTR */
+#define WM8523_LRCLK_INV 0x0040 /* LRCLK_INV */
+#define WM8523_LRCLK_INV_MASK 0x0040 /* LRCLK_INV */
+#define WM8523_LRCLK_INV_SHIFT 6 /* LRCLK_INV */
+#define WM8523_LRCLK_INV_WIDTH 1 /* LRCLK_INV */
+#define WM8523_BCLK_INV 0x0020 /* BCLK_INV */
+#define WM8523_BCLK_INV_MASK 0x0020 /* BCLK_INV */
+#define WM8523_BCLK_INV_SHIFT 5 /* BCLK_INV */
+#define WM8523_BCLK_INV_WIDTH 1 /* BCLK_INV */
+#define WM8523_WL_MASK 0x0018 /* WL - [4:3] */
+#define WM8523_WL_SHIFT 3 /* WL - [4:3] */
+#define WM8523_WL_WIDTH 2 /* WL - [4:3] */
+#define WM8523_FMT_MASK 0x0007 /* FMT - [2:0] */
+#define WM8523_FMT_SHIFT 0 /* FMT - [2:0] */
+#define WM8523_FMT_WIDTH 3 /* FMT - [2:0] */
+
+/*
+ * R4 (0x04) - AIF_CTRL2
+ */
+#define WM8523_DAC_OP_MUX_MASK 0x00C0 /* DAC_OP_MUX - [7:6] */
+#define WM8523_DAC_OP_MUX_SHIFT 6 /* DAC_OP_MUX - [7:6] */
+#define WM8523_DAC_OP_MUX_WIDTH 2 /* DAC_OP_MUX - [7:6] */
+#define WM8523_BCLKDIV_MASK 0x0038 /* BCLKDIV - [5:3] */
+#define WM8523_BCLKDIV_SHIFT 3 /* BCLKDIV - [5:3] */
+#define WM8523_BCLKDIV_WIDTH 3 /* BCLKDIV - [5:3] */
+#define WM8523_SR_MASK 0x0007 /* SR - [2:0] */
+#define WM8523_SR_SHIFT 0 /* SR - [2:0] */
+#define WM8523_SR_WIDTH 3 /* SR - [2:0] */
+
+/*
+ * R5 (0x05) - DAC_CTRL3
+ */
+#define WM8523_ZC 0x0010 /* ZC */
+#define WM8523_ZC_MASK 0x0010 /* ZC */
+#define WM8523_ZC_SHIFT 4 /* ZC */
+#define WM8523_ZC_WIDTH 1 /* ZC */
+#define WM8523_DACR 0x0008 /* DACR */
+#define WM8523_DACR_MASK 0x0008 /* DACR */
+#define WM8523_DACR_SHIFT 3 /* DACR */
+#define WM8523_DACR_WIDTH 1 /* DACR */
+#define WM8523_DACL 0x0004 /* DACL */
+#define WM8523_DACL_MASK 0x0004 /* DACL */
+#define WM8523_DACL_SHIFT 2 /* DACL */
+#define WM8523_DACL_WIDTH 1 /* DACL */
+#define WM8523_VOL_UP_RAMP 0x0002 /* VOL_UP_RAMP */
+#define WM8523_VOL_UP_RAMP_MASK 0x0002 /* VOL_UP_RAMP */
+#define WM8523_VOL_UP_RAMP_SHIFT 1 /* VOL_UP_RAMP */
+#define WM8523_VOL_UP_RAMP_WIDTH 1 /* VOL_UP_RAMP */
+#define WM8523_VOL_DOWN_RAMP 0x0001 /* VOL_DOWN_RAMP */
+#define WM8523_VOL_DOWN_RAMP_MASK 0x0001 /* VOL_DOWN_RAMP */
+#define WM8523_VOL_DOWN_RAMP_SHIFT 0 /* VOL_DOWN_RAMP */
+#define WM8523_VOL_DOWN_RAMP_WIDTH 1 /* VOL_DOWN_RAMP */
+
+/*
+ * R6 (0x06) - DAC_GAINL
+ */
+#define WM8523_DACL_VU 0x0200 /* DACL_VU */
+#define WM8523_DACL_VU_MASK 0x0200 /* DACL_VU */
+#define WM8523_DACL_VU_SHIFT 9 /* DACL_VU */
+#define WM8523_DACL_VU_WIDTH 1 /* DACL_VU */
+#define WM8523_DACL_VOL_MASK 0x01FF /* DACL_VOL - [8:0] */
+#define WM8523_DACL_VOL_SHIFT 0 /* DACL_VOL - [8:0] */
+#define WM8523_DACL_VOL_WIDTH 9 /* DACL_VOL - [8:0] */
+
+/*
+ * R7 (0x07) - DAC_GAINR
+ */
+#define WM8523_DACR_VU 0x0200 /* DACR_VU */
+#define WM8523_DACR_VU_MASK 0x0200 /* DACR_VU */
+#define WM8523_DACR_VU_SHIFT 9 /* DACR_VU */
+#define WM8523_DACR_VU_WIDTH 1 /* DACR_VU */
+#define WM8523_DACR_VOL_MASK 0x01FF /* DACR_VOL - [8:0] */
+#define WM8523_DACR_VOL_SHIFT 0 /* DACR_VOL - [8:0] */
+#define WM8523_DACR_VOL_WIDTH 9 /* DACR_VOL - [8:0] */
+
+/*
+ * R8 (0x08) - ZERO_DETECT
+ */
+#define WM8523_ZD_COUNT_MASK 0x0003 /* ZD_COUNT - [1:0] */
+#define WM8523_ZD_COUNT_SHIFT 0 /* ZD_COUNT - [1:0] */
+#define WM8523_ZD_COUNT_WIDTH 2 /* ZD_COUNT - [1:0] */
+
+extern struct snd_soc_dai wm8523_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8523;
+
+#endif
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 86c4b24db81..6bded8c7815 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -24,6 +24,8 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -187,82 +189,22 @@ struct pll_state {
unsigned int out;
};
+#define WM8580_NUM_SUPPLIES 3
+static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = {
+ "AVDD",
+ "DVDD",
+ "PVDD",
+};
+
/* codec private data */
struct wm8580_priv {
struct snd_soc_codec codec;
+ struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES];
u16 reg_cache[WM8580_MAX_REGISTER + 1];
struct pll_state a;
struct pll_state b;
};
-
-/*
- * read wm8580 register cache
- */
-static inline unsigned int wm8580_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- BUG_ON(reg >= ARRAY_SIZE(wm8580_reg));
- return cache[reg];
-}
-
-/*
- * write wm8580 register cache
- */
-static inline void wm8580_write_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
-
- cache[reg] = value;
-}
-
-/*
- * write to the WM8580 register space
- */
-static int wm8580_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- BUG_ON(reg >= ARRAY_SIZE(wm8580_reg));
-
- /* Registers are 9 bits wide */
- value &= 0x1ff;
-
- switch (reg) {
- case WM8580_RESET:
- /* Uncached */
- break;
- default:
- if (value == wm8580_read_reg_cache(codec, reg))
- return 0;
- }
-
- /* data is
- * D15..D9 WM8580 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8580_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-static inline unsigned int wm8580_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- switch (reg) {
- default:
- return wm8580_read_reg_cache(codec, reg);
- }
-}
-
static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
static int wm8580_out_vu(struct snd_kcontrol *kcontrol,
@@ -271,25 +213,22 @@ static int wm8580_out_vu(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u16 *reg_cache = codec->reg_cache;
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
int ret;
- u16 val;
/* Clear the register cache so we write without VU set */
- wm8580_write_reg_cache(codec, reg, 0);
- wm8580_write_reg_cache(codec, reg2, 0);
+ reg_cache[reg] = 0;
+ reg_cache[reg2] = 0;
ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
if (ret < 0)
return ret;
/* Now write again with the volume update bit set */
- val = wm8580_read_reg_cache(codec, reg);
- wm8580_write(codec, reg, val | 0x0100);
-
- val = wm8580_read_reg_cache(codec, reg2);
- wm8580_write(codec, reg2, val | 0x0100);
+ snd_soc_update_bits(codec, reg, 0x100, 0x100);
+ snd_soc_update_bits(codec, reg2, 0x100, 0x100);
return 0;
}
@@ -512,27 +451,27 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
/* Always disable the PLL - it is not safe to leave it running
* while reprogramming it.
*/
- reg = wm8580_read(codec, WM8580_PWRDN2);
- wm8580_write(codec, WM8580_PWRDN2, reg | pwr_mask);
+ reg = snd_soc_read(codec, WM8580_PWRDN2);
+ snd_soc_write(codec, WM8580_PWRDN2, reg | pwr_mask);
if (!freq_in || !freq_out)
return 0;
- wm8580_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff);
- wm8580_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0xff);
- wm8580_write(codec, WM8580_PLLA3 + offset,
+ snd_soc_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff);
+ snd_soc_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8580_PLLA3 + offset,
(pll_div.k >> 18 & 0xf) | (pll_div.n << 4));
- reg = wm8580_read(codec, WM8580_PLLA4 + offset);
- reg &= ~0x3f;
+ reg = snd_soc_read(codec, WM8580_PLLA4 + offset);
+ reg &= ~0x1b;
reg |= pll_div.prescale | pll_div.postscale << 1 |
pll_div.freqmode << 3;
- wm8580_write(codec, WM8580_PLLA4 + offset, reg);
+ snd_soc_write(codec, WM8580_PLLA4 + offset, reg);
/* All done, turn it on */
- reg = wm8580_read(codec, WM8580_PWRDN2);
- wm8580_write(codec, WM8580_PWRDN2, reg & ~pwr_mask);
+ reg = snd_soc_read(codec, WM8580_PWRDN2);
+ snd_soc_write(codec, WM8580_PWRDN2, reg & ~pwr_mask);
return 0;
}
@@ -547,7 +486,7 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->id);
+ u16 paifb = snd_soc_read(codec, WM8580_PAIF3 + dai->id);
paifb &= ~WM8580_AIF_LENGTH_MASK;
/* bit size */
@@ -567,7 +506,7 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- wm8580_write(codec, WM8580_PAIF3 + dai->id, paifb);
+ snd_soc_write(codec, WM8580_PAIF3 + dai->id, paifb);
return 0;
}
@@ -579,8 +518,8 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int aifb;
int can_invert_lrclk;
- aifa = wm8580_read(codec, WM8580_PAIF1 + codec_dai->id);
- aifb = wm8580_read(codec, WM8580_PAIF3 + codec_dai->id);
+ aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->id);
+ aifb = snd_soc_read(codec, WM8580_PAIF3 + codec_dai->id);
aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP);
@@ -646,8 +585,8 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8580_write(codec, WM8580_PAIF1 + codec_dai->id, aifa);
- wm8580_write(codec, WM8580_PAIF3 + codec_dai->id, aifb);
+ snd_soc_write(codec, WM8580_PAIF1 + codec_dai->id, aifa);
+ snd_soc_write(codec, WM8580_PAIF3 + codec_dai->id, aifb);
return 0;
}
@@ -660,7 +599,7 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
switch (div_id) {
case WM8580_MCLK:
- reg = wm8580_read(codec, WM8580_PLLB4);
+ reg = snd_soc_read(codec, WM8580_PLLB4);
reg &= ~WM8580_PLLB4_MCLKOUTSRC_MASK;
switch (div) {
@@ -682,11 +621,11 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
default:
return -EINVAL;
}
- wm8580_write(codec, WM8580_PLLB4, reg);
+ snd_soc_write(codec, WM8580_PLLB4, reg);
break;
case WM8580_DAC_CLKSEL:
- reg = wm8580_read(codec, WM8580_CLKSEL);
+ reg = snd_soc_read(codec, WM8580_CLKSEL);
reg &= ~WM8580_CLKSEL_DAC_CLKSEL_MASK;
switch (div) {
@@ -704,11 +643,11 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
default:
return -EINVAL;
}
- wm8580_write(codec, WM8580_CLKSEL, reg);
+ snd_soc_write(codec, WM8580_CLKSEL, reg);
break;
case WM8580_CLKOUTSRC:
- reg = wm8580_read(codec, WM8580_PLLB4);
+ reg = snd_soc_read(codec, WM8580_PLLB4);
reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK;
switch (div) {
@@ -730,7 +669,7 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
default:
return -EINVAL;
}
- wm8580_write(codec, WM8580_PLLB4, reg);
+ snd_soc_write(codec, WM8580_PLLB4, reg);
break;
default:
@@ -745,14 +684,14 @@ static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute)
struct snd_soc_codec *codec = codec_dai->codec;
unsigned int reg;
- reg = wm8580_read(codec, WM8580_DAC_CONTROL5);
+ reg = snd_soc_read(codec, WM8580_DAC_CONTROL5);
if (mute)
reg |= WM8580_DAC_CONTROL5_MUTEALL;
else
reg &= ~WM8580_DAC_CONTROL5_MUTEALL;
- wm8580_write(codec, WM8580_DAC_CONTROL5, reg);
+ snd_soc_write(codec, WM8580_DAC_CONTROL5, reg);
return 0;
}
@@ -769,20 +708,20 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Power up and get individual control of the DACs */
- reg = wm8580_read(codec, WM8580_PWRDN1);
+ reg = snd_soc_read(codec, WM8580_PWRDN1);
reg &= ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD);
- wm8580_write(codec, WM8580_PWRDN1, reg);
+ snd_soc_write(codec, WM8580_PWRDN1, reg);
/* Make VMID high impedence */
- reg = wm8580_read(codec, WM8580_ADC_CONTROL1);
+ reg = snd_soc_read(codec, WM8580_ADC_CONTROL1);
reg &= ~0x100;
- wm8580_write(codec, WM8580_ADC_CONTROL1, reg);
+ snd_soc_write(codec, WM8580_ADC_CONTROL1, reg);
}
break;
case SND_SOC_BIAS_OFF:
- reg = wm8580_read(codec, WM8580_PWRDN1);
- wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN);
+ reg = snd_soc_read(codec, WM8580_PWRDN1);
+ snd_soc_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN);
break;
}
codec->bias_level = level;
@@ -893,7 +832,8 @@ struct snd_soc_codec_device soc_codec_dev_wm8580 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
-static int wm8580_register(struct wm8580_priv *wm8580)
+static int wm8580_register(struct wm8580_priv *wm8580,
+ enum snd_soc_control_type control)
{
int ret, i;
struct snd_soc_codec *codec = &wm8580->codec;
@@ -911,8 +851,6 @@ static int wm8580_register(struct wm8580_priv *wm8580)
codec->private_data = wm8580;
codec->name = "WM8580";
codec->owner = THIS_MODULE;
- codec->read = wm8580_read_reg_cache;
- codec->write = wm8580_write;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8580_set_bias_level;
codec->dai = wm8580_dai;
@@ -922,11 +860,34 @@ static int wm8580_register(struct wm8580_priv *wm8580)
memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg));
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8580->supplies); i++)
+ wm8580->supplies[i].supply = wm8580_supply_names[i];
+
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8580->supplies),
+ wm8580->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+ goto err;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies),
+ wm8580->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+ goto err_regulator_get;
+ }
+
/* Get the codec into a known state */
- ret = wm8580_write(codec, WM8580_RESET, 0);
+ ret = snd_soc_write(codec, WM8580_RESET, 0);
if (ret != 0) {
dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
- goto err;
+ goto err_regulator_enable;
}
for (i = 0; i < ARRAY_SIZE(wm8580_dai); i++)
@@ -939,7 +900,7 @@ static int wm8580_register(struct wm8580_priv *wm8580)
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
- goto err;
+ goto err_regulator_enable;
}
ret = snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
@@ -952,6 +913,10 @@ static int wm8580_register(struct wm8580_priv *wm8580)
err_codec:
snd_soc_unregister_codec(codec);
+err_regulator_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
+err_regulator_get:
+ regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
err:
kfree(wm8580);
return ret;
@@ -962,6 +927,8 @@ static void wm8580_unregister(struct wm8580_priv *wm8580)
wm8580_set_bias_level(&wm8580->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
snd_soc_unregister_codec(&wm8580->codec);
+ regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
+ regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
kfree(wm8580);
wm8580_codec = NULL;
}
@@ -978,14 +945,13 @@ static int wm8580_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
codec = &wm8580->codec;
- codec->hw_write = (hw_write_t)i2c_master_send;
i2c_set_clientdata(i2c, wm8580);
codec->control_data = i2c;
codec->dev = &i2c->dev;
- return wm8580_register(wm8580);
+ return wm8580_register(wm8580, SND_SOC_I2C);
}
static int wm8580_i2c_remove(struct i2c_client *client)
@@ -995,6 +961,21 @@ static int wm8580_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM
+static int wm8580_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8580_i2c_resume(struct i2c_client *client)
+{
+ return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8580_i2c_suspend NULL
+#define wm8580_i2c_resume NULL
+#endif
+
static const struct i2c_device_id wm8580_i2c_id[] = {
{ "wm8580", 0 },
{ }
@@ -1008,6 +989,8 @@ static struct i2c_driver wm8580_i2c_driver = {
},
.probe = wm8580_i2c_probe,
.remove = wm8580_i2c_remove,
+ .suspend = wm8580_i2c_suspend,
+ .resume = wm8580_i2c_resume,
.id_table = wm8580_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index e7ff2121ede..16e969a762c 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -43,45 +43,6 @@ static const u16 wm8728_reg_defaults[] = {
0x100,
};
-static inline unsigned int wm8728_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- BUG_ON(reg >= ARRAY_SIZE(wm8728_reg_defaults));
- return cache[reg];
-}
-
-static inline void wm8728_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- BUG_ON(reg >= ARRAY_SIZE(wm8728_reg_defaults));
- cache[reg] = value;
-}
-
-/*
- * write to the WM8728 register space
- */
-static int wm8728_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8728 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8728_write_reg_cache(codec, reg, value);
-
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1);
static const struct snd_kcontrol_new wm8728_snd_controls[] = {
@@ -121,12 +82,12 @@ static int wm8728_add_widgets(struct snd_soc_codec *codec)
static int wm8728_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+ u16 mute_reg = snd_soc_read(codec, WM8728_DACCTL);
if (mute)
- wm8728_write(codec, WM8728_DACCTL, mute_reg | 1);
+ snd_soc_write(codec, WM8728_DACCTL, mute_reg | 1);
else
- wm8728_write(codec, WM8728_DACCTL, mute_reg & ~1);
+ snd_soc_write(codec, WM8728_DACCTL, mute_reg & ~1);
return 0;
}
@@ -138,7 +99,7 @@ static int wm8728_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- u16 dac = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+ u16 dac = snd_soc_read(codec, WM8728_DACCTL);
dac &= ~0x18;
@@ -155,7 +116,7 @@ static int wm8728_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- wm8728_write(codec, WM8728_DACCTL, dac);
+ snd_soc_write(codec, WM8728_DACCTL, dac);
return 0;
}
@@ -164,7 +125,7 @@ static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
- u16 iface = wm8728_read_reg_cache(codec, WM8728_IFCTL);
+ u16 iface = snd_soc_read(codec, WM8728_IFCTL);
/* Currently only I2S is supported by the driver, though the
* hardware is more flexible.
@@ -204,7 +165,7 @@ static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8728_write(codec, WM8728_IFCTL, iface);
+ snd_soc_write(codec, WM8728_IFCTL, iface);
return 0;
}
@@ -220,19 +181,19 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Power everything up... */
- reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
- wm8728_write(codec, WM8728_DACCTL, reg & ~0x4);
+ reg = snd_soc_read(codec, WM8728_DACCTL);
+ snd_soc_write(codec, WM8728_DACCTL, reg & ~0x4);
/* ..then sync in the register cache. */
for (i = 0; i < ARRAY_SIZE(wm8728_reg_defaults); i++)
- wm8728_write(codec, i,
- wm8728_read_reg_cache(codec, i));
+ snd_soc_write(codec, i,
+ snd_soc_read(codec, i));
}
break;
case SND_SOC_BIAS_OFF:
- reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
- wm8728_write(codec, WM8728_DACCTL, reg | 0x4);
+ reg = snd_soc_read(codec, WM8728_DACCTL);
+ snd_soc_write(codec, WM8728_DACCTL, reg | 0x4);
break;
}
codec->bias_level = level;
@@ -287,15 +248,14 @@ static int wm8728_resume(struct platform_device *pdev)
* initialise the WM8728 driver
* register the mixer and dsp interfaces with the kernel
*/
-static int wm8728_init(struct snd_soc_device *socdev)
+static int wm8728_init(struct snd_soc_device *socdev,
+ enum snd_soc_control_type control)
{
struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
codec->name = "WM8728";
codec->owner = THIS_MODULE;
- codec->read = wm8728_read_reg_cache;
- codec->write = wm8728_write;
codec->set_bias_level = wm8728_set_bias_level;
codec->dai = &wm8728_dai;
codec->num_dai = 1;
@@ -307,11 +267,18 @@ static int wm8728_init(struct snd_soc_device *socdev)
if (codec->reg_cache == NULL)
return -ENOMEM;
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8728: failed to configure cache I/O: %d\n",
+ ret);
+ goto err;
+ }
+
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "wm8728: failed to create pcms\n");
- goto pcm_err;
+ goto err;
}
/* power on device */
@@ -331,7 +298,7 @@ static int wm8728_init(struct snd_soc_device *socdev)
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-pcm_err:
+err:
kfree(codec->reg_cache);
return ret;
}
@@ -357,7 +324,7 @@ static int wm8728_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, codec);
codec->control_data = i2c;
- ret = wm8728_init(socdev);
+ ret = wm8728_init(socdev, SND_SOC_I2C);
if (ret < 0)
pr_err("failed to initialise WM8728\n");
@@ -437,7 +404,7 @@ static int __devinit wm8728_spi_probe(struct spi_device *spi)
codec->control_data = spi;
- ret = wm8728_init(socdev);
+ ret = wm8728_init(socdev, SND_SOC_SPI);
if (ret < 0)
dev_err(&spi->dev, "failed to initialise WM8728\n");
@@ -458,30 +425,6 @@ static struct spi_driver wm8728_spi_driver = {
.probe = wm8728_spi_probe,
.remove = __devexit_p(wm8728_spi_remove),
};
-
-static int wm8728_spi_write(struct spi_device *spi, const char *data, int len)
-{
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
-
- spi_message_init(&m);
- memset(&t, 0, (sizeof t));
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
-}
#endif /* CONFIG_SPI_MASTER */
static int wm8728_probe(struct platform_device *pdev)
@@ -506,13 +449,11 @@ static int wm8728_probe(struct platform_device *pdev)
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
- codec->hw_write = (hw_write_t)i2c_master_send;
ret = wm8728_add_i2c_device(pdev, setup);
}
#endif
#if defined(CONFIG_SPI_MASTER)
if (setup->spi) {
- codec->hw_write = (hw_write_t)wm8728_spi_write;
ret = spi_register_driver(&wm8728_spi_driver);
if (ret != 0)
printk(KERN_ERR "can't add spi driver");
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 7a205876ef4..d3fd4f28d96 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -26,6 +26,7 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
+#include <sound/tlv.h>
#include "wm8731.h"
@@ -39,9 +40,6 @@ struct wm8731_priv {
unsigned int sysclk;
};
-#ifdef CONFIG_SPI_MASTER
-static int wm8731_spi_write(struct spi_device *spi, const char *data, int len);
-#endif
/*
* wm8731 register cache
@@ -50,60 +48,12 @@ static int wm8731_spi_write(struct spi_device *spi, const char *data, int len);
* There is no point in caching the reset register
*/
static const u16 wm8731_reg[WM8731_CACHEREGNUM] = {
- 0x0097, 0x0097, 0x0079, 0x0079,
- 0x000a, 0x0008, 0x009f, 0x000a,
- 0x0000, 0x0000
+ 0x0097, 0x0097, 0x0079, 0x0079,
+ 0x000a, 0x0008, 0x009f, 0x000a,
+ 0x0000, 0x0000
};
-/*
- * read wm8731 register cache
- */
-static inline unsigned int wm8731_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg == WM8731_RESET)
- return 0;
- if (reg >= WM8731_CACHEREGNUM)
- return -1;
- return cache[reg];
-}
-
-/*
- * write wm8731 register cache
- */
-static inline void wm8731_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg >= WM8731_CACHEREGNUM)
- return;
- cache[reg] = value;
-}
-
-/*
- * write to the WM8731 register space
- */
-static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8731 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8731_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define wm8731_reset(c) wm8731_write(c, WM8731_RESET, 0)
+#define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0)
static const char *wm8731_input_select[] = {"Line In", "Mic"};
static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
@@ -113,20 +63,26 @@ static const struct soc_enum wm8731_enum[] = {
SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph),
};
+static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+
static const struct snd_kcontrol_new wm8731_snd_controls[] = {
-SOC_DOUBLE_R("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
- 0, 127, 0),
+SOC_DOUBLE_R_TLV("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
+ 0, 127, 0, out_tlv),
SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V,
7, 1, 0),
-SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0),
+SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0,
+ in_tlv),
SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),
SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0),
-SOC_SINGLE("Capture Mic Switch", WM8731_APANA, 1, 1, 1),
+SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1),
-SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1),
+SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1,
+ sidetone_tlv),
SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1),
SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
@@ -260,12 +216,12 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8731_priv *wm8731 = codec->private_data;
- u16 iface = wm8731_read_reg_cache(codec, WM8731_IFACE) & 0xfff3;
+ u16 iface = snd_soc_read(codec, WM8731_IFACE) & 0xfff3;
int i = get_coeff(wm8731->sysclk, params_rate(params));
u16 srate = (coeff_div[i].sr << 2) |
(coeff_div[i].bosr << 1) | coeff_div[i].usb;
- wm8731_write(codec, WM8731_SRATE, srate);
+ snd_soc_write(codec, WM8731_SRATE, srate);
/* bit size */
switch (params_format(params)) {
@@ -279,7 +235,7 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
break;
}
- wm8731_write(codec, WM8731_IFACE, iface);
+ snd_soc_write(codec, WM8731_IFACE, iface);
return 0;
}
@@ -291,7 +247,7 @@ static int wm8731_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = socdev->card->codec;
/* set active */
- wm8731_write(codec, WM8731_ACTIVE, 0x0001);
+ snd_soc_write(codec, WM8731_ACTIVE, 0x0001);
return 0;
}
@@ -306,19 +262,19 @@ static void wm8731_shutdown(struct snd_pcm_substream *substream,
/* deactivate */
if (!codec->active) {
udelay(50);
- wm8731_write(codec, WM8731_ACTIVE, 0x0);
+ snd_soc_write(codec, WM8731_ACTIVE, 0x0);
}
}
static int wm8731_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7;
+ u16 mute_reg = snd_soc_read(codec, WM8731_APDIGI) & 0xfff7;
if (mute)
- wm8731_write(codec, WM8731_APDIGI, mute_reg | 0x8);
+ snd_soc_write(codec, WM8731_APDIGI, mute_reg | 0x8);
else
- wm8731_write(codec, WM8731_APDIGI, mute_reg);
+ snd_soc_write(codec, WM8731_APDIGI, mute_reg);
return 0;
}
@@ -396,7 +352,7 @@ static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
/* set iface */
- wm8731_write(codec, WM8731_IFACE, iface);
+ snd_soc_write(codec, WM8731_IFACE, iface);
return 0;
}
@@ -412,12 +368,12 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
/* Clear PWROFF, gate CLKOUT, everything else as-is */
- reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
- wm8731_write(codec, WM8731_PWR, reg | 0x0040);
+ reg = snd_soc_read(codec, WM8731_PWR) & 0xff7f;
+ snd_soc_write(codec, WM8731_PWR, reg | 0x0040);
break;
case SND_SOC_BIAS_OFF:
- wm8731_write(codec, WM8731_ACTIVE, 0x0);
- wm8731_write(codec, WM8731_PWR, 0xffff);
+ snd_soc_write(codec, WM8731_ACTIVE, 0x0);
+ snd_soc_write(codec, WM8731_PWR, 0xffff);
break;
}
codec->bias_level = level;
@@ -457,15 +413,17 @@ struct snd_soc_dai wm8731_dai = {
.rates = WM8731_RATES,
.formats = WM8731_FORMATS,},
.ops = &wm8731_dai_ops,
+ .symmetric_rates = 1,
};
EXPORT_SYMBOL_GPL(wm8731_dai);
+#ifdef CONFIG_PM
static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
- wm8731_write(codec, WM8731_ACTIVE, 0x0);
+ snd_soc_write(codec, WM8731_ACTIVE, 0x0);
wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -488,6 +446,10 @@ static int wm8731_resume(struct platform_device *pdev)
wm8731_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
+#else
+#define wm8731_suspend NULL
+#define wm8731_resume NULL
+#endif
static int wm8731_probe(struct platform_device *pdev)
{
@@ -547,15 +509,16 @@ struct snd_soc_codec_device soc_codec_dev_wm8731 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
-static int wm8731_register(struct wm8731_priv *wm8731)
+static int wm8731_register(struct wm8731_priv *wm8731,
+ enum snd_soc_control_type control)
{
int ret;
struct snd_soc_codec *codec = &wm8731->codec;
- u16 reg;
if (wm8731_codec) {
dev_err(codec->dev, "Another WM8731 is registered\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
mutex_init(&codec->mutex);
@@ -565,8 +528,6 @@ static int wm8731_register(struct wm8731_priv *wm8731)
codec->private_data = wm8731;
codec->name = "WM8731";
codec->owner = THIS_MODULE;
- codec->read = wm8731_read_reg_cache;
- codec->write = wm8731_write;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8731_set_bias_level;
codec->dai = &wm8731_dai;
@@ -576,10 +537,16 @@ static int wm8731_register(struct wm8731_priv *wm8731)
memcpy(codec->reg_cache, wm8731_reg, sizeof(wm8731_reg));
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
ret = wm8731_reset(codec);
if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset\n");
- return ret;
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ goto err;
}
wm8731_dai.dev = codec->dev;
@@ -587,35 +554,36 @@ static int wm8731_register(struct wm8731_priv *wm8731)
wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Latch the update bits */
- reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
- wm8731_write(codec, WM8731_LOUT1V, reg & ~0x0100);
- reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V);
- wm8731_write(codec, WM8731_ROUT1V, reg & ~0x0100);
- reg = wm8731_read_reg_cache(codec, WM8731_LINVOL);
- wm8731_write(codec, WM8731_LINVOL, reg & ~0x0100);
- reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
- wm8731_write(codec, WM8731_RINVOL, reg & ~0x0100);
+ snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0);
+ snd_soc_update_bits(codec, WM8731_ROUT1V, 0x100, 0);
+ snd_soc_update_bits(codec, WM8731_LINVOL, 0x100, 0);
+ snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0);
/* Disable bypass path by default */
- reg = wm8731_read_reg_cache(codec, WM8731_APANA);
- wm8731_write(codec, WM8731_APANA, reg & ~0x4);
+ snd_soc_update_bits(codec, WM8731_APANA, 0x4, 0);
wm8731_codec = codec;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
- return ret;
+ goto err;
}
ret = snd_soc_register_dai(&wm8731_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
snd_soc_unregister_codec(codec);
- return ret;
+ goto err_codec;
}
return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ kfree(wm8731);
+ return ret;
}
static void wm8731_unregister(struct wm8731_priv *wm8731)
@@ -628,30 +596,6 @@ static void wm8731_unregister(struct wm8731_priv *wm8731)
}
#if defined(CONFIG_SPI_MASTER)
-static int wm8731_spi_write(struct spi_device *spi, const char *data, int len)
-{
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
-
- spi_message_init(&m);
- memset(&t, 0, (sizeof t));
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
-}
-
static int __devinit wm8731_spi_probe(struct spi_device *spi)
{
struct snd_soc_codec *codec;
@@ -663,12 +607,11 @@ static int __devinit wm8731_spi_probe(struct spi_device *spi)
codec = &wm8731->codec;
codec->control_data = spi;
- codec->hw_write = (hw_write_t)wm8731_spi_write;
codec->dev = &spi->dev;
dev_set_drvdata(&spi->dev, wm8731);
- return wm8731_register(wm8731);
+ return wm8731_register(wm8731, SND_SOC_SPI);
}
static int __devexit wm8731_spi_remove(struct spi_device *spi)
@@ -680,6 +623,21 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi)
return 0;
}
+#ifdef CONFIG_PM
+static int wm8731_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&spi->dev);
+}
+
+static int wm8731_spi_resume(struct spi_device *spi)
+{
+ return snd_soc_resume_device(&spi->dev);
+}
+#else
+#define wm8731_spi_suspend NULL
+#define wm8731_spi_resume NULL
+#endif
+
static struct spi_driver wm8731_spi_driver = {
.driver = {
.name = "wm8731",
@@ -687,6 +645,8 @@ static struct spi_driver wm8731_spi_driver = {
.owner = THIS_MODULE,
},
.probe = wm8731_spi_probe,
+ .suspend = wm8731_spi_suspend,
+ .resume = wm8731_spi_resume,
.remove = __devexit_p(wm8731_spi_remove),
};
#endif /* CONFIG_SPI_MASTER */
@@ -703,14 +663,13 @@ static __devinit int wm8731_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
codec = &wm8731->codec;
- codec->hw_write = (hw_write_t)i2c_master_send;
i2c_set_clientdata(i2c, wm8731);
codec->control_data = i2c;
codec->dev = &i2c->dev;
- return wm8731_register(wm8731);
+ return wm8731_register(wm8731, SND_SOC_I2C);
}
static __devexit int wm8731_i2c_remove(struct i2c_client *client)
@@ -720,6 +679,21 @@ static __devexit int wm8731_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM
+static int wm8731_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&i2c->dev);
+}
+
+static int wm8731_i2c_resume(struct i2c_client *i2c)
+{
+ return snd_soc_resume_device(&i2c->dev);
+}
+#else
+#define wm8731_i2c_suspend NULL
+#define wm8731_i2c_resume NULL
+#endif
+
static const struct i2c_device_id wm8731_i2c_id[] = {
{ "wm8731", 0 },
{ }
@@ -733,6 +707,8 @@ static struct i2c_driver wm8731_i2c_driver = {
},
.probe = wm8731_i2c_probe,
.remove = __devexit_p(wm8731_i2c_remove),
+ .suspend = wm8731_i2c_suspend,
+ .resume = wm8731_i2c_resume,
.id_table = wm8731_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index b64509b01a4..4ba1e7e93fb 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -55,50 +55,7 @@ static const u16 wm8750_reg[] = {
0x0079, 0x0079, 0x0079, /* 40 */
};
-/*
- * read wm8750 register cache
- */
-static inline unsigned int wm8750_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg > WM8750_CACHE_REGNUM)
- return -1;
- return cache[reg];
-}
-
-/*
- * write wm8750 register cache
- */
-static inline void wm8750_write_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg > WM8750_CACHE_REGNUM)
- return;
- cache[reg] = value;
-}
-
-static int wm8750_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8753 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8750_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define wm8750_reset(c) wm8750_write(c, WM8750_RESET, 0)
+#define wm8750_reset(c) snd_soc_write(c, WM8750_RESET, 0)
/*
* WM8750 Controls
@@ -594,7 +551,7 @@ static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8750_write(codec, WM8750_IFACE, iface);
+ snd_soc_write(codec, WM8750_IFACE, iface);
return 0;
}
@@ -606,8 +563,8 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8750_priv *wm8750 = codec->private_data;
- u16 iface = wm8750_read_reg_cache(codec, WM8750_IFACE) & 0x1f3;
- u16 srate = wm8750_read_reg_cache(codec, WM8750_SRATE) & 0x1c0;
+ u16 iface = snd_soc_read(codec, WM8750_IFACE) & 0x1f3;
+ u16 srate = snd_soc_read(codec, WM8750_SRATE) & 0x1c0;
int coeff = get_coeff(wm8750->sysclk, params_rate(params));
/* bit size */
@@ -626,9 +583,9 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- wm8750_write(codec, WM8750_IFACE, iface);
+ snd_soc_write(codec, WM8750_IFACE, iface);
if (coeff >= 0)
- wm8750_write(codec, WM8750_SRATE, srate |
+ snd_soc_write(codec, WM8750_SRATE, srate |
(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
return 0;
@@ -637,35 +594,35 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
static int wm8750_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7;
+ u16 mute_reg = snd_soc_read(codec, WM8750_ADCDAC) & 0xfff7;
if (mute)
- wm8750_write(codec, WM8750_ADCDAC, mute_reg | 0x8);
+ snd_soc_write(codec, WM8750_ADCDAC, mute_reg | 0x8);
else
- wm8750_write(codec, WM8750_ADCDAC, mute_reg);
+ snd_soc_write(codec, WM8750_ADCDAC, mute_reg);
return 0;
}
static int wm8750_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e;
+ u16 pwr_reg = snd_soc_read(codec, WM8750_PWR1) & 0xfe3e;
switch (level) {
case SND_SOC_BIAS_ON:
/* set vmid to 50k and unmute dac */
- wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
+ snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
break;
case SND_SOC_BIAS_PREPARE:
/* set vmid to 5k for quick power up */
- wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
+ snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
break;
case SND_SOC_BIAS_STANDBY:
/* mute dac and set vmid to 500k, enable VREF */
- wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
+ snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
break;
case SND_SOC_BIAS_OFF:
- wm8750_write(codec, WM8750_PWR1, 0x0001);
+ snd_soc_write(codec, WM8750_PWR1, 0x0001);
break;
}
codec->bias_level = level;
@@ -754,15 +711,14 @@ static int wm8750_resume(struct platform_device *pdev)
* initialise the WM8750 driver
* register the mixer and dsp interfaces with the kernel
*/
-static int wm8750_init(struct snd_soc_device *socdev)
+static int wm8750_init(struct snd_soc_device *socdev,
+ enum snd_soc_control_type control)
{
struct snd_soc_codec *codec = socdev->card->codec;
int reg, ret = 0;
codec->name = "WM8750";
codec->owner = THIS_MODULE;
- codec->read = wm8750_read_reg_cache;
- codec->write = wm8750_write;
codec->set_bias_level = wm8750_set_bias_level;
codec->dai = &wm8750_dai;
codec->num_dai = 1;
@@ -771,13 +727,23 @@ static int wm8750_init(struct snd_soc_device *socdev)
if (codec->reg_cache == NULL)
return -ENOMEM;
- wm8750_reset(codec);
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8750: failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ ret = wm8750_reset(codec);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8750: failed to reset: %d\n", ret);
+ goto err;
+ }
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "wm8750: failed to create pcms\n");
- goto pcm_err;
+ goto err;
}
/* charge output caps */
@@ -786,22 +752,22 @@ static int wm8750_init(struct snd_soc_device *socdev)
schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));
/* set the update bits */
- reg = wm8750_read_reg_cache(codec, WM8750_LDAC);
- wm8750_write(codec, WM8750_LDAC, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_RDAC);
- wm8750_write(codec, WM8750_RDAC, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_LOUT1V);
- wm8750_write(codec, WM8750_LOUT1V, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_ROUT1V);
- wm8750_write(codec, WM8750_ROUT1V, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_LOUT2V);
- wm8750_write(codec, WM8750_LOUT2V, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_ROUT2V);
- wm8750_write(codec, WM8750_ROUT2V, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_LINVOL);
- wm8750_write(codec, WM8750_LINVOL, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_RINVOL);
- wm8750_write(codec, WM8750_RINVOL, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_LDAC);
+ snd_soc_write(codec, WM8750_LDAC, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_RDAC);
+ snd_soc_write(codec, WM8750_RDAC, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_LOUT1V);
+ snd_soc_write(codec, WM8750_LOUT1V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_ROUT1V);
+ snd_soc_write(codec, WM8750_ROUT1V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_LOUT2V);
+ snd_soc_write(codec, WM8750_LOUT2V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_ROUT2V);
+ snd_soc_write(codec, WM8750_ROUT2V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_LINVOL);
+ snd_soc_write(codec, WM8750_LINVOL, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_RINVOL);
+ snd_soc_write(codec, WM8750_RINVOL, reg | 0x0100);
snd_soc_add_controls(codec, wm8750_snd_controls,
ARRAY_SIZE(wm8750_snd_controls));
@@ -816,7 +782,7 @@ static int wm8750_init(struct snd_soc_device *socdev)
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-pcm_err:
+err:
kfree(codec->reg_cache);
return ret;
}
@@ -844,7 +810,7 @@ static int wm8750_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, codec);
codec->control_data = i2c;
- ret = wm8750_init(socdev);
+ ret = wm8750_init(socdev, SND_SOC_I2C);
if (ret < 0)
pr_err("failed to initialise WM8750\n");
@@ -924,7 +890,7 @@ static int __devinit wm8750_spi_probe(struct spi_device *spi)
codec->control_data = spi;
- ret = wm8750_init(socdev);
+ ret = wm8750_init(socdev, SND_SOC_SPI);
if (ret < 0)
dev_err(&spi->dev, "failed to initialise WM8750\n");
@@ -945,30 +911,6 @@ static struct spi_driver wm8750_spi_driver = {
.probe = wm8750_spi_probe,
.remove = __devexit_p(wm8750_spi_remove),
};
-
-static int wm8750_spi_write(struct spi_device *spi, const char *data, int len)
-{
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
-
- spi_message_init(&m);
- memset(&t, 0, (sizeof t));
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
-}
#endif
static int wm8750_probe(struct platform_device *pdev)
@@ -1002,13 +944,11 @@ static int wm8750_probe(struct platform_device *pdev)
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
- codec->hw_write = (hw_write_t)i2c_master_send;
ret = wm8750_add_i2c_device(pdev, setup);
}
#endif
#if defined(CONFIG_SPI_MASTER)
if (setup->spi) {
- codec->hw_write = (hw_write_t)wm8750_spi_write;
ret = spi_register_driver(&wm8750_spi_driver);
if (ret != 0)
printk(KERN_ERR "can't add spi driver");
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index 49c4b2898af..d80d414cfbb 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1766,6 +1766,21 @@ static int wm8753_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM
+static int wm8753_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8753_i2c_resume(struct i2c_client *client)
+{
+ return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8753_i2c_suspend NULL
+#define wm8753_i2c_resume NULL
+#endif
+
static const struct i2c_device_id wm8753_i2c_id[] = {
{ "wm8753", 0 },
{ }
@@ -1779,6 +1794,8 @@ static struct i2c_driver wm8753_i2c_driver = {
},
.probe = wm8753_i2c_probe,
.remove = wm8753_i2c_remove,
+ .suspend = wm8753_i2c_suspend,
+ .resume = wm8753_i2c_resume,
.id_table = wm8753_i2c_id,
};
#endif
@@ -1834,6 +1851,22 @@ static int __devexit wm8753_spi_remove(struct spi_device *spi)
return 0;
}
+#ifdef CONFIG_PM
+static int wm8753_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&spi->dev);
+}
+
+static int wm8753_spi_resume(struct spi_device *spi)
+{
+ return snd_soc_resume_device(&spi->dev);
+}
+
+#else
+#define wm8753_spi_suspend NULL
+#define wm8753_spi_resume NULL
+#endif
+
static struct spi_driver wm8753_spi_driver = {
.driver = {
.name = "wm8753",
@@ -1842,6 +1875,8 @@ static struct spi_driver wm8753_spi_driver = {
},
.probe = wm8753_spi_probe,
.remove = __devexit_p(wm8753_spi_remove),
+ .suspend = wm8753_spi_suspend,
+ .resume = wm8753_spi_resume,
};
#endif
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
new file mode 100644
index 00000000000..a9829aa26e5
--- /dev/null
+++ b/sound/soc/codecs/wm8776.c
@@ -0,0 +1,744 @@
+/*
+ * wm8776.c -- WM8776 ALSA SoC Audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * 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.
+ *
+ * TODO: Input ALC/limiter support
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8776.h"
+
+static struct snd_soc_codec *wm8776_codec;
+struct snd_soc_codec_device soc_codec_dev_wm8776;
+
+/* codec private data */
+struct wm8776_priv {
+ struct snd_soc_codec codec;
+ u16 reg_cache[WM8776_CACHEREGNUM];
+ int sysclk[2];
+};
+
+#ifdef CONFIG_SPI_MASTER
+static int wm8776_spi_write(struct spi_device *spi, const char *data, int len);
+#endif
+
+static const u16 wm8776_reg[WM8776_CACHEREGNUM] = {
+ 0x79, 0x79, 0x79, 0xff, 0xff, /* 4 */
+ 0xff, 0x00, 0x90, 0x00, 0x00, /* 9 */
+ 0x22, 0x22, 0x22, 0x08, 0xcf, /* 14 */
+ 0xcf, 0x7b, 0x00, 0x32, 0x00, /* 19 */
+ 0xa6, 0x01, 0x01
+};
+
+static int wm8776_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, WM8776_RESET, 0);
+}
+
+static const DECLARE_TLV_DB_SCALE(hp_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1);
+
+static const struct snd_kcontrol_new wm8776_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8776_HPLVOL, WM8776_HPRVOL,
+ 0, 127, 0, hp_tlv),
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8776_DACLVOL, WM8776_DACRVOL,
+ 0, 255, 0, dac_tlv),
+SOC_SINGLE("Digital Playback ZC Switch", WM8776_DACCTRL1, 0, 1, 0),
+
+SOC_SINGLE("Deemphasis Switch", WM8776_DACCTRL2, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("Capture Volume", WM8776_ADCLVOL, WM8776_ADCRVOL,
+ 0, 255, 0, adc_tlv),
+SOC_DOUBLE("Capture Switch", WM8776_ADCMUX, 7, 6, 1, 1),
+SOC_DOUBLE_R("Capture ZC Switch", WM8776_ADCLVOL, WM8776_ADCRVOL, 8, 1, 0),
+SOC_SINGLE("Capture HPF Switch", WM8776_ADCIFCTRL, 8, 1, 1),
+};
+
+static const struct snd_kcontrol_new inmix_controls[] = {
+SOC_DAPM_SINGLE("AIN1 Switch", WM8776_ADCMUX, 0, 1, 0),
+SOC_DAPM_SINGLE("AIN2 Switch", WM8776_ADCMUX, 1, 1, 0),
+SOC_DAPM_SINGLE("AIN3 Switch", WM8776_ADCMUX, 2, 1, 0),
+SOC_DAPM_SINGLE("AIN4 Switch", WM8776_ADCMUX, 3, 1, 0),
+SOC_DAPM_SINGLE("AIN5 Switch", WM8776_ADCMUX, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new outmix_controls[] = {
+SOC_DAPM_SINGLE("DAC Switch", WM8776_OUTMUX, 0, 1, 0),
+SOC_DAPM_SINGLE("AUX Switch", WM8776_OUTMUX, 1, 1, 0),
+SOC_DAPM_SINGLE("Bypass Switch", WM8776_OUTMUX, 2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8776_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AUX"),
+SND_SOC_DAPM_INPUT("AUX"),
+
+SND_SOC_DAPM_INPUT("AIN1"),
+SND_SOC_DAPM_INPUT("AIN2"),
+SND_SOC_DAPM_INPUT("AIN3"),
+SND_SOC_DAPM_INPUT("AIN4"),
+SND_SOC_DAPM_INPUT("AIN5"),
+
+SND_SOC_DAPM_MIXER("Input Mixer", WM8776_PWRDOWN, 6, 1,
+ inmix_controls, ARRAY_SIZE(inmix_controls)),
+
+SND_SOC_DAPM_ADC("ADC", "Capture", WM8776_PWRDOWN, 1, 1),
+SND_SOC_DAPM_DAC("DAC", "Playback", WM8776_PWRDOWN, 2, 1),
+
+SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
+ outmix_controls, ARRAY_SIZE(outmix_controls)),
+
+SND_SOC_DAPM_PGA("Headphone PGA", WM8776_PWRDOWN, 3, 1, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("VOUT"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+};
+
+static const struct snd_soc_dapm_route routes[] = {
+ { "Input Mixer", "AIN1 Switch", "AIN1" },
+ { "Input Mixer", "AIN2 Switch", "AIN2" },
+ { "Input Mixer", "AIN3 Switch", "AIN3" },
+ { "Input Mixer", "AIN4 Switch", "AIN4" },
+ { "Input Mixer", "AIN5 Switch", "AIN5" },
+
+ { "ADC", NULL, "Input Mixer" },
+
+ { "Output Mixer", "DAC Switch", "DAC" },
+ { "Output Mixer", "AUX Switch", "AUX" },
+ { "Output Mixer", "Bypass Switch", "Input Mixer" },
+
+ { "VOUT", NULL, "Output Mixer" },
+
+ { "Headphone PGA", NULL, "Output Mixer" },
+
+ { "HPOUTL", NULL, "Headphone PGA" },
+ { "HPOUTR", NULL, "Headphone PGA" },
+};
+
+static int wm8776_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int reg, iface, master;
+
+ switch (dai->id) {
+ case WM8776_DAI_DAC:
+ reg = WM8776_DACIFCTRL;
+ master = 0x80;
+ break;
+ case WM8776_DAI_ADC:
+ reg = WM8776_ADCIFCTRL;
+ master = 0x100;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ iface = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ master = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0002;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0001;
+ break;
+ /* FIXME: CHECK A/B */
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x0003;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x0007;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x00c;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x008;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x004;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Finally, write out the values */
+ snd_soc_update_bits(codec, reg, 0xf, iface);
+ snd_soc_update_bits(codec, WM8776_MSTRCTRL, 0x180, master);
+
+ return 0;
+}
+
+static int mclk_ratios[] = {
+ 128,
+ 192,
+ 256,
+ 384,
+ 512,
+ 768,
+};
+
+static int wm8776_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 wm8776_priv *wm8776 = codec->private_data;
+ int iface_reg, iface;
+ int ratio_shift, master;
+ int i;
+
+ iface = 0;
+
+ switch (dai->id) {
+ case WM8776_DAI_DAC:
+ iface_reg = WM8776_DACIFCTRL;
+ master = 0x80;
+ ratio_shift = 4;
+ break;
+ case WM8776_DAI_ADC:
+ iface_reg = WM8776_ADCIFCTRL;
+ master = 0x100;
+ ratio_shift = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ /* Set word length */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x10;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface |= 0x20;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x30;
+ break;
+ }
+
+ /* Only need to set MCLK/LRCLK ratio if we're master */
+ if (snd_soc_read(codec, WM8776_MSTRCTRL) & master) {
+ for (i = 0; i < ARRAY_SIZE(mclk_ratios); i++) {
+ if (wm8776->sysclk[dai->id] / params_rate(params)
+ == mclk_ratios[i])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(mclk_ratios)) {
+ dev_err(codec->dev,
+ "Unable to configure MCLK ratio %d/%d\n",
+ wm8776->sysclk[dai->id], params_rate(params));
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "MCLK is %dfs\n", mclk_ratios[i]);
+
+ snd_soc_update_bits(codec, WM8776_MSTRCTRL,
+ 0x7 << ratio_shift, i << ratio_shift);
+ } else {
+ dev_dbg(codec->dev, "DAI in slave mode\n");
+ }
+
+ snd_soc_update_bits(codec, iface_reg, 0x30, iface);
+
+ return 0;
+}
+
+static int wm8776_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ return snd_soc_write(codec, WM8776_DACMUTE, !!mute);
+}
+
+static int wm8776_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8776_priv *wm8776 = codec->private_data;
+
+ BUG_ON(dai->id >= ARRAY_SIZE(wm8776->sysclk));
+
+ wm8776->sysclk[dai->id] = freq;
+
+ return 0;
+}
+
+static int wm8776_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Disable the global powerdown; DAPM does the rest */
+ snd_soc_update_bits(codec, WM8776_PWRDOWN, 1, 0);
+ }
+
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, WM8776_PWRDOWN, 1, 1);
+ break;
+ }
+
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8776_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000)
+
+
+#define WM8776_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops wm8776_dac_ops = {
+ .digital_mute = wm8776_mute,
+ .hw_params = wm8776_hw_params,
+ .set_fmt = wm8776_set_fmt,
+ .set_sysclk = wm8776_set_sysclk,
+};
+
+static struct snd_soc_dai_ops wm8776_adc_ops = {
+ .hw_params = wm8776_hw_params,
+ .set_fmt = wm8776_set_fmt,
+ .set_sysclk = wm8776_set_sysclk,
+};
+
+struct snd_soc_dai wm8776_dai[] = {
+ {
+ .name = "WM8776 Playback",
+ .id = WM8776_DAI_DAC,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8776_RATES,
+ .formats = WM8776_FORMATS,
+ },
+ .ops = &wm8776_dac_ops,
+ },
+ {
+ .name = "WM8776 Capture",
+ .id = WM8776_DAI_ADC,
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8776_RATES,
+ .formats = WM8776_FORMATS,
+ },
+ .ops = &wm8776_adc_ops,
+ },
+};
+EXPORT_SYMBOL_GPL(wm8776_dai);
+
+#ifdef CONFIG_PM
+static int wm8776_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ wm8776_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int wm8776_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(wm8776_reg); i++) {
+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+
+ wm8776_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define wm8776_suspend NULL
+#define wm8776_resume NULL
+#endif
+
+static int wm8776_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (wm8776_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = wm8776_codec;
+ codec = wm8776_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, wm8776_snd_controls,
+ ARRAY_SIZE(wm8776_snd_controls));
+ snd_soc_dapm_new_controls(codec, wm8776_dapm_widgets,
+ ARRAY_SIZE(wm8776_dapm_widgets));
+ snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes));
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to register card: %d\n", ret);
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ return ret;
+}
+
+/* power down chip */
+static int wm8776_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8776 = {
+ .probe = wm8776_probe,
+ .remove = wm8776_remove,
+ .suspend = wm8776_suspend,
+ .resume = wm8776_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8776);
+
+static int wm8776_register(struct wm8776_priv *wm8776,
+ enum snd_soc_control_type control)
+{
+ int ret, i;
+ struct snd_soc_codec *codec = &wm8776->codec;
+
+ if (wm8776_codec) {
+ dev_err(codec->dev, "Another WM8776 is registered\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->private_data = wm8776;
+ codec->name = "WM8776";
+ codec->owner = THIS_MODULE;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8776_set_bias_level;
+ codec->dai = wm8776_dai;
+ codec->num_dai = ARRAY_SIZE(wm8776_dai);
+ codec->reg_cache_size = WM8776_CACHEREGNUM;
+ codec->reg_cache = &wm8776->reg_cache;
+
+ memcpy(codec->reg_cache, wm8776_reg, sizeof(wm8776_reg));
+
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8776_dai); i++)
+ wm8776_dai[i].dev = codec->dev;
+
+ ret = wm8776_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+ goto err;
+ }
+
+ wm8776_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* Latch the update bits; right channel only since we always
+ * update both. */
+ snd_soc_update_bits(codec, WM8776_HPRVOL, 0x100, 0x100);
+ snd_soc_update_bits(codec, WM8776_DACRVOL, 0x100, 0x100);
+
+ wm8776_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dais(wm8776_dai, ARRAY_SIZE(wm8776_dai));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+ goto err_codec;
+ }
+
+ return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ kfree(wm8776);
+ return ret;
+}
+
+static void wm8776_unregister(struct wm8776_priv *wm8776)
+{
+ wm8776_set_bias_level(&wm8776->codec, SND_SOC_BIAS_OFF);
+ snd_soc_unregister_dais(wm8776_dai, ARRAY_SIZE(wm8776_dai));
+ snd_soc_unregister_codec(&wm8776->codec);
+ kfree(wm8776);
+ wm8776_codec = NULL;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8776_spi_write(struct spi_device *spi, const char *data, int len)
+{
+ struct spi_transfer t;
+ struct spi_message m;
+ u8 msg[2];
+
+ if (len <= 0)
+ return 0;
+
+ msg[0] = data[0];
+ msg[1] = data[1];
+
+ spi_message_init(&m);
+ memset(&t, 0, (sizeof t));
+
+ t.tx_buf = &msg[0];
+ t.len = len;
+
+ spi_message_add_tail(&t, &m);
+ spi_sync(spi, &m);
+
+ return len;
+}
+
+static int __devinit wm8776_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_codec *codec;
+ struct wm8776_priv *wm8776;
+
+ wm8776 = kzalloc(sizeof(struct wm8776_priv), GFP_KERNEL);
+ if (wm8776 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8776->codec;
+ codec->control_data = spi;
+ codec->hw_write = (hw_write_t)wm8776_spi_write;
+ codec->dev = &spi->dev;
+
+ dev_set_drvdata(&spi->dev, wm8776);
+
+ return wm8776_register(wm8776, SND_SOC_SPI);
+}
+
+static int __devexit wm8776_spi_remove(struct spi_device *spi)
+{
+ struct wm8776_priv *wm8776 = dev_get_drvdata(&spi->dev);
+
+ wm8776_unregister(wm8776);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8776_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&spi->dev);
+}
+
+static int wm8776_spi_resume(struct spi_device *spi)
+{
+ return snd_soc_resume_device(&spi->dev);
+}
+#else
+#define wm8776_spi_suspend NULL
+#define wm8776_spi_resume NULL
+#endif
+
+static struct spi_driver wm8776_spi_driver = {
+ .driver = {
+ .name = "wm8776",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8776_spi_probe,
+ .suspend = wm8776_spi_suspend,
+ .resume = wm8776_spi_resume,
+ .remove = __devexit_p(wm8776_spi_remove),
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8776_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8776_priv *wm8776;
+ struct snd_soc_codec *codec;
+
+ wm8776 = kzalloc(sizeof(struct wm8776_priv), GFP_KERNEL);
+ if (wm8776 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8776->codec;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+
+ i2c_set_clientdata(i2c, wm8776);
+ codec->control_data = i2c;
+
+ codec->dev = &i2c->dev;
+
+ return wm8776_register(wm8776, SND_SOC_I2C);
+}
+
+static __devexit int wm8776_i2c_remove(struct i2c_client *client)
+{
+ struct wm8776_priv *wm8776 = i2c_get_clientdata(client);
+ wm8776_unregister(wm8776);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8776_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&i2c->dev);
+}
+
+static int wm8776_i2c_resume(struct i2c_client *i2c)
+{
+ return snd_soc_resume_device(&i2c->dev);
+}
+#else
+#define wm8776_i2c_suspend NULL
+#define wm8776_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm8776_i2c_id[] = {
+ { "wm8776", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id);
+
+static struct i2c_driver wm8776_i2c_driver = {
+ .driver = {
+ .name = "wm8776",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8776_i2c_probe,
+ .remove = __devexit_p(wm8776_i2c_remove),
+ .suspend = wm8776_i2c_suspend,
+ .resume = wm8776_i2c_resume,
+ .id_table = wm8776_i2c_id,
+};
+#endif
+
+static int __init wm8776_modinit(void)
+{
+ int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ ret = i2c_add_driver(&wm8776_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8776 I2C driver: %d\n",
+ ret);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ ret = spi_register_driver(&wm8776_spi_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8776 SPI driver: %d\n",
+ ret);
+ }
+#endif
+ return 0;
+}
+module_init(wm8776_modinit);
+
+static void __exit wm8776_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&wm8776_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8776_spi_driver);
+#endif
+}
+module_exit(wm8776_exit);
+
+MODULE_DESCRIPTION("ASoC WM8776 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8776.h b/sound/soc/codecs/wm8776.h
new file mode 100644
index 00000000000..6606d25d2d8
--- /dev/null
+++ b/sound/soc/codecs/wm8776.h
@@ -0,0 +1,51 @@
+/*
+ * wm8776.h -- WM8776 ASoC driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * 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 _WM8776_H
+#define _WM8776_H
+
+/* Registers */
+
+#define WM8776_HPLVOL 0x00
+#define WM8776_HPRVOL 0x01
+#define WM8776_HPMASTER 0x02
+#define WM8776_DACLVOL 0x03
+#define WM8776_DACRVOL 0x04
+#define WM8776_DACMASTER 0x05
+#define WM8776_PHASESWAP 0x06
+#define WM8776_DACCTRL1 0x07
+#define WM8776_DACMUTE 0x08
+#define WM8776_DACCTRL2 0x09
+#define WM8776_DACIFCTRL 0x0a
+#define WM8776_ADCIFCTRL 0x0b
+#define WM8776_MSTRCTRL 0x0c
+#define WM8776_PWRDOWN 0x0d
+#define WM8776_ADCLVOL 0x0e
+#define WM8776_ADCRVOL 0x0f
+#define WM8776_ALCCTRL1 0x10
+#define WM8776_ALCCTRL2 0x11
+#define WM8776_ALCCTRL3 0x12
+#define WM8776_NOISEGATE 0x13
+#define WM8776_LIMITER 0x14
+#define WM8776_ADCMUX 0x15
+#define WM8776_OUTMUX 0x16
+#define WM8776_RESET 0x17
+
+#define WM8776_CACHEREGNUM 0x17
+
+#define WM8776_DAI_DAC 0
+#define WM8776_DAI_ADC 1
+
+extern struct snd_soc_dai wm8776_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_wm8776;
+
+#endif
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 3c78945244b..5e9c855c003 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -116,6 +116,7 @@
#define WM8900_REG_CLOCKING2_DAC_CLKDIV 0x1c
#define WM8900_REG_DACCTRL_MUTE 0x004
+#define WM8900_REG_DACCTRL_DAC_SB_FILT 0x100
#define WM8900_REG_DACCTRL_AIF_LRCLKRATE 0x400
#define WM8900_REG_AUDIO3_ADCLRC_DIR 0x0800
@@ -182,111 +183,20 @@ static const u16 wm8900_reg_defaults[WM8900_MAXREG] = {
/* Remaining registers all zero */
};
-/*
- * read wm8900 register cache
- */
-static inline unsigned int wm8900_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
-
- BUG_ON(reg >= WM8900_MAXREG);
-
- if (reg == WM8900_REG_ID)
- return 0;
-
- return cache[reg];
-}
-
-/*
- * write wm8900 register cache
- */
-static inline void wm8900_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
-
- BUG_ON(reg >= WM8900_MAXREG);
-
- cache[reg] = value;
-}
-
-/*
- * write to the WM8900 register space
- */
-static int wm8900_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[3];
-
- if (value == wm8900_read_reg_cache(codec, reg))
- return 0;
-
- /* data is
- * D15..D9 WM8900 register offset
- * D8...D0 register data
- */
- data[0] = reg;
- data[1] = value >> 8;
- data[2] = value & 0x00ff;
-
- wm8900_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 3) == 3)
- return 0;
- else
- return -EIO;
-}
-
-/*
- * Read from the wm8900.
- */
-static unsigned int wm8900_chip_read(struct snd_soc_codec *codec, u8 reg)
-{
- struct i2c_msg xfer[2];
- u16 data;
- int ret;
- struct i2c_client *client = codec->control_data;
-
- BUG_ON(reg != WM8900_REG_ID && reg != WM8900_REG_POWER1);
-
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 1;
- xfer[0].buf = &reg;
-
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 2;
- xfer[1].buf = (u8 *)&data;
-
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- printk(KERN_CRIT "i2c_transfer returned %d\n", ret);
- return 0;
- }
-
- return (data >> 8) | ((data & 0xff) << 8);
-}
-
-/*
- * Read from the WM8900 register space. Most registers can't be read
- * and are therefore supplied from cache.
- */
-static unsigned int wm8900_read(struct snd_soc_codec *codec, unsigned int reg)
+static int wm8900_volatile_register(unsigned int reg)
{
switch (reg) {
case WM8900_REG_ID:
- return wm8900_chip_read(codec, reg);
+ case WM8900_REG_POWER1:
+ return 1;
default:
- return wm8900_read_reg_cache(codec, reg);
+ return 0;
}
}
static void wm8900_reset(struct snd_soc_codec *codec)
{
- wm8900_write(codec, WM8900_REG_RESET, 0);
+ snd_soc_write(codec, WM8900_REG_RESET, 0);
memcpy(codec->reg_cache, wm8900_reg_defaults,
sizeof(codec->reg_cache));
@@ -296,14 +206,14 @@ static int wm8900_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
- u16 hpctl1 = wm8900_read(codec, WM8900_REG_HPCTL1);
+ u16 hpctl1 = snd_soc_read(codec, WM8900_REG_HPCTL1);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Clamp headphone outputs */
hpctl1 = WM8900_REG_HPCTL1_HP_CLAMP_IP |
WM8900_REG_HPCTL1_HP_CLAMP_OP;
- wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
break;
case SND_SOC_DAPM_POST_PMU:
@@ -312,41 +222,41 @@ static int wm8900_hp_event(struct snd_soc_dapm_widget *w,
hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT |
WM8900_REG_HPCTL1_HP_SHORT2 |
WM8900_REG_HPCTL1_HP_IPSTAGE_ENA;
- wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
msleep(400);
/* Enable the output stage */
hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_OP;
hpctl1 |= WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
- wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
/* Remove the shorts */
hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT2;
- wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT;
- wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
break;
case SND_SOC_DAPM_PRE_PMD:
/* Short the output */
hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT;
- wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
/* Disable the output stage */
hpctl1 &= ~WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
- wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
/* Clamp the outputs and power down input */
hpctl1 |= WM8900_REG_HPCTL1_HP_CLAMP_IP |
WM8900_REG_HPCTL1_HP_CLAMP_OP;
hpctl1 &= ~WM8900_REG_HPCTL1_HP_IPSTAGE_ENA;
- wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+ snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
break;
case SND_SOC_DAPM_POST_PMD:
/* Disable everything */
- wm8900_write(codec, WM8900_REG_HPCTL1, 0);
+ snd_soc_write(codec, WM8900_REG_HPCTL1, 0);
break;
default:
@@ -439,7 +349,6 @@ SOC_SINGLE("DAC Soft Mute Switch", WM8900_REG_DACCTRL, 6, 1, 1),
SOC_ENUM("DAC Mute Rate", dac_mute_rate),
SOC_SINGLE("DAC Mono Switch", WM8900_REG_DACCTRL, 9, 1, 0),
SOC_ENUM("DAC Deemphasis", dac_deemphasis),
-SOC_SINGLE("DAC Sloping Stopband Filter Switch", WM8900_REG_DACCTRL, 8, 1, 0),
SOC_SINGLE("DAC Sigma-Delta Modulator Clock Switch", WM8900_REG_DACCTRL,
12, 1, 0),
@@ -723,7 +632,7 @@ static int wm8900_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = socdev->card->codec;
u16 reg;
- reg = wm8900_read(codec, WM8900_REG_AUDIO1) & ~0x60;
+ reg = snd_soc_read(codec, WM8900_REG_AUDIO1) & ~0x60;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -741,7 +650,18 @@ static int wm8900_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- wm8900_write(codec, WM8900_REG_AUDIO1, reg);
+ snd_soc_write(codec, WM8900_REG_AUDIO1, reg);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg = snd_soc_read(codec, WM8900_REG_DACCTRL);
+
+ if (params_rate(params) <= 24000)
+ reg |= WM8900_REG_DACCTRL_DAC_SB_FILT;
+ else
+ reg &= ~WM8900_REG_DACCTRL_DAC_SB_FILT;
+
+ snd_soc_write(codec, WM8900_REG_DACCTRL, reg);
+ }
return 0;
}
@@ -834,18 +754,18 @@ static int wm8900_set_fll(struct snd_soc_codec *codec,
return 0;
/* The digital side should be disabled during any change. */
- reg = wm8900_read(codec, WM8900_REG_POWER1);
- wm8900_write(codec, WM8900_REG_POWER1,
+ reg = snd_soc_read(codec, WM8900_REG_POWER1);
+ snd_soc_write(codec, WM8900_REG_POWER1,
reg & (~WM8900_REG_POWER1_FLL_ENA));
/* Disable the FLL? */
if (!freq_in || !freq_out) {
- reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
- wm8900_write(codec, WM8900_REG_CLOCKING1,
+ reg = snd_soc_read(codec, WM8900_REG_CLOCKING1);
+ snd_soc_write(codec, WM8900_REG_CLOCKING1,
reg & (~WM8900_REG_CLOCKING1_MCLK_SRC));
- reg = wm8900_read(codec, WM8900_REG_FLLCTL1);
- wm8900_write(codec, WM8900_REG_FLLCTL1,
+ reg = snd_soc_read(codec, WM8900_REG_FLLCTL1);
+ snd_soc_write(codec, WM8900_REG_FLLCTL1,
reg & (~WM8900_REG_FLLCTL1_OSC_ENA));
wm8900->fll_in = freq_in;
@@ -862,33 +782,33 @@ static int wm8900_set_fll(struct snd_soc_codec *codec,
/* The osclilator *MUST* be enabled before we enable the
* digital circuit. */
- wm8900_write(codec, WM8900_REG_FLLCTL1,
+ snd_soc_write(codec, WM8900_REG_FLLCTL1,
fll_div.fll_ratio | WM8900_REG_FLLCTL1_OSC_ENA);
- wm8900_write(codec, WM8900_REG_FLLCTL4, fll_div.n >> 5);
- wm8900_write(codec, WM8900_REG_FLLCTL5,
+ snd_soc_write(codec, WM8900_REG_FLLCTL4, fll_div.n >> 5);
+ snd_soc_write(codec, WM8900_REG_FLLCTL5,
(fll_div.fllclk_div << 6) | (fll_div.n & 0x1f));
if (fll_div.k) {
- wm8900_write(codec, WM8900_REG_FLLCTL2,
+ snd_soc_write(codec, WM8900_REG_FLLCTL2,
(fll_div.k >> 8) | 0x100);
- wm8900_write(codec, WM8900_REG_FLLCTL3, fll_div.k & 0xff);
+ snd_soc_write(codec, WM8900_REG_FLLCTL3, fll_div.k & 0xff);
} else
- wm8900_write(codec, WM8900_REG_FLLCTL2, 0);
+ snd_soc_write(codec, WM8900_REG_FLLCTL2, 0);
if (fll_div.fll_slow_lock_ref)
- wm8900_write(codec, WM8900_REG_FLLCTL6,
+ snd_soc_write(codec, WM8900_REG_FLLCTL6,
WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF);
else
- wm8900_write(codec, WM8900_REG_FLLCTL6, 0);
+ snd_soc_write(codec, WM8900_REG_FLLCTL6, 0);
- reg = wm8900_read(codec, WM8900_REG_POWER1);
- wm8900_write(codec, WM8900_REG_POWER1,
+ reg = snd_soc_read(codec, WM8900_REG_POWER1);
+ snd_soc_write(codec, WM8900_REG_POWER1,
reg | WM8900_REG_POWER1_FLL_ENA);
reenable:
- reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
- wm8900_write(codec, WM8900_REG_CLOCKING1,
+ reg = snd_soc_read(codec, WM8900_REG_CLOCKING1);
+ snd_soc_write(codec, WM8900_REG_CLOCKING1,
reg | WM8900_REG_CLOCKING1_MCLK_SRC);
return 0;
@@ -908,38 +828,38 @@ static int wm8900_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
switch (div_id) {
case WM8900_BCLK_DIV:
- reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
- wm8900_write(codec, WM8900_REG_CLOCKING1,
+ reg = snd_soc_read(codec, WM8900_REG_CLOCKING1);
+ snd_soc_write(codec, WM8900_REG_CLOCKING1,
div | (reg & WM8900_REG_CLOCKING1_BCLK_MASK));
break;
case WM8900_OPCLK_DIV:
- reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
- wm8900_write(codec, WM8900_REG_CLOCKING1,
+ reg = snd_soc_read(codec, WM8900_REG_CLOCKING1);
+ snd_soc_write(codec, WM8900_REG_CLOCKING1,
div | (reg & WM8900_REG_CLOCKING1_OPCLK_MASK));
break;
case WM8900_DAC_LRCLK:
- reg = wm8900_read(codec, WM8900_REG_AUDIO4);
- wm8900_write(codec, WM8900_REG_AUDIO4,
+ reg = snd_soc_read(codec, WM8900_REG_AUDIO4);
+ snd_soc_write(codec, WM8900_REG_AUDIO4,
div | (reg & WM8900_LRC_MASK));
break;
case WM8900_ADC_LRCLK:
- reg = wm8900_read(codec, WM8900_REG_AUDIO3);
- wm8900_write(codec, WM8900_REG_AUDIO3,
+ reg = snd_soc_read(codec, WM8900_REG_AUDIO3);
+ snd_soc_write(codec, WM8900_REG_AUDIO3,
div | (reg & WM8900_LRC_MASK));
break;
case WM8900_DAC_CLKDIV:
- reg = wm8900_read(codec, WM8900_REG_CLOCKING2);
- wm8900_write(codec, WM8900_REG_CLOCKING2,
+ reg = snd_soc_read(codec, WM8900_REG_CLOCKING2);
+ snd_soc_write(codec, WM8900_REG_CLOCKING2,
div | (reg & WM8900_REG_CLOCKING2_DAC_CLKDIV));
break;
case WM8900_ADC_CLKDIV:
- reg = wm8900_read(codec, WM8900_REG_CLOCKING2);
- wm8900_write(codec, WM8900_REG_CLOCKING2,
+ reg = snd_soc_read(codec, WM8900_REG_CLOCKING2);
+ snd_soc_write(codec, WM8900_REG_CLOCKING2,
div | (reg & WM8900_REG_CLOCKING2_ADC_CLKDIV));
break;
case WM8900_LRCLK_MODE:
- reg = wm8900_read(codec, WM8900_REG_DACCTRL);
- wm8900_write(codec, WM8900_REG_DACCTRL,
+ reg = snd_soc_read(codec, WM8900_REG_DACCTRL);
+ snd_soc_write(codec, WM8900_REG_DACCTRL,
div | (reg & WM8900_REG_DACCTRL_AIF_LRCLKRATE));
break;
default:
@@ -956,10 +876,10 @@ static int wm8900_set_dai_fmt(struct snd_soc_dai *codec_dai,
struct snd_soc_codec *codec = codec_dai->codec;
unsigned int clocking1, aif1, aif3, aif4;
- clocking1 = wm8900_read(codec, WM8900_REG_CLOCKING1);
- aif1 = wm8900_read(codec, WM8900_REG_AUDIO1);
- aif3 = wm8900_read(codec, WM8900_REG_AUDIO3);
- aif4 = wm8900_read(codec, WM8900_REG_AUDIO4);
+ clocking1 = snd_soc_read(codec, WM8900_REG_CLOCKING1);
+ aif1 = snd_soc_read(codec, WM8900_REG_AUDIO1);
+ aif3 = snd_soc_read(codec, WM8900_REG_AUDIO3);
+ aif4 = snd_soc_read(codec, WM8900_REG_AUDIO4);
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -1055,10 +975,10 @@ static int wm8900_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8900_write(codec, WM8900_REG_CLOCKING1, clocking1);
- wm8900_write(codec, WM8900_REG_AUDIO1, aif1);
- wm8900_write(codec, WM8900_REG_AUDIO3, aif3);
- wm8900_write(codec, WM8900_REG_AUDIO4, aif4);
+ snd_soc_write(codec, WM8900_REG_CLOCKING1, clocking1);
+ snd_soc_write(codec, WM8900_REG_AUDIO1, aif1);
+ snd_soc_write(codec, WM8900_REG_AUDIO3, aif3);
+ snd_soc_write(codec, WM8900_REG_AUDIO4, aif4);
return 0;
}
@@ -1068,14 +988,14 @@ static int wm8900_digital_mute(struct snd_soc_dai *codec_dai, int mute)
struct snd_soc_codec *codec = codec_dai->codec;
u16 reg;
- reg = wm8900_read(codec, WM8900_REG_DACCTRL);
+ reg = snd_soc_read(codec, WM8900_REG_DACCTRL);
if (mute)
reg |= WM8900_REG_DACCTRL_MUTE;
else
reg &= ~WM8900_REG_DACCTRL_MUTE;
- wm8900_write(codec, WM8900_REG_DACCTRL, reg);
+ snd_soc_write(codec, WM8900_REG_DACCTRL, reg);
return 0;
}
@@ -1124,11 +1044,11 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
/* Enable thermal shutdown */
- reg = wm8900_read(codec, WM8900_REG_GPIO);
- wm8900_write(codec, WM8900_REG_GPIO,
+ reg = snd_soc_read(codec, WM8900_REG_GPIO);
+ snd_soc_write(codec, WM8900_REG_GPIO,
reg | WM8900_REG_GPIO_TEMP_ENA);
- reg = wm8900_read(codec, WM8900_REG_ADDCTL);
- wm8900_write(codec, WM8900_REG_ADDCTL,
+ reg = snd_soc_read(codec, WM8900_REG_ADDCTL);
+ snd_soc_write(codec, WM8900_REG_ADDCTL,
reg | WM8900_REG_ADDCTL_TEMP_SD);
break;
@@ -1139,69 +1059,69 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec,
/* Charge capacitors if initial power up */
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* STARTUP_BIAS_ENA on */
- wm8900_write(codec, WM8900_REG_POWER1,
+ snd_soc_write(codec, WM8900_REG_POWER1,
WM8900_REG_POWER1_STARTUP_BIAS_ENA);
/* Startup bias mode */
- wm8900_write(codec, WM8900_REG_ADDCTL,
+ snd_soc_write(codec, WM8900_REG_ADDCTL,
WM8900_REG_ADDCTL_BIAS_SRC |
WM8900_REG_ADDCTL_VMID_SOFTST);
/* VMID 2x50k */
- wm8900_write(codec, WM8900_REG_POWER1,
+ snd_soc_write(codec, WM8900_REG_POWER1,
WM8900_REG_POWER1_STARTUP_BIAS_ENA | 0x1);
/* Allow capacitors to charge */
schedule_timeout_interruptible(msecs_to_jiffies(400));
/* Enable bias */
- wm8900_write(codec, WM8900_REG_POWER1,
+ snd_soc_write(codec, WM8900_REG_POWER1,
WM8900_REG_POWER1_STARTUP_BIAS_ENA |
WM8900_REG_POWER1_BIAS_ENA | 0x1);
- wm8900_write(codec, WM8900_REG_ADDCTL, 0);
+ snd_soc_write(codec, WM8900_REG_ADDCTL, 0);
- wm8900_write(codec, WM8900_REG_POWER1,
+ snd_soc_write(codec, WM8900_REG_POWER1,
WM8900_REG_POWER1_BIAS_ENA | 0x1);
}
- reg = wm8900_read(codec, WM8900_REG_POWER1);
- wm8900_write(codec, WM8900_REG_POWER1,
+ reg = snd_soc_read(codec, WM8900_REG_POWER1);
+ snd_soc_write(codec, WM8900_REG_POWER1,
(reg & WM8900_REG_POWER1_FLL_ENA) |
WM8900_REG_POWER1_BIAS_ENA | 0x1);
- wm8900_write(codec, WM8900_REG_POWER2,
+ snd_soc_write(codec, WM8900_REG_POWER2,
WM8900_REG_POWER2_SYSCLK_ENA);
- wm8900_write(codec, WM8900_REG_POWER3, 0);
+ snd_soc_write(codec, WM8900_REG_POWER3, 0);
break;
case SND_SOC_BIAS_OFF:
/* Startup bias enable */
- reg = wm8900_read(codec, WM8900_REG_POWER1);
- wm8900_write(codec, WM8900_REG_POWER1,
+ reg = snd_soc_read(codec, WM8900_REG_POWER1);
+ snd_soc_write(codec, WM8900_REG_POWER1,
reg & WM8900_REG_POWER1_STARTUP_BIAS_ENA);
- wm8900_write(codec, WM8900_REG_ADDCTL,
+ snd_soc_write(codec, WM8900_REG_ADDCTL,
WM8900_REG_ADDCTL_BIAS_SRC |
WM8900_REG_ADDCTL_VMID_SOFTST);
/* Discharge caps */
- wm8900_write(codec, WM8900_REG_POWER1,
+ snd_soc_write(codec, WM8900_REG_POWER1,
WM8900_REG_POWER1_STARTUP_BIAS_ENA);
schedule_timeout_interruptible(msecs_to_jiffies(500));
/* Remove clamp */
- wm8900_write(codec, WM8900_REG_HPCTL1, 0);
+ snd_soc_write(codec, WM8900_REG_HPCTL1, 0);
/* Power down */
- wm8900_write(codec, WM8900_REG_ADDCTL, 0);
- wm8900_write(codec, WM8900_REG_POWER1, 0);
- wm8900_write(codec, WM8900_REG_POWER2, 0);
- wm8900_write(codec, WM8900_REG_POWER3, 0);
+ snd_soc_write(codec, WM8900_REG_ADDCTL, 0);
+ snd_soc_write(codec, WM8900_REG_POWER1, 0);
+ snd_soc_write(codec, WM8900_REG_POWER2, 0);
+ snd_soc_write(codec, WM8900_REG_POWER3, 0);
/* Need to let things settle before stopping the clock
* to ensure that restart works, see "Stopping the
* master clock" in the datasheet. */
schedule_timeout_interruptible(msecs_to_jiffies(1));
- wm8900_write(codec, WM8900_REG_POWER2,
+ snd_soc_write(codec, WM8900_REG_POWER2,
WM8900_REG_POWER2_SYSCLK_ENA);
break;
}
@@ -1264,7 +1184,7 @@ static int wm8900_resume(struct platform_device *pdev)
if (cache) {
for (i = 0; i < WM8900_MAXREG; i++)
- wm8900_write(codec, i, cache[i]);
+ snd_soc_write(codec, i, cache[i]);
kfree(cache);
} else
dev_err(&pdev->dev, "Unable to allocate register cache\n");
@@ -1297,16 +1217,20 @@ static __devinit int wm8900_i2c_probe(struct i2c_client *i2c,
codec->name = "WM8900";
codec->owner = THIS_MODULE;
- codec->read = wm8900_read;
- codec->write = wm8900_write;
codec->dai = &wm8900_dai;
codec->num_dai = 1;
- codec->hw_write = (hw_write_t)i2c_master_send;
codec->control_data = i2c;
codec->set_bias_level = wm8900_set_bias_level;
+ codec->volatile_register = wm8900_volatile_register;
codec->dev = &i2c->dev;
- reg = wm8900_read(codec, WM8900_REG_ID);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ reg = snd_soc_read(codec, WM8900_REG_ID);
if (reg != 0x8900) {
dev_err(&i2c->dev, "Device is not a WM8900 - ID %x\n", reg);
ret = -ENODEV;
@@ -1314,7 +1238,7 @@ static __devinit int wm8900_i2c_probe(struct i2c_client *i2c,
}
/* Read back from the chip */
- reg = wm8900_chip_read(codec, WM8900_REG_POWER1);
+ reg = snd_soc_read(codec, WM8900_REG_POWER1);
reg = (reg >> 12) & 0xf;
dev_info(&i2c->dev, "WM8900 revision %d\n", reg);
@@ -1324,29 +1248,29 @@ static __devinit int wm8900_i2c_probe(struct i2c_client *i2c,
wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Latch the volume update bits */
- wm8900_write(codec, WM8900_REG_LINVOL,
- wm8900_read(codec, WM8900_REG_LINVOL) | 0x100);
- wm8900_write(codec, WM8900_REG_RINVOL,
- wm8900_read(codec, WM8900_REG_RINVOL) | 0x100);
- wm8900_write(codec, WM8900_REG_LOUT1CTL,
- wm8900_read(codec, WM8900_REG_LOUT1CTL) | 0x100);
- wm8900_write(codec, WM8900_REG_ROUT1CTL,
- wm8900_read(codec, WM8900_REG_ROUT1CTL) | 0x100);
- wm8900_write(codec, WM8900_REG_LOUT2CTL,
- wm8900_read(codec, WM8900_REG_LOUT2CTL) | 0x100);
- wm8900_write(codec, WM8900_REG_ROUT2CTL,
- wm8900_read(codec, WM8900_REG_ROUT2CTL) | 0x100);
- wm8900_write(codec, WM8900_REG_LDAC_DV,
- wm8900_read(codec, WM8900_REG_LDAC_DV) | 0x100);
- wm8900_write(codec, WM8900_REG_RDAC_DV,
- wm8900_read(codec, WM8900_REG_RDAC_DV) | 0x100);
- wm8900_write(codec, WM8900_REG_LADC_DV,
- wm8900_read(codec, WM8900_REG_LADC_DV) | 0x100);
- wm8900_write(codec, WM8900_REG_RADC_DV,
- wm8900_read(codec, WM8900_REG_RADC_DV) | 0x100);
+ snd_soc_write(codec, WM8900_REG_LINVOL,
+ snd_soc_read(codec, WM8900_REG_LINVOL) | 0x100);
+ snd_soc_write(codec, WM8900_REG_RINVOL,
+ snd_soc_read(codec, WM8900_REG_RINVOL) | 0x100);
+ snd_soc_write(codec, WM8900_REG_LOUT1CTL,
+ snd_soc_read(codec, WM8900_REG_LOUT1CTL) | 0x100);
+ snd_soc_write(codec, WM8900_REG_ROUT1CTL,
+ snd_soc_read(codec, WM8900_REG_ROUT1CTL) | 0x100);
+ snd_soc_write(codec, WM8900_REG_LOUT2CTL,
+ snd_soc_read(codec, WM8900_REG_LOUT2CTL) | 0x100);
+ snd_soc_write(codec, WM8900_REG_ROUT2CTL,
+ snd_soc_read(codec, WM8900_REG_ROUT2CTL) | 0x100);
+ snd_soc_write(codec, WM8900_REG_LDAC_DV,
+ snd_soc_read(codec, WM8900_REG_LDAC_DV) | 0x100);
+ snd_soc_write(codec, WM8900_REG_RDAC_DV,
+ snd_soc_read(codec, WM8900_REG_RDAC_DV) | 0x100);
+ snd_soc_write(codec, WM8900_REG_LADC_DV,
+ snd_soc_read(codec, WM8900_REG_LADC_DV) | 0x100);
+ snd_soc_write(codec, WM8900_REG_RADC_DV,
+ snd_soc_read(codec, WM8900_REG_RADC_DV) | 0x100);
/* Set the DAC and mixer output bias */
- wm8900_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
+ snd_soc_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
wm8900_dai.dev = &i2c->dev;
@@ -1388,6 +1312,21 @@ static __devexit int wm8900_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM
+static int wm8900_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8900_i2c_resume(struct i2c_client *client)
+{
+ return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8900_i2c_suspend NULL
+#define wm8900_i2c_resume NULL
+#endif
+
static const struct i2c_device_id wm8900_i2c_id[] = {
{ "wm8900", 0 },
{ }
@@ -1401,6 +1340,8 @@ static struct i2c_driver wm8900_i2c_driver = {
},
.probe = wm8900_i2c_probe,
.remove = __devexit_p(wm8900_i2c_remove),
+ .suspend = wm8900_i2c_suspend,
+ .resume = wm8900_i2c_resume,
.id_table = wm8900_i2c_id,
};
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index e8d2e3e14c4..fe1307b500c 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -225,94 +225,18 @@ struct wm8903_priv {
struct snd_pcm_substream *slave_substream;
};
-
-static unsigned int wm8903_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
-
- BUG_ON(reg >= ARRAY_SIZE(wm8903_reg_defaults));
-
- return cache[reg];
-}
-
-static unsigned int wm8903_hw_read(struct snd_soc_codec *codec, u8 reg)
-{
- struct i2c_msg xfer[2];
- u16 data;
- int ret;
- struct i2c_client *client = codec->control_data;
-
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 1;
- xfer[0].buf = &reg;
-
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 2;
- xfer[1].buf = (u8 *)&data;
-
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- pr_err("i2c_transfer returned %d\n", ret);
- return 0;
- }
-
- return (data >> 8) | ((data & 0xff) << 8);
-}
-
-static unsigned int wm8903_read(struct snd_soc_codec *codec,
- unsigned int reg)
+static int wm8903_volatile_register(unsigned int reg)
{
switch (reg) {
case WM8903_SW_RESET_AND_ID:
case WM8903_REVISION_NUMBER:
case WM8903_INTERRUPT_STATUS_1:
case WM8903_WRITE_SEQUENCER_4:
- return wm8903_hw_read(codec, reg);
+ return 1;
default:
- return wm8903_read_reg_cache(codec, reg);
- }
-}
-
-static void wm8903_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
-
- BUG_ON(reg >= ARRAY_SIZE(wm8903_reg_defaults));
-
- switch (reg) {
- case WM8903_SW_RESET_AND_ID:
- case WM8903_REVISION_NUMBER:
- break;
-
- default:
- cache[reg] = value;
- break;
- }
-}
-
-static int wm8903_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[3];
-
- wm8903_write_reg_cache(codec, reg, value);
-
- /* Data format is 1 byte of address followed by 2 bytes of data */
- data[0] = reg;
- data[1] = (value >> 8) & 0xff;
- data[2] = value & 0xff;
-
- if (codec->hw_write(codec->control_data, data, 3) == 2)
return 0;
- else
- return -EIO;
+ }
}
static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start)
@@ -323,13 +247,13 @@ static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start)
BUG_ON(start > 48);
/* Enable the sequencer */
- reg[0] = wm8903_read(codec, WM8903_WRITE_SEQUENCER_0);
+ reg[0] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_0);
reg[0] |= WM8903_WSEQ_ENA;
- wm8903_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]);
+ snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]);
dev_dbg(&i2c->dev, "Starting sequence at %d\n", start);
- wm8903_write(codec, WM8903_WRITE_SEQUENCER_3,
+ snd_soc_write(codec, WM8903_WRITE_SEQUENCER_3,
start | WM8903_WSEQ_START);
/* Wait for it to complete. If we have the interrupt wired up then
@@ -339,13 +263,13 @@ static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start)
do {
msleep(10);
- reg[4] = wm8903_read(codec, WM8903_WRITE_SEQUENCER_4);
+ reg[4] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_4);
} while (reg[4] & WM8903_WSEQ_BUSY);
dev_dbg(&i2c->dev, "Sequence complete\n");
/* Disable the sequencer again */
- wm8903_write(codec, WM8903_WRITE_SEQUENCER_0,
+ snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0,
reg[0] & ~WM8903_WSEQ_ENA);
return 0;
@@ -357,12 +281,12 @@ static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache)
/* There really ought to be something better we can do here :/ */
for (i = 0; i < ARRAY_SIZE(wm8903_reg_defaults); i++)
- cache[i] = wm8903_hw_read(codec, i);
+ cache[i] = codec->hw_read(codec, i);
}
static void wm8903_reset(struct snd_soc_codec *codec)
{
- wm8903_write(codec, WM8903_SW_RESET_AND_ID, 0);
+ snd_soc_write(codec, WM8903_SW_RESET_AND_ID, 0);
memcpy(codec->reg_cache, wm8903_reg_defaults,
sizeof(wm8903_reg_defaults));
}
@@ -423,52 +347,52 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
}
if (event & SND_SOC_DAPM_PRE_PMU) {
- val = wm8903_read(codec, reg);
+ val = snd_soc_read(codec, reg);
/* Short the output */
val &= ~(WM8903_OUTPUT_SHORT << shift);
- wm8903_write(codec, reg, val);
+ snd_soc_write(codec, reg, val);
}
if (event & SND_SOC_DAPM_POST_PMU) {
- val = wm8903_read(codec, reg);
+ val = snd_soc_read(codec, reg);
val |= (WM8903_OUTPUT_IN << shift);
- wm8903_write(codec, reg, val);
+ snd_soc_write(codec, reg, val);
val |= (WM8903_OUTPUT_INT << shift);
- wm8903_write(codec, reg, val);
+ snd_soc_write(codec, reg, val);
/* Turn on the output ENA_OUTP */
val |= (WM8903_OUTPUT_OUT << shift);
- wm8903_write(codec, reg, val);
+ snd_soc_write(codec, reg, val);
/* Enable the DC servo */
- dcs_reg = wm8903_read(codec, WM8903_DC_SERVO_0);
+ dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0);
dcs_reg |= dcs_bit;
- wm8903_write(codec, WM8903_DC_SERVO_0, dcs_reg);
+ snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg);
/* Remove the short */
val |= (WM8903_OUTPUT_SHORT << shift);
- wm8903_write(codec, reg, val);
+ snd_soc_write(codec, reg, val);
}
if (event & SND_SOC_DAPM_PRE_PMD) {
- val = wm8903_read(codec, reg);
+ val = snd_soc_read(codec, reg);
/* Short the output */
val &= ~(WM8903_OUTPUT_SHORT << shift);
- wm8903_write(codec, reg, val);
+ snd_soc_write(codec, reg, val);
/* Disable the DC servo */
- dcs_reg = wm8903_read(codec, WM8903_DC_SERVO_0);
+ dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0);
dcs_reg &= ~dcs_bit;
- wm8903_write(codec, WM8903_DC_SERVO_0, dcs_reg);
+ snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg);
/* Then disable the intermediate and output stages */
val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT |
WM8903_OUTPUT_IN) << shift);
- wm8903_write(codec, reg, val);
+ snd_soc_write(codec, reg, val);
}
return 0;
@@ -492,13 +416,13 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
u16 reg;
int ret;
- reg = wm8903_read(codec, WM8903_CLASS_W_0);
+ reg = snd_soc_read(codec, WM8903_CLASS_W_0);
/* Turn it off if we're about to enable bypass */
if (ucontrol->value.integer.value[0]) {
if (wm8903->class_w_users == 0) {
dev_dbg(&i2c->dev, "Disabling Class W\n");
- wm8903_write(codec, WM8903_CLASS_W_0, reg &
+ snd_soc_write(codec, WM8903_CLASS_W_0, reg &
~(WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V));
}
wm8903->class_w_users++;
@@ -511,7 +435,7 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
if (!ucontrol->value.integer.value[0]) {
if (wm8903->class_w_users == 1) {
dev_dbg(&i2c->dev, "Enabling Class W\n");
- wm8903_write(codec, WM8903_CLASS_W_0, reg |
+ snd_soc_write(codec, WM8903_CLASS_W_0, reg |
WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V);
}
wm8903->class_w_users--;
@@ -715,8 +639,6 @@ SOC_ENUM("DAC Soft Mute Rate", soft_mute),
SOC_ENUM("DAC Mute Mode", mute_mode),
SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0),
SOC_ENUM("DAC De-emphasis", dac_deemphasis),
-SOC_SINGLE("DAC Sloping Stopband Filter Switch",
- WM8903_DAC_DIGITAL_1, 11, 1, 0),
SOC_ENUM("DAC Companding Mode", dac_companding),
SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0),
@@ -1011,55 +933,55 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
- reg = wm8903_read(codec, WM8903_VMID_CONTROL_0);
+ reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0);
reg &= ~(WM8903_VMID_RES_MASK);
reg |= WM8903_VMID_RES_50K;
- wm8903_write(codec, WM8903_VMID_CONTROL_0, reg);
+ snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
- wm8903_write(codec, WM8903_CLOCK_RATES_2,
+ snd_soc_write(codec, WM8903_CLOCK_RATES_2,
WM8903_CLK_SYS_ENA);
/* Change DC servo dither level in startup sequence */
- wm8903_write(codec, WM8903_WRITE_SEQUENCER_0, 0x11);
- wm8903_write(codec, WM8903_WRITE_SEQUENCER_1, 0x1257);
- wm8903_write(codec, WM8903_WRITE_SEQUENCER_2, 0x2);
+ snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, 0x11);
+ snd_soc_write(codec, WM8903_WRITE_SEQUENCER_1, 0x1257);
+ snd_soc_write(codec, WM8903_WRITE_SEQUENCER_2, 0x2);
wm8903_run_sequence(codec, 0);
wm8903_sync_reg_cache(codec, codec->reg_cache);
/* Enable low impedence charge pump output */
- reg = wm8903_read(codec,
+ reg = snd_soc_read(codec,
WM8903_CONTROL_INTERFACE_TEST_1);
- wm8903_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,
+ snd_soc_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,
reg | WM8903_TEST_KEY);
- reg2 = wm8903_read(codec, WM8903_CHARGE_PUMP_TEST_1);
- wm8903_write(codec, WM8903_CHARGE_PUMP_TEST_1,
+ reg2 = snd_soc_read(codec, WM8903_CHARGE_PUMP_TEST_1);
+ snd_soc_write(codec, WM8903_CHARGE_PUMP_TEST_1,
reg2 | WM8903_CP_SW_KELVIN_MODE_MASK);
- wm8903_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,
+ snd_soc_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,
reg);
/* By default no bypass paths are enabled so
* enable Class W support.
*/
dev_dbg(&i2c->dev, "Enabling Class W\n");
- wm8903_write(codec, WM8903_CLASS_W_0, reg |
+ snd_soc_write(codec, WM8903_CLASS_W_0, reg |
WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V);
}
- reg = wm8903_read(codec, WM8903_VMID_CONTROL_0);
+ reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0);
reg &= ~(WM8903_VMID_RES_MASK);
reg |= WM8903_VMID_RES_250K;
- wm8903_write(codec, WM8903_VMID_CONTROL_0, reg);
+ snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg);
break;
case SND_SOC_BIAS_OFF:
wm8903_run_sequence(codec, 32);
- reg = wm8903_read(codec, WM8903_CLOCK_RATES_2);
+ reg = snd_soc_read(codec, WM8903_CLOCK_RATES_2);
reg &= ~WM8903_CLK_SYS_ENA;
- wm8903_write(codec, WM8903_CLOCK_RATES_2, reg);
+ snd_soc_write(codec, WM8903_CLOCK_RATES_2, reg);
break;
}
@@ -1083,7 +1005,7 @@ static int wm8903_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
- u16 aif1 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_1);
+ u16 aif1 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_1);
aif1 &= ~(WM8903_LRCLK_DIR | WM8903_BCLK_DIR | WM8903_AIF_FMT_MASK |
WM8903_AIF_LRCLK_INV | WM8903_AIF_BCLK_INV);
@@ -1161,7 +1083,7 @@ static int wm8903_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
+ snd_soc_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
return 0;
}
@@ -1171,14 +1093,14 @@ static int wm8903_digital_mute(struct snd_soc_dai *codec_dai, int mute)
struct snd_soc_codec *codec = codec_dai->codec;
u16 reg;
- reg = wm8903_read(codec, WM8903_DAC_DIGITAL_1);
+ reg = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);
if (mute)
reg |= WM8903_DAC_MUTE;
else
reg &= ~WM8903_DAC_MUTE;
- wm8903_write(codec, WM8903_DAC_DIGITAL_1, reg);
+ snd_soc_write(codec, WM8903_DAC_DIGITAL_1, reg);
return 0;
}
@@ -1368,17 +1290,24 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
int cur_val;
int clk_sys;
- u16 aif1 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_1);
- u16 aif2 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_2);
- u16 aif3 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_3);
- u16 clock0 = wm8903_read(codec, WM8903_CLOCK_RATES_0);
- u16 clock1 = wm8903_read(codec, WM8903_CLOCK_RATES_1);
+ u16 aif1 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_1);
+ u16 aif2 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_2);
+ u16 aif3 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_3);
+ u16 clock0 = snd_soc_read(codec, WM8903_CLOCK_RATES_0);
+ u16 clock1 = snd_soc_read(codec, WM8903_CLOCK_RATES_1);
+ u16 dac_digital1 = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);
if (substream == wm8903->slave_substream) {
dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n");
return 0;
}
+ /* Enable sloping stopband filter for low sample rates */
+ if (fs <= 24000)
+ dac_digital1 |= WM8903_DAC_SB_FILT;
+ else
+ dac_digital1 &= ~WM8903_DAC_SB_FILT;
+
/* Configure sample rate logic for DSP - choose nearest rate */
dsp_config = 0;
best_val = abs(sample_rates[dsp_config].rate - fs);
@@ -1498,11 +1427,12 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
aif2 |= bclk_divs[bclk_div].div;
aif3 |= bclk / fs;
- wm8903_write(codec, WM8903_CLOCK_RATES_0, clock0);
- wm8903_write(codec, WM8903_CLOCK_RATES_1, clock1);
- wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
- wm8903_write(codec, WM8903_AUDIO_INTERFACE_2, aif2);
- wm8903_write(codec, WM8903_AUDIO_INTERFACE_3, aif3);
+ snd_soc_write(codec, WM8903_CLOCK_RATES_0, clock0);
+ snd_soc_write(codec, WM8903_CLOCK_RATES_1, clock1);
+ snd_soc_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
+ snd_soc_write(codec, WM8903_AUDIO_INTERFACE_2, aif2);
+ snd_soc_write(codec, WM8903_AUDIO_INTERFACE_3, aif3);
+ snd_soc_write(codec, WM8903_DAC_DIGITAL_1, dac_digital1);
return 0;
}
@@ -1587,7 +1517,7 @@ static int wm8903_resume(struct platform_device *pdev)
if (tmp_cache) {
for (i = 2; i < ARRAY_SIZE(wm8903_reg_defaults); i++)
if (tmp_cache[i] != reg_cache[i])
- wm8903_write(codec, i, tmp_cache[i]);
+ snd_soc_write(codec, i, tmp_cache[i]);
} else {
dev_err(&i2c->dev, "Failed to allocate temporary cache\n");
}
@@ -1618,9 +1548,6 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
codec->dev = &i2c->dev;
codec->name = "WM8903";
codec->owner = THIS_MODULE;
- codec->read = wm8903_read;
- codec->write = wm8903_write;
- codec->hw_write = (hw_write_t)i2c_master_send;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8903_set_bias_level;
codec->dai = &wm8903_dai;
@@ -1628,18 +1555,25 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
codec->reg_cache_size = ARRAY_SIZE(wm8903->reg_cache);
codec->reg_cache = &wm8903->reg_cache[0];
codec->private_data = wm8903;
+ codec->volatile_register = wm8903_volatile_register;
i2c_set_clientdata(i2c, codec);
codec->control_data = i2c;
- val = wm8903_hw_read(codec, WM8903_SW_RESET_AND_ID);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ val = snd_soc_read(codec, WM8903_SW_RESET_AND_ID);
if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) {
dev_err(&i2c->dev,
"Device with ID register %x is not a WM8903\n", val);
return -ENODEV;
}
- val = wm8903_read(codec, WM8903_REVISION_NUMBER);
+ val = snd_soc_read(codec, WM8903_REVISION_NUMBER);
dev_info(&i2c->dev, "WM8903 revision %d\n",
val & WM8903_CHIP_REV_MASK);
@@ -1649,35 +1583,35 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Latch volume update bits */
- val = wm8903_read(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT);
+ val = snd_soc_read(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT);
val |= WM8903_ADCVU;
- wm8903_write(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT, val);
- wm8903_write(codec, WM8903_ADC_DIGITAL_VOLUME_RIGHT, val);
+ snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT, val);
+ snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_RIGHT, val);
- val = wm8903_read(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT);
+ val = snd_soc_read(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT);
val |= WM8903_DACVU;
- wm8903_write(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT, val);
- wm8903_write(codec, WM8903_DAC_DIGITAL_VOLUME_RIGHT, val);
+ snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT, val);
+ snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_RIGHT, val);
- val = wm8903_read(codec, WM8903_ANALOGUE_OUT1_LEFT);
+ val = snd_soc_read(codec, WM8903_ANALOGUE_OUT1_LEFT);
val |= WM8903_HPOUTVU;
- wm8903_write(codec, WM8903_ANALOGUE_OUT1_LEFT, val);
- wm8903_write(codec, WM8903_ANALOGUE_OUT1_RIGHT, val);
+ snd_soc_write(codec, WM8903_ANALOGUE_OUT1_LEFT, val);
+ snd_soc_write(codec, WM8903_ANALOGUE_OUT1_RIGHT, val);
- val = wm8903_read(codec, WM8903_ANALOGUE_OUT2_LEFT);
+ val = snd_soc_read(codec, WM8903_ANALOGUE_OUT2_LEFT);
val |= WM8903_LINEOUTVU;
- wm8903_write(codec, WM8903_ANALOGUE_OUT2_LEFT, val);
- wm8903_write(codec, WM8903_ANALOGUE_OUT2_RIGHT, val);
+ snd_soc_write(codec, WM8903_ANALOGUE_OUT2_LEFT, val);
+ snd_soc_write(codec, WM8903_ANALOGUE_OUT2_RIGHT, val);
- val = wm8903_read(codec, WM8903_ANALOGUE_OUT3_LEFT);
+ val = snd_soc_read(codec, WM8903_ANALOGUE_OUT3_LEFT);
val |= WM8903_SPKVU;
- wm8903_write(codec, WM8903_ANALOGUE_OUT3_LEFT, val);
- wm8903_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val);
+ snd_soc_write(codec, WM8903_ANALOGUE_OUT3_LEFT, val);
+ snd_soc_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val);
/* Enable DAC soft mute by default */
- val = wm8903_read(codec, WM8903_DAC_DIGITAL_1);
+ val = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);
val |= WM8903_DAC_MUTEMODE;
- wm8903_write(codec, WM8903_DAC_DIGITAL_1, val);
+ snd_soc_write(codec, WM8903_DAC_DIGITAL_1, val);
wm8903_dai.dev = &i2c->dev;
wm8903_codec = codec;
@@ -1721,6 +1655,21 @@ static __devexit int wm8903_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM
+static int wm8903_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8903_i2c_resume(struct i2c_client *client)
+{
+ return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8903_i2c_suspend NULL
+#define wm8903_i2c_resume NULL
+#endif
+
/* i2c codec control layer */
static const struct i2c_device_id wm8903_i2c_id[] = {
{ "wm8903", 0 },
@@ -1735,6 +1684,8 @@ static struct i2c_driver wm8903_i2c_driver = {
},
.probe = wm8903_i2c_probe,
.remove = __devexit_p(wm8903_i2c_remove),
+ .suspend = wm8903_i2c_suspend,
+ .resume = wm8903_i2c_resume,
.id_table = wm8903_i2c_id,
};
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index b8e17d6bc1f..da97aae475a 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -106,50 +106,6 @@ static u16 wm8940_reg_defaults[] = {
0x0000, /* Mono Mixer Control */
};
-static inline unsigned int wm8940_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
-
- if (reg >= ARRAY_SIZE(wm8940_reg_defaults))
- return -1;
-
- return cache[reg];
-}
-
-static inline int wm8940_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
-
- if (reg >= ARRAY_SIZE(wm8940_reg_defaults))
- return -1;
-
- cache[reg] = value;
-
- return 0;
-}
-
-static int wm8940_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- int ret;
- u8 data[3] = { reg,
- (value & 0xff00) >> 8,
- (value & 0x00ff)
- };
-
- wm8940_write_reg_cache(codec, reg, value);
-
- ret = codec->hw_write(codec->control_data, data, 3);
-
- if (ret < 0)
- return ret;
- else if (ret != 3)
- return -EIO;
- return 0;
-}
-
static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" };
static const struct soc_enum wm8940_adc_companding_enum
= SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 1, 4, wm8940_companding);
@@ -348,14 +304,14 @@ error_ret:
return ret;
}
-#define wm8940_reset(c) wm8940_write(c, WM8940_SOFTRESET, 0);
+#define wm8940_reset(c) snd_soc_write(c, WM8940_SOFTRESET, 0);
static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
- u16 iface = wm8940_read_reg_cache(codec, WM8940_IFACE) & 0xFE67;
- u16 clk = wm8940_read_reg_cache(codec, WM8940_CLOCK) & 0x1fe;
+ u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFE67;
+ u16 clk = snd_soc_read(codec, WM8940_CLOCK) & 0x1fe;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
@@ -366,7 +322,7 @@ static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
default:
return -EINVAL;
}
- wm8940_write(codec, WM8940_CLOCK, clk);
+ snd_soc_write(codec, WM8940_CLOCK, clk);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
@@ -399,7 +355,7 @@ static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
break;
}
- wm8940_write(codec, WM8940_IFACE, iface);
+ snd_soc_write(codec, WM8940_IFACE, iface);
return 0;
}
@@ -411,9 +367,9 @@ static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- u16 iface = wm8940_read_reg_cache(codec, WM8940_IFACE) & 0xFD9F;
- u16 addcntrl = wm8940_read_reg_cache(codec, WM8940_ADDCNTRL) & 0xFFF1;
- u16 companding = wm8940_read_reg_cache(codec,
+ u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFD9F;
+ u16 addcntrl = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFF1;
+ u16 companding = snd_soc_read(codec,
WM8940_COMPANDINGCTL) & 0xFFDF;
int ret;
@@ -442,7 +398,7 @@ static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
case SNDRV_PCM_RATE_48000:
break;
}
- ret = wm8940_write(codec, WM8940_ADDCNTRL, addcntrl);
+ ret = snd_soc_write(codec, WM8940_ADDCNTRL, addcntrl);
if (ret)
goto error_ret;
@@ -462,10 +418,10 @@ static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
iface |= (3 << 5);
break;
}
- ret = wm8940_write(codec, WM8940_COMPANDINGCTL, companding);
+ ret = snd_soc_write(codec, WM8940_COMPANDINGCTL, companding);
if (ret)
goto error_ret;
- ret = wm8940_write(codec, WM8940_IFACE, iface);
+ ret = snd_soc_write(codec, WM8940_IFACE, iface);
error_ret:
return ret;
@@ -474,19 +430,19 @@ error_ret:
static int wm8940_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8940_read_reg_cache(codec, WM8940_DAC) & 0xffbf;
+ u16 mute_reg = snd_soc_read(codec, WM8940_DAC) & 0xffbf;
if (mute)
mute_reg |= 0x40;
- return wm8940_write(codec, WM8940_DAC, mute_reg);
+ return snd_soc_write(codec, WM8940_DAC, mute_reg);
}
static int wm8940_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
u16 val;
- u16 pwr_reg = wm8940_read_reg_cache(codec, WM8940_POWER1) & 0x1F0;
+ u16 pwr_reg = snd_soc_read(codec, WM8940_POWER1) & 0x1F0;
int ret = 0;
switch (level) {
@@ -494,26 +450,26 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec,
/* ensure bufioen and biasen */
pwr_reg |= (1 << 2) | (1 << 3);
/* Enable thermal shutdown */
- val = wm8940_read_reg_cache(codec, WM8940_OUTPUTCTL);
- ret = wm8940_write(codec, WM8940_OUTPUTCTL, val | 0x2);
+ val = snd_soc_read(codec, WM8940_OUTPUTCTL);
+ ret = snd_soc_write(codec, WM8940_OUTPUTCTL, val | 0x2);
if (ret)
break;
/* set vmid to 75k */
- ret = wm8940_write(codec, WM8940_POWER1, pwr_reg | 0x1);
+ ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
break;
case SND_SOC_BIAS_PREPARE:
/* ensure bufioen and biasen */
pwr_reg |= (1 << 2) | (1 << 3);
- ret = wm8940_write(codec, WM8940_POWER1, pwr_reg | 0x1);
+ ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
break;
case SND_SOC_BIAS_STANDBY:
/* ensure bufioen and biasen */
pwr_reg |= (1 << 2) | (1 << 3);
/* set vmid to 300k for standby */
- ret = wm8940_write(codec, WM8940_POWER1, pwr_reg | 0x2);
+ ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x2);
break;
case SND_SOC_BIAS_OFF:
- ret = wm8940_write(codec, WM8940_POWER1, pwr_reg);
+ ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg);
break;
}
@@ -587,36 +543,36 @@ static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai,
u16 reg;
/* Turn off PLL */
- reg = wm8940_read_reg_cache(codec, WM8940_POWER1);
- wm8940_write(codec, WM8940_POWER1, reg & 0x1df);
+ reg = snd_soc_read(codec, WM8940_POWER1);
+ snd_soc_write(codec, WM8940_POWER1, reg & 0x1df);
if (freq_in == 0 || freq_out == 0) {
/* Clock CODEC directly from MCLK */
- reg = wm8940_read_reg_cache(codec, WM8940_CLOCK);
- wm8940_write(codec, WM8940_CLOCK, reg & 0x0ff);
+ reg = snd_soc_read(codec, WM8940_CLOCK);
+ snd_soc_write(codec, WM8940_CLOCK, reg & 0x0ff);
/* Pll power down */
- wm8940_write(codec, WM8940_PLLN, (1 << 7));
+ snd_soc_write(codec, WM8940_PLLN, (1 << 7));
return 0;
}
/* Pll is followed by a frequency divide by 4 */
pll_factors(freq_out*4, freq_in);
if (pll_div.k)
- wm8940_write(codec, WM8940_PLLN,
+ snd_soc_write(codec, WM8940_PLLN,
(pll_div.pre_scale << 4) | pll_div.n | (1 << 6));
else /* No factional component */
- wm8940_write(codec, WM8940_PLLN,
+ snd_soc_write(codec, WM8940_PLLN,
(pll_div.pre_scale << 4) | pll_div.n);
- wm8940_write(codec, WM8940_PLLK1, pll_div.k >> 18);
- wm8940_write(codec, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff);
- wm8940_write(codec, WM8940_PLLK3, pll_div.k & 0x1ff);
+ snd_soc_write(codec, WM8940_PLLK1, pll_div.k >> 18);
+ snd_soc_write(codec, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8940_PLLK3, pll_div.k & 0x1ff);
/* Enable the PLL */
- reg = wm8940_read_reg_cache(codec, WM8940_POWER1);
- wm8940_write(codec, WM8940_POWER1, reg | 0x020);
+ reg = snd_soc_read(codec, WM8940_POWER1);
+ snd_soc_write(codec, WM8940_POWER1, reg | 0x020);
/* Run CODEC from PLL instead of MCLK */
- reg = wm8940_read_reg_cache(codec, WM8940_CLOCK);
- wm8940_write(codec, WM8940_CLOCK, reg | 0x100);
+ reg = snd_soc_read(codec, WM8940_CLOCK);
+ snd_soc_write(codec, WM8940_CLOCK, reg | 0x100);
return 0;
}
@@ -648,16 +604,16 @@ static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
switch (div_id) {
case WM8940_BCLKDIV:
- reg = wm8940_read_reg_cache(codec, WM8940_CLOCK) & 0xFFEF3;
- ret = wm8940_write(codec, WM8940_CLOCK, reg | (div << 2));
+ reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFFEF3;
+ ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 2));
break;
case WM8940_MCLKDIV:
- reg = wm8940_read_reg_cache(codec, WM8940_CLOCK) & 0xFF1F;
- ret = wm8940_write(codec, WM8940_CLOCK, reg | (div << 5));
+ reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFF1F;
+ ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 5));
break;
case WM8940_OPCLKDIV:
- reg = wm8940_read_reg_cache(codec, WM8940_ADDCNTRL) & 0xFFCF;
- ret = wm8940_write(codec, WM8940_ADDCNTRL, reg | (div << 4));
+ reg = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFCF;
+ ret = snd_soc_write(codec, WM8940_ADDCNTRL, reg | (div << 4));
break;
}
return ret;
@@ -808,7 +764,8 @@ struct snd_soc_codec_device soc_codec_dev_wm8940 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8940);
-static int wm8940_register(struct wm8940_priv *wm8940)
+static int wm8940_register(struct wm8940_priv *wm8940,
+ enum snd_soc_control_type control)
{
struct wm8940_setup_data *pdata = wm8940->codec.dev->platform_data;
struct snd_soc_codec *codec = &wm8940->codec;
@@ -825,8 +782,6 @@ static int wm8940_register(struct wm8940_priv *wm8940)
codec->private_data = wm8940;
codec->name = "WM8940";
codec->owner = THIS_MODULE;
- codec->read = wm8940_read_reg_cache;
- codec->write = wm8940_write;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8940_set_bias_level;
codec->dai = &wm8940_dai;
@@ -834,6 +789,12 @@ static int wm8940_register(struct wm8940_priv *wm8940)
codec->reg_cache_size = ARRAY_SIZE(wm8940_reg_defaults);
codec->reg_cache = &wm8940->reg_cache;
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, control);
+ if (ret == 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
memcpy(codec->reg_cache, wm8940_reg_defaults,
sizeof(wm8940_reg_defaults));
@@ -847,15 +808,15 @@ static int wm8940_register(struct wm8940_priv *wm8940)
wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- ret = wm8940_write(codec, WM8940_POWER1, 0x180);
+ ret = snd_soc_write(codec, WM8940_POWER1, 0x180);
if (ret < 0)
return ret;
if (!pdata)
dev_warn(codec->dev, "No platform data supplied\n");
else {
- reg = wm8940_read_reg_cache(codec, WM8940_OUTPUTCTL);
- ret = wm8940_write(codec, WM8940_OUTPUTCTL, reg | pdata->vroi);
+ reg = snd_soc_read(codec, WM8940_OUTPUTCTL);
+ ret = snd_soc_write(codec, WM8940_OUTPUTCTL, reg | pdata->vroi);
if (ret < 0)
return ret;
}
@@ -904,7 +865,7 @@ static int wm8940_i2c_probe(struct i2c_client *i2c,
codec->control_data = i2c;
codec->dev = &i2c->dev;
- return wm8940_register(wm8940);
+ return wm8940_register(wm8940, SND_SOC_I2C);
}
static int __devexit wm8940_i2c_remove(struct i2c_client *client)
@@ -916,6 +877,21 @@ static int __devexit wm8940_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM
+static int wm8940_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8940_i2c_resume(struct i2c_client *client)
+{
+ return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8940_i2c_suspend NULL
+#define wm8940_i2c_resume NULL
+#endif
+
static const struct i2c_device_id wm8940_i2c_id[] = {
{ "wm8940", 0 },
{ }
@@ -929,6 +905,8 @@ static struct i2c_driver wm8940_i2c_driver = {
},
.probe = wm8940_i2c_probe,
.remove = __devexit_p(wm8940_i2c_remove),
+ .suspend = wm8940_i2c_suspend,
+ .resume = wm8940_i2c_resume,
.id_table = wm8940_i2c_id,
};
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index e224d8add17..f59703be61c 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -69,61 +69,7 @@ struct wm8960_priv {
struct snd_soc_codec codec;
};
-/*
- * read wm8960 register cache
- */
-static inline unsigned int wm8960_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg == WM8960_RESET)
- return 0;
- if (reg >= WM8960_CACHEREGNUM)
- return -1;
- return cache[reg];
-}
-
-/*
- * write wm8960 register cache
- */
-static inline void wm8960_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg >= WM8960_CACHEREGNUM)
- return;
- cache[reg] = value;
-}
-
-static inline unsigned int wm8960_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- return wm8960_read_reg_cache(codec, reg);
-}
-
-/*
- * write to the WM8960 register space
- */
-static int wm8960_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8960 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8960_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define wm8960_reset(c) wm8960_write(c, WM8960_RESET, 0)
+#define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0)
/* enumerated controls */
static const char *wm8960_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
@@ -420,7 +366,7 @@ static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
/* set iface */
- wm8960_write(codec, WM8960_IFACE1, iface);
+ snd_soc_write(codec, WM8960_IFACE1, iface);
return 0;
}
@@ -431,7 +377,7 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- u16 iface = wm8960_read(codec, WM8960_IFACE1) & 0xfff3;
+ u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
/* bit size */
switch (params_format(params)) {
@@ -446,19 +392,19 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
}
/* set iface */
- wm8960_write(codec, WM8960_IFACE1, iface);
+ snd_soc_write(codec, WM8960_IFACE1, iface);
return 0;
}
static int wm8960_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8960_read(codec, WM8960_DACCTL1) & 0xfff7;
+ u16 mute_reg = snd_soc_read(codec, WM8960_DACCTL1) & 0xfff7;
if (mute)
- wm8960_write(codec, WM8960_DACCTL1, mute_reg | 0x8);
+ snd_soc_write(codec, WM8960_DACCTL1, mute_reg | 0x8);
else
- wm8960_write(codec, WM8960_DACCTL1, mute_reg);
+ snd_soc_write(codec, WM8960_DACCTL1, mute_reg);
return 0;
}
@@ -474,16 +420,16 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* Set VMID to 2x50k */
- reg = wm8960_read(codec, WM8960_POWER1);
+ reg = snd_soc_read(codec, WM8960_POWER1);
reg &= ~0x180;
reg |= 0x80;
- wm8960_write(codec, WM8960_POWER1, reg);
+ snd_soc_write(codec, WM8960_POWER1, reg);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Enable anti-pop features */
- wm8960_write(codec, WM8960_APOP1,
+ snd_soc_write(codec, WM8960_APOP1,
WM8960_POBCTRL | WM8960_SOFT_ST |
WM8960_BUFDCOPEN | WM8960_BUFIOEN);
@@ -491,43 +437,43 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
reg = WM8960_DISOP;
if (pdata)
reg |= pdata->dres << 4;
- wm8960_write(codec, WM8960_APOP2, reg);
+ snd_soc_write(codec, WM8960_APOP2, reg);
msleep(400);
- wm8960_write(codec, WM8960_APOP2, 0);
+ snd_soc_write(codec, WM8960_APOP2, 0);
/* Enable & ramp VMID at 2x50k */
- reg = wm8960_read(codec, WM8960_POWER1);
+ reg = snd_soc_read(codec, WM8960_POWER1);
reg |= 0x80;
- wm8960_write(codec, WM8960_POWER1, reg);
+ snd_soc_write(codec, WM8960_POWER1, reg);
msleep(100);
/* Enable VREF */
- wm8960_write(codec, WM8960_POWER1, reg | WM8960_VREF);
+ snd_soc_write(codec, WM8960_POWER1, reg | WM8960_VREF);
/* Disable anti-pop features */
- wm8960_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
+ snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
}
/* Set VMID to 2x250k */
- reg = wm8960_read(codec, WM8960_POWER1);
+ reg = snd_soc_read(codec, WM8960_POWER1);
reg &= ~0x180;
reg |= 0x100;
- wm8960_write(codec, WM8960_POWER1, reg);
+ snd_soc_write(codec, WM8960_POWER1, reg);
break;
case SND_SOC_BIAS_OFF:
/* Enable anti-pop features */
- wm8960_write(codec, WM8960_APOP1,
+ snd_soc_write(codec, WM8960_APOP1,
WM8960_POBCTRL | WM8960_SOFT_ST |
WM8960_BUFDCOPEN | WM8960_BUFIOEN);
/* Disable VMID and VREF, let them discharge */
- wm8960_write(codec, WM8960_POWER1, 0);
+ snd_soc_write(codec, WM8960_POWER1, 0);
msleep(600);
- wm8960_write(codec, WM8960_APOP1, 0);
+ snd_soc_write(codec, WM8960_APOP1, 0);
break;
}
@@ -610,33 +556,33 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai,
/* Disable the PLL: even if we are changing the frequency the
* PLL needs to be disabled while we do so. */
- wm8960_write(codec, WM8960_CLOCK1,
- wm8960_read(codec, WM8960_CLOCK1) & ~1);
- wm8960_write(codec, WM8960_POWER2,
- wm8960_read(codec, WM8960_POWER2) & ~1);
+ snd_soc_write(codec, WM8960_CLOCK1,
+ snd_soc_read(codec, WM8960_CLOCK1) & ~1);
+ snd_soc_write(codec, WM8960_POWER2,
+ snd_soc_read(codec, WM8960_POWER2) & ~1);
if (!freq_in || !freq_out)
return 0;
- reg = wm8960_read(codec, WM8960_PLL1) & ~0x3f;
+ reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f;
reg |= pll_div.pre_div << 4;
reg |= pll_div.n;
if (pll_div.k) {
reg |= 0x20;
- wm8960_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
- wm8960_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
- wm8960_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
+ snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
+ snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
}
- wm8960_write(codec, WM8960_PLL1, reg);
+ snd_soc_write(codec, WM8960_PLL1, reg);
/* Turn it on */
- wm8960_write(codec, WM8960_POWER2,
- wm8960_read(codec, WM8960_POWER2) | 1);
+ snd_soc_write(codec, WM8960_POWER2,
+ snd_soc_read(codec, WM8960_POWER2) | 1);
msleep(250);
- wm8960_write(codec, WM8960_CLOCK1,
- wm8960_read(codec, WM8960_CLOCK1) | 1);
+ snd_soc_write(codec, WM8960_CLOCK1,
+ snd_soc_read(codec, WM8960_CLOCK1) | 1);
return 0;
}
@@ -649,28 +595,28 @@ static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
switch (div_id) {
case WM8960_SYSCLKSEL:
- reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1fe;
- wm8960_write(codec, WM8960_CLOCK1, reg | div);
+ reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1fe;
+ snd_soc_write(codec, WM8960_CLOCK1, reg | div);
break;
case WM8960_SYSCLKDIV:
- reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1f9;
- wm8960_write(codec, WM8960_CLOCK1, reg | div);
+ reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9;
+ snd_soc_write(codec, WM8960_CLOCK1, reg | div);
break;
case WM8960_DACDIV:
- reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1c7;
- wm8960_write(codec, WM8960_CLOCK1, reg | div);
+ reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7;
+ snd_soc_write(codec, WM8960_CLOCK1, reg | div);
break;
case WM8960_OPCLKDIV:
- reg = wm8960_read(codec, WM8960_PLL1) & 0x03f;
- wm8960_write(codec, WM8960_PLL1, reg | div);
+ reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f;
+ snd_soc_write(codec, WM8960_PLL1, reg | div);
break;
case WM8960_DCLKDIV:
- reg = wm8960_read(codec, WM8960_CLOCK2) & 0x03f;
- wm8960_write(codec, WM8960_CLOCK2, reg | div);
+ reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f;
+ snd_soc_write(codec, WM8960_CLOCK2, reg | div);
break;
case WM8960_TOCLKSEL:
- reg = wm8960_read(codec, WM8960_ADDCTL1) & 0x1fd;
- wm8960_write(codec, WM8960_ADDCTL1, reg | div);
+ reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd;
+ snd_soc_write(codec, WM8960_ADDCTL1, reg | div);
break;
default:
return -EINVAL;
@@ -801,7 +747,8 @@ struct snd_soc_codec_device soc_codec_dev_wm8960 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8960);
-static int wm8960_register(struct wm8960_priv *wm8960)
+static int wm8960_register(struct wm8960_priv *wm8960,
+ enum snd_soc_control_type control)
{
struct wm8960_data *pdata = wm8960->codec.dev->platform_data;
struct snd_soc_codec *codec = &wm8960->codec;
@@ -810,7 +757,8 @@ static int wm8960_register(struct wm8960_priv *wm8960)
if (wm8960_codec) {
dev_err(codec->dev, "Another WM8960 is registered\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err;
}
if (!pdata) {
@@ -829,8 +777,6 @@ static int wm8960_register(struct wm8960_priv *wm8960)
codec->private_data = wm8960;
codec->name = "WM8960";
codec->owner = THIS_MODULE;
- codec->read = wm8960_read_reg_cache;
- codec->write = wm8960_write;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8960_set_bias_level;
codec->dai = &wm8960_dai;
@@ -840,10 +786,16 @@ static int wm8960_register(struct wm8960_priv *wm8960)
memcpy(codec->reg_cache, wm8960_reg, sizeof(wm8960_reg));
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
ret = wm8960_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset\n");
- return ret;
+ goto err;
}
wm8960_dai.dev = codec->dev;
@@ -851,43 +803,48 @@ static int wm8960_register(struct wm8960_priv *wm8960)
wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Latch the update bits */
- reg = wm8960_read(codec, WM8960_LINVOL);
- wm8960_write(codec, WM8960_LINVOL, reg | 0x100);
- reg = wm8960_read(codec, WM8960_RINVOL);
- wm8960_write(codec, WM8960_RINVOL, reg | 0x100);
- reg = wm8960_read(codec, WM8960_LADC);
- wm8960_write(codec, WM8960_LADC, reg | 0x100);
- reg = wm8960_read(codec, WM8960_RADC);
- wm8960_write(codec, WM8960_RADC, reg | 0x100);
- reg = wm8960_read(codec, WM8960_LDAC);
- wm8960_write(codec, WM8960_LDAC, reg | 0x100);
- reg = wm8960_read(codec, WM8960_RDAC);
- wm8960_write(codec, WM8960_RDAC, reg | 0x100);
- reg = wm8960_read(codec, WM8960_LOUT1);
- wm8960_write(codec, WM8960_LOUT1, reg | 0x100);
- reg = wm8960_read(codec, WM8960_ROUT1);
- wm8960_write(codec, WM8960_ROUT1, reg | 0x100);
- reg = wm8960_read(codec, WM8960_LOUT2);
- wm8960_write(codec, WM8960_LOUT2, reg | 0x100);
- reg = wm8960_read(codec, WM8960_ROUT2);
- wm8960_write(codec, WM8960_ROUT2, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_LINVOL);
+ snd_soc_write(codec, WM8960_LINVOL, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_RINVOL);
+ snd_soc_write(codec, WM8960_RINVOL, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_LADC);
+ snd_soc_write(codec, WM8960_LADC, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_RADC);
+ snd_soc_write(codec, WM8960_RADC, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_LDAC);
+ snd_soc_write(codec, WM8960_LDAC, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_RDAC);
+ snd_soc_write(codec, WM8960_RDAC, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_LOUT1);
+ snd_soc_write(codec, WM8960_LOUT1, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_ROUT1);
+ snd_soc_write(codec, WM8960_ROUT1, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_LOUT2);
+ snd_soc_write(codec, WM8960_LOUT2, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_ROUT2);
+ snd_soc_write(codec, WM8960_ROUT2, reg | 0x100);
wm8960_codec = codec;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
- return ret;
+ goto err;
}
ret = snd_soc_register_dai(&wm8960_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
- snd_soc_unregister_codec(codec);
- return ret;
+ goto err_codec;
}
return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ kfree(wm8960);
+ return ret;
}
static void wm8960_unregister(struct wm8960_priv *wm8960)
@@ -910,14 +867,13 @@ static __devinit int wm8960_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
codec = &wm8960->codec;
- codec->hw_write = (hw_write_t)i2c_master_send;
i2c_set_clientdata(i2c, wm8960);
codec->control_data = i2c;
codec->dev = &i2c->dev;
- return wm8960_register(wm8960);
+ return wm8960_register(wm8960, SND_SOC_I2C);
}
static __devexit int wm8960_i2c_remove(struct i2c_client *client)
@@ -927,6 +883,21 @@ static __devexit int wm8960_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM
+static int wm8960_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8960_i2c_resume(struct i2c_client *client)
+{
+ return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8960_i2c_suspend NULL
+#define wm8960_i2c_resume NULL
+#endif
+
static const struct i2c_device_id wm8960_i2c_id[] = {
{ "wm8960", 0 },
{ }
@@ -940,6 +911,8 @@ static struct i2c_driver wm8960_i2c_driver = {
},
.probe = wm8960_i2c_probe,
.remove = __devexit_p(wm8960_i2c_remove),
+ .suspend = wm8960_i2c_suspend,
+ .resume = wm8960_i2c_resume,
.id_table = wm8960_i2c_id,
};
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
new file mode 100644
index 00000000000..50303208589
--- /dev/null
+++ b/sound/soc/codecs/wm8961.c
@@ -0,0 +1,1265 @@
+/*
+ * wm8961.c -- WM8961 ALSA SoC Audio driver
+ *
+ * Author: Mark Brown
+ *
+ * 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.
+ *
+ * Currently unimplemented features:
+ * - ALC
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8961.h"
+
+#define WM8961_MAX_REGISTER 0xFC
+
+static u16 wm8961_reg_defaults[] = {
+ 0x009F, /* R0 - Left Input volume */
+ 0x009F, /* R1 - Right Input volume */
+ 0x0000, /* R2 - LOUT1 volume */
+ 0x0000, /* R3 - ROUT1 volume */
+ 0x0020, /* R4 - Clocking1 */
+ 0x0008, /* R5 - ADC & DAC Control 1 */
+ 0x0000, /* R6 - ADC & DAC Control 2 */
+ 0x000A, /* R7 - Audio Interface 0 */
+ 0x01F4, /* R8 - Clocking2 */
+ 0x0000, /* R9 - Audio Interface 1 */
+ 0x00FF, /* R10 - Left DAC volume */
+ 0x00FF, /* R11 - Right DAC volume */
+ 0x0000, /* R12 */
+ 0x0000, /* R13 */
+ 0x0040, /* R14 - Audio Interface 2 */
+ 0x0000, /* R15 - Software Reset */
+ 0x0000, /* R16 */
+ 0x007B, /* R17 - ALC1 */
+ 0x0000, /* R18 - ALC2 */
+ 0x0032, /* R19 - ALC3 */
+ 0x0000, /* R20 - Noise Gate */
+ 0x00C0, /* R21 - Left ADC volume */
+ 0x00C0, /* R22 - Right ADC volume */
+ 0x0120, /* R23 - Additional control(1) */
+ 0x0000, /* R24 - Additional control(2) */
+ 0x0000, /* R25 - Pwr Mgmt (1) */
+ 0x0000, /* R26 - Pwr Mgmt (2) */
+ 0x0000, /* R27 - Additional Control (3) */
+ 0x0000, /* R28 - Anti-pop */
+ 0x0000, /* R29 */
+ 0x005F, /* R30 - Clocking 3 */
+ 0x0000, /* R31 */
+ 0x0000, /* R32 - ADCL signal path */
+ 0x0000, /* R33 - ADCR signal path */
+ 0x0000, /* R34 */
+ 0x0000, /* R35 */
+ 0x0000, /* R36 */
+ 0x0000, /* R37 */
+ 0x0000, /* R38 */
+ 0x0000, /* R39 */
+ 0x0000, /* R40 - LOUT2 volume */
+ 0x0000, /* R41 - ROUT2 volume */
+ 0x0000, /* R42 */
+ 0x0000, /* R43 */
+ 0x0000, /* R44 */
+ 0x0000, /* R45 */
+ 0x0000, /* R46 */
+ 0x0000, /* R47 - Pwr Mgmt (3) */
+ 0x0023, /* R48 - Additional Control (4) */
+ 0x0000, /* R49 - Class D Control 1 */
+ 0x0000, /* R50 */
+ 0x0003, /* R51 - Class D Control 2 */
+ 0x0000, /* R52 */
+ 0x0000, /* R53 */
+ 0x0000, /* R54 */
+ 0x0000, /* R55 */
+ 0x0106, /* R56 - Clocking 4 */
+ 0x0000, /* R57 - DSP Sidetone 0 */
+ 0x0000, /* R58 - DSP Sidetone 1 */
+ 0x0000, /* R59 */
+ 0x0000, /* R60 - DC Servo 0 */
+ 0x0000, /* R61 - DC Servo 1 */
+ 0x0000, /* R62 */
+ 0x015E, /* R63 - DC Servo 3 */
+ 0x0010, /* R64 */
+ 0x0010, /* R65 - DC Servo 5 */
+ 0x0000, /* R66 */
+ 0x0001, /* R67 */
+ 0x0003, /* R68 - Analogue PGA Bias */
+ 0x0000, /* R69 - Analogue HP 0 */
+ 0x0060, /* R70 */
+ 0x01FB, /* R71 - Analogue HP 2 */
+ 0x0000, /* R72 - Charge Pump 1 */
+ 0x0065, /* R73 */
+ 0x005F, /* R74 */
+ 0x0059, /* R75 */
+ 0x006B, /* R76 */
+ 0x0038, /* R77 */
+ 0x000C, /* R78 */
+ 0x000A, /* R79 */
+ 0x006B, /* R80 */
+ 0x0000, /* R81 */
+ 0x0000, /* R82 - Charge Pump B */
+ 0x0087, /* R83 */
+ 0x0000, /* R84 */
+ 0x005C, /* R85 */
+ 0x0000, /* R86 */
+ 0x0000, /* R87 - Write Sequencer 1 */
+ 0x0000, /* R88 - Write Sequencer 2 */
+ 0x0000, /* R89 - Write Sequencer 3 */
+ 0x0000, /* R90 - Write Sequencer 4 */
+ 0x0000, /* R91 - Write Sequencer 5 */
+ 0x0000, /* R92 - Write Sequencer 6 */
+ 0x0000, /* R93 - Write Sequencer 7 */
+ 0x0000, /* R94 */
+ 0x0000, /* R95 */
+ 0x0000, /* R96 */
+ 0x0000, /* R97 */
+ 0x0000, /* R98 */
+ 0x0000, /* R99 */
+ 0x0000, /* R100 */
+ 0x0000, /* R101 */
+ 0x0000, /* R102 */
+ 0x0000, /* R103 */
+ 0x0000, /* R104 */
+ 0x0000, /* R105 */
+ 0x0000, /* R106 */
+ 0x0000, /* R107 */
+ 0x0000, /* R108 */
+ 0x0000, /* R109 */
+ 0x0000, /* R110 */
+ 0x0000, /* R111 */
+ 0x0000, /* R112 */
+ 0x0000, /* R113 */
+ 0x0000, /* R114 */
+ 0x0000, /* R115 */
+ 0x0000, /* R116 */
+ 0x0000, /* R117 */
+ 0x0000, /* R118 */
+ 0x0000, /* R119 */
+ 0x0000, /* R120 */
+ 0x0000, /* R121 */
+ 0x0000, /* R122 */
+ 0x0000, /* R123 */
+ 0x0000, /* R124 */
+ 0x0000, /* R125 */
+ 0x0000, /* R126 */
+ 0x0000, /* R127 */
+ 0x0000, /* R128 */
+ 0x0000, /* R129 */
+ 0x0000, /* R130 */
+ 0x0000, /* R131 */
+ 0x0000, /* R132 */
+ 0x0000, /* R133 */
+ 0x0000, /* R134 */
+ 0x0000, /* R135 */
+ 0x0000, /* R136 */
+ 0x0000, /* R137 */
+ 0x0000, /* R138 */
+ 0x0000, /* R139 */
+ 0x0000, /* R140 */
+ 0x0000, /* R141 */
+ 0x0000, /* R142 */
+ 0x0000, /* R143 */
+ 0x0000, /* R144 */
+ 0x0000, /* R145 */
+ 0x0000, /* R146 */
+ 0x0000, /* R147 */
+ 0x0000, /* R148 */
+ 0x0000, /* R149 */
+ 0x0000, /* R150 */
+ 0x0000, /* R151 */
+ 0x0000, /* R152 */
+ 0x0000, /* R153 */
+ 0x0000, /* R154 */
+ 0x0000, /* R155 */
+ 0x0000, /* R156 */
+ 0x0000, /* R157 */
+ 0x0000, /* R158 */
+ 0x0000, /* R159 */
+ 0x0000, /* R160 */
+ 0x0000, /* R161 */
+ 0x0000, /* R162 */
+ 0x0000, /* R163 */
+ 0x0000, /* R164 */
+ 0x0000, /* R165 */
+ 0x0000, /* R166 */
+ 0x0000, /* R167 */
+ 0x0000, /* R168 */
+ 0x0000, /* R169 */
+ 0x0000, /* R170 */
+ 0x0000, /* R171 */
+ 0x0000, /* R172 */
+ 0x0000, /* R173 */
+ 0x0000, /* R174 */
+ 0x0000, /* R175 */
+ 0x0000, /* R176 */
+ 0x0000, /* R177 */
+ 0x0000, /* R178 */
+ 0x0000, /* R179 */
+ 0x0000, /* R180 */
+ 0x0000, /* R181 */
+ 0x0000, /* R182 */
+ 0x0000, /* R183 */
+ 0x0000, /* R184 */
+ 0x0000, /* R185 */
+ 0x0000, /* R186 */
+ 0x0000, /* R187 */
+ 0x0000, /* R188 */
+ 0x0000, /* R189 */
+ 0x0000, /* R190 */
+ 0x0000, /* R191 */
+ 0x0000, /* R192 */
+ 0x0000, /* R193 */
+ 0x0000, /* R194 */
+ 0x0000, /* R195 */
+ 0x0030, /* R196 */
+ 0x0006, /* R197 */
+ 0x0000, /* R198 */
+ 0x0060, /* R199 */
+ 0x0000, /* R200 */
+ 0x003F, /* R201 */
+ 0x0000, /* R202 */
+ 0x0000, /* R203 */
+ 0x0000, /* R204 */
+ 0x0001, /* R205 */
+ 0x0000, /* R206 */
+ 0x0181, /* R207 */
+ 0x0005, /* R208 */
+ 0x0008, /* R209 */
+ 0x0008, /* R210 */
+ 0x0000, /* R211 */
+ 0x013B, /* R212 */
+ 0x0000, /* R213 */
+ 0x0000, /* R214 */
+ 0x0000, /* R215 */
+ 0x0000, /* R216 */
+ 0x0070, /* R217 */
+ 0x0000, /* R218 */
+ 0x0000, /* R219 */
+ 0x0000, /* R220 */
+ 0x0000, /* R221 */
+ 0x0000, /* R222 */
+ 0x0003, /* R223 */
+ 0x0000, /* R224 */
+ 0x0000, /* R225 */
+ 0x0001, /* R226 */
+ 0x0008, /* R227 */
+ 0x0000, /* R228 */
+ 0x0000, /* R229 */
+ 0x0000, /* R230 */
+ 0x0000, /* R231 */
+ 0x0004, /* R232 */
+ 0x0000, /* R233 */
+ 0x0000, /* R234 */
+ 0x0000, /* R235 */
+ 0x0000, /* R236 */
+ 0x0000, /* R237 */
+ 0x0080, /* R238 */
+ 0x0000, /* R239 */
+ 0x0000, /* R240 */
+ 0x0000, /* R241 */
+ 0x0000, /* R242 */
+ 0x0000, /* R243 */
+ 0x0000, /* R244 */
+ 0x0052, /* R245 */
+ 0x0110, /* R246 */
+ 0x0040, /* R247 */
+ 0x0000, /* R248 */
+ 0x0030, /* R249 */
+ 0x0000, /* R250 */
+ 0x0000, /* R251 */
+ 0x0001, /* R252 - General test 1 */
+};
+
+struct wm8961_priv {
+ struct snd_soc_codec codec;
+ int sysclk;
+ u16 reg_cache[WM8961_MAX_REGISTER];
+};
+
+static int wm8961_volatile_register(unsigned int reg)
+{
+ switch (reg) {
+ case WM8961_SOFTWARE_RESET:
+ case WM8961_WRITE_SEQUENCER_7:
+ case WM8961_DC_SERVO_1:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static int wm8961_reset(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, WM8961_SOFTWARE_RESET, 0);
+}
+
+/*
+ * The headphone output supports special anti-pop sequences giving
+ * silent power up and power down.
+ */
+static int wm8961_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ u16 hp_reg = snd_soc_read(codec, WM8961_ANALOGUE_HP_0);
+ u16 cp_reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_1);
+ u16 pwr_reg = snd_soc_read(codec, WM8961_PWR_MGMT_2);
+ u16 dcs_reg = snd_soc_read(codec, WM8961_DC_SERVO_1);
+ int timeout = 500;
+
+ if (event & SND_SOC_DAPM_POST_PMU) {
+ /* Make sure the output is shorted */
+ hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Enable the charge pump */
+ cp_reg |= WM8961_CP_ENA;
+ snd_soc_write(codec, WM8961_CHARGE_PUMP_1, cp_reg);
+ mdelay(5);
+
+ /* Enable the PGA */
+ pwr_reg |= WM8961_LOUT1_PGA | WM8961_ROUT1_PGA;
+ snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+
+ /* Enable the amplifier */
+ hp_reg |= WM8961_HPR_ENA | WM8961_HPL_ENA;
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Second stage enable */
+ hp_reg |= WM8961_HPR_ENA_DLY | WM8961_HPL_ENA_DLY;
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Enable the DC servo & trigger startup */
+ dcs_reg |=
+ WM8961_DCS_ENA_CHAN_HPR | WM8961_DCS_TRIG_STARTUP_HPR |
+ WM8961_DCS_ENA_CHAN_HPL | WM8961_DCS_TRIG_STARTUP_HPL;
+ dev_dbg(codec->dev, "Enabling DC servo\n");
+
+ snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg);
+ do {
+ msleep(1);
+ dcs_reg = snd_soc_read(codec, WM8961_DC_SERVO_1);
+ } while (--timeout &&
+ dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
+ WM8961_DCS_TRIG_STARTUP_HPL));
+ if (dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
+ WM8961_DCS_TRIG_STARTUP_HPL))
+ dev_err(codec->dev, "DC servo timed out\n");
+ else
+ dev_dbg(codec->dev, "DC servo startup complete\n");
+
+ /* Enable the output stage */
+ hp_reg |= WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP;
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Remove the short on the output stage */
+ hp_reg |= WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT;
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+ }
+
+ if (event & SND_SOC_DAPM_PRE_PMD) {
+ /* Short the output */
+ hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Disable the output stage */
+ hp_reg &= ~(WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP);
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Disable DC offset cancellation */
+ dcs_reg &= ~(WM8961_DCS_ENA_CHAN_HPR |
+ WM8961_DCS_ENA_CHAN_HPL);
+ snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg);
+
+ /* Finish up */
+ hp_reg &= ~(WM8961_HPR_ENA_DLY | WM8961_HPR_ENA |
+ WM8961_HPL_ENA_DLY | WM8961_HPL_ENA);
+ snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+ /* Disable the PGA */
+ pwr_reg &= ~(WM8961_LOUT1_PGA | WM8961_ROUT1_PGA);
+ snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+
+ /* Disable the charge pump */
+ dev_dbg(codec->dev, "Disabling charge pump\n");
+ snd_soc_write(codec, WM8961_CHARGE_PUMP_1,
+ cp_reg & ~WM8961_CP_ENA);
+ }
+
+ return 0;
+}
+
+static int wm8961_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ u16 pwr_reg = snd_soc_read(codec, WM8961_PWR_MGMT_2);
+ u16 spk_reg = snd_soc_read(codec, WM8961_CLASS_D_CONTROL_1);
+
+ if (event & SND_SOC_DAPM_POST_PMU) {
+ /* Enable the PGA */
+ pwr_reg |= WM8961_SPKL_PGA | WM8961_SPKR_PGA;
+ snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+
+ /* Enable the amplifier */
+ spk_reg |= WM8961_SPKL_ENA | WM8961_SPKR_ENA;
+ snd_soc_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg);
+ }
+
+ if (event & SND_SOC_DAPM_PRE_PMD) {
+ /* Enable the amplifier */
+ spk_reg &= ~(WM8961_SPKL_ENA | WM8961_SPKR_ENA);
+ snd_soc_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg);
+
+ /* Enable the PGA */
+ pwr_reg &= ~(WM8961_SPKL_PGA | WM8961_SPKR_PGA);
+ snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+ }
+
+ return 0;
+}
+
+static const char *adc_hpf_text[] = {
+ "Hi-fi", "Voice 1", "Voice 2", "Voice 3",
+};
+
+static const struct soc_enum adc_hpf =
+ SOC_ENUM_SINGLE(WM8961_ADC_DAC_CONTROL_2, 7, 4, adc_hpf_text);
+
+static const char *dac_deemph_text[] = {
+ "None", "32kHz", "44.1kHz", "48kHz",
+};
+
+static const struct soc_enum dac_deemph =
+ SOC_ENUM_SINGLE(WM8961_ADC_DAC_CONTROL_1, 1, 4, dac_deemph_text);
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(hp_sec_tlv, -700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
+static unsigned int boost_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(13, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(20, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(29, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -2325, 75, 0);
+
+static const struct snd_kcontrol_new wm8961_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Headphone Volume", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME,
+ 0, 127, 0, out_tlv),
+SOC_DOUBLE_TLV("Headphone Secondary Volume", WM8961_ANALOGUE_HP_2,
+ 6, 3, 7, 0, hp_sec_tlv),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME,
+ 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Speaker Volume", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME,
+ 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Speaker ZC Switch", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME,
+ 7, 1, 0),
+SOC_SINGLE("Speaker AC Gain", WM8961_CLASS_D_CONTROL_2, 0, 7, 0),
+
+SOC_SINGLE("DAC x128 OSR Switch", WM8961_ADC_DAC_CONTROL_2, 0, 1, 0),
+SOC_ENUM("DAC Deemphasis", dac_deemph),
+SOC_SINGLE("DAC Soft Mute Switch", WM8961_ADC_DAC_CONTROL_2, 3, 1, 0),
+
+SOC_DOUBLE_R_TLV("Sidetone Volume", WM8961_DSP_SIDETONE_0,
+ WM8961_DSP_SIDETONE_1, 4, 12, 0, sidetone_tlv),
+
+SOC_SINGLE("ADC High Pass Filter Switch", WM8961_ADC_DAC_CONTROL_1, 0, 1, 0),
+SOC_ENUM("ADC High Pass Filter Mode", adc_hpf),
+
+SOC_DOUBLE_R_TLV("Capture Volume",
+ WM8961_LEFT_ADC_VOLUME, WM8961_RIGHT_ADC_VOLUME,
+ 1, 119, 0, adc_tlv),
+SOC_DOUBLE_R_TLV("Capture Boost Volume",
+ WM8961_ADCL_SIGNAL_PATH, WM8961_ADCR_SIGNAL_PATH,
+ 4, 3, 0, boost_tlv),
+SOC_DOUBLE_R_TLV("Capture PGA Volume",
+ WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
+ 0, 62, 0, pga_tlv),
+SOC_DOUBLE_R("Capture PGA ZC Switch",
+ WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
+ 6, 1, 1),
+SOC_DOUBLE_R("Capture PGA Switch",
+ WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
+ 7, 1, 1),
+};
+
+static const char *sidetone_text[] = {
+ "None", "Left", "Right"
+};
+
+static const struct soc_enum dacl_sidetone =
+ SOC_ENUM_SINGLE(WM8961_DSP_SIDETONE_0, 2, 3, sidetone_text);
+
+static const struct soc_enum dacr_sidetone =
+ SOC_ENUM_SINGLE(WM8961_DSP_SIDETONE_1, 2, 3, sidetone_text);
+
+static const struct snd_kcontrol_new dacl_mux =
+ SOC_DAPM_ENUM("DACL Sidetone", dacl_sidetone);
+
+static const struct snd_kcontrol_new dacr_mux =
+ SOC_DAPM_ENUM("DACR Sidetone", dacr_sidetone);
+
+static const struct snd_soc_dapm_widget wm8961_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("LINPUT"),
+SND_SOC_DAPM_INPUT("RINPUT"),
+
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8961_CLOCKING2, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Left Input", WM8961_PWR_MGMT_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Input", WM8961_PWR_MGMT_1, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", WM8961_PWR_MGMT_1, 3, 0),
+SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", WM8961_PWR_MGMT_1, 2, 0),
+
+SND_SOC_DAPM_MICBIAS("MICBIAS", WM8961_PWR_MGMT_1, 1, 0),
+
+SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &dacl_mux),
+SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &dacr_mux),
+
+SND_SOC_DAPM_DAC("DACL", "HiFi Playback", WM8961_PWR_MGMT_2, 8, 0),
+SND_SOC_DAPM_DAC("DACR", "HiFi Playback", WM8961_PWR_MGMT_2, 7, 0),
+
+/* Handle as a mono path for DCS */
+SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM,
+ 4, 0, NULL, 0, wm8961_hp_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("Speaker Output", SND_SOC_NOPM,
+ 4, 0, NULL, 0, wm8961_spk_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("HP_L"),
+SND_SOC_DAPM_OUTPUT("HP_R"),
+SND_SOC_DAPM_OUTPUT("SPK_LN"),
+SND_SOC_DAPM_OUTPUT("SPK_LP"),
+SND_SOC_DAPM_OUTPUT("SPK_RN"),
+SND_SOC_DAPM_OUTPUT("SPK_RP"),
+};
+
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+ { "DACL", NULL, "CLK_DSP" },
+ { "DACL", NULL, "DACL Sidetone" },
+ { "DACR", NULL, "CLK_DSP" },
+ { "DACR", NULL, "DACR Sidetone" },
+
+ { "DACL Sidetone", "Left", "ADCL" },
+ { "DACL Sidetone", "Right", "ADCR" },
+
+ { "DACR Sidetone", "Left", "ADCL" },
+ { "DACR Sidetone", "Right", "ADCR" },
+
+ { "HP_L", NULL, "Headphone Output" },
+ { "HP_R", NULL, "Headphone Output" },
+ { "Headphone Output", NULL, "DACL" },
+ { "Headphone Output", NULL, "DACR" },
+
+ { "SPK_LN", NULL, "Speaker Output" },
+ { "SPK_LP", NULL, "Speaker Output" },
+ { "SPK_RN", NULL, "Speaker Output" },
+ { "SPK_RP", NULL, "Speaker Output" },
+
+ { "Speaker Output", NULL, "DACL" },
+ { "Speaker Output", NULL, "DACR" },
+
+ { "ADCL", NULL, "Left Input" },
+ { "ADCL", NULL, "CLK_DSP" },
+ { "ADCR", NULL, "Right Input" },
+ { "ADCR", NULL, "CLK_DSP" },
+
+ { "Left Input", NULL, "LINPUT" },
+ { "Right Input", NULL, "RINPUT" },
+
+};
+
+/* Values for CLK_SYS_RATE */
+static struct {
+ int ratio;
+ u16 val;
+} wm8961_clk_sys_ratio[] = {
+ { 64, 0 },
+ { 128, 1 },
+ { 192, 2 },
+ { 256, 3 },
+ { 384, 4 },
+ { 512, 5 },
+ { 768, 6 },
+ { 1024, 7 },
+ { 1408, 8 },
+ { 1536, 9 },
+};
+
+/* Values for SAMPLE_RATE */
+static struct {
+ int rate;
+ u16 val;
+} wm8961_srate[] = {
+ { 48000, 0 },
+ { 44100, 0 },
+ { 32000, 1 },
+ { 22050, 2 },
+ { 24000, 2 },
+ { 16000, 3 },
+ { 11250, 4 },
+ { 12000, 4 },
+ { 8000, 5 },
+};
+
+static int wm8961_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 wm8961_priv *wm8961 = codec->private_data;
+ int i, best, target, fs;
+ u16 reg;
+
+ fs = params_rate(params);
+
+ if (!wm8961->sysclk) {
+ dev_err(codec->dev, "MCLK has not been specified\n");
+ return -EINVAL;
+ }
+
+ /* Find the closest sample rate for the filters */
+ best = 0;
+ for (i = 0; i < ARRAY_SIZE(wm8961_srate); i++) {
+ if (abs(wm8961_srate[i].rate - fs) <
+ abs(wm8961_srate[best].rate - fs))
+ best = i;
+ }
+ reg = snd_soc_read(codec, WM8961_ADDITIONAL_CONTROL_3);
+ reg &= ~WM8961_SAMPLE_RATE_MASK;
+ reg |= wm8961_srate[best].val;
+ snd_soc_write(codec, WM8961_ADDITIONAL_CONTROL_3, reg);
+ dev_dbg(codec->dev, "Selected SRATE %dHz for %dHz\n",
+ wm8961_srate[best].rate, fs);
+
+ /* Select a CLK_SYS/fs ratio equal to or higher than required */
+ target = wm8961->sysclk / fs;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && target < 64) {
+ dev_err(codec->dev,
+ "SYSCLK must be at least 64*fs for DAC\n");
+ return -EINVAL;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && target < 256) {
+ dev_err(codec->dev,
+ "SYSCLK must be at least 256*fs for ADC\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8961_clk_sys_ratio); i++) {
+ if (wm8961_clk_sys_ratio[i].ratio >= target)
+ break;
+ }
+ if (i == ARRAY_SIZE(wm8961_clk_sys_ratio)) {
+ dev_err(codec->dev, "Unable to generate CLK_SYS_RATE\n");
+ return -EINVAL;
+ }
+ dev_dbg(codec->dev, "Selected CLK_SYS_RATE of %d for %d/%d=%d\n",
+ wm8961_clk_sys_ratio[i].ratio, wm8961->sysclk, fs,
+ wm8961->sysclk / fs);
+
+ reg = snd_soc_read(codec, WM8961_CLOCKING_4);
+ reg &= ~WM8961_CLK_SYS_RATE_MASK;
+ reg |= wm8961_clk_sys_ratio[i].val << WM8961_CLK_SYS_RATE_SHIFT;
+ snd_soc_write(codec, WM8961_CLOCKING_4, reg);
+
+ reg = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_0);
+ reg &= ~WM8961_WL_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ reg |= 1 << WM8961_WL_SHIFT;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ reg |= 2 << WM8961_WL_SHIFT;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ reg |= 3 << WM8961_WL_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, reg);
+
+ /* Sloping stop-band filter is recommended for <= 24kHz */
+ reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2);
+ if (fs <= 24000)
+ reg |= WM8961_DACSLOPE;
+ else
+ reg &= WM8961_DACSLOPE;
+ snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_2, reg);
+
+ return 0;
+}
+
+static int wm8961_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq,
+ int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8961_priv *wm8961 = codec->private_data;
+ u16 reg = snd_soc_read(codec, WM8961_CLOCKING1);
+
+ if (freq > 33000000) {
+ dev_err(codec->dev, "MCLK must be <33MHz\n");
+ return -EINVAL;
+ }
+
+ if (freq > 16500000) {
+ dev_dbg(codec->dev, "Using MCLK/2 for %dHz MCLK\n", freq);
+ reg |= WM8961_MCLKDIV;
+ freq /= 2;
+ } else {
+ dev_dbg(codec->dev, "Using MCLK/1 for %dHz MCLK\n", freq);
+ reg &= WM8961_MCLKDIV;
+ }
+
+ snd_soc_write(codec, WM8961_CLOCKING1, reg);
+
+ wm8961->sysclk = freq;
+
+ return 0;
+}
+
+static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 aif = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_0);
+
+ aif &= ~(WM8961_BCLKINV | WM8961_LRP |
+ WM8961_MS | WM8961_FORMAT_MASK);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif |= WM8961_MS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif |= 1;
+ break;
+
+ case SND_SOC_DAIFMT_I2S:
+ aif |= 2;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ aif |= WM8961_LRP;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif |= 3;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif |= WM8961_LRP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif |= WM8961_BCLKINV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif |= WM8961_BCLKINV | WM8961_LRP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, aif);
+}
+
+static int wm8961_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 reg = snd_soc_read(codec, WM8961_ADDITIONAL_CONTROL_2);
+
+ if (tristate)
+ reg |= WM8961_TRIS;
+ else
+ reg &= ~WM8961_TRIS;
+
+ return snd_soc_write(codec, WM8961_ADDITIONAL_CONTROL_2, reg);
+}
+
+static int wm8961_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_1);
+
+ if (mute)
+ reg |= WM8961_DACMU;
+ else
+ reg &= ~WM8961_DACMU;
+
+ msleep(17);
+
+ return snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_1, reg);
+}
+
+static int wm8961_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8961_BCLK:
+ reg = snd_soc_read(codec, WM8961_CLOCKING2);
+ reg &= ~WM8961_BCLKDIV_MASK;
+ reg |= div;
+ snd_soc_write(codec, WM8961_CLOCKING2, reg);
+ break;
+
+ case WM8961_LRCLK:
+ reg = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_2);
+ reg &= ~WM8961_LRCLK_RATE_MASK;
+ reg |= div;
+ snd_soc_write(codec, WM8961_AUDIO_INTERFACE_2, reg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8961_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg;
+
+ /* This is all slightly unusual since we have no bypass paths
+ * and the output amplifier structure means we can just slam
+ * the biases straight up rather than having to ramp them
+ * slowly.
+ */
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ if (codec->bias_level == SND_SOC_BIAS_STANDBY) {
+ /* Enable bias generation */
+ reg = snd_soc_read(codec, WM8961_ANTI_POP);
+ reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN;
+ snd_soc_write(codec, WM8961_ANTI_POP, reg);
+
+ /* VMID=2*50k, VREF */
+ reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
+ reg &= ~WM8961_VMIDSEL_MASK;
+ reg |= (1 << WM8961_VMIDSEL_SHIFT) | WM8961_VREF;
+ snd_soc_write(codec, WM8961_PWR_MGMT_1, reg);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_PREPARE) {
+ /* VREF off */
+ reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
+ reg &= ~WM8961_VREF;
+ snd_soc_write(codec, WM8961_PWR_MGMT_1, reg);
+
+ /* Bias generation off */
+ reg = snd_soc_read(codec, WM8961_ANTI_POP);
+ reg &= ~(WM8961_BUFIOEN | WM8961_BUFDCOPEN);
+ snd_soc_write(codec, WM8961_ANTI_POP, reg);
+
+ /* VMID off */
+ reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
+ reg &= ~WM8961_VMIDSEL_MASK;
+ snd_soc_write(codec, WM8961_PWR_MGMT_1, reg);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+
+ codec->bias_level = level;
+
+ return 0;
+}
+
+
+#define WM8961_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8961_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8961_dai_ops = {
+ .hw_params = wm8961_hw_params,
+ .set_sysclk = wm8961_set_sysclk,
+ .set_fmt = wm8961_set_fmt,
+ .digital_mute = wm8961_digital_mute,
+ .set_tristate = wm8961_set_tristate,
+ .set_clkdiv = wm8961_set_clkdiv,
+};
+
+struct snd_soc_dai wm8961_dai = {
+ .name = "WM8961",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8961_RATES,
+ .formats = WM8961_FORMATS,},
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8961_RATES,
+ .formats = WM8961_FORMATS,},
+ .ops = &wm8961_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm8961_dai);
+
+
+static struct snd_soc_codec *wm8961_codec;
+
+static int wm8961_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (wm8961_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = wm8961_codec;
+ codec = wm8961_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, wm8961_snd_controls,
+ ARRAY_SIZE(wm8961_snd_controls));
+ snd_soc_dapm_new_controls(codec, wm8961_dapm_widgets,
+ ARRAY_SIZE(wm8961_dapm_widgets));
+ snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+ snd_soc_dapm_new_widgets(codec);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to register card: %d\n", ret);
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ return ret;
+}
+
+static int wm8961_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8961_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int wm8961_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+ u16 *reg_cache = codec->reg_cache;
+ int i;
+
+ for (i = 0; i < codec->reg_cache_size; i++) {
+ if (i == WM8961_SOFTWARE_RESET)
+ continue;
+
+ snd_soc_write(codec, i, reg_cache[i]);
+ }
+
+ wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define wm8961_suspend NULL
+#define wm8961_resume NULL
+#endif
+
+struct snd_soc_codec_device soc_codec_dev_wm8961 = {
+ .probe = wm8961_probe,
+ .remove = wm8961_remove,
+ .suspend = wm8961_suspend,
+ .resume = wm8961_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8961);
+
+static int wm8961_register(struct wm8961_priv *wm8961)
+{
+ struct snd_soc_codec *codec = &wm8961->codec;
+ int ret;
+ u16 reg;
+
+ if (wm8961_codec) {
+ dev_err(codec->dev, "Another WM8961 is registered\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->private_data = wm8961;
+ codec->name = "WM8961";
+ codec->owner = THIS_MODULE;
+ codec->dai = &wm8961_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ARRAY_SIZE(wm8961->reg_cache);
+ codec->reg_cache = &wm8961->reg_cache;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8961_set_bias_level;
+ codec->volatile_register = wm8961_volatile_register;
+
+ memcpy(codec->reg_cache, wm8961_reg_defaults,
+ sizeof(wm8961_reg_defaults));
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ reg = snd_soc_read(codec, WM8961_SOFTWARE_RESET);
+ if (reg != 0x1801) {
+ dev_err(codec->dev, "Device is not a WM8961: ID=0x%x\n", reg);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* This isn't volatile - readback doesn't correspond to write */
+ reg = codec->hw_read(codec, WM8961_RIGHT_INPUT_VOLUME);
+ dev_info(codec->dev, "WM8961 family %d revision %c\n",
+ (reg & WM8961_DEVICE_ID_MASK) >> WM8961_DEVICE_ID_SHIFT,
+ ((reg & WM8961_CHIP_REV_MASK) >> WM8961_CHIP_REV_SHIFT)
+ + 'A');
+
+ ret = wm8961_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ return ret;
+ }
+
+ /* Enable class W */
+ reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_B);
+ reg |= WM8961_CP_DYN_PWR_MASK;
+ snd_soc_write(codec, WM8961_CHARGE_PUMP_B, reg);
+
+ /* Latch volume update bits (right channel only, we always
+ * write both out) and default ZC on. */
+ reg = snd_soc_read(codec, WM8961_ROUT1_VOLUME);
+ snd_soc_write(codec, WM8961_ROUT1_VOLUME,
+ reg | WM8961_LO1ZC | WM8961_OUT1VU);
+ snd_soc_write(codec, WM8961_LOUT1_VOLUME, reg | WM8961_LO1ZC);
+ reg = snd_soc_read(codec, WM8961_ROUT2_VOLUME);
+ snd_soc_write(codec, WM8961_ROUT2_VOLUME,
+ reg | WM8961_SPKRZC | WM8961_SPKVU);
+ snd_soc_write(codec, WM8961_LOUT2_VOLUME, reg | WM8961_SPKLZC);
+
+ reg = snd_soc_read(codec, WM8961_RIGHT_ADC_VOLUME);
+ snd_soc_write(codec, WM8961_RIGHT_ADC_VOLUME, reg | WM8961_ADCVU);
+ reg = snd_soc_read(codec, WM8961_RIGHT_INPUT_VOLUME);
+ snd_soc_write(codec, WM8961_RIGHT_INPUT_VOLUME, reg | WM8961_IPVU);
+
+ /* Use soft mute by default */
+ reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2);
+ reg |= WM8961_DACSMM;
+ snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_2, reg);
+
+ /* Use automatic clocking mode by default; for now this is all
+ * we support.
+ */
+ reg = snd_soc_read(codec, WM8961_CLOCKING_3);
+ reg &= ~WM8961_MANUAL_MODE;
+ snd_soc_write(codec, WM8961_CLOCKING_3, reg);
+
+ wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ wm8961_dai.dev = codec->dev;
+
+ wm8961_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_dai(&wm8961_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ snd_soc_unregister_codec(codec);
+ return ret;
+ }
+
+ return 0;
+
+err:
+ kfree(wm8961);
+ return ret;
+}
+
+static void wm8961_unregister(struct wm8961_priv *wm8961)
+{
+ wm8961_set_bias_level(&wm8961->codec, SND_SOC_BIAS_OFF);
+ snd_soc_unregister_dai(&wm8961_dai);
+ snd_soc_unregister_codec(&wm8961->codec);
+ kfree(wm8961);
+ wm8961_codec = NULL;
+}
+
+static __devinit int wm8961_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8961_priv *wm8961;
+ struct snd_soc_codec *codec;
+
+ wm8961 = kzalloc(sizeof(struct wm8961_priv), GFP_KERNEL);
+ if (wm8961 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8961->codec;
+
+ i2c_set_clientdata(i2c, wm8961);
+ codec->control_data = i2c;
+
+ codec->dev = &i2c->dev;
+
+ return wm8961_register(wm8961);
+}
+
+static __devexit int wm8961_i2c_remove(struct i2c_client *client)
+{
+ struct wm8961_priv *wm8961 = i2c_get_clientdata(client);
+ wm8961_unregister(wm8961);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8961_i2c_suspend(struct i2c_client *client, pm_message_t state)
+{
+ return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8961_i2c_resume(struct i2c_client *client)
+{
+ return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8961_i2c_suspend NULL
+#define wm8961_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm8961_i2c_id[] = {
+ { "wm8961", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id);
+
+static struct i2c_driver wm8961_i2c_driver = {
+ .driver = {
+ .name = "wm8961",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8961_i2c_probe,
+ .remove = __devexit_p(wm8961_i2c_remove),
+ .suspend = wm8961_i2c_suspend,
+ .resume = wm8961_i2c_resume,
+ .id_table = wm8961_i2c_id,
+};
+
+static int __init wm8961_modinit(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&wm8961_i2c_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Failed to register WM8961 I2C driver: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+module_init(wm8961_modinit);
+
+static void __exit wm8961_exit(void)
+{
+ i2c_del_driver(&wm8961_i2c_driver);
+}
+module_exit(wm8961_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM8961 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8961.h b/sound/soc/codecs/wm8961.h
new file mode 100644
index 00000000000..5513bfd720d
--- /dev/null
+++ b/sound/soc/codecs/wm8961.h
@@ -0,0 +1,866 @@
+/*
+ * wm8961.h -- WM8961 Soc Audio driver
+ *
+ * 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 _WM8961_H
+#define _WM8961_H
+
+#include <sound/soc.h>
+
+extern struct snd_soc_codec_device soc_codec_dev_wm8961;
+extern struct snd_soc_dai wm8961_dai;
+
+#define WM8961_BCLK 1
+#define WM8961_LRCLK 2
+
+#define WM8961_BCLK_DIV_1 0
+#define WM8961_BCLK_DIV_1_5 1
+#define WM8961_BCLK_DIV_2 2
+#define WM8961_BCLK_DIV_3 3
+#define WM8961_BCLK_DIV_4 4
+#define WM8961_BCLK_DIV_5_5 5
+#define WM8961_BCLK_DIV_6 6
+#define WM8961_BCLK_DIV_8 7
+#define WM8961_BCLK_DIV_11 8
+#define WM8961_BCLK_DIV_12 9
+#define WM8961_BCLK_DIV_16 10
+#define WM8961_BCLK_DIV_24 11
+#define WM8961_BCLK_DIV_32 13
+
+
+/*
+ * Register values.
+ */
+#define WM8961_LEFT_INPUT_VOLUME 0x00
+#define WM8961_RIGHT_INPUT_VOLUME 0x01
+#define WM8961_LOUT1_VOLUME 0x02
+#define WM8961_ROUT1_VOLUME 0x03
+#define WM8961_CLOCKING1 0x04
+#define WM8961_ADC_DAC_CONTROL_1 0x05
+#define WM8961_ADC_DAC_CONTROL_2 0x06
+#define WM8961_AUDIO_INTERFACE_0 0x07
+#define WM8961_CLOCKING2 0x08
+#define WM8961_AUDIO_INTERFACE_1 0x09
+#define WM8961_LEFT_DAC_VOLUME 0x0A
+#define WM8961_RIGHT_DAC_VOLUME 0x0B
+#define WM8961_AUDIO_INTERFACE_2 0x0E
+#define WM8961_SOFTWARE_RESET 0x0F
+#define WM8961_ALC1 0x11
+#define WM8961_ALC2 0x12
+#define WM8961_ALC3 0x13
+#define WM8961_NOISE_GATE 0x14
+#define WM8961_LEFT_ADC_VOLUME 0x15
+#define WM8961_RIGHT_ADC_VOLUME 0x16
+#define WM8961_ADDITIONAL_CONTROL_1 0x17
+#define WM8961_ADDITIONAL_CONTROL_2 0x18
+#define WM8961_PWR_MGMT_1 0x19
+#define WM8961_PWR_MGMT_2 0x1A
+#define WM8961_ADDITIONAL_CONTROL_3 0x1B
+#define WM8961_ANTI_POP 0x1C
+#define WM8961_CLOCKING_3 0x1E
+#define WM8961_ADCL_SIGNAL_PATH 0x20
+#define WM8961_ADCR_SIGNAL_PATH 0x21
+#define WM8961_LOUT2_VOLUME 0x28
+#define WM8961_ROUT2_VOLUME 0x29
+#define WM8961_PWR_MGMT_3 0x2F
+#define WM8961_ADDITIONAL_CONTROL_4 0x30
+#define WM8961_CLASS_D_CONTROL_1 0x31
+#define WM8961_CLASS_D_CONTROL_2 0x33
+#define WM8961_CLOCKING_4 0x38
+#define WM8961_DSP_SIDETONE_0 0x39
+#define WM8961_DSP_SIDETONE_1 0x3A
+#define WM8961_DC_SERVO_0 0x3C
+#define WM8961_DC_SERVO_1 0x3D
+#define WM8961_DC_SERVO_3 0x3F
+#define WM8961_DC_SERVO_5 0x41
+#define WM8961_ANALOGUE_PGA_BIAS 0x44
+#define WM8961_ANALOGUE_HP_0 0x45
+#define WM8961_ANALOGUE_HP_2 0x47
+#define WM8961_CHARGE_PUMP_1 0x48
+#define WM8961_CHARGE_PUMP_B 0x52
+#define WM8961_WRITE_SEQUENCER_1 0x57
+#define WM8961_WRITE_SEQUENCER_2 0x58
+#define WM8961_WRITE_SEQUENCER_3 0x59
+#define WM8961_WRITE_SEQUENCER_4 0x5A
+#define WM8961_WRITE_SEQUENCER_5 0x5B
+#define WM8961_WRITE_SEQUENCER_6 0x5C
+#define WM8961_WRITE_SEQUENCER_7 0x5D
+#define WM8961_GENERAL_TEST_1 0xFC
+
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Left Input volume
+ */
+#define WM8961_IPVU 0x0100 /* IPVU */
+#define WM8961_IPVU_MASK 0x0100 /* IPVU */
+#define WM8961_IPVU_SHIFT 8 /* IPVU */
+#define WM8961_IPVU_WIDTH 1 /* IPVU */
+#define WM8961_LINMUTE 0x0080 /* LINMUTE */
+#define WM8961_LINMUTE_MASK 0x0080 /* LINMUTE */
+#define WM8961_LINMUTE_SHIFT 7 /* LINMUTE */
+#define WM8961_LINMUTE_WIDTH 1 /* LINMUTE */
+#define WM8961_LIZC 0x0040 /* LIZC */
+#define WM8961_LIZC_MASK 0x0040 /* LIZC */
+#define WM8961_LIZC_SHIFT 6 /* LIZC */
+#define WM8961_LIZC_WIDTH 1 /* LIZC */
+#define WM8961_LINVOL_MASK 0x003F /* LINVOL - [5:0] */
+#define WM8961_LINVOL_SHIFT 0 /* LINVOL - [5:0] */
+#define WM8961_LINVOL_WIDTH 6 /* LINVOL - [5:0] */
+
+/*
+ * R1 (0x01) - Right Input volume
+ */
+#define WM8961_DEVICE_ID_MASK 0xF000 /* DEVICE_ID - [15:12] */
+#define WM8961_DEVICE_ID_SHIFT 12 /* DEVICE_ID - [15:12] */
+#define WM8961_DEVICE_ID_WIDTH 4 /* DEVICE_ID - [15:12] */
+#define WM8961_CHIP_REV_MASK 0x0E00 /* CHIP_REV - [11:9] */
+#define WM8961_CHIP_REV_SHIFT 9 /* CHIP_REV - [11:9] */
+#define WM8961_CHIP_REV_WIDTH 3 /* CHIP_REV - [11:9] */
+#define WM8961_IPVU 0x0100 /* IPVU */
+#define WM8961_IPVU_MASK 0x0100 /* IPVU */
+#define WM8961_IPVU_SHIFT 8 /* IPVU */
+#define WM8961_IPVU_WIDTH 1 /* IPVU */
+#define WM8961_RINMUTE 0x0080 /* RINMUTE */
+#define WM8961_RINMUTE_MASK 0x0080 /* RINMUTE */
+#define WM8961_RINMUTE_SHIFT 7 /* RINMUTE */
+#define WM8961_RINMUTE_WIDTH 1 /* RINMUTE */
+#define WM8961_RIZC 0x0040 /* RIZC */
+#define WM8961_RIZC_MASK 0x0040 /* RIZC */
+#define WM8961_RIZC_SHIFT 6 /* RIZC */
+#define WM8961_RIZC_WIDTH 1 /* RIZC */
+#define WM8961_RINVOL_MASK 0x003F /* RINVOL - [5:0] */
+#define WM8961_RINVOL_SHIFT 0 /* RINVOL - [5:0] */
+#define WM8961_RINVOL_WIDTH 6 /* RINVOL - [5:0] */
+
+/*
+ * R2 (0x02) - LOUT1 volume
+ */
+#define WM8961_OUT1VU 0x0100 /* OUT1VU */
+#define WM8961_OUT1VU_MASK 0x0100 /* OUT1VU */
+#define WM8961_OUT1VU_SHIFT 8 /* OUT1VU */
+#define WM8961_OUT1VU_WIDTH 1 /* OUT1VU */
+#define WM8961_LO1ZC 0x0080 /* LO1ZC */
+#define WM8961_LO1ZC_MASK 0x0080 /* LO1ZC */
+#define WM8961_LO1ZC_SHIFT 7 /* LO1ZC */
+#define WM8961_LO1ZC_WIDTH 1 /* LO1ZC */
+#define WM8961_LOUT1VOL_MASK 0x007F /* LOUT1VOL - [6:0] */
+#define WM8961_LOUT1VOL_SHIFT 0 /* LOUT1VOL - [6:0] */
+#define WM8961_LOUT1VOL_WIDTH 7 /* LOUT1VOL - [6:0] */
+
+/*
+ * R3 (0x03) - ROUT1 volume
+ */
+#define WM8961_OUT1VU 0x0100 /* OUT1VU */
+#define WM8961_OUT1VU_MASK 0x0100 /* OUT1VU */
+#define WM8961_OUT1VU_SHIFT 8 /* OUT1VU */
+#define WM8961_OUT1VU_WIDTH 1 /* OUT1VU */
+#define WM8961_RO1ZC 0x0080 /* RO1ZC */
+#define WM8961_RO1ZC_MASK 0x0080 /* RO1ZC */
+#define WM8961_RO1ZC_SHIFT 7 /* RO1ZC */
+#define WM8961_RO1ZC_WIDTH 1 /* RO1ZC */
+#define WM8961_ROUT1VOL_MASK 0x007F /* ROUT1VOL - [6:0] */
+#define WM8961_ROUT1VOL_SHIFT 0 /* ROUT1VOL - [6:0] */
+#define WM8961_ROUT1VOL_WIDTH 7 /* ROUT1VOL - [6:0] */
+
+/*
+ * R4 (0x04) - Clocking1
+ */
+#define WM8961_ADCDIV_MASK 0x01C0 /* ADCDIV - [8:6] */
+#define WM8961_ADCDIV_SHIFT 6 /* ADCDIV - [8:6] */
+#define WM8961_ADCDIV_WIDTH 3 /* ADCDIV - [8:6] */
+#define WM8961_DACDIV_MASK 0x0038 /* DACDIV - [5:3] */
+#define WM8961_DACDIV_SHIFT 3 /* DACDIV - [5:3] */
+#define WM8961_DACDIV_WIDTH 3 /* DACDIV - [5:3] */
+#define WM8961_MCLKDIV 0x0004 /* MCLKDIV */
+#define WM8961_MCLKDIV_MASK 0x0004 /* MCLKDIV */
+#define WM8961_MCLKDIV_SHIFT 2 /* MCLKDIV */
+#define WM8961_MCLKDIV_WIDTH 1 /* MCLKDIV */
+
+/*
+ * R5 (0x05) - ADC & DAC Control 1
+ */
+#define WM8961_ADCPOL_MASK 0x0060 /* ADCPOL - [6:5] */
+#define WM8961_ADCPOL_SHIFT 5 /* ADCPOL - [6:5] */
+#define WM8961_ADCPOL_WIDTH 2 /* ADCPOL - [6:5] */
+#define WM8961_DACMU 0x0008 /* DACMU */
+#define WM8961_DACMU_MASK 0x0008 /* DACMU */
+#define WM8961_DACMU_SHIFT 3 /* DACMU */
+#define WM8961_DACMU_WIDTH 1 /* DACMU */
+#define WM8961_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */
+#define WM8961_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */
+#define WM8961_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */
+#define WM8961_ADCHPD 0x0001 /* ADCHPD */
+#define WM8961_ADCHPD_MASK 0x0001 /* ADCHPD */
+#define WM8961_ADCHPD_SHIFT 0 /* ADCHPD */
+#define WM8961_ADCHPD_WIDTH 1 /* ADCHPD */
+
+/*
+ * R6 (0x06) - ADC & DAC Control 2
+ */
+#define WM8961_ADC_HPF_CUT_MASK 0x0180 /* ADC_HPF_CUT - [8:7] */
+#define WM8961_ADC_HPF_CUT_SHIFT 7 /* ADC_HPF_CUT - [8:7] */
+#define WM8961_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [8:7] */
+#define WM8961_DACPOL_MASK 0x0060 /* DACPOL - [6:5] */
+#define WM8961_DACPOL_SHIFT 5 /* DACPOL - [6:5] */
+#define WM8961_DACPOL_WIDTH 2 /* DACPOL - [6:5] */
+#define WM8961_DACSMM 0x0008 /* DACSMM */
+#define WM8961_DACSMM_MASK 0x0008 /* DACSMM */
+#define WM8961_DACSMM_SHIFT 3 /* DACSMM */
+#define WM8961_DACSMM_WIDTH 1 /* DACSMM */
+#define WM8961_DACMR 0x0004 /* DACMR */
+#define WM8961_DACMR_MASK 0x0004 /* DACMR */
+#define WM8961_DACMR_SHIFT 2 /* DACMR */
+#define WM8961_DACMR_WIDTH 1 /* DACMR */
+#define WM8961_DACSLOPE 0x0002 /* DACSLOPE */
+#define WM8961_DACSLOPE_MASK 0x0002 /* DACSLOPE */
+#define WM8961_DACSLOPE_SHIFT 1 /* DACSLOPE */
+#define WM8961_DACSLOPE_WIDTH 1 /* DACSLOPE */
+#define WM8961_DAC_OSR128 0x0001 /* DAC_OSR128 */
+#define WM8961_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */
+#define WM8961_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */
+#define WM8961_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */
+
+/*
+ * R7 (0x07) - Audio Interface 0
+ */
+#define WM8961_ALRSWAP 0x0100 /* ALRSWAP */
+#define WM8961_ALRSWAP_MASK 0x0100 /* ALRSWAP */
+#define WM8961_ALRSWAP_SHIFT 8 /* ALRSWAP */
+#define WM8961_ALRSWAP_WIDTH 1 /* ALRSWAP */
+#define WM8961_BCLKINV 0x0080 /* BCLKINV */
+#define WM8961_BCLKINV_MASK 0x0080 /* BCLKINV */
+#define WM8961_BCLKINV_SHIFT 7 /* BCLKINV */
+#define WM8961_BCLKINV_WIDTH 1 /* BCLKINV */
+#define WM8961_MS 0x0040 /* MS */
+#define WM8961_MS_MASK 0x0040 /* MS */
+#define WM8961_MS_SHIFT 6 /* MS */
+#define WM8961_MS_WIDTH 1 /* MS */
+#define WM8961_DLRSWAP 0x0020 /* DLRSWAP */
+#define WM8961_DLRSWAP_MASK 0x0020 /* DLRSWAP */
+#define WM8961_DLRSWAP_SHIFT 5 /* DLRSWAP */
+#define WM8961_DLRSWAP_WIDTH 1 /* DLRSWAP */
+#define WM8961_LRP 0x0010 /* LRP */
+#define WM8961_LRP_MASK 0x0010 /* LRP */
+#define WM8961_LRP_SHIFT 4 /* LRP */
+#define WM8961_LRP_WIDTH 1 /* LRP */
+#define WM8961_WL_MASK 0x000C /* WL - [3:2] */
+#define WM8961_WL_SHIFT 2 /* WL - [3:2] */
+#define WM8961_WL_WIDTH 2 /* WL - [3:2] */
+#define WM8961_FORMAT_MASK 0x0003 /* FORMAT - [1:0] */
+#define WM8961_FORMAT_SHIFT 0 /* FORMAT - [1:0] */
+#define WM8961_FORMAT_WIDTH 2 /* FORMAT - [1:0] */
+
+/*
+ * R8 (0x08) - Clocking2
+ */
+#define WM8961_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */
+#define WM8961_DCLKDIV_SHIFT 6 /* DCLKDIV - [8:6] */
+#define WM8961_DCLKDIV_WIDTH 3 /* DCLKDIV - [8:6] */
+#define WM8961_CLK_SYS_ENA 0x0020 /* CLK_SYS_ENA */
+#define WM8961_CLK_SYS_ENA_MASK 0x0020 /* CLK_SYS_ENA */
+#define WM8961_CLK_SYS_ENA_SHIFT 5 /* CLK_SYS_ENA */
+#define WM8961_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */
+#define WM8961_CLK_DSP_ENA 0x0010 /* CLK_DSP_ENA */
+#define WM8961_CLK_DSP_ENA_MASK 0x0010 /* CLK_DSP_ENA */
+#define WM8961_CLK_DSP_ENA_SHIFT 4 /* CLK_DSP_ENA */
+#define WM8961_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */
+#define WM8961_BCLKDIV_MASK 0x000F /* BCLKDIV - [3:0] */
+#define WM8961_BCLKDIV_SHIFT 0 /* BCLKDIV - [3:0] */
+#define WM8961_BCLKDIV_WIDTH 4 /* BCLKDIV - [3:0] */
+
+/*
+ * R9 (0x09) - Audio Interface 1
+ */
+#define WM8961_DACCOMP_MASK 0x0018 /* DACCOMP - [4:3] */
+#define WM8961_DACCOMP_SHIFT 3 /* DACCOMP - [4:3] */
+#define WM8961_DACCOMP_WIDTH 2 /* DACCOMP - [4:3] */
+#define WM8961_ADCCOMP_MASK 0x0006 /* ADCCOMP - [2:1] */
+#define WM8961_ADCCOMP_SHIFT 1 /* ADCCOMP - [2:1] */
+#define WM8961_ADCCOMP_WIDTH 2 /* ADCCOMP - [2:1] */
+#define WM8961_LOOPBACK 0x0001 /* LOOPBACK */
+#define WM8961_LOOPBACK_MASK 0x0001 /* LOOPBACK */
+#define WM8961_LOOPBACK_SHIFT 0 /* LOOPBACK */
+#define WM8961_LOOPBACK_WIDTH 1 /* LOOPBACK */
+
+/*
+ * R10 (0x0A) - Left DAC volume
+ */
+#define WM8961_DACVU 0x0100 /* DACVU */
+#define WM8961_DACVU_MASK 0x0100 /* DACVU */
+#define WM8961_DACVU_SHIFT 8 /* DACVU */
+#define WM8961_DACVU_WIDTH 1 /* DACVU */
+#define WM8961_LDACVOL_MASK 0x00FF /* LDACVOL - [7:0] */
+#define WM8961_LDACVOL_SHIFT 0 /* LDACVOL - [7:0] */
+#define WM8961_LDACVOL_WIDTH 8 /* LDACVOL - [7:0] */
+
+/*
+ * R11 (0x0B) - Right DAC volume
+ */
+#define WM8961_DACVU 0x0100 /* DACVU */
+#define WM8961_DACVU_MASK 0x0100 /* DACVU */
+#define WM8961_DACVU_SHIFT 8 /* DACVU */
+#define WM8961_DACVU_WIDTH 1 /* DACVU */
+#define WM8961_RDACVOL_MASK 0x00FF /* RDACVOL - [7:0] */
+#define WM8961_RDACVOL_SHIFT 0 /* RDACVOL - [7:0] */
+#define WM8961_RDACVOL_WIDTH 8 /* RDACVOL - [7:0] */
+
+/*
+ * R14 (0x0E) - Audio Interface 2
+ */
+#define WM8961_LRCLK_RATE_MASK 0x01FF /* LRCLK_RATE - [8:0] */
+#define WM8961_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [8:0] */
+#define WM8961_LRCLK_RATE_WIDTH 9 /* LRCLK_RATE - [8:0] */
+
+/*
+ * R15 (0x0F) - Software Reset
+ */
+#define WM8961_SW_RST_DEV_ID1_MASK 0xFFFF /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8961_SW_RST_DEV_ID1_SHIFT 0 /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8961_SW_RST_DEV_ID1_WIDTH 16 /* SW_RST_DEV_ID1 - [15:0] */
+
+/*
+ * R17 (0x11) - ALC1
+ */
+#define WM8961_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */
+#define WM8961_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */
+#define WM8961_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */
+#define WM8961_MAXGAIN_MASK 0x0070 /* MAXGAIN - [6:4] */
+#define WM8961_MAXGAIN_SHIFT 4 /* MAXGAIN - [6:4] */
+#define WM8961_MAXGAIN_WIDTH 3 /* MAXGAIN - [6:4] */
+#define WM8961_ALCL_MASK 0x000F /* ALCL - [3:0] */
+#define WM8961_ALCL_SHIFT 0 /* ALCL - [3:0] */
+#define WM8961_ALCL_WIDTH 4 /* ALCL - [3:0] */
+
+/*
+ * R18 (0x12) - ALC2
+ */
+#define WM8961_ALCZC 0x0080 /* ALCZC */
+#define WM8961_ALCZC_MASK 0x0080 /* ALCZC */
+#define WM8961_ALCZC_SHIFT 7 /* ALCZC */
+#define WM8961_ALCZC_WIDTH 1 /* ALCZC */
+#define WM8961_MINGAIN_MASK 0x0070 /* MINGAIN - [6:4] */
+#define WM8961_MINGAIN_SHIFT 4 /* MINGAIN - [6:4] */
+#define WM8961_MINGAIN_WIDTH 3 /* MINGAIN - [6:4] */
+#define WM8961_HLD_MASK 0x000F /* HLD - [3:0] */
+#define WM8961_HLD_SHIFT 0 /* HLD - [3:0] */
+#define WM8961_HLD_WIDTH 4 /* HLD - [3:0] */
+
+/*
+ * R19 (0x13) - ALC3
+ */
+#define WM8961_ALCMODE 0x0100 /* ALCMODE */
+#define WM8961_ALCMODE_MASK 0x0100 /* ALCMODE */
+#define WM8961_ALCMODE_SHIFT 8 /* ALCMODE */
+#define WM8961_ALCMODE_WIDTH 1 /* ALCMODE */
+#define WM8961_DCY_MASK 0x00F0 /* DCY - [7:4] */
+#define WM8961_DCY_SHIFT 4 /* DCY - [7:4] */
+#define WM8961_DCY_WIDTH 4 /* DCY - [7:4] */
+#define WM8961_ATK_MASK 0x000F /* ATK - [3:0] */
+#define WM8961_ATK_SHIFT 0 /* ATK - [3:0] */
+#define WM8961_ATK_WIDTH 4 /* ATK - [3:0] */
+
+/*
+ * R20 (0x14) - Noise Gate
+ */
+#define WM8961_NGTH_MASK 0x00F8 /* NGTH - [7:3] */
+#define WM8961_NGTH_SHIFT 3 /* NGTH - [7:3] */
+#define WM8961_NGTH_WIDTH 5 /* NGTH - [7:3] */
+#define WM8961_NGG 0x0002 /* NGG */
+#define WM8961_NGG_MASK 0x0002 /* NGG */
+#define WM8961_NGG_SHIFT 1 /* NGG */
+#define WM8961_NGG_WIDTH 1 /* NGG */
+#define WM8961_NGAT 0x0001 /* NGAT */
+#define WM8961_NGAT_MASK 0x0001 /* NGAT */
+#define WM8961_NGAT_SHIFT 0 /* NGAT */
+#define WM8961_NGAT_WIDTH 1 /* NGAT */
+
+/*
+ * R21 (0x15) - Left ADC volume
+ */
+#define WM8961_ADCVU 0x0100 /* ADCVU */
+#define WM8961_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8961_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8961_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8961_LADCVOL_MASK 0x00FF /* LADCVOL - [7:0] */
+#define WM8961_LADCVOL_SHIFT 0 /* LADCVOL - [7:0] */
+#define WM8961_LADCVOL_WIDTH 8 /* LADCVOL - [7:0] */
+
+/*
+ * R22 (0x16) - Right ADC volume
+ */
+#define WM8961_ADCVU 0x0100 /* ADCVU */
+#define WM8961_ADCVU_MASK 0x0100 /* ADCVU */
+#define WM8961_ADCVU_SHIFT 8 /* ADCVU */
+#define WM8961_ADCVU_WIDTH 1 /* ADCVU */
+#define WM8961_RADCVOL_MASK 0x00FF /* RADCVOL - [7:0] */
+#define WM8961_RADCVOL_SHIFT 0 /* RADCVOL - [7:0] */
+#define WM8961_RADCVOL_WIDTH 8 /* RADCVOL - [7:0] */
+
+/*
+ * R23 (0x17) - Additional control(1)
+ */
+#define WM8961_TSDEN 0x0100 /* TSDEN */
+#define WM8961_TSDEN_MASK 0x0100 /* TSDEN */
+#define WM8961_TSDEN_SHIFT 8 /* TSDEN */
+#define WM8961_TSDEN_WIDTH 1 /* TSDEN */
+#define WM8961_DMONOMIX 0x0010 /* DMONOMIX */
+#define WM8961_DMONOMIX_MASK 0x0010 /* DMONOMIX */
+#define WM8961_DMONOMIX_SHIFT 4 /* DMONOMIX */
+#define WM8961_DMONOMIX_WIDTH 1 /* DMONOMIX */
+#define WM8961_TOEN 0x0001 /* TOEN */
+#define WM8961_TOEN_MASK 0x0001 /* TOEN */
+#define WM8961_TOEN_SHIFT 0 /* TOEN */
+#define WM8961_TOEN_WIDTH 1 /* TOEN */
+
+/*
+ * R24 (0x18) - Additional control(2)
+ */
+#define WM8961_TRIS 0x0008 /* TRIS */
+#define WM8961_TRIS_MASK 0x0008 /* TRIS */
+#define WM8961_TRIS_SHIFT 3 /* TRIS */
+#define WM8961_TRIS_WIDTH 1 /* TRIS */
+
+/*
+ * R25 (0x19) - Pwr Mgmt (1)
+ */
+#define WM8961_VMIDSEL_MASK 0x0180 /* VMIDSEL - [8:7] */
+#define WM8961_VMIDSEL_SHIFT 7 /* VMIDSEL - [8:7] */
+#define WM8961_VMIDSEL_WIDTH 2 /* VMIDSEL - [8:7] */
+#define WM8961_VREF 0x0040 /* VREF */
+#define WM8961_VREF_MASK 0x0040 /* VREF */
+#define WM8961_VREF_SHIFT 6 /* VREF */
+#define WM8961_VREF_WIDTH 1 /* VREF */
+#define WM8961_AINL 0x0020 /* AINL */
+#define WM8961_AINL_MASK 0x0020 /* AINL */
+#define WM8961_AINL_SHIFT 5 /* AINL */
+#define WM8961_AINL_WIDTH 1 /* AINL */
+#define WM8961_AINR 0x0010 /* AINR */
+#define WM8961_AINR_MASK 0x0010 /* AINR */
+#define WM8961_AINR_SHIFT 4 /* AINR */
+#define WM8961_AINR_WIDTH 1 /* AINR */
+#define WM8961_ADCL 0x0008 /* ADCL */
+#define WM8961_ADCL_MASK 0x0008 /* ADCL */
+#define WM8961_ADCL_SHIFT 3 /* ADCL */
+#define WM8961_ADCL_WIDTH 1 /* ADCL */
+#define WM8961_ADCR 0x0004 /* ADCR */
+#define WM8961_ADCR_MASK 0x0004 /* ADCR */
+#define WM8961_ADCR_SHIFT 2 /* ADCR */
+#define WM8961_ADCR_WIDTH 1 /* ADCR */
+#define WM8961_MICB 0x0002 /* MICB */
+#define WM8961_MICB_MASK 0x0002 /* MICB */
+#define WM8961_MICB_SHIFT 1 /* MICB */
+#define WM8961_MICB_WIDTH 1 /* MICB */
+
+/*
+ * R26 (0x1A) - Pwr Mgmt (2)
+ */
+#define WM8961_DACL 0x0100 /* DACL */
+#define WM8961_DACL_MASK 0x0100 /* DACL */
+#define WM8961_DACL_SHIFT 8 /* DACL */
+#define WM8961_DACL_WIDTH 1 /* DACL */
+#define WM8961_DACR 0x0080 /* DACR */
+#define WM8961_DACR_MASK 0x0080 /* DACR */
+#define WM8961_DACR_SHIFT 7 /* DACR */
+#define WM8961_DACR_WIDTH 1 /* DACR */
+#define WM8961_LOUT1_PGA 0x0040 /* LOUT1_PGA */
+#define WM8961_LOUT1_PGA_MASK 0x0040 /* LOUT1_PGA */
+#define WM8961_LOUT1_PGA_SHIFT 6 /* LOUT1_PGA */
+#define WM8961_LOUT1_PGA_WIDTH 1 /* LOUT1_PGA */
+#define WM8961_ROUT1_PGA 0x0020 /* ROUT1_PGA */
+#define WM8961_ROUT1_PGA_MASK 0x0020 /* ROUT1_PGA */
+#define WM8961_ROUT1_PGA_SHIFT 5 /* ROUT1_PGA */
+#define WM8961_ROUT1_PGA_WIDTH 1 /* ROUT1_PGA */
+#define WM8961_SPKL_PGA 0x0010 /* SPKL_PGA */
+#define WM8961_SPKL_PGA_MASK 0x0010 /* SPKL_PGA */
+#define WM8961_SPKL_PGA_SHIFT 4 /* SPKL_PGA */
+#define WM8961_SPKL_PGA_WIDTH 1 /* SPKL_PGA */
+#define WM8961_SPKR_PGA 0x0008 /* SPKR_PGA */
+#define WM8961_SPKR_PGA_MASK 0x0008 /* SPKR_PGA */
+#define WM8961_SPKR_PGA_SHIFT 3 /* SPKR_PGA */
+#define WM8961_SPKR_PGA_WIDTH 1 /* SPKR_PGA */
+
+/*
+ * R27 (0x1B) - Additional Control (3)
+ */
+#define WM8961_SAMPLE_RATE_MASK 0x0007 /* SAMPLE_RATE - [2:0] */
+#define WM8961_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [2:0] */
+#define WM8961_SAMPLE_RATE_WIDTH 3 /* SAMPLE_RATE - [2:0] */
+
+/*
+ * R28 (0x1C) - Anti-pop
+ */
+#define WM8961_BUFDCOPEN 0x0010 /* BUFDCOPEN */
+#define WM8961_BUFDCOPEN_MASK 0x0010 /* BUFDCOPEN */
+#define WM8961_BUFDCOPEN_SHIFT 4 /* BUFDCOPEN */
+#define WM8961_BUFDCOPEN_WIDTH 1 /* BUFDCOPEN */
+#define WM8961_BUFIOEN 0x0008 /* BUFIOEN */
+#define WM8961_BUFIOEN_MASK 0x0008 /* BUFIOEN */
+#define WM8961_BUFIOEN_SHIFT 3 /* BUFIOEN */
+#define WM8961_BUFIOEN_WIDTH 1 /* BUFIOEN */
+#define WM8961_SOFT_ST 0x0004 /* SOFT_ST */
+#define WM8961_SOFT_ST_MASK 0x0004 /* SOFT_ST */
+#define WM8961_SOFT_ST_SHIFT 2 /* SOFT_ST */
+#define WM8961_SOFT_ST_WIDTH 1 /* SOFT_ST */
+
+/*
+ * R30 (0x1E) - Clocking 3
+ */
+#define WM8961_CLK_TO_DIV_MASK 0x0180 /* CLK_TO_DIV - [8:7] */
+#define WM8961_CLK_TO_DIV_SHIFT 7 /* CLK_TO_DIV - [8:7] */
+#define WM8961_CLK_TO_DIV_WIDTH 2 /* CLK_TO_DIV - [8:7] */
+#define WM8961_CLK_256K_DIV_MASK 0x007E /* CLK_256K_DIV - [6:1] */
+#define WM8961_CLK_256K_DIV_SHIFT 1 /* CLK_256K_DIV - [6:1] */
+#define WM8961_CLK_256K_DIV_WIDTH 6 /* CLK_256K_DIV - [6:1] */
+#define WM8961_MANUAL_MODE 0x0001 /* MANUAL_MODE */
+#define WM8961_MANUAL_MODE_MASK 0x0001 /* MANUAL_MODE */
+#define WM8961_MANUAL_MODE_SHIFT 0 /* MANUAL_MODE */
+#define WM8961_MANUAL_MODE_WIDTH 1 /* MANUAL_MODE */
+
+/*
+ * R32 (0x20) - ADCL signal path
+ */
+#define WM8961_LMICBOOST_MASK 0x0030 /* LMICBOOST - [5:4] */
+#define WM8961_LMICBOOST_SHIFT 4 /* LMICBOOST - [5:4] */
+#define WM8961_LMICBOOST_WIDTH 2 /* LMICBOOST - [5:4] */
+
+/*
+ * R33 (0x21) - ADCR signal path
+ */
+#define WM8961_RMICBOOST_MASK 0x0030 /* RMICBOOST - [5:4] */
+#define WM8961_RMICBOOST_SHIFT 4 /* RMICBOOST - [5:4] */
+#define WM8961_RMICBOOST_WIDTH 2 /* RMICBOOST - [5:4] */
+
+/*
+ * R40 (0x28) - LOUT2 volume
+ */
+#define WM8961_SPKVU 0x0100 /* SPKVU */
+#define WM8961_SPKVU_MASK 0x0100 /* SPKVU */
+#define WM8961_SPKVU_SHIFT 8 /* SPKVU */
+#define WM8961_SPKVU_WIDTH 1 /* SPKVU */
+#define WM8961_SPKLZC 0x0080 /* SPKLZC */
+#define WM8961_SPKLZC_MASK 0x0080 /* SPKLZC */
+#define WM8961_SPKLZC_SHIFT 7 /* SPKLZC */
+#define WM8961_SPKLZC_WIDTH 1 /* SPKLZC */
+#define WM8961_SPKLVOL_MASK 0x007F /* SPKLVOL - [6:0] */
+#define WM8961_SPKLVOL_SHIFT 0 /* SPKLVOL - [6:0] */
+#define WM8961_SPKLVOL_WIDTH 7 /* SPKLVOL - [6:0] */
+
+/*
+ * R41 (0x29) - ROUT2 volume
+ */
+#define WM8961_SPKVU 0x0100 /* SPKVU */
+#define WM8961_SPKVU_MASK 0x0100 /* SPKVU */
+#define WM8961_SPKVU_SHIFT 8 /* SPKVU */
+#define WM8961_SPKVU_WIDTH 1 /* SPKVU */
+#define WM8961_SPKRZC 0x0080 /* SPKRZC */
+#define WM8961_SPKRZC_MASK 0x0080 /* SPKRZC */
+#define WM8961_SPKRZC_SHIFT 7 /* SPKRZC */
+#define WM8961_SPKRZC_WIDTH 1 /* SPKRZC */
+#define WM8961_SPKRVOL_MASK 0x007F /* SPKRVOL - [6:0] */
+#define WM8961_SPKRVOL_SHIFT 0 /* SPKRVOL - [6:0] */
+#define WM8961_SPKRVOL_WIDTH 7 /* SPKRVOL - [6:0] */
+
+/*
+ * R47 (0x2F) - Pwr Mgmt (3)
+ */
+#define WM8961_TEMP_SHUT 0x0002 /* TEMP_SHUT */
+#define WM8961_TEMP_SHUT_MASK 0x0002 /* TEMP_SHUT */
+#define WM8961_TEMP_SHUT_SHIFT 1 /* TEMP_SHUT */
+#define WM8961_TEMP_SHUT_WIDTH 1 /* TEMP_SHUT */
+#define WM8961_TEMP_WARN 0x0001 /* TEMP_WARN */
+#define WM8961_TEMP_WARN_MASK 0x0001 /* TEMP_WARN */
+#define WM8961_TEMP_WARN_SHIFT 0 /* TEMP_WARN */
+#define WM8961_TEMP_WARN_WIDTH 1 /* TEMP_WARN */
+
+/*
+ * R48 (0x30) - Additional Control (4)
+ */
+#define WM8961_TSENSEN 0x0002 /* TSENSEN */
+#define WM8961_TSENSEN_MASK 0x0002 /* TSENSEN */
+#define WM8961_TSENSEN_SHIFT 1 /* TSENSEN */
+#define WM8961_TSENSEN_WIDTH 1 /* TSENSEN */
+#define WM8961_MBSEL 0x0001 /* MBSEL */
+#define WM8961_MBSEL_MASK 0x0001 /* MBSEL */
+#define WM8961_MBSEL_SHIFT 0 /* MBSEL */
+#define WM8961_MBSEL_WIDTH 1 /* MBSEL */
+
+/*
+ * R49 (0x31) - Class D Control 1
+ */
+#define WM8961_SPKR_ENA 0x0080 /* SPKR_ENA */
+#define WM8961_SPKR_ENA_MASK 0x0080 /* SPKR_ENA */
+#define WM8961_SPKR_ENA_SHIFT 7 /* SPKR_ENA */
+#define WM8961_SPKR_ENA_WIDTH 1 /* SPKR_ENA */
+#define WM8961_SPKL_ENA 0x0040 /* SPKL_ENA */
+#define WM8961_SPKL_ENA_MASK 0x0040 /* SPKL_ENA */
+#define WM8961_SPKL_ENA_SHIFT 6 /* SPKL_ENA */
+#define WM8961_SPKL_ENA_WIDTH 1 /* SPKL_ENA */
+
+/*
+ * R51 (0x33) - Class D Control 2
+ */
+#define WM8961_CLASSD_ACGAIN_MASK 0x0007 /* CLASSD_ACGAIN - [2:0] */
+#define WM8961_CLASSD_ACGAIN_SHIFT 0 /* CLASSD_ACGAIN - [2:0] */
+#define WM8961_CLASSD_ACGAIN_WIDTH 3 /* CLASSD_ACGAIN - [2:0] */
+
+/*
+ * R56 (0x38) - Clocking 4
+ */
+#define WM8961_CLK_DCS_DIV_MASK 0x01E0 /* CLK_DCS_DIV - [8:5] */
+#define WM8961_CLK_DCS_DIV_SHIFT 5 /* CLK_DCS_DIV - [8:5] */
+#define WM8961_CLK_DCS_DIV_WIDTH 4 /* CLK_DCS_DIV - [8:5] */
+#define WM8961_CLK_SYS_RATE_MASK 0x001E /* CLK_SYS_RATE - [4:1] */
+#define WM8961_CLK_SYS_RATE_SHIFT 1 /* CLK_SYS_RATE - [4:1] */
+#define WM8961_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [4:1] */
+
+/*
+ * R57 (0x39) - DSP Sidetone 0
+ */
+#define WM8961_ADCR_DAC_SVOL_MASK 0x00F0 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8961_ADCR_DAC_SVOL_SHIFT 4 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8961_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [7:4] */
+#define WM8961_ADC_TO_DACR_MASK 0x000C /* ADC_TO_DACR - [3:2] */
+#define WM8961_ADC_TO_DACR_SHIFT 2 /* ADC_TO_DACR - [3:2] */
+#define WM8961_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [3:2] */
+
+/*
+ * R58 (0x3A) - DSP Sidetone 1
+ */
+#define WM8961_ADCL_DAC_SVOL_MASK 0x00F0 /* ADCL_DAC_SVOL - [7:4] */
+#define WM8961_ADCL_DAC_SVOL_SHIFT 4 /* ADCL_DAC_SVOL - [7:4] */
+#define WM8961_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [7:4] */
+#define WM8961_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */
+#define WM8961_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */
+#define WM8961_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */
+
+/*
+ * R60 (0x3C) - DC Servo 0
+ */
+#define WM8961_DCS_ENA_CHAN_INL 0x0080 /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_ENA_CHAN_INL_MASK 0x0080 /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_ENA_CHAN_INL_SHIFT 7 /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_ENA_CHAN_INL_WIDTH 1 /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL 0x0040 /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL_MASK 0x0040 /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL_SHIFT 6 /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL_WIDTH 1 /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_SERIES_INL 0x0010 /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_TRIG_SERIES_INL_MASK 0x0010 /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_TRIG_SERIES_INL_SHIFT 4 /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_TRIG_SERIES_INL_WIDTH 1 /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_ENA_CHAN_INR 0x0008 /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_ENA_CHAN_INR_MASK 0x0008 /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_ENA_CHAN_INR_SHIFT 3 /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_ENA_CHAN_INR_WIDTH 1 /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR 0x0004 /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR_MASK 0x0004 /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR_SHIFT 2 /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR_WIDTH 1 /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_SERIES_INR 0x0001 /* DCS_TRIG_SERIES_INR */
+#define WM8961_DCS_TRIG_SERIES_INR_MASK 0x0001 /* DCS_TRIG_SERIES_INR */
+#define WM8961_DCS_TRIG_SERIES_INR_SHIFT 0 /* DCS_TRIG_SERIES_INR */
+#define WM8961_DCS_TRIG_SERIES_INR_WIDTH 1 /* DCS_TRIG_SERIES_INR */
+
+/*
+ * R61 (0x3D) - DC Servo 1
+ */
+#define WM8961_DCS_ENA_CHAN_HPL 0x0080 /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_ENA_CHAN_HPL_MASK 0x0080 /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_ENA_CHAN_HPL_SHIFT 7 /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_ENA_CHAN_HPL_WIDTH 1 /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL 0x0040 /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL_MASK 0x0040 /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL_SHIFT 6 /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL_WIDTH 1 /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL 0x0010 /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL_MASK 0x0010 /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL_SHIFT 4 /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL_WIDTH 1 /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_ENA_CHAN_HPR 0x0008 /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_ENA_CHAN_HPR_MASK 0x0008 /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_ENA_CHAN_HPR_SHIFT 3 /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_ENA_CHAN_HPR_WIDTH 1 /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR 0x0004 /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR_MASK 0x0004 /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR_SHIFT 2 /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR_WIDTH 1 /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR 0x0001 /* DCS_TRIG_SERIES_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR_MASK 0x0001 /* DCS_TRIG_SERIES_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR_SHIFT 0 /* DCS_TRIG_SERIES_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR_WIDTH 1 /* DCS_TRIG_SERIES_HPR */
+
+/*
+ * R63 (0x3F) - DC Servo 3
+ */
+#define WM8961_DCS_FILT_BW_SERIES_MASK 0x0030 /* DCS_FILT_BW_SERIES - [5:4] */
+#define WM8961_DCS_FILT_BW_SERIES_SHIFT 4 /* DCS_FILT_BW_SERIES - [5:4] */
+#define WM8961_DCS_FILT_BW_SERIES_WIDTH 2 /* DCS_FILT_BW_SERIES - [5:4] */
+
+/*
+ * R65 (0x41) - DC Servo 5
+ */
+#define WM8961_DCS_SERIES_NO_HP_MASK 0x007F /* DCS_SERIES_NO_HP - [6:0] */
+#define WM8961_DCS_SERIES_NO_HP_SHIFT 0 /* DCS_SERIES_NO_HP - [6:0] */
+#define WM8961_DCS_SERIES_NO_HP_WIDTH 7 /* DCS_SERIES_NO_HP - [6:0] */
+
+/*
+ * R68 (0x44) - Analogue PGA Bias
+ */
+#define WM8961_HP_PGAS_BIAS_MASK 0x0007 /* HP_PGAS_BIAS - [2:0] */
+#define WM8961_HP_PGAS_BIAS_SHIFT 0 /* HP_PGAS_BIAS - [2:0] */
+#define WM8961_HP_PGAS_BIAS_WIDTH 3 /* HP_PGAS_BIAS - [2:0] */
+
+/*
+ * R69 (0x45) - Analogue HP 0
+ */
+#define WM8961_HPL_RMV_SHORT 0x0080 /* HPL_RMV_SHORT */
+#define WM8961_HPL_RMV_SHORT_MASK 0x0080 /* HPL_RMV_SHORT */
+#define WM8961_HPL_RMV_SHORT_SHIFT 7 /* HPL_RMV_SHORT */
+#define WM8961_HPL_RMV_SHORT_WIDTH 1 /* HPL_RMV_SHORT */
+#define WM8961_HPL_ENA_OUTP 0x0040 /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_OUTP_MASK 0x0040 /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_OUTP_SHIFT 6 /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_OUTP_WIDTH 1 /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_DLY 0x0020 /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA_DLY_MASK 0x0020 /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA_DLY_SHIFT 5 /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA_DLY_WIDTH 1 /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA 0x0010 /* HPL_ENA */
+#define WM8961_HPL_ENA_MASK 0x0010 /* HPL_ENA */
+#define WM8961_HPL_ENA_SHIFT 4 /* HPL_ENA */
+#define WM8961_HPL_ENA_WIDTH 1 /* HPL_ENA */
+#define WM8961_HPR_RMV_SHORT 0x0008 /* HPR_RMV_SHORT */
+#define WM8961_HPR_RMV_SHORT_MASK 0x0008 /* HPR_RMV_SHORT */
+#define WM8961_HPR_RMV_SHORT_SHIFT 3 /* HPR_RMV_SHORT */
+#define WM8961_HPR_RMV_SHORT_WIDTH 1 /* HPR_RMV_SHORT */
+#define WM8961_HPR_ENA_OUTP 0x0004 /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_OUTP_MASK 0x0004 /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_OUTP_SHIFT 2 /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_OUTP_WIDTH 1 /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_DLY 0x0002 /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA_DLY_MASK 0x0002 /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA_DLY_SHIFT 1 /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA_DLY_WIDTH 1 /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA 0x0001 /* HPR_ENA */
+#define WM8961_HPR_ENA_MASK 0x0001 /* HPR_ENA */
+#define WM8961_HPR_ENA_SHIFT 0 /* HPR_ENA */
+#define WM8961_HPR_ENA_WIDTH 1 /* HPR_ENA */
+
+/*
+ * R71 (0x47) - Analogue HP 2
+ */
+#define WM8961_HPL_VOL_MASK 0x01C0 /* HPL_VOL - [8:6] */
+#define WM8961_HPL_VOL_SHIFT 6 /* HPL_VOL - [8:6] */
+#define WM8961_HPL_VOL_WIDTH 3 /* HPL_VOL - [8:6] */
+#define WM8961_HPR_VOL_MASK 0x0038 /* HPR_VOL - [5:3] */
+#define WM8961_HPR_VOL_SHIFT 3 /* HPR_VOL - [5:3] */
+#define WM8961_HPR_VOL_WIDTH 3 /* HPR_VOL - [5:3] */
+#define WM8961_HP_BIAS_BOOST_MASK 0x0007 /* HP_BIAS_BOOST - [2:0] */
+#define WM8961_HP_BIAS_BOOST_SHIFT 0 /* HP_BIAS_BOOST - [2:0] */
+#define WM8961_HP_BIAS_BOOST_WIDTH 3 /* HP_BIAS_BOOST - [2:0] */
+
+/*
+ * R72 (0x48) - Charge Pump 1
+ */
+#define WM8961_CP_ENA 0x0001 /* CP_ENA */
+#define WM8961_CP_ENA_MASK 0x0001 /* CP_ENA */
+#define WM8961_CP_ENA_SHIFT 0 /* CP_ENA */
+#define WM8961_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R82 (0x52) - Charge Pump B
+ */
+#define WM8961_CP_DYN_PWR_MASK 0x0003 /* CP_DYN_PWR - [1:0] */
+#define WM8961_CP_DYN_PWR_SHIFT 0 /* CP_DYN_PWR - [1:0] */
+#define WM8961_CP_DYN_PWR_WIDTH 2 /* CP_DYN_PWR - [1:0] */
+
+/*
+ * R87 (0x57) - Write Sequencer 1
+ */
+#define WM8961_WSEQ_ENA 0x0020 /* WSEQ_ENA */
+#define WM8961_WSEQ_ENA_MASK 0x0020 /* WSEQ_ENA */
+#define WM8961_WSEQ_ENA_SHIFT 5 /* WSEQ_ENA */
+#define WM8961_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM8961_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8961_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8961_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R88 (0x58) - Write Sequencer 2
+ */
+#define WM8961_WSEQ_EOS 0x0100 /* WSEQ_EOS */
+#define WM8961_WSEQ_EOS_MASK 0x0100 /* WSEQ_EOS */
+#define WM8961_WSEQ_EOS_SHIFT 8 /* WSEQ_EOS */
+#define WM8961_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */
+#define WM8961_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */
+#define WM8961_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */
+#define WM8961_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R89 (0x59) - Write Sequencer 3
+ */
+#define WM8961_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */
+#define WM8961_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */
+#define WM8961_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */
+
+/*
+ * R90 (0x5A) - Write Sequencer 4
+ */
+#define WM8961_WSEQ_ABORT 0x0100 /* WSEQ_ABORT */
+#define WM8961_WSEQ_ABORT_MASK 0x0100 /* WSEQ_ABORT */
+#define WM8961_WSEQ_ABORT_SHIFT 8 /* WSEQ_ABORT */
+#define WM8961_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8961_WSEQ_START 0x0080 /* WSEQ_START */
+#define WM8961_WSEQ_START_MASK 0x0080 /* WSEQ_START */
+#define WM8961_WSEQ_START_SHIFT 7 /* WSEQ_START */
+#define WM8961_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8961_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */
+#define WM8961_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */
+#define WM8961_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R91 (0x5B) - Write Sequencer 5
+ */
+#define WM8961_WSEQ_DATA_WIDTH_MASK 0x0070 /* WSEQ_DATA_WIDTH - [6:4] */
+#define WM8961_WSEQ_DATA_WIDTH_SHIFT 4 /* WSEQ_DATA_WIDTH - [6:4] */
+#define WM8961_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [6:4] */
+#define WM8961_WSEQ_DATA_START_MASK 0x000F /* WSEQ_DATA_START - [3:0] */
+#define WM8961_WSEQ_DATA_START_SHIFT 0 /* WSEQ_DATA_START - [3:0] */
+#define WM8961_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [3:0] */
+
+/*
+ * R92 (0x5C) - Write Sequencer 6
+ */
+#define WM8961_WSEQ_DELAY_MASK 0x000F /* WSEQ_DELAY - [3:0] */
+#define WM8961_WSEQ_DELAY_SHIFT 0 /* WSEQ_DELAY - [3:0] */
+#define WM8961_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [3:0] */
+
+/*
+ * R93 (0x5D) - Write Sequencer 7
+ */
+#define WM8961_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */
+#define WM8961_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */
+#define WM8961_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */
+#define WM8961_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+
+/*
+ * R252 (0xFC) - General test 1
+ */
+#define WM8961_ARA_ENA 0x0002 /* ARA_ENA */
+#define WM8961_ARA_ENA_MASK 0x0002 /* ARA_ENA */
+#define WM8961_ARA_ENA_SHIFT 1 /* ARA_ENA */
+#define WM8961_ARA_ENA_WIDTH 1 /* ARA_ENA */
+#define WM8961_AUTO_INC 0x0001 /* AUTO_INC */
+#define WM8961_AUTO_INC_MASK 0x0001 /* AUTO_INC */
+#define WM8961_AUTO_INC_SHIFT 0 /* AUTO_INC */
+#define WM8961_AUTO_INC_WIDTH 1 /* AUTO_INC */
+
+#endif
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index 032dca22dbd..d66efb0546e 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -59,44 +59,7 @@ static const u16 wm8971_reg[] = {
0x0079, 0x0079, 0x0079, /* 40 */
};
-static inline unsigned int wm8971_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg < WM8971_REG_COUNT)
- return cache[reg];
-
- return -1;
-}
-
-static inline void wm8971_write_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg < WM8971_REG_COUNT)
- cache[reg] = value;
-}
-
-static int wm8971_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8753 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8971_write_reg_cache (codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define wm8971_reset(c) wm8971_write(c, WM8971_RESET, 0)
+#define wm8971_reset(c) snd_soc_write(c, WM8971_RESET, 0)
/* WM8971 Controls */
static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" };
@@ -521,7 +484,7 @@ static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8971_write(codec, WM8971_IFACE, iface);
+ snd_soc_write(codec, WM8971_IFACE, iface);
return 0;
}
@@ -533,8 +496,8 @@ static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8971_priv *wm8971 = codec->private_data;
- u16 iface = wm8971_read_reg_cache(codec, WM8971_IFACE) & 0x1f3;
- u16 srate = wm8971_read_reg_cache(codec, WM8971_SRATE) & 0x1c0;
+ u16 iface = snd_soc_read(codec, WM8971_IFACE) & 0x1f3;
+ u16 srate = snd_soc_read(codec, WM8971_SRATE) & 0x1c0;
int coeff = get_coeff(wm8971->sysclk, params_rate(params));
/* bit size */
@@ -553,9 +516,9 @@ static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- wm8971_write(codec, WM8971_IFACE, iface);
+ snd_soc_write(codec, WM8971_IFACE, iface);
if (coeff >= 0)
- wm8971_write(codec, WM8971_SRATE, srate |
+ snd_soc_write(codec, WM8971_SRATE, srate |
(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
return 0;
@@ -564,33 +527,33 @@ static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
static int wm8971_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8971_read_reg_cache(codec, WM8971_ADCDAC) & 0xfff7;
+ u16 mute_reg = snd_soc_read(codec, WM8971_ADCDAC) & 0xfff7;
if (mute)
- wm8971_write(codec, WM8971_ADCDAC, mute_reg | 0x8);
+ snd_soc_write(codec, WM8971_ADCDAC, mute_reg | 0x8);
else
- wm8971_write(codec, WM8971_ADCDAC, mute_reg);
+ snd_soc_write(codec, WM8971_ADCDAC, mute_reg);
return 0;
}
static int wm8971_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u16 pwr_reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
+ u16 pwr_reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
switch (level) {
case SND_SOC_BIAS_ON:
/* set vmid to 50k and unmute dac */
- wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
+ snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* mute dac and set vmid to 500k, enable VREF */
- wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
+ snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
break;
case SND_SOC_BIAS_OFF:
- wm8971_write(codec, WM8971_PWR1, 0x0001);
+ snd_soc_write(codec, WM8971_PWR1, 0x0001);
break;
}
codec->bias_level = level;
@@ -667,8 +630,8 @@ static int wm8971_resume(struct platform_device *pdev)
/* charge wm8971 caps */
if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
- reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
- wm8971_write(codec, WM8971_PWR1, reg | 0x01c0);
+ reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
+ snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0);
codec->bias_level = SND_SOC_BIAS_ON;
queue_delayed_work(wm8971_workq, &codec->delayed_work,
msecs_to_jiffies(1000));
@@ -677,15 +640,14 @@ static int wm8971_resume(struct platform_device *pdev)
return 0;
}
-static int wm8971_init(struct snd_soc_device *socdev)
+static int wm8971_init(struct snd_soc_device *socdev,
+ enum snd_soc_control_type control)
{
struct snd_soc_codec *codec = socdev->card->codec;
int reg, ret = 0;
codec->name = "WM8971";
codec->owner = THIS_MODULE;
- codec->read = wm8971_read_reg_cache;
- codec->write = wm8971_write;
codec->set_bias_level = wm8971_set_bias_level;
codec->dai = &wm8971_dai;
codec->reg_cache_size = ARRAY_SIZE(wm8971_reg);
@@ -695,42 +657,48 @@ static int wm8971_init(struct snd_soc_device *socdev)
if (codec->reg_cache == NULL)
return -ENOMEM;
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8971: failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
wm8971_reset(codec);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "wm8971: failed to create pcms\n");
- goto pcm_err;
+ goto err;
}
/* charge output caps - set vmid to 5k for quick power up */
- reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
- wm8971_write(codec, WM8971_PWR1, reg | 0x01c0);
+ reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
+ snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0);
codec->bias_level = SND_SOC_BIAS_STANDBY;
queue_delayed_work(wm8971_workq, &codec->delayed_work,
msecs_to_jiffies(1000));
/* set the update bits */
- reg = wm8971_read_reg_cache(codec, WM8971_LDAC);
- wm8971_write(codec, WM8971_LDAC, reg | 0x0100);
- reg = wm8971_read_reg_cache(codec, WM8971_RDAC);
- wm8971_write(codec, WM8971_RDAC, reg | 0x0100);
-
- reg = wm8971_read_reg_cache(codec, WM8971_LOUT1V);
- wm8971_write(codec, WM8971_LOUT1V, reg | 0x0100);
- reg = wm8971_read_reg_cache(codec, WM8971_ROUT1V);
- wm8971_write(codec, WM8971_ROUT1V, reg | 0x0100);
-
- reg = wm8971_read_reg_cache(codec, WM8971_LOUT2V);
- wm8971_write(codec, WM8971_LOUT2V, reg | 0x0100);
- reg = wm8971_read_reg_cache(codec, WM8971_ROUT2V);
- wm8971_write(codec, WM8971_ROUT2V, reg | 0x0100);
-
- reg = wm8971_read_reg_cache(codec, WM8971_LINVOL);
- wm8971_write(codec, WM8971_LINVOL, reg | 0x0100);
- reg = wm8971_read_reg_cache(codec, WM8971_RINVOL);
- wm8971_write(codec, WM8971_RINVOL, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8971_LDAC);
+ snd_soc_write(codec, WM8971_LDAC, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8971_RDAC);
+ snd_soc_write(codec, WM8971_RDAC, reg | 0x0100);
+
+ reg = snd_soc_read(codec, WM8971_LOUT1V);
+ snd_soc_write(codec, WM8971_LOUT1V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8971_ROUT1V);
+ snd_soc_write(codec, WM8971_ROUT1V, reg | 0x0100);
+
+ reg = snd_soc_read(codec, WM8971_LOUT2V);
+ snd_soc_write(codec, WM8971_LOUT2V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8971_ROUT2V);
+ snd_soc_write(codec, WM8971_ROUT2V, reg | 0x0100);
+
+ reg = snd_soc_read(codec, WM8971_LINVOL);
+ snd_soc_write(codec, WM8971_LINVOL, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8971_RINVOL);
+ snd_soc_write(codec, WM8971_RINVOL, reg | 0x0100);
snd_soc_add_controls(codec, wm8971_snd_controls,
ARRAY_SIZE(wm8971_snd_controls));
@@ -745,7 +713,7 @@ static int wm8971_init(struct snd_soc_device *socdev)
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-pcm_err:
+err:
kfree(codec->reg_cache);
return ret;
}
@@ -767,7 +735,7 @@ static int wm8971_i2c_probe(struct i2c_client *i2c,
codec->control_data = i2c;
- ret = wm8971_init(socdev);
+ ret = wm8971_init(socdev, SND_SOC_I2C);
if (ret < 0)
pr_err("failed to initialise WM8971\n");
@@ -877,7 +845,6 @@ static int wm8971_probe(struct platform_device *pdev)
#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
if (setup->i2c_address) {
- codec->hw_write = (hw_write_t)i2c_master_send;
ret = wm8971_add_i2c_device(pdev, setup);
}
#endif
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
new file mode 100644
index 00000000000..d8a013ab317
--- /dev/null
+++ b/sound/soc/codecs/wm8974.c
@@ -0,0 +1,808 @@
+/*
+ * wm8974.c -- WM8974 ALSA Soc Audio driver
+ *
+ * Copyright 2006-2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <linux@wolfsonmicro.com>
+ *
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8974.h"
+
+static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0050, 0x0000, 0x0140, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x00ff,
+ 0x0000, 0x0000, 0x0100, 0x00ff,
+ 0x0000, 0x0000, 0x012c, 0x002c,
+ 0x002c, 0x002c, 0x002c, 0x0000,
+ 0x0032, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0038, 0x000b, 0x0032, 0x0000,
+ 0x0008, 0x000c, 0x0093, 0x00e9,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0003, 0x0010, 0x0000, 0x0000,
+ 0x0000, 0x0002, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0039, 0x0000,
+ 0x0000,
+};
+
+#define WM8974_POWER1_BIASEN 0x08
+#define WM8974_POWER1_BUFIOEN 0x10
+
+struct wm8974_priv {
+ struct snd_soc_codec codec;
+ u16 reg_cache[WM8974_CACHEREGNUM];
+};
+
+static struct snd_soc_codec *wm8974_codec;
+
+#define wm8974_reset(c) snd_soc_write(c, WM8974_RESET, 0)
+
+static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
+static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8974_eqmode[] = {"Capture", "Playback" };
+static const char *wm8974_bw[] = {"Narrow", "Wide" };
+static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
+static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
+static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
+static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
+static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
+static const char *wm8974_alc[] = {"ALC", "Limiter" };
+
+static const struct soc_enum wm8974_enum[] = {
+ SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
+ SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
+ SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
+ SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
+
+ SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
+ SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
+ SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
+ SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
+
+ SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
+ SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
+ SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
+ SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
+
+ SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
+ SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
+};
+
+static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
+
+static const struct soc_enum wm8974_auxmode =
+ SOC_ENUM_SINGLE(WM8974_INPUT, 3, 2, wm8974_auxmode_text);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
+
+static const struct snd_kcontrol_new wm8974_snd_controls[] = {
+
+SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
+
+SOC_ENUM("DAC Companding", wm8974_enum[1]),
+SOC_ENUM("ADC Companding", wm8974_enum[0]),
+
+SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
+SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
+
+SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
+
+SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
+SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
+SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0),
+
+SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv),
+
+SOC_ENUM("Equaliser Function", wm8974_enum[3]),
+SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
+SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv),
+
+SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
+SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
+SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv),
+
+SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
+SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
+SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv),
+
+SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
+SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
+SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv),
+
+SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
+SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
+SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv),
+
+SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
+SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
+SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
+
+SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
+SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
+
+SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
+SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
+SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
+
+SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
+SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
+SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
+
+SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
+SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
+
+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
+
+SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
+SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv),
+
+SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
+SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
+SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv),
+
+SOC_ENUM("Aux Mode", wm8974_auxmode),
+
+SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
+SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
+};
+
+/* Speaker Output Mixer */
+static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
+};
+
+/* Mono Output Mixer */
+static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0),
+};
+
+/* Boost mixer */
+static const struct snd_kcontrol_new wm8974_boost_mixer[] = {
+SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0),
+};
+
+/* Input PGA */
+static const struct snd_kcontrol_new wm8974_inpga[] = {
+SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0),
+SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0),
+SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0),
+};
+
+/* AUX Input boost vol */
+static const struct snd_kcontrol_new wm8974_aux_boost_controls =
+SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
+
+/* Mic Input boost vol */
+static const struct snd_kcontrol_new wm8974_mic_boost_controls =
+SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
+
+static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
+ &wm8974_speaker_mixer_controls[0],
+ ARRAY_SIZE(wm8974_speaker_mixer_controls)),
+SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
+ &wm8974_mono_mixer_controls[0],
+ ARRAY_SIZE(wm8974_mono_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
+SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga,
+ ARRAY_SIZE(wm8974_inpga)),
+SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0,
+ wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)),
+
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
+
+SND_SOC_DAPM_INPUT("MICN"),
+SND_SOC_DAPM_INPUT("MICP"),
+SND_SOC_DAPM_INPUT("AUX"),
+SND_SOC_DAPM_OUTPUT("MONOOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Mono output mixer */
+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+ /* Speaker output mixer */
+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+ /* Outputs */
+ {"Mono Out", NULL, "Mono Mixer"},
+ {"MONOOUT", NULL, "Mono Out"},
+ {"SpkN Out", NULL, "Speaker Mixer"},
+ {"SpkP Out", NULL, "Speaker Mixer"},
+ {"SPKOUTN", NULL, "SpkN Out"},
+ {"SPKOUTP", NULL, "SpkP Out"},
+
+ /* Boost Mixer */
+ {"ADC", NULL, "Boost Mixer"},
+ {"Boost Mixer", "Aux Switch", "Aux Input"},
+ {"Boost Mixer", NULL, "Input PGA"},
+ {"Boost Mixer", NULL, "MICP"},
+
+ /* Input PGA */
+ {"Input PGA", "Aux Switch", "Aux Input"},
+ {"Input PGA", "MicN Switch", "MICN"},
+ {"Input PGA", "MicP Switch", "MICP"},
+
+ /* Inputs */
+ {"Aux Input", NULL, "AUX"},
+};
+
+static int wm8974_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
+ ARRAY_SIZE(wm8974_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+struct pll_ {
+ unsigned int pre_div:4; /* prescale - 1 */
+ unsigned int n:4;
+ unsigned int k;
+};
+
+static struct pll_ pll_div;
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+
+static void pll_factors(unsigned int target, unsigned int source)
+{
+ unsigned long long Kpart;
+ unsigned int K, Ndiv, Nmod;
+
+ Ndiv = target / source;
+ if (Ndiv < 6) {
+ source >>= 1;
+ pll_div.pre_div = 1;
+ Ndiv = target / source;
+ } else
+ pll_div.pre_div = 0;
+
+ if ((Ndiv < 6) || (Ndiv > 12))
+ printk(KERN_WARNING
+ "WM8974 N value %u outwith recommended range!\n",
+ Ndiv);
+
+ pll_div.n = Ndiv;
+ Nmod = target % source;
+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, source);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+
+ pll_div.k = K;
+}
+
+static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ if (freq_in == 0 || freq_out == 0) {
+ /* Clock CODEC directly from MCLK */
+ reg = snd_soc_read(codec, WM8974_CLOCK);
+ snd_soc_write(codec, WM8974_CLOCK, reg & 0x0ff);
+
+ /* Turn off PLL */
+ reg = snd_soc_read(codec, WM8974_POWER1);
+ snd_soc_write(codec, WM8974_POWER1, reg & 0x1df);
+ return 0;
+ }
+
+ pll_factors(freq_out*4, freq_in);
+
+ snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
+ snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
+ snd_soc_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
+ reg = snd_soc_read(codec, WM8974_POWER1);
+ snd_soc_write(codec, WM8974_POWER1, reg | 0x020);
+
+ /* Run CODEC from PLL instead of MCLK */
+ reg = snd_soc_read(codec, WM8974_CLOCK);
+ snd_soc_write(codec, WM8974_CLOCK, reg | 0x100);
+
+ return 0;
+}
+
+/*
+ * Configure WM8974 clock dividers.
+ */
+static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 reg;
+
+ switch (div_id) {
+ case WM8974_OPCLKDIV:
+ reg = snd_soc_read(codec, WM8974_GPIO) & 0x1cf;
+ snd_soc_write(codec, WM8974_GPIO, reg | div);
+ break;
+ case WM8974_MCLKDIV:
+ reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
+ snd_soc_write(codec, WM8974_CLOCK, reg | div);
+ break;
+ case WM8974_ADCCLK:
+ reg = snd_soc_read(codec, WM8974_ADC) & 0x1f7;
+ snd_soc_write(codec, WM8974_ADC, reg | div);
+ break;
+ case WM8974_DACCLK:
+ reg = snd_soc_read(codec, WM8974_DAC) & 0x1f7;
+ snd_soc_write(codec, WM8974_DAC, reg | div);
+ break;
+ case WM8974_BCLKDIV:
+ reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
+ snd_soc_write(codec, WM8974_CLOCK, reg | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = 0;
+ u16 clk = snd_soc_read(codec, WM8974_CLOCK) & 0x1fe;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ clk |= 0x0001;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x0010;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x0008;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x00018;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x0180;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x0100;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x0080;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_write(codec, WM8974_IFACE, iface);
+ snd_soc_write(codec, WM8974_CLOCK, clk);
+ return 0;
+}
+
+static int wm8974_pcm_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;
+ u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
+ u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x0020;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface |= 0x0040;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x0060;
+ break;
+ }
+
+ /* filter coefficient */
+ switch (params_rate(params)) {
+ case SNDRV_PCM_RATE_8000:
+ adn |= 0x5 << 1;
+ break;
+ case SNDRV_PCM_RATE_11025:
+ adn |= 0x4 << 1;
+ break;
+ case SNDRV_PCM_RATE_16000:
+ adn |= 0x3 << 1;
+ break;
+ case SNDRV_PCM_RATE_22050:
+ adn |= 0x2 << 1;
+ break;
+ case SNDRV_PCM_RATE_32000:
+ adn |= 0x1 << 1;
+ break;
+ case SNDRV_PCM_RATE_44100:
+ case SNDRV_PCM_RATE_48000:
+ break;
+ }
+
+ snd_soc_write(codec, WM8974_IFACE, iface);
+ snd_soc_write(codec, WM8974_ADD, adn);
+ return 0;
+}
+
+static int wm8974_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;
+
+ if (mute)
+ snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
+ else
+ snd_soc_write(codec, WM8974_DAC, mute_reg);
+ return 0;
+}
+
+/* liam need to make this lower power with dapm */
+static int wm8974_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 power1 = snd_soc_read(codec, WM8974_POWER1) & ~0x3;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ power1 |= 0x1; /* VMID 50k */
+ snd_soc_write(codec, WM8974_POWER1, power1);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
+
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Initial cap charge at VMID 5k */
+ snd_soc_write(codec, WM8974_POWER1, power1 | 0x3);
+ mdelay(100);
+ }
+
+ power1 |= 0x2; /* VMID 500k */
+ snd_soc_write(codec, WM8974_POWER1, power1);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_write(codec, WM8974_POWER1, 0);
+ snd_soc_write(codec, WM8974_POWER2, 0);
+ snd_soc_write(codec, WM8974_POWER3, 0);
+ break;
+ }
+
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8974_ops = {
+ .hw_params = wm8974_pcm_hw_params,
+ .digital_mute = wm8974_mute,
+ .set_fmt = wm8974_set_dai_fmt,
+ .set_clkdiv = wm8974_set_dai_clkdiv,
+ .set_pll = wm8974_set_dai_pll,
+};
+
+struct snd_soc_dai wm8974_dai = {
+ .name = "WM8974 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2, /* Only 1 channel of data */
+ .rates = WM8974_RATES,
+ .formats = WM8974_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2, /* Only 1 channel of data */
+ .rates = WM8974_RATES,
+ .formats = WM8974_FORMATS,},
+ .ops = &wm8974_ops,
+ .symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(wm8974_dai);
+
+static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8974_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+ int i;
+ u8 data[2];
+ u16 *cache = codec->reg_cache;
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+ data[1] = cache[i] & 0x00ff;
+ codec->hw_write(codec->control_data, data, 2);
+ }
+ wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ wm8974_set_bias_level(codec, codec->suspend_bias_level);
+ return 0;
+}
+
+static int wm8974_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ if (wm8974_codec == NULL) {
+ dev_err(&pdev->dev, "Codec device not registered\n");
+ return -ENODEV;
+ }
+
+ socdev->card->codec = wm8974_codec;
+ codec = wm8974_codec;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, wm8974_snd_controls,
+ ARRAY_SIZE(wm8974_snd_controls));
+ wm8974_add_widgets(codec);
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to register card: %d\n", ret);
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ return ret;
+}
+
+/* power down chip */
+static int wm8974_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8974 = {
+ .probe = wm8974_probe,
+ .remove = wm8974_remove,
+ .suspend = wm8974_suspend,
+ .resume = wm8974_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
+
+static __devinit int wm8974_register(struct wm8974_priv *wm8974)
+{
+ int ret;
+ struct snd_soc_codec *codec = &wm8974->codec;
+
+ if (wm8974_codec) {
+ dev_err(codec->dev, "Another WM8974 is registered\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->private_data = wm8974;
+ codec->name = "WM8974";
+ codec->owner = THIS_MODULE;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8974_set_bias_level;
+ codec->dai = &wm8974_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = WM8974_CACHEREGNUM;
+ codec->reg_cache = &wm8974->reg_cache;
+
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
+
+ ret = wm8974_reset(codec);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to issue reset\n");
+ goto err;
+ }
+
+ wm8974_dai.dev = codec->dev;
+
+ wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ wm8974_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dai(&wm8974_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+ goto err_codec;
+ }
+
+ return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ kfree(wm8974);
+ return ret;
+}
+
+static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
+{
+ wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
+ snd_soc_unregister_dai(&wm8974_dai);
+ snd_soc_unregister_codec(&wm8974->codec);
+ kfree(wm8974);
+ wm8974_codec = NULL;
+}
+
+static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8974_priv *wm8974;
+ struct snd_soc_codec *codec;
+
+ wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
+ if (wm8974 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8974->codec;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+
+ i2c_set_clientdata(i2c, wm8974);
+ codec->control_data = i2c;
+
+ codec->dev = &i2c->dev;
+
+ return wm8974_register(wm8974);
+}
+
+static __devexit int wm8974_i2c_remove(struct i2c_client *client)
+{
+ struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
+ wm8974_unregister(wm8974);
+ return 0;
+}
+
+static const struct i2c_device_id wm8974_i2c_id[] = {
+ { "wm8974", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
+
+static struct i2c_driver wm8974_i2c_driver = {
+ .driver = {
+ .name = "WM8974",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8974_i2c_probe,
+ .remove = __devexit_p(wm8974_i2c_remove),
+ .id_table = wm8974_i2c_id,
+};
+
+static int __init wm8974_modinit(void)
+{
+ return i2c_add_driver(&wm8974_i2c_driver);
+}
+module_init(wm8974_modinit);
+
+static void __exit wm8974_exit(void)
+{
+ i2c_del_driver(&wm8974_i2c_driver);
+}
+module_exit(wm8974_exit);
+
+MODULE_DESCRIPTION("ASoC WM8974 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8974.h b/sound/soc/codecs/wm8974.h
new file mode 100644
index 00000000000..98de9562d4d
--- /dev/null
+++ b/sound/soc/codecs/wm8974.h
@@ -0,0 +1,99 @@
+/*
+ * wm8974.h -- WM8974 Soc Audio driver
+ *
+ * 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 _WM8974_H
+#define _WM8974_H
+
+/* WM8974 register space */
+
+#define WM8974_RESET 0x0
+#define WM8974_POWER1 0x1
+#define WM8974_POWER2 0x2
+#define WM8974_POWER3 0x3
+#define WM8974_IFACE 0x4
+#define WM8974_COMP 0x5
+#define WM8974_CLOCK 0x6
+#define WM8974_ADD 0x7
+#define WM8974_GPIO 0x8
+#define WM8974_DAC 0xa
+#define WM8974_DACVOL 0xb
+#define WM8974_ADC 0xe
+#define WM8974_ADCVOL 0xf
+#define WM8974_EQ1 0x12
+#define WM8974_EQ2 0x13
+#define WM8974_EQ3 0x14
+#define WM8974_EQ4 0x15
+#define WM8974_EQ5 0x16
+#define WM8974_DACLIM1 0x18
+#define WM8974_DACLIM2 0x19
+#define WM8974_NOTCH1 0x1b
+#define WM8974_NOTCH2 0x1c
+#define WM8974_NOTCH3 0x1d
+#define WM8974_NOTCH4 0x1e
+#define WM8974_ALC1 0x20
+#define WM8974_ALC2 0x21
+#define WM8974_ALC3 0x22
+#define WM8974_NGATE 0x23
+#define WM8974_PLLN 0x24
+#define WM8974_PLLK1 0x25
+#define WM8974_PLLK2 0x26
+#define WM8974_PLLK3 0x27
+#define WM8974_ATTEN 0x28
+#define WM8974_INPUT 0x2c
+#define WM8974_INPPGA 0x2d
+#define WM8974_ADCBOOST 0x2f
+#define WM8974_OUTPUT 0x31
+#define WM8974_SPKMIX 0x32
+#define WM8974_SPKVOL 0x36
+#define WM8974_MONOMIX 0x38
+
+#define WM8974_CACHEREGNUM 57
+
+/* Clock divider Id's */
+#define WM8974_OPCLKDIV 0
+#define WM8974_MCLKDIV 1
+#define WM8974_ADCCLK 2
+#define WM8974_DACCLK 3
+#define WM8974_BCLKDIV 4
+
+/* DAC clock dividers */
+#define WM8974_DACCLK_F2 (1 << 3)
+#define WM8974_DACCLK_F4 (0 << 3)
+
+/* ADC clock dividers */
+#define WM8974_ADCCLK_F2 (1 << 3)
+#define WM8974_ADCCLK_F4 (0 << 3)
+
+/* PLL Out dividers */
+#define WM8974_OPCLKDIV_1 (0 << 4)
+#define WM8974_OPCLKDIV_2 (1 << 4)
+#define WM8974_OPCLKDIV_3 (2 << 4)
+#define WM8974_OPCLKDIV_4 (3 << 4)
+
+/* BCLK clock dividers */
+#define WM8974_BCLKDIV_1 (0 << 2)
+#define WM8974_BCLKDIV_2 (1 << 2)
+#define WM8974_BCLKDIV_4 (2 << 2)
+#define WM8974_BCLKDIV_8 (3 << 2)
+#define WM8974_BCLKDIV_16 (4 << 2)
+#define WM8974_BCLKDIV_32 (5 << 2)
+
+/* MCLK clock dividers */
+#define WM8974_MCLKDIV_1 (0 << 5)
+#define WM8974_MCLKDIV_1_5 (1 << 5)
+#define WM8974_MCLKDIV_2 (2 << 5)
+#define WM8974_MCLKDIV_3 (3 << 5)
+#define WM8974_MCLKDIV_4 (4 << 5)
+#define WM8974_MCLKDIV_6 (5 << 5)
+#define WM8974_MCLKDIV_8 (6 << 5)
+#define WM8974_MCLKDIV_12 (7 << 5)
+
+extern struct snd_soc_dai wm8974_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8974;
+
+#endif
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 8c0fdf84aac..3f530f8a972 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -57,50 +57,7 @@ struct wm8988_priv {
};
-/*
- * read wm8988 register cache
- */
-static inline unsigned int wm8988_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg > WM8988_NUM_REG)
- return -1;
- return cache[reg];
-}
-
-/*
- * write wm8988 register cache
- */
-static inline void wm8988_write_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg > WM8988_NUM_REG)
- return;
- cache[reg] = value;
-}
-
-static int wm8988_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8753 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8988_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define wm8988_reset(c) wm8988_write(c, WM8988_RESET, 0)
+#define wm8988_reset(c) snd_soc_write(c, WM8988_RESET, 0)
/*
* WM8988 Controls
@@ -226,15 +183,15 @@ static int wm8988_lrc_control(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
- u16 adctl2 = wm8988_read_reg_cache(codec, WM8988_ADCTL2);
+ u16 adctl2 = snd_soc_read(codec, WM8988_ADCTL2);
/* Use the DAC to gate LRC if active, otherwise use ADC */
- if (wm8988_read_reg_cache(codec, WM8988_PWR2) & 0x180)
+ if (snd_soc_read(codec, WM8988_PWR2) & 0x180)
adctl2 &= ~0x4;
else
adctl2 |= 0x4;
- return wm8988_write(codec, WM8988_ADCTL2, adctl2);
+ return snd_soc_write(codec, WM8988_ADCTL2, adctl2);
}
static const char *wm8988_line_texts[] = {
@@ -619,7 +576,7 @@ static int wm8988_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8988_write(codec, WM8988_IFACE, iface);
+ snd_soc_write(codec, WM8988_IFACE, iface);
return 0;
}
@@ -653,8 +610,8 @@ static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8988_priv *wm8988 = codec->private_data;
- u16 iface = wm8988_read_reg_cache(codec, WM8988_IFACE) & 0x1f3;
- u16 srate = wm8988_read_reg_cache(codec, WM8988_SRATE) & 0x180;
+ u16 iface = snd_soc_read(codec, WM8988_IFACE) & 0x1f3;
+ u16 srate = snd_soc_read(codec, WM8988_SRATE) & 0x180;
int coeff;
coeff = get_coeff(wm8988->sysclk, params_rate(params));
@@ -685,9 +642,9 @@ static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- wm8988_write(codec, WM8988_IFACE, iface);
+ snd_soc_write(codec, WM8988_IFACE, iface);
if (coeff >= 0)
- wm8988_write(codec, WM8988_SRATE, srate |
+ snd_soc_write(codec, WM8988_SRATE, srate |
(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
return 0;
@@ -696,19 +653,19 @@ static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
static int wm8988_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8988_read_reg_cache(codec, WM8988_ADCDAC) & 0xfff7;
+ u16 mute_reg = snd_soc_read(codec, WM8988_ADCDAC) & 0xfff7;
if (mute)
- wm8988_write(codec, WM8988_ADCDAC, mute_reg | 0x8);
+ snd_soc_write(codec, WM8988_ADCDAC, mute_reg | 0x8);
else
- wm8988_write(codec, WM8988_ADCDAC, mute_reg);
+ snd_soc_write(codec, WM8988_ADCDAC, mute_reg);
return 0;
}
static int wm8988_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u16 pwr_reg = wm8988_read_reg_cache(codec, WM8988_PWR1) & ~0x1c1;
+ u16 pwr_reg = snd_soc_read(codec, WM8988_PWR1) & ~0x1c1;
switch (level) {
case SND_SOC_BIAS_ON:
@@ -716,24 +673,24 @@ static int wm8988_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VREF, VMID=2x50k, digital enabled */
- wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x00c0);
+ snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x00c0);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* VREF, VMID=2x5k */
- wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x1c1);
+ snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x1c1);
/* Charge caps */
msleep(100);
}
/* VREF, VMID=2*500k, digital stopped */
- wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x0141);
+ snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x0141);
break;
case SND_SOC_BIAS_OFF:
- wm8988_write(codec, WM8988_PWR1, 0x0000);
+ snd_soc_write(codec, WM8988_PWR1, 0x0000);
break;
}
codec->bias_level = level;
@@ -868,7 +825,8 @@ struct snd_soc_codec_device soc_codec_dev_wm8988 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8988);
-static int wm8988_register(struct wm8988_priv *wm8988)
+static int wm8988_register(struct wm8988_priv *wm8988,
+ enum snd_soc_control_type control)
{
struct snd_soc_codec *codec = &wm8988->codec;
int ret;
@@ -887,8 +845,6 @@ static int wm8988_register(struct wm8988_priv *wm8988)
codec->private_data = wm8988;
codec->name = "WM8988";
codec->owner = THIS_MODULE;
- codec->read = wm8988_read_reg_cache;
- codec->write = wm8988_write;
codec->dai = &wm8988_dai;
codec->num_dai = 1;
codec->reg_cache_size = ARRAY_SIZE(wm8988->reg_cache);
@@ -899,23 +855,29 @@ static int wm8988_register(struct wm8988_priv *wm8988)
memcpy(codec->reg_cache, wm8988_reg,
sizeof(wm8988_reg));
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
ret = wm8988_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset\n");
- return ret;
+ goto err;
}
/* set the update bits (we always update left then right) */
- reg = wm8988_read_reg_cache(codec, WM8988_RADC);
- wm8988_write(codec, WM8988_RADC, reg | 0x100);
- reg = wm8988_read_reg_cache(codec, WM8988_RDAC);
- wm8988_write(codec, WM8988_RDAC, reg | 0x0100);
- reg = wm8988_read_reg_cache(codec, WM8988_ROUT1V);
- wm8988_write(codec, WM8988_ROUT1V, reg | 0x0100);
- reg = wm8988_read_reg_cache(codec, WM8988_ROUT2V);
- wm8988_write(codec, WM8988_ROUT2V, reg | 0x0100);
- reg = wm8988_read_reg_cache(codec, WM8988_RINVOL);
- wm8988_write(codec, WM8988_RINVOL, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8988_RADC);
+ snd_soc_write(codec, WM8988_RADC, reg | 0x100);
+ reg = snd_soc_read(codec, WM8988_RDAC);
+ snd_soc_write(codec, WM8988_RDAC, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8988_ROUT1V);
+ snd_soc_write(codec, WM8988_ROUT1V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8988_ROUT2V);
+ snd_soc_write(codec, WM8988_ROUT2V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8988_RINVOL);
+ snd_soc_write(codec, WM8988_RINVOL, reg | 0x0100);
wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_STANDBY);
@@ -926,18 +888,20 @@ static int wm8988_register(struct wm8988_priv *wm8988)
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
- return ret;
+ goto err;
}
ret = snd_soc_register_dai(&wm8988_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
snd_soc_unregister_codec(codec);
- return ret;
+ goto err_codec;
}
return 0;
+err_codec:
+ snd_soc_unregister_codec(codec);
err:
kfree(wm8988);
return ret;
@@ -964,14 +928,13 @@ static int wm8988_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
codec = &wm8988->codec;
- codec->hw_write = (hw_write_t)i2c_master_send;
i2c_set_clientdata(i2c, wm8988);
codec->control_data = i2c;
codec->dev = &i2c->dev;
- return wm8988_register(wm8988);
+ return wm8988_register(wm8988, SND_SOC_I2C);
}
static int wm8988_i2c_remove(struct i2c_client *client)
@@ -981,6 +944,21 @@ static int wm8988_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM
+static int wm8988_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8988_i2c_resume(struct i2c_client *client)
+{
+ return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8988_i2c_suspend NULL
+#define wm8988_i2c_resume NULL
+#endif
+
static const struct i2c_device_id wm8988_i2c_id[] = {
{ "wm8988", 0 },
{ }
@@ -994,35 +972,13 @@ static struct i2c_driver wm8988_i2c_driver = {
},
.probe = wm8988_i2c_probe,
.remove = wm8988_i2c_remove,
+ .suspend = wm8988_i2c_suspend,
+ .resume = wm8988_i2c_resume,
.id_table = wm8988_i2c_id,
};
#endif
#if defined(CONFIG_SPI_MASTER)
-static int wm8988_spi_write(struct spi_device *spi, const char *data, int len)
-{
- struct spi_transfer t;
- struct spi_message m;
- u8 msg[2];
-
- if (len <= 0)
- return 0;
-
- msg[0] = data[0];
- msg[1] = data[1];
-
- spi_message_init(&m);
- memset(&t, 0, (sizeof t));
-
- t.tx_buf = &msg[0];
- t.len = len;
-
- spi_message_add_tail(&t, &m);
- spi_sync(spi, &m);
-
- return len;
-}
-
static int __devinit wm8988_spi_probe(struct spi_device *spi)
{
struct wm8988_priv *wm8988;
@@ -1033,13 +989,12 @@ static int __devinit wm8988_spi_probe(struct spi_device *spi)
return -ENOMEM;
codec = &wm8988->codec;
- codec->hw_write = (hw_write_t)wm8988_spi_write;
codec->control_data = spi;
codec->dev = &spi->dev;
dev_set_drvdata(&spi->dev, wm8988);
- return wm8988_register(wm8988);
+ return wm8988_register(wm8988, SND_SOC_SPI);
}
static int __devexit wm8988_spi_remove(struct spi_device *spi)
@@ -1051,6 +1006,21 @@ static int __devexit wm8988_spi_remove(struct spi_device *spi)
return 0;
}
+#ifdef CONFIG_PM
+static int wm8988_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&spi->dev);
+}
+
+static int wm8988_spi_resume(struct spi_device *spi)
+{
+ return snd_soc_resume_device(&spi->dev);
+}
+#else
+#define wm8988_spi_suspend NULL
+#define wm8988_spi_resume NULL
+#endif
+
static struct spi_driver wm8988_spi_driver = {
.driver = {
.name = "wm8988",
@@ -1059,6 +1029,8 @@ static struct spi_driver wm8988_spi_driver = {
},
.probe = wm8988_spi_probe,
.remove = __devexit_p(wm8988_spi_remove),
+ .suspend = wm8988_spi_suspend,
+ .resume = wm8988_spi_resume,
};
#endif
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index d029818350e..2d702db4131 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -108,53 +108,7 @@ static const u16 wm8990_reg[] = {
0x0000, /* R63 - Driver internal */
};
-/*
- * read wm8990 register cache
- */
-static inline unsigned int wm8990_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- BUG_ON(reg >= ARRAY_SIZE(wm8990_reg));
- return cache[reg];
-}
-
-/*
- * write wm8990 register cache
- */
-static inline void wm8990_write_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
-
- /* Reset register and reserved registers are uncached */
- if (reg == 0 || reg >= ARRAY_SIZE(wm8990_reg))
- return;
-
- cache[reg] = value;
-}
-
-/*
- * write to the wm8990 register space
- */
-static int wm8990_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[3];
-
- data[0] = reg & 0xFF;
- data[1] = (value >> 8) & 0xFF;
- data[2] = value & 0xFF;
-
- wm8990_write_reg_cache(codec, reg, value);
-
- if (codec->hw_write(codec->control_data, data, 3) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define wm8990_reset(c) wm8990_write(c, WM8990_RESET, 0)
+#define wm8990_reset(c) snd_soc_write(c, WM8990_RESET, 0)
static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
@@ -187,8 +141,8 @@ static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
return ret;
/* now hit the volume update bits (always bit 8) */
- val = wm8990_read_reg_cache(codec, reg);
- return wm8990_write(codec, reg, val | 0x0100);
+ val = snd_soc_read(codec, reg);
+ return snd_soc_write(codec, reg, val | 0x0100);
}
#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
@@ -427,8 +381,8 @@ static int inmixer_event(struct snd_soc_dapm_widget *w,
{
u16 reg, fakepower;
- reg = wm8990_read_reg_cache(w->codec, WM8990_POWER_MANAGEMENT_2);
- fakepower = wm8990_read_reg_cache(w->codec, WM8990_INTDRIVBITS);
+ reg = snd_soc_read(w->codec, WM8990_POWER_MANAGEMENT_2);
+ fakepower = snd_soc_read(w->codec, WM8990_INTDRIVBITS);
if (fakepower & ((1 << WM8990_INMIXL_PWR_BIT) |
(1 << WM8990_AINLMUX_PWR_BIT))) {
@@ -443,7 +397,7 @@ static int inmixer_event(struct snd_soc_dapm_widget *w,
} else {
reg &= ~WM8990_AINL_ENA;
}
- wm8990_write(w->codec, WM8990_POWER_MANAGEMENT_2, reg);
+ snd_soc_write(w->codec, WM8990_POWER_MANAGEMENT_2, reg);
return 0;
}
@@ -457,7 +411,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
switch (reg_shift) {
case WM8990_SPEAKER_MIXER | (WM8990_LDSPK_BIT << 8) :
- reg = wm8990_read_reg_cache(w->codec, WM8990_OUTPUT_MIXER1);
+ reg = snd_soc_read(w->codec, WM8990_OUTPUT_MIXER1);
if (reg & WM8990_LDLO) {
printk(KERN_WARNING
"Cannot set as Output Mixer 1 LDLO Set\n");
@@ -465,7 +419,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
}
break;
case WM8990_SPEAKER_MIXER | (WM8990_RDSPK_BIT << 8):
- reg = wm8990_read_reg_cache(w->codec, WM8990_OUTPUT_MIXER2);
+ reg = snd_soc_read(w->codec, WM8990_OUTPUT_MIXER2);
if (reg & WM8990_RDRO) {
printk(KERN_WARNING
"Cannot set as Output Mixer 2 RDRO Set\n");
@@ -473,7 +427,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
}
break;
case WM8990_OUTPUT_MIXER1 | (WM8990_LDLO_BIT << 8):
- reg = wm8990_read_reg_cache(w->codec, WM8990_SPEAKER_MIXER);
+ reg = snd_soc_read(w->codec, WM8990_SPEAKER_MIXER);
if (reg & WM8990_LDSPK) {
printk(KERN_WARNING
"Cannot set as Speaker Mixer LDSPK Set\n");
@@ -481,7 +435,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
}
break;
case WM8990_OUTPUT_MIXER2 | (WM8990_RDRO_BIT << 8):
- reg = wm8990_read_reg_cache(w->codec, WM8990_SPEAKER_MIXER);
+ reg = snd_soc_read(w->codec, WM8990_SPEAKER_MIXER);
if (reg & WM8990_RDSPK) {
printk(KERN_WARNING
"Cannot set as Speaker Mixer RDSPK Set\n");
@@ -1029,24 +983,24 @@ static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai,
pll_factors(&pll_div, freq_out * 4, freq_in);
/* Turn on PLL */
- reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+ reg = snd_soc_read(codec, WM8990_POWER_MANAGEMENT_2);
reg |= WM8990_PLL_ENA;
- wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
/* sysclk comes from PLL */
- reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2);
- wm8990_write(codec, WM8990_CLOCKING_2, reg | WM8990_SYSCLK_SRC);
+ reg = snd_soc_read(codec, WM8990_CLOCKING_2);
+ snd_soc_write(codec, WM8990_CLOCKING_2, reg | WM8990_SYSCLK_SRC);
/* set up N , fractional mode and pre-divisor if neccessary */
- wm8990_write(codec, WM8990_PLL1, pll_div.n | WM8990_SDM |
+ snd_soc_write(codec, WM8990_PLL1, pll_div.n | WM8990_SDM |
(pll_div.div2?WM8990_PRESCALE:0));
- wm8990_write(codec, WM8990_PLL2, (u8)(pll_div.k>>8));
- wm8990_write(codec, WM8990_PLL3, (u8)(pll_div.k & 0xFF));
+ snd_soc_write(codec, WM8990_PLL2, (u8)(pll_div.k>>8));
+ snd_soc_write(codec, WM8990_PLL3, (u8)(pll_div.k & 0xFF));
} else {
/* Turn on PLL */
- reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+ reg = snd_soc_read(codec, WM8990_POWER_MANAGEMENT_2);
reg &= ~WM8990_PLL_ENA;
- wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
}
return 0;
}
@@ -1073,8 +1027,8 @@ static int wm8990_set_dai_fmt(struct snd_soc_dai *codec_dai,
struct snd_soc_codec *codec = codec_dai->codec;
u16 audio1, audio3;
- audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
- audio3 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_3);
+ audio1 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_1);
+ audio3 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_3);
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -1115,8 +1069,8 @@ static int wm8990_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8990_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
- wm8990_write(codec, WM8990_AUDIO_INTERFACE_3, audio3);
+ snd_soc_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+ snd_soc_write(codec, WM8990_AUDIO_INTERFACE_3, audio3);
return 0;
}
@@ -1128,24 +1082,24 @@ static int wm8990_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
switch (div_id) {
case WM8990_MCLK_DIV:
- reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+ reg = snd_soc_read(codec, WM8990_CLOCKING_2) &
~WM8990_MCLK_DIV_MASK;
- wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+ snd_soc_write(codec, WM8990_CLOCKING_2, reg | div);
break;
case WM8990_DACCLK_DIV:
- reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+ reg = snd_soc_read(codec, WM8990_CLOCKING_2) &
~WM8990_DAC_CLKDIV_MASK;
- wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+ snd_soc_write(codec, WM8990_CLOCKING_2, reg | div);
break;
case WM8990_ADCCLK_DIV:
- reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+ reg = snd_soc_read(codec, WM8990_CLOCKING_2) &
~WM8990_ADC_CLKDIV_MASK;
- wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+ snd_soc_write(codec, WM8990_CLOCKING_2, reg | div);
break;
case WM8990_BCLK_DIV:
- reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_1) &
+ reg = snd_soc_read(codec, WM8990_CLOCKING_1) &
~WM8990_BCLK_DIV_MASK;
- wm8990_write(codec, WM8990_CLOCKING_1, reg | div);
+ snd_soc_write(codec, WM8990_CLOCKING_1, reg | div);
break;
default:
return -EINVAL;
@@ -1164,7 +1118,7 @@ static int wm8990_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- u16 audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
+ u16 audio1 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_1);
audio1 &= ~WM8990_AIF_WL_MASK;
/* bit size */
@@ -1182,7 +1136,7 @@ static int wm8990_hw_params(struct snd_pcm_substream *substream,
break;
}
- wm8990_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+ snd_soc_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
return 0;
}
@@ -1191,12 +1145,12 @@ static int wm8990_mute(struct snd_soc_dai *dai, int mute)
struct snd_soc_codec *codec = dai->codec;
u16 val;
- val = wm8990_read_reg_cache(codec, WM8990_DAC_CTRL) & ~WM8990_DAC_MUTE;
+ val = snd_soc_read(codec, WM8990_DAC_CTRL) & ~WM8990_DAC_MUTE;
if (mute)
- wm8990_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+ snd_soc_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
else
- wm8990_write(codec, WM8990_DAC_CTRL, val);
+ snd_soc_write(codec, WM8990_DAC_CTRL, val);
return 0;
}
@@ -1212,21 +1166,21 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VMID=2*50k */
- val = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_1) &
+ val = snd_soc_read(codec, WM8990_POWER_MANAGEMENT_1) &
~WM8990_VMID_MODE_MASK;
- wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x2);
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x2);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Enable all output discharge bits */
- wm8990_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+ snd_soc_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
WM8990_DIS_ROUT);
/* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
- wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
WM8990_BUFDCOPEN | WM8990_POBCTRL |
WM8990_VMIDTOG);
@@ -1234,83 +1188,83 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
msleep(msecs_to_jiffies(300));
/* Disable VMIDTOG */
- wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
WM8990_BUFDCOPEN | WM8990_POBCTRL);
/* disable all output discharge bits */
- wm8990_write(codec, WM8990_ANTIPOP1, 0);
+ snd_soc_write(codec, WM8990_ANTIPOP1, 0);
/* Enable outputs */
- wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1b00);
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1b00);
msleep(msecs_to_jiffies(50));
/* Enable VMID at 2x50k */
- wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f02);
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f02);
msleep(msecs_to_jiffies(100));
/* Enable VREF */
- wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
msleep(msecs_to_jiffies(600));
/* Enable BUFIOEN */
- wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
WM8990_BUFDCOPEN | WM8990_POBCTRL |
WM8990_BUFIOEN);
/* Disable outputs */
- wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x3);
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x3);
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
- wm8990_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
+ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
/* Enable workaround for ADC clocking issue. */
- wm8990_write(codec, WM8990_EXT_ACCESS_ENA, 0x2);
- wm8990_write(codec, WM8990_EXT_CTL1, 0xa003);
- wm8990_write(codec, WM8990_EXT_ACCESS_ENA, 0);
+ snd_soc_write(codec, WM8990_EXT_ACCESS_ENA, 0x2);
+ snd_soc_write(codec, WM8990_EXT_CTL1, 0xa003);
+ snd_soc_write(codec, WM8990_EXT_ACCESS_ENA, 0);
}
/* VMID=2*250k */
- val = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_1) &
+ val = snd_soc_read(codec, WM8990_POWER_MANAGEMENT_1) &
~WM8990_VMID_MODE_MASK;
- wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x4);
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x4);
break;
case SND_SOC_BIAS_OFF:
/* Enable POBCTRL and SOFT_ST */
- wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
WM8990_POBCTRL | WM8990_BUFIOEN);
/* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
- wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+ snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
WM8990_BUFDCOPEN | WM8990_POBCTRL |
WM8990_BUFIOEN);
/* mute DAC */
- val = wm8990_read_reg_cache(codec, WM8990_DAC_CTRL);
- wm8990_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+ val = snd_soc_read(codec, WM8990_DAC_CTRL);
+ snd_soc_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
/* Enable any disabled outputs */
- wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
/* Disable VMID */
- wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f01);
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f01);
msleep(msecs_to_jiffies(300));
/* Enable all output discharge bits */
- wm8990_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+ snd_soc_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
WM8990_DIS_ROUT);
/* Disable VREF */
- wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x0);
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x0);
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
- wm8990_write(codec, WM8990_ANTIPOP2, 0x0);
+ snd_soc_write(codec, WM8990_ANTIPOP2, 0x0);
break;
}
@@ -1411,8 +1365,6 @@ static int wm8990_init(struct snd_soc_device *socdev)
codec->name = "WM8990";
codec->owner = THIS_MODULE;
- codec->read = wm8990_read_reg_cache;
- codec->write = wm8990_write;
codec->set_bias_level = wm8990_set_bias_level;
codec->dai = &wm8990_dai;
codec->num_dai = 2;
@@ -1422,6 +1374,12 @@ static int wm8990_init(struct snd_soc_device *socdev)
if (codec->reg_cache == NULL)
return -ENOMEM;
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8990: failed to set cache I/O: %d\n", ret);
+ goto pcm_err;
+ }
+
wm8990_reset(codec);
/* register pcms */
@@ -1435,18 +1393,18 @@ static int wm8990_init(struct snd_soc_device *socdev)
codec->bias_level = SND_SOC_BIAS_OFF;
wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- reg = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_4);
- wm8990_write(codec, WM8990_AUDIO_INTERFACE_4, reg | WM8990_ALRCGPIO1);
+ reg = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_4);
+ snd_soc_write(codec, WM8990_AUDIO_INTERFACE_4, reg | WM8990_ALRCGPIO1);
- reg = wm8990_read_reg_cache(codec, WM8990_GPIO1_GPIO2) &
+ reg = snd_soc_read(codec, WM8990_GPIO1_GPIO2) &
~WM8990_GPIO1_SEL_MASK;
- wm8990_write(codec, WM8990_GPIO1_GPIO2, reg | 1);
+ snd_soc_write(codec, WM8990_GPIO1_GPIO2, reg | 1);
- reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
- wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg | WM8990_OPCLK_ENA);
+ reg = snd_soc_read(codec, WM8990_POWER_MANAGEMENT_2);
+ snd_soc_write(codec, WM8990_POWER_MANAGEMENT_2, reg | WM8990_OPCLK_ENA);
- wm8990_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
- wm8990_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+ snd_soc_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+ snd_soc_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
snd_soc_add_controls(codec, wm8990_snd_controls,
ARRAY_SIZE(wm8990_snd_controls));
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
new file mode 100644
index 00000000000..d9987999e92
--- /dev/null
+++ b/sound/soc/codecs/wm8993.c
@@ -0,0 +1,1675 @@
+/*
+ * wm8993.c -- WM8993 ALSA SoC audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/wm8993.h>
+
+#include "wm8993.h"
+#include "wm_hubs.h"
+
+static u16 wm8993_reg_defaults[WM8993_REGISTER_COUNT] = {
+ 0x8993, /* R0 - Software Reset */
+ 0x0000, /* R1 - Power Management (1) */
+ 0x6000, /* R2 - Power Management (2) */
+ 0x0000, /* R3 - Power Management (3) */
+ 0x4050, /* R4 - Audio Interface (1) */
+ 0x4000, /* R5 - Audio Interface (2) */
+ 0x01C8, /* R6 - Clocking 1 */
+ 0x0000, /* R7 - Clocking 2 */
+ 0x0000, /* R8 - Audio Interface (3) */
+ 0x0040, /* R9 - Audio Interface (4) */
+ 0x0004, /* R10 - DAC CTRL */
+ 0x00C0, /* R11 - Left DAC Digital Volume */
+ 0x00C0, /* R12 - Right DAC Digital Volume */
+ 0x0000, /* R13 - Digital Side Tone */
+ 0x0300, /* R14 - ADC CTRL */
+ 0x00C0, /* R15 - Left ADC Digital Volume */
+ 0x00C0, /* R16 - Right ADC Digital Volume */
+ 0x0000, /* R17 */
+ 0x0000, /* R18 - GPIO CTRL 1 */
+ 0x0010, /* R19 - GPIO1 */
+ 0x0000, /* R20 - IRQ_DEBOUNCE */
+ 0x0000, /* R21 */
+ 0x8000, /* R22 - GPIOCTRL 2 */
+ 0x0800, /* R23 - GPIO_POL */
+ 0x008B, /* R24 - Left Line Input 1&2 Volume */
+ 0x008B, /* R25 - Left Line Input 3&4 Volume */
+ 0x008B, /* R26 - Right Line Input 1&2 Volume */
+ 0x008B, /* R27 - Right Line Input 3&4 Volume */
+ 0x006D, /* R28 - Left Output Volume */
+ 0x006D, /* R29 - Right Output Volume */
+ 0x0066, /* R30 - Line Outputs Volume */
+ 0x0020, /* R31 - HPOUT2 Volume */
+ 0x0079, /* R32 - Left OPGA Volume */
+ 0x0079, /* R33 - Right OPGA Volume */
+ 0x0003, /* R34 - SPKMIXL Attenuation */
+ 0x0003, /* R35 - SPKMIXR Attenuation */
+ 0x0011, /* R36 - SPKOUT Mixers */
+ 0x0100, /* R37 - SPKOUT Boost */
+ 0x0079, /* R38 - Speaker Volume Left */
+ 0x0079, /* R39 - Speaker Volume Right */
+ 0x0000, /* R40 - Input Mixer2 */
+ 0x0000, /* R41 - Input Mixer3 */
+ 0x0000, /* R42 - Input Mixer4 */
+ 0x0000, /* R43 - Input Mixer5 */
+ 0x0000, /* R44 - Input Mixer6 */
+ 0x0000, /* R45 - Output Mixer1 */
+ 0x0000, /* R46 - Output Mixer2 */
+ 0x0000, /* R47 - Output Mixer3 */
+ 0x0000, /* R48 - Output Mixer4 */
+ 0x0000, /* R49 - Output Mixer5 */
+ 0x0000, /* R50 - Output Mixer6 */
+ 0x0000, /* R51 - HPOUT2 Mixer */
+ 0x0000, /* R52 - Line Mixer1 */
+ 0x0000, /* R53 - Line Mixer2 */
+ 0x0000, /* R54 - Speaker Mixer */
+ 0x0000, /* R55 - Additional Control */
+ 0x0000, /* R56 - AntiPOP1 */
+ 0x0000, /* R57 - AntiPOP2 */
+ 0x0000, /* R58 - MICBIAS */
+ 0x0000, /* R59 */
+ 0x0000, /* R60 - FLL Control 1 */
+ 0x0000, /* R61 - FLL Control 2 */
+ 0x0000, /* R62 - FLL Control 3 */
+ 0x2EE0, /* R63 - FLL Control 4 */
+ 0x0002, /* R64 - FLL Control 5 */
+ 0x2287, /* R65 - Clocking 3 */
+ 0x025F, /* R66 - Clocking 4 */
+ 0x0000, /* R67 - MW Slave Control */
+ 0x0000, /* R68 */
+ 0x0002, /* R69 - Bus Control 1 */
+ 0x0000, /* R70 - Write Sequencer 0 */
+ 0x0000, /* R71 - Write Sequencer 1 */
+ 0x0000, /* R72 - Write Sequencer 2 */
+ 0x0000, /* R73 - Write Sequencer 3 */
+ 0x0000, /* R74 - Write Sequencer 4 */
+ 0x0000, /* R75 - Write Sequencer 5 */
+ 0x1F25, /* R76 - Charge Pump 1 */
+ 0x0000, /* R77 */
+ 0x0000, /* R78 */
+ 0x0000, /* R79 */
+ 0x0000, /* R80 */
+ 0x0000, /* R81 - Class W 0 */
+ 0x0000, /* R82 */
+ 0x0000, /* R83 */
+ 0x0000, /* R84 - DC Servo 0 */
+ 0x054A, /* R85 - DC Servo 1 */
+ 0x0000, /* R86 */
+ 0x0000, /* R87 - DC Servo 3 */
+ 0x0000, /* R88 - DC Servo Readback 0 */
+ 0x0000, /* R89 - DC Servo Readback 1 */
+ 0x0000, /* R90 - DC Servo Readback 2 */
+ 0x0000, /* R91 */
+ 0x0000, /* R92 */
+ 0x0000, /* R93 */
+ 0x0000, /* R94 */
+ 0x0000, /* R95 */
+ 0x0100, /* R96 - Analogue HP 0 */
+ 0x0000, /* R97 */
+ 0x0000, /* R98 - EQ1 */
+ 0x000C, /* R99 - EQ2 */
+ 0x000C, /* R100 - EQ3 */
+ 0x000C, /* R101 - EQ4 */
+ 0x000C, /* R102 - EQ5 */
+ 0x000C, /* R103 - EQ6 */
+ 0x0FCA, /* R104 - EQ7 */
+ 0x0400, /* R105 - EQ8 */
+ 0x00D8, /* R106 - EQ9 */
+ 0x1EB5, /* R107 - EQ10 */
+ 0xF145, /* R108 - EQ11 */
+ 0x0B75, /* R109 - EQ12 */
+ 0x01C5, /* R110 - EQ13 */
+ 0x1C58, /* R111 - EQ14 */
+ 0xF373, /* R112 - EQ15 */
+ 0x0A54, /* R113 - EQ16 */
+ 0x0558, /* R114 - EQ17 */
+ 0x168E, /* R115 - EQ18 */
+ 0xF829, /* R116 - EQ19 */
+ 0x07AD, /* R117 - EQ20 */
+ 0x1103, /* R118 - EQ21 */
+ 0x0564, /* R119 - EQ22 */
+ 0x0559, /* R120 - EQ23 */
+ 0x4000, /* R121 - EQ24 */
+ 0x0000, /* R122 - Digital Pulls */
+ 0x0F08, /* R123 - DRC Control 1 */
+ 0x0000, /* R124 - DRC Control 2 */
+ 0x0080, /* R125 - DRC Control 3 */
+ 0x0000, /* R126 - DRC Control 4 */
+};
+
+static struct {
+ int ratio;
+ int clk_sys_rate;
+} clk_sys_rates[] = {
+ { 64, 0 },
+ { 128, 1 },
+ { 192, 2 },
+ { 256, 3 },
+ { 384, 4 },
+ { 512, 5 },
+ { 768, 6 },
+ { 1024, 7 },
+ { 1408, 8 },
+ { 1536, 9 },
+};
+
+static struct {
+ int rate;
+ int sample_rate;
+} sample_rates[] = {
+ { 8000, 0 },
+ { 11025, 1 },
+ { 12000, 1 },
+ { 16000, 2 },
+ { 22050, 3 },
+ { 24000, 3 },
+ { 32000, 4 },
+ { 44100, 5 },
+ { 48000, 5 },
+};
+
+static struct {
+ int div; /* *10 due to .5s */
+ int bclk_div;
+} bclk_divs[] = {
+ { 10, 0 },
+ { 15, 1 },
+ { 20, 2 },
+ { 30, 3 },
+ { 40, 4 },
+ { 55, 5 },
+ { 60, 6 },
+ { 80, 7 },
+ { 110, 8 },
+ { 120, 9 },
+ { 160, 10 },
+ { 220, 11 },
+ { 240, 12 },
+ { 320, 13 },
+ { 440, 14 },
+ { 480, 15 },
+};
+
+struct wm8993_priv {
+ u16 reg_cache[WM8993_REGISTER_COUNT];
+ struct wm8993_platform_data pdata;
+ struct snd_soc_codec codec;
+ int master;
+ int sysclk_source;
+ int tdm_slots;
+ int tdm_width;
+ unsigned int mclk_rate;
+ unsigned int sysclk_rate;
+ unsigned int fs;
+ unsigned int bclk;
+ int class_w_users;
+ unsigned int fll_fref;
+ unsigned int fll_fout;
+};
+
+static unsigned int wm8993_read_hw(struct snd_soc_codec *codec, u8 reg)
+{
+ struct i2c_msg xfer[2];
+ u16 data;
+ int ret;
+ struct i2c_client *i2c = codec->control_data;
+
+ /* Write register */
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 1;
+ xfer[0].buf = &reg;
+
+ /* Read data */
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = 2;
+ xfer[1].buf = (u8 *)&data;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret != 2) {
+ dev_err(codec->dev, "Failed to read 0x%x: %d\n", reg, ret);
+ return 0;
+ }
+
+ return (data >> 8) | ((data & 0xff) << 8);
+}
+
+static int wm8993_volatile(unsigned int reg)
+{
+ switch (reg) {
+ case WM8993_SOFTWARE_RESET:
+ case WM8993_DC_SERVO_0:
+ case WM8993_DC_SERVO_READBACK_0:
+ case WM8993_DC_SERVO_READBACK_1:
+ case WM8993_DC_SERVO_READBACK_2:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int wm8993_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *reg_cache = codec->reg_cache;
+
+ BUG_ON(reg > WM8993_MAX_REGISTER);
+
+ if (wm8993_volatile(reg))
+ return wm8993_read_hw(codec, reg);
+ else
+ return reg_cache[reg];
+}
+
+static int wm8993_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u16 *reg_cache = codec->reg_cache;
+ u8 data[3];
+ int ret;
+
+ BUG_ON(reg > WM8993_MAX_REGISTER);
+
+ /* data is
+ * D15..D9 WM8993 register offset
+ * D8...D0 register data
+ */
+ data[0] = reg;
+ data[1] = value >> 8;
+ data[2] = value & 0x00ff;
+
+ if (!wm8993_volatile(reg))
+ reg_cache[reg] = value;
+
+ ret = codec->hw_write(codec->control_data, data, 3);
+
+ if (ret == 3)
+ return 0;
+ if (ret < 0)
+ return ret;
+ return -EIO;
+}
+
+struct _fll_div {
+ u16 fll_fratio;
+ u16 fll_outdiv;
+ u16 fll_clk_ref_div;
+ u16 n;
+ u16 k;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static struct {
+ unsigned int min;
+ unsigned int max;
+ u16 fll_fratio;
+ int ratio;
+} fll_fratios[] = {
+ { 0, 64000, 4, 16 },
+ { 64000, 128000, 3, 8 },
+ { 128000, 256000, 2, 4 },
+ { 256000, 1000000, 1, 2 },
+ { 1000000, 13500000, 0, 1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+ unsigned int Fout)
+{
+ u64 Kpart;
+ unsigned int K, Ndiv, Nmod, target;
+ unsigned int div;
+ int i;
+
+ /* Fref must be <=13.5MHz */
+ div = 1;
+ fll_div->fll_clk_ref_div = 0;
+ while ((Fref / div) > 13500000) {
+ div *= 2;
+ fll_div->fll_clk_ref_div++;
+
+ if (div > 8) {
+ pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+ Fref);
+ return -EINVAL;
+ }
+ }
+
+ pr_debug("Fref=%u Fout=%u\n", Fref, Fout);
+
+ /* Apply the division for our remaining calculations */
+ Fref /= div;
+
+ /* Fvco should be 90-100MHz; don't check the upper bound */
+ div = 0;
+ target = Fout * 2;
+ while (target < 90000000) {
+ div++;
+ target *= 2;
+ if (div > 7) {
+ pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+ Fout);
+ return -EINVAL;
+ }
+ }
+ fll_div->fll_outdiv = div;
+
+ pr_debug("Fvco=%dHz\n", target);
+
+ /* Find an appropraite FLL_FRATIO and factor it out of the target */
+ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+ if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+ fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+ target /= fll_fratios[i].ratio;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(fll_fratios)) {
+ pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+ return -EINVAL;
+ }
+
+ /* Now, calculate N.K */
+ Ndiv = target / Fref;
+
+ fll_div->n = Ndiv;
+ Nmod = target % Fref;
+ pr_debug("Nmod=%d\n", Nmod);
+
+ /* Calculate fractional part - scale up so we can round. */
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+ do_div(Kpart, Fref);
+
+ K = Kpart & 0xFFFFFFFF;
+
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ fll_div->k = K / 10;
+
+ pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n",
+ fll_div->n, fll_div->k,
+ fll_div->fll_fratio, fll_div->fll_outdiv,
+ fll_div->fll_clk_ref_div);
+
+ return 0;
+}
+
+static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id,
+ unsigned int Fref, unsigned int Fout)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8993_priv *wm8993 = codec->private_data;
+ u16 reg1, reg4, reg5;
+ struct _fll_div fll_div;
+ int ret;
+
+ /* Any change? */
+ if (Fref == wm8993->fll_fref && Fout == wm8993->fll_fout)
+ return 0;
+
+ /* Disable the FLL */
+ if (Fout == 0) {
+ dev_dbg(codec->dev, "FLL disabled\n");
+ wm8993->fll_fref = 0;
+ wm8993->fll_fout = 0;
+
+ reg1 = wm8993_read(codec, WM8993_FLL_CONTROL_1);
+ reg1 &= ~WM8993_FLL_ENA;
+ wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+
+ return 0;
+ }
+
+ ret = fll_factors(&fll_div, Fref, Fout);
+ if (ret != 0)
+ return ret;
+
+ reg5 = wm8993_read(codec, WM8993_FLL_CONTROL_5);
+ reg5 &= ~WM8993_FLL_CLK_SRC_MASK;
+
+ switch (fll_id) {
+ case WM8993_FLL_MCLK:
+ break;
+
+ case WM8993_FLL_LRCLK:
+ reg5 |= 1;
+ break;
+
+ case WM8993_FLL_BCLK:
+ reg5 |= 2;
+ break;
+
+ default:
+ dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
+ return -EINVAL;
+ }
+
+ /* Any FLL configuration change requires that the FLL be
+ * disabled first. */
+ reg1 = wm8993_read(codec, WM8993_FLL_CONTROL_1);
+ reg1 &= ~WM8993_FLL_ENA;
+ wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+
+ /* Apply the configuration */
+ if (fll_div.k)
+ reg1 |= WM8993_FLL_FRAC_MASK;
+ else
+ reg1 &= ~WM8993_FLL_FRAC_MASK;
+ wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+
+ wm8993_write(codec, WM8993_FLL_CONTROL_2,
+ (fll_div.fll_outdiv << WM8993_FLL_OUTDIV_SHIFT) |
+ (fll_div.fll_fratio << WM8993_FLL_FRATIO_SHIFT));
+ wm8993_write(codec, WM8993_FLL_CONTROL_3, fll_div.k);
+
+ reg4 = wm8993_read(codec, WM8993_FLL_CONTROL_4);
+ reg4 &= ~WM8993_FLL_N_MASK;
+ reg4 |= fll_div.n << WM8993_FLL_N_SHIFT;
+ wm8993_write(codec, WM8993_FLL_CONTROL_4, reg4);
+
+ reg5 &= ~WM8993_FLL_CLK_REF_DIV_MASK;
+ reg5 |= fll_div.fll_clk_ref_div << WM8993_FLL_CLK_REF_DIV_SHIFT;
+ wm8993_write(codec, WM8993_FLL_CONTROL_5, reg5);
+
+ /* Enable the FLL */
+ wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA);
+
+ dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
+
+ wm8993->fll_fref = Fref;
+ wm8993->fll_fout = Fout;
+
+ return 0;
+}
+
+static int configure_clock(struct snd_soc_codec *codec)
+{
+ struct wm8993_priv *wm8993 = codec->private_data;
+ unsigned int reg;
+
+ /* This should be done on init() for bypass paths */
+ switch (wm8993->sysclk_source) {
+ case WM8993_SYSCLK_MCLK:
+ dev_dbg(codec->dev, "Using %dHz MCLK\n", wm8993->mclk_rate);
+
+ reg = wm8993_read(codec, WM8993_CLOCKING_2);
+ reg &= ~(WM8993_MCLK_DIV | WM8993_SYSCLK_SRC);
+ if (wm8993->mclk_rate > 13500000) {
+ reg |= WM8993_MCLK_DIV;
+ wm8993->sysclk_rate = wm8993->mclk_rate / 2;
+ } else {
+ reg &= ~WM8993_MCLK_DIV;
+ wm8993->sysclk_rate = wm8993->mclk_rate;
+ }
+ wm8993_write(codec, WM8993_CLOCKING_2, reg);
+ break;
+
+ case WM8993_SYSCLK_FLL:
+ dev_dbg(codec->dev, "Using %dHz FLL clock\n",
+ wm8993->fll_fout);
+
+ reg = wm8993_read(codec, WM8993_CLOCKING_2);
+ reg |= WM8993_SYSCLK_SRC;
+ if (wm8993->fll_fout > 13500000) {
+ reg |= WM8993_MCLK_DIV;
+ wm8993->sysclk_rate = wm8993->fll_fout / 2;
+ } else {
+ reg &= ~WM8993_MCLK_DIV;
+ wm8993->sysclk_rate = wm8993->fll_fout;
+ }
+ wm8993_write(codec, WM8993_CLOCKING_2, reg);
+ break;
+
+ default:
+ dev_err(codec->dev, "System clock not configured\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm8993->sysclk_rate);
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(drc_comp_threash, -4500, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_comp_amp, -2250, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0);
+static const unsigned int drc_max_tlv[] = {
+ TLV_DB_RANGE_HEAD(4),
+ 0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -1800, 300, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+
+static const char *dac_deemph_text[] = {
+ "None",
+ "32kHz",
+ "44.1kHz",
+ "48kHz",
+};
+
+static const struct soc_enum dac_deemph =
+ SOC_ENUM_SINGLE(WM8993_DAC_CTRL, 4, 4, dac_deemph_text);
+
+static const char *adc_hpf_text[] = {
+ "Hi-Fi",
+ "Voice 1",
+ "Voice 2",
+ "Voice 3",
+};
+
+static const struct soc_enum adc_hpf =
+ SOC_ENUM_SINGLE(WM8993_ADC_CTRL, 5, 4, adc_hpf_text);
+
+static const char *drc_path_text[] = {
+ "ADC",
+ "DAC"
+};
+
+static const struct soc_enum drc_path =
+ SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_1, 14, 2, drc_path_text);
+
+static const char *drc_r0_text[] = {
+ "1",
+ "1/2",
+ "1/4",
+ "1/8",
+ "1/16",
+ "0",
+};
+
+static const struct soc_enum drc_r0 =
+ SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 8, 6, drc_r0_text);
+
+static const char *drc_r1_text[] = {
+ "1",
+ "1/2",
+ "1/4",
+ "1/8",
+ "0",
+};
+
+static const struct soc_enum drc_r1 =
+ SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_4, 13, 5, drc_r1_text);
+
+static const char *drc_attack_text[] = {
+ "Reserved",
+ "181us",
+ "363us",
+ "726us",
+ "1.45ms",
+ "2.9ms",
+ "5.8ms",
+ "11.6ms",
+ "23.2ms",
+ "46.4ms",
+ "92.8ms",
+ "185.6ms",
+};
+
+static const struct soc_enum drc_attack =
+ SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_2, 12, 12, drc_attack_text);
+
+static const char *drc_decay_text[] = {
+ "186ms",
+ "372ms",
+ "743ms",
+ "1.49s",
+ "2.97ms",
+ "5.94ms",
+ "11.89ms",
+ "23.78ms",
+ "47.56ms",
+};
+
+static const struct soc_enum drc_decay =
+ SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_2, 8, 9, drc_decay_text);
+
+static const char *drc_ff_text[] = {
+ "5 samples",
+ "9 samples",
+};
+
+static const struct soc_enum drc_ff =
+ SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 7, 2, drc_ff_text);
+
+static const char *drc_qr_rate_text[] = {
+ "0.725ms",
+ "1.45ms",
+ "5.8ms",
+};
+
+static const struct soc_enum drc_qr_rate =
+ SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 0, 3, drc_qr_rate_text);
+
+static const char *drc_smooth_text[] = {
+ "Low",
+ "Medium",
+ "High",
+};
+
+static const struct soc_enum drc_smooth =
+ SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_1, 4, 3, drc_smooth_text);
+
+static const struct snd_kcontrol_new wm8993_snd_controls[] = {
+SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8993_DIGITAL_SIDE_TONE,
+ 5, 9, 12, 0, sidetone_tlv),
+
+SOC_SINGLE("DRC Switch", WM8993_DRC_CONTROL_1, 15, 1, 0),
+SOC_ENUM("DRC Path", drc_path),
+SOC_SINGLE_TLV("DRC Compressor Threashold Volume", WM8993_DRC_CONTROL_2,
+ 2, 60, 1, drc_comp_threash),
+SOC_SINGLE_TLV("DRC Compressor Amplitude Volume", WM8993_DRC_CONTROL_3,
+ 11, 30, 1, drc_comp_amp),
+SOC_ENUM("DRC R0", drc_r0),
+SOC_ENUM("DRC R1", drc_r1),
+SOC_SINGLE_TLV("DRC Minimum Volume", WM8993_DRC_CONTROL_1, 2, 3, 1,
+ drc_min_tlv),
+SOC_SINGLE_TLV("DRC Maximum Volume", WM8993_DRC_CONTROL_1, 0, 3, 0,
+ drc_max_tlv),
+SOC_ENUM("DRC Attack Rate", drc_attack),
+SOC_ENUM("DRC Decay Rate", drc_decay),
+SOC_ENUM("DRC FF Delay", drc_ff),
+SOC_SINGLE("DRC Anti-clip Switch", WM8993_DRC_CONTROL_1, 9, 1, 0),
+SOC_SINGLE("DRC Quick Release Switch", WM8993_DRC_CONTROL_1, 10, 1, 0),
+SOC_SINGLE_TLV("DRC Quick Release Volume", WM8993_DRC_CONTROL_3, 2, 3, 0,
+ drc_qr_tlv),
+SOC_ENUM("DRC Quick Release Rate", drc_qr_rate),
+SOC_SINGLE("DRC Smoothing Switch", WM8993_DRC_CONTROL_1, 11, 1, 0),
+SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8993_DRC_CONTROL_1, 8, 1, 0),
+SOC_ENUM("DRC Smoothing Hysteresis Threashold", drc_smooth),
+SOC_SINGLE_TLV("DRC Startup Volume", WM8993_DRC_CONTROL_4, 8, 18, 0,
+ drc_startup_tlv),
+
+SOC_SINGLE("EQ Switch", WM8993_EQ1, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("Capture Volume", WM8993_LEFT_ADC_DIGITAL_VOLUME,
+ WM8993_RIGHT_ADC_DIGITAL_VOLUME, 1, 96, 0, digital_tlv),
+SOC_SINGLE("ADC High Pass Filter Switch", WM8993_ADC_CTRL, 8, 1, 0),
+SOC_ENUM("ADC High Pass Filter Mode", adc_hpf),
+
+SOC_DOUBLE_R_TLV("Playback Volume", WM8993_LEFT_DAC_DIGITAL_VOLUME,
+ WM8993_RIGHT_DAC_DIGITAL_VOLUME, 1, 96, 0, digital_tlv),
+SOC_SINGLE_TLV("Playback Boost Volume", WM8993_AUDIO_INTERFACE_2, 10, 3, 0,
+ dac_boost_tlv),
+SOC_ENUM("DAC Deemphasis", dac_deemph),
+
+SOC_SINGLE_TLV("SPKL DAC Volume", WM8993_SPKMIXL_ATTENUATION,
+ 2, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_SINGLE_TLV("SPKR DAC Volume", WM8993_SPKMIXR_ATTENUATION,
+ 2, 1, 1, wm_hubs_spkmix_tlv),
+};
+
+static const struct snd_kcontrol_new wm8993_eq_controls[] = {
+SOC_SINGLE_TLV("EQ1 Volume", WM8993_EQ2, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Volume", WM8993_EQ3, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Volume", WM8993_EQ4, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Volume", WM8993_EQ5, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ5 Volume", WM8993_EQ6, 0, 24, 0, eq_tlv),
+};
+
+static int clk_sys_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return configure_clock(codec);
+
+ case SND_SOC_DAPM_POST_PMD:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * When used with DAC outputs only the WM8993 charge pump supports
+ * operation in class W mode, providing very low power consumption
+ * when used with digital sources. Enable and disable this mode
+ * automatically depending on the mixer configuration.
+ *
+ * Currently the only supported paths are the direct DAC->headphone
+ * paths (which provide minimum power consumption anyway).
+ */
+static int class_w_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = widget->codec;
+ struct wm8993_priv *wm8993 = codec->private_data;
+ int ret;
+
+ /* Turn it off if we're using the main output mixer */
+ if (ucontrol->value.integer.value[0] == 0) {
+ if (wm8993->class_w_users == 0) {
+ dev_dbg(codec->dev, "Disabling Class W\n");
+ snd_soc_update_bits(codec, WM8993_CLASS_W_0,
+ WM8993_CP_DYN_FREQ |
+ WM8993_CP_DYN_V,
+ 0);
+ }
+ wm8993->class_w_users++;
+ }
+
+ /* Implement the change */
+ ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ /* Enable it if we're using the direct DAC path */
+ if (ucontrol->value.integer.value[0] == 1) {
+ if (wm8993->class_w_users == 1) {
+ dev_dbg(codec->dev, "Enabling Class W\n");
+ snd_soc_update_bits(codec, WM8993_CLASS_W_0,
+ WM8993_CP_DYN_FREQ |
+ WM8993_CP_DYN_V,
+ WM8993_CP_DYN_FREQ |
+ WM8993_CP_DYN_V);
+ }
+ wm8993->class_w_users--;
+ }
+
+ dev_dbg(codec->dev, "Indirect DAC use count now %d\n",
+ wm8993->class_w_users);
+
+ return ret;
+}
+
+#define SOC_DAPM_ENUM_W(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_enum_double, \
+ .get = snd_soc_dapm_get_enum_double, \
+ .put = class_w_put, \
+ .private_value = (unsigned long)&xenum }
+
+static const char *hp_mux_text[] = {
+ "Mixer",
+ "DAC",
+};
+
+static const struct soc_enum hpl_enum =
+ SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text);
+
+static const struct snd_kcontrol_new hpl_mux =
+ SOC_DAPM_ENUM_W("Left Headphone Mux", hpl_enum);
+
+static const struct soc_enum hpr_enum =
+ SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text);
+
+static const struct snd_kcontrol_new hpr_mux =
+ SOC_DAPM_ENUM_W("Right Headphone Mux", hpr_enum);
+
+static const struct snd_kcontrol_new left_speaker_mixer[] = {
+SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0),
+SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 3, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_mixer[] = {
+SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),
+SOC_DAPM_SINGLE("IN1RP Switch", WM8993_SPEAKER_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 0, 1, 0),
+};
+
+static const char *aif_text[] = {
+ "Left", "Right"
+};
+
+static const struct soc_enum aifoutl_enum =
+ SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_1, 15, 2, aif_text);
+
+static const struct snd_kcontrol_new aifoutl_mux =
+ SOC_DAPM_ENUM("AIFOUTL Mux", aifoutl_enum);
+
+static const struct soc_enum aifoutr_enum =
+ SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_1, 14, 2, aif_text);
+
+static const struct snd_kcontrol_new aifoutr_mux =
+ SOC_DAPM_ENUM("AIFOUTR Mux", aifoutr_enum);
+
+static const struct soc_enum aifinl_enum =
+ SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_2, 15, 2, aif_text);
+
+static const struct snd_kcontrol_new aifinl_mux =
+ SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum);
+
+static const struct soc_enum aifinr_enum =
+ SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_2, 14, 2, aif_text);
+
+static const struct snd_kcontrol_new aifinr_mux =
+ SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum);
+
+static const char *sidetone_text[] = {
+ "None", "Left", "Right"
+};
+
+static const struct soc_enum sidetonel_enum =
+ SOC_ENUM_SINGLE(WM8993_DIGITAL_SIDE_TONE, 2, 3, sidetone_text);
+
+static const struct snd_kcontrol_new sidetonel_mux =
+ SOC_DAPM_ENUM("Left Sidetone", sidetonel_enum);
+
+static const struct soc_enum sidetoner_enum =
+ SOC_ENUM_SINGLE(WM8993_DIGITAL_SIDE_TONE, 0, 3, sidetone_text);
+
+static const struct snd_kcontrol_new sidetoner_mux =
+ SOC_DAPM_ENUM("Right Sidetone", sidetoner_enum);
+
+static const struct snd_soc_dapm_widget wm8993_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8993_BUS_CONTROL_1, 1, 0, clk_sys_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("TOCLK", WM8993_CLOCKING_1, 14, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8993_CLOCKING_3, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8993_POWER_MANAGEMENT_2, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8993_POWER_MANAGEMENT_2, 0, 0),
+
+SND_SOC_DAPM_MUX("AIFOUTL Mux", SND_SOC_NOPM, 0, 0, &aifoutl_mux),
+SND_SOC_DAPM_MUX("AIFOUTR Mux", SND_SOC_NOPM, 0, 0, &aifoutr_mux),
+
+SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &aifinl_mux),
+SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &aifinr_mux),
+
+SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &sidetonel_mux),
+SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &sidetoner_mux),
+
+SND_SOC_DAPM_DAC("DACL", NULL, WM8993_POWER_MANAGEMENT_3, 1, 0),
+SND_SOC_DAPM_DAC("DACR", NULL, WM8993_POWER_MANAGEMENT_3, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+
+SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0,
+ left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
+SND_SOC_DAPM_MIXER("SPKR", WM8993_POWER_MANAGEMENT_3, 9, 0,
+ right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
+
+};
+
+static const struct snd_soc_dapm_route routes[] = {
+ { "ADCL", NULL, "CLK_SYS" },
+ { "ADCL", NULL, "CLK_DSP" },
+ { "ADCR", NULL, "CLK_SYS" },
+ { "ADCR", NULL, "CLK_DSP" },
+
+ { "AIFOUTL Mux", "Left", "ADCL" },
+ { "AIFOUTL Mux", "Right", "ADCR" },
+ { "AIFOUTR Mux", "Left", "ADCL" },
+ { "AIFOUTR Mux", "Right", "ADCR" },
+
+ { "AIFOUTL", NULL, "AIFOUTL Mux" },
+ { "AIFOUTR", NULL, "AIFOUTR Mux" },
+
+ { "DACL Mux", "Left", "AIFINL" },
+ { "DACL Mux", "Right", "AIFINR" },
+ { "DACR Mux", "Left", "AIFINL" },
+ { "DACR Mux", "Right", "AIFINR" },
+
+ { "DACL Sidetone", "Left", "ADCL" },
+ { "DACL Sidetone", "Right", "ADCR" },
+ { "DACR Sidetone", "Left", "ADCL" },
+ { "DACR Sidetone", "Right", "ADCR" },
+
+ { "DACL", NULL, "CLK_SYS" },
+ { "DACL", NULL, "CLK_DSP" },
+ { "DACL", NULL, "DACL Mux" },
+ { "DACL", NULL, "DACL Sidetone" },
+ { "DACR", NULL, "CLK_SYS" },
+ { "DACR", NULL, "CLK_DSP" },
+ { "DACR", NULL, "DACR Mux" },
+ { "DACR", NULL, "DACR Sidetone" },
+
+ { "Left Output Mixer", "DAC Switch", "DACL" },
+
+ { "Right Output Mixer", "DAC Switch", "DACR" },
+
+ { "Left Output PGA", NULL, "CLK_SYS" },
+
+ { "Right Output PGA", NULL, "CLK_SYS" },
+
+ { "SPKL", "DAC Switch", "DACL" },
+ { "SPKL", NULL, "CLK_SYS" },
+
+ { "SPKR", "DAC Switch", "DACR" },
+ { "SPKR", NULL, "CLK_SYS" },
+
+ { "Left Headphone Mux", "DAC", "DACL" },
+ { "Right Headphone Mux", "DAC", "DACR" },
+};
+
+static int wm8993_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8993_priv *wm8993 = codec->private_data;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ /* VMID=2*40k */
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_VMID_SEL_MASK, 0x2);
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_2,
+ WM8993_TSHUT_ENA, WM8993_TSHUT_ENA);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Bring up VMID with fast soft start */
+ snd_soc_update_bits(codec, WM8993_ANTIPOP2,
+ WM8993_STARTUP_BIAS_ENA |
+ WM8993_VMID_BUF_ENA |
+ WM8993_VMID_RAMP_MASK |
+ WM8993_BIAS_SRC,
+ WM8993_STARTUP_BIAS_ENA |
+ WM8993_VMID_BUF_ENA |
+ WM8993_VMID_RAMP_MASK |
+ WM8993_BIAS_SRC);
+
+ /* If either line output is single ended we
+ * need the VMID buffer */
+ if (!wm8993->pdata.lineout1_diff ||
+ !wm8993->pdata.lineout2_diff)
+ snd_soc_update_bits(codec, WM8993_ANTIPOP1,
+ WM8993_LINEOUT_VMID_BUF_ENA,
+ WM8993_LINEOUT_VMID_BUF_ENA);
+
+ /* VMID=2*40k */
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_VMID_SEL_MASK |
+ WM8993_BIAS_ENA,
+ WM8993_BIAS_ENA | 0x2);
+ msleep(32);
+
+ /* Switch to normal bias */
+ snd_soc_update_bits(codec, WM8993_ANTIPOP2,
+ WM8993_BIAS_SRC |
+ WM8993_STARTUP_BIAS_ENA, 0);
+ }
+
+ /* VMID=2*240k */
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_VMID_SEL_MASK, 0x4);
+
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_2,
+ WM8993_TSHUT_ENA, 0);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, WM8993_ANTIPOP1,
+ WM8993_LINEOUT_VMID_BUF_ENA, 0);
+
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_VMID_SEL_MASK | WM8993_BIAS_ENA,
+ 0);
+ break;
+ }
+
+ codec->bias_level = level;
+
+ return 0;
+}
+
+static int wm8993_set_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8993_priv *wm8993 = codec->private_data;
+
+ switch (clk_id) {
+ case WM8993_SYSCLK_MCLK:
+ wm8993->mclk_rate = freq;
+ case WM8993_SYSCLK_FLL:
+ wm8993->sysclk_source = clk_id;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8993_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8993_priv *wm8993 = codec->private_data;
+ unsigned int aif1 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_1);
+ unsigned int aif4 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_4);
+
+ aif1 &= ~(WM8993_BCLK_DIR | WM8993_AIF_BCLK_INV |
+ WM8993_AIF_LRCLK_INV | WM8993_AIF_FMT_MASK);
+ aif4 &= ~WM8993_LRCLK_DIR;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ wm8993->master = 0;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ aif4 |= WM8993_LRCLK_DIR;
+ wm8993->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ aif1 |= WM8993_BCLK_DIR;
+ wm8993->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aif1 |= WM8993_BCLK_DIR;
+ aif4 |= WM8993_LRCLK_DIR;
+ wm8993->master = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_B:
+ aif1 |= WM8993_AIF_LRCLK_INV;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif1 |= 0x18;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ aif1 |= 0x10;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ aif1 |= 0x8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ /* frame inversion not valid for DSP modes */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8993_AIF_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ aif1 |= WM8993_AIF_BCLK_INV | WM8993_AIF_LRCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aif1 |= WM8993_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ aif1 |= WM8993_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8993_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
+ wm8993_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
+
+ return 0;
+}
+
+static int wm8993_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 wm8993_priv *wm8993 = codec->private_data;
+ int ret, i, best, best_val, cur_val;
+ unsigned int clocking1, clocking3, aif1, aif4;
+
+ clocking1 = wm8993_read(codec, WM8993_CLOCKING_1);
+ clocking1 &= ~WM8993_BCLK_DIV_MASK;
+
+ clocking3 = wm8993_read(codec, WM8993_CLOCKING_3);
+ clocking3 &= ~(WM8993_CLK_SYS_RATE_MASK | WM8993_SAMPLE_RATE_MASK);
+
+ aif1 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_1);
+ aif1 &= ~WM8993_AIF_WL_MASK;
+
+ aif4 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_4);
+ aif4 &= ~WM8993_LRCLK_RATE_MASK;
+
+ /* What BCLK do we need? */
+ wm8993->fs = params_rate(params);
+ wm8993->bclk = 2 * wm8993->fs;
+ if (wm8993->tdm_slots) {
+ dev_dbg(codec->dev, "Configuring for %d %d bit TDM slots\n",
+ wm8993->tdm_slots, wm8993->tdm_width);
+ wm8993->bclk *= wm8993->tdm_width * wm8993->tdm_slots;
+ } else {
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ wm8993->bclk *= 16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ wm8993->bclk *= 20;
+ aif1 |= 0x8;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ wm8993->bclk *= 24;
+ aif1 |= 0x10;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ wm8993->bclk *= 32;
+ aif1 |= 0x18;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8993->bclk);
+
+ ret = configure_clock(codec);
+ if (ret != 0)
+ return ret;
+
+ /* Select nearest CLK_SYS_RATE */
+ best = 0;
+ best_val = abs((wm8993->sysclk_rate / clk_sys_rates[0].ratio)
+ - wm8993->fs);
+ for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) {
+ cur_val = abs((wm8993->sysclk_rate /
+ clk_sys_rates[i].ratio) - wm8993->fs);;
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+ dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n",
+ clk_sys_rates[best].ratio);
+ clocking3 |= (clk_sys_rates[best].clk_sys_rate
+ << WM8993_CLK_SYS_RATE_SHIFT);
+
+ /* SAMPLE_RATE */
+ best = 0;
+ best_val = abs(wm8993->fs - sample_rates[0].rate);
+ for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+ /* Closest match */
+ cur_val = abs(wm8993->fs - sample_rates[i].rate);
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+ dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n",
+ sample_rates[best].rate);
+ clocking3 |= (sample_rates[best].sample_rate
+ << WM8993_SAMPLE_RATE_SHIFT);
+
+ /* BCLK_DIV */
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ cur_val = ((wm8993->sysclk_rate * 10) / bclk_divs[i].div)
+ - wm8993->bclk;
+ if (cur_val < 0) /* Table is sorted */
+ break;
+ if (cur_val < best_val) {
+ best = i;
+ best_val = cur_val;
+ }
+ }
+ wm8993->bclk = (wm8993->sysclk_rate * 10) / bclk_divs[best].div;
+ dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
+ bclk_divs[best].div, wm8993->bclk);
+ clocking1 |= bclk_divs[best].bclk_div << WM8993_BCLK_DIV_SHIFT;
+
+ /* LRCLK is a simple fraction of BCLK */
+ dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm8993->bclk / wm8993->fs);
+ aif4 |= wm8993->bclk / wm8993->fs;
+
+ wm8993_write(codec, WM8993_CLOCKING_1, clocking1);
+ wm8993_write(codec, WM8993_CLOCKING_3, clocking3);
+ wm8993_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
+ wm8993_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
+
+ /* ReTune Mobile? */
+ if (wm8993->pdata.num_retune_configs) {
+ u16 eq1 = wm8993_read(codec, WM8993_EQ1);
+ struct wm8993_retune_mobile_setting *s;
+
+ best = 0;
+ best_val = abs(wm8993->pdata.retune_configs[0].rate
+ - wm8993->fs);
+ for (i = 0; i < wm8993->pdata.num_retune_configs; i++) {
+ cur_val = abs(wm8993->pdata.retune_configs[i].rate
+ - wm8993->fs);
+ if (cur_val < best_val) {
+ best_val = cur_val;
+ best = i;
+ }
+ }
+ s = &wm8993->pdata.retune_configs[best];
+
+ dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n",
+ s->name, s->rate);
+
+ /* Disable EQ while we reconfigure */
+ snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, 0);
+
+ for (i = 1; i < ARRAY_SIZE(s->config); i++)
+ wm8993_write(codec, WM8993_EQ1 + i, s->config[i]);
+
+ snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, eq1);
+ }
+
+ return 0;
+}
+
+static int wm8993_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int reg;
+
+ reg = wm8993_read(codec, WM8993_DAC_CTRL);
+
+ if (mute)
+ reg |= WM8993_DAC_MUTE;
+ else
+ reg &= ~WM8993_DAC_MUTE;
+
+ wm8993_write(codec, WM8993_DAC_CTRL, reg);
+
+ return 0;
+}
+
+static int wm8993_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8993_priv *wm8993 = codec->private_data;
+ int aif1 = 0;
+ int aif2 = 0;
+
+ /* Don't need to validate anything if we're turning off TDM */
+ if (slots == 0) {
+ wm8993->tdm_slots = 0;
+ goto out;
+ }
+
+ /* Note that we allow configurations we can't handle ourselves -
+ * for example, we can generate clocks for slots 2 and up even if
+ * we can't use those slots ourselves.
+ */
+ aif1 |= WM8993_AIFADC_TDM;
+ aif2 |= WM8993_AIFDAC_TDM;
+
+ switch (rx_mask) {
+ case 3:
+ break;
+ case 0xc:
+ aif1 |= WM8993_AIFADC_TDM_CHAN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ switch (tx_mask) {
+ case 3:
+ break;
+ case 0xc:
+ aif2 |= WM8993_AIFDAC_TDM_CHAN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+out:
+ wm8993->tdm_width = slot_width;
+ wm8993->tdm_slots = slots / 2;
+
+ snd_soc_update_bits(codec, WM8993_AUDIO_INTERFACE_1,
+ WM8993_AIFADC_TDM | WM8993_AIFADC_TDM_CHAN, aif1);
+ snd_soc_update_bits(codec, WM8993_AUDIO_INTERFACE_2,
+ WM8993_AIFDAC_TDM | WM8993_AIFDAC_TDM_CHAN, aif2);
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops wm8993_ops = {
+ .set_sysclk = wm8993_set_sysclk,
+ .set_fmt = wm8993_set_dai_fmt,
+ .hw_params = wm8993_hw_params,
+ .digital_mute = wm8993_digital_mute,
+ .set_pll = wm8993_set_fll,
+ .set_tdm_slot = wm8993_set_tdm_slot,
+};
+
+#define WM8993_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8993_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai wm8993_dai = {
+ .name = "WM8993",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8993_RATES,
+ .formats = WM8993_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8993_RATES,
+ .formats = WM8993_FORMATS,
+ },
+ .ops = &wm8993_ops,
+ .symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(wm8993_dai);
+
+static struct snd_soc_codec *wm8993_codec;
+
+static int wm8993_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct wm8993_priv *wm8993;
+ int ret = 0;
+
+ if (!wm8993_codec) {
+ dev_err(&pdev->dev, "I2C device not yet probed\n");
+ goto err;
+ }
+
+ socdev->card->codec = wm8993_codec;
+ codec = wm8993_codec;
+ wm8993 = codec->private_data;
+
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to create pcms\n");
+ goto err;
+ }
+
+ snd_soc_add_controls(codec, wm8993_snd_controls,
+ ARRAY_SIZE(wm8993_snd_controls));
+ if (wm8993->pdata.num_retune_configs != 0) {
+ dev_dbg(codec->dev, "Using ReTune Mobile\n");
+ } else {
+ dev_dbg(codec->dev, "No ReTune Mobile, using normal EQ\n");
+ snd_soc_add_controls(codec, wm8993_eq_controls,
+ ARRAY_SIZE(wm8993_eq_controls));
+ }
+
+ snd_soc_dapm_new_controls(codec, wm8993_dapm_widgets,
+ ARRAY_SIZE(wm8993_dapm_widgets));
+ wm_hubs_add_analogue_controls(codec);
+
+ snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes));
+ wm_hubs_add_analogue_routes(codec, wm8993->pdata.lineout1_diff,
+ wm8993->pdata.lineout2_diff);
+
+ snd_soc_dapm_new_widgets(codec);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(codec->dev, "failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+err:
+ return ret;
+}
+
+static int wm8993_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8993 = {
+ .probe = wm8993_probe,
+ .remove = wm8993_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8993);
+
+static int wm8993_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm8993_priv *wm8993;
+ struct snd_soc_codec *codec;
+ unsigned int val;
+ int ret;
+
+ if (wm8993_codec) {
+ dev_err(&i2c->dev, "A WM8993 is already registered\n");
+ return -EINVAL;
+ }
+
+ wm8993 = kzalloc(sizeof(struct wm8993_priv), GFP_KERNEL);
+ if (wm8993 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8993->codec;
+ if (i2c->dev.platform_data)
+ memcpy(&wm8993->pdata, i2c->dev.platform_data,
+ sizeof(wm8993->pdata));
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->name = "WM8993";
+ codec->read = wm8993_read;
+ codec->write = wm8993_write;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ codec->reg_cache = wm8993->reg_cache;
+ codec->reg_cache_size = ARRAY_SIZE(wm8993->reg_cache);
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8993_set_bias_level;
+ codec->dai = &wm8993_dai;
+ codec->num_dai = 1;
+ codec->private_data = wm8993;
+
+ memcpy(wm8993->reg_cache, wm8993_reg_defaults,
+ sizeof(wm8993->reg_cache));
+
+ i2c_set_clientdata(i2c, wm8993);
+ codec->control_data = i2c;
+ wm8993_codec = codec;
+
+ codec->dev = &i2c->dev;
+
+ val = wm8993_read_hw(codec, WM8993_SOFTWARE_RESET);
+ if (val != wm8993_reg_defaults[WM8993_SOFTWARE_RESET]) {
+ dev_err(codec->dev, "Invalid ID register value %x\n", val);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = wm8993_write(codec, WM8993_SOFTWARE_RESET, 0xffff);
+ if (ret != 0)
+ goto err;
+
+ /* By default we're using the output mixers */
+ wm8993->class_w_users = 2;
+
+ /* Latch volume update bits and default ZC on */
+ snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME,
+ WM8993_DAC_VU, WM8993_DAC_VU);
+ snd_soc_update_bits(codec, WM8993_RIGHT_ADC_DIGITAL_VOLUME,
+ WM8993_ADC_VU, WM8993_ADC_VU);
+
+ /* Manualy manage the HPOUT sequencing for independent stereo
+ * control. */
+ snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ WM8993_HPOUT1_AUTO_PU, 0);
+
+ /* Use automatic clock configuration */
+ snd_soc_update_bits(codec, WM8993_CLOCKING_4, WM8993_SR_MODE, 0);
+
+ if (!wm8993->pdata.lineout1_diff)
+ snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
+ WM8993_LINEOUT1_MODE,
+ WM8993_LINEOUT1_MODE);
+ if (!wm8993->pdata.lineout2_diff)
+ snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
+ WM8993_LINEOUT2_MODE,
+ WM8993_LINEOUT2_MODE);
+
+ if (wm8993->pdata.lineout1fb)
+ snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+ WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
+
+ if (wm8993->pdata.lineout2fb)
+ snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+ WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
+
+ /* Apply the microphone bias/detection configuration - the
+ * platform data is directly applicable to the register. */
+ snd_soc_update_bits(codec, WM8993_MICBIAS,
+ WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
+ WM8993_MICB1_LVL | WM8993_MICB2_LVL,
+ wm8993->pdata.jd_scthr << WM8993_JD_SCTHR_SHIFT |
+ wm8993->pdata.jd_thr << WM8993_JD_THR_SHIFT |
+ wm8993->pdata.micbias1_lvl |
+ wm8993->pdata.micbias1_lvl << 1);
+
+ ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ if (ret != 0)
+ goto err;
+
+ wm8993_dai.dev = codec->dev;
+
+ ret = snd_soc_register_dai(&wm8993_dai);
+ if (ret != 0)
+ goto err_bias;
+
+ ret = snd_soc_register_codec(codec);
+
+ return 0;
+
+err_bias:
+ wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
+err:
+ wm8993_codec = NULL;
+ kfree(wm8993);
+ return ret;
+}
+
+static int wm8993_i2c_remove(struct i2c_client *client)
+{
+ struct wm8993_priv *wm8993 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&wm8993->codec);
+ snd_soc_unregister_dai(&wm8993_dai);
+
+ wm8993_set_bias_level(&wm8993->codec, SND_SOC_BIAS_OFF);
+ kfree(wm8993);
+
+ return 0;
+}
+
+static const struct i2c_device_id wm8993_i2c_id[] = {
+ { "wm8993", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8993_i2c_id);
+
+static struct i2c_driver wm8993_i2c_driver = {
+ .driver = {
+ .name = "WM8993",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8993_i2c_probe,
+ .remove = wm8993_i2c_remove,
+ .id_table = wm8993_i2c_id,
+};
+
+
+static int __init wm8993_modinit(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&wm8993_i2c_driver);
+ if (ret != 0)
+ pr_err("WM8993: Unable to register I2C driver: %d\n", ret);
+
+ return ret;
+}
+module_init(wm8993_modinit);
+
+static void __exit wm8993_exit(void)
+{
+ i2c_del_driver(&wm8993_i2c_driver);
+}
+module_exit(wm8993_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM8993 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8993.h b/sound/soc/codecs/wm8993.h
new file mode 100644
index 00000000000..30e71ca88da
--- /dev/null
+++ b/sound/soc/codecs/wm8993.h
@@ -0,0 +1,2132 @@
+#ifndef WM8993_H
+#define WM8993_H
+
+extern struct snd_soc_dai wm8993_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8993;
+
+#define WM8993_SYSCLK_MCLK 1
+#define WM8993_SYSCLK_FLL 2
+
+#define WM8993_FLL_MCLK 1
+#define WM8993_FLL_BCLK 2
+#define WM8993_FLL_LRCLK 3
+
+/*
+ * Register values.
+ */
+#define WM8993_SOFTWARE_RESET 0x00
+#define WM8993_POWER_MANAGEMENT_1 0x01
+#define WM8993_POWER_MANAGEMENT_2 0x02
+#define WM8993_POWER_MANAGEMENT_3 0x03
+#define WM8993_AUDIO_INTERFACE_1 0x04
+#define WM8993_AUDIO_INTERFACE_2 0x05
+#define WM8993_CLOCKING_1 0x06
+#define WM8993_CLOCKING_2 0x07
+#define WM8993_AUDIO_INTERFACE_3 0x08
+#define WM8993_AUDIO_INTERFACE_4 0x09
+#define WM8993_DAC_CTRL 0x0A
+#define WM8993_LEFT_DAC_DIGITAL_VOLUME 0x0B
+#define WM8993_RIGHT_DAC_DIGITAL_VOLUME 0x0C
+#define WM8993_DIGITAL_SIDE_TONE 0x0D
+#define WM8993_ADC_CTRL 0x0E
+#define WM8993_LEFT_ADC_DIGITAL_VOLUME 0x0F
+#define WM8993_RIGHT_ADC_DIGITAL_VOLUME 0x10
+#define WM8993_GPIO_CTRL_1 0x12
+#define WM8993_GPIO1 0x13
+#define WM8993_IRQ_DEBOUNCE 0x14
+#define WM8993_GPIOCTRL_2 0x16
+#define WM8993_GPIO_POL 0x17
+#define WM8993_LEFT_LINE_INPUT_1_2_VOLUME 0x18
+#define WM8993_LEFT_LINE_INPUT_3_4_VOLUME 0x19
+#define WM8993_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A
+#define WM8993_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B
+#define WM8993_LEFT_OUTPUT_VOLUME 0x1C
+#define WM8993_RIGHT_OUTPUT_VOLUME 0x1D
+#define WM8993_LINE_OUTPUTS_VOLUME 0x1E
+#define WM8993_HPOUT2_VOLUME 0x1F
+#define WM8993_LEFT_OPGA_VOLUME 0x20
+#define WM8993_RIGHT_OPGA_VOLUME 0x21
+#define WM8993_SPKMIXL_ATTENUATION 0x22
+#define WM8993_SPKMIXR_ATTENUATION 0x23
+#define WM8993_SPKOUT_MIXERS 0x24
+#define WM8993_SPKOUT_BOOST 0x25
+#define WM8993_SPEAKER_VOLUME_LEFT 0x26
+#define WM8993_SPEAKER_VOLUME_RIGHT 0x27
+#define WM8993_INPUT_MIXER2 0x28
+#define WM8993_INPUT_MIXER3 0x29
+#define WM8993_INPUT_MIXER4 0x2A
+#define WM8993_INPUT_MIXER5 0x2B
+#define WM8993_INPUT_MIXER6 0x2C
+#define WM8993_OUTPUT_MIXER1 0x2D
+#define WM8993_OUTPUT_MIXER2 0x2E
+#define WM8993_OUTPUT_MIXER3 0x2F
+#define WM8993_OUTPUT_MIXER4 0x30
+#define WM8993_OUTPUT_MIXER5 0x31
+#define WM8993_OUTPUT_MIXER6 0x32
+#define WM8993_HPOUT2_MIXER 0x33
+#define WM8993_LINE_MIXER1 0x34
+#define WM8993_LINE_MIXER2 0x35
+#define WM8993_SPEAKER_MIXER 0x36
+#define WM8993_ADDITIONAL_CONTROL 0x37
+#define WM8993_ANTIPOP1 0x38
+#define WM8993_ANTIPOP2 0x39
+#define WM8993_MICBIAS 0x3A
+#define WM8993_FLL_CONTROL_1 0x3C
+#define WM8993_FLL_CONTROL_2 0x3D
+#define WM8993_FLL_CONTROL_3 0x3E
+#define WM8993_FLL_CONTROL_4 0x3F
+#define WM8993_FLL_CONTROL_5 0x40
+#define WM8993_CLOCKING_3 0x41
+#define WM8993_CLOCKING_4 0x42
+#define WM8993_MW_SLAVE_CONTROL 0x43
+#define WM8993_BUS_CONTROL_1 0x45
+#define WM8993_WRITE_SEQUENCER_0 0x46
+#define WM8993_WRITE_SEQUENCER_1 0x47
+#define WM8993_WRITE_SEQUENCER_2 0x48
+#define WM8993_WRITE_SEQUENCER_3 0x49
+#define WM8993_WRITE_SEQUENCER_4 0x4A
+#define WM8993_WRITE_SEQUENCER_5 0x4B
+#define WM8993_CHARGE_PUMP_1 0x4C
+#define WM8993_CLASS_W_0 0x51
+#define WM8993_DC_SERVO_0 0x54
+#define WM8993_DC_SERVO_1 0x55
+#define WM8993_DC_SERVO_3 0x57
+#define WM8993_DC_SERVO_READBACK_0 0x58
+#define WM8993_DC_SERVO_READBACK_1 0x59
+#define WM8993_DC_SERVO_READBACK_2 0x5A
+#define WM8993_ANALOGUE_HP_0 0x60
+#define WM8993_EQ1 0x62
+#define WM8993_EQ2 0x63
+#define WM8993_EQ3 0x64
+#define WM8993_EQ4 0x65
+#define WM8993_EQ5 0x66
+#define WM8993_EQ6 0x67
+#define WM8993_EQ7 0x68
+#define WM8993_EQ8 0x69
+#define WM8993_EQ9 0x6A
+#define WM8993_EQ10 0x6B
+#define WM8993_EQ11 0x6C
+#define WM8993_EQ12 0x6D
+#define WM8993_EQ13 0x6E
+#define WM8993_EQ14 0x6F
+#define WM8993_EQ15 0x70
+#define WM8993_EQ16 0x71
+#define WM8993_EQ17 0x72
+#define WM8993_EQ18 0x73
+#define WM8993_EQ19 0x74
+#define WM8993_EQ20 0x75
+#define WM8993_EQ21 0x76
+#define WM8993_EQ22 0x77
+#define WM8993_EQ23 0x78
+#define WM8993_EQ24 0x79
+#define WM8993_DIGITAL_PULLS 0x7A
+#define WM8993_DRC_CONTROL_1 0x7B
+#define WM8993_DRC_CONTROL_2 0x7C
+#define WM8993_DRC_CONTROL_3 0x7D
+#define WM8993_DRC_CONTROL_4 0x7E
+
+#define WM8993_REGISTER_COUNT 0x7F
+#define WM8993_MAX_REGISTER 0x7E
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM8993_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */
+#define WM8993_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */
+#define WM8993_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8993_SPKOUTR_ENA 0x2000 /* SPKOUTR_ENA */
+#define WM8993_SPKOUTR_ENA_MASK 0x2000 /* SPKOUTR_ENA */
+#define WM8993_SPKOUTR_ENA_SHIFT 13 /* SPKOUTR_ENA */
+#define WM8993_SPKOUTR_ENA_WIDTH 1 /* SPKOUTR_ENA */
+#define WM8993_SPKOUTL_ENA 0x1000 /* SPKOUTL_ENA */
+#define WM8993_SPKOUTL_ENA_MASK 0x1000 /* SPKOUTL_ENA */
+#define WM8993_SPKOUTL_ENA_SHIFT 12 /* SPKOUTL_ENA */
+#define WM8993_SPKOUTL_ENA_WIDTH 1 /* SPKOUTL_ENA */
+#define WM8993_HPOUT2_ENA 0x0800 /* HPOUT2_ENA */
+#define WM8993_HPOUT2_ENA_MASK 0x0800 /* HPOUT2_ENA */
+#define WM8993_HPOUT2_ENA_SHIFT 11 /* HPOUT2_ENA */
+#define WM8993_HPOUT2_ENA_WIDTH 1 /* HPOUT2_ENA */
+#define WM8993_HPOUT1L_ENA 0x0200 /* HPOUT1L_ENA */
+#define WM8993_HPOUT1L_ENA_MASK 0x0200 /* HPOUT1L_ENA */
+#define WM8993_HPOUT1L_ENA_SHIFT 9 /* HPOUT1L_ENA */
+#define WM8993_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */
+#define WM8993_HPOUT1R_ENA 0x0100 /* HPOUT1R_ENA */
+#define WM8993_HPOUT1R_ENA_MASK 0x0100 /* HPOUT1R_ENA */
+#define WM8993_HPOUT1R_ENA_SHIFT 8 /* HPOUT1R_ENA */
+#define WM8993_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */
+#define WM8993_MICB2_ENA 0x0020 /* MICB2_ENA */
+#define WM8993_MICB2_ENA_MASK 0x0020 /* MICB2_ENA */
+#define WM8993_MICB2_ENA_SHIFT 5 /* MICB2_ENA */
+#define WM8993_MICB2_ENA_WIDTH 1 /* MICB2_ENA */
+#define WM8993_MICB1_ENA 0x0010 /* MICB1_ENA */
+#define WM8993_MICB1_ENA_MASK 0x0010 /* MICB1_ENA */
+#define WM8993_MICB1_ENA_SHIFT 4 /* MICB1_ENA */
+#define WM8993_MICB1_ENA_WIDTH 1 /* MICB1_ENA */
+#define WM8993_VMID_SEL_MASK 0x0006 /* VMID_SEL - [2:1] */
+#define WM8993_VMID_SEL_SHIFT 1 /* VMID_SEL - [2:1] */
+#define WM8993_VMID_SEL_WIDTH 2 /* VMID_SEL - [2:1] */
+#define WM8993_BIAS_ENA 0x0001 /* BIAS_ENA */
+#define WM8993_BIAS_ENA_MASK 0x0001 /* BIAS_ENA */
+#define WM8993_BIAS_ENA_SHIFT 0 /* BIAS_ENA */
+#define WM8993_BIAS_ENA_WIDTH 1 /* BIAS_ENA */
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8993_TSHUT_ENA 0x4000 /* TSHUT_ENA */
+#define WM8993_TSHUT_ENA_MASK 0x4000 /* TSHUT_ENA */
+#define WM8993_TSHUT_ENA_SHIFT 14 /* TSHUT_ENA */
+#define WM8993_TSHUT_ENA_WIDTH 1 /* TSHUT_ENA */
+#define WM8993_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */
+#define WM8993_TSHUT_OPDIS_MASK 0x2000 /* TSHUT_OPDIS */
+#define WM8993_TSHUT_OPDIS_SHIFT 13 /* TSHUT_OPDIS */
+#define WM8993_TSHUT_OPDIS_WIDTH 1 /* TSHUT_OPDIS */
+#define WM8993_OPCLK_ENA 0x0800 /* OPCLK_ENA */
+#define WM8993_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */
+#define WM8993_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */
+#define WM8993_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */
+#define WM8993_MIXINL_ENA 0x0200 /* MIXINL_ENA */
+#define WM8993_MIXINL_ENA_MASK 0x0200 /* MIXINL_ENA */
+#define WM8993_MIXINL_ENA_SHIFT 9 /* MIXINL_ENA */
+#define WM8993_MIXINL_ENA_WIDTH 1 /* MIXINL_ENA */
+#define WM8993_MIXINR_ENA 0x0100 /* MIXINR_ENA */
+#define WM8993_MIXINR_ENA_MASK 0x0100 /* MIXINR_ENA */
+#define WM8993_MIXINR_ENA_SHIFT 8 /* MIXINR_ENA */
+#define WM8993_MIXINR_ENA_WIDTH 1 /* MIXINR_ENA */
+#define WM8993_IN2L_ENA 0x0080 /* IN2L_ENA */
+#define WM8993_IN2L_ENA_MASK 0x0080 /* IN2L_ENA */
+#define WM8993_IN2L_ENA_SHIFT 7 /* IN2L_ENA */
+#define WM8993_IN2L_ENA_WIDTH 1 /* IN2L_ENA */
+#define WM8993_IN1L_ENA 0x0040 /* IN1L_ENA */
+#define WM8993_IN1L_ENA_MASK 0x0040 /* IN1L_ENA */
+#define WM8993_IN1L_ENA_SHIFT 6 /* IN1L_ENA */
+#define WM8993_IN1L_ENA_WIDTH 1 /* IN1L_ENA */
+#define WM8993_IN2R_ENA 0x0020 /* IN2R_ENA */
+#define WM8993_IN2R_ENA_MASK 0x0020 /* IN2R_ENA */
+#define WM8993_IN2R_ENA_SHIFT 5 /* IN2R_ENA */
+#define WM8993_IN2R_ENA_WIDTH 1 /* IN2R_ENA */
+#define WM8993_IN1R_ENA 0x0010 /* IN1R_ENA */
+#define WM8993_IN1R_ENA_MASK 0x0010 /* IN1R_ENA */
+#define WM8993_IN1R_ENA_SHIFT 4 /* IN1R_ENA */
+#define WM8993_IN1R_ENA_WIDTH 1 /* IN1R_ENA */
+#define WM8993_ADCL_ENA 0x0002 /* ADCL_ENA */
+#define WM8993_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */
+#define WM8993_ADCL_ENA_SHIFT 1 /* ADCL_ENA */
+#define WM8993_ADCL_ENA_WIDTH 1 /* ADCL_ENA */
+#define WM8993_ADCR_ENA 0x0001 /* ADCR_ENA */
+#define WM8993_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */
+#define WM8993_ADCR_ENA_SHIFT 0 /* ADCR_ENA */
+#define WM8993_ADCR_ENA_WIDTH 1 /* ADCR_ENA */
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8993_LINEOUT1N_ENA 0x2000 /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1N_ENA_MASK 0x2000 /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1N_ENA_SHIFT 13 /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1N_ENA_WIDTH 1 /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1P_ENA 0x1000 /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT1P_ENA_MASK 0x1000 /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT1P_ENA_SHIFT 12 /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT1P_ENA_WIDTH 1 /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT2N_ENA 0x0800 /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2N_ENA_MASK 0x0800 /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2N_ENA_SHIFT 11 /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2N_ENA_WIDTH 1 /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2P_ENA 0x0400 /* LINEOUT2P_ENA */
+#define WM8993_LINEOUT2P_ENA_MASK 0x0400 /* LINEOUT2P_ENA */
+#define WM8993_LINEOUT2P_ENA_SHIFT 10 /* LINEOUT2P_ENA */
+#define WM8993_LINEOUT2P_ENA_WIDTH 1 /* LINEOUT2P_ENA */
+#define WM8993_SPKRVOL_ENA 0x0200 /* SPKRVOL_ENA */
+#define WM8993_SPKRVOL_ENA_MASK 0x0200 /* SPKRVOL_ENA */
+#define WM8993_SPKRVOL_ENA_SHIFT 9 /* SPKRVOL_ENA */
+#define WM8993_SPKRVOL_ENA_WIDTH 1 /* SPKRVOL_ENA */
+#define WM8993_SPKLVOL_ENA 0x0100 /* SPKLVOL_ENA */
+#define WM8993_SPKLVOL_ENA_MASK 0x0100 /* SPKLVOL_ENA */
+#define WM8993_SPKLVOL_ENA_SHIFT 8 /* SPKLVOL_ENA */
+#define WM8993_SPKLVOL_ENA_WIDTH 1 /* SPKLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA 0x0080 /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA_MASK 0x0080 /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA_SHIFT 7 /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA_WIDTH 1 /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA 0x0040 /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA_MASK 0x0040 /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA_SHIFT 6 /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA_WIDTH 1 /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTL_ENA 0x0020 /* MIXOUTL_ENA */
+#define WM8993_MIXOUTL_ENA_MASK 0x0020 /* MIXOUTL_ENA */
+#define WM8993_MIXOUTL_ENA_SHIFT 5 /* MIXOUTL_ENA */
+#define WM8993_MIXOUTL_ENA_WIDTH 1 /* MIXOUTL_ENA */
+#define WM8993_MIXOUTR_ENA 0x0010 /* MIXOUTR_ENA */
+#define WM8993_MIXOUTR_ENA_MASK 0x0010 /* MIXOUTR_ENA */
+#define WM8993_MIXOUTR_ENA_SHIFT 4 /* MIXOUTR_ENA */
+#define WM8993_MIXOUTR_ENA_WIDTH 1 /* MIXOUTR_ENA */
+#define WM8993_DACL_ENA 0x0002 /* DACL_ENA */
+#define WM8993_DACL_ENA_MASK 0x0002 /* DACL_ENA */
+#define WM8993_DACL_ENA_SHIFT 1 /* DACL_ENA */
+#define WM8993_DACL_ENA_WIDTH 1 /* DACL_ENA */
+#define WM8993_DACR_ENA 0x0001 /* DACR_ENA */
+#define WM8993_DACR_ENA_MASK 0x0001 /* DACR_ENA */
+#define WM8993_DACR_ENA_SHIFT 0 /* DACR_ENA */
+#define WM8993_DACR_ENA_WIDTH 1 /* DACR_ENA */
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8993_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */
+#define WM8993_AIFADCL_SRC_MASK 0x8000 /* AIFADCL_SRC */
+#define WM8993_AIFADCL_SRC_SHIFT 15 /* AIFADCL_SRC */
+#define WM8993_AIFADCL_SRC_WIDTH 1 /* AIFADCL_SRC */
+#define WM8993_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */
+#define WM8993_AIFADCR_SRC_MASK 0x4000 /* AIFADCR_SRC */
+#define WM8993_AIFADCR_SRC_SHIFT 14 /* AIFADCR_SRC */
+#define WM8993_AIFADCR_SRC_WIDTH 1 /* AIFADCR_SRC */
+#define WM8993_AIFADC_TDM 0x2000 /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_MASK 0x2000 /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_SHIFT 13 /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_WIDTH 1 /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */
+#define WM8993_AIFADC_TDM_CHAN_MASK 0x1000 /* AIFADC_TDM_CHAN */
+#define WM8993_AIFADC_TDM_CHAN_SHIFT 12 /* AIFADC_TDM_CHAN */
+#define WM8993_AIFADC_TDM_CHAN_WIDTH 1 /* AIFADC_TDM_CHAN */
+#define WM8993_BCLK_DIR 0x0200 /* BCLK_DIR */
+#define WM8993_BCLK_DIR_MASK 0x0200 /* BCLK_DIR */
+#define WM8993_BCLK_DIR_SHIFT 9 /* BCLK_DIR */
+#define WM8993_BCLK_DIR_WIDTH 1 /* BCLK_DIR */
+#define WM8993_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */
+#define WM8993_AIF_BCLK_INV_MASK 0x0100 /* AIF_BCLK_INV */
+#define WM8993_AIF_BCLK_INV_SHIFT 8 /* AIF_BCLK_INV */
+#define WM8993_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */
+#define WM8993_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */
+#define WM8993_AIF_LRCLK_INV_MASK 0x0080 /* AIF_LRCLK_INV */
+#define WM8993_AIF_LRCLK_INV_SHIFT 7 /* AIF_LRCLK_INV */
+#define WM8993_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */
+#define WM8993_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */
+#define WM8993_AIF_WL_SHIFT 5 /* AIF_WL - [6:5] */
+#define WM8993_AIF_WL_WIDTH 2 /* AIF_WL - [6:5] */
+#define WM8993_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */
+#define WM8993_AIF_FMT_SHIFT 3 /* AIF_FMT - [4:3] */
+#define WM8993_AIF_FMT_WIDTH 2 /* AIF_FMT - [4:3] */
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8993_AIFDACL_SRC 0x8000 /* AIFDACL_SRC */
+#define WM8993_AIFDACL_SRC_MASK 0x8000 /* AIFDACL_SRC */
+#define WM8993_AIFDACL_SRC_SHIFT 15 /* AIFDACL_SRC */
+#define WM8993_AIFDACL_SRC_WIDTH 1 /* AIFDACL_SRC */
+#define WM8993_AIFDACR_SRC 0x4000 /* AIFDACR_SRC */
+#define WM8993_AIFDACR_SRC_MASK 0x4000 /* AIFDACR_SRC */
+#define WM8993_AIFDACR_SRC_SHIFT 14 /* AIFDACR_SRC */
+#define WM8993_AIFDACR_SRC_WIDTH 1 /* AIFDACR_SRC */
+#define WM8993_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_MASK 0x2000 /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_SHIFT 13 /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_WIDTH 1 /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8993_AIFDAC_TDM_CHAN_MASK 0x1000 /* AIFDAC_TDM_CHAN */
+#define WM8993_AIFDAC_TDM_CHAN_SHIFT 12 /* AIFDAC_TDM_CHAN */
+#define WM8993_AIFDAC_TDM_CHAN_WIDTH 1 /* AIFDAC_TDM_CHAN */
+#define WM8993_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST - [11:10] */
+#define WM8993_DAC_BOOST_SHIFT 10 /* DAC_BOOST - [11:10] */
+#define WM8993_DAC_BOOST_WIDTH 2 /* DAC_BOOST - [11:10] */
+#define WM8993_DAC_COMP 0x0010 /* DAC_COMP */
+#define WM8993_DAC_COMP_MASK 0x0010 /* DAC_COMP */
+#define WM8993_DAC_COMP_SHIFT 4 /* DAC_COMP */
+#define WM8993_DAC_COMP_WIDTH 1 /* DAC_COMP */
+#define WM8993_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */
+#define WM8993_DAC_COMPMODE_MASK 0x0008 /* DAC_COMPMODE */
+#define WM8993_DAC_COMPMODE_SHIFT 3 /* DAC_COMPMODE */
+#define WM8993_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */
+#define WM8993_ADC_COMP 0x0004 /* ADC_COMP */
+#define WM8993_ADC_COMP_MASK 0x0004 /* ADC_COMP */
+#define WM8993_ADC_COMP_SHIFT 2 /* ADC_COMP */
+#define WM8993_ADC_COMP_WIDTH 1 /* ADC_COMP */
+#define WM8993_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */
+#define WM8993_ADC_COMPMODE_MASK 0x0002 /* ADC_COMPMODE */
+#define WM8993_ADC_COMPMODE_SHIFT 1 /* ADC_COMPMODE */
+#define WM8993_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */
+#define WM8993_LOOPBACK 0x0001 /* LOOPBACK */
+#define WM8993_LOOPBACK_MASK 0x0001 /* LOOPBACK */
+#define WM8993_LOOPBACK_SHIFT 0 /* LOOPBACK */
+#define WM8993_LOOPBACK_WIDTH 1 /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking 1
+ */
+#define WM8993_TOCLK_RATE 0x8000 /* TOCLK_RATE */
+#define WM8993_TOCLK_RATE_MASK 0x8000 /* TOCLK_RATE */
+#define WM8993_TOCLK_RATE_SHIFT 15 /* TOCLK_RATE */
+#define WM8993_TOCLK_RATE_WIDTH 1 /* TOCLK_RATE */
+#define WM8993_TOCLK_ENA 0x4000 /* TOCLK_ENA */
+#define WM8993_TOCLK_ENA_MASK 0x4000 /* TOCLK_ENA */
+#define WM8993_TOCLK_ENA_SHIFT 14 /* TOCLK_ENA */
+#define WM8993_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */
+#define WM8993_OPCLK_DIV_MASK 0x1E00 /* OPCLK_DIV - [12:9] */
+#define WM8993_OPCLK_DIV_SHIFT 9 /* OPCLK_DIV - [12:9] */
+#define WM8993_OPCLK_DIV_WIDTH 4 /* OPCLK_DIV - [12:9] */
+#define WM8993_DCLK_DIV_MASK 0x01C0 /* DCLK_DIV - [8:6] */
+#define WM8993_DCLK_DIV_SHIFT 6 /* DCLK_DIV - [8:6] */
+#define WM8993_DCLK_DIV_WIDTH 3 /* DCLK_DIV - [8:6] */
+#define WM8993_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */
+#define WM8993_BCLK_DIV_SHIFT 1 /* BCLK_DIV - [4:1] */
+#define WM8993_BCLK_DIV_WIDTH 4 /* BCLK_DIV - [4:1] */
+
+/*
+ * R7 (0x07) - Clocking 2
+ */
+#define WM8993_MCLK_SRC 0x8000 /* MCLK_SRC */
+#define WM8993_MCLK_SRC_MASK 0x8000 /* MCLK_SRC */
+#define WM8993_MCLK_SRC_SHIFT 15 /* MCLK_SRC */
+#define WM8993_MCLK_SRC_WIDTH 1 /* MCLK_SRC */
+#define WM8993_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */
+#define WM8993_SYSCLK_SRC_MASK 0x4000 /* SYSCLK_SRC */
+#define WM8993_SYSCLK_SRC_SHIFT 14 /* SYSCLK_SRC */
+#define WM8993_SYSCLK_SRC_WIDTH 1 /* SYSCLK_SRC */
+#define WM8993_MCLK_DIV 0x1000 /* MCLK_DIV */
+#define WM8993_MCLK_DIV_MASK 0x1000 /* MCLK_DIV */
+#define WM8993_MCLK_DIV_SHIFT 12 /* MCLK_DIV */
+#define WM8993_MCLK_DIV_WIDTH 1 /* MCLK_DIV */
+#define WM8993_MCLK_INV 0x0400 /* MCLK_INV */
+#define WM8993_MCLK_INV_MASK 0x0400 /* MCLK_INV */
+#define WM8993_MCLK_INV_SHIFT 10 /* MCLK_INV */
+#define WM8993_MCLK_INV_WIDTH 1 /* MCLK_INV */
+#define WM8993_ADC_DIV_MASK 0x00E0 /* ADC_DIV - [7:5] */
+#define WM8993_ADC_DIV_SHIFT 5 /* ADC_DIV - [7:5] */
+#define WM8993_ADC_DIV_WIDTH 3 /* ADC_DIV - [7:5] */
+#define WM8993_DAC_DIV_MASK 0x001C /* DAC_DIV - [4:2] */
+#define WM8993_DAC_DIV_SHIFT 2 /* DAC_DIV - [4:2] */
+#define WM8993_DAC_DIV_WIDTH 3 /* DAC_DIV - [4:2] */
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8993_AIF_MSTR1 0x8000 /* AIF_MSTR1 */
+#define WM8993_AIF_MSTR1_MASK 0x8000 /* AIF_MSTR1 */
+#define WM8993_AIF_MSTR1_SHIFT 15 /* AIF_MSTR1 */
+#define WM8993_AIF_MSTR1_WIDTH 1 /* AIF_MSTR1 */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8993_AIF_TRIS 0x2000 /* AIF_TRIS */
+#define WM8993_AIF_TRIS_MASK 0x2000 /* AIF_TRIS */
+#define WM8993_AIF_TRIS_SHIFT 13 /* AIF_TRIS */
+#define WM8993_AIF_TRIS_WIDTH 1 /* AIF_TRIS */
+#define WM8993_LRCLK_DIR 0x0800 /* LRCLK_DIR */
+#define WM8993_LRCLK_DIR_MASK 0x0800 /* LRCLK_DIR */
+#define WM8993_LRCLK_DIR_SHIFT 11 /* LRCLK_DIR */
+#define WM8993_LRCLK_DIR_WIDTH 1 /* LRCLK_DIR */
+#define WM8993_LRCLK_RATE_MASK 0x07FF /* LRCLK_RATE - [10:0] */
+#define WM8993_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [10:0] */
+#define WM8993_LRCLK_RATE_WIDTH 11 /* LRCLK_RATE - [10:0] */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8993_DAC_OSR128 0x2000 /* DAC_OSR128 */
+#define WM8993_DAC_OSR128_MASK 0x2000 /* DAC_OSR128 */
+#define WM8993_DAC_OSR128_SHIFT 13 /* DAC_OSR128 */
+#define WM8993_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */
+#define WM8993_DAC_MONO 0x0200 /* DAC_MONO */
+#define WM8993_DAC_MONO_MASK 0x0200 /* DAC_MONO */
+#define WM8993_DAC_MONO_SHIFT 9 /* DAC_MONO */
+#define WM8993_DAC_MONO_WIDTH 1 /* DAC_MONO */
+#define WM8993_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */
+#define WM8993_DAC_SB_FILT_MASK 0x0100 /* DAC_SB_FILT */
+#define WM8993_DAC_SB_FILT_SHIFT 8 /* DAC_SB_FILT */
+#define WM8993_DAC_SB_FILT_WIDTH 1 /* DAC_SB_FILT */
+#define WM8993_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */
+#define WM8993_DAC_MUTERATE_MASK 0x0080 /* DAC_MUTERATE */
+#define WM8993_DAC_MUTERATE_SHIFT 7 /* DAC_MUTERATE */
+#define WM8993_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */
+#define WM8993_DAC_UNMUTE_RAMP 0x0040 /* DAC_UNMUTE_RAMP */
+#define WM8993_DAC_UNMUTE_RAMP_MASK 0x0040 /* DAC_UNMUTE_RAMP */
+#define WM8993_DAC_UNMUTE_RAMP_SHIFT 6 /* DAC_UNMUTE_RAMP */
+#define WM8993_DAC_UNMUTE_RAMP_WIDTH 1 /* DAC_UNMUTE_RAMP */
+#define WM8993_DEEMPH_MASK 0x0030 /* DEEMPH - [5:4] */
+#define WM8993_DEEMPH_SHIFT 4 /* DEEMPH - [5:4] */
+#define WM8993_DEEMPH_WIDTH 2 /* DEEMPH - [5:4] */
+#define WM8993_DAC_MUTE 0x0004 /* DAC_MUTE */
+#define WM8993_DAC_MUTE_MASK 0x0004 /* DAC_MUTE */
+#define WM8993_DAC_MUTE_SHIFT 2 /* DAC_MUTE */
+#define WM8993_DAC_MUTE_WIDTH 1 /* DAC_MUTE */
+#define WM8993_DACL_DATINV 0x0002 /* DACL_DATINV */
+#define WM8993_DACL_DATINV_MASK 0x0002 /* DACL_DATINV */
+#define WM8993_DACL_DATINV_SHIFT 1 /* DACL_DATINV */
+#define WM8993_DACL_DATINV_WIDTH 1 /* DACL_DATINV */
+#define WM8993_DACR_DATINV 0x0001 /* DACR_DATINV */
+#define WM8993_DACR_DATINV_MASK 0x0001 /* DACR_DATINV */
+#define WM8993_DACR_DATINV_SHIFT 0 /* DACR_DATINV */
+#define WM8993_DACR_DATINV_WIDTH 1 /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8993_DAC_VU 0x0100 /* DAC_VU */
+#define WM8993_DAC_VU_MASK 0x0100 /* DAC_VU */
+#define WM8993_DAC_VU_SHIFT 8 /* DAC_VU */
+#define WM8993_DAC_VU_WIDTH 1 /* DAC_VU */
+#define WM8993_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */
+#define WM8993_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */
+#define WM8993_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */
+
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8993_DAC_VU 0x0100 /* DAC_VU */
+#define WM8993_DAC_VU_MASK 0x0100 /* DAC_VU */
+#define WM8993_DAC_VU_SHIFT 8 /* DAC_VU */
+#define WM8993_DAC_VU_WIDTH 1 /* DAC_VU */
+#define WM8993_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */
+#define WM8993_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */
+#define WM8993_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */
+
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8993_ADCL_DAC_SVOL_MASK 0x1E00 /* ADCL_DAC_SVOL - [12:9] */
+#define WM8993_ADCL_DAC_SVOL_SHIFT 9 /* ADCL_DAC_SVOL - [12:9] */
+#define WM8993_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [12:9] */
+#define WM8993_ADCR_DAC_SVOL_MASK 0x01E0 /* ADCR_DAC_SVOL - [8:5] */
+#define WM8993_ADCR_DAC_SVOL_SHIFT 5 /* ADCR_DAC_SVOL - [8:5] */
+#define WM8993_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [8:5] */
+#define WM8993_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */
+#define WM8993_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */
+#define WM8993_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */
+#define WM8993_ADC_TO_DACR_MASK 0x0003 /* ADC_TO_DACR - [1:0] */
+#define WM8993_ADC_TO_DACR_SHIFT 0 /* ADC_TO_DACR - [1:0] */
+#define WM8993_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [1:0] */
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8993_ADC_OSR128 0x0200 /* ADC_OSR128 */
+#define WM8993_ADC_OSR128_MASK 0x0200 /* ADC_OSR128 */
+#define WM8993_ADC_OSR128_SHIFT 9 /* ADC_OSR128 */
+#define WM8993_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */
+#define WM8993_ADC_HPF 0x0100 /* ADC_HPF */
+#define WM8993_ADC_HPF_MASK 0x0100 /* ADC_HPF */
+#define WM8993_ADC_HPF_SHIFT 8 /* ADC_HPF */
+#define WM8993_ADC_HPF_WIDTH 1 /* ADC_HPF */
+#define WM8993_ADC_HPF_CUT_MASK 0x0060 /* ADC_HPF_CUT - [6:5] */
+#define WM8993_ADC_HPF_CUT_SHIFT 5 /* ADC_HPF_CUT - [6:5] */
+#define WM8993_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [6:5] */
+#define WM8993_ADCL_DATINV 0x0002 /* ADCL_DATINV */
+#define WM8993_ADCL_DATINV_MASK 0x0002 /* ADCL_DATINV */
+#define WM8993_ADCL_DATINV_SHIFT 1 /* ADCL_DATINV */
+#define WM8993_ADCL_DATINV_WIDTH 1 /* ADCL_DATINV */
+#define WM8993_ADCR_DATINV 0x0001 /* ADCR_DATINV */
+#define WM8993_ADCR_DATINV_MASK 0x0001 /* ADCR_DATINV */
+#define WM8993_ADCR_DATINV_SHIFT 0 /* ADCR_DATINV */
+#define WM8993_ADCR_DATINV_WIDTH 1 /* ADCR_DATINV */
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8993_ADC_VU 0x0100 /* ADC_VU */
+#define WM8993_ADC_VU_MASK 0x0100 /* ADC_VU */
+#define WM8993_ADC_VU_SHIFT 8 /* ADC_VU */
+#define WM8993_ADC_VU_WIDTH 1 /* ADC_VU */
+#define WM8993_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */
+#define WM8993_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */
+#define WM8993_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8993_ADC_VU 0x0100 /* ADC_VU */
+#define WM8993_ADC_VU_MASK 0x0100 /* ADC_VU */
+#define WM8993_ADC_VU_SHIFT 8 /* ADC_VU */
+#define WM8993_ADC_VU_WIDTH 1 /* ADC_VU */
+#define WM8993_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */
+#define WM8993_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */
+#define WM8993_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8993_JD2_SC_EINT 0x8000 /* JD2_SC_EINT */
+#define WM8993_JD2_SC_EINT_MASK 0x8000 /* JD2_SC_EINT */
+#define WM8993_JD2_SC_EINT_SHIFT 15 /* JD2_SC_EINT */
+#define WM8993_JD2_SC_EINT_WIDTH 1 /* JD2_SC_EINT */
+#define WM8993_JD2_EINT 0x4000 /* JD2_EINT */
+#define WM8993_JD2_EINT_MASK 0x4000 /* JD2_EINT */
+#define WM8993_JD2_EINT_SHIFT 14 /* JD2_EINT */
+#define WM8993_JD2_EINT_WIDTH 1 /* JD2_EINT */
+#define WM8993_WSEQ_EINT 0x2000 /* WSEQ_EINT */
+#define WM8993_WSEQ_EINT_MASK 0x2000 /* WSEQ_EINT */
+#define WM8993_WSEQ_EINT_SHIFT 13 /* WSEQ_EINT */
+#define WM8993_WSEQ_EINT_WIDTH 1 /* WSEQ_EINT */
+#define WM8993_IRQ 0x1000 /* IRQ */
+#define WM8993_IRQ_MASK 0x1000 /* IRQ */
+#define WM8993_IRQ_SHIFT 12 /* IRQ */
+#define WM8993_IRQ_WIDTH 1 /* IRQ */
+#define WM8993_TEMPOK_EINT 0x0800 /* TEMPOK_EINT */
+#define WM8993_TEMPOK_EINT_MASK 0x0800 /* TEMPOK_EINT */
+#define WM8993_TEMPOK_EINT_SHIFT 11 /* TEMPOK_EINT */
+#define WM8993_TEMPOK_EINT_WIDTH 1 /* TEMPOK_EINT */
+#define WM8993_JD1_SC_EINT 0x0400 /* JD1_SC_EINT */
+#define WM8993_JD1_SC_EINT_MASK 0x0400 /* JD1_SC_EINT */
+#define WM8993_JD1_SC_EINT_SHIFT 10 /* JD1_SC_EINT */
+#define WM8993_JD1_SC_EINT_WIDTH 1 /* JD1_SC_EINT */
+#define WM8993_JD1_EINT 0x0200 /* JD1_EINT */
+#define WM8993_JD1_EINT_MASK 0x0200 /* JD1_EINT */
+#define WM8993_JD1_EINT_SHIFT 9 /* JD1_EINT */
+#define WM8993_JD1_EINT_WIDTH 1 /* JD1_EINT */
+#define WM8993_FLL_LOCK_EINT 0x0100 /* FLL_LOCK_EINT */
+#define WM8993_FLL_LOCK_EINT_MASK 0x0100 /* FLL_LOCK_EINT */
+#define WM8993_FLL_LOCK_EINT_SHIFT 8 /* FLL_LOCK_EINT */
+#define WM8993_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */
+#define WM8993_GPI8_EINT 0x0080 /* GPI8_EINT */
+#define WM8993_GPI8_EINT_MASK 0x0080 /* GPI8_EINT */
+#define WM8993_GPI8_EINT_SHIFT 7 /* GPI8_EINT */
+#define WM8993_GPI8_EINT_WIDTH 1 /* GPI8_EINT */
+#define WM8993_GPI7_EINT 0x0040 /* GPI7_EINT */
+#define WM8993_GPI7_EINT_MASK 0x0040 /* GPI7_EINT */
+#define WM8993_GPI7_EINT_SHIFT 6 /* GPI7_EINT */
+#define WM8993_GPI7_EINT_WIDTH 1 /* GPI7_EINT */
+#define WM8993_GPIO1_EINT 0x0001 /* GPIO1_EINT */
+#define WM8993_GPIO1_EINT_MASK 0x0001 /* GPIO1_EINT */
+#define WM8993_GPIO1_EINT_SHIFT 0 /* GPIO1_EINT */
+#define WM8993_GPIO1_EINT_WIDTH 1 /* GPIO1_EINT */
+
+/*
+ * R19 (0x13) - GPIO1
+ */
+#define WM8993_GPIO1_PU 0x0020 /* GPIO1_PU */
+#define WM8993_GPIO1_PU_MASK 0x0020 /* GPIO1_PU */
+#define WM8993_GPIO1_PU_SHIFT 5 /* GPIO1_PU */
+#define WM8993_GPIO1_PU_WIDTH 1 /* GPIO1_PU */
+#define WM8993_GPIO1_PD 0x0010 /* GPIO1_PD */
+#define WM8993_GPIO1_PD_MASK 0x0010 /* GPIO1_PD */
+#define WM8993_GPIO1_PD_SHIFT 4 /* GPIO1_PD */
+#define WM8993_GPIO1_PD_WIDTH 1 /* GPIO1_PD */
+#define WM8993_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */
+#define WM8993_GPIO1_SEL_SHIFT 0 /* GPIO1_SEL - [3:0] */
+#define WM8993_GPIO1_SEL_WIDTH 4 /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - IRQ_DEBOUNCE
+ */
+#define WM8993_JD2_SC_DB 0x8000 /* JD2_SC_DB */
+#define WM8993_JD2_SC_DB_MASK 0x8000 /* JD2_SC_DB */
+#define WM8993_JD2_SC_DB_SHIFT 15 /* JD2_SC_DB */
+#define WM8993_JD2_SC_DB_WIDTH 1 /* JD2_SC_DB */
+#define WM8993_JD2_DB 0x4000 /* JD2_DB */
+#define WM8993_JD2_DB_MASK 0x4000 /* JD2_DB */
+#define WM8993_JD2_DB_SHIFT 14 /* JD2_DB */
+#define WM8993_JD2_DB_WIDTH 1 /* JD2_DB */
+#define WM8993_WSEQ_DB 0x2000 /* WSEQ_DB */
+#define WM8993_WSEQ_DB_MASK 0x2000 /* WSEQ_DB */
+#define WM8993_WSEQ_DB_SHIFT 13 /* WSEQ_DB */
+#define WM8993_WSEQ_DB_WIDTH 1 /* WSEQ_DB */
+#define WM8993_TEMPOK_DB 0x0800 /* TEMPOK_DB */
+#define WM8993_TEMPOK_DB_MASK 0x0800 /* TEMPOK_DB */
+#define WM8993_TEMPOK_DB_SHIFT 11 /* TEMPOK_DB */
+#define WM8993_TEMPOK_DB_WIDTH 1 /* TEMPOK_DB */
+#define WM8993_JD1_SC_DB 0x0400 /* JD1_SC_DB */
+#define WM8993_JD1_SC_DB_MASK 0x0400 /* JD1_SC_DB */
+#define WM8993_JD1_SC_DB_SHIFT 10 /* JD1_SC_DB */
+#define WM8993_JD1_SC_DB_WIDTH 1 /* JD1_SC_DB */
+#define WM8993_JD1_DB 0x0200 /* JD1_DB */
+#define WM8993_JD1_DB_MASK 0x0200 /* JD1_DB */
+#define WM8993_JD1_DB_SHIFT 9 /* JD1_DB */
+#define WM8993_JD1_DB_WIDTH 1 /* JD1_DB */
+#define WM8993_FLL_LOCK_DB 0x0100 /* FLL_LOCK_DB */
+#define WM8993_FLL_LOCK_DB_MASK 0x0100 /* FLL_LOCK_DB */
+#define WM8993_FLL_LOCK_DB_SHIFT 8 /* FLL_LOCK_DB */
+#define WM8993_FLL_LOCK_DB_WIDTH 1 /* FLL_LOCK_DB */
+#define WM8993_GPI8_DB 0x0080 /* GPI8_DB */
+#define WM8993_GPI8_DB_MASK 0x0080 /* GPI8_DB */
+#define WM8993_GPI8_DB_SHIFT 7 /* GPI8_DB */
+#define WM8993_GPI8_DB_WIDTH 1 /* GPI8_DB */
+#define WM8993_GPI7_DB 0x0008 /* GPI7_DB */
+#define WM8993_GPI7_DB_MASK 0x0008 /* GPI7_DB */
+#define WM8993_GPI7_DB_SHIFT 3 /* GPI7_DB */
+#define WM8993_GPI7_DB_WIDTH 1 /* GPI7_DB */
+#define WM8993_GPIO1_DB 0x0001 /* GPIO1_DB */
+#define WM8993_GPIO1_DB_MASK 0x0001 /* GPIO1_DB */
+#define WM8993_GPIO1_DB_SHIFT 0 /* GPIO1_DB */
+#define WM8993_GPIO1_DB_WIDTH 1 /* GPIO1_DB */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8993_IM_JD2_EINT 0x2000 /* IM_JD2_EINT */
+#define WM8993_IM_JD2_EINT_MASK 0x2000 /* IM_JD2_EINT */
+#define WM8993_IM_JD2_EINT_SHIFT 13 /* IM_JD2_EINT */
+#define WM8993_IM_JD2_EINT_WIDTH 1 /* IM_JD2_EINT */
+#define WM8993_IM_JD2_SC_EINT 0x1000 /* IM_JD2_SC_EINT */
+#define WM8993_IM_JD2_SC_EINT_MASK 0x1000 /* IM_JD2_SC_EINT */
+#define WM8993_IM_JD2_SC_EINT_SHIFT 12 /* IM_JD2_SC_EINT */
+#define WM8993_IM_JD2_SC_EINT_WIDTH 1 /* IM_JD2_SC_EINT */
+#define WM8993_IM_TEMPOK_EINT 0x0800 /* IM_TEMPOK_EINT */
+#define WM8993_IM_TEMPOK_EINT_MASK 0x0800 /* IM_TEMPOK_EINT */
+#define WM8993_IM_TEMPOK_EINT_SHIFT 11 /* IM_TEMPOK_EINT */
+#define WM8993_IM_TEMPOK_EINT_WIDTH 1 /* IM_TEMPOK_EINT */
+#define WM8993_IM_JD1_SC_EINT 0x0400 /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_SC_EINT_MASK 0x0400 /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_SC_EINT_SHIFT 10 /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_SC_EINT_WIDTH 1 /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_EINT 0x0200 /* IM_JD1_EINT */
+#define WM8993_IM_JD1_EINT_MASK 0x0200 /* IM_JD1_EINT */
+#define WM8993_IM_JD1_EINT_SHIFT 9 /* IM_JD1_EINT */
+#define WM8993_IM_JD1_EINT_WIDTH 1 /* IM_JD1_EINT */
+#define WM8993_IM_FLL_LOCK_EINT 0x0100 /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_FLL_LOCK_EINT_MASK 0x0100 /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_FLL_LOCK_EINT_SHIFT 8 /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_GPI8_EINT 0x0040 /* IM_GPI8_EINT */
+#define WM8993_IM_GPI8_EINT_MASK 0x0040 /* IM_GPI8_EINT */
+#define WM8993_IM_GPI8_EINT_SHIFT 6 /* IM_GPI8_EINT */
+#define WM8993_IM_GPI8_EINT_WIDTH 1 /* IM_GPI8_EINT */
+#define WM8993_IM_GPIO1_EINT 0x0020 /* IM_GPIO1_EINT */
+#define WM8993_IM_GPIO1_EINT_MASK 0x0020 /* IM_GPIO1_EINT */
+#define WM8993_IM_GPIO1_EINT_SHIFT 5 /* IM_GPIO1_EINT */
+#define WM8993_IM_GPIO1_EINT_WIDTH 1 /* IM_GPIO1_EINT */
+#define WM8993_GPI8_ENA 0x0010 /* GPI8_ENA */
+#define WM8993_GPI8_ENA_MASK 0x0010 /* GPI8_ENA */
+#define WM8993_GPI8_ENA_SHIFT 4 /* GPI8_ENA */
+#define WM8993_GPI8_ENA_WIDTH 1 /* GPI8_ENA */
+#define WM8993_IM_GPI7_EINT 0x0004 /* IM_GPI7_EINT */
+#define WM8993_IM_GPI7_EINT_MASK 0x0004 /* IM_GPI7_EINT */
+#define WM8993_IM_GPI7_EINT_SHIFT 2 /* IM_GPI7_EINT */
+#define WM8993_IM_GPI7_EINT_WIDTH 1 /* IM_GPI7_EINT */
+#define WM8993_IM_WSEQ_EINT 0x0002 /* IM_WSEQ_EINT */
+#define WM8993_IM_WSEQ_EINT_MASK 0x0002 /* IM_WSEQ_EINT */
+#define WM8993_IM_WSEQ_EINT_SHIFT 1 /* IM_WSEQ_EINT */
+#define WM8993_IM_WSEQ_EINT_WIDTH 1 /* IM_WSEQ_EINT */
+#define WM8993_GPI7_ENA 0x0001 /* GPI7_ENA */
+#define WM8993_GPI7_ENA_MASK 0x0001 /* GPI7_ENA */
+#define WM8993_GPI7_ENA_SHIFT 0 /* GPI7_ENA */
+#define WM8993_GPI7_ENA_WIDTH 1 /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8993_JD2_SC_POL 0x8000 /* JD2_SC_POL */
+#define WM8993_JD2_SC_POL_MASK 0x8000 /* JD2_SC_POL */
+#define WM8993_JD2_SC_POL_SHIFT 15 /* JD2_SC_POL */
+#define WM8993_JD2_SC_POL_WIDTH 1 /* JD2_SC_POL */
+#define WM8993_JD2_POL 0x4000 /* JD2_POL */
+#define WM8993_JD2_POL_MASK 0x4000 /* JD2_POL */
+#define WM8993_JD2_POL_SHIFT 14 /* JD2_POL */
+#define WM8993_JD2_POL_WIDTH 1 /* JD2_POL */
+#define WM8993_WSEQ_POL 0x2000 /* WSEQ_POL */
+#define WM8993_WSEQ_POL_MASK 0x2000 /* WSEQ_POL */
+#define WM8993_WSEQ_POL_SHIFT 13 /* WSEQ_POL */
+#define WM8993_WSEQ_POL_WIDTH 1 /* WSEQ_POL */
+#define WM8993_IRQ_POL 0x1000 /* IRQ_POL */
+#define WM8993_IRQ_POL_MASK 0x1000 /* IRQ_POL */
+#define WM8993_IRQ_POL_SHIFT 12 /* IRQ_POL */
+#define WM8993_IRQ_POL_WIDTH 1 /* IRQ_POL */
+#define WM8993_TEMPOK_POL 0x0800 /* TEMPOK_POL */
+#define WM8993_TEMPOK_POL_MASK 0x0800 /* TEMPOK_POL */
+#define WM8993_TEMPOK_POL_SHIFT 11 /* TEMPOK_POL */
+#define WM8993_TEMPOK_POL_WIDTH 1 /* TEMPOK_POL */
+#define WM8993_JD1_SC_POL 0x0400 /* JD1_SC_POL */
+#define WM8993_JD1_SC_POL_MASK 0x0400 /* JD1_SC_POL */
+#define WM8993_JD1_SC_POL_SHIFT 10 /* JD1_SC_POL */
+#define WM8993_JD1_SC_POL_WIDTH 1 /* JD1_SC_POL */
+#define WM8993_JD1_POL 0x0200 /* JD1_POL */
+#define WM8993_JD1_POL_MASK 0x0200 /* JD1_POL */
+#define WM8993_JD1_POL_SHIFT 9 /* JD1_POL */
+#define WM8993_JD1_POL_WIDTH 1 /* JD1_POL */
+#define WM8993_FLL_LOCK_POL 0x0100 /* FLL_LOCK_POL */
+#define WM8993_FLL_LOCK_POL_MASK 0x0100 /* FLL_LOCK_POL */
+#define WM8993_FLL_LOCK_POL_SHIFT 8 /* FLL_LOCK_POL */
+#define WM8993_FLL_LOCK_POL_WIDTH 1 /* FLL_LOCK_POL */
+#define WM8993_GPI8_POL 0x0080 /* GPI8_POL */
+#define WM8993_GPI8_POL_MASK 0x0080 /* GPI8_POL */
+#define WM8993_GPI8_POL_SHIFT 7 /* GPI8_POL */
+#define WM8993_GPI8_POL_WIDTH 1 /* GPI8_POL */
+#define WM8993_GPI7_POL 0x0040 /* GPI7_POL */
+#define WM8993_GPI7_POL_MASK 0x0040 /* GPI7_POL */
+#define WM8993_GPI7_POL_SHIFT 6 /* GPI7_POL */
+#define WM8993_GPI7_POL_WIDTH 1 /* GPI7_POL */
+#define WM8993_GPIO1_POL 0x0001 /* GPIO1_POL */
+#define WM8993_GPIO1_POL_MASK 0x0001 /* GPIO1_POL */
+#define WM8993_GPIO1_POL_SHIFT 0 /* GPIO1_POL */
+#define WM8993_GPIO1_POL_WIDTH 1 /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8993_IN1_VU 0x0100 /* IN1_VU */
+#define WM8993_IN1_VU_MASK 0x0100 /* IN1_VU */
+#define WM8993_IN1_VU_SHIFT 8 /* IN1_VU */
+#define WM8993_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8993_IN1L_MUTE 0x0080 /* IN1L_MUTE */
+#define WM8993_IN1L_MUTE_MASK 0x0080 /* IN1L_MUTE */
+#define WM8993_IN1L_MUTE_SHIFT 7 /* IN1L_MUTE */
+#define WM8993_IN1L_MUTE_WIDTH 1 /* IN1L_MUTE */
+#define WM8993_IN1L_ZC 0x0040 /* IN1L_ZC */
+#define WM8993_IN1L_ZC_MASK 0x0040 /* IN1L_ZC */
+#define WM8993_IN1L_ZC_SHIFT 6 /* IN1L_ZC */
+#define WM8993_IN1L_ZC_WIDTH 1 /* IN1L_ZC */
+#define WM8993_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */
+#define WM8993_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */
+#define WM8993_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */
+
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8993_IN2_VU 0x0100 /* IN2_VU */
+#define WM8993_IN2_VU_MASK 0x0100 /* IN2_VU */
+#define WM8993_IN2_VU_SHIFT 8 /* IN2_VU */
+#define WM8993_IN2_VU_WIDTH 1 /* IN2_VU */
+#define WM8993_IN2L_MUTE 0x0080 /* IN2L_MUTE */
+#define WM8993_IN2L_MUTE_MASK 0x0080 /* IN2L_MUTE */
+#define WM8993_IN2L_MUTE_SHIFT 7 /* IN2L_MUTE */
+#define WM8993_IN2L_MUTE_WIDTH 1 /* IN2L_MUTE */
+#define WM8993_IN2L_ZC 0x0040 /* IN2L_ZC */
+#define WM8993_IN2L_ZC_MASK 0x0040 /* IN2L_ZC */
+#define WM8993_IN2L_ZC_SHIFT 6 /* IN2L_ZC */
+#define WM8993_IN2L_ZC_WIDTH 1 /* IN2L_ZC */
+#define WM8993_IN2L_VOL_MASK 0x001F /* IN2L_VOL - [4:0] */
+#define WM8993_IN2L_VOL_SHIFT 0 /* IN2L_VOL - [4:0] */
+#define WM8993_IN2L_VOL_WIDTH 5 /* IN2L_VOL - [4:0] */
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8993_IN1_VU 0x0100 /* IN1_VU */
+#define WM8993_IN1_VU_MASK 0x0100 /* IN1_VU */
+#define WM8993_IN1_VU_SHIFT 8 /* IN1_VU */
+#define WM8993_IN1_VU_WIDTH 1 /* IN1_VU */
+#define WM8993_IN1R_MUTE 0x0080 /* IN1R_MUTE */
+#define WM8993_IN1R_MUTE_MASK 0x0080 /* IN1R_MUTE */
+#define WM8993_IN1R_MUTE_SHIFT 7 /* IN1R_MUTE */
+#define WM8993_IN1R_MUTE_WIDTH 1 /* IN1R_MUTE */
+#define WM8993_IN1R_ZC 0x0040 /* IN1R_ZC */
+#define WM8993_IN1R_ZC_MASK 0x0040 /* IN1R_ZC */
+#define WM8993_IN1R_ZC_SHIFT 6 /* IN1R_ZC */
+#define WM8993_IN1R_ZC_WIDTH 1 /* IN1R_ZC */
+#define WM8993_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */
+#define WM8993_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */
+#define WM8993_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8993_IN2_VU 0x0100 /* IN2_VU */
+#define WM8993_IN2_VU_MASK 0x0100 /* IN2_VU */
+#define WM8993_IN2_VU_SHIFT 8 /* IN2_VU */
+#define WM8993_IN2_VU_WIDTH 1 /* IN2_VU */
+#define WM8993_IN2R_MUTE 0x0080 /* IN2R_MUTE */
+#define WM8993_IN2R_MUTE_MASK 0x0080 /* IN2R_MUTE */
+#define WM8993_IN2R_MUTE_SHIFT 7 /* IN2R_MUTE */
+#define WM8993_IN2R_MUTE_WIDTH 1 /* IN2R_MUTE */
+#define WM8993_IN2R_ZC 0x0040 /* IN2R_ZC */
+#define WM8993_IN2R_ZC_MASK 0x0040 /* IN2R_ZC */
+#define WM8993_IN2R_ZC_SHIFT 6 /* IN2R_ZC */
+#define WM8993_IN2R_ZC_WIDTH 1 /* IN2R_ZC */
+#define WM8993_IN2R_VOL_MASK 0x001F /* IN2R_VOL - [4:0] */
+#define WM8993_IN2R_VOL_SHIFT 0 /* IN2R_VOL - [4:0] */
+#define WM8993_IN2R_VOL_WIDTH 5 /* IN2R_VOL - [4:0] */
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8993_HPOUT1_VU 0x0100 /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */
+#define WM8993_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_MUTE_N 0x0040 /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_MUTE_N_MASK 0x0040 /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_MUTE_N_SHIFT 6 /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_MUTE_N_WIDTH 1 /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_VOL_MASK 0x003F /* HPOUT1L_VOL - [5:0] */
+#define WM8993_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [5:0] */
+#define WM8993_HPOUT1L_VOL_WIDTH 6 /* HPOUT1L_VOL - [5:0] */
+
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8993_HPOUT1_VU 0x0100 /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */
+#define WM8993_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_MUTE_N 0x0040 /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_MUTE_N_MASK 0x0040 /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_MUTE_N_SHIFT 6 /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_MUTE_N_WIDTH 1 /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_VOL_MASK 0x003F /* HPOUT1R_VOL - [5:0] */
+#define WM8993_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [5:0] */
+#define WM8993_HPOUT1R_VOL_WIDTH 6 /* HPOUT1R_VOL - [5:0] */
+
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8993_LINEOUT1N_MUTE 0x0040 /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1N_MUTE_MASK 0x0040 /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1N_MUTE_SHIFT 6 /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1N_MUTE_WIDTH 1 /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1P_MUTE 0x0020 /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1P_MUTE_MASK 0x0020 /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1P_MUTE_SHIFT 5 /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1P_MUTE_WIDTH 1 /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1_VOL 0x0010 /* LINEOUT1_VOL */
+#define WM8993_LINEOUT1_VOL_MASK 0x0010 /* LINEOUT1_VOL */
+#define WM8993_LINEOUT1_VOL_SHIFT 4 /* LINEOUT1_VOL */
+#define WM8993_LINEOUT1_VOL_WIDTH 1 /* LINEOUT1_VOL */
+#define WM8993_LINEOUT2N_MUTE 0x0004 /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2N_MUTE_MASK 0x0004 /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2N_MUTE_SHIFT 2 /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2N_MUTE_WIDTH 1 /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2P_MUTE 0x0002 /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2P_MUTE_MASK 0x0002 /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2P_MUTE_SHIFT 1 /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2P_MUTE_WIDTH 1 /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2_VOL 0x0001 /* LINEOUT2_VOL */
+#define WM8993_LINEOUT2_VOL_MASK 0x0001 /* LINEOUT2_VOL */
+#define WM8993_LINEOUT2_VOL_SHIFT 0 /* LINEOUT2_VOL */
+#define WM8993_LINEOUT2_VOL_WIDTH 1 /* LINEOUT2_VOL */
+
+/*
+ * R31 (0x1F) - HPOUT2 Volume
+ */
+#define WM8993_HPOUT2_MUTE 0x0020 /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_MUTE_MASK 0x0020 /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_MUTE_SHIFT 5 /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_MUTE_WIDTH 1 /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_VOL 0x0010 /* HPOUT2_VOL */
+#define WM8993_HPOUT2_VOL_MASK 0x0010 /* HPOUT2_VOL */
+#define WM8993_HPOUT2_VOL_SHIFT 4 /* HPOUT2_VOL */
+#define WM8993_HPOUT2_VOL_WIDTH 1 /* HPOUT2_VOL */
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8993_MIXOUT_VU 0x0100 /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_MASK 0x0100 /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_SHIFT 8 /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_WIDTH 1 /* MIXOUT_VU */
+#define WM8993_MIXOUTL_ZC 0x0080 /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_ZC_MASK 0x0080 /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_ZC_SHIFT 7 /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_ZC_WIDTH 1 /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_MUTE_N 0x0040 /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_MUTE_N_MASK 0x0040 /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_MUTE_N_SHIFT 6 /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_MUTE_N_WIDTH 1 /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_VOL_MASK 0x003F /* MIXOUTL_VOL - [5:0] */
+#define WM8993_MIXOUTL_VOL_SHIFT 0 /* MIXOUTL_VOL - [5:0] */
+#define WM8993_MIXOUTL_VOL_WIDTH 6 /* MIXOUTL_VOL - [5:0] */
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8993_MIXOUT_VU 0x0100 /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_MASK 0x0100 /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_SHIFT 8 /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_WIDTH 1 /* MIXOUT_VU */
+#define WM8993_MIXOUTR_ZC 0x0080 /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_ZC_MASK 0x0080 /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_ZC_SHIFT 7 /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_ZC_WIDTH 1 /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_MUTE_N 0x0040 /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_MUTE_N_MASK 0x0040 /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_MUTE_N_SHIFT 6 /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_MUTE_N_WIDTH 1 /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_VOL_MASK 0x003F /* MIXOUTR_VOL - [5:0] */
+#define WM8993_MIXOUTR_VOL_SHIFT 0 /* MIXOUTR_VOL - [5:0] */
+#define WM8993_MIXOUTR_VOL_WIDTH 6 /* MIXOUTR_VOL - [5:0] */
+
+/*
+ * R34 (0x22) - SPKMIXL Attenuation
+ */
+#define WM8993_MIXINL_SPKMIXL_VOL 0x0020 /* MIXINL_SPKMIXL_VOL */
+#define WM8993_MIXINL_SPKMIXL_VOL_MASK 0x0020 /* MIXINL_SPKMIXL_VOL */
+#define WM8993_MIXINL_SPKMIXL_VOL_SHIFT 5 /* MIXINL_SPKMIXL_VOL */
+#define WM8993_MIXINL_SPKMIXL_VOL_WIDTH 1 /* MIXINL_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL 0x0010 /* IN1LP_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL_MASK 0x0010 /* IN1LP_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL_SHIFT 4 /* IN1LP_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL_WIDTH 1 /* IN1LP_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL 0x0008 /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL_MASK 0x0008 /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL_SHIFT 3 /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL_WIDTH 1 /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL 0x0004 /* DACL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL_MASK 0x0004 /* DACL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL_SHIFT 2 /* DACL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL_WIDTH 1 /* DACL_SPKMIXL_VOL */
+#define WM8993_SPKMIXL_VOL_MASK 0x0003 /* SPKMIXL_VOL - [1:0] */
+#define WM8993_SPKMIXL_VOL_SHIFT 0 /* SPKMIXL_VOL - [1:0] */
+#define WM8993_SPKMIXL_VOL_WIDTH 2 /* SPKMIXL_VOL - [1:0] */
+
+/*
+ * R35 (0x23) - SPKMIXR Attenuation
+ */
+#define WM8993_SPKOUT_CLASSAB_MODE 0x0100 /* SPKOUT_CLASSAB_MODE */
+#define WM8993_SPKOUT_CLASSAB_MODE_MASK 0x0100 /* SPKOUT_CLASSAB_MODE */
+#define WM8993_SPKOUT_CLASSAB_MODE_SHIFT 8 /* SPKOUT_CLASSAB_MODE */
+#define WM8993_SPKOUT_CLASSAB_MODE_WIDTH 1 /* SPKOUT_CLASSAB_MODE */
+#define WM8993_MIXINR_SPKMIXR_VOL 0x0020 /* MIXINR_SPKMIXR_VOL */
+#define WM8993_MIXINR_SPKMIXR_VOL_MASK 0x0020 /* MIXINR_SPKMIXR_VOL */
+#define WM8993_MIXINR_SPKMIXR_VOL_SHIFT 5 /* MIXINR_SPKMIXR_VOL */
+#define WM8993_MIXINR_SPKMIXR_VOL_WIDTH 1 /* MIXINR_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL 0x0010 /* IN1RP_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL_MASK 0x0010 /* IN1RP_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL_SHIFT 4 /* IN1RP_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL_WIDTH 1 /* IN1RP_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL 0x0008 /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL_MASK 0x0008 /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL_SHIFT 3 /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL_WIDTH 1 /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL 0x0004 /* DACR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL_MASK 0x0004 /* DACR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL_SHIFT 2 /* DACR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL_WIDTH 1 /* DACR_SPKMIXR_VOL */
+#define WM8993_SPKMIXR_VOL_MASK 0x0003 /* SPKMIXR_VOL - [1:0] */
+#define WM8993_SPKMIXR_VOL_SHIFT 0 /* SPKMIXR_VOL - [1:0] */
+#define WM8993_SPKMIXR_VOL_WIDTH 2 /* SPKMIXR_VOL - [1:0] */
+
+/*
+ * R36 (0x24) - SPKOUT Mixers
+ */
+#define WM8993_VRX_TO_SPKOUTL 0x0020 /* VRX_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTL_MASK 0x0020 /* VRX_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTL_SHIFT 5 /* VRX_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTL_WIDTH 1 /* VRX_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL 0x0010 /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL_MASK 0x0010 /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL_SHIFT 4 /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL_WIDTH 1 /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL 0x0008 /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL_MASK 0x0008 /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL_SHIFT 3 /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL_WIDTH 1 /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTR 0x0004 /* VRX_TO_SPKOUTR */
+#define WM8993_VRX_TO_SPKOUTR_MASK 0x0004 /* VRX_TO_SPKOUTR */
+#define WM8993_VRX_TO_SPKOUTR_SHIFT 2 /* VRX_TO_SPKOUTR */
+#define WM8993_VRX_TO_SPKOUTR_WIDTH 1 /* VRX_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR 0x0002 /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR_MASK 0x0002 /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR_SHIFT 1 /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR_WIDTH 1 /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR 0x0001 /* SPKMIXR_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR_MASK 0x0001 /* SPKMIXR_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR_SHIFT 0 /* SPKMIXR_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR_WIDTH 1 /* SPKMIXR_TO_SPKOUTR */
+
+/*
+ * R37 (0x25) - SPKOUT Boost
+ */
+#define WM8993_SPKOUTL_BOOST_MASK 0x0038 /* SPKOUTL_BOOST - [5:3] */
+#define WM8993_SPKOUTL_BOOST_SHIFT 3 /* SPKOUTL_BOOST - [5:3] */
+#define WM8993_SPKOUTL_BOOST_WIDTH 3 /* SPKOUTL_BOOST - [5:3] */
+#define WM8993_SPKOUTR_BOOST_MASK 0x0007 /* SPKOUTR_BOOST - [2:0] */
+#define WM8993_SPKOUTR_BOOST_SHIFT 0 /* SPKOUTR_BOOST - [2:0] */
+#define WM8993_SPKOUTR_BOOST_WIDTH 3 /* SPKOUTR_BOOST - [2:0] */
+
+/*
+ * R38 (0x26) - Speaker Volume Left
+ */
+#define WM8993_SPKOUT_VU 0x0100 /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */
+#define WM8993_SPKOUTL_ZC 0x0080 /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_ZC_MASK 0x0080 /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_ZC_SHIFT 7 /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_ZC_WIDTH 1 /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_MUTE_N 0x0040 /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_MUTE_N_MASK 0x0040 /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_MUTE_N_SHIFT 6 /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_MUTE_N_WIDTH 1 /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_VOL_MASK 0x003F /* SPKOUTL_VOL - [5:0] */
+#define WM8993_SPKOUTL_VOL_SHIFT 0 /* SPKOUTL_VOL - [5:0] */
+#define WM8993_SPKOUTL_VOL_WIDTH 6 /* SPKOUTL_VOL - [5:0] */
+
+/*
+ * R39 (0x27) - Speaker Volume Right
+ */
+#define WM8993_SPKOUT_VU 0x0100 /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */
+#define WM8993_SPKOUTR_ZC 0x0080 /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_ZC_MASK 0x0080 /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_ZC_SHIFT 7 /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_ZC_WIDTH 1 /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_MUTE_N 0x0040 /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_MUTE_N_MASK 0x0040 /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_MUTE_N_SHIFT 6 /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_MUTE_N_WIDTH 1 /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_VOL_MASK 0x003F /* SPKOUTR_VOL - [5:0] */
+#define WM8993_SPKOUTR_VOL_SHIFT 0 /* SPKOUTR_VOL - [5:0] */
+#define WM8993_SPKOUTR_VOL_WIDTH 6 /* SPKOUTR_VOL - [5:0] */
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8993_IN2LP_TO_IN2L 0x0080 /* IN2LP_TO_IN2L */
+#define WM8993_IN2LP_TO_IN2L_MASK 0x0080 /* IN2LP_TO_IN2L */
+#define WM8993_IN2LP_TO_IN2L_SHIFT 7 /* IN2LP_TO_IN2L */
+#define WM8993_IN2LP_TO_IN2L_WIDTH 1 /* IN2LP_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L 0x0040 /* IN2LN_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L_MASK 0x0040 /* IN2LN_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L_SHIFT 6 /* IN2LN_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L_WIDTH 1 /* IN2LN_TO_IN2L */
+#define WM8993_IN1LP_TO_IN1L 0x0020 /* IN1LP_TO_IN1L */
+#define WM8993_IN1LP_TO_IN1L_MASK 0x0020 /* IN1LP_TO_IN1L */
+#define WM8993_IN1LP_TO_IN1L_SHIFT 5 /* IN1LP_TO_IN1L */
+#define WM8993_IN1LP_TO_IN1L_WIDTH 1 /* IN1LP_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L 0x0010 /* IN1LN_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L_MASK 0x0010 /* IN1LN_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L_SHIFT 4 /* IN1LN_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L_WIDTH 1 /* IN1LN_TO_IN1L */
+#define WM8993_IN2RP_TO_IN2R 0x0008 /* IN2RP_TO_IN2R */
+#define WM8993_IN2RP_TO_IN2R_MASK 0x0008 /* IN2RP_TO_IN2R */
+#define WM8993_IN2RP_TO_IN2R_SHIFT 3 /* IN2RP_TO_IN2R */
+#define WM8993_IN2RP_TO_IN2R_WIDTH 1 /* IN2RP_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R 0x0004 /* IN2RN_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R_MASK 0x0004 /* IN2RN_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R_SHIFT 2 /* IN2RN_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R_WIDTH 1 /* IN2RN_TO_IN2R */
+#define WM8993_IN1RP_TO_IN1R 0x0002 /* IN1RP_TO_IN1R */
+#define WM8993_IN1RP_TO_IN1R_MASK 0x0002 /* IN1RP_TO_IN1R */
+#define WM8993_IN1RP_TO_IN1R_SHIFT 1 /* IN1RP_TO_IN1R */
+#define WM8993_IN1RP_TO_IN1R_WIDTH 1 /* IN1RP_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R 0x0001 /* IN1RN_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R_MASK 0x0001 /* IN1RN_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R_SHIFT 0 /* IN1RN_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R_WIDTH 1 /* IN1RN_TO_IN1R */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8993_IN2L_TO_MIXINL 0x0100 /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_TO_MIXINL_MASK 0x0100 /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_TO_MIXINL_SHIFT 8 /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_TO_MIXINL_WIDTH 1 /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_MIXINL_VOL 0x0080 /* IN2L_MIXINL_VOL */
+#define WM8993_IN2L_MIXINL_VOL_MASK 0x0080 /* IN2L_MIXINL_VOL */
+#define WM8993_IN2L_MIXINL_VOL_SHIFT 7 /* IN2L_MIXINL_VOL */
+#define WM8993_IN2L_MIXINL_VOL_WIDTH 1 /* IN2L_MIXINL_VOL */
+#define WM8993_IN1L_TO_MIXINL 0x0020 /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_TO_MIXINL_MASK 0x0020 /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_TO_MIXINL_SHIFT 5 /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_TO_MIXINL_WIDTH 1 /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_MIXINL_VOL 0x0010 /* IN1L_MIXINL_VOL */
+#define WM8993_IN1L_MIXINL_VOL_MASK 0x0010 /* IN1L_MIXINL_VOL */
+#define WM8993_IN1L_MIXINL_VOL_SHIFT 4 /* IN1L_MIXINL_VOL */
+#define WM8993_IN1L_MIXINL_VOL_WIDTH 1 /* IN1L_MIXINL_VOL */
+#define WM8993_MIXOUTL_MIXINL_VOL_MASK 0x0007 /* MIXOUTL_MIXINL_VOL - [2:0] */
+#define WM8993_MIXOUTL_MIXINL_VOL_SHIFT 0 /* MIXOUTL_MIXINL_VOL - [2:0] */
+#define WM8993_MIXOUTL_MIXINL_VOL_WIDTH 3 /* MIXOUTL_MIXINL_VOL - [2:0] */
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8993_IN2R_TO_MIXINR 0x0100 /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_TO_MIXINR_MASK 0x0100 /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_TO_MIXINR_SHIFT 8 /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_TO_MIXINR_WIDTH 1 /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_MIXINR_VOL 0x0080 /* IN2R_MIXINR_VOL */
+#define WM8993_IN2R_MIXINR_VOL_MASK 0x0080 /* IN2R_MIXINR_VOL */
+#define WM8993_IN2R_MIXINR_VOL_SHIFT 7 /* IN2R_MIXINR_VOL */
+#define WM8993_IN2R_MIXINR_VOL_WIDTH 1 /* IN2R_MIXINR_VOL */
+#define WM8993_IN1R_TO_MIXINR 0x0020 /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_TO_MIXINR_MASK 0x0020 /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_TO_MIXINR_SHIFT 5 /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_TO_MIXINR_WIDTH 1 /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_MIXINR_VOL 0x0010 /* IN1R_MIXINR_VOL */
+#define WM8993_IN1R_MIXINR_VOL_MASK 0x0010 /* IN1R_MIXINR_VOL */
+#define WM8993_IN1R_MIXINR_VOL_SHIFT 4 /* IN1R_MIXINR_VOL */
+#define WM8993_IN1R_MIXINR_VOL_WIDTH 1 /* IN1R_MIXINR_VOL */
+#define WM8993_MIXOUTR_MIXINR_VOL_MASK 0x0007 /* MIXOUTR_MIXINR_VOL - [2:0] */
+#define WM8993_MIXOUTR_MIXINR_VOL_SHIFT 0 /* MIXOUTR_MIXINR_VOL - [2:0] */
+#define WM8993_MIXOUTR_MIXINR_VOL_WIDTH 3 /* MIXOUTR_MIXINR_VOL - [2:0] */
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8993_IN1LP_MIXINL_VOL_MASK 0x01C0 /* IN1LP_MIXINL_VOL - [8:6] */
+#define WM8993_IN1LP_MIXINL_VOL_SHIFT 6 /* IN1LP_MIXINL_VOL - [8:6] */
+#define WM8993_IN1LP_MIXINL_VOL_WIDTH 3 /* IN1LP_MIXINL_VOL - [8:6] */
+#define WM8993_VRX_MIXINL_VOL_MASK 0x0007 /* VRX_MIXINL_VOL - [2:0] */
+#define WM8993_VRX_MIXINL_VOL_SHIFT 0 /* VRX_MIXINL_VOL - [2:0] */
+#define WM8993_VRX_MIXINL_VOL_WIDTH 3 /* VRX_MIXINL_VOL - [2:0] */
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8993_IN1RP_MIXINR_VOL_MASK 0x01C0 /* IN1RP_MIXINR_VOL - [8:6] */
+#define WM8993_IN1RP_MIXINR_VOL_SHIFT 6 /* IN1RP_MIXINR_VOL - [8:6] */
+#define WM8993_IN1RP_MIXINR_VOL_WIDTH 3 /* IN1RP_MIXINR_VOL - [8:6] */
+#define WM8993_VRX_MIXINR_VOL_MASK 0x0007 /* VRX_MIXINR_VOL - [2:0] */
+#define WM8993_VRX_MIXINR_VOL_SHIFT 0 /* VRX_MIXINR_VOL - [2:0] */
+#define WM8993_VRX_MIXINR_VOL_WIDTH 3 /* VRX_MIXINR_VOL - [2:0] */
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8993_DACL_TO_HPOUT1L 0x0100 /* DACL_TO_HPOUT1L */
+#define WM8993_DACL_TO_HPOUT1L_MASK 0x0100 /* DACL_TO_HPOUT1L */
+#define WM8993_DACL_TO_HPOUT1L_SHIFT 8 /* DACL_TO_HPOUT1L */
+#define WM8993_DACL_TO_HPOUT1L_WIDTH 1 /* DACL_TO_HPOUT1L */
+#define WM8993_MIXINR_TO_MIXOUTL 0x0080 /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINR_TO_MIXOUTL_MASK 0x0080 /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINR_TO_MIXOUTL_SHIFT 7 /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINR_TO_MIXOUTL_WIDTH 1 /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL 0x0040 /* MIXINL_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL_MASK 0x0040 /* MIXINL_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL_SHIFT 6 /* MIXINL_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL_WIDTH 1 /* MIXINL_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL 0x0020 /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL_MASK 0x0020 /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL_SHIFT 5 /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL_WIDTH 1 /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL 0x0010 /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL_MASK 0x0010 /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL_SHIFT 4 /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL_WIDTH 1 /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL 0x0008 /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL_MASK 0x0008 /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL_SHIFT 3 /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL_WIDTH 1 /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL 0x0004 /* IN1L_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL_MASK 0x0004 /* IN1L_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL_SHIFT 2 /* IN1L_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL_WIDTH 1 /* IN1L_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL 0x0002 /* IN2LP_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL_MASK 0x0002 /* IN2LP_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL_SHIFT 1 /* IN2LP_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL_WIDTH 1 /* IN2LP_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL 0x0001 /* DACL_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL_MASK 0x0001 /* DACL_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL_SHIFT 0 /* DACL_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL_WIDTH 1 /* DACL_TO_MIXOUTL */
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8993_DACR_TO_HPOUT1R 0x0100 /* DACR_TO_HPOUT1R */
+#define WM8993_DACR_TO_HPOUT1R_MASK 0x0100 /* DACR_TO_HPOUT1R */
+#define WM8993_DACR_TO_HPOUT1R_SHIFT 8 /* DACR_TO_HPOUT1R */
+#define WM8993_DACR_TO_HPOUT1R_WIDTH 1 /* DACR_TO_HPOUT1R */
+#define WM8993_MIXINL_TO_MIXOUTR 0x0080 /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINL_TO_MIXOUTR_MASK 0x0080 /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINL_TO_MIXOUTR_SHIFT 7 /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINL_TO_MIXOUTR_WIDTH 1 /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR 0x0040 /* MIXINR_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR_MASK 0x0040 /* MIXINR_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR_SHIFT 6 /* MIXINR_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR_WIDTH 1 /* MIXINR_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR 0x0020 /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR_MASK 0x0020 /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR_SHIFT 5 /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR_WIDTH 1 /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR 0x0010 /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR_MASK 0x0010 /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR_SHIFT 4 /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR_WIDTH 1 /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR 0x0008 /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR_MASK 0x0008 /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR_SHIFT 3 /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR_WIDTH 1 /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR 0x0004 /* IN1R_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR_MASK 0x0004 /* IN1R_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR_SHIFT 2 /* IN1R_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR_WIDTH 1 /* IN1R_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR 0x0002 /* IN2RP_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR_MASK 0x0002 /* IN2RP_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR_SHIFT 1 /* IN2RP_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR_WIDTH 1 /* IN2RP_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR 0x0001 /* DACR_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR_MASK 0x0001 /* DACR_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR_SHIFT 0 /* DACR_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR_WIDTH 1 /* DACR_TO_MIXOUTR */
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8993_IN2LP_MIXOUTL_VOL_MASK 0x0E00 /* IN2LP_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2LP_MIXOUTL_VOL_SHIFT 9 /* IN2LP_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2LP_MIXOUTL_VOL_WIDTH 3 /* IN2LP_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2LN_MIXOUTL_VOL_MASK 0x01C0 /* IN2LN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTL_VOL_SHIFT 6 /* IN2LN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTL_VOL_WIDTH 3 /* IN2LN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN1R_MIXOUTL_VOL_MASK 0x0038 /* IN1R_MIXOUTL_VOL - [5:3] */
+#define WM8993_IN1R_MIXOUTL_VOL_SHIFT 3 /* IN1R_MIXOUTL_VOL - [5:3] */
+#define WM8993_IN1R_MIXOUTL_VOL_WIDTH 3 /* IN1R_MIXOUTL_VOL - [5:3] */
+#define WM8993_IN1L_MIXOUTL_VOL_MASK 0x0007 /* IN1L_MIXOUTL_VOL - [2:0] */
+#define WM8993_IN1L_MIXOUTL_VOL_SHIFT 0 /* IN1L_MIXOUTL_VOL - [2:0] */
+#define WM8993_IN1L_MIXOUTL_VOL_WIDTH 3 /* IN1L_MIXOUTL_VOL - [2:0] */
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8993_IN2RP_MIXOUTR_VOL_MASK 0x0E00 /* IN2RP_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2RP_MIXOUTR_VOL_SHIFT 9 /* IN2RP_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2RP_MIXOUTR_VOL_WIDTH 3 /* IN2RP_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2RN_MIXOUTR_VOL_MASK 0x01C0 /* IN2RN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTR_VOL_SHIFT 6 /* IN2RN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTR_VOL_WIDTH 3 /* IN2RN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN1L_MIXOUTR_VOL_MASK 0x0038 /* IN1L_MIXOUTR_VOL - [5:3] */
+#define WM8993_IN1L_MIXOUTR_VOL_SHIFT 3 /* IN1L_MIXOUTR_VOL - [5:3] */
+#define WM8993_IN1L_MIXOUTR_VOL_WIDTH 3 /* IN1L_MIXOUTR_VOL - [5:3] */
+#define WM8993_IN1R_MIXOUTR_VOL_MASK 0x0007 /* IN1R_MIXOUTR_VOL - [2:0] */
+#define WM8993_IN1R_MIXOUTR_VOL_SHIFT 0 /* IN1R_MIXOUTR_VOL - [2:0] */
+#define WM8993_IN1R_MIXOUTR_VOL_WIDTH 3 /* IN1R_MIXOUTR_VOL - [2:0] */
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8993_DACL_MIXOUTL_VOL_MASK 0x0E00 /* DACL_MIXOUTL_VOL - [11:9] */
+#define WM8993_DACL_MIXOUTL_VOL_SHIFT 9 /* DACL_MIXOUTL_VOL - [11:9] */
+#define WM8993_DACL_MIXOUTL_VOL_WIDTH 3 /* DACL_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2RN_MIXOUTL_VOL_MASK 0x01C0 /* IN2RN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTL_VOL_SHIFT 6 /* IN2RN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTL_VOL_WIDTH 3 /* IN2RN_MIXOUTL_VOL - [8:6] */
+#define WM8993_MIXINR_MIXOUTL_VOL_MASK 0x0038 /* MIXINR_MIXOUTL_VOL - [5:3] */
+#define WM8993_MIXINR_MIXOUTL_VOL_SHIFT 3 /* MIXINR_MIXOUTL_VOL - [5:3] */
+#define WM8993_MIXINR_MIXOUTL_VOL_WIDTH 3 /* MIXINR_MIXOUTL_VOL - [5:3] */
+#define WM8993_MIXINL_MIXOUTL_VOL_MASK 0x0007 /* MIXINL_MIXOUTL_VOL - [2:0] */
+#define WM8993_MIXINL_MIXOUTL_VOL_SHIFT 0 /* MIXINL_MIXOUTL_VOL - [2:0] */
+#define WM8993_MIXINL_MIXOUTL_VOL_WIDTH 3 /* MIXINL_MIXOUTL_VOL - [2:0] */
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8993_DACR_MIXOUTR_VOL_MASK 0x0E00 /* DACR_MIXOUTR_VOL - [11:9] */
+#define WM8993_DACR_MIXOUTR_VOL_SHIFT 9 /* DACR_MIXOUTR_VOL - [11:9] */
+#define WM8993_DACR_MIXOUTR_VOL_WIDTH 3 /* DACR_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2LN_MIXOUTR_VOL_MASK 0x01C0 /* IN2LN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTR_VOL_SHIFT 6 /* IN2LN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTR_VOL_WIDTH 3 /* IN2LN_MIXOUTR_VOL - [8:6] */
+#define WM8993_MIXINL_MIXOUTR_VOL_MASK 0x0038 /* MIXINL_MIXOUTR_VOL - [5:3] */
+#define WM8993_MIXINL_MIXOUTR_VOL_SHIFT 3 /* MIXINL_MIXOUTR_VOL - [5:3] */
+#define WM8993_MIXINL_MIXOUTR_VOL_WIDTH 3 /* MIXINL_MIXOUTR_VOL - [5:3] */
+#define WM8993_MIXINR_MIXOUTR_VOL_MASK 0x0007 /* MIXINR_MIXOUTR_VOL - [2:0] */
+#define WM8993_MIXINR_MIXOUTR_VOL_SHIFT 0 /* MIXINR_MIXOUTR_VOL - [2:0] */
+#define WM8993_MIXINR_MIXOUTR_VOL_WIDTH 3 /* MIXINR_MIXOUTR_VOL - [2:0] */
+
+/*
+ * R51 (0x33) - HPOUT2 Mixer
+ */
+#define WM8993_VRX_TO_HPOUT2 0x0020 /* VRX_TO_HPOUT2 */
+#define WM8993_VRX_TO_HPOUT2_MASK 0x0020 /* VRX_TO_HPOUT2 */
+#define WM8993_VRX_TO_HPOUT2_SHIFT 5 /* VRX_TO_HPOUT2 */
+#define WM8993_VRX_TO_HPOUT2_WIDTH 1 /* VRX_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2 0x0010 /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2_MASK 0x0010 /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2_SHIFT 4 /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2_WIDTH 1 /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2 0x0008 /* MIXOUTRVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2_MASK 0x0008 /* MIXOUTRVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2_SHIFT 3 /* MIXOUTRVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2_WIDTH 1 /* MIXOUTRVOL_TO_HPOUT2 */
+
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8993_MIXOUTL_TO_LINEOUT1N 0x0040 /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTL_TO_LINEOUT1N_MASK 0x0040 /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTL_TO_LINEOUT1N_SHIFT 6 /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTL_TO_LINEOUT1N_WIDTH 1 /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N 0x0020 /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N_MASK 0x0020 /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N_SHIFT 5 /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N_WIDTH 1 /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_LINEOUT1_MODE 0x0010 /* LINEOUT1_MODE */
+#define WM8993_LINEOUT1_MODE_MASK 0x0010 /* LINEOUT1_MODE */
+#define WM8993_LINEOUT1_MODE_SHIFT 4 /* LINEOUT1_MODE */
+#define WM8993_LINEOUT1_MODE_WIDTH 1 /* LINEOUT1_MODE */
+#define WM8993_IN1R_TO_LINEOUT1P 0x0004 /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1R_TO_LINEOUT1P_MASK 0x0004 /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1R_TO_LINEOUT1P_SHIFT 2 /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1R_TO_LINEOUT1P_WIDTH 1 /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P 0x0002 /* IN1L_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P_MASK 0x0002 /* IN1L_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P_SHIFT 1 /* IN1L_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P_WIDTH 1 /* IN1L_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P 0x0001 /* MIXOUTL_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P_MASK 0x0001 /* MIXOUTL_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P_SHIFT 0 /* MIXOUTL_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P_WIDTH 1 /* MIXOUTL_TO_LINEOUT1P */
+
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8993_MIXOUTR_TO_LINEOUT2N 0x0040 /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTR_TO_LINEOUT2N_MASK 0x0040 /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTR_TO_LINEOUT2N_SHIFT 6 /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTR_TO_LINEOUT2N_WIDTH 1 /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N 0x0020 /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N_MASK 0x0020 /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N_SHIFT 5 /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N_WIDTH 1 /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_LINEOUT2_MODE 0x0010 /* LINEOUT2_MODE */
+#define WM8993_LINEOUT2_MODE_MASK 0x0010 /* LINEOUT2_MODE */
+#define WM8993_LINEOUT2_MODE_SHIFT 4 /* LINEOUT2_MODE */
+#define WM8993_LINEOUT2_MODE_WIDTH 1 /* LINEOUT2_MODE */
+#define WM8993_IN1L_TO_LINEOUT2P 0x0004 /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1L_TO_LINEOUT2P_MASK 0x0004 /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1L_TO_LINEOUT2P_SHIFT 2 /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1L_TO_LINEOUT2P_WIDTH 1 /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P 0x0002 /* IN1R_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P_MASK 0x0002 /* IN1R_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P_SHIFT 1 /* IN1R_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P_WIDTH 1 /* IN1R_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P 0x0001 /* MIXOUTR_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P_MASK 0x0001 /* MIXOUTR_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P_SHIFT 0 /* MIXOUTR_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P_WIDTH 1 /* MIXOUTR_TO_LINEOUT2P */
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8993_SPKAB_REF_SEL 0x0100 /* SPKAB_REF_SEL */
+#define WM8993_SPKAB_REF_SEL_MASK 0x0100 /* SPKAB_REF_SEL */
+#define WM8993_SPKAB_REF_SEL_SHIFT 8 /* SPKAB_REF_SEL */
+#define WM8993_SPKAB_REF_SEL_WIDTH 1 /* SPKAB_REF_SEL */
+#define WM8993_MIXINL_TO_SPKMIXL 0x0080 /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINL_TO_SPKMIXL_MASK 0x0080 /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINL_TO_SPKMIXL_SHIFT 7 /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINL_TO_SPKMIXL_WIDTH 1 /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINR_TO_SPKMIXR 0x0040 /* MIXINR_TO_SPKMIXR */
+#define WM8993_MIXINR_TO_SPKMIXR_MASK 0x0040 /* MIXINR_TO_SPKMIXR */
+#define WM8993_MIXINR_TO_SPKMIXR_SHIFT 6 /* MIXINR_TO_SPKMIXR */
+#define WM8993_MIXINR_TO_SPKMIXR_WIDTH 1 /* MIXINR_TO_SPKMIXR */
+#define WM8993_IN1LP_TO_SPKMIXL 0x0020 /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1LP_TO_SPKMIXL_MASK 0x0020 /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1LP_TO_SPKMIXL_SHIFT 5 /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1LP_TO_SPKMIXL_WIDTH 1 /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1RP_TO_SPKMIXR 0x0010 /* IN1RP_TO_SPKMIXR */
+#define WM8993_IN1RP_TO_SPKMIXR_MASK 0x0010 /* IN1RP_TO_SPKMIXR */
+#define WM8993_IN1RP_TO_SPKMIXR_SHIFT 4 /* IN1RP_TO_SPKMIXR */
+#define WM8993_IN1RP_TO_SPKMIXR_WIDTH 1 /* IN1RP_TO_SPKMIXR */
+#define WM8993_MIXOUTL_TO_SPKMIXL 0x0008 /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTL_TO_SPKMIXL_MASK 0x0008 /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTL_TO_SPKMIXL_SHIFT 3 /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTL_TO_SPKMIXL_WIDTH 1 /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTR_TO_SPKMIXR 0x0004 /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_MIXOUTR_TO_SPKMIXR_MASK 0x0004 /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_MIXOUTR_TO_SPKMIXR_SHIFT 2 /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_MIXOUTR_TO_SPKMIXR_WIDTH 1 /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_DACL_TO_SPKMIXL 0x0002 /* DACL_TO_SPKMIXL */
+#define WM8993_DACL_TO_SPKMIXL_MASK 0x0002 /* DACL_TO_SPKMIXL */
+#define WM8993_DACL_TO_SPKMIXL_SHIFT 1 /* DACL_TO_SPKMIXL */
+#define WM8993_DACL_TO_SPKMIXL_WIDTH 1 /* DACL_TO_SPKMIXL */
+#define WM8993_DACR_TO_SPKMIXR 0x0001 /* DACR_TO_SPKMIXR */
+#define WM8993_DACR_TO_SPKMIXR_MASK 0x0001 /* DACR_TO_SPKMIXR */
+#define WM8993_DACR_TO_SPKMIXR_SHIFT 0 /* DACR_TO_SPKMIXR */
+#define WM8993_DACR_TO_SPKMIXR_WIDTH 1 /* DACR_TO_SPKMIXR */
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8993_LINEOUT1_FB 0x0080 /* LINEOUT1_FB */
+#define WM8993_LINEOUT1_FB_MASK 0x0080 /* LINEOUT1_FB */
+#define WM8993_LINEOUT1_FB_SHIFT 7 /* LINEOUT1_FB */
+#define WM8993_LINEOUT1_FB_WIDTH 1 /* LINEOUT1_FB */
+#define WM8993_LINEOUT2_FB 0x0040 /* LINEOUT2_FB */
+#define WM8993_LINEOUT2_FB_MASK 0x0040 /* LINEOUT2_FB */
+#define WM8993_LINEOUT2_FB_SHIFT 6 /* LINEOUT2_FB */
+#define WM8993_LINEOUT2_FB_WIDTH 1 /* LINEOUT2_FB */
+#define WM8993_VROI 0x0001 /* VROI */
+#define WM8993_VROI_MASK 0x0001 /* VROI */
+#define WM8993_VROI_SHIFT 0 /* VROI */
+#define WM8993_VROI_WIDTH 1 /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8993_LINEOUT_VMID_BUF_ENA 0x0080 /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_LINEOUT_VMID_BUF_ENA_MASK 0x0080 /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_LINEOUT_VMID_BUF_ENA_SHIFT 7 /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_LINEOUT_VMID_BUF_ENA_WIDTH 1 /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_HPOUT2_IN_ENA 0x0040 /* HPOUT2_IN_ENA */
+#define WM8993_HPOUT2_IN_ENA_MASK 0x0040 /* HPOUT2_IN_ENA */
+#define WM8993_HPOUT2_IN_ENA_SHIFT 6 /* HPOUT2_IN_ENA */
+#define WM8993_HPOUT2_IN_ENA_WIDTH 1 /* HPOUT2_IN_ENA */
+#define WM8993_LINEOUT1_DISCH 0x0020 /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT1_DISCH_MASK 0x0020 /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT1_DISCH_SHIFT 5 /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT1_DISCH_WIDTH 1 /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT2_DISCH 0x0010 /* LINEOUT2_DISCH */
+#define WM8993_LINEOUT2_DISCH_MASK 0x0010 /* LINEOUT2_DISCH */
+#define WM8993_LINEOUT2_DISCH_SHIFT 4 /* LINEOUT2_DISCH */
+#define WM8993_LINEOUT2_DISCH_WIDTH 1 /* LINEOUT2_DISCH */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8993_VMID_RAMP_MASK 0x0060 /* VMID_RAMP - [6:5] */
+#define WM8993_VMID_RAMP_SHIFT 5 /* VMID_RAMP - [6:5] */
+#define WM8993_VMID_RAMP_WIDTH 2 /* VMID_RAMP - [6:5] */
+#define WM8993_VMID_BUF_ENA 0x0008 /* VMID_BUF_ENA */
+#define WM8993_VMID_BUF_ENA_MASK 0x0008 /* VMID_BUF_ENA */
+#define WM8993_VMID_BUF_ENA_SHIFT 3 /* VMID_BUF_ENA */
+#define WM8993_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */
+#define WM8993_STARTUP_BIAS_ENA 0x0004 /* STARTUP_BIAS_ENA */
+#define WM8993_STARTUP_BIAS_ENA_MASK 0x0004 /* STARTUP_BIAS_ENA */
+#define WM8993_STARTUP_BIAS_ENA_SHIFT 2 /* STARTUP_BIAS_ENA */
+#define WM8993_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */
+#define WM8993_BIAS_SRC 0x0002 /* BIAS_SRC */
+#define WM8993_BIAS_SRC_MASK 0x0002 /* BIAS_SRC */
+#define WM8993_BIAS_SRC_SHIFT 1 /* BIAS_SRC */
+#define WM8993_BIAS_SRC_WIDTH 1 /* BIAS_SRC */
+#define WM8993_VMID_DISCH 0x0001 /* VMID_DISCH */
+#define WM8993_VMID_DISCH_MASK 0x0001 /* VMID_DISCH */
+#define WM8993_VMID_DISCH_SHIFT 0 /* VMID_DISCH */
+#define WM8993_VMID_DISCH_WIDTH 1 /* VMID_DISCH */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8993_JD_SCTHR_MASK 0x00C0 /* JD_SCTHR - [7:6] */
+#define WM8993_JD_SCTHR_SHIFT 6 /* JD_SCTHR - [7:6] */
+#define WM8993_JD_SCTHR_WIDTH 2 /* JD_SCTHR - [7:6] */
+#define WM8993_JD_THR_MASK 0x0030 /* JD_THR - [5:4] */
+#define WM8993_JD_THR_SHIFT 4 /* JD_THR - [5:4] */
+#define WM8993_JD_THR_WIDTH 2 /* JD_THR - [5:4] */
+#define WM8993_JD_ENA 0x0004 /* JD_ENA */
+#define WM8993_JD_ENA_MASK 0x0004 /* JD_ENA */
+#define WM8993_JD_ENA_SHIFT 2 /* JD_ENA */
+#define WM8993_JD_ENA_WIDTH 1 /* JD_ENA */
+#define WM8993_MICB2_LVL 0x0002 /* MICB2_LVL */
+#define WM8993_MICB2_LVL_MASK 0x0002 /* MICB2_LVL */
+#define WM8993_MICB2_LVL_SHIFT 1 /* MICB2_LVL */
+#define WM8993_MICB2_LVL_WIDTH 1 /* MICB2_LVL */
+#define WM8993_MICB1_LVL 0x0001 /* MICB1_LVL */
+#define WM8993_MICB1_LVL_MASK 0x0001 /* MICB1_LVL */
+#define WM8993_MICB1_LVL_SHIFT 0 /* MICB1_LVL */
+#define WM8993_MICB1_LVL_WIDTH 1 /* MICB1_LVL */
+
+/*
+ * R60 (0x3C) - FLL Control 1
+ */
+#define WM8993_FLL_FRAC 0x0004 /* FLL_FRAC */
+#define WM8993_FLL_FRAC_MASK 0x0004 /* FLL_FRAC */
+#define WM8993_FLL_FRAC_SHIFT 2 /* FLL_FRAC */
+#define WM8993_FLL_FRAC_WIDTH 1 /* FLL_FRAC */
+#define WM8993_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */
+#define WM8993_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */
+#define WM8993_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */
+#define WM8993_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */
+#define WM8993_FLL_ENA 0x0001 /* FLL_ENA */
+#define WM8993_FLL_ENA_MASK 0x0001 /* FLL_ENA */
+#define WM8993_FLL_ENA_SHIFT 0 /* FLL_ENA */
+#define WM8993_FLL_ENA_WIDTH 1 /* FLL_ENA */
+
+/*
+ * R61 (0x3D) - FLL Control 2
+ */
+#define WM8993_FLL_OUTDIV_MASK 0x0700 /* FLL_OUTDIV - [10:8] */
+#define WM8993_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [10:8] */
+#define WM8993_FLL_OUTDIV_WIDTH 3 /* FLL_OUTDIV - [10:8] */
+#define WM8993_FLL_CTRL_RATE_MASK 0x0070 /* FLL_CTRL_RATE - [6:4] */
+#define WM8993_FLL_CTRL_RATE_SHIFT 4 /* FLL_CTRL_RATE - [6:4] */
+#define WM8993_FLL_CTRL_RATE_WIDTH 3 /* FLL_CTRL_RATE - [6:4] */
+#define WM8993_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */
+#define WM8993_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */
+#define WM8993_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */
+
+/*
+ * R62 (0x3E) - FLL Control 3
+ */
+#define WM8993_FLL_K_MASK 0xFFFF /* FLL_K - [15:0] */
+#define WM8993_FLL_K_SHIFT 0 /* FLL_K - [15:0] */
+#define WM8993_FLL_K_WIDTH 16 /* FLL_K - [15:0] */
+
+/*
+ * R63 (0x3F) - FLL Control 4
+ */
+#define WM8993_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */
+#define WM8993_FLL_N_SHIFT 5 /* FLL_N - [14:5] */
+#define WM8993_FLL_N_WIDTH 10 /* FLL_N - [14:5] */
+#define WM8993_FLL_GAIN_MASK 0x000F /* FLL_GAIN - [3:0] */
+#define WM8993_FLL_GAIN_SHIFT 0 /* FLL_GAIN - [3:0] */
+#define WM8993_FLL_GAIN_WIDTH 4 /* FLL_GAIN - [3:0] */
+
+/*
+ * R64 (0x40) - FLL Control 5
+ */
+#define WM8993_FLL_FRC_NCO_VAL_MASK 0x1F80 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8993_FLL_FRC_NCO_VAL_SHIFT 7 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8993_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8993_FLL_FRC_NCO 0x0040 /* FLL_FRC_NCO */
+#define WM8993_FLL_FRC_NCO_MASK 0x0040 /* FLL_FRC_NCO */
+#define WM8993_FLL_FRC_NCO_SHIFT 6 /* FLL_FRC_NCO */
+#define WM8993_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */
+#define WM8993_FLL_CLK_REF_DIV_MASK 0x0018 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8993_FLL_CLK_REF_DIV_SHIFT 3 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8993_FLL_CLK_REF_DIV_WIDTH 2 /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8993_FLL_CLK_SRC_MASK 0x0003 /* FLL_CLK_SRC - [1:0] */
+#define WM8993_FLL_CLK_SRC_SHIFT 0 /* FLL_CLK_SRC - [1:0] */
+#define WM8993_FLL_CLK_SRC_WIDTH 2 /* FLL_CLK_SRC - [1:0] */
+
+/*
+ * R65 (0x41) - Clocking 3
+ */
+#define WM8993_CLK_DCS_DIV_MASK 0x3C00 /* CLK_DCS_DIV - [13:10] */
+#define WM8993_CLK_DCS_DIV_SHIFT 10 /* CLK_DCS_DIV - [13:10] */
+#define WM8993_CLK_DCS_DIV_WIDTH 4 /* CLK_DCS_DIV - [13:10] */
+#define WM8993_SAMPLE_RATE_MASK 0x0380 /* SAMPLE_RATE - [9:7] */
+#define WM8993_SAMPLE_RATE_SHIFT 7 /* SAMPLE_RATE - [9:7] */
+#define WM8993_SAMPLE_RATE_WIDTH 3 /* SAMPLE_RATE - [9:7] */
+#define WM8993_CLK_SYS_RATE_MASK 0x001E /* CLK_SYS_RATE - [4:1] */
+#define WM8993_CLK_SYS_RATE_SHIFT 1 /* CLK_SYS_RATE - [4:1] */
+#define WM8993_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [4:1] */
+#define WM8993_CLK_DSP_ENA 0x0001 /* CLK_DSP_ENA */
+#define WM8993_CLK_DSP_ENA_MASK 0x0001 /* CLK_DSP_ENA */
+#define WM8993_CLK_DSP_ENA_SHIFT 0 /* CLK_DSP_ENA */
+#define WM8993_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */
+
+/*
+ * R66 (0x42) - Clocking 4
+ */
+#define WM8993_DAC_DIV4 0x0200 /* DAC_DIV4 */
+#define WM8993_DAC_DIV4_MASK 0x0200 /* DAC_DIV4 */
+#define WM8993_DAC_DIV4_SHIFT 9 /* DAC_DIV4 */
+#define WM8993_DAC_DIV4_WIDTH 1 /* DAC_DIV4 */
+#define WM8993_CLK_256K_DIV_MASK 0x007E /* CLK_256K_DIV - [6:1] */
+#define WM8993_CLK_256K_DIV_SHIFT 1 /* CLK_256K_DIV - [6:1] */
+#define WM8993_CLK_256K_DIV_WIDTH 6 /* CLK_256K_DIV - [6:1] */
+#define WM8993_SR_MODE 0x0001 /* SR_MODE */
+#define WM8993_SR_MODE_MASK 0x0001 /* SR_MODE */
+#define WM8993_SR_MODE_SHIFT 0 /* SR_MODE */
+#define WM8993_SR_MODE_WIDTH 1 /* SR_MODE */
+
+/*
+ * R67 (0x43) - MW Slave Control
+ */
+#define WM8993_MASK_WRITE_ENA 0x0001 /* MASK_WRITE_ENA */
+#define WM8993_MASK_WRITE_ENA_MASK 0x0001 /* MASK_WRITE_ENA */
+#define WM8993_MASK_WRITE_ENA_SHIFT 0 /* MASK_WRITE_ENA */
+#define WM8993_MASK_WRITE_ENA_WIDTH 1 /* MASK_WRITE_ENA */
+
+/*
+ * R69 (0x45) - Bus Control 1
+ */
+#define WM8993_CLK_SYS_ENA 0x0002 /* CLK_SYS_ENA */
+#define WM8993_CLK_SYS_ENA_MASK 0x0002 /* CLK_SYS_ENA */
+#define WM8993_CLK_SYS_ENA_SHIFT 1 /* CLK_SYS_ENA */
+#define WM8993_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */
+
+/*
+ * R70 (0x46) - Write Sequencer 0
+ */
+#define WM8993_WSEQ_ENA 0x0100 /* WSEQ_ENA */
+#define WM8993_WSEQ_ENA_MASK 0x0100 /* WSEQ_ENA */
+#define WM8993_WSEQ_ENA_SHIFT 8 /* WSEQ_ENA */
+#define WM8993_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */
+#define WM8993_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8993_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8993_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R71 (0x47) - Write Sequencer 1
+ */
+#define WM8993_WSEQ_DATA_WIDTH_MASK 0x7000 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8993_WSEQ_DATA_WIDTH_SHIFT 12 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8993_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8993_WSEQ_DATA_START_MASK 0x0F00 /* WSEQ_DATA_START - [11:8] */
+#define WM8993_WSEQ_DATA_START_SHIFT 8 /* WSEQ_DATA_START - [11:8] */
+#define WM8993_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [11:8] */
+#define WM8993_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */
+#define WM8993_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */
+#define WM8993_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R72 (0x48) - Write Sequencer 2
+ */
+#define WM8993_WSEQ_EOS 0x4000 /* WSEQ_EOS */
+#define WM8993_WSEQ_EOS_MASK 0x4000 /* WSEQ_EOS */
+#define WM8993_WSEQ_EOS_SHIFT 14 /* WSEQ_EOS */
+#define WM8993_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */
+#define WM8993_WSEQ_DELAY_MASK 0x0F00 /* WSEQ_DELAY - [11:8] */
+#define WM8993_WSEQ_DELAY_SHIFT 8 /* WSEQ_DELAY - [11:8] */
+#define WM8993_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [11:8] */
+#define WM8993_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */
+#define WM8993_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */
+#define WM8993_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */
+
+/*
+ * R73 (0x49) - Write Sequencer 3
+ */
+#define WM8993_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */
+#define WM8993_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */
+#define WM8993_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */
+#define WM8993_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */
+#define WM8993_WSEQ_START 0x0100 /* WSEQ_START */
+#define WM8993_WSEQ_START_MASK 0x0100 /* WSEQ_START */
+#define WM8993_WSEQ_START_SHIFT 8 /* WSEQ_START */
+#define WM8993_WSEQ_START_WIDTH 1 /* WSEQ_START */
+#define WM8993_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */
+#define WM8993_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */
+#define WM8993_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R74 (0x4A) - Write Sequencer 4
+ */
+#define WM8993_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */
+#define WM8993_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */
+#define WM8993_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */
+#define WM8993_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */
+
+/*
+ * R75 (0x4B) - Write Sequencer 5
+ */
+#define WM8993_WSEQ_CURRENT_INDEX_MASK 0x003F /* WSEQ_CURRENT_INDEX - [5:0] */
+#define WM8993_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [5:0] */
+#define WM8993_WSEQ_CURRENT_INDEX_WIDTH 6 /* WSEQ_CURRENT_INDEX - [5:0] */
+
+/*
+ * R76 (0x4C) - Charge Pump 1
+ */
+#define WM8993_CP_ENA 0x8000 /* CP_ENA */
+#define WM8993_CP_ENA_MASK 0x8000 /* CP_ENA */
+#define WM8993_CP_ENA_SHIFT 15 /* CP_ENA */
+#define WM8993_CP_ENA_WIDTH 1 /* CP_ENA */
+
+/*
+ * R81 (0x51) - Class W 0
+ */
+#define WM8993_CP_DYN_FREQ 0x0002 /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_FREQ_MASK 0x0002 /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_FREQ_SHIFT 1 /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_FREQ_WIDTH 1 /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_V 0x0001 /* CP_DYN_V */
+#define WM8993_CP_DYN_V_MASK 0x0001 /* CP_DYN_V */
+#define WM8993_CP_DYN_V_SHIFT 0 /* CP_DYN_V */
+#define WM8993_CP_DYN_V_WIDTH 1 /* CP_DYN_V */
+
+/*
+ * R84 (0x54) - DC Servo 0
+ */
+#define WM8993_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_DAC_WR_1 0x0008 /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_1_MASK 0x0008 /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_1_SHIFT 3 /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_0 0x0004 /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_TRIG_DAC_WR_0_MASK 0x0004 /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_TRIG_DAC_WR_0_SHIFT 2 /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8993_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */
+#define WM8993_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */
+#define WM8993_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */
+
+/*
+ * R85 (0x55) - DC Servo 1
+ */
+#define WM8993_DCS_SERIES_NO_01_MASK 0x0FE0 /* DCS_SERIES_NO_01 - [11:5] */
+#define WM8993_DCS_SERIES_NO_01_SHIFT 5 /* DCS_SERIES_NO_01 - [11:5] */
+#define WM8993_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [11:5] */
+#define WM8993_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8993_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8993_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R87 (0x57) - DC Servo 3
+ */
+#define WM8993_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8993_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8993_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8993_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8993_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8993_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R88 (0x58) - DC Servo Readback 0
+ */
+#define WM8993_DCS_DATAPATH_BUSY 0x4000 /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_DATAPATH_BUSY_MASK 0x4000 /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_DATAPATH_BUSY_SHIFT 14 /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_DATAPATH_BUSY_WIDTH 1 /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_CHANNEL_MASK 0x3000 /* DCS_CHANNEL - [13:12] */
+#define WM8993_DCS_CHANNEL_SHIFT 12 /* DCS_CHANNEL - [13:12] */
+#define WM8993_DCS_CHANNEL_WIDTH 2 /* DCS_CHANNEL - [13:12] */
+#define WM8993_DCS_CAL_COMPLETE_MASK 0x0300 /* DCS_CAL_COMPLETE - [9:8] */
+#define WM8993_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [9:8] */
+#define WM8993_DCS_CAL_COMPLETE_WIDTH 2 /* DCS_CAL_COMPLETE - [9:8] */
+#define WM8993_DCS_DAC_WR_COMPLETE_MASK 0x0030 /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM8993_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM8993_DCS_DAC_WR_COMPLETE_WIDTH 2 /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM8993_DCS_STARTUP_COMPLETE_MASK 0x0003 /* DCS_STARTUP_COMPLETE - [1:0] */
+#define WM8993_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [1:0] */
+#define WM8993_DCS_STARTUP_COMPLETE_WIDTH 2 /* DCS_STARTUP_COMPLETE - [1:0] */
+
+/*
+ * R89 (0x59) - DC Servo Readback 1
+ */
+#define WM8993_DCS_INTEG_CHAN_1_MASK 0x00FF /* DCS_INTEG_CHAN_1 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_1_SHIFT 0 /* DCS_INTEG_CHAN_1 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_1_WIDTH 8 /* DCS_INTEG_CHAN_1 - [7:0] */
+
+/*
+ * R90 (0x5A) - DC Servo Readback 2
+ */
+#define WM8993_DCS_INTEG_CHAN_0_MASK 0x00FF /* DCS_INTEG_CHAN_0 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_0_SHIFT 0 /* DCS_INTEG_CHAN_0 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_0_WIDTH 8 /* DCS_INTEG_CHAN_0 - [7:0] */
+
+/*
+ * R96 (0x60) - Analogue HP 0
+ */
+#define WM8993_HPOUT1_AUTO_PU 0x0100 /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1_AUTO_PU_MASK 0x0100 /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1_AUTO_PU_SHIFT 8 /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1_AUTO_PU_WIDTH 1 /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */
+#define WM8993_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */
+#define WM8993_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */
+#define WM8993_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */
+#define WM8993_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */
+#define WM8993_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */
+#define WM8993_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */
+#define WM8993_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */
+
+/*
+ * R98 (0x62) - EQ1
+ */
+#define WM8993_EQ_ENA 0x0001 /* EQ_ENA */
+#define WM8993_EQ_ENA_MASK 0x0001 /* EQ_ENA */
+#define WM8993_EQ_ENA_SHIFT 0 /* EQ_ENA */
+#define WM8993_EQ_ENA_WIDTH 1 /* EQ_ENA */
+
+/*
+ * R99 (0x63) - EQ2
+ */
+#define WM8993_EQ_B1_GAIN_MASK 0x001F /* EQ_B1_GAIN - [4:0] */
+#define WM8993_EQ_B1_GAIN_SHIFT 0 /* EQ_B1_GAIN - [4:0] */
+#define WM8993_EQ_B1_GAIN_WIDTH 5 /* EQ_B1_GAIN - [4:0] */
+
+/*
+ * R100 (0x64) - EQ3
+ */
+#define WM8993_EQ_B2_GAIN_MASK 0x001F /* EQ_B2_GAIN - [4:0] */
+#define WM8993_EQ_B2_GAIN_SHIFT 0 /* EQ_B2_GAIN - [4:0] */
+#define WM8993_EQ_B2_GAIN_WIDTH 5 /* EQ_B2_GAIN - [4:0] */
+
+/*
+ * R101 (0x65) - EQ4
+ */
+#define WM8993_EQ_B3_GAIN_MASK 0x001F /* EQ_B3_GAIN - [4:0] */
+#define WM8993_EQ_B3_GAIN_SHIFT 0 /* EQ_B3_GAIN - [4:0] */
+#define WM8993_EQ_B3_GAIN_WIDTH 5 /* EQ_B3_GAIN - [4:0] */
+
+/*
+ * R102 (0x66) - EQ5
+ */
+#define WM8993_EQ_B4_GAIN_MASK 0x001F /* EQ_B4_GAIN - [4:0] */
+#define WM8993_EQ_B4_GAIN_SHIFT 0 /* EQ_B4_GAIN - [4:0] */
+#define WM8993_EQ_B4_GAIN_WIDTH 5 /* EQ_B4_GAIN - [4:0] */
+
+/*
+ * R103 (0x67) - EQ6
+ */
+#define WM8993_EQ_B5_GAIN_MASK 0x001F /* EQ_B5_GAIN - [4:0] */
+#define WM8993_EQ_B5_GAIN_SHIFT 0 /* EQ_B5_GAIN - [4:0] */
+#define WM8993_EQ_B5_GAIN_WIDTH 5 /* EQ_B5_GAIN - [4:0] */
+
+/*
+ * R104 (0x68) - EQ7
+ */
+#define WM8993_EQ_B1_A_MASK 0xFFFF /* EQ_B1_A - [15:0] */
+#define WM8993_EQ_B1_A_SHIFT 0 /* EQ_B1_A - [15:0] */
+#define WM8993_EQ_B1_A_WIDTH 16 /* EQ_B1_A - [15:0] */
+
+/*
+ * R105 (0x69) - EQ8
+ */
+#define WM8993_EQ_B1_B_MASK 0xFFFF /* EQ_B1_B - [15:0] */
+#define WM8993_EQ_B1_B_SHIFT 0 /* EQ_B1_B - [15:0] */
+#define WM8993_EQ_B1_B_WIDTH 16 /* EQ_B1_B - [15:0] */
+
+/*
+ * R106 (0x6A) - EQ9
+ */
+#define WM8993_EQ_B1_PG_MASK 0xFFFF /* EQ_B1_PG - [15:0] */
+#define WM8993_EQ_B1_PG_SHIFT 0 /* EQ_B1_PG - [15:0] */
+#define WM8993_EQ_B1_PG_WIDTH 16 /* EQ_B1_PG - [15:0] */
+
+/*
+ * R107 (0x6B) - EQ10
+ */
+#define WM8993_EQ_B2_A_MASK 0xFFFF /* EQ_B2_A - [15:0] */
+#define WM8993_EQ_B2_A_SHIFT 0 /* EQ_B2_A - [15:0] */
+#define WM8993_EQ_B2_A_WIDTH 16 /* EQ_B2_A - [15:0] */
+
+/*
+ * R108 (0x6C) - EQ11
+ */
+#define WM8993_EQ_B2_B_MASK 0xFFFF /* EQ_B2_B - [15:0] */
+#define WM8993_EQ_B2_B_SHIFT 0 /* EQ_B2_B - [15:0] */
+#define WM8993_EQ_B2_B_WIDTH 16 /* EQ_B2_B - [15:0] */
+
+/*
+ * R109 (0x6D) - EQ12
+ */
+#define WM8993_EQ_B2_C_MASK 0xFFFF /* EQ_B2_C - [15:0] */
+#define WM8993_EQ_B2_C_SHIFT 0 /* EQ_B2_C - [15:0] */
+#define WM8993_EQ_B2_C_WIDTH 16 /* EQ_B2_C - [15:0] */
+
+/*
+ * R110 (0x6E) - EQ13
+ */
+#define WM8993_EQ_B2_PG_MASK 0xFFFF /* EQ_B2_PG - [15:0] */
+#define WM8993_EQ_B2_PG_SHIFT 0 /* EQ_B2_PG - [15:0] */
+#define WM8993_EQ_B2_PG_WIDTH 16 /* EQ_B2_PG - [15:0] */
+
+/*
+ * R111 (0x6F) - EQ14
+ */
+#define WM8993_EQ_B3_A_MASK 0xFFFF /* EQ_B3_A - [15:0] */
+#define WM8993_EQ_B3_A_SHIFT 0 /* EQ_B3_A - [15:0] */
+#define WM8993_EQ_B3_A_WIDTH 16 /* EQ_B3_A - [15:0] */
+
+/*
+ * R112 (0x70) - EQ15
+ */
+#define WM8993_EQ_B3_B_MASK 0xFFFF /* EQ_B3_B - [15:0] */
+#define WM8993_EQ_B3_B_SHIFT 0 /* EQ_B3_B - [15:0] */
+#define WM8993_EQ_B3_B_WIDTH 16 /* EQ_B3_B - [15:0] */
+
+/*
+ * R113 (0x71) - EQ16
+ */
+#define WM8993_EQ_B3_C_MASK 0xFFFF /* EQ_B3_C - [15:0] */
+#define WM8993_EQ_B3_C_SHIFT 0 /* EQ_B3_C - [15:0] */
+#define WM8993_EQ_B3_C_WIDTH 16 /* EQ_B3_C - [15:0] */
+
+/*
+ * R114 (0x72) - EQ17
+ */
+#define WM8993_EQ_B3_PG_MASK 0xFFFF /* EQ_B3_PG - [15:0] */
+#define WM8993_EQ_B3_PG_SHIFT 0 /* EQ_B3_PG - [15:0] */
+#define WM8993_EQ_B3_PG_WIDTH 16 /* EQ_B3_PG - [15:0] */
+
+/*
+ * R115 (0x73) - EQ18
+ */
+#define WM8993_EQ_B4_A_MASK 0xFFFF /* EQ_B4_A - [15:0] */
+#define WM8993_EQ_B4_A_SHIFT 0 /* EQ_B4_A - [15:0] */
+#define WM8993_EQ_B4_A_WIDTH 16 /* EQ_B4_A - [15:0] */
+
+/*
+ * R116 (0x74) - EQ19
+ */
+#define WM8993_EQ_B4_B_MASK 0xFFFF /* EQ_B4_B - [15:0] */
+#define WM8993_EQ_B4_B_SHIFT 0 /* EQ_B4_B - [15:0] */
+#define WM8993_EQ_B4_B_WIDTH 16 /* EQ_B4_B - [15:0] */
+
+/*
+ * R117 (0x75) - EQ20
+ */
+#define WM8993_EQ_B4_C_MASK 0xFFFF /* EQ_B4_C - [15:0] */
+#define WM8993_EQ_B4_C_SHIFT 0 /* EQ_B4_C - [15:0] */
+#define WM8993_EQ_B4_C_WIDTH 16 /* EQ_B4_C - [15:0] */
+
+/*
+ * R118 (0x76) - EQ21
+ */
+#define WM8993_EQ_B4_PG_MASK 0xFFFF /* EQ_B4_PG - [15:0] */
+#define WM8993_EQ_B4_PG_SHIFT 0 /* EQ_B4_PG - [15:0] */
+#define WM8993_EQ_B4_PG_WIDTH 16 /* EQ_B4_PG - [15:0] */
+
+/*
+ * R119 (0x77) - EQ22
+ */
+#define WM8993_EQ_B5_A_MASK 0xFFFF /* EQ_B5_A - [15:0] */
+#define WM8993_EQ_B5_A_SHIFT 0 /* EQ_B5_A - [15:0] */
+#define WM8993_EQ_B5_A_WIDTH 16 /* EQ_B5_A - [15:0] */
+
+/*
+ * R120 (0x78) - EQ23
+ */
+#define WM8993_EQ_B5_B_MASK 0xFFFF /* EQ_B5_B - [15:0] */
+#define WM8993_EQ_B5_B_SHIFT 0 /* EQ_B5_B - [15:0] */
+#define WM8993_EQ_B5_B_WIDTH 16 /* EQ_B5_B - [15:0] */
+
+/*
+ * R121 (0x79) - EQ24
+ */
+#define WM8993_EQ_B5_PG_MASK 0xFFFF /* EQ_B5_PG - [15:0] */
+#define WM8993_EQ_B5_PG_SHIFT 0 /* EQ_B5_PG - [15:0] */
+#define WM8993_EQ_B5_PG_WIDTH 16 /* EQ_B5_PG - [15:0] */
+
+/*
+ * R122 (0x7A) - Digital Pulls
+ */
+#define WM8993_MCLK_PU 0x0080 /* MCLK_PU */
+#define WM8993_MCLK_PU_MASK 0x0080 /* MCLK_PU */
+#define WM8993_MCLK_PU_SHIFT 7 /* MCLK_PU */
+#define WM8993_MCLK_PU_WIDTH 1 /* MCLK_PU */
+#define WM8993_MCLK_PD 0x0040 /* MCLK_PD */
+#define WM8993_MCLK_PD_MASK 0x0040 /* MCLK_PD */
+#define WM8993_MCLK_PD_SHIFT 6 /* MCLK_PD */
+#define WM8993_MCLK_PD_WIDTH 1 /* MCLK_PD */
+#define WM8993_DACDAT_PU 0x0020 /* DACDAT_PU */
+#define WM8993_DACDAT_PU_MASK 0x0020 /* DACDAT_PU */
+#define WM8993_DACDAT_PU_SHIFT 5 /* DACDAT_PU */
+#define WM8993_DACDAT_PU_WIDTH 1 /* DACDAT_PU */
+#define WM8993_DACDAT_PD 0x0010 /* DACDAT_PD */
+#define WM8993_DACDAT_PD_MASK 0x0010 /* DACDAT_PD */
+#define WM8993_DACDAT_PD_SHIFT 4 /* DACDAT_PD */
+#define WM8993_DACDAT_PD_WIDTH 1 /* DACDAT_PD */
+#define WM8993_LRCLK_PU 0x0008 /* LRCLK_PU */
+#define WM8993_LRCLK_PU_MASK 0x0008 /* LRCLK_PU */
+#define WM8993_LRCLK_PU_SHIFT 3 /* LRCLK_PU */
+#define WM8993_LRCLK_PU_WIDTH 1 /* LRCLK_PU */
+#define WM8993_LRCLK_PD 0x0004 /* LRCLK_PD */
+#define WM8993_LRCLK_PD_MASK 0x0004 /* LRCLK_PD */
+#define WM8993_LRCLK_PD_SHIFT 2 /* LRCLK_PD */
+#define WM8993_LRCLK_PD_WIDTH 1 /* LRCLK_PD */
+#define WM8993_BCLK_PU 0x0002 /* BCLK_PU */
+#define WM8993_BCLK_PU_MASK 0x0002 /* BCLK_PU */
+#define WM8993_BCLK_PU_SHIFT 1 /* BCLK_PU */
+#define WM8993_BCLK_PU_WIDTH 1 /* BCLK_PU */
+#define WM8993_BCLK_PD 0x0001 /* BCLK_PD */
+#define WM8993_BCLK_PD_MASK 0x0001 /* BCLK_PD */
+#define WM8993_BCLK_PD_SHIFT 0 /* BCLK_PD */
+#define WM8993_BCLK_PD_WIDTH 1 /* BCLK_PD */
+
+/*
+ * R123 (0x7B) - DRC Control 1
+ */
+#define WM8993_DRC_ENA 0x8000 /* DRC_ENA */
+#define WM8993_DRC_ENA_MASK 0x8000 /* DRC_ENA */
+#define WM8993_DRC_ENA_SHIFT 15 /* DRC_ENA */
+#define WM8993_DRC_ENA_WIDTH 1 /* DRC_ENA */
+#define WM8993_DRC_DAC_PATH 0x4000 /* DRC_DAC_PATH */
+#define WM8993_DRC_DAC_PATH_MASK 0x4000 /* DRC_DAC_PATH */
+#define WM8993_DRC_DAC_PATH_SHIFT 14 /* DRC_DAC_PATH */
+#define WM8993_DRC_DAC_PATH_WIDTH 1 /* DRC_DAC_PATH */
+#define WM8993_DRC_SMOOTH_ENA 0x0800 /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_SMOOTH_ENA_MASK 0x0800 /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_SMOOTH_ENA_SHIFT 11 /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_SMOOTH_ENA_WIDTH 1 /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_QR_ENA 0x0400 /* DRC_QR_ENA */
+#define WM8993_DRC_QR_ENA_MASK 0x0400 /* DRC_QR_ENA */
+#define WM8993_DRC_QR_ENA_SHIFT 10 /* DRC_QR_ENA */
+#define WM8993_DRC_QR_ENA_WIDTH 1 /* DRC_QR_ENA */
+#define WM8993_DRC_ANTICLIP_ENA 0x0200 /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_ANTICLIP_ENA_MASK 0x0200 /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_ANTICLIP_ENA_SHIFT 9 /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_ANTICLIP_ENA_WIDTH 1 /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_HYST_ENA 0x0100 /* DRC_HYST_ENA */
+#define WM8993_DRC_HYST_ENA_MASK 0x0100 /* DRC_HYST_ENA */
+#define WM8993_DRC_HYST_ENA_SHIFT 8 /* DRC_HYST_ENA */
+#define WM8993_DRC_HYST_ENA_WIDTH 1 /* DRC_HYST_ENA */
+#define WM8993_DRC_THRESH_HYST_MASK 0x0030 /* DRC_THRESH_HYST - [5:4] */
+#define WM8993_DRC_THRESH_HYST_SHIFT 4 /* DRC_THRESH_HYST - [5:4] */
+#define WM8993_DRC_THRESH_HYST_WIDTH 2 /* DRC_THRESH_HYST - [5:4] */
+#define WM8993_DRC_MINGAIN_MASK 0x000C /* DRC_MINGAIN - [3:2] */
+#define WM8993_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [3:2] */
+#define WM8993_DRC_MINGAIN_WIDTH 2 /* DRC_MINGAIN - [3:2] */
+#define WM8993_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */
+#define WM8993_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */
+#define WM8993_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R124 (0x7C) - DRC Control 2
+ */
+#define WM8993_DRC_ATTACK_RATE_MASK 0xF000 /* DRC_ATTACK_RATE - [15:12] */
+#define WM8993_DRC_ATTACK_RATE_SHIFT 12 /* DRC_ATTACK_RATE - [15:12] */
+#define WM8993_DRC_ATTACK_RATE_WIDTH 4 /* DRC_ATTACK_RATE - [15:12] */
+#define WM8993_DRC_DECAY_RATE_MASK 0x0F00 /* DRC_DECAY_RATE - [11:8] */
+#define WM8993_DRC_DECAY_RATE_SHIFT 8 /* DRC_DECAY_RATE - [11:8] */
+#define WM8993_DRC_DECAY_RATE_WIDTH 4 /* DRC_DECAY_RATE - [11:8] */
+#define WM8993_DRC_THRESH_COMP_MASK 0x00FC /* DRC_THRESH_COMP - [7:2] */
+#define WM8993_DRC_THRESH_COMP_SHIFT 2 /* DRC_THRESH_COMP - [7:2] */
+#define WM8993_DRC_THRESH_COMP_WIDTH 6 /* DRC_THRESH_COMP - [7:2] */
+
+/*
+ * R125 (0x7D) - DRC Control 3
+ */
+#define WM8993_DRC_AMP_COMP_MASK 0xF800 /* DRC_AMP_COMP - [15:11] */
+#define WM8993_DRC_AMP_COMP_SHIFT 11 /* DRC_AMP_COMP - [15:11] */
+#define WM8993_DRC_AMP_COMP_WIDTH 5 /* DRC_AMP_COMP - [15:11] */
+#define WM8993_DRC_R0_SLOPE_COMP_MASK 0x0700 /* DRC_R0_SLOPE_COMP - [10:8] */
+#define WM8993_DRC_R0_SLOPE_COMP_SHIFT 8 /* DRC_R0_SLOPE_COMP - [10:8] */
+#define WM8993_DRC_R0_SLOPE_COMP_WIDTH 3 /* DRC_R0_SLOPE_COMP - [10:8] */
+#define WM8993_DRC_FF_DELAY 0x0080 /* DRC_FF_DELAY */
+#define WM8993_DRC_FF_DELAY_MASK 0x0080 /* DRC_FF_DELAY */
+#define WM8993_DRC_FF_DELAY_SHIFT 7 /* DRC_FF_DELAY */
+#define WM8993_DRC_FF_DELAY_WIDTH 1 /* DRC_FF_DELAY */
+#define WM8993_DRC_THRESH_QR_MASK 0x000C /* DRC_THRESH_QR - [3:2] */
+#define WM8993_DRC_THRESH_QR_SHIFT 2 /* DRC_THRESH_QR - [3:2] */
+#define WM8993_DRC_THRESH_QR_WIDTH 2 /* DRC_THRESH_QR - [3:2] */
+#define WM8993_DRC_RATE_QR_MASK 0x0003 /* DRC_RATE_QR - [1:0] */
+#define WM8993_DRC_RATE_QR_SHIFT 0 /* DRC_RATE_QR - [1:0] */
+#define WM8993_DRC_RATE_QR_WIDTH 2 /* DRC_RATE_QR - [1:0] */
+
+/*
+ * R126 (0x7E) - DRC Control 4
+ */
+#define WM8993_DRC_R1_SLOPE_COMP_MASK 0xE000 /* DRC_R1_SLOPE_COMP - [15:13] */
+#define WM8993_DRC_R1_SLOPE_COMP_SHIFT 13 /* DRC_R1_SLOPE_COMP - [15:13] */
+#define WM8993_DRC_R1_SLOPE_COMP_WIDTH 3 /* DRC_R1_SLOPE_COMP - [15:13] */
+#define WM8993_DRC_STARTUP_GAIN_MASK 0x1F00 /* DRC_STARTUP_GAIN - [12:8] */
+#define WM8993_DRC_STARTUP_GAIN_SHIFT 8 /* DRC_STARTUP_GAIN - [12:8] */
+#define WM8993_DRC_STARTUP_GAIN_WIDTH 5 /* DRC_STARTUP_GAIN - [12:8] */
+
+#endif
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 86fc57e25f9..c64e55aa63b 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -165,87 +165,23 @@ struct wm9081_priv {
int master;
int fll_fref;
int fll_fout;
+ int tdm_width;
struct wm9081_retune_mobile_config *retune;
};
-static int wm9081_reg_is_volatile(int reg)
+static int wm9081_volatile_register(unsigned int reg)
{
switch (reg) {
+ case WM9081_SOFTWARE_RESET:
+ return 1;
default:
return 0;
}
}
-static unsigned int wm9081_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- BUG_ON(reg > WM9081_MAX_REGISTER);
- return cache[reg];
-}
-
-static unsigned int wm9081_read_hw(struct snd_soc_codec *codec, u8 reg)
-{
- struct i2c_msg xfer[2];
- u16 data;
- int ret;
- struct i2c_client *client = codec->control_data;
-
- BUG_ON(reg > WM9081_MAX_REGISTER);
-
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 1;
- xfer[0].buf = &reg;
-
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = 2;
- xfer[1].buf = (u8 *)&data;
-
- ret = i2c_transfer(client->adapter, xfer, 2);
- if (ret != 2) {
- dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
- return 0;
- }
-
- return (data >> 8) | ((data & 0xff) << 8);
-}
-
-static unsigned int wm9081_read(struct snd_soc_codec *codec, unsigned int reg)
-{
- if (wm9081_reg_is_volatile(reg))
- return wm9081_read_hw(codec, reg);
- else
- return wm9081_read_reg_cache(codec, reg);
-}
-
-static int wm9081_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- u8 data[3];
-
- BUG_ON(reg > WM9081_MAX_REGISTER);
-
- if (!wm9081_reg_is_volatile(reg))
- cache[reg] = value;
-
- data[0] = reg;
- data[1] = value >> 8;
- data[2] = value & 0x00ff;
-
- if (codec->hw_write(codec->control_data, data, 3) == 3)
- return 0;
- else
- return -EIO;
-}
-
static int wm9081_reset(struct snd_soc_codec *codec)
{
- return wm9081_write(codec, WM9081_SOFTWARE_RESET, 0);
+ return snd_soc_write(codec, WM9081_SOFTWARE_RESET, 0);
}
static const DECLARE_TLV_DB_SCALE(drc_in_tlv, -4500, 75, 0);
@@ -356,7 +292,7 @@ static int speaker_mode_get(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
unsigned int reg;
- reg = wm9081_read(codec, WM9081_ANALOGUE_SPEAKER_2);
+ reg = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_2);
if (reg & WM9081_SPK_MODE)
ucontrol->value.integer.value[0] = 1;
else
@@ -375,8 +311,8 @@ static int speaker_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int reg_pwr = wm9081_read(codec, WM9081_POWER_MANAGEMENT);
- unsigned int reg2 = wm9081_read(codec, WM9081_ANALOGUE_SPEAKER_2);
+ unsigned int reg_pwr = snd_soc_read(codec, WM9081_POWER_MANAGEMENT);
+ unsigned int reg2 = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_2);
/* Are we changing anything? */
if (ucontrol->value.integer.value[0] ==
@@ -397,7 +333,7 @@ static int speaker_mode_put(struct snd_kcontrol *kcontrol,
reg2 &= ~WM9081_SPK_MODE;
}
- wm9081_write(codec, WM9081_ANALOGUE_SPEAKER_2, reg2);
+ snd_soc_write(codec, WM9081_ANALOGUE_SPEAKER_2, reg2);
return 0;
}
@@ -456,7 +392,7 @@ static int speaker_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
- unsigned int reg = wm9081_read(codec, WM9081_POWER_MANAGEMENT);
+ unsigned int reg = snd_soc_read(codec, WM9081_POWER_MANAGEMENT);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -468,7 +404,7 @@ static int speaker_event(struct snd_soc_dapm_widget *w,
break;
}
- wm9081_write(codec, WM9081_POWER_MANAGEMENT, reg);
+ snd_soc_write(codec, WM9081_POWER_MANAGEMENT, reg);
return 0;
}
@@ -607,7 +543,7 @@ static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id,
if (ret != 0)
return ret;
- reg5 = wm9081_read(codec, WM9081_FLL_CONTROL_5);
+ reg5 = snd_soc_read(codec, WM9081_FLL_CONTROL_5);
reg5 &= ~WM9081_FLL_CLK_SRC_MASK;
switch (fll_id) {
@@ -621,44 +557,44 @@ static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id,
}
/* Disable CLK_SYS while we reconfigure */
- clk_sys_reg = wm9081_read(codec, WM9081_CLOCK_CONTROL_3);
+ clk_sys_reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_3);
if (clk_sys_reg & WM9081_CLK_SYS_ENA)
- wm9081_write(codec, WM9081_CLOCK_CONTROL_3,
+ snd_soc_write(codec, WM9081_CLOCK_CONTROL_3,
clk_sys_reg & ~WM9081_CLK_SYS_ENA);
/* Any FLL configuration change requires that the FLL be
* disabled first. */
- reg1 = wm9081_read(codec, WM9081_FLL_CONTROL_1);
+ reg1 = snd_soc_read(codec, WM9081_FLL_CONTROL_1);
reg1 &= ~WM9081_FLL_ENA;
- wm9081_write(codec, WM9081_FLL_CONTROL_1, reg1);
+ snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1);
/* Apply the configuration */
if (fll_div.k)
reg1 |= WM9081_FLL_FRAC_MASK;
else
reg1 &= ~WM9081_FLL_FRAC_MASK;
- wm9081_write(codec, WM9081_FLL_CONTROL_1, reg1);
+ snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1);
- wm9081_write(codec, WM9081_FLL_CONTROL_2,
+ snd_soc_write(codec, WM9081_FLL_CONTROL_2,
(fll_div.fll_outdiv << WM9081_FLL_OUTDIV_SHIFT) |
(fll_div.fll_fratio << WM9081_FLL_FRATIO_SHIFT));
- wm9081_write(codec, WM9081_FLL_CONTROL_3, fll_div.k);
+ snd_soc_write(codec, WM9081_FLL_CONTROL_3, fll_div.k);
- reg4 = wm9081_read(codec, WM9081_FLL_CONTROL_4);
+ reg4 = snd_soc_read(codec, WM9081_FLL_CONTROL_4);
reg4 &= ~WM9081_FLL_N_MASK;
reg4 |= fll_div.n << WM9081_FLL_N_SHIFT;
- wm9081_write(codec, WM9081_FLL_CONTROL_4, reg4);
+ snd_soc_write(codec, WM9081_FLL_CONTROL_4, reg4);
reg5 &= ~WM9081_FLL_CLK_REF_DIV_MASK;
reg5 |= fll_div.fll_clk_ref_div << WM9081_FLL_CLK_REF_DIV_SHIFT;
- wm9081_write(codec, WM9081_FLL_CONTROL_5, reg5);
+ snd_soc_write(codec, WM9081_FLL_CONTROL_5, reg5);
/* Enable the FLL */
- wm9081_write(codec, WM9081_FLL_CONTROL_1, reg1 | WM9081_FLL_ENA);
+ snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1 | WM9081_FLL_ENA);
/* Then bring CLK_SYS up again if it was disabled */
if (clk_sys_reg & WM9081_CLK_SYS_ENA)
- wm9081_write(codec, WM9081_CLOCK_CONTROL_3, clk_sys_reg);
+ snd_soc_write(codec, WM9081_CLOCK_CONTROL_3, clk_sys_reg);
dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
@@ -707,6 +643,10 @@ static int configure_clock(struct snd_soc_codec *codec)
target > 3000000)
break;
}
+
+ if (i == ARRAY_SIZE(clk_sys_rates))
+ return -EINVAL;
+
} else if (wm9081->fs) {
for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) {
new_sysclk = clk_sys_rates[i].ratio
@@ -714,6 +654,10 @@ static int configure_clock(struct snd_soc_codec *codec)
if (new_sysclk > 3000000)
break;
}
+
+ if (i == ARRAY_SIZE(clk_sys_rates))
+ return -EINVAL;
+
} else {
new_sysclk = 12288000;
}
@@ -734,19 +678,19 @@ static int configure_clock(struct snd_soc_codec *codec)
return -EINVAL;
}
- reg = wm9081_read(codec, WM9081_CLOCK_CONTROL_1);
+ reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_1);
if (mclkdiv)
reg |= WM9081_MCLKDIV2;
else
reg &= ~WM9081_MCLKDIV2;
- wm9081_write(codec, WM9081_CLOCK_CONTROL_1, reg);
+ snd_soc_write(codec, WM9081_CLOCK_CONTROL_1, reg);
- reg = wm9081_read(codec, WM9081_CLOCK_CONTROL_3);
+ reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_3);
if (fll)
reg |= WM9081_CLK_SRC_SEL;
else
reg &= ~WM9081_CLK_SRC_SEL;
- wm9081_write(codec, WM9081_CLOCK_CONTROL_3, reg);
+ snd_soc_write(codec, WM9081_CLOCK_CONTROL_3, reg);
dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm9081->sysclk_rate);
@@ -846,76 +790,76 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VMID=2*40k */
- reg = wm9081_read(codec, WM9081_VMID_CONTROL);
+ reg = snd_soc_read(codec, WM9081_VMID_CONTROL);
reg &= ~WM9081_VMID_SEL_MASK;
reg |= 0x2;
- wm9081_write(codec, WM9081_VMID_CONTROL, reg);
+ snd_soc_write(codec, WM9081_VMID_CONTROL, reg);
/* Normal bias current */
- reg = wm9081_read(codec, WM9081_BIAS_CONTROL_1);
+ reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
reg &= ~WM9081_STBY_BIAS_ENA;
- wm9081_write(codec, WM9081_BIAS_CONTROL_1, reg);
+ snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);
break;
case SND_SOC_BIAS_STANDBY:
/* Initial cold start */
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Disable LINEOUT discharge */
- reg = wm9081_read(codec, WM9081_ANTI_POP_CONTROL);
+ reg = snd_soc_read(codec, WM9081_ANTI_POP_CONTROL);
reg &= ~WM9081_LINEOUT_DISCH;
- wm9081_write(codec, WM9081_ANTI_POP_CONTROL, reg);
+ snd_soc_write(codec, WM9081_ANTI_POP_CONTROL, reg);
/* Select startup bias source */
- reg = wm9081_read(codec, WM9081_BIAS_CONTROL_1);
+ reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
reg |= WM9081_BIAS_SRC | WM9081_BIAS_ENA;
- wm9081_write(codec, WM9081_BIAS_CONTROL_1, reg);
+ snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);
/* VMID 2*4k; Soft VMID ramp enable */
- reg = wm9081_read(codec, WM9081_VMID_CONTROL);
+ reg = snd_soc_read(codec, WM9081_VMID_CONTROL);
reg |= WM9081_VMID_RAMP | 0x6;
- wm9081_write(codec, WM9081_VMID_CONTROL, reg);
+ snd_soc_write(codec, WM9081_VMID_CONTROL, reg);
mdelay(100);
/* Normal bias enable & soft start off */
reg |= WM9081_BIAS_ENA;
reg &= ~WM9081_VMID_RAMP;
- wm9081_write(codec, WM9081_VMID_CONTROL, reg);
+ snd_soc_write(codec, WM9081_VMID_CONTROL, reg);
/* Standard bias source */
- reg = wm9081_read(codec, WM9081_BIAS_CONTROL_1);
+ reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
reg &= ~WM9081_BIAS_SRC;
- wm9081_write(codec, WM9081_BIAS_CONTROL_1, reg);
+ snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);
}
/* VMID 2*240k */
- reg = wm9081_read(codec, WM9081_BIAS_CONTROL_1);
+ reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
reg &= ~WM9081_VMID_SEL_MASK;
reg |= 0x40;
- wm9081_write(codec, WM9081_VMID_CONTROL, reg);
+ snd_soc_write(codec, WM9081_VMID_CONTROL, reg);
/* Standby bias current on */
- reg = wm9081_read(codec, WM9081_BIAS_CONTROL_1);
+ reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
reg |= WM9081_STBY_BIAS_ENA;
- wm9081_write(codec, WM9081_BIAS_CONTROL_1, reg);
+ snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);
break;
case SND_SOC_BIAS_OFF:
/* Startup bias source */
- reg = wm9081_read(codec, WM9081_BIAS_CONTROL_1);
+ reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
reg |= WM9081_BIAS_SRC;
- wm9081_write(codec, WM9081_BIAS_CONTROL_1, reg);
+ snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);
/* Disable VMID and biases with soft ramping */
- reg = wm9081_read(codec, WM9081_VMID_CONTROL);
+ reg = snd_soc_read(codec, WM9081_VMID_CONTROL);
reg &= ~(WM9081_VMID_SEL_MASK | WM9081_BIAS_ENA);
reg |= WM9081_VMID_RAMP;
- wm9081_write(codec, WM9081_VMID_CONTROL, reg);
+ snd_soc_write(codec, WM9081_VMID_CONTROL, reg);
/* Actively discharge LINEOUT */
- reg = wm9081_read(codec, WM9081_ANTI_POP_CONTROL);
+ reg = snd_soc_read(codec, WM9081_ANTI_POP_CONTROL);
reg |= WM9081_LINEOUT_DISCH;
- wm9081_write(codec, WM9081_ANTI_POP_CONTROL, reg);
+ snd_soc_write(codec, WM9081_ANTI_POP_CONTROL, reg);
break;
}
@@ -929,7 +873,7 @@ static int wm9081_set_dai_fmt(struct snd_soc_dai *dai,
{
struct snd_soc_codec *codec = dai->codec;
struct wm9081_priv *wm9081 = codec->private_data;
- unsigned int aif2 = wm9081_read(codec, WM9081_AUDIO_INTERFACE_2);
+ unsigned int aif2 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_2);
aif2 &= ~(WM9081_AIF_BCLK_INV | WM9081_AIF_LRCLK_INV |
WM9081_BCLK_DIR | WM9081_LRCLK_DIR | WM9081_AIF_FMT_MASK);
@@ -1010,7 +954,7 @@ static int wm9081_set_dai_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
- wm9081_write(codec, WM9081_AUDIO_INTERFACE_2, aif2);
+ snd_soc_write(codec, WM9081_AUDIO_INTERFACE_2, aif2);
return 0;
}
@@ -1024,47 +968,51 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream,
int ret, i, best, best_val, cur_val;
unsigned int clk_ctrl2, aif1, aif2, aif3, aif4;
- clk_ctrl2 = wm9081_read(codec, WM9081_CLOCK_CONTROL_2);
+ clk_ctrl2 = snd_soc_read(codec, WM9081_CLOCK_CONTROL_2);
clk_ctrl2 &= ~(WM9081_CLK_SYS_RATE_MASK | WM9081_SAMPLE_RATE_MASK);
- aif1 = wm9081_read(codec, WM9081_AUDIO_INTERFACE_1);
+ aif1 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_1);
- aif2 = wm9081_read(codec, WM9081_AUDIO_INTERFACE_2);
+ aif2 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_2);
aif2 &= ~WM9081_AIF_WL_MASK;
- aif3 = wm9081_read(codec, WM9081_AUDIO_INTERFACE_3);
+ aif3 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_3);
aif3 &= ~WM9081_BCLK_DIV_MASK;
- aif4 = wm9081_read(codec, WM9081_AUDIO_INTERFACE_4);
+ aif4 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_4);
aif4 &= ~WM9081_LRCLK_RATE_MASK;
- /* What BCLK do we need? */
wm9081->fs = params_rate(params);
- wm9081->bclk = 2 * wm9081->fs;
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- wm9081->bclk *= 16;
- break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- wm9081->bclk *= 20;
- aif2 |= 0x4;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- wm9081->bclk *= 24;
- aif2 |= 0x8;
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- wm9081->bclk *= 32;
- aif2 |= 0xc;
- break;
- default:
- return -EINVAL;
- }
- if (aif1 & WM9081_AIFDAC_TDM_MODE_MASK) {
+ if (wm9081->tdm_width) {
+ /* If TDM is set up then that fixes our BCLK. */
int slots = ((aif1 & WM9081_AIFDAC_TDM_MODE_MASK) >>
WM9081_AIFDAC_TDM_MODE_SHIFT) + 1;
- wm9081->bclk *= slots;
+
+ wm9081->bclk = wm9081->fs * wm9081->tdm_width * slots;
+ } else {
+ /* Otherwise work out a BCLK from the sample size */
+ wm9081->bclk = 2 * wm9081->fs;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ wm9081->bclk *= 16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ wm9081->bclk *= 20;
+ aif2 |= 0x4;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ wm9081->bclk *= 24;
+ aif2 |= 0x8;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ wm9081->bclk *= 32;
+ aif2 |= 0xc;
+ break;
+ default:
+ return -EINVAL;
+ }
}
dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm9081->bclk);
@@ -1149,22 +1097,22 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream,
s->name, s->rate);
/* If the EQ is enabled then disable it while we write out */
- eq1 = wm9081_read(codec, WM9081_EQ_1) & WM9081_EQ_ENA;
+ eq1 = snd_soc_read(codec, WM9081_EQ_1) & WM9081_EQ_ENA;
if (eq1 & WM9081_EQ_ENA)
- wm9081_write(codec, WM9081_EQ_1, 0);
+ snd_soc_write(codec, WM9081_EQ_1, 0);
/* Write out the other values */
for (i = 1; i < ARRAY_SIZE(s->config); i++)
- wm9081_write(codec, WM9081_EQ_1 + i, s->config[i]);
+ snd_soc_write(codec, WM9081_EQ_1 + i, s->config[i]);
eq1 |= (s->config[0] & ~WM9081_EQ_ENA);
- wm9081_write(codec, WM9081_EQ_1, eq1);
+ snd_soc_write(codec, WM9081_EQ_1, eq1);
}
- wm9081_write(codec, WM9081_CLOCK_CONTROL_2, clk_ctrl2);
- wm9081_write(codec, WM9081_AUDIO_INTERFACE_2, aif2);
- wm9081_write(codec, WM9081_AUDIO_INTERFACE_3, aif3);
- wm9081_write(codec, WM9081_AUDIO_INTERFACE_4, aif4);
+ snd_soc_write(codec, WM9081_CLOCK_CONTROL_2, clk_ctrl2);
+ snd_soc_write(codec, WM9081_AUDIO_INTERFACE_2, aif2);
+ snd_soc_write(codec, WM9081_AUDIO_INTERFACE_3, aif3);
+ snd_soc_write(codec, WM9081_AUDIO_INTERFACE_4, aif4);
return 0;
}
@@ -1174,14 +1122,14 @@ static int wm9081_digital_mute(struct snd_soc_dai *codec_dai, int mute)
struct snd_soc_codec *codec = codec_dai->codec;
unsigned int reg;
- reg = wm9081_read(codec, WM9081_DAC_DIGITAL_2);
+ reg = snd_soc_read(codec, WM9081_DAC_DIGITAL_2);
if (mute)
reg |= WM9081_DAC_MUTE;
else
reg &= ~WM9081_DAC_MUTE;
- wm9081_write(codec, WM9081_DAC_DIGITAL_2, reg);
+ snd_soc_write(codec, WM9081_DAC_DIGITAL_2, reg);
return 0;
}
@@ -1207,19 +1155,25 @@ static int wm9081_set_sysclk(struct snd_soc_dai *codec_dai,
}
static int wm9081_set_tdm_slot(struct snd_soc_dai *dai,
- unsigned int mask, int slots)
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
{
struct snd_soc_codec *codec = dai->codec;
- unsigned int aif1 = wm9081_read(codec, WM9081_AUDIO_INTERFACE_1);
+ struct wm9081_priv *wm9081 = codec->private_data;
+ unsigned int aif1 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_1);
aif1 &= ~(WM9081_AIFDAC_TDM_SLOT_MASK | WM9081_AIFDAC_TDM_MODE_MASK);
- if (slots < 1 || slots > 4)
+ if (slots < 0 || slots > 4)
return -EINVAL;
+ wm9081->tdm_width = slot_width;
+
+ if (slots == 0)
+ slots = 1;
+
aif1 |= (slots - 1) << WM9081_AIFDAC_TDM_MODE_SHIFT;
- switch (mask) {
+ switch (rx_mask) {
case 1:
break;
case 2:
@@ -1235,7 +1189,7 @@ static int wm9081_set_tdm_slot(struct snd_soc_dai *dai,
return -EINVAL;
}
- wm9081_write(codec, WM9081_AUDIO_INTERFACE_1, aif1);
+ snd_soc_write(codec, WM9081_AUDIO_INTERFACE_1, aif1);
return 0;
}
@@ -1357,7 +1311,7 @@ static int wm9081_resume(struct platform_device *pdev)
if (i == WM9081_SOFTWARE_RESET)
continue;
- wm9081_write(codec, i, reg_cache[i]);
+ snd_soc_write(codec, i, reg_cache[i]);
}
wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1377,7 +1331,8 @@ struct snd_soc_codec_device soc_codec_dev_wm9081 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm9081);
-static int wm9081_register(struct wm9081_priv *wm9081)
+static int wm9081_register(struct wm9081_priv *wm9081,
+ enum snd_soc_control_type control)
{
struct snd_soc_codec *codec = &wm9081->codec;
int ret;
@@ -1396,19 +1351,24 @@ static int wm9081_register(struct wm9081_priv *wm9081)
codec->private_data = wm9081;
codec->name = "WM9081";
codec->owner = THIS_MODULE;
- codec->read = wm9081_read;
- codec->write = wm9081_write;
codec->dai = &wm9081_dai;
codec->num_dai = 1;
codec->reg_cache_size = ARRAY_SIZE(wm9081->reg_cache);
codec->reg_cache = &wm9081->reg_cache;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm9081_set_bias_level;
+ codec->volatile_register = wm9081_volatile_register;
memcpy(codec->reg_cache, wm9081_reg_defaults,
sizeof(wm9081_reg_defaults));
- reg = wm9081_read_hw(codec, WM9081_SOFTWARE_RESET);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 16, control);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ reg = snd_soc_read(codec, WM9081_SOFTWARE_RESET);
if (reg != 0x9081) {
dev_err(codec->dev, "Device is not a WM9081: ID=0x%x\n", reg);
ret = -EINVAL;
@@ -1424,10 +1384,10 @@ static int wm9081_register(struct wm9081_priv *wm9081)
wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Enable zero cross by default */
- reg = wm9081_read(codec, WM9081_ANALOGUE_LINEOUT);
- wm9081_write(codec, WM9081_ANALOGUE_LINEOUT, reg | WM9081_LINEOUTZC);
- reg = wm9081_read(codec, WM9081_ANALOGUE_SPEAKER_PGA);
- wm9081_write(codec, WM9081_ANALOGUE_SPEAKER_PGA,
+ reg = snd_soc_read(codec, WM9081_ANALOGUE_LINEOUT);
+ snd_soc_write(codec, WM9081_ANALOGUE_LINEOUT, reg | WM9081_LINEOUTZC);
+ reg = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_PGA);
+ snd_soc_write(codec, WM9081_ANALOGUE_SPEAKER_PGA,
reg | WM9081_SPKPGAZC);
wm9081_dai.dev = codec->dev;
@@ -1482,7 +1442,7 @@ static __devinit int wm9081_i2c_probe(struct i2c_client *i2c,
codec->dev = &i2c->dev;
- return wm9081_register(wm9081);
+ return wm9081_register(wm9081, SND_SOC_I2C);
}
static __devexit int wm9081_i2c_remove(struct i2c_client *client)
@@ -1492,6 +1452,21 @@ static __devexit int wm9081_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM
+static int wm9081_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+ return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm9081_i2c_resume(struct i2c_client *client)
+{
+ return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm9081_i2c_suspend NULL
+#define wm9081_i2c_resume NULL
+#endif
+
static const struct i2c_device_id wm9081_i2c_id[] = {
{ "wm9081", 0 },
{ }
@@ -1505,6 +1480,8 @@ static struct i2c_driver wm9081_i2c_driver = {
},
.probe = wm9081_i2c_probe,
.remove = __devexit_p(wm9081_i2c_remove),
+ .suspend = wm9081_i2c_suspend,
+ .resume = wm9081_i2c_resume,
.id_table = wm9081_i2c_id,
};
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index fa88b463e71..e7d2840d9e5 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -406,7 +406,7 @@ static int wm9705_soc_probe(struct platform_device *pdev)
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm9705: failed to register card\n");
- goto pcm_err;
+ goto reset_err;
}
return 0;
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
new file mode 100644
index 00000000000..e542027eea8
--- /dev/null
+++ b/sound/soc/codecs/wm_hubs.c
@@ -0,0 +1,743 @@
+/*
+ * wm_hubs.c -- WM8993/4 common code
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8993.h"
+#include "wm_hubs.h"
+
+const DECLARE_TLV_DB_SCALE(wm_hubs_spkmix_tlv, -300, 300, 0);
+EXPORT_SYMBOL_GPL(wm_hubs_spkmix_tlv);
+
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(inmix_sw_tlv, 0, 3000, 0);
+static const DECLARE_TLV_DB_SCALE(inmix_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(earpiece_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(outmix_tlv, -2100, 300, 0);
+static const DECLARE_TLV_DB_SCALE(spkmixout_tlv, -1800, 600, 1);
+static const DECLARE_TLV_DB_SCALE(outpga_tlv, -5700, 100, 0);
+static const unsigned int spkboost_tlv[] = {
+ TLV_DB_RANGE_HEAD(7),
+ 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(line_tlv, -600, 600, 0);
+
+static const char *speaker_ref_text[] = {
+ "SPKVDD/2",
+ "VMID",
+};
+
+static const struct soc_enum speaker_ref =
+ SOC_ENUM_SINGLE(WM8993_SPEAKER_MIXER, 8, 2, speaker_ref_text);
+
+static const char *speaker_mode_text[] = {
+ "Class D",
+ "Class AB",
+};
+
+static const struct soc_enum speaker_mode =
+ SOC_ENUM_SINGLE(WM8993_SPKMIXR_ATTENUATION, 8, 2, speaker_mode_text);
+
+static void wait_for_dc_servo(struct snd_soc_codec *codec)
+{
+ unsigned int reg;
+ int count = 0;
+
+ dev_dbg(codec->dev, "Waiting for DC servo...\n");
+ do {
+ count++;
+ msleep(1);
+ reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0);
+ dev_dbg(codec->dev, "DC servo status: %x\n", reg);
+ } while ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
+ != WM8993_DCS_CAL_COMPLETE_MASK && count < 1000);
+
+ if ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
+ != WM8993_DCS_CAL_COMPLETE_MASK)
+ dev_err(codec->dev, "Timed out waiting for DC Servo\n");
+}
+
+/*
+ * Update the DC servo calibration on gain changes
+ */
+static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int ret;
+
+ ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
+
+ /* Only need to do this if the outputs are active */
+ if (snd_soc_read(codec, WM8993_POWER_MANAGEMENT_1)
+ & (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA))
+ snd_soc_update_bits(codec,
+ WM8993_DC_SERVO_0,
+ WM8993_DCS_TRIG_SINGLE_0 |
+ WM8993_DCS_TRIG_SINGLE_1,
+ WM8993_DCS_TRIG_SINGLE_0 |
+ WM8993_DCS_TRIG_SINGLE_1);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new analogue_snd_controls[] = {
+SOC_SINGLE_TLV("IN1L Volume", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 0, 31, 0,
+ inpga_tlv),
+SOC_SINGLE("IN1L Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN1L ZC Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("IN1R Volume", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 0, 31, 0,
+ inpga_tlv),
+SOC_SINGLE("IN1R Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN1R ZC Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 7, 1, 0),
+
+
+SOC_SINGLE_TLV("IN2L Volume", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 0, 31, 0,
+ inpga_tlv),
+SOC_SINGLE("IN2L Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN2L ZC Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("IN2R Volume", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 0, 31, 0,
+ inpga_tlv),
+SOC_SINGLE("IN2R Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN2R ZC Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("MIXINL IN2L Volume", WM8993_INPUT_MIXER3, 7, 1, 0,
+ inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINL IN1L Volume", WM8993_INPUT_MIXER3, 4, 1, 0,
+ inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINL Output Record Volume", WM8993_INPUT_MIXER3, 0, 7, 0,
+ inmix_tlv),
+SOC_SINGLE_TLV("MIXINL IN1LP Volume", WM8993_INPUT_MIXER5, 6, 7, 0, inmix_tlv),
+SOC_SINGLE_TLV("MIXINL Direct Voice Volume", WM8993_INPUT_MIXER5, 0, 6, 0,
+ inmix_tlv),
+
+SOC_SINGLE_TLV("MIXINR IN2R Volume", WM8993_INPUT_MIXER4, 7, 1, 0,
+ inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINR IN1R Volume", WM8993_INPUT_MIXER4, 4, 1, 0,
+ inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINR Output Record Volume", WM8993_INPUT_MIXER4, 0, 7, 0,
+ inmix_tlv),
+SOC_SINGLE_TLV("MIXINR IN1RP Volume", WM8993_INPUT_MIXER6, 6, 7, 0, inmix_tlv),
+SOC_SINGLE_TLV("MIXINR Direct Voice Volume", WM8993_INPUT_MIXER6, 0, 6, 0,
+ inmix_tlv),
+
+SOC_SINGLE_TLV("Left Output Mixer IN2RN Volume", WM8993_OUTPUT_MIXER5, 6, 7, 1,
+ outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN2LN Volume", WM8993_OUTPUT_MIXER3, 6, 7, 1,
+ outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN2LP Volume", WM8993_OUTPUT_MIXER3, 9, 7, 1,
+ outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN1L Volume", WM8993_OUTPUT_MIXER3, 0, 7, 1,
+ outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN1R Volume", WM8993_OUTPUT_MIXER3, 3, 7, 1,
+ outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer Right Input Volume",
+ WM8993_OUTPUT_MIXER5, 3, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer Left Input Volume",
+ WM8993_OUTPUT_MIXER5, 0, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer DAC Volume", WM8993_OUTPUT_MIXER5, 9, 7, 1,
+ outmix_tlv),
+
+SOC_SINGLE_TLV("Right Output Mixer IN2LN Volume",
+ WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN2RN Volume",
+ WM8993_OUTPUT_MIXER4, 6, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN1L Volume",
+ WM8993_OUTPUT_MIXER4, 3, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN1R Volume",
+ WM8993_OUTPUT_MIXER4, 0, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN2RP Volume",
+ WM8993_OUTPUT_MIXER4, 9, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer Left Input Volume",
+ WM8993_OUTPUT_MIXER6, 3, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer Right Input Volume",
+ WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer DAC Volume",
+ WM8993_OUTPUT_MIXER6, 9, 7, 1, outmix_tlv),
+
+SOC_DOUBLE_R_TLV("Output Volume", WM8993_LEFT_OPGA_VOLUME,
+ WM8993_RIGHT_OPGA_VOLUME, 0, 63, 0, outpga_tlv),
+SOC_DOUBLE_R("Output Switch", WM8993_LEFT_OPGA_VOLUME,
+ WM8993_RIGHT_OPGA_VOLUME, 6, 1, 0),
+SOC_DOUBLE_R("Output ZC Switch", WM8993_LEFT_OPGA_VOLUME,
+ WM8993_RIGHT_OPGA_VOLUME, 7, 1, 0),
+
+SOC_SINGLE("Earpiece Switch", WM8993_HPOUT2_VOLUME, 5, 1, 1),
+SOC_SINGLE_TLV("Earpiece Volume", WM8993_HPOUT2_VOLUME, 4, 1, 1, earpiece_tlv),
+
+SOC_SINGLE_TLV("SPKL Input Volume", WM8993_SPKMIXL_ATTENUATION,
+ 5, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKL IN1LP Volume", WM8993_SPKMIXL_ATTENUATION,
+ 4, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKL Output Volume", WM8993_SPKMIXL_ATTENUATION,
+ 3, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_SINGLE_TLV("SPKR Input Volume", WM8993_SPKMIXR_ATTENUATION,
+ 5, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKR IN1RP Volume", WM8993_SPKMIXR_ATTENUATION,
+ 4, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKR Output Volume", WM8993_SPKMIXR_ATTENUATION,
+ 3, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_DOUBLE_R_TLV("Speaker Mixer Volume",
+ WM8993_SPKMIXL_ATTENUATION, WM8993_SPKMIXR_ATTENUATION,
+ 0, 3, 1, spkmixout_tlv),
+SOC_DOUBLE_R_TLV("Speaker Volume",
+ WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
+ 0, 63, 0, outpga_tlv),
+SOC_DOUBLE_R("Speaker Switch",
+ WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
+ 6, 1, 0),
+SOC_DOUBLE_R("Speaker ZC Switch",
+ WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
+ 7, 1, 0),
+SOC_DOUBLE_TLV("Speaker Boost Volume", WM8993_SPKOUT_BOOST, 0, 3, 7, 0,
+ spkboost_tlv),
+SOC_ENUM("Speaker Reference", speaker_ref),
+SOC_ENUM("Speaker Mode", speaker_mode),
+
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .tlv.p = outpga_tlv,
+ .info = snd_soc_info_volsw_2r,
+ .get = snd_soc_get_volsw_2r, .put = wm8993_put_dc_servo,
+ .private_value = (unsigned long)&(struct soc_mixer_control) {
+ .reg = WM8993_LEFT_OUTPUT_VOLUME,
+ .rreg = WM8993_RIGHT_OUTPUT_VOLUME,
+ .shift = 0, .max = 63
+ },
+},
+SOC_DOUBLE_R("Headphone Switch", WM8993_LEFT_OUTPUT_VOLUME,
+ WM8993_RIGHT_OUTPUT_VOLUME, 6, 1, 0),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8993_LEFT_OUTPUT_VOLUME,
+ WM8993_RIGHT_OUTPUT_VOLUME, 7, 1, 0),
+
+SOC_SINGLE("LINEOUT1N Switch", WM8993_LINE_OUTPUTS_VOLUME, 6, 1, 1),
+SOC_SINGLE("LINEOUT1P Switch", WM8993_LINE_OUTPUTS_VOLUME, 5, 1, 1),
+SOC_SINGLE_TLV("LINEOUT1 Volume", WM8993_LINE_OUTPUTS_VOLUME, 4, 1, 1,
+ line_tlv),
+
+SOC_SINGLE("LINEOUT2N Switch", WM8993_LINE_OUTPUTS_VOLUME, 2, 1, 1),
+SOC_SINGLE("LINEOUT2P Switch", WM8993_LINE_OUTPUTS_VOLUME, 1, 1, 1),
+SOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1,
+ line_tlv),
+};
+
+static int hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ unsigned int reg = snd_soc_read(codec, WM8993_ANALOGUE_HP_0);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
+ WM8993_CP_ENA, WM8993_CP_ENA);
+
+ msleep(5);
+
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
+ WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA);
+
+ reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
+ snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
+
+ /* Start the DC servo */
+ snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
+ 0xFFFF,
+ WM8993_DCS_ENA_CHAN_0 |
+ WM8993_DCS_ENA_CHAN_1 |
+ WM8993_DCS_TRIG_STARTUP_1 |
+ WM8993_DCS_TRIG_STARTUP_0);
+ wait_for_dc_servo(codec);
+
+ reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
+ WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
+ snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ reg &= ~(WM8993_HPOUT1L_RMV_SHORT |
+ WM8993_HPOUT1L_DLY |
+ WM8993_HPOUT1L_OUTP |
+ WM8993_HPOUT1R_RMV_SHORT |
+ WM8993_HPOUT1R_DLY |
+ WM8993_HPOUT1R_OUTP);
+
+ snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
+ 0xffff, 0);
+
+ snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
+ 0);
+
+ snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
+ WM8993_CP_ENA, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int earpiece_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *control, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ u16 reg = snd_soc_read(codec, WM8993_ANTIPOP1) & ~WM8993_HPOUT2_IN_ENA;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ reg |= WM8993_HPOUT2_IN_ENA;
+ snd_soc_write(codec, WM8993_ANTIPOP1, reg);
+ udelay(50);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_write(codec, WM8993_ANTIPOP1, reg);
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new in1l_pga[] = {
+SOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0),
+SOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new in1r_pga[] = {
+SOC_DAPM_SINGLE("IN1RP Switch", WM8993_INPUT_MIXER2, 1, 1, 0),
+SOC_DAPM_SINGLE("IN1RN Switch", WM8993_INPUT_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new in2l_pga[] = {
+SOC_DAPM_SINGLE("IN2LP Switch", WM8993_INPUT_MIXER2, 7, 1, 0),
+SOC_DAPM_SINGLE("IN2LN Switch", WM8993_INPUT_MIXER2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new in2r_pga[] = {
+SOC_DAPM_SINGLE("IN2RP Switch", WM8993_INPUT_MIXER2, 3, 1, 0),
+SOC_DAPM_SINGLE("IN2RN Switch", WM8993_INPUT_MIXER2, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixinl[] = {
+SOC_DAPM_SINGLE("IN2L Switch", WM8993_INPUT_MIXER3, 8, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_INPUT_MIXER3, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixinr[] = {
+SOC_DAPM_SINGLE("IN2R Switch", WM8993_INPUT_MIXER4, 8, 1, 0),
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_INPUT_MIXER4, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_output_mixer[] = {
+SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0),
+SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0),
+SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0),
+SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0),
+SOC_DAPM_SINGLE("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0),
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_output_mixer[] = {
+SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0),
+SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0),
+SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0),
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0),
+SOC_DAPM_SINGLE("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new earpiece_mixer[] = {
+SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_HPOUT2_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_HPOUT2_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_HPOUT2_MIXER, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_speaker_boost[] = {
+SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 5, 1, 0),
+SOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 4, 1, 0),
+SOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_boost[] = {
+SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 2, 1, 0),
+SOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 1, 1, 0),
+SOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line1_mix[] = {
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER1, 2, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER1, 1, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line1n_mix[] = {
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 6, 1, 0),
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER1, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new line1p_mix[] = {
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line2_mix[] = {
+SOC_DAPM_SINGLE("IN2R Switch", WM8993_LINE_MIXER2, 2, 1, 0),
+SOC_DAPM_SINGLE("IN2L Switch", WM8993_LINE_MIXER2, 1, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line2n_mix[] = {
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER2, 6, 1, 0),
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new line2p_mix[] = {
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget analogue_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1LN"),
+SND_SOC_DAPM_INPUT("IN1LP"),
+SND_SOC_DAPM_INPUT("IN2LN"),
+SND_SOC_DAPM_INPUT("IN2LP/VXRN"),
+SND_SOC_DAPM_INPUT("IN1RN"),
+SND_SOC_DAPM_INPUT("IN1RP"),
+SND_SOC_DAPM_INPUT("IN2RN"),
+SND_SOC_DAPM_INPUT("IN2RP/VXRP"),
+
+SND_SOC_DAPM_MICBIAS("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0),
+SND_SOC_DAPM_MICBIAS("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0),
+
+SND_SOC_DAPM_MIXER("IN1L PGA", WM8993_POWER_MANAGEMENT_2, 6, 0,
+ in1l_pga, ARRAY_SIZE(in1l_pga)),
+SND_SOC_DAPM_MIXER("IN1R PGA", WM8993_POWER_MANAGEMENT_2, 4, 0,
+ in1r_pga, ARRAY_SIZE(in1r_pga)),
+
+SND_SOC_DAPM_MIXER("IN2L PGA", WM8993_POWER_MANAGEMENT_2, 7, 0,
+ in2l_pga, ARRAY_SIZE(in2l_pga)),
+SND_SOC_DAPM_MIXER("IN2R PGA", WM8993_POWER_MANAGEMENT_2, 5, 0,
+ in2r_pga, ARRAY_SIZE(in2r_pga)),
+
+/* Dummy widgets to represent differential paths */
+SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("MIXINL", WM8993_POWER_MANAGEMENT_2, 9, 0,
+ mixinl, ARRAY_SIZE(mixinl)),
+SND_SOC_DAPM_MIXER("MIXINR", WM8993_POWER_MANAGEMENT_2, 8, 0,
+ mixinr, ARRAY_SIZE(mixinr)),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", WM8993_POWER_MANAGEMENT_3, 5, 0,
+ left_output_mixer, ARRAY_SIZE(left_output_mixer)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0,
+ right_output_mixer, ARRAY_SIZE(right_output_mixer)),
+
+SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0,
+ NULL, 0,
+ hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
+ earpiece_mixer, ARRAY_SIZE(earpiece_mixer)),
+SND_SOC_DAPM_PGA_E("Earpiece Driver", WM8993_POWER_MANAGEMENT_1, 11, 0,
+ NULL, 0, earpiece_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_MIXER("SPKL Boost", SND_SOC_NOPM, 0, 0,
+ left_speaker_boost, ARRAY_SIZE(left_speaker_boost)),
+SND_SOC_DAPM_MIXER("SPKR Boost", SND_SOC_NOPM, 0, 0,
+ right_speaker_boost, ARRAY_SIZE(right_speaker_boost)),
+
+SND_SOC_DAPM_PGA("SPKL Driver", WM8993_POWER_MANAGEMENT_1, 12, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("SPKR Driver", WM8993_POWER_MANAGEMENT_1, 13, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_MIXER("LINEOUT1 Mixer", SND_SOC_NOPM, 0, 0,
+ line1_mix, ARRAY_SIZE(line1_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT2 Mixer", SND_SOC_NOPM, 0, 0,
+ line2_mix, ARRAY_SIZE(line2_mix)),
+
+SND_SOC_DAPM_MIXER("LINEOUT1N Mixer", SND_SOC_NOPM, 0, 0,
+ line1n_mix, ARRAY_SIZE(line1n_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT1P Mixer", SND_SOC_NOPM, 0, 0,
+ line1p_mix, ARRAY_SIZE(line1p_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT2N Mixer", SND_SOC_NOPM, 0, 0,
+ line2n_mix, ARRAY_SIZE(line2n_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT2P Mixer", SND_SOC_NOPM, 0, 0,
+ line2p_mix, ARRAY_SIZE(line2p_mix)),
+
+SND_SOC_DAPM_PGA("LINEOUT1N Driver", WM8993_POWER_MANAGEMENT_3, 13, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT1P Driver", WM8993_POWER_MANAGEMENT_3, 12, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT2N Driver", WM8993_POWER_MANAGEMENT_3, 11, 0,
+ NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT2P Driver", WM8993_POWER_MANAGEMENT_3, 10, 0,
+ NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2P"),
+SND_SOC_DAPM_OUTPUT("HPOUT2N"),
+SND_SOC_DAPM_OUTPUT("LINEOUT1P"),
+SND_SOC_DAPM_OUTPUT("LINEOUT1N"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2P"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2N"),
+};
+
+static const struct snd_soc_dapm_route analogue_routes[] = {
+ { "IN1L PGA", "IN1LP Switch", "IN1LP" },
+ { "IN1L PGA", "IN1LN Switch", "IN1LN" },
+
+ { "IN1R PGA", "IN1RP Switch", "IN1RP" },
+ { "IN1R PGA", "IN1RN Switch", "IN1RN" },
+
+ { "IN2L PGA", "IN2LP Switch", "IN2LP/VXRN" },
+ { "IN2L PGA", "IN2LN Switch", "IN2LN" },
+
+ { "IN2R PGA", "IN2RP Switch", "IN2RP/VXRP" },
+ { "IN2R PGA", "IN2RN Switch", "IN2RN" },
+
+ { "Direct Voice", NULL, "IN2LP/VXRN" },
+ { "Direct Voice", NULL, "IN2RP/VXRP" },
+
+ { "MIXINL", "IN1L Switch", "IN1L PGA" },
+ { "MIXINL", "IN2L Switch", "IN2L PGA" },
+ { "MIXINL", NULL, "Direct Voice" },
+ { "MIXINL", NULL, "IN1LP" },
+ { "MIXINL", NULL, "Left Output Mixer" },
+
+ { "MIXINR", "IN1R Switch", "IN1R PGA" },
+ { "MIXINR", "IN2R Switch", "IN2R PGA" },
+ { "MIXINR", NULL, "Direct Voice" },
+ { "MIXINR", NULL, "IN1RP" },
+ { "MIXINR", NULL, "Right Output Mixer" },
+
+ { "ADCL", NULL, "MIXINL" },
+ { "ADCR", NULL, "MIXINR" },
+
+ { "Left Output Mixer", "Left Input Switch", "MIXINL" },
+ { "Left Output Mixer", "Right Input Switch", "MIXINR" },
+ { "Left Output Mixer", "IN2RN Switch", "IN2RN" },
+ { "Left Output Mixer", "IN2LN Switch", "IN2LN" },
+ { "Left Output Mixer", "IN2LP Switch", "IN2LP/VXRN" },
+ { "Left Output Mixer", "IN1L Switch", "IN1L PGA" },
+ { "Left Output Mixer", "IN1R Switch", "IN1R PGA" },
+
+ { "Right Output Mixer", "Left Input Switch", "MIXINL" },
+ { "Right Output Mixer", "Right Input Switch", "MIXINR" },
+ { "Right Output Mixer", "IN2LN Switch", "IN2LN" },
+ { "Right Output Mixer", "IN2RN Switch", "IN2RN" },
+ { "Right Output Mixer", "IN2RP Switch", "IN2RP/VXRP" },
+ { "Right Output Mixer", "IN1L Switch", "IN1L PGA" },
+ { "Right Output Mixer", "IN1R Switch", "IN1R PGA" },
+
+ { "Left Output PGA", NULL, "Left Output Mixer" },
+ { "Left Output PGA", NULL, "TOCLK" },
+
+ { "Right Output PGA", NULL, "Right Output Mixer" },
+ { "Right Output PGA", NULL, "TOCLK" },
+
+ { "Earpiece Mixer", "Direct Voice Switch", "Direct Voice" },
+ { "Earpiece Mixer", "Left Output Switch", "Left Output PGA" },
+ { "Earpiece Mixer", "Right Output Switch", "Right Output PGA" },
+
+ { "Earpiece Driver", NULL, "Earpiece Mixer" },
+ { "HPOUT2N", NULL, "Earpiece Driver" },
+ { "HPOUT2P", NULL, "Earpiece Driver" },
+
+ { "SPKL", "Input Switch", "MIXINL" },
+ { "SPKL", "IN1LP Switch", "IN1LP" },
+ { "SPKL", "Output Switch", "Left Output Mixer" },
+ { "SPKL", NULL, "TOCLK" },
+
+ { "SPKR", "Input Switch", "MIXINR" },
+ { "SPKR", "IN1RP Switch", "IN1RP" },
+ { "SPKR", "Output Switch", "Right Output Mixer" },
+ { "SPKR", NULL, "TOCLK" },
+
+ { "SPKL Boost", "Direct Voice Switch", "Direct Voice" },
+ { "SPKL Boost", "SPKL Switch", "SPKL" },
+ { "SPKL Boost", "SPKR Switch", "SPKR" },
+
+ { "SPKR Boost", "Direct Voice Switch", "Direct Voice" },
+ { "SPKR Boost", "SPKR Switch", "SPKR" },
+ { "SPKR Boost", "SPKL Switch", "SPKL" },
+
+ { "SPKL Driver", NULL, "SPKL Boost" },
+ { "SPKL Driver", NULL, "CLK_SYS" },
+
+ { "SPKR Driver", NULL, "SPKR Boost" },
+ { "SPKR Driver", NULL, "CLK_SYS" },
+
+ { "SPKOUTLP", NULL, "SPKL Driver" },
+ { "SPKOUTLN", NULL, "SPKL Driver" },
+ { "SPKOUTRP", NULL, "SPKR Driver" },
+ { "SPKOUTRN", NULL, "SPKR Driver" },
+
+ { "Left Headphone Mux", "Mixer", "Left Output Mixer" },
+ { "Right Headphone Mux", "Mixer", "Right Output Mixer" },
+
+ { "Headphone PGA", NULL, "Left Headphone Mux" },
+ { "Headphone PGA", NULL, "Right Headphone Mux" },
+ { "Headphone PGA", NULL, "CLK_SYS" },
+
+ { "HPOUT1L", NULL, "Headphone PGA" },
+ { "HPOUT1R", NULL, "Headphone PGA" },
+
+ { "LINEOUT1N", NULL, "LINEOUT1N Driver" },
+ { "LINEOUT1P", NULL, "LINEOUT1P Driver" },
+ { "LINEOUT2N", NULL, "LINEOUT2N Driver" },
+ { "LINEOUT2P", NULL, "LINEOUT2P Driver" },
+};
+
+static const struct snd_soc_dapm_route lineout1_diff_routes[] = {
+ { "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" },
+ { "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" },
+ { "LINEOUT1 Mixer", "Output Switch", "Left Output Mixer" },
+
+ { "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" },
+ { "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" },
+};
+
+static const struct snd_soc_dapm_route lineout1_se_routes[] = {
+ { "LINEOUT1N Mixer", "Left Output Switch", "Left Output Mixer" },
+ { "LINEOUT1N Mixer", "Right Output Switch", "Left Output Mixer" },
+
+ { "LINEOUT1P Mixer", "Left Output Switch", "Left Output Mixer" },
+
+ { "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" },
+ { "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" },
+};
+
+static const struct snd_soc_dapm_route lineout2_diff_routes[] = {
+ { "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" },
+ { "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" },
+ { "LINEOUT2 Mixer", "Output Switch", "Right Output Mixer" },
+
+ { "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" },
+ { "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" },
+};
+
+static const struct snd_soc_dapm_route lineout2_se_routes[] = {
+ { "LINEOUT2N Mixer", "Left Output Switch", "Left Output Mixer" },
+ { "LINEOUT2N Mixer", "Right Output Switch", "Left Output Mixer" },
+
+ { "LINEOUT2P Mixer", "Right Output Switch", "Right Output Mixer" },
+
+ { "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" },
+ { "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" },
+};
+
+int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec)
+{
+ /* Latch volume update bits & default ZC on */
+ snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_1_2_VOLUME,
+ WM8993_IN1_VU, WM8993_IN1_VU);
+ snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_1_2_VOLUME,
+ WM8993_IN1_VU, WM8993_IN1_VU);
+ snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_3_4_VOLUME,
+ WM8993_IN2_VU, WM8993_IN2_VU);
+ snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME,
+ WM8993_IN2_VU, WM8993_IN2_VU);
+
+ snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT,
+ WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
+
+ snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME,
+ WM8993_HPOUT1L_ZC, WM8993_HPOUT1L_ZC);
+ snd_soc_update_bits(codec, WM8993_RIGHT_OUTPUT_VOLUME,
+ WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC,
+ WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC);
+
+ snd_soc_update_bits(codec, WM8993_LEFT_OPGA_VOLUME,
+ WM8993_MIXOUTL_ZC, WM8993_MIXOUTL_ZC);
+ snd_soc_update_bits(codec, WM8993_RIGHT_OPGA_VOLUME,
+ WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU,
+ WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU);
+
+ snd_soc_add_controls(codec, analogue_snd_controls,
+ ARRAY_SIZE(analogue_snd_controls));
+
+ snd_soc_dapm_new_controls(codec, analogue_dapm_widgets,
+ ARRAY_SIZE(analogue_dapm_widgets));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_controls);
+
+int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
+ int lineout1_diff, int lineout2_diff)
+{
+ snd_soc_dapm_add_routes(codec, analogue_routes,
+ ARRAY_SIZE(analogue_routes));
+
+ if (lineout1_diff)
+ snd_soc_dapm_add_routes(codec,
+ lineout1_diff_routes,
+ ARRAY_SIZE(lineout1_diff_routes));
+ else
+ snd_soc_dapm_add_routes(codec,
+ lineout1_se_routes,
+ ARRAY_SIZE(lineout1_se_routes));
+
+ if (lineout2_diff)
+ snd_soc_dapm_add_routes(codec,
+ lineout2_diff_routes,
+ ARRAY_SIZE(lineout2_diff_routes));
+ else
+ snd_soc_dapm_add_routes(codec,
+ lineout2_se_routes,
+ ARRAY_SIZE(lineout2_se_routes));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
+
+MODULE_DESCRIPTION("Shared support for Wolfson hubs products");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
new file mode 100644
index 00000000000..ec09cb6a293
--- /dev/null
+++ b/sound/soc/codecs/wm_hubs.h
@@ -0,0 +1,24 @@
+/*
+ * wm_hubs.h -- WM899x common code
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *
+ * 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 _WM_HUBS_H
+#define _WM_HUBS_H
+
+struct snd_soc_codec;
+
+extern const unsigned int wm_hubs_spkmix_tlv[];
+
+extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
+extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int);
+
+#endif
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 411a710be66..4dfd4ad9d90 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -9,6 +9,9 @@ config SND_DAVINCI_SOC
config SND_DAVINCI_SOC_I2S
tristate
+config SND_DAVINCI_SOC_MCASP
+ tristate
+
config SND_DAVINCI_SOC_EVM
tristate "SoC Audio support for DaVinci DM6446 or DM355 EVM"
depends on SND_DAVINCI_SOC
@@ -19,6 +22,16 @@ config SND_DAVINCI_SOC_EVM
Say Y if you want to add support for SoC audio on TI
DaVinci DM6446 or DM355 EVM platforms.
+config SND_DM6467_SOC_EVM
+ tristate "SoC Audio support for DaVinci DM6467 EVM"
+ depends on SND_DAVINCI_SOC && MACH_DAVINCI_DM6467_EVM
+ select SND_DAVINCI_SOC_MCASP
+ select SND_SOC_TLV320AIC3X
+ select SND_SOC_SPDIF
+
+ help
+ Say Y if you want to add support for SoC audio on TI
+
config SND_DAVINCI_SOC_SFFSDR
tristate "SoC Audio support for SFFSDR"
depends on SND_DAVINCI_SOC && MACH_SFFSDR
@@ -28,3 +41,23 @@ config SND_DAVINCI_SOC_SFFSDR
help
Say Y if you want to add support for SoC audio on
Lyrtech SFFSDR board.
+
+config SND_DA830_SOC_EVM
+ tristate "SoC Audio support for DA830/OMAP-L137 EVM"
+ depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA830_EVM
+ select SND_DAVINCI_SOC_MCASP
+ select SND_SOC_TLV320AIC3X
+
+ help
+ Say Y if you want to add support for SoC audio on TI
+ DA830/OMAP-L137 EVM
+
+config SND_DA850_SOC_EVM
+ tristate "SoC Audio support for DA850/OMAP-L138 EVM"
+ depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM
+ select SND_DAVINCI_SOC_MCASP
+ select SND_SOC_TLV320AIC3X
+ help
+ Say Y if you want to add support for SoC audio on TI
+ DA850/OMAP-L138 EVM
+
diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile
index ca8bae1fc3f..a6939d71b98 100644
--- a/sound/soc/davinci/Makefile
+++ b/sound/soc/davinci/Makefile
@@ -1,13 +1,18 @@
# DAVINCI Platform Support
snd-soc-davinci-objs := davinci-pcm.o
snd-soc-davinci-i2s-objs := davinci-i2s.o
+snd-soc-davinci-mcasp-objs:= davinci-mcasp.o
obj-$(CONFIG_SND_DAVINCI_SOC) += snd-soc-davinci.o
obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
+obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o
# DAVINCI Machine Support
snd-soc-evm-objs := davinci-evm.o
snd-soc-sffsdr-objs := davinci-sffsdr.o
obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o
+obj-$(CONFIG_SND_DM6467_SOC_EVM) += snd-soc-evm.o
+obj-$(CONFIG_SND_DA830_SOC_EVM) += snd-soc-evm.o
+obj-$(CONFIG_SND_DA850_SOC_EVM) += snd-soc-evm.o
obj-$(CONFIG_SND_DAVINCI_SOC_SFFSDR) += snd-soc-sffsdr.o
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index 58fd1cbedd8..67414f65940 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -14,6 +14,7 @@
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
@@ -27,9 +28,10 @@
#include <mach/mux.h>
#include "../codecs/tlv320aic3x.h"
+#include "../codecs/spdif_transciever.h"
#include "davinci-pcm.h"
#include "davinci-i2s.h"
-
+#include "davinci-mcasp.h"
#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF)
@@ -43,7 +45,7 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
unsigned sysclk;
/* ASP1 on DM355 EVM is clocked by an external oscillator */
- if (machine_is_davinci_dm355_evm())
+ if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm())
sysclk = 27000000;
/* ASP0 in DM6446 EVM is clocked by U55, as configured by
@@ -53,6 +55,10 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
else if (machine_is_davinci_evm())
sysclk = 12288000;
+ else if (machine_is_davinci_da830_evm() ||
+ machine_is_davinci_da850_evm())
+ sysclk = 24576000;
+
else
return -EINVAL;
@@ -144,6 +150,32 @@ static struct snd_soc_dai_link evm_dai = {
.ops = &evm_ops,
};
+static struct snd_soc_dai_link dm6467_evm_dai[] = {
+ {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_I2S_DAI],
+ .codec_dai = &aic3x_dai,
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+ },
+ {
+ .name = "McASP",
+ .stream_name = "spdif",
+ .cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_DIT_DAI],
+ .codec_dai = &dit_stub_dai,
+ .ops = &evm_ops,
+ },
+};
+static struct snd_soc_dai_link da8xx_evm_dai = {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_I2S_DAI],
+ .codec_dai = &aic3x_dai,
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+};
+
/* davinci-evm audio machine driver */
static struct snd_soc_card snd_soc_card_evm = {
.name = "DaVinci EVM",
@@ -152,73 +184,80 @@ static struct snd_soc_card snd_soc_card_evm = {
.num_links = 1,
};
-/* evm audio private data */
-static struct aic3x_setup_data evm_aic3x_setup = {
- .i2c_bus = 1,
- .i2c_address = 0x1b,
+/* davinci dm6467 evm audio machine driver */
+static struct snd_soc_card dm6467_snd_soc_card_evm = {
+ .name = "DaVinci DM6467 EVM",
+ .platform = &davinci_soc_platform,
+ .dai_link = dm6467_evm_dai,
+ .num_links = ARRAY_SIZE(dm6467_evm_dai),
};
+static struct snd_soc_card da830_snd_soc_card = {
+ .name = "DA830/OMAP-L137 EVM",
+ .dai_link = &da8xx_evm_dai,
+ .platform = &davinci_soc_platform,
+ .num_links = 1,
+};
+
+static struct snd_soc_card da850_snd_soc_card = {
+ .name = "DA850/OMAP-L138 EVM",
+ .dai_link = &da8xx_evm_dai,
+ .platform = &davinci_soc_platform,
+ .num_links = 1,
+};
+
+static struct aic3x_setup_data aic3x_setup;
+
/* evm audio subsystem */
static struct snd_soc_device evm_snd_devdata = {
.card = &snd_soc_card_evm,
.codec_dev = &soc_codec_dev_aic3x,
- .codec_data = &evm_aic3x_setup,
-};
-
-/* DM6446 EVM uses ASP0; line-out is a pair of RCA jacks */
-static struct resource evm_snd_resources[] = {
- {
- .start = DAVINCI_ASP0_BASE,
- .end = DAVINCI_ASP0_BASE + SZ_8K - 1,
- .flags = IORESOURCE_MEM,
- },
+ .codec_data = &aic3x_setup,
};
-static struct evm_snd_platform_data evm_snd_data = {
- .tx_dma_ch = DAVINCI_DMA_ASP0_TX,
- .rx_dma_ch = DAVINCI_DMA_ASP0_RX,
+/* evm audio subsystem */
+static struct snd_soc_device dm6467_evm_snd_devdata = {
+ .card = &dm6467_snd_soc_card_evm,
+ .codec_dev = &soc_codec_dev_aic3x,
+ .codec_data = &aic3x_setup,
};
-/* DM335 EVM uses ASP1; line-out is a stereo mini-jack */
-static struct resource dm335evm_snd_resources[] = {
- {
- .start = DAVINCI_ASP1_BASE,
- .end = DAVINCI_ASP1_BASE + SZ_8K - 1,
- .flags = IORESOURCE_MEM,
- },
+/* evm audio subsystem */
+static struct snd_soc_device da830_evm_snd_devdata = {
+ .card = &da830_snd_soc_card,
+ .codec_dev = &soc_codec_dev_aic3x,
+ .codec_data = &aic3x_setup,
};
-static struct evm_snd_platform_data dm335evm_snd_data = {
- .tx_dma_ch = DAVINCI_DMA_ASP1_TX,
- .rx_dma_ch = DAVINCI_DMA_ASP1_RX,
+static struct snd_soc_device da850_evm_snd_devdata = {
+ .card = &da850_snd_soc_card,
+ .codec_dev = &soc_codec_dev_aic3x,
+ .codec_data = &aic3x_setup,
};
static struct platform_device *evm_snd_device;
static int __init evm_init(void)
{
- struct resource *resources;
- unsigned num_resources;
- struct evm_snd_platform_data *data;
+ struct snd_soc_device *evm_snd_dev_data;
int index;
int ret;
if (machine_is_davinci_evm()) {
- davinci_cfg_reg(DM644X_MCBSP);
-
- resources = evm_snd_resources;
- num_resources = ARRAY_SIZE(evm_snd_resources);
- data = &evm_snd_data;
+ evm_snd_dev_data = &evm_snd_devdata;
index = 0;
} else if (machine_is_davinci_dm355_evm()) {
- /* we don't use ASP1 IRQs, or we'd need to mux them ... */
- davinci_cfg_reg(DM355_EVT8_ASP1_TX);
- davinci_cfg_reg(DM355_EVT9_ASP1_RX);
-
- resources = dm335evm_snd_resources;
- num_resources = ARRAY_SIZE(dm335evm_snd_resources);
- data = &dm335evm_snd_data;
+ evm_snd_dev_data = &evm_snd_devdata;
+ index = 1;
+ } else if (machine_is_davinci_dm6467_evm()) {
+ evm_snd_dev_data = &dm6467_evm_snd_devdata;
+ index = 0;
+ } else if (machine_is_davinci_da830_evm()) {
+ evm_snd_dev_data = &da830_evm_snd_devdata;
index = 1;
+ } else if (machine_is_davinci_da850_evm()) {
+ evm_snd_dev_data = &da850_evm_snd_devdata;
+ index = 0;
} else
return -EINVAL;
@@ -226,17 +265,8 @@ static int __init evm_init(void)
if (!evm_snd_device)
return -ENOMEM;
- platform_set_drvdata(evm_snd_device, &evm_snd_devdata);
- evm_snd_devdata.dev = &evm_snd_device->dev;
- platform_device_add_data(evm_snd_device, data, sizeof(*data));
-
- ret = platform_device_add_resources(evm_snd_device, resources,
- num_resources);
- if (ret) {
- platform_device_put(evm_snd_device);
- return ret;
- }
-
+ platform_set_drvdata(evm_snd_device, evm_snd_dev_data);
+ evm_snd_dev_data->dev = &evm_snd_device->dev;
ret = platform_device_add(evm_snd_device);
if (ret)
platform_device_put(evm_snd_device);
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index b1ea52fc83c..12a6c549ee6 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -22,6 +22,8 @@
#include <sound/initval.h>
#include <sound/soc.h>
+#include <mach/asp.h>
+
#include "davinci-pcm.h"
@@ -63,6 +65,7 @@
#define DAVINCI_MCBSP_RCR_RWDLEN1(v) ((v) << 5)
#define DAVINCI_MCBSP_RCR_RFRLEN1(v) ((v) << 8)
#define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16)
+#define DAVINCI_MCBSP_RCR_RFIG (1 << 18)
#define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21)
#define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5)
@@ -85,14 +88,6 @@
#define DAVINCI_MCBSP_PCR_FSRM (1 << 10)
#define DAVINCI_MCBSP_PCR_FSXM (1 << 11)
-#define MOD_REG_BIT(val, mask, set) do { \
- if (set) { \
- val |= mask; \
- } else { \
- val &= ~mask; \
- } \
-} while (0)
-
enum {
DAVINCI_MCBSP_WORD_8 = 0,
DAVINCI_MCBSP_WORD_12,
@@ -112,6 +107,10 @@ static struct davinci_pcm_dma_params davinci_i2s_pcm_in = {
struct davinci_mcbsp_dev {
void __iomem *base;
+#define MOD_DSP_A 0
+#define MOD_DSP_B 1
+ int mode;
+ u32 pcr;
struct clk *clk;
struct davinci_pcm_dma_params *dma_params[2];
};
@@ -127,96 +126,100 @@ static inline u32 davinci_mcbsp_read_reg(struct davinci_mcbsp_dev *dev, int reg)
return __raw_readl(dev->base + reg);
}
-static void davinci_mcbsp_start(struct snd_pcm_substream *substream)
+static void toggle_clock(struct davinci_mcbsp_dev *dev, int playback)
+{
+ u32 m = playback ? DAVINCI_MCBSP_PCR_CLKXP : DAVINCI_MCBSP_PCR_CLKRP;
+ /* The clock needs to toggle to complete reset.
+ * So, fake it by toggling the clk polarity.
+ */
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr ^ m);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr);
+}
+
+static void davinci_mcbsp_start(struct davinci_mcbsp_dev *dev,
+ struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_platform *platform = socdev->card->platform;
- u32 w;
- int ret;
-
- /* Start the sample generator and enable transmitter/receiver */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ u32 spcr;
+ u32 mask = playback ? DAVINCI_MCBSP_SPCR_XRST : DAVINCI_MCBSP_SPCR_RRST;
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ if (spcr & mask) {
+ /* start off disabled */
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG,
+ spcr & ~mask);
+ toggle_clock(dev, playback);
+ }
+ if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM |
+ DAVINCI_MCBSP_PCR_CLKXM | DAVINCI_MCBSP_PCR_CLKRM)) {
+ /* Start the sample generator */
+ spcr |= DAVINCI_MCBSP_SPCR_GRST;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+ }
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (playback) {
/* Stop the DMA to avoid data loss */
/* while the transmitter is out of reset to handle XSYNCERR */
if (platform->pcm_ops->trigger) {
- ret = platform->pcm_ops->trigger(substream,
+ int ret = platform->pcm_ops->trigger(substream,
SNDRV_PCM_TRIGGER_STOP);
if (ret < 0)
printk(KERN_DEBUG "Playback DMA stop failed\n");
}
/* Enable the transmitter */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr |= DAVINCI_MCBSP_SPCR_XRST;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
/* wait for any unexpected frame sync error to occur */
udelay(100);
/* Disable the transmitter to clear any outstanding XSYNCERR */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr &= ~DAVINCI_MCBSP_SPCR_XRST;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+ toggle_clock(dev, playback);
/* Restart the DMA */
if (platform->pcm_ops->trigger) {
- ret = platform->pcm_ops->trigger(substream,
+ int ret = platform->pcm_ops->trigger(substream,
SNDRV_PCM_TRIGGER_START);
if (ret < 0)
printk(KERN_DEBUG "Playback DMA start failed\n");
}
- /* Enable the transmitter */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
-
- } else {
-
- /* Enable the reciever */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
}
+ /* Enable transmitter or receiver */
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr |= mask;
- /* Start frame sync */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_FRST, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM)) {
+ /* Start frame sync */
+ spcr |= DAVINCI_MCBSP_SPCR_FRST;
+ }
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
}
-static void davinci_mcbsp_stop(struct snd_pcm_substream *substream)
+static void davinci_mcbsp_stop(struct davinci_mcbsp_dev *dev, int playback)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
- u32 w;
+ u32 spcr;
/* Reset transmitter/receiver and sample rate/frame sync generators */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST |
- DAVINCI_MCBSP_SPCR_FRST, 0);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
- else
- MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 0);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr &= ~(DAVINCI_MCBSP_SPCR_GRST | DAVINCI_MCBSP_SPCR_FRST);
+ spcr &= playback ? ~DAVINCI_MCBSP_SPCR_XRST : ~DAVINCI_MCBSP_SPCR_RRST;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+ toggle_clock(dev, playback);
}
static int davinci_i2s_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+ struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
-
+ struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
cpu_dai->dma_data = dev->dma_params[substream->stream];
-
return 0;
}
@@ -228,12 +231,11 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
unsigned int pcr;
unsigned int srgr;
- unsigned int rcr;
- unsigned int xcr;
srgr = DAVINCI_MCBSP_SRGR_FSGM |
DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
+ /* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
/* cpu is master */
@@ -258,11 +260,8 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return -EINVAL;
}
- rcr = DAVINCI_MCBSP_RCR_RFRLEN1(1);
- xcr = DAVINCI_MCBSP_XCR_XFIG | DAVINCI_MCBSP_XCR_XFRLEN1(1);
+ /* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_DSP_B:
- break;
case SND_SOC_DAIFMT_I2S:
/* Davinci doesn't support TRUE I2S, but some codecs will have
* the left and right channels contiguous. This allows
@@ -282,8 +281,10 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
*/
fmt ^= SND_SOC_DAIFMT_NB_IF;
case SND_SOC_DAIFMT_DSP_A:
- rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
- xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
+ dev->mode = MOD_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ dev->mode = MOD_DSP_B;
break;
default:
printk(KERN_ERR "%s:bad format\n", __func__);
@@ -343,9 +344,8 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return -EINVAL;
}
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
+ dev->pcr = pcr;
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
return 0;
}
@@ -353,31 +353,40 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct davinci_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
- struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+ struct davinci_pcm_dma_params *dma_params = dai->dma_data;
+ struct davinci_mcbsp_dev *dev = dai->private_data;
struct snd_interval *i = NULL;
int mcbsp_word_length;
- u32 w;
+ unsigned int rcr, xcr, srgr;
+ u32 spcr;
/* general line settings */
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- w |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ spcr |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
} else {
- w |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ spcr |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
}
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
- w = DAVINCI_MCBSP_SRGR_FSGM;
- MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1), 1);
+ srgr = DAVINCI_MCBSP_SRGR_FSGM;
+ srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
- MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1), 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
+ srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
+ rcr = DAVINCI_MCBSP_RCR_RFIG;
+ xcr = DAVINCI_MCBSP_XCR_XFIG;
+ if (dev->mode == MOD_DSP_B) {
+ rcr |= DAVINCI_MCBSP_RCR_RDATDLY(0);
+ xcr |= DAVINCI_MCBSP_XCR_XDATDLY(0);
+ } else {
+ rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
+ xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
+ }
/* Determine xfer data type */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
@@ -397,18 +406,31 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
- DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
+ dma_params->acnt = dma_params->data_type;
+ rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1);
+ xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1);
- } else {
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
- DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
+ rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
+ DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
+ xcr |= DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
+ DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
+ else
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
+ return 0;
+}
+
+static int davinci_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct davinci_mcbsp_dev *dev = dai->private_data;
+ int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ davinci_mcbsp_stop(dev, playback);
+ if ((dev->pcr & DAVINCI_MCBSP_PCR_FSXM) == 0) {
+ /* codec is master */
+ davinci_mcbsp_start(dev, substream);
}
return 0;
}
@@ -416,35 +438,72 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
+ struct davinci_mcbsp_dev *dev = dai->private_data;
int ret = 0;
+ int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ if ((dev->pcr & DAVINCI_MCBSP_PCR_FSXM) == 0)
+ return 0; /* return if codec is master */
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- davinci_mcbsp_start(substream);
+ davinci_mcbsp_start(dev, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- davinci_mcbsp_stop(substream);
+ davinci_mcbsp_stop(dev, playback);
break;
default:
ret = -EINVAL;
}
-
return ret;
}
-static int davinci_i2s_probe(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static void davinci_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct davinci_mcbsp_dev *dev = dai->private_data;
+ int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ davinci_mcbsp_stop(dev, playback);
+}
+
+#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
+
+static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
+ .startup = davinci_i2s_startup,
+ .shutdown = davinci_i2s_shutdown,
+ .prepare = davinci_i2s_prepare,
+ .trigger = davinci_i2s_trigger,
+ .hw_params = davinci_i2s_hw_params,
+ .set_fmt = davinci_i2s_set_dai_fmt,
+
+};
+
+struct snd_soc_dai davinci_i2s_dai = {
+ .name = "davinci-i2s",
+ .id = 0,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = DAVINCI_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = DAVINCI_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &davinci_i2s_dai_ops,
+
+};
+EXPORT_SYMBOL_GPL(davinci_i2s_dai);
+
+static int davinci_i2s_probe(struct platform_device *pdev)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_card *card = socdev->card;
- struct snd_soc_dai *cpu_dai = card->dai_link->cpu_dai;
+ struct snd_platform_data *pdata = pdev->dev.platform_data;
struct davinci_mcbsp_dev *dev;
- struct resource *mem, *ioarea;
- struct evm_snd_platform_data *pdata;
+ struct resource *mem, *ioarea, *res;
int ret;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -466,8 +525,6 @@ static int davinci_i2s_probe(struct platform_device *pdev,
goto err_release_region;
}
- cpu_dai->private_data = dev;
-
dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
ret = -ENODEV;
@@ -476,18 +533,37 @@ static int davinci_i2s_probe(struct platform_device *pdev,
clk_enable(dev->clk);
dev->base = (void __iomem *)IO_ADDRESS(mem->start);
- pdata = pdev->dev.platform_data;
dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &davinci_i2s_pcm_out;
- dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->channel = pdata->tx_dma_ch;
dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->dma_addr =
(dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG);
dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &davinci_i2s_pcm_in;
- dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->channel = pdata->rx_dma_ch;
dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->dma_addr =
(dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG);
+ /* first TX, then RX */
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no DMA resource\n");
+ ret = -ENXIO;
+ goto err_free_mem;
+ }
+ dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->channel = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "no DMA resource\n");
+ ret = -ENXIO;
+ goto err_free_mem;
+ }
+ dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->channel = res->start;
+
+ davinci_i2s_dai.private_data = dev;
+ ret = snd_soc_register_dai(&davinci_i2s_dai);
+ if (ret != 0)
+ goto err_free_mem;
+
return 0;
err_free_mem:
@@ -498,62 +574,40 @@ err_release_region:
return ret;
}
-static void davinci_i2s_remove(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static int davinci_i2s_remove(struct platform_device *pdev)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_card *card = socdev->card;
- struct snd_soc_dai *cpu_dai = card->dai_link->cpu_dai;
- struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
+ struct davinci_mcbsp_dev *dev = davinci_i2s_dai.private_data;
struct resource *mem;
+ snd_soc_unregister_dai(&davinci_i2s_dai);
clk_disable(dev->clk);
clk_put(dev->clk);
dev->clk = NULL;
-
kfree(dev);
-
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(mem->start, (mem->end - mem->start) + 1);
-}
-#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
-
-static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
- .startup = davinci_i2s_startup,
- .trigger = davinci_i2s_trigger,
- .hw_params = davinci_i2s_hw_params,
- .set_fmt = davinci_i2s_set_dai_fmt,
-};
+ return 0;
+}
-struct snd_soc_dai davinci_i2s_dai = {
- .name = "davinci-i2s",
- .id = 0,
- .probe = davinci_i2s_probe,
- .remove = davinci_i2s_remove,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = DAVINCI_I2S_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = DAVINCI_I2S_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = &davinci_i2s_dai_ops,
+static struct platform_driver davinci_mcbsp_driver = {
+ .probe = davinci_i2s_probe,
+ .remove = davinci_i2s_remove,
+ .driver = {
+ .name = "davinci-asp",
+ .owner = THIS_MODULE,
+ },
};
-EXPORT_SYMBOL_GPL(davinci_i2s_dai);
static int __init davinci_i2s_init(void)
{
- return snd_soc_register_dai(&davinci_i2s_dai);
+ return platform_driver_register(&davinci_mcbsp_driver);
}
module_init(davinci_i2s_init);
static void __exit davinci_i2s_exit(void)
{
- snd_soc_unregister_dai(&davinci_i2s_dai);
+ platform_driver_unregister(&davinci_mcbsp_driver);
}
module_exit(davinci_i2s_exit);
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
new file mode 100644
index 00000000000..eca22d7829d
--- /dev/null
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -0,0 +1,973 @@
+/*
+ * ALSA SoC McASP Audio Layer for TI DAVINCI processor
+ *
+ * Multi-channel Audio Serial Port Driver
+ *
+ * Author: Nirmal Pandey <n-pandey@ti.com>,
+ * Suresh Rajashekara <suresh.r@ti.com>
+ * Steve Chen <schen@.mvista.com>
+ *
+ * Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright: (C) 2009 Texas Instruments, India
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "davinci-pcm.h"
+#include "davinci-mcasp.h"
+
+/*
+ * McASP register definitions
+ */
+#define DAVINCI_MCASP_PID_REG 0x00
+#define DAVINCI_MCASP_PWREMUMGT_REG 0x04
+
+#define DAVINCI_MCASP_PFUNC_REG 0x10
+#define DAVINCI_MCASP_PDIR_REG 0x14
+#define DAVINCI_MCASP_PDOUT_REG 0x18
+#define DAVINCI_MCASP_PDSET_REG 0x1c
+
+#define DAVINCI_MCASP_PDCLR_REG 0x20
+
+#define DAVINCI_MCASP_TLGC_REG 0x30
+#define DAVINCI_MCASP_TLMR_REG 0x34
+
+#define DAVINCI_MCASP_GBLCTL_REG 0x44
+#define DAVINCI_MCASP_AMUTE_REG 0x48
+#define DAVINCI_MCASP_LBCTL_REG 0x4c
+
+#define DAVINCI_MCASP_TXDITCTL_REG 0x50
+
+#define DAVINCI_MCASP_GBLCTLR_REG 0x60
+#define DAVINCI_MCASP_RXMASK_REG 0x64
+#define DAVINCI_MCASP_RXFMT_REG 0x68
+#define DAVINCI_MCASP_RXFMCTL_REG 0x6c
+
+#define DAVINCI_MCASP_ACLKRCTL_REG 0x70
+#define DAVINCI_MCASP_AHCLKRCTL_REG 0x74
+#define DAVINCI_MCASP_RXTDM_REG 0x78
+#define DAVINCI_MCASP_EVTCTLR_REG 0x7c
+
+#define DAVINCI_MCASP_RXSTAT_REG 0x80
+#define DAVINCI_MCASP_RXTDMSLOT_REG 0x84
+#define DAVINCI_MCASP_RXCLKCHK_REG 0x88
+#define DAVINCI_MCASP_REVTCTL_REG 0x8c
+
+#define DAVINCI_MCASP_GBLCTLX_REG 0xa0
+#define DAVINCI_MCASP_TXMASK_REG 0xa4
+#define DAVINCI_MCASP_TXFMT_REG 0xa8
+#define DAVINCI_MCASP_TXFMCTL_REG 0xac
+
+#define DAVINCI_MCASP_ACLKXCTL_REG 0xb0
+#define DAVINCI_MCASP_AHCLKXCTL_REG 0xb4
+#define DAVINCI_MCASP_TXTDM_REG 0xb8
+#define DAVINCI_MCASP_EVTCTLX_REG 0xbc
+
+#define DAVINCI_MCASP_TXSTAT_REG 0xc0
+#define DAVINCI_MCASP_TXTDMSLOT_REG 0xc4
+#define DAVINCI_MCASP_TXCLKCHK_REG 0xc8
+#define DAVINCI_MCASP_XEVTCTL_REG 0xcc
+
+/* Left(even TDM Slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRA_REG 0x100
+/* Right(odd TDM slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRB_REG 0x118
+/* Left(even TDM slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRA_REG 0x130
+/* Right(odd TDM Slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRB_REG 0x148
+
+/* Serializer n Control Register */
+#define DAVINCI_MCASP_XRSRCTL_BASE_REG 0x180
+#define DAVINCI_MCASP_XRSRCTL_REG(n) (DAVINCI_MCASP_XRSRCTL_BASE_REG + \
+ (n << 2))
+
+/* Transmit Buffer for Serializer n */
+#define DAVINCI_MCASP_TXBUF_REG 0x200
+/* Receive Buffer for Serializer n */
+#define DAVINCI_MCASP_RXBUF_REG 0x280
+
+/* McASP FIFO Registers */
+#define DAVINCI_MCASP_WFIFOCTL (0x1010)
+#define DAVINCI_MCASP_WFIFOSTS (0x1014)
+#define DAVINCI_MCASP_RFIFOCTL (0x1018)
+#define DAVINCI_MCASP_RFIFOSTS (0x101C)
+
+/*
+ * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management
+ * Register Bits
+ */
+#define MCASP_FREE BIT(0)
+#define MCASP_SOFT BIT(1)
+
+/*
+ * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits
+ */
+#define AXR(n) (1<<n)
+#define PFUNC_AMUTE BIT(25)
+#define ACLKX BIT(26)
+#define AHCLKX BIT(27)
+#define AFSX BIT(28)
+#define ACLKR BIT(29)
+#define AHCLKR BIT(30)
+#define AFSR BIT(31)
+
+/*
+ * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits
+ */
+#define AXR(n) (1<<n)
+#define PDIR_AMUTE BIT(25)
+#define ACLKX BIT(26)
+#define AHCLKX BIT(27)
+#define AFSX BIT(28)
+#define ACLKR BIT(29)
+#define AHCLKR BIT(30)
+#define AFSR BIT(31)
+
+/*
+ * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits
+ */
+#define DITEN BIT(0) /* Transmit DIT mode enable/disable */
+#define VA BIT(2)
+#define VB BIT(3)
+
+/*
+ * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits
+ */
+#define TXROT(val) (val)
+#define TXSEL BIT(3)
+#define TXSSZ(val) (val<<4)
+#define TXPBIT(val) (val<<8)
+#define TXPAD(val) (val<<13)
+#define TXORD BIT(15)
+#define FSXDLY(val) (val<<16)
+
+/*
+ * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits
+ */
+#define RXROT(val) (val)
+#define RXSEL BIT(3)
+#define RXSSZ(val) (val<<4)
+#define RXPBIT(val) (val<<8)
+#define RXPAD(val) (val<<13)
+#define RXORD BIT(15)
+#define FSRDLY(val) (val<<16)
+
+/*
+ * DAVINCI_MCASP_TXFMCTL_REG - Transmit Frame Control Register Bits
+ */
+#define FSXPOL BIT(0)
+#define AFSXE BIT(1)
+#define FSXDUR BIT(4)
+#define FSXMOD(val) (val<<7)
+
+/*
+ * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits
+ */
+#define FSRPOL BIT(0)
+#define AFSRE BIT(1)
+#define FSRDUR BIT(4)
+#define FSRMOD(val) (val<<7)
+
+/*
+ * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits
+ */
+#define ACLKXDIV(val) (val)
+#define ACLKXE BIT(5)
+#define TX_ASYNC BIT(6)
+#define ACLKXPOL BIT(7)
+
+/*
+ * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits
+ */
+#define ACLKRDIV(val) (val)
+#define ACLKRE BIT(5)
+#define RX_ASYNC BIT(6)
+#define ACLKRPOL BIT(7)
+
+/*
+ * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control
+ * Register Bits
+ */
+#define AHCLKXDIV(val) (val)
+#define AHCLKXPOL BIT(14)
+#define AHCLKXE BIT(15)
+
+/*
+ * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control
+ * Register Bits
+ */
+#define AHCLKRDIV(val) (val)
+#define AHCLKRPOL BIT(14)
+#define AHCLKRE BIT(15)
+
+/*
+ * DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits
+ */
+#define MODE(val) (val)
+#define DISMOD (val)(val<<2)
+#define TXSTATE BIT(4)
+#define RXSTATE BIT(5)
+
+/*
+ * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits
+ */
+#define LBEN BIT(0)
+#define LBORD BIT(1)
+#define LBGENMODE(val) (val<<2)
+
+/*
+ * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration
+ */
+#define TXTDMS(n) (1<<n)
+
+/*
+ * DAVINCI_MCASP_RXTDMSLOT_REG - Receive TDM Slot Register configuration
+ */
+#define RXTDMS(n) (1<<n)
+
+/*
+ * DAVINCI_MCASP_GBLCTL_REG - Global Control Register Bits
+ */
+#define RXCLKRST BIT(0) /* Receiver Clock Divider Reset */
+#define RXHCLKRST BIT(1) /* Receiver High Frequency Clock Divider */
+#define RXSERCLR BIT(2) /* Receiver Serializer Clear */
+#define RXSMRST BIT(3) /* Receiver State Machine Reset */
+#define RXFSRST BIT(4) /* Frame Sync Generator Reset */
+#define TXCLKRST BIT(8) /* Transmitter Clock Divider Reset */
+#define TXHCLKRST BIT(9) /* Transmitter High Frequency Clock Divider*/
+#define TXSERCLR BIT(10) /* Transmit Serializer Clear */
+#define TXSMRST BIT(11) /* Transmitter State Machine Reset */
+#define TXFSRST BIT(12) /* Frame Sync Generator Reset */
+
+/*
+ * DAVINCI_MCASP_AMUTE_REG - Mute Control Register Bits
+ */
+#define MUTENA(val) (val)
+#define MUTEINPOL BIT(2)
+#define MUTEINENA BIT(3)
+#define MUTEIN BIT(4)
+#define MUTER BIT(5)
+#define MUTEX BIT(6)
+#define MUTEFSR BIT(7)
+#define MUTEFSX BIT(8)
+#define MUTEBADCLKR BIT(9)
+#define MUTEBADCLKX BIT(10)
+#define MUTERXDMAERR BIT(11)
+#define MUTETXDMAERR BIT(12)
+
+/*
+ * DAVINCI_MCASP_REVTCTL_REG - Receiver DMA Event Control Register bits
+ */
+#define RXDATADMADIS BIT(0)
+
+/*
+ * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits
+ */
+#define TXDATADMADIS BIT(0)
+
+/*
+ * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits
+ */
+#define FIFO_ENABLE BIT(16)
+#define NUMEVT_MASK (0xFF << 8)
+#define NUMDMA_MASK (0xFF)
+
+#define DAVINCI_MCASP_NUM_SERIALIZER 16
+
+static inline void mcasp_set_bits(void __iomem *reg, u32 val)
+{
+ __raw_writel(__raw_readl(reg) | val, reg);
+}
+
+static inline void mcasp_clr_bits(void __iomem *reg, u32 val)
+{
+ __raw_writel((__raw_readl(reg) & ~(val)), reg);
+}
+
+static inline void mcasp_mod_bits(void __iomem *reg, u32 val, u32 mask)
+{
+ __raw_writel((__raw_readl(reg) & ~mask) | val, reg);
+}
+
+static inline void mcasp_set_reg(void __iomem *reg, u32 val)
+{
+ __raw_writel(val, reg);
+}
+
+static inline u32 mcasp_get_reg(void __iomem *reg)
+{
+ return (unsigned int)__raw_readl(reg);
+}
+
+static inline void mcasp_set_ctl_reg(void __iomem *regs, u32 val)
+{
+ int i = 0;
+
+ mcasp_set_bits(regs, val);
+
+ /* programming GBLCTL needs to read back from GBLCTL and verfiy */
+ /* loop count is to avoid the lock-up */
+ for (i = 0; i < 1000; i++) {
+ if ((mcasp_get_reg(regs) & val) == val)
+ break;
+ }
+
+ if (i == 1000 && ((mcasp_get_reg(regs) & val) != val))
+ printk(KERN_ERR "GBLCTL write error\n");
+}
+
+static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct davinci_audio_dev *dev = cpu_dai->private_data;
+ cpu_dai->dma_data = dev->dma_params[substream->stream];
+ return 0;
+}
+
+static void mcasp_start_rx(struct davinci_audio_dev *dev)
+{
+ mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
+ mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
+ mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0);
+
+ mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+ mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0);
+
+ mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+ mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
+}
+
+static void mcasp_start_tx(struct davinci_audio_dev *dev)
+{
+ u8 offset = 0, i;
+ u32 cnt;
+
+ mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
+ mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+ mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
+
+ mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
+ mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
+ for (i = 0; i < dev->num_serializer; i++) {
+ if (dev->serial_dir[i] == TX_MODE) {
+ offset = i;
+ break;
+ }
+ }
+
+ /* wait for TX ready */
+ cnt = 0;
+ while (!(mcasp_get_reg(dev->base + DAVINCI_MCASP_XRSRCTL_REG(offset)) &
+ TXSTATE) && (cnt < 100000))
+ cnt++;
+
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
+}
+
+static void davinci_mcasp_start(struct davinci_audio_dev *dev, int stream)
+{
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mcasp_start_tx(dev);
+ else
+ mcasp_start_rx(dev);
+
+ /* enable FIFO */
+ if (dev->txnumevt)
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE);
+
+ if (dev->rxnumevt)
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE);
+}
+
+static void mcasp_stop_rx(struct davinci_audio_dev *dev)
+{
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, 0);
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+}
+
+static void mcasp_stop_tx(struct davinci_audio_dev *dev)
+{
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, 0);
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+}
+
+static void davinci_mcasp_stop(struct davinci_audio_dev *dev, int stream)
+{
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mcasp_stop_tx(dev);
+ else
+ mcasp_stop_rx(dev);
+
+ /* disable FIFO */
+ if (dev->txnumevt)
+ mcasp_clr_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE);
+
+ if (dev->rxnumevt)
+ mcasp_clr_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE);
+}
+
+static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct davinci_audio_dev *dev = cpu_dai->private_data;
+ void __iomem *base = dev->base;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* codec is clock and frame slave */
+ mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+ mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+ mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x7 << 26));
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ /* codec is clock master and frame slave */
+ mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+ mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+ mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x2d << 26));
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* codec is clock and frame master */
+ mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+ mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+ mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, (0x3f << 26));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+
+ mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+ break;
+
+ case SND_SOC_DAIFMT_NB_IF:
+ mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+
+ mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+ break;
+
+ case SND_SOC_DAIFMT_IB_IF:
+ mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+
+ mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+ break;
+
+ case SND_SOC_DAIFMT_NB_NF:
+ mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+
+ mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int davinci_config_channel_size(struct davinci_audio_dev *dev,
+ int channel_size)
+{
+ u32 fmt = 0;
+
+ switch (channel_size) {
+ case DAVINCI_AUDIO_WORD_8:
+ fmt = 0x03;
+ break;
+
+ case DAVINCI_AUDIO_WORD_12:
+ fmt = 0x05;
+ break;
+
+ case DAVINCI_AUDIO_WORD_16:
+ fmt = 0x07;
+ break;
+
+ case DAVINCI_AUDIO_WORD_20:
+ fmt = 0x09;
+ break;
+
+ case DAVINCI_AUDIO_WORD_24:
+ fmt = 0x0B;
+ break;
+
+ case DAVINCI_AUDIO_WORD_28:
+ fmt = 0x0D;
+ break;
+
+ case DAVINCI_AUDIO_WORD_32:
+ fmt = 0x0F;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG,
+ RXSSZ(fmt), RXSSZ(0x0F));
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
+ TXSSZ(fmt), TXSSZ(0x0F));
+ return 0;
+}
+
+static void davinci_hw_common_param(struct davinci_audio_dev *dev, int stream)
+{
+ int i;
+ u8 tx_ser = 0;
+ u8 rx_ser = 0;
+
+ /* Default configuration */
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
+
+ /* All PINS as McASP */
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_PFUNC_REG, 0x00000000);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+ mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG,
+ TXDATADMADIS);
+ } else {
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+ mcasp_clr_bits(dev->base + DAVINCI_MCASP_REVTCTL_REG,
+ RXDATADMADIS);
+ }
+
+ for (i = 0; i < dev->num_serializer; i++) {
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_XRSRCTL_REG(i),
+ dev->serial_dir[i]);
+ if (dev->serial_dir[i] == TX_MODE) {
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG,
+ AXR(i));
+ tx_ser++;
+ } else if (dev->serial_dir[i] == RX_MODE) {
+ mcasp_clr_bits(dev->base + DAVINCI_MCASP_PDIR_REG,
+ AXR(i));
+ rx_ser++;
+ }
+ }
+
+ if (dev->txnumevt && stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (dev->txnumevt * tx_ser > 64)
+ dev->txnumevt = 1;
+
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, tx_ser,
+ NUMDMA_MASK);
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
+ ((dev->txnumevt * tx_ser) << 8), NUMEVT_MASK);
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE);
+ }
+
+ if (dev->rxnumevt && stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (dev->rxnumevt * rx_ser > 64)
+ dev->rxnumevt = 1;
+
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, rx_ser,
+ NUMDMA_MASK);
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
+ ((dev->rxnumevt * rx_ser) << 8), NUMEVT_MASK);
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE);
+ }
+}
+
+static void davinci_hw_param(struct davinci_audio_dev *dev, int stream)
+{
+ int i, active_slots;
+ u32 mask = 0;
+
+ active_slots = (dev->tdm_slots > 31) ? 32 : dev->tdm_slots;
+ for (i = 0; i < active_slots; i++)
+ mask |= (1 << i);
+
+ mcasp_clr_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* bit stream is MSB first with no delay */
+ /* DSP_B mode */
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXE);
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, mask);
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXORD);
+
+ if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32))
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG,
+ FSXMOD(dev->tdm_slots), FSXMOD(0x1FF));
+ else
+ printk(KERN_ERR "playback tdm slot %d not supported\n",
+ dev->tdm_slots);
+
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, 0xFFFFFFFF);
+ mcasp_clr_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+ } else {
+ /* bit stream is MSB first with no delay */
+ /* DSP_B mode */
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, RXORD);
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRE);
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_RXTDM_REG, mask);
+
+ if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32))
+ mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG,
+ FSRMOD(dev->tdm_slots), FSRMOD(0x1FF));
+ else
+ printk(KERN_ERR "capture tdm slot %d not supported\n",
+ dev->tdm_slots);
+
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_RXMASK_REG, 0xFFFFFFFF);
+ mcasp_clr_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+ }
+}
+
+/* S/PDIF */
+static void davinci_hw_dit_param(struct davinci_audio_dev *dev)
+{
+ /* Set the PDIR for Serialiser as output */
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG, AFSX);
+
+ /* TXMASK for 24 bits */
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, 0x00FFFFFF);
+
+ /* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0
+ and LSB first */
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
+ TXROT(6) | TXSSZ(15));
+
+ /* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_TXFMCTL_REG,
+ AFSXE | FSXMOD(0x180));
+
+ /* Set the TX tdm : for all the slots */
+ mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF);
+
+ /* Set the TX clock controls : div = 1 and internal */
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG,
+ ACLKXE | TX_ASYNC);
+
+ mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
+
+ /* Only 44100 and 48000 are valid, both have the same setting */
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3));
+
+ /* Enable the DIT */
+ mcasp_set_bits(dev->base + DAVINCI_MCASP_TXDITCTL_REG, DITEN);
+}
+
+static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct davinci_audio_dev *dev = cpu_dai->private_data;
+ struct davinci_pcm_dma_params *dma_params =
+ dev->dma_params[substream->stream];
+ int word_length;
+ u8 numevt;
+
+ davinci_hw_common_param(dev, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ numevt = dev->txnumevt;
+ else
+ numevt = dev->rxnumevt;
+
+ if (!numevt)
+ numevt = 1;
+
+ if (dev->op_mode == DAVINCI_MCASP_DIT_MODE)
+ davinci_hw_dit_param(dev);
+ else
+ davinci_hw_param(dev, substream->stream);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ dma_params->data_type = 1;
+ word_length = DAVINCI_AUDIO_WORD_8;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dma_params->data_type = 2;
+ word_length = DAVINCI_AUDIO_WORD_16;
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dma_params->data_type = 4;
+ word_length = DAVINCI_AUDIO_WORD_32;
+ break;
+
+ default:
+ printk(KERN_WARNING "davinci-mcasp: unsupported PCM format");
+ return -EINVAL;
+ }
+
+ if (dev->version == MCASP_VERSION_2) {
+ dma_params->data_type *= numevt;
+ dma_params->acnt = 4 * numevt;
+ } else
+ dma_params->acnt = dma_params->data_type;
+
+ davinci_config_channel_size(dev, word_length);
+
+ return 0;
+}
+
+static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct davinci_audio_dev *dev = rtd->dai->cpu_dai->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ davinci_mcasp_start(dev, substream->stream);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ davinci_mcasp_stop(dev, substream->stream);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
+ .startup = davinci_mcasp_startup,
+ .trigger = davinci_mcasp_trigger,
+ .hw_params = davinci_mcasp_hw_params,
+ .set_fmt = davinci_mcasp_set_dai_fmt,
+
+};
+
+struct snd_soc_dai davinci_mcasp_dai[] = {
+ {
+ .name = "davinci-i2s",
+ .id = 0,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = DAVINCI_MCASP_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = DAVINCI_MCASP_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &davinci_mcasp_dai_ops,
+
+ },
+ {
+ .name = "davinci-dit",
+ .id = 1,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 384,
+ .rates = DAVINCI_MCASP_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &davinci_mcasp_dai_ops,
+ },
+
+};
+EXPORT_SYMBOL_GPL(davinci_mcasp_dai);
+
+static int davinci_mcasp_probe(struct platform_device *pdev)
+{
+ struct davinci_pcm_dma_params *dma_data;
+ struct resource *mem, *ioarea, *res;
+ struct snd_platform_data *pdata;
+ struct davinci_audio_dev *dev;
+ int count = 0;
+ int ret = 0;
+
+ dev = kzalloc(sizeof(struct davinci_audio_dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dma_data = kzalloc(sizeof(struct davinci_pcm_dma_params) * 2,
+ GFP_KERNEL);
+ if (!dma_data) {
+ ret = -ENOMEM;
+ goto err_release_dev;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ ret = -ENODEV;
+ goto err_release_data;
+ }
+
+ ioarea = request_mem_region(mem->start,
+ (mem->end - mem->start) + 1, pdev->name);
+ if (!ioarea) {
+ dev_err(&pdev->dev, "Audio region already claimed\n");
+ ret = -EBUSY;
+ goto err_release_data;
+ }
+
+ pdata = pdev->dev.platform_data;
+ dev->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk)) {
+ ret = -ENODEV;
+ goto err_release_region;
+ }
+
+ clk_enable(dev->clk);
+
+ dev->base = (void __iomem *)IO_ADDRESS(mem->start);
+ dev->op_mode = pdata->op_mode;
+ dev->tdm_slots = pdata->tdm_slots;
+ dev->num_serializer = pdata->num_serializer;
+ dev->serial_dir = pdata->serial_dir;
+ dev->codec_fmt = pdata->codec_fmt;
+ dev->version = pdata->version;
+ dev->txnumevt = pdata->txnumevt;
+ dev->rxnumevt = pdata->rxnumevt;
+
+ dma_data[count].name = "I2S PCM Stereo out";
+ dma_data[count].eventq_no = pdata->eventq_no;
+ dma_data[count].dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
+ io_v2p(dev->base));
+ dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &dma_data[count];
+
+ /* first TX, then RX */
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no DMA resource\n");
+ goto err_release_region;
+ }
+
+ dma_data[count].channel = res->start;
+ count++;
+ dma_data[count].name = "I2S PCM Stereo in";
+ dma_data[count].eventq_no = pdata->eventq_no;
+ dma_data[count].dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
+ io_v2p(dev->base));
+ dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &dma_data[count];
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "no DMA resource\n");
+ goto err_release_region;
+ }
+
+ dma_data[count].channel = res->start;
+ davinci_mcasp_dai[pdata->op_mode].private_data = dev;
+ davinci_mcasp_dai[pdata->op_mode].dev = &pdev->dev;
+ ret = snd_soc_register_dai(&davinci_mcasp_dai[pdata->op_mode]);
+
+ if (ret != 0)
+ goto err_release_region;
+ return 0;
+
+err_release_region:
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+err_release_data:
+ kfree(dma_data);
+err_release_dev:
+ kfree(dev);
+
+ return ret;
+}
+
+static int davinci_mcasp_remove(struct platform_device *pdev)
+{
+ struct snd_platform_data *pdata = pdev->dev.platform_data;
+ struct davinci_pcm_dma_params *dma_data;
+ struct davinci_audio_dev *dev;
+ struct resource *mem;
+
+ snd_soc_unregister_dai(&davinci_mcasp_dai[pdata->op_mode]);
+ dev = davinci_mcasp_dai[pdata->op_mode].private_data;
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+ dev->clk = NULL;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+
+ dma_data = dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
+ kfree(dma_data);
+ kfree(dev);
+
+ return 0;
+}
+
+static struct platform_driver davinci_mcasp_driver = {
+ .probe = davinci_mcasp_probe,
+ .remove = davinci_mcasp_remove,
+ .driver = {
+ .name = "davinci-mcasp",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init davinci_mcasp_init(void)
+{
+ return platform_driver_register(&davinci_mcasp_driver);
+}
+module_init(davinci_mcasp_init);
+
+static void __exit davinci_mcasp_exit(void)
+{
+ platform_driver_unregister(&davinci_mcasp_driver);
+}
+module_exit(davinci_mcasp_exit);
+
+MODULE_AUTHOR("Steve Chen");
+MODULE_DESCRIPTION("TI DAVINCI McASP SoC Interface");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
new file mode 100644
index 00000000000..554354c1cc2
--- /dev/null
+++ b/sound/soc/davinci/davinci-mcasp.h
@@ -0,0 +1,60 @@
+/*
+ * ALSA SoC McASP Audio Layer for TI DAVINCI processor
+ *
+ * MCASP related definitions
+ *
+ * Author: Nirmal Pandey <n-pandey@ti.com>,
+ * Suresh Rajashekara <suresh.r@ti.com>
+ * Steve Chen <schen@.mvista.com>
+ *
+ * Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright: (C) 2009 Texas Instruments, India
+ *
+ * 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 DAVINCI_MCASP_H
+#define DAVINCI_MCASP_H
+
+#include <linux/io.h>
+#include <mach/asp.h>
+#include "davinci-pcm.h"
+
+extern struct snd_soc_dai davinci_mcasp_dai[];
+
+#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_96000
+#define DAVINCI_MCASP_I2S_DAI 0
+#define DAVINCI_MCASP_DIT_DAI 1
+
+enum {
+ DAVINCI_AUDIO_WORD_8 = 0,
+ DAVINCI_AUDIO_WORD_12,
+ DAVINCI_AUDIO_WORD_16,
+ DAVINCI_AUDIO_WORD_20,
+ DAVINCI_AUDIO_WORD_24,
+ DAVINCI_AUDIO_WORD_32,
+ DAVINCI_AUDIO_WORD_28, /* This is only valid for McASP */
+};
+
+struct davinci_audio_dev {
+ void __iomem *base;
+ int sample_rate;
+ struct clk *clk;
+ struct davinci_pcm_dma_params *dma_params[2];
+ unsigned int codec_fmt;
+
+ /* McASP specific data */
+ int tdm_slots;
+ u8 op_mode;
+ u8 num_serializer;
+ u8 *serial_dir;
+ u8 version;
+
+ /* McASP FIFO related */
+ u8 txnumevt;
+ u8 rxnumevt;
+};
+
+#endif /* DAVINCI_MCASP_H */
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index a0599658848..091dacb78b4 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -67,6 +67,7 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
dma_addr_t src, dst;
unsigned short src_bidx, dst_bidx;
unsigned int data_type;
+ unsigned short acnt;
unsigned int count;
period_size = snd_pcm_lib_period_bytes(substream);
@@ -91,11 +92,12 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
dst_bidx = data_type;
}
+ acnt = prtd->params->acnt;
edma_set_src(lch, src, INCR, W8BIT);
edma_set_dest(lch, dst, INCR, W8BIT);
edma_set_src_index(lch, src_bidx, 0);
edma_set_dest_index(lch, dst_bidx, 0);
- edma_set_transfer_params(lch, data_type, count, 1, 0, ASYNC);
+ edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC);
prtd->period++;
if (unlikely(prtd->period >= runtime->periods))
@@ -206,6 +208,7 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
/* Copy self-linked parameter RAM entry into master channel */
edma_read_slot(prtd->slave_lch, &temp);
edma_write_slot(prtd->master_lch, &temp);
+ davinci_pcm_enqueue_dma(substream);
return 0;
}
@@ -243,6 +246,11 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream)
int ret = 0;
snd_soc_set_runtime_hwparams(substream, &davinci_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;
prtd = kzalloc(sizeof(struct davinci_runtime_data), GFP_KERNEL);
if (prtd == NULL)
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h
index 62cb4eb07e3..63d96253c73 100644
--- a/sound/soc/davinci/davinci-pcm.h
+++ b/sound/soc/davinci/davinci-pcm.h
@@ -12,17 +12,20 @@
#ifndef _DAVINCI_PCM_H
#define _DAVINCI_PCM_H
+#include <mach/edma.h>
+#include <mach/asp.h>
+
+
struct davinci_pcm_dma_params {
- char *name; /* stream identifier */
- int channel; /* sync dma channel ID */
- dma_addr_t dma_addr; /* device physical address for DMA */
- unsigned int data_type; /* xfer data type */
+ char *name; /* stream identifier */
+ int channel; /* sync dma channel ID */
+ unsigned short acnt;
+ dma_addr_t dma_addr; /* device physical address for DMA */
+ enum dma_event_q eventq_no; /* event queue number */
+ unsigned char data_type; /* xfer data type */
+ unsigned char convert_mono_stereo;
};
-struct evm_snd_platform_data {
- int tx_dma_ch;
- int rx_dma_ch;
-};
extern struct snd_soc_platform davinci_soc_platform;
diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c
index 85b0e756950..3326e2a1e86 100644
--- a/sound/soc/fsl/efika-audio-fabric.c
+++ b/sound/soc/fsl/efika-audio-fabric.c
@@ -30,6 +30,8 @@
#include "mpc5200_psc_ac97.h"
#include "../codecs/stac9766.h"
+#define DRV_NAME "efika-audio-fabric"
+
static struct snd_soc_device device;
static struct snd_soc_card card;
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c
index f0a2d407199..9ff62e3a9b1 100644
--- a/sound/soc/fsl/mpc5200_dma.c
+++ b/sound/soc/fsl/mpc5200_dma.c
@@ -69,6 +69,23 @@ static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s)
static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s)
{
+ if (s->appl_ptr > s->runtime->control->appl_ptr) {
+ /*
+ * In this case s->runtime->control->appl_ptr has wrapped around.
+ * Play the data to the end of the boundary, then wrap our own
+ * appl_ptr back around.
+ */
+ while (s->appl_ptr < s->runtime->boundary) {
+ if (bcom_queue_full(s->bcom_task))
+ return;
+
+ s->appl_ptr += s->period_size;
+
+ psc_dma_bcom_enqueue_next_buffer(s);
+ }
+ s->appl_ptr -= s->runtime->boundary;
+ }
+
while (s->appl_ptr < s->runtime->control->appl_ptr) {
if (bcom_queue_full(s->bcom_task))
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c
index 7eb549985d4..c4ae3e096bb 100644
--- a/sound/soc/fsl/mpc5200_psc_ac97.c
+++ b/sound/soc/fsl/mpc5200_psc_ac97.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
+#include <linux/delay.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -112,7 +113,7 @@ static void psc_ac97_cold_reset(struct snd_ac97 *ac97)
out_8(&regs->op1, MPC52xx_PSC_OP_RES);
udelay(10);
out_8(&regs->op0, MPC52xx_PSC_OP_RES);
- udelay(50);
+ msleep(1);
psc_ac97_warm_reset(ac97);
}
diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c
index 8766f7a3893..b928ef7d28e 100644
--- a/sound/soc/fsl/pcm030-audio-fabric.c
+++ b/sound/soc/fsl/pcm030-audio-fabric.c
@@ -30,6 +30,8 @@
#include "mpc5200_psc_ac97.h"
#include "../codecs/wm9712.h"
+#define DRV_NAME "pcm030-audio-fabric"
+
static struct snd_soc_device device;
static struct snd_soc_card card;
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
new file mode 100644
index 00000000000..a700562e869
--- /dev/null
+++ b/sound/soc/imx/Kconfig
@@ -0,0 +1,21 @@
+config SND_MX1_MX2_SOC
+ tristate "SoC Audio for Freecale i.MX1x i.MX2x CPUs"
+ depends on ARCH_MX2 || ARCH_MX1
+ select SND_PCM
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the MX1 or MX2 SSI interface.
+
+config SND_MXC_SOC_SSI
+ tristate
+
+config SND_SOC_MX27VIS_WM8974
+ tristate "SoC Audio support for MX27 - WM8974 Visstrim_sm10 board"
+ depends on SND_MX1_MX2_SOC && MACH_MX27 && MACH_IMX27_VISSTRIM_M10
+ select SND_MXC_SOC_SSI
+ select SND_SOC_WM8974
+ help
+ Say Y if you want to add support for SoC audio on Visstrim SM10
+ board with WM8974.
+
+
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
new file mode 100644
index 00000000000..c2ffd2c8df5
--- /dev/null
+++ b/sound/soc/imx/Makefile
@@ -0,0 +1,10 @@
+# i.MX Platform Support
+snd-soc-mx1_mx2-objs := mx1_mx2-pcm.o
+snd-soc-mxc-ssi-objs := mxc-ssi.o
+
+obj-$(CONFIG_SND_MX1_MX2_SOC) += snd-soc-mx1_mx2.o
+obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-mxc-ssi.o
+
+# i.MX Machine Support
+snd-soc-mx27vis-wm8974-objs := mx27vis_wm8974.o
+obj-$(CONFIG_SND_SOC_MX27VIS_WM8974) += snd-soc-mx27vis-wm8974.o
diff --git a/sound/soc/imx/mx1_mx2-pcm.c b/sound/soc/imx/mx1_mx2-pcm.c
new file mode 100644
index 00000000000..b8386652939
--- /dev/null
+++ b/sound/soc/imx/mx1_mx2-pcm.c
@@ -0,0 +1,488 @@
+/*
+ * mx1_mx2-pcm.c -- ALSA SoC interface for Freescale i.MX1x, i.MX2x CPUs
+ *
+ * Copyright 2009 Vista Silicon S.L.
+ * Author: Javier Martin
+ * javier.martin@vista-silicon.com
+ *
+ * Based on mxc-pcm.c by Liam Girdwood.
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <asm/dma.h>
+#include <mach/hardware.h>
+#include <mach/dma-mx1-mx2.h>
+
+#include "mx1_mx2-pcm.h"
+
+
+static const struct snd_pcm_hardware mx1_mx2_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .buffer_bytes_max = 32 * 1024,
+ .period_bytes_min = 64,
+ .period_bytes_max = 8 * 1024,
+ .periods_min = 2,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+struct mx1_mx2_runtime_data {
+ int dma_ch;
+ int active;
+ unsigned int period;
+ unsigned int periods;
+ int tx_spin;
+ spinlock_t dma_lock;
+ struct mx1_mx2_pcm_dma_params *dma_params;
+};
+
+
+/**
+ * This function stops the current dma transfer for playback
+ * and clears the dma pointers.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ */
+static int audio_stop_dma(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd = runtime->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prtd->dma_lock, flags);
+
+ pr_debug("%s\n", __func__);
+
+ prtd->active = 0;
+ prtd->period = 0;
+ prtd->periods = 0;
+
+ /* this stops the dma channel and clears the buffer ptrs */
+
+ imx_dma_disable(prtd->dma_ch);
+
+ spin_unlock_irqrestore(&prtd->dma_lock, flags);
+
+ return 0;
+}
+
+/**
+ * This function is called whenever a new audio block needs to be
+ * transferred to the codec. The function receives the address and the size
+ * of the new block and start a new DMA transfer.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ */
+static int dma_new_period(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd = runtime->private_data;
+ unsigned int dma_size;
+ unsigned int offset;
+ int ret = 0;
+ dma_addr_t mem_addr;
+ unsigned int dev_addr;
+
+ if (prtd->active) {
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ offset = dma_size * prtd->period;
+
+ pr_debug("%s: period (%d) out of (%d)\n", __func__,
+ prtd->period,
+ runtime->periods);
+ pr_debug("period_size %d frames\n offset %d bytes\n",
+ (unsigned int)runtime->period_size,
+ offset);
+ pr_debug("dma_size %d bytes\n", dma_size);
+
+ snd_BUG_ON(dma_size > mx1_mx2_pcm_hardware.period_bytes_max);
+
+ mem_addr = (dma_addr_t)(runtime->dma_addr + offset);
+ dev_addr = prtd->dma_params->per_address;
+ pr_debug("%s: mem_addr is %x\n dev_addr is %x\n",
+ __func__, mem_addr, dev_addr);
+
+ ret = imx_dma_setup_single(prtd->dma_ch, mem_addr,
+ dma_size, dev_addr,
+ prtd->dma_params->transfer_type);
+ if (ret < 0) {
+ printk(KERN_ERR "Error %d configuring DMA\n", ret);
+ return ret;
+ }
+ imx_dma_enable(prtd->dma_ch);
+
+ pr_debug("%s: transfer enabled\nmem_addr = %x\n",
+ __func__, (unsigned int) mem_addr);
+ pr_debug("dev_addr = %x\ndma_size = %d\n",
+ (unsigned int) dev_addr, dma_size);
+
+ prtd->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
+ prtd->period++;
+ prtd->period %= runtime->periods;
+ }
+ return ret;
+}
+
+
+/**
+ * This is a callback which will be called
+ * when a TX transfer finishes. The call occurs
+ * in interrupt context.
+ *
+ * @param dat pointer to the structure of the current stream.
+ *
+ */
+static void audio_dma_irq(int channel, void *data)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ struct mx1_mx2_runtime_data *prtd;
+ unsigned int dma_size;
+ unsigned int previous_period;
+ unsigned int offset;
+
+ substream = data;
+ runtime = substream->runtime;
+ prtd = runtime->private_data;
+ previous_period = prtd->periods;
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ offset = dma_size * previous_period;
+
+ prtd->tx_spin = 0;
+ prtd->periods++;
+ prtd->periods %= runtime->periods;
+
+ pr_debug("%s: irq per %d offset %x\n", __func__, prtd->periods, offset);
+
+ /*
+ * If we are getting a callback for an active stream then we inform
+ * the PCM middle layer we've finished a period
+ */
+ if (prtd->active)
+ snd_pcm_period_elapsed(substream);
+
+ /*
+ * Trig next DMA transfer
+ */
+ dma_new_period(substream);
+}
+
+/**
+ * This function configures the hardware to allow audio
+ * playback operations. It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int
+snd_mx1_mx2_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd = runtime->private_data;
+
+ prtd->period = 0;
+ prtd->periods = 0;
+
+ return 0;
+}
+
+static int mx1_mx2_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ ret = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (ret < 0) {
+ printk(KERN_ERR "%s: Error %d failed to malloc pcm pages \n",
+ __func__, ret);
+ return ret;
+ }
+
+ pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_addr 0x(%x)\n",
+ __func__, (unsigned int)runtime->dma_addr);
+ pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_area 0x(%x)\n",
+ __func__, (unsigned int)runtime->dma_area);
+ pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_bytes 0x(%x)\n",
+ __func__, (unsigned int)runtime->dma_bytes);
+
+ return ret;
+}
+
+static int mx1_mx2_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd = runtime->private_data;
+
+ imx_dma_free(prtd->dma_ch);
+
+ snd_pcm_lib_free_pages(substream);
+
+ return 0;
+}
+
+static int mx1_mx2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct mx1_mx2_runtime_data *prtd = substream->runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ prtd->tx_spin = 0;
+ /* requested stream startup */
+ prtd->active = 1;
+ pr_debug("%s: starting dma_new_period\n", __func__);
+ ret = dma_new_period(substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* requested stream shutdown */
+ pr_debug("%s: stopping dma transfer\n", __func__);
+ ret = audio_stop_dma(substream);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t
+mx1_mx2_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd = runtime->private_data;
+ unsigned int offset = 0;
+
+ /* tx_spin value is used here to check if a transfer is active */
+ if (prtd->tx_spin) {
+ offset = (runtime->period_size * (prtd->periods)) +
+ (runtime->period_size >> 1);
+ if (offset >= runtime->buffer_size)
+ offset = runtime->period_size >> 1;
+ } else {
+ offset = (runtime->period_size * (prtd->periods));
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+ }
+ pr_debug("%s: pointer offset %x\n", __func__, offset);
+
+ return offset;
+}
+
+static int mx1_mx2_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mx1_mx2_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data;
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, &mx1_mx2_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ prtd = kzalloc(sizeof(struct mx1_mx2_runtime_data), GFP_KERNEL);
+ if (prtd == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ runtime->private_data = prtd;
+
+ if (!dma_data)
+ return -ENODEV;
+
+ prtd->dma_params = dma_data;
+
+ pr_debug("%s: Requesting dma channel (%s)\n", __func__,
+ prtd->dma_params->name);
+ prtd->dma_ch = imx_dma_request_by_prio(prtd->dma_params->name,
+ DMA_PRIO_HIGH);
+ if (prtd->dma_ch < 0) {
+ printk(KERN_ERR "Error %d requesting dma channel\n", ret);
+ return ret;
+ }
+ imx_dma_config_burstlen(prtd->dma_ch,
+ prtd->dma_params->watermark_level);
+
+ ret = imx_dma_config_channel(prtd->dma_ch,
+ prtd->dma_params->per_config,
+ prtd->dma_params->mem_config,
+ prtd->dma_params->event_id, 0);
+
+ if (ret) {
+ pr_debug(KERN_ERR "Error %d configuring dma channel %d\n",
+ ret, prtd->dma_ch);
+ return ret;
+ }
+
+ pr_debug("%s: Setting tx dma callback function\n", __func__);
+ ret = imx_dma_setup_handlers(prtd->dma_ch,
+ audio_dma_irq, NULL,
+ (void *)substream);
+ if (ret < 0) {
+ printk(KERN_ERR "Error %d setting dma callback function\n", ret);
+ return ret;
+ }
+ return 0;
+
+ out:
+ return ret;
+}
+
+static int mx1_mx2_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd = runtime->private_data;
+
+ kfree(prtd);
+
+ return 0;
+}
+
+static int mx1_mx2_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops mx1_mx2_pcm_ops = {
+ .open = mx1_mx2_pcm_open,
+ .close = mx1_mx2_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = mx1_mx2_pcm_hw_params,
+ .hw_free = mx1_mx2_pcm_hw_free,
+ .prepare = snd_mx1_mx2_prepare,
+ .trigger = mx1_mx2_pcm_trigger,
+ .pointer = mx1_mx2_pcm_pointer,
+ .mmap = mx1_mx2_pcm_mmap,
+};
+
+static u64 mx1_mx2_pcm_dmamask = 0xffffffff;
+
+static int mx1_mx2_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = mx1_mx2_pcm_hardware.buffer_bytes_max;
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+
+ /* Reserve uncached-buffered memory area for DMA */
+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+
+ pr_debug("%s: preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
+ __func__, (void *) buf->area, (void *) buf->addr, size);
+
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+ return 0;
+}
+
+static void mx1_mx2_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_writecombine(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+static int mx1_mx2_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &mx1_mx2_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = 0xffffffff;
+
+ if (dai->playback.channels_min) {
+ ret = mx1_mx2_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ pr_debug("%s: preallocate playback buffer\n", __func__);
+ if (ret)
+ goto out;
+ }
+
+ if (dai->capture.channels_min) {
+ ret = mx1_mx2_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ pr_debug("%s: preallocate capture buffer\n", __func__);
+ if (ret)
+ goto out;
+ }
+ out:
+ return ret;
+}
+
+struct snd_soc_platform mx1_mx2_soc_platform = {
+ .name = "mx1_mx2-audio",
+ .pcm_ops = &mx1_mx2_pcm_ops,
+ .pcm_new = mx1_mx2_pcm_new,
+ .pcm_free = mx1_mx2_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(mx1_mx2_soc_platform);
+
+static int __init mx1_mx2_soc_platform_init(void)
+{
+ return snd_soc_register_platform(&mx1_mx2_soc_platform);
+}
+module_init(mx1_mx2_soc_platform_init);
+
+static void __exit mx1_mx2_soc_platform_exit(void)
+{
+ snd_soc_unregister_platform(&mx1_mx2_soc_platform);
+}
+module_exit(mx1_mx2_soc_platform_exit);
+
+MODULE_AUTHOR("Javier Martin, javier.martin@vista-silicon.com");
+MODULE_DESCRIPTION("Freescale i.MX2x, i.MX1x PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/mx1_mx2-pcm.h b/sound/soc/imx/mx1_mx2-pcm.h
new file mode 100644
index 00000000000..2e528106570
--- /dev/null
+++ b/sound/soc/imx/mx1_mx2-pcm.h
@@ -0,0 +1,26 @@
+/*
+ * mx1_mx2-pcm.h :- ASoC platform header for Freescale i.MX1x, i.MX2x
+ *
+ * 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 _MX1_MX2_PCM_H
+#define _MX1_MX2_PCM_H
+
+/* DMA information for mx1_mx2 platforms */
+struct mx1_mx2_pcm_dma_params {
+ char *name; /* stream identifier */
+ unsigned int transfer_type; /* READ or WRITE DMA transfer */
+ dma_addr_t per_address; /* physical address of SSI fifo */
+ int event_id; /* fixed DMA number for SSI fifo */
+ int watermark_level; /* SSI fifo watermark level */
+ int per_config; /* DMA Config flags for peripheral */
+ int mem_config; /* DMA Config flags for RAM */
+ };
+
+/* platform data */
+extern struct snd_soc_platform mx1_mx2_soc_platform;
+
+#endif
diff --git a/sound/soc/imx/mx27vis_wm8974.c b/sound/soc/imx/mx27vis_wm8974.c
new file mode 100644
index 00000000000..e4dcb539108
--- /dev/null
+++ b/sound/soc/imx/mx27vis_wm8974.c
@@ -0,0 +1,317 @@
+/*
+ * mx27vis_wm8974.c -- SoC audio for mx27vis
+ *
+ * Copyright 2009 Vista Silicon S.L.
+ * Author: Javier Martin
+ * javier.martin@vista-silicon.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+
+#include "../codecs/wm8974.h"
+#include "mx1_mx2-pcm.h"
+#include "mxc-ssi.h"
+#include <mach/gpio.h>
+#include <mach/iomux.h>
+
+#define IGNORED_ARG 0
+
+
+static struct snd_soc_card mx27vis;
+
+/**
+ * This function connects SSI1 (HPCR1) as slave to
+ * SSI1 external signals (PPCR1)
+ * As slave, HPCR1 must set TFSDIR and TCLKDIR as inputs from
+ * port 4
+ */
+void audmux_connect_1_4(void)
+{
+ pr_debug("AUDMUX: normal operation mode\n");
+ /* Reset HPCR1 and PPCR1 */
+
+ DAM_HPCR1 = 0x00000000;
+ DAM_PPCR1 = 0x00000000;
+
+ /* set to synchronous */
+ DAM_HPCR1 |= AUDMUX_HPCR_SYN;
+ DAM_PPCR1 |= AUDMUX_PPCR_SYN;
+
+
+ /* set Rx sources 1 <--> 4 */
+ DAM_HPCR1 |= AUDMUX_HPCR_RXDSEL(3); /* port 4 */
+ DAM_PPCR1 |= AUDMUX_PPCR_RXDSEL(0); /* port 1 */
+
+ /* set Tx frame and Clock direction and source 4 --> 1 output */
+ DAM_HPCR1 |= AUDMUX_HPCR_TFSDIR | AUDMUX_HPCR_TCLKDIR;
+ DAM_HPCR1 |= AUDMUX_HPCR_TFCSEL(3); /* TxDS and TxCclk from port 4 */
+
+ return;
+}
+
+static int mx27vis_hifi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ unsigned int pll_out = 0, bclk = 0, fmt = 0, mclk = 0;
+ int ret = 0;
+
+ /*
+ * The WM8974 is better at generating accurate audio clocks than the
+ * MX27 SSI controller, so we will use it as master when we can.
+ */
+ switch (params_rate(params)) {
+ case 8000:
+ fmt = SND_SOC_DAIFMT_CBM_CFM;
+ mclk = WM8974_MCLKDIV_12;
+ pll_out = 24576000;
+ break;
+ case 16000:
+ fmt = SND_SOC_DAIFMT_CBM_CFM;
+ pll_out = 12288000;
+ break;
+ case 48000:
+ fmt = SND_SOC_DAIFMT_CBM_CFM;
+ bclk = WM8974_BCLKDIV_4;
+ pll_out = 12288000;
+ break;
+ case 96000:
+ fmt = SND_SOC_DAIFMT_CBM_CFM;
+ bclk = WM8974_BCLKDIV_2;
+ pll_out = 12288000;
+ break;
+ case 11025:
+ fmt = SND_SOC_DAIFMT_CBM_CFM;
+ bclk = WM8974_BCLKDIV_16;
+ pll_out = 11289600;
+ break;
+ case 22050:
+ fmt = SND_SOC_DAIFMT_CBM_CFM;
+ bclk = WM8974_BCLKDIV_8;
+ pll_out = 11289600;
+ break;
+ case 44100:
+ fmt = SND_SOC_DAIFMT_CBM_CFM;
+ bclk = WM8974_BCLKDIV_4;
+ mclk = WM8974_MCLKDIV_2;
+ pll_out = 11289600;
+ break;
+ case 88200:
+ fmt = SND_SOC_DAIFMT_CBM_CFM;
+ bclk = WM8974_BCLKDIV_2;
+ pll_out = 11289600;
+ break;
+ }
+
+ /* set codec DAI configuration */
+ ret = codec_dai->ops->set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
+ SND_SOC_DAIFMT_SYNC | fmt);
+ if (ret < 0) {
+ printk(KERN_ERR "Error from codec DAI configuration\n");
+ return ret;
+ }
+
+ /* set cpu DAI configuration */
+ ret = cpu_dai->ops->set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_SYNC | fmt);
+ if (ret < 0) {
+ printk(KERN_ERR "Error from cpu DAI configuration\n");
+ return ret;
+ }
+
+ /* Put DC field of STCCR to 1 (not zero) */
+ ret = cpu_dai->ops->set_tdm_slot(cpu_dai, 0, 2);
+
+ /* set the SSI system clock as input */
+ ret = cpu_dai->ops->set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "Error when setting system SSI clk\n");
+ return ret;
+ }
+
+ /* set codec BCLK division for sample rate */
+ ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_BCLKDIV, bclk);
+ if (ret < 0) {
+ printk(KERN_ERR "Error when setting BCLK division\n");
+ return ret;
+ }
+
+
+ /* codec PLL input is 25 MHz */
+ ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG,
+ 25000000, pll_out);
+ if (ret < 0) {
+ printk(KERN_ERR "Error when setting PLL input\n");
+ return ret;
+ }
+
+ /*set codec MCLK division for sample rate */
+ ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_MCLKDIV, mclk);
+ if (ret < 0) {
+ printk(KERN_ERR "Error when setting MCLK division\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mx27vis_hifi_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+ /* disable the PLL */
+ return codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, 0, 0);
+}
+
+/*
+ * mx27vis WM8974 HiFi DAI opserations.
+ */
+static struct snd_soc_ops mx27vis_hifi_ops = {
+ .hw_params = mx27vis_hifi_hw_params,
+ .hw_free = mx27vis_hifi_hw_free,
+};
+
+
+static int mx27vis_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+static int mx27vis_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int mx27vis_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ ret = get_ssi_clk(0, &pdev->dev);
+
+ if (ret < 0) {
+ printk(KERN_ERR "%s: cant get ssi clock\n", __func__);
+ return ret;
+ }
+
+
+ return 0;
+}
+
+static int mx27vis_remove(struct platform_device *pdev)
+{
+ put_ssi_clk(0);
+ return 0;
+}
+
+static struct snd_soc_dai_link mx27vis_dai[] = {
+{ /* Hifi Playback*/
+ .name = "WM8974",
+ .stream_name = "WM8974 HiFi",
+ .cpu_dai = &imx_ssi_pcm_dai[0],
+ .codec_dai = &wm8974_dai,
+ .ops = &mx27vis_hifi_ops,
+},
+};
+
+static struct snd_soc_card mx27vis = {
+ .name = "mx27vis",
+ .platform = &mx1_mx2_soc_platform,
+ .probe = mx27vis_probe,
+ .remove = mx27vis_remove,
+ .suspend_pre = mx27vis_suspend,
+ .resume_post = mx27vis_resume,
+ .dai_link = mx27vis_dai,
+ .num_links = ARRAY_SIZE(mx27vis_dai),
+};
+
+static struct snd_soc_device mx27vis_snd_devdata = {
+ .card = &mx27vis,
+ .codec_dev = &soc_codec_dev_wm8974,
+};
+
+static struct platform_device *mx27vis_snd_device;
+
+/* Temporal definition of board specific behaviour */
+void gpio_ssi_active(int ssi_num)
+{
+ int ret = 0;
+
+ unsigned int ssi1_pins[] = {
+ PC20_PF_SSI1_FS,
+ PC21_PF_SSI1_RXD,
+ PC22_PF_SSI1_TXD,
+ PC23_PF_SSI1_CLK,
+ };
+ unsigned int ssi2_pins[] = {
+ PC24_PF_SSI2_FS,
+ PC25_PF_SSI2_RXD,
+ PC26_PF_SSI2_TXD,
+ PC27_PF_SSI2_CLK,
+ };
+ if (ssi_num == 0)
+ ret = mxc_gpio_setup_multiple_pins(ssi1_pins,
+ ARRAY_SIZE(ssi1_pins), "USB OTG");
+ else
+ ret = mxc_gpio_setup_multiple_pins(ssi2_pins,
+ ARRAY_SIZE(ssi2_pins), "USB OTG");
+ if (ret)
+ printk(KERN_ERR "Error requesting ssi %x pins\n", ssi_num);
+}
+
+
+static int __init mx27vis_init(void)
+{
+ int ret;
+
+ mx27vis_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!mx27vis_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(mx27vis_snd_device, &mx27vis_snd_devdata);
+ mx27vis_snd_devdata.dev = &mx27vis_snd_device->dev;
+ ret = platform_device_add(mx27vis_snd_device);
+
+ if (ret) {
+ printk(KERN_ERR "ASoC: Platform device allocation failed\n");
+ platform_device_put(mx27vis_snd_device);
+ }
+
+ /* WM8974 uses SSI1 (HPCR1) via AUDMUX port 4 for audio (PPCR1) */
+ gpio_ssi_active(0);
+ audmux_connect_1_4();
+
+ return ret;
+}
+
+static void __exit mx27vis_exit(void)
+{
+ /* We should call some "ssi_gpio_inactive()" properly */
+}
+
+module_init(mx27vis_init);
+module_exit(mx27vis_exit);
+
+
+MODULE_AUTHOR("Javier Martin, javier.martin@vista-silicon.com");
+MODULE_DESCRIPTION("ALSA SoC WM8974 mx27vis");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/mxc-ssi.c b/sound/soc/imx/mxc-ssi.c
new file mode 100644
index 00000000000..3806ff2c0cd
--- /dev/null
+++ b/sound/soc/imx/mxc-ssi.c
@@ -0,0 +1,868 @@
+/*
+ * mxc-ssi.c -- SSI driver for Freescale IMX
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * Based on mxc-alsa-mc13783 (C) 2006 Freescale.
+ *
+ * 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.
+ *
+ * TODO:
+ * Need to rework SSI register defs when new defs go into mainline.
+ * Add support for TDM and FIFO 1.
+ * Add support for i.mx3x DMA interface.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <mach/dma-mx1-mx2.h>
+#include <asm/mach-types.h>
+
+#include "mxc-ssi.h"
+#include "mx1_mx2-pcm.h"
+
+#define SSI1_PORT 0
+#define SSI2_PORT 1
+
+static int ssi_active[2] = {0, 0};
+
+/* DMA information for mx1_mx2 platforms */
+static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out0 = {
+ .name = "SSI1 PCM Stereo out 0",
+ .transfer_type = DMA_MODE_WRITE,
+ .per_address = SSI1_BASE_ADDR + STX0,
+ .event_id = DMA_REQ_SSI1_TX0,
+ .watermark_level = TXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out1 = {
+ .name = "SSI1 PCM Stereo out 1",
+ .transfer_type = DMA_MODE_WRITE,
+ .per_address = SSI1_BASE_ADDR + STX1,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .watermark_level = TXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in0 = {
+ .name = "SSI1 PCM Stereo in 0",
+ .transfer_type = DMA_MODE_READ,
+ .per_address = SSI1_BASE_ADDR + SRX0,
+ .event_id = DMA_REQ_SSI1_RX0,
+ .watermark_level = RXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in1 = {
+ .name = "SSI1 PCM Stereo in 1",
+ .transfer_type = DMA_MODE_READ,
+ .per_address = SSI1_BASE_ADDR + SRX1,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .watermark_level = RXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out0 = {
+ .name = "SSI2 PCM Stereo out 0",
+ .transfer_type = DMA_MODE_WRITE,
+ .per_address = SSI2_BASE_ADDR + STX0,
+ .event_id = DMA_REQ_SSI2_TX0,
+ .watermark_level = TXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out1 = {
+ .name = "SSI2 PCM Stereo out 1",
+ .transfer_type = DMA_MODE_WRITE,
+ .per_address = SSI2_BASE_ADDR + STX1,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .watermark_level = TXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in0 = {
+ .name = "SSI2 PCM Stereo in 0",
+ .transfer_type = DMA_MODE_READ,
+ .per_address = SSI2_BASE_ADDR + SRX0,
+ .event_id = DMA_REQ_SSI2_RX0,
+ .watermark_level = RXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in1 = {
+ .name = "SSI2 PCM Stereo in 1",
+ .transfer_type = DMA_MODE_READ,
+ .per_address = SSI2_BASE_ADDR + SRX1,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .watermark_level = RXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct clk *ssi_clk0, *ssi_clk1;
+
+int get_ssi_clk(int ssi, struct device *dev)
+{
+ switch (ssi) {
+ case 0:
+ ssi_clk0 = clk_get(dev, "ssi1");
+ if (IS_ERR(ssi_clk0))
+ return PTR_ERR(ssi_clk0);
+ return 0;
+ case 1:
+ ssi_clk1 = clk_get(dev, "ssi2");
+ if (IS_ERR(ssi_clk1))
+ return PTR_ERR(ssi_clk1);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(get_ssi_clk);
+
+void put_ssi_clk(int ssi)
+{
+ switch (ssi) {
+ case 0:
+ clk_put(ssi_clk0);
+ ssi_clk0 = NULL;
+ break;
+ case 1:
+ clk_put(ssi_clk1);
+ ssi_clk1 = NULL;
+ break;
+ }
+}
+EXPORT_SYMBOL(put_ssi_clk);
+
+/*
+ * SSI system clock configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ u32 scr;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ scr = SSI1_SCR;
+ pr_debug("%s: SCR for SSI1 is %x\n", __func__, scr);
+ } else {
+ scr = SSI2_SCR;
+ pr_debug("%s: SCR for SSI2 is %x\n", __func__, scr);
+ }
+
+ if (scr & SSI_SCR_SSIEN) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+
+ switch (clk_id) {
+ case IMX_SSP_SYS_CLK:
+ if (dir == SND_SOC_CLOCK_OUT) {
+ scr |= SSI_SCR_SYS_CLK_EN;
+ pr_debug("%s: clk of is output\n", __func__);
+ } else {
+ scr &= ~SSI_SCR_SYS_CLK_EN;
+ pr_debug("%s: clk of is input\n", __func__);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ pr_debug("%s: writeback of SSI1_SCR\n", __func__);
+ SSI1_SCR = scr;
+ } else {
+ pr_debug("%s: writeback of SSI2_SCR\n", __func__);
+ SSI2_SCR = scr;
+ }
+
+ return 0;
+}
+
+/*
+ * SSI Clock dividers
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ u32 stccr, srccr;
+
+ pr_debug("%s\n", __func__);
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ if (SSI1_SCR & SSI_SCR_SSIEN)
+ return 0;
+ srccr = SSI1_STCCR;
+ stccr = SSI1_STCCR;
+ } else {
+ if (SSI2_SCR & SSI_SCR_SSIEN)
+ return 0;
+ srccr = SSI2_STCCR;
+ stccr = SSI2_STCCR;
+ }
+
+ switch (div_id) {
+ case IMX_SSI_TX_DIV_2:
+ stccr &= ~SSI_STCCR_DIV2;
+ stccr |= div;
+ break;
+ case IMX_SSI_TX_DIV_PSR:
+ stccr &= ~SSI_STCCR_PSR;
+ stccr |= div;
+ break;
+ case IMX_SSI_TX_DIV_PM:
+ stccr &= ~0xff;
+ stccr |= SSI_STCCR_PM(div);
+ break;
+ case IMX_SSI_RX_DIV_2:
+ stccr &= ~SSI_STCCR_DIV2;
+ stccr |= div;
+ break;
+ case IMX_SSI_RX_DIV_PSR:
+ stccr &= ~SSI_STCCR_PSR;
+ stccr |= div;
+ break;
+ case IMX_SSI_RX_DIV_PM:
+ stccr &= ~0xff;
+ stccr |= SSI_STCCR_PM(div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ SSI1_STCCR = stccr;
+ SSI1_SRCCR = srccr;
+ } else {
+ SSI2_STCCR = stccr;
+ SSI2_SRCCR = srccr;
+ }
+ return 0;
+}
+
+/*
+ * SSI Network Mode or TDM slots configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+ unsigned int mask, int slots)
+{
+ u32 stmsk, srmsk, stccr;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ if (SSI1_SCR & SSI_SCR_SSIEN) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+ stccr = SSI1_STCCR;
+ } else {
+ if (SSI2_SCR & SSI_SCR_SSIEN) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+ stccr = SSI2_STCCR;
+ }
+
+ stmsk = srmsk = mask;
+ stccr &= ~SSI_STCCR_DC_MASK;
+ stccr |= SSI_STCCR_DC(slots - 1);
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ SSI1_STMSK = stmsk;
+ SSI1_SRMSK = srmsk;
+ SSI1_SRCCR = SSI1_STCCR = stccr;
+ } else {
+ SSI2_STMSK = stmsk;
+ SSI2_SRMSK = srmsk;
+ SSI2_SRCCR = SSI2_STCCR = stccr;
+ }
+
+ return 0;
+}
+
+/*
+ * SSI DAI format configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ * Note: We don't use the I2S modes but instead manually configure the
+ * SSI for I2S.
+ */
+static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ u32 stcr = 0, srcr = 0, scr;
+
+ /*
+ * This is done to avoid this function to modify
+ * previous set values in stcr
+ */
+ stcr = SSI1_STCR;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+ scr = SSI1_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET);
+ else
+ scr = SSI2_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET);
+
+ if (scr & SSI_SCR_SSIEN) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+
+ /* DAI mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* data on rising edge of bclk, frame low 1clk before data */
+ stcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
+ srcr |= SSI_SRCR_RFSI | SSI_SRCR_REFS | SSI_SRCR_RXBIT0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ /* data on rising edge of bclk, frame high with data */
+ stcr |= SSI_STCR_TXBIT0;
+ srcr |= SSI_SRCR_RXBIT0;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ /* data on rising edge of bclk, frame high with data */
+ stcr |= SSI_STCR_TFSL;
+ srcr |= SSI_SRCR_RFSL;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /* data on rising edge of bclk, frame high 1clk before data */
+ stcr |= SSI_STCR_TFSL | SSI_STCR_TEFS;
+ srcr |= SSI_SRCR_RFSL | SSI_SRCR_REFS;
+ break;
+ }
+
+ /* DAI clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ stcr |= SSI_STCR_TFSI;
+ stcr &= ~SSI_STCR_TSCKP;
+ srcr |= SSI_SRCR_RFSI;
+ srcr &= ~SSI_SRCR_RSCKP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ stcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
+ srcr &= ~(SSI_SRCR_RSCKP | SSI_SRCR_RFSI);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
+ srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ stcr &= ~SSI_STCR_TFSI;
+ stcr |= SSI_STCR_TSCKP;
+ srcr &= ~SSI_SRCR_RFSI;
+ srcr |= SSI_SRCR_RSCKP;
+ break;
+ }
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
+ srcr |= SSI_SRCR_RFDIR | SSI_SRCR_RXDIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ stcr |= SSI_STCR_TFDIR;
+ srcr |= SSI_SRCR_RFDIR;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ stcr |= SSI_STCR_TXDIR;
+ srcr |= SSI_SRCR_RXDIR;
+ break;
+ }
+
+ /* sync */
+ if (!(fmt & SND_SOC_DAIFMT_ASYNC))
+ scr |= SSI_SCR_SYN;
+
+ /* tdm - only for stereo atm */
+ if (fmt & SND_SOC_DAIFMT_TDM)
+ scr |= SSI_SCR_NET;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ SSI1_STCR = stcr;
+ SSI1_SRCR = srcr;
+ SSI1_SCR = scr;
+ } else {
+ SSI2_STCR = stcr;
+ SSI2_SRCR = srcr;
+ SSI2_SCR = scr;
+ }
+
+ return 0;
+}
+
+static int imx_ssi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* set up TX DMA params */
+ switch (cpu_dai->id) {
+ case IMX_DAI_SSI0:
+ cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out0;
+ break;
+ case IMX_DAI_SSI1:
+ cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out1;
+ break;
+ case IMX_DAI_SSI2:
+ cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out0;
+ break;
+ case IMX_DAI_SSI3:
+ cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out1;
+ }
+ pr_debug("%s: (playback)\n", __func__);
+ } else {
+ /* set up RX DMA params */
+ switch (cpu_dai->id) {
+ case IMX_DAI_SSI0:
+ cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in0;
+ break;
+ case IMX_DAI_SSI1:
+ cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in1;
+ break;
+ case IMX_DAI_SSI2:
+ cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in0;
+ break;
+ case IMX_DAI_SSI3:
+ cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in1;
+ }
+ pr_debug("%s: (capture)\n", __func__);
+ }
+
+ /*
+ * we cant really change any SSI values after SSI is enabled
+ * need to fix in software for max flexibility - lrg
+ */
+ if (cpu_dai->active) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+
+ /* reset the SSI port - Sect 45.4.4 */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+
+ if (!ssi_clk0)
+ return -EINVAL;
+
+ if (ssi_active[SSI1_PORT]++) {
+ pr_debug("%s: exit before reset\n", __func__);
+ return 0;
+ }
+
+ /* SSI1 Reset */
+ SSI1_SCR = 0;
+
+ SSI1_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) |
+ SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM0(TXFIFO_WATERMARK);
+ } else {
+
+ if (!ssi_clk1)
+ return -EINVAL;
+
+ if (ssi_active[SSI2_PORT]++) {
+ pr_debug("%s: exit before reset\n", __func__);
+ return 0;
+ }
+
+ /* SSI2 Reset */
+ SSI2_SCR = 0;
+
+ SSI2_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) |
+ SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM0(TXFIFO_WATERMARK);
+ }
+
+ return 0;
+}
+
+int imx_ssi_hw_tx_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ u32 stccr, stcr, sier;
+
+ pr_debug("%s\n", __func__);
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ stccr = SSI1_STCCR & ~SSI_STCCR_WL_MASK;
+ stcr = SSI1_STCR;
+ sier = SSI1_SIER;
+ } else {
+ stccr = SSI2_STCCR & ~SSI_STCCR_WL_MASK;
+ stcr = SSI2_STCR;
+ sier = SSI2_SIER;
+ }
+
+ /* DAI data (word) size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ stccr |= SSI_STCCR_WL(16);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ stccr |= SSI_STCCR_WL(20);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ stccr |= SSI_STCCR_WL(24);
+ break;
+ }
+
+ /* enable interrupts */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+ stcr |= SSI_STCR_TFEN0;
+ else
+ stcr |= SSI_STCR_TFEN1;
+ sier |= SSI_SIER_TDMAE;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ SSI1_STCR = stcr;
+ SSI1_STCCR = stccr;
+ SSI1_SIER = sier;
+ } else {
+ SSI2_STCR = stcr;
+ SSI2_STCCR = stccr;
+ SSI2_SIER = sier;
+ }
+
+ return 0;
+}
+
+int imx_ssi_hw_rx_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ u32 srccr, srcr, sier;
+
+ pr_debug("%s\n", __func__);
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ srccr = SSI1_SRCCR & ~SSI_SRCCR_WL_MASK;
+ srcr = SSI1_SRCR;
+ sier = SSI1_SIER;
+ } else {
+ srccr = SSI2_SRCCR & ~SSI_SRCCR_WL_MASK;
+ srcr = SSI2_SRCR;
+ sier = SSI2_SIER;
+ }
+
+ /* DAI data (word) size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ srccr |= SSI_SRCCR_WL(16);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ srccr |= SSI_SRCCR_WL(20);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ srccr |= SSI_SRCCR_WL(24);
+ break;
+ }
+
+ /* enable interrupts */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+ srcr |= SSI_SRCR_RFEN0;
+ else
+ srcr |= SSI_SRCR_RFEN1;
+ sier |= SSI_SIER_RDMAE;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ SSI1_SRCR = srcr;
+ SSI1_SRCCR = srccr;
+ SSI1_SIER = sier;
+ } else {
+ SSI2_SRCR = srcr;
+ SSI2_SRCCR = srccr;
+ SSI2_SIER = sier;
+ }
+
+ return 0;
+}
+
+/*
+ * Should only be called when port is inactive (i.e. SSIEN = 0),
+ * although can be called multiple times by upper layers.
+ */
+int imx_ssi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ int ret;
+
+ /* cant change any parameters when SSI is running */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ if (SSI1_SCR & SSI_SCR_SSIEN) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+ } else {
+ if (SSI2_SCR & SSI_SCR_SSIEN) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+ }
+
+ /*
+ * Configure both tx and rx params with the same settings. This is
+ * really a harware restriction because SSI must be disabled until
+ * we can change those values. If there is an active audio stream in
+ * one direction, enabling the other direction with different
+ * settings would mean disturbing the running one.
+ */
+ ret = imx_ssi_hw_tx_params(substream, params);
+ if (ret < 0)
+ return ret;
+ return imx_ssi_hw_rx_params(substream, params);
+}
+
+int imx_ssi_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+
+ /* Enable clks here to follow SSI recommended init sequence */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ ret = clk_enable(ssi_clk0);
+ if (ret < 0)
+ printk(KERN_ERR "Unable to enable ssi_clk0\n");
+ } else {
+ ret = clk_enable(ssi_clk1);
+ if (ret < 0)
+ printk(KERN_ERR "Unable to enable ssi_clk1\n");
+ }
+
+ return 0;
+}
+
+static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ u32 scr;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+ scr = SSI1_SCR;
+ else
+ scr = SSI2_SCR;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ scr |= SSI_SCR_TE | SSI_SCR_SSIEN;
+ else
+ scr |= SSI_SCR_RE | SSI_SCR_SSIEN;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ scr &= ~SSI_SCR_TE;
+ else
+ scr &= ~SSI_SCR_RE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+ SSI1_SCR = scr;
+ else
+ SSI2_SCR = scr;
+
+ return 0;
+}
+
+static void imx_ssi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ /* shutdown SSI if neither Tx or Rx is active */
+ if (!cpu_dai->active) {
+
+ if (cpu_dai->id == IMX_DAI_SSI0 ||
+ cpu_dai->id == IMX_DAI_SSI2) {
+
+ if (--ssi_active[SSI1_PORT] > 1)
+ return;
+
+ SSI1_SCR = 0;
+ clk_disable(ssi_clk0);
+ } else {
+ if (--ssi_active[SSI2_PORT])
+ return;
+ SSI2_SCR = 0;
+ clk_disable(ssi_clk1);
+ }
+ }
+}
+
+#ifdef CONFIG_PM
+static int imx_ssi_suspend(struct platform_device *dev,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static int imx_ssi_resume(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+#else
+#define imx_ssi_suspend NULL
+#define imx_ssi_resume NULL
+#endif
+
+#define IMX_SSI_RATES \
+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+#define IMX_SSI_BITS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
+ .startup = imx_ssi_startup,
+ .shutdown = imx_ssi_shutdown,
+ .trigger = imx_ssi_trigger,
+ .prepare = imx_ssi_prepare,
+ .hw_params = imx_ssi_hw_params,
+ .set_sysclk = imx_ssi_set_dai_sysclk,
+ .set_clkdiv = imx_ssi_set_dai_clkdiv,
+ .set_fmt = imx_ssi_set_dai_fmt,
+ .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
+};
+
+struct snd_soc_dai imx_ssi_pcm_dai[] = {
+{
+ .name = "imx-i2s-1-0",
+ .id = IMX_DAI_SSI0,
+ .suspend = imx_ssi_suspend,
+ .resume = imx_ssi_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .ops = &imx_ssi_pcm_dai_ops,
+},
+{
+ .name = "imx-i2s-2-0",
+ .id = IMX_DAI_SSI1,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .ops = &imx_ssi_pcm_dai_ops,
+},
+{
+ .name = "imx-i2s-1-1",
+ .id = IMX_DAI_SSI2,
+ .suspend = imx_ssi_suspend,
+ .resume = imx_ssi_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .ops = &imx_ssi_pcm_dai_ops,
+},
+{
+ .name = "imx-i2s-2-1",
+ .id = IMX_DAI_SSI3,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .ops = &imx_ssi_pcm_dai_ops,
+},
+};
+EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
+
+static int __init imx_ssi_init(void)
+{
+ return snd_soc_register_dais(imx_ssi_pcm_dai,
+ ARRAY_SIZE(imx_ssi_pcm_dai));
+}
+
+static void __exit imx_ssi_exit(void)
+{
+ snd_soc_unregister_dais(imx_ssi_pcm_dai,
+ ARRAY_SIZE(imx_ssi_pcm_dai));
+}
+
+module_init(imx_ssi_init);
+module_exit(imx_ssi_exit);
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com");
+MODULE_DESCRIPTION("i.MX ASoC I2S driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/mxc-ssi.h b/sound/soc/imx/mxc-ssi.h
new file mode 100644
index 00000000000..12bbdc9c7ec
--- /dev/null
+++ b/sound/soc/imx/mxc-ssi.h
@@ -0,0 +1,238 @@
+/*
+ * 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 _IMX_SSI_H
+#define _IMX_SSI_H
+
+#include <mach/hardware.h>
+
+/* SSI regs definition - MOVE to /arch/arm/plat-mxc/include/mach/ when stable */
+#define SSI1_IO_BASE_ADDR IO_ADDRESS(SSI1_BASE_ADDR)
+#define SSI2_IO_BASE_ADDR IO_ADDRESS(SSI2_BASE_ADDR)
+
+#define STX0 0x00
+#define STX1 0x04
+#define SRX0 0x08
+#define SRX1 0x0c
+#define SCR 0x10
+#define SISR 0x14
+#define SIER 0x18
+#define STCR 0x1c
+#define SRCR 0x20
+#define STCCR 0x24
+#define SRCCR 0x28
+#define SFCSR 0x2c
+#define STR 0x30
+#define SOR 0x34
+#define SACNT 0x38
+#define SACADD 0x3c
+#define SACDAT 0x40
+#define SATAG 0x44
+#define STMSK 0x48
+#define SRMSK 0x4c
+
+#define SSI1_STX0 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STX0)))
+#define SSI1_STX1 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STX1)))
+#define SSI1_SRX0 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRX0)))
+#define SSI1_SRX1 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRX1)))
+#define SSI1_SCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SCR)))
+#define SSI1_SISR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SISR)))
+#define SSI1_SIER (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SIER)))
+#define SSI1_STCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STCR)))
+#define SSI1_SRCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRCR)))
+#define SSI1_STCCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STCCR)))
+#define SSI1_SRCCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRCCR)))
+#define SSI1_SFCSR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SFCSR)))
+#define SSI1_STR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STR)))
+#define SSI1_SOR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SOR)))
+#define SSI1_SACNT (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACNT)))
+#define SSI1_SACADD (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACADD)))
+#define SSI1_SACDAT (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACDAT)))
+#define SSI1_SATAG (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SATAG)))
+#define SSI1_STMSK (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STMSK)))
+#define SSI1_SRMSK (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRMSK)))
+
+
+#define SSI2_STX0 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STX0)))
+#define SSI2_STX1 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STX1)))
+#define SSI2_SRX0 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRX0)))
+#define SSI2_SRX1 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRX1)))
+#define SSI2_SCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SCR)))
+#define SSI2_SISR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SISR)))
+#define SSI2_SIER (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SIER)))
+#define SSI2_STCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STCR)))
+#define SSI2_SRCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRCR)))
+#define SSI2_STCCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STCCR)))
+#define SSI2_SRCCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRCCR)))
+#define SSI2_SFCSR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SFCSR)))
+#define SSI2_STR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STR)))
+#define SSI2_SOR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SOR)))
+#define SSI2_SACNT (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACNT)))
+#define SSI2_SACADD (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACADD)))
+#define SSI2_SACDAT (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACDAT)))
+#define SSI2_SATAG (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SATAG)))
+#define SSI2_STMSK (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STMSK)))
+#define SSI2_SRMSK (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRMSK)))
+
+#define SSI_SCR_CLK_IST (1 << 9)
+#define SSI_SCR_TCH_EN (1 << 8)
+#define SSI_SCR_SYS_CLK_EN (1 << 7)
+#define SSI_SCR_I2S_MODE_NORM (0 << 5)
+#define SSI_SCR_I2S_MODE_MSTR (1 << 5)
+#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
+#define SSI_SCR_SYN (1 << 4)
+#define SSI_SCR_NET (1 << 3)
+#define SSI_SCR_RE (1 << 2)
+#define SSI_SCR_TE (1 << 1)
+#define SSI_SCR_SSIEN (1 << 0)
+
+#define SSI_SISR_CMDAU (1 << 18)
+#define SSI_SISR_CMDDU (1 << 17)
+#define SSI_SISR_RXT (1 << 16)
+#define SSI_SISR_RDR1 (1 << 15)
+#define SSI_SISR_RDR0 (1 << 14)
+#define SSI_SISR_TDE1 (1 << 13)
+#define SSI_SISR_TDE0 (1 << 12)
+#define SSI_SISR_ROE1 (1 << 11)
+#define SSI_SISR_ROE0 (1 << 10)
+#define SSI_SISR_TUE1 (1 << 9)
+#define SSI_SISR_TUE0 (1 << 8)
+#define SSI_SISR_TFS (1 << 7)
+#define SSI_SISR_RFS (1 << 6)
+#define SSI_SISR_TLS (1 << 5)
+#define SSI_SISR_RLS (1 << 4)
+#define SSI_SISR_RFF1 (1 << 3)
+#define SSI_SISR_RFF0 (1 << 2)
+#define SSI_SISR_TFE1 (1 << 1)
+#define SSI_SISR_TFE0 (1 << 0)
+
+#define SSI_SIER_RDMAE (1 << 22)
+#define SSI_SIER_RIE (1 << 21)
+#define SSI_SIER_TDMAE (1 << 20)
+#define SSI_SIER_TIE (1 << 19)
+#define SSI_SIER_CMDAU_EN (1 << 18)
+#define SSI_SIER_CMDDU_EN (1 << 17)
+#define SSI_SIER_RXT_EN (1 << 16)
+#define SSI_SIER_RDR1_EN (1 << 15)
+#define SSI_SIER_RDR0_EN (1 << 14)
+#define SSI_SIER_TDE1_EN (1 << 13)
+#define SSI_SIER_TDE0_EN (1 << 12)
+#define SSI_SIER_ROE1_EN (1 << 11)
+#define SSI_SIER_ROE0_EN (1 << 10)
+#define SSI_SIER_TUE1_EN (1 << 9)
+#define SSI_SIER_TUE0_EN (1 << 8)
+#define SSI_SIER_TFS_EN (1 << 7)
+#define SSI_SIER_RFS_EN (1 << 6)
+#define SSI_SIER_TLS_EN (1 << 5)
+#define SSI_SIER_RLS_EN (1 << 4)
+#define SSI_SIER_RFF1_EN (1 << 3)
+#define SSI_SIER_RFF0_EN (1 << 2)
+#define SSI_SIER_TFE1_EN (1 << 1)
+#define SSI_SIER_TFE0_EN (1 << 0)
+
+#define SSI_STCR_TXBIT0 (1 << 9)
+#define SSI_STCR_TFEN1 (1 << 8)
+#define SSI_STCR_TFEN0 (1 << 7)
+#define SSI_STCR_TFDIR (1 << 6)
+#define SSI_STCR_TXDIR (1 << 5)
+#define SSI_STCR_TSHFD (1 << 4)
+#define SSI_STCR_TSCKP (1 << 3)
+#define SSI_STCR_TFSI (1 << 2)
+#define SSI_STCR_TFSL (1 << 1)
+#define SSI_STCR_TEFS (1 << 0)
+
+#define SSI_SRCR_RXBIT0 (1 << 9)
+#define SSI_SRCR_RFEN1 (1 << 8)
+#define SSI_SRCR_RFEN0 (1 << 7)
+#define SSI_SRCR_RFDIR (1 << 6)
+#define SSI_SRCR_RXDIR (1 << 5)
+#define SSI_SRCR_RSHFD (1 << 4)
+#define SSI_SRCR_RSCKP (1 << 3)
+#define SSI_SRCR_RFSI (1 << 2)
+#define SSI_SRCR_RFSL (1 << 1)
+#define SSI_SRCR_REFS (1 << 0)
+
+#define SSI_STCCR_DIV2 (1 << 18)
+#define SSI_STCCR_PSR (1 << 15)
+#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13)
+#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8)
+#define SSI_STCCR_PM(x) (((x) & 0xff) << 0)
+#define SSI_STCCR_WL_MASK (0xf << 13)
+#define SSI_STCCR_DC_MASK (0x1f << 8)
+#define SSI_STCCR_PM_MASK (0xff << 0)
+
+#define SSI_SRCCR_DIV2 (1 << 18)
+#define SSI_SRCCR_PSR (1 << 15)
+#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13)
+#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8)
+#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0)
+#define SSI_SRCCR_WL_MASK (0xf << 13)
+#define SSI_SRCCR_DC_MASK (0x1f << 8)
+#define SSI_SRCCR_PM_MASK (0xff << 0)
+
+
+#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28)
+#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24)
+#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20)
+#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16)
+#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12)
+#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8)
+#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4)
+#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0)
+
+#define SSI_STR_TEST (1 << 15)
+#define SSI_STR_RCK2TCK (1 << 14)
+#define SSI_STR_RFS2TFS (1 << 13)
+#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8)
+#define SSI_STR_TXD2RXD (1 << 7)
+#define SSI_STR_TCK2RCK (1 << 6)
+#define SSI_STR_TFS2RFS (1 << 5)
+#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0)
+
+#define SSI_SOR_CLKOFF (1 << 6)
+#define SSI_SOR_RX_CLR (1 << 5)
+#define SSI_SOR_TX_CLR (1 << 4)
+#define SSI_SOR_INIT (1 << 3)
+#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1)
+#define SSI_SOR_SYNRST (1 << 0)
+
+#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
+#define SSI_SACNT_WR (x << 4)
+#define SSI_SACNT_RD (x << 3)
+#define SSI_SACNT_TIF (x << 2)
+#define SSI_SACNT_FV (x << 1)
+#define SSI_SACNT_AC97EN (x << 0)
+
+/* Watermarks for FIFO's */
+#define TXFIFO_WATERMARK 0x4
+#define RXFIFO_WATERMARK 0x4
+
+/* i.MX DAI SSP ID's */
+#define IMX_DAI_SSI0 0 /* SSI1 FIFO 0 */
+#define IMX_DAI_SSI1 1 /* SSI1 FIFO 1 */
+#define IMX_DAI_SSI2 2 /* SSI2 FIFO 0 */
+#define IMX_DAI_SSI3 3 /* SSI2 FIFO 1 */
+
+/* SSI clock sources */
+#define IMX_SSP_SYS_CLK 0
+
+/* SSI audio dividers */
+#define IMX_SSI_TX_DIV_2 0
+#define IMX_SSI_TX_DIV_PSR 1
+#define IMX_SSI_TX_DIV_PM 2
+#define IMX_SSI_RX_DIV_2 3
+#define IMX_SSI_RX_DIV_PSR 4
+#define IMX_SSI_RX_DIV_PM 5
+
+
+/* SSI Div 2 */
+#define IMX_SSI_DIV_2_OFF (~SSI_STCCR_DIV2)
+#define IMX_SSI_DIV_2_ON SSI_STCCR_DIV2
+
+extern struct snd_soc_dai imx_ssi_pcm_dai[4];
+extern int get_ssi_clk(int ssi, struct device *dev);
+extern void put_ssi_clk(int ssi);
+#endif
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index b771238662b..2dee9839be8 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -15,6 +15,14 @@ config SND_OMAP_SOC_N810
help
Say Y if you want to add support for SoC audio on Nokia N810.
+config SND_OMAP_SOC_AMS_DELTA
+ tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
+ depends on SND_OMAP_SOC && MACH_AMS_DELTA
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_CX20442
+ help
+ Say Y if you want to add support for SoC audio on Amstrad Delta.
+
config SND_OMAP_SOC_OSK5912
tristate "SoC Audio support for omap osk5912"
depends on SND_OMAP_SOC && MACH_OMAP_OSK && I2C
@@ -72,4 +80,11 @@ config SND_OMAP_SOC_OMAP3_BEAGLE
help
Say Y if you want to add support for SoC audio on the Beagleboard.
+config SND_OMAP_SOC_ZOOM2
+ tristate "SoC Audio support for Zoom2"
+ depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_ZOOM2
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_TWL4030
+ help
+ Say Y if you want to add support for Soc audio on Zoom2 board.
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index a37f4986238..02d69471dcb 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
# OMAP Machine Support
snd-soc-n810-objs := n810.o
+snd-soc-ams-delta-objs := ams-delta.o
snd-soc-osk5912-objs := osk5912.o
snd-soc-overo-objs := overo.o
snd-soc-omap2evm-objs := omap2evm.o
@@ -14,8 +15,10 @@ snd-soc-omap3evm-objs := omap3evm.o
snd-soc-sdp3430-objs := sdp3430.o
snd-soc-omap3pandora-objs := omap3pandora.o
snd-soc-omap3beagle-objs := omap3beagle.o
+snd-soc-zoom2-objs := zoom2.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
+obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o
obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o
obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
@@ -23,3 +26,4 @@ obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o
obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
+obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c
new file mode 100644
index 00000000000..5a5166ac727
--- /dev/null
+++ b/sound/soc/omap/ams-delta.c
@@ -0,0 +1,646 @@
+/*
+ * ams-delta.c -- SoC audio for Amstrad E3 (Delta) videophone
+ *
+ * Copyright (C) 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Initially based on sound/soc/omap/osk5912.x
+ * Copyright (C) 2008 Mistral Solutions
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/tty.h>
+
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/board-ams-delta.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/cx20442.h"
+
+
+/* Board specific DAPM widgets */
+ const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
+ /* Handset */
+ SND_SOC_DAPM_MIC("Mouthpiece", NULL),
+ SND_SOC_DAPM_HP("Earpiece", NULL),
+ /* Handsfree/Speakerphone */
+ SND_SOC_DAPM_MIC("Microphone", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+/* How they are connected to codec pins */
+static const struct snd_soc_dapm_route ams_delta_audio_map[] = {
+ {"TELIN", NULL, "Mouthpiece"},
+ {"Earpiece", NULL, "TELOUT"},
+
+ {"MIC", NULL, "Microphone"},
+ {"Speaker", NULL, "SPKOUT"},
+};
+
+/*
+ * Controls, functional after the modem line discipline is activated.
+ */
+
+/* Virtual switch: audio input/output constellations */
+static const char *ams_delta_audio_mode[] =
+ {"Mixed", "Handset", "Handsfree", "Speakerphone"};
+
+/* Selection <-> pin translation */
+#define AMS_DELTA_MOUTHPIECE 0
+#define AMS_DELTA_EARPIECE 1
+#define AMS_DELTA_MICROPHONE 2
+#define AMS_DELTA_SPEAKER 3
+#define AMS_DELTA_AGC 4
+
+#define AMS_DELTA_MIXED ((1 << AMS_DELTA_EARPIECE) | \
+ (1 << AMS_DELTA_MICROPHONE))
+#define AMS_DELTA_HANDSET ((1 << AMS_DELTA_MOUTHPIECE) | \
+ (1 << AMS_DELTA_EARPIECE))
+#define AMS_DELTA_HANDSFREE ((1 << AMS_DELTA_MICROPHONE) | \
+ (1 << AMS_DELTA_SPEAKER))
+#define AMS_DELTA_SPEAKERPHONE (AMS_DELTA_HANDSFREE | (1 << AMS_DELTA_AGC))
+
+unsigned short ams_delta_audio_mode_pins[] = {
+ AMS_DELTA_MIXED,
+ AMS_DELTA_HANDSET,
+ AMS_DELTA_HANDSFREE,
+ AMS_DELTA_SPEAKERPHONE,
+};
+
+static unsigned short ams_delta_audio_agc;
+
+static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *control = (struct soc_enum *)kcontrol->private_value;
+ unsigned short pins;
+ int pin, changed = 0;
+
+ /* Refuse any mode changes if we are not able to control the codec. */
+ if (!codec->control_data)
+ return -EUNATCH;
+
+ if (ucontrol->value.enumerated.item[0] >= control->max)
+ return -EINVAL;
+
+ mutex_lock(&codec->mutex);
+
+ /* Translate selection to bitmap */
+ pins = ams_delta_audio_mode_pins[ucontrol->value.enumerated.item[0]];
+
+ /* Setup pins after corresponding bits if changed */
+ pin = !!(pins & (1 << AMS_DELTA_MOUTHPIECE));
+ if (pin != snd_soc_dapm_get_pin_status(codec, "Mouthpiece")) {
+ changed = 1;
+ if (pin)
+ snd_soc_dapm_enable_pin(codec, "Mouthpiece");
+ else
+ snd_soc_dapm_disable_pin(codec, "Mouthpiece");
+ }
+ pin = !!(pins & (1 << AMS_DELTA_EARPIECE));
+ if (pin != snd_soc_dapm_get_pin_status(codec, "Earpiece")) {
+ changed = 1;
+ if (pin)
+ snd_soc_dapm_enable_pin(codec, "Earpiece");
+ else
+ snd_soc_dapm_disable_pin(codec, "Earpiece");
+ }
+ pin = !!(pins & (1 << AMS_DELTA_MICROPHONE));
+ if (pin != snd_soc_dapm_get_pin_status(codec, "Microphone")) {
+ changed = 1;
+ if (pin)
+ snd_soc_dapm_enable_pin(codec, "Microphone");
+ else
+ snd_soc_dapm_disable_pin(codec, "Microphone");
+ }
+ pin = !!(pins & (1 << AMS_DELTA_SPEAKER));
+ if (pin != snd_soc_dapm_get_pin_status(codec, "Speaker")) {
+ changed = 1;
+ if (pin)
+ snd_soc_dapm_enable_pin(codec, "Speaker");
+ else
+ snd_soc_dapm_disable_pin(codec, "Speaker");
+ }
+ pin = !!(pins & (1 << AMS_DELTA_AGC));
+ if (pin != ams_delta_audio_agc) {
+ ams_delta_audio_agc = pin;
+ changed = 1;
+ if (pin)
+ snd_soc_dapm_enable_pin(codec, "AGCIN");
+ else
+ snd_soc_dapm_disable_pin(codec, "AGCIN");
+ }
+ if (changed)
+ snd_soc_dapm_sync(codec);
+
+ mutex_unlock(&codec->mutex);
+
+ return changed;
+}
+
+static int ams_delta_get_audio_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned short pins, mode;
+
+ pins = ((snd_soc_dapm_get_pin_status(codec, "Mouthpiece") <<
+ AMS_DELTA_MOUTHPIECE) |
+ (snd_soc_dapm_get_pin_status(codec, "Earpiece") <<
+ AMS_DELTA_EARPIECE));
+ if (pins)
+ pins |= (snd_soc_dapm_get_pin_status(codec, "Microphone") <<
+ AMS_DELTA_MICROPHONE);
+ else
+ pins = ((snd_soc_dapm_get_pin_status(codec, "Microphone") <<
+ AMS_DELTA_MICROPHONE) |
+ (snd_soc_dapm_get_pin_status(codec, "Speaker") <<
+ AMS_DELTA_SPEAKER) |
+ (ams_delta_audio_agc << AMS_DELTA_AGC));
+
+ for (mode = 0; mode < ARRAY_SIZE(ams_delta_audio_mode); mode++)
+ if (pins == ams_delta_audio_mode_pins[mode])
+ break;
+
+ if (mode >= ARRAY_SIZE(ams_delta_audio_mode))
+ return -EINVAL;
+
+ ucontrol->value.enumerated.item[0] = mode;
+
+ return 0;
+}
+
+static const struct soc_enum ams_delta_audio_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ams_delta_audio_mode),
+ ams_delta_audio_mode),
+};
+
+static const struct snd_kcontrol_new ams_delta_audio_controls[] = {
+ SOC_ENUM_EXT("Audio Mode", ams_delta_audio_enum[0],
+ ams_delta_get_audio_mode, ams_delta_set_audio_mode),
+};
+
+/* Hook switch */
+static struct snd_soc_jack ams_delta_hook_switch;
+static struct snd_soc_jack_gpio ams_delta_hook_switch_gpios[] = {
+ {
+ .gpio = 4,
+ .name = "hook_switch",
+ .report = SND_JACK_HEADSET,
+ .invert = 1,
+ .debounce_time = 150,
+ }
+};
+
+/* After we are able to control the codec over the modem,
+ * the hook switch can be used for dynamic DAPM reconfiguration. */
+static struct snd_soc_jack_pin ams_delta_hook_switch_pins[] = {
+ /* Handset */
+ {
+ .pin = "Mouthpiece",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Earpiece",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ /* Handsfree */
+ {
+ .pin = "Microphone",
+ .mask = SND_JACK_MICROPHONE,
+ .invert = 1,
+ },
+ {
+ .pin = "Speaker",
+ .mask = SND_JACK_HEADPHONE,
+ .invert = 1,
+ },
+};
+
+
+/*
+ * Modem line discipline, required for making above controls functional.
+ * Activated from userspace with ldattach, possibly invoked from udev rule.
+ */
+
+/* To actually apply any modem controlled configuration changes to the codec,
+ * we must connect codec DAI pins to the modem for a moment. Be carefull not
+ * to interfere with our digital mute function that shares the same hardware. */
+static struct timer_list cx81801_timer;
+static bool cx81801_cmd_pending;
+static bool ams_delta_muted;
+static DEFINE_SPINLOCK(ams_delta_lock);
+
+static void cx81801_timeout(unsigned long data)
+{
+ int muted;
+
+ spin_lock(&ams_delta_lock);
+ cx81801_cmd_pending = 0;
+ muted = ams_delta_muted;
+ spin_unlock(&ams_delta_lock);
+
+ /* Reconnect the codec DAI back from the modem to the CPU DAI
+ * only if digital mute still off */
+ if (!muted)
+ ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_CODEC, 0);
+}
+
+/* Line discipline .open() */
+static int cx81801_open(struct tty_struct *tty)
+{
+ return v253_ops.open(tty);
+}
+
+/* Line discipline .close() */
+static void cx81801_close(struct tty_struct *tty)
+{
+ struct snd_soc_codec *codec = tty->disc_data;
+
+ del_timer_sync(&cx81801_timer);
+
+ v253_ops.close(tty);
+
+ /* Prevent the hook switch from further changing the DAPM pins */
+ INIT_LIST_HEAD(&ams_delta_hook_switch.pins);
+
+ /* Revert back to default audio input/output constellation */
+ snd_soc_dapm_disable_pin(codec, "Mouthpiece");
+ snd_soc_dapm_enable_pin(codec, "Earpiece");
+ snd_soc_dapm_enable_pin(codec, "Microphone");
+ snd_soc_dapm_disable_pin(codec, "Speaker");
+ snd_soc_dapm_disable_pin(codec, "AGCIN");
+ snd_soc_dapm_sync(codec);
+}
+
+/* Line discipline .hangup() */
+static int cx81801_hangup(struct tty_struct *tty)
+{
+ cx81801_close(tty);
+ return 0;
+}
+
+/* Line discipline .recieve_buf() */
+static void cx81801_receive(struct tty_struct *tty,
+ const unsigned char *cp, char *fp, int count)
+{
+ struct snd_soc_codec *codec = tty->disc_data;
+ const unsigned char *c;
+ int apply, ret;
+
+ if (!codec->control_data) {
+ /* First modem response, complete setup procedure */
+
+ /* Initialize timer used for config pulse generation */
+ setup_timer(&cx81801_timer, cx81801_timeout, 0);
+
+ v253_ops.receive_buf(tty, cp, fp, count);
+
+ /* Link hook switch to DAPM pins */
+ ret = snd_soc_jack_add_pins(&ams_delta_hook_switch,
+ ARRAY_SIZE(ams_delta_hook_switch_pins),
+ ams_delta_hook_switch_pins);
+ if (ret)
+ dev_warn(codec->socdev->card->dev,
+ "Failed to link hook switch to DAPM pins, "
+ "will continue with hook switch unlinked.\n");
+
+ return;
+ }
+
+ v253_ops.receive_buf(tty, cp, fp, count);
+
+ for (c = &cp[count - 1]; c >= cp; c--) {
+ if (*c != '\r')
+ continue;
+ /* Complete modem response received, apply config to codec */
+
+ spin_lock_bh(&ams_delta_lock);
+ mod_timer(&cx81801_timer, jiffies + msecs_to_jiffies(150));
+ apply = !ams_delta_muted && !cx81801_cmd_pending;
+ cx81801_cmd_pending = 1;
+ spin_unlock_bh(&ams_delta_lock);
+
+ /* Apply config pulse by connecting the codec to the modem
+ * if not already done */
+ if (apply)
+ ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_CODEC,
+ AMS_DELTA_LATCH2_MODEM_CODEC);
+ break;
+ }
+}
+
+/* Line discipline .write_wakeup() */
+static void cx81801_wakeup(struct tty_struct *tty)
+{
+ v253_ops.write_wakeup(tty);
+}
+
+static struct tty_ldisc_ops cx81801_ops = {
+ .magic = TTY_LDISC_MAGIC,
+ .name = "cx81801",
+ .owner = THIS_MODULE,
+ .open = cx81801_open,
+ .close = cx81801_close,
+ .hangup = cx81801_hangup,
+ .receive_buf = cx81801_receive,
+ .write_wakeup = cx81801_wakeup,
+};
+
+
+/*
+ * Even if not very usefull, the sound card can still work without any of the
+ * above functonality activated. You can still control its audio input/output
+ * constellation and speakerphone gain from userspace by issueing AT commands
+ * over the modem port.
+ */
+
+static int ams_delta_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ /* Set cpu DAI configuration */
+ return snd_soc_dai_set_fmt(rtd->dai->cpu_dai,
+ SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+}
+
+static struct snd_soc_ops ams_delta_ops = {
+ .hw_params = ams_delta_hw_params,
+};
+
+
+/* Board specific codec bias level control */
+static int ams_delta_set_bias_level(struct snd_soc_card *card,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_codec *codec = card->codec;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF)
+ ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_NRESET,
+ AMS_DELTA_LATCH2_MODEM_NRESET);
+ break;
+ case SND_SOC_BIAS_OFF:
+ if (codec->bias_level != SND_SOC_BIAS_OFF)
+ ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_NRESET,
+ 0);
+ }
+ codec->bias_level = level;
+
+ return 0;
+}
+
+/* Digital mute implemented using modem/CPU multiplexer.
+ * Shares hardware with codec config pulse generation */
+static bool ams_delta_muted = 1;
+
+static int ams_delta_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ int apply;
+
+ if (ams_delta_muted == mute)
+ return 0;
+
+ spin_lock_bh(&ams_delta_lock);
+ ams_delta_muted = mute;
+ apply = !cx81801_cmd_pending;
+ spin_unlock_bh(&ams_delta_lock);
+
+ if (apply)
+ ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_CODEC,
+ mute ? AMS_DELTA_LATCH2_MODEM_CODEC : 0);
+ return 0;
+}
+
+/* Our codec DAI probably doesn't have its own .ops structure */
+static struct snd_soc_dai_ops ams_delta_dai_ops = {
+ .digital_mute = ams_delta_digital_mute,
+};
+
+/* Will be used if the codec ever has its own digital_mute function */
+static int ams_delta_startup(struct snd_pcm_substream *substream)
+{
+ return ams_delta_digital_mute(NULL, 0);
+}
+
+static void ams_delta_shutdown(struct snd_pcm_substream *substream)
+{
+ ams_delta_digital_mute(NULL, 1);
+}
+
+
+/*
+ * Card initialization
+ */
+
+static int ams_delta_cx20442_init(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dai *codec_dai = codec->dai;
+ struct snd_soc_card *card = codec->socdev->card;
+ int ret;
+ /* Codec is ready, now add/activate board specific controls */
+
+ /* Set up digital mute if not provided by the codec */
+ if (!codec_dai->ops) {
+ codec_dai->ops = &ams_delta_dai_ops;
+ } else if (!codec_dai->ops->digital_mute) {
+ codec_dai->ops->digital_mute = ams_delta_digital_mute;
+ } else {
+ ams_delta_ops.startup = ams_delta_startup;
+ ams_delta_ops.shutdown = ams_delta_shutdown;
+ }
+
+ /* Set codec bias level */
+ ams_delta_set_bias_level(card, SND_SOC_BIAS_STANDBY);
+
+ /* Add hook switch - can be used to control the codec from userspace
+ * even if line discipline fails */
+ ret = snd_soc_jack_new(card, "hook_switch",
+ SND_JACK_HEADSET, &ams_delta_hook_switch);
+ if (ret)
+ dev_warn(card->dev,
+ "Failed to allocate resources for hook switch, "
+ "will continue without one.\n");
+ else {
+ ret = snd_soc_jack_add_gpios(&ams_delta_hook_switch,
+ ARRAY_SIZE(ams_delta_hook_switch_gpios),
+ ams_delta_hook_switch_gpios);
+ if (ret)
+ dev_warn(card->dev,
+ "Failed to set up hook switch GPIO line, "
+ "will continue with hook switch inactive.\n");
+ }
+
+ /* Register optional line discipline for over the modem control */
+ ret = tty_register_ldisc(N_V253, &cx81801_ops);
+ if (ret) {
+ dev_warn(card->dev,
+ "Failed to register line discipline, "
+ "will continue without any controls.\n");
+ return 0;
+ }
+
+ /* Add board specific DAPM widgets and routes */
+ ret = snd_soc_dapm_new_controls(codec, ams_delta_dapm_widgets,
+ ARRAY_SIZE(ams_delta_dapm_widgets));
+ if (ret) {
+ dev_warn(card->dev,
+ "Failed to register DAPM controls, "
+ "will continue without any.\n");
+ return 0;
+ }
+
+ ret = snd_soc_dapm_add_routes(codec, ams_delta_audio_map,
+ ARRAY_SIZE(ams_delta_audio_map));
+ if (ret) {
+ dev_warn(card->dev,
+ "Failed to set up DAPM routes, "
+ "will continue with codec default map.\n");
+ return 0;
+ }
+
+ /* Set up initial pin constellation */
+ snd_soc_dapm_disable_pin(codec, "Mouthpiece");
+ snd_soc_dapm_enable_pin(codec, "Earpiece");
+ snd_soc_dapm_enable_pin(codec, "Microphone");
+ snd_soc_dapm_disable_pin(codec, "Speaker");
+ snd_soc_dapm_disable_pin(codec, "AGCIN");
+ snd_soc_dapm_disable_pin(codec, "AGCOUT");
+ snd_soc_dapm_sync(codec);
+
+ /* Add virtual switch */
+ ret = snd_soc_add_controls(codec, ams_delta_audio_controls,
+ ARRAY_SIZE(ams_delta_audio_controls));
+ if (ret)
+ dev_warn(card->dev,
+ "Failed to register audio mode control, "
+ "will continue without it.\n");
+
+ return 0;
+}
+
+/* DAI glue - connects codec <--> CPU */
+static struct snd_soc_dai_link ams_delta_dai_link = {
+ .name = "CX20442",
+ .stream_name = "CX20442",
+ .cpu_dai = &omap_mcbsp_dai[0],
+ .codec_dai = &cx20442_dai,
+ .init = ams_delta_cx20442_init,
+ .ops = &ams_delta_ops,
+};
+
+/* Audio card driver */
+static struct snd_soc_card ams_delta_audio_card = {
+ .name = "AMS_DELTA",
+ .platform = &omap_soc_platform,
+ .dai_link = &ams_delta_dai_link,
+ .num_links = 1,
+ .set_bias_level = ams_delta_set_bias_level,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device ams_delta_snd_soc_device = {
+ .card = &ams_delta_audio_card,
+ .codec_dev = &cx20442_codec_dev,
+};
+
+/* Module init/exit */
+static struct platform_device *ams_delta_audio_platform_device;
+static struct platform_device *cx20442_platform_device;
+
+static int __init ams_delta_module_init(void)
+{
+ int ret;
+
+ if (!(machine_is_ams_delta()))
+ return -ENODEV;
+
+ ams_delta_audio_platform_device =
+ platform_device_alloc("soc-audio", -1);
+ if (!ams_delta_audio_platform_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(ams_delta_audio_platform_device,
+ &ams_delta_snd_soc_device);
+ ams_delta_snd_soc_device.dev = &ams_delta_audio_platform_device->dev;
+ *(unsigned int *)ams_delta_dai_link.cpu_dai->private_data = OMAP_MCBSP1;
+
+ ret = platform_device_add(ams_delta_audio_platform_device);
+ if (ret)
+ goto err;
+
+ /*
+ * Codec platform device could be registered from elsewhere (board?),
+ * but I do it here as it makes sense only if used with the card.
+ */
+ cx20442_platform_device = platform_device_register_simple("cx20442",
+ -1, NULL, 0);
+ return 0;
+err:
+ platform_device_put(ams_delta_audio_platform_device);
+ return ret;
+}
+module_init(ams_delta_module_init);
+
+static void __exit ams_delta_module_exit(void)
+{
+ struct snd_soc_codec *codec;
+ struct tty_struct *tty;
+
+ if (ams_delta_audio_card.codec) {
+ codec = ams_delta_audio_card.codec;
+
+ if (codec->control_data) {
+ tty = codec->control_data;
+
+ tty_hangup(tty);
+ }
+ }
+
+ if (tty_unregister_ldisc(N_V253) != 0)
+ dev_warn(&ams_delta_audio_platform_device->dev,
+ "failed to unregister V253 line discipline\n");
+
+ snd_soc_jack_free_gpios(&ams_delta_hook_switch,
+ ARRAY_SIZE(ams_delta_hook_switch_gpios),
+ ams_delta_hook_switch_gpios);
+
+ /* Keep modem power on */
+ ams_delta_set_bias_level(&ams_delta_audio_card, SND_SOC_BIAS_STANDBY);
+
+ platform_device_unregister(cx20442_platform_device);
+ platform_device_unregister(ams_delta_audio_platform_device);
+}
+module_exit(ams_delta_module_exit);
+
+MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
+MODULE_DESCRIPTION("ALSA SoC driver for Amstrad E3 (Delta) videophone");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index b60b1dfbc43..0a505938e42 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -22,6 +22,7 @@
*/
#include <linux/clk.h>
+#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -322,8 +323,6 @@ static struct snd_soc_card snd_soc_n810 = {
/* Audio private data */
static struct aic3x_setup_data n810_aic33_setup = {
- .i2c_bus = 2,
- .i2c_address = 0x18,
.gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED,
.gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT,
};
@@ -337,6 +336,13 @@ static struct snd_soc_device n810_snd_devdata = {
static struct platform_device *n810_snd_device;
+/* temporary i2c device creation until this can be moved into the machine
+ * support file.
+*/
+static struct i2c_board_info i2c_device[] = {
+ { I2C_BOARD_INFO("tlv320aic3x", 0x1b), }
+};
+
static int __init n810_soc_init(void)
{
int err;
@@ -345,6 +351,8 @@ static int __init n810_soc_init(void)
if (!(machine_is_nokia_n810() || machine_is_nokia_n810_wimax()))
return -ENODEV;
+ i2c_register_board_info(1, i2c_device, ARRAY_SIZE(i2c_device));
+
n810_snd_device = platform_device_alloc("soc-audio", -1);
if (!n810_snd_device)
return -ENOMEM;
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index a5d46a7b196..3341f49402c 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -139,27 +139,67 @@ static const unsigned long omap34xx_mcbsp_port[][2] = {
static const unsigned long omap34xx_mcbsp_port[][2] = {};
#endif
+static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
+ int dma_op_mode = omap_mcbsp_get_dma_op_mode(mcbsp_data->bus_id);
+ int samples;
+
+ /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
+ if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD)
+ samples = snd_pcm_lib_period_bytes(substream) >> 1;
+ else
+ samples = 1;
+
+ /* Configure McBSP internal buffer usage */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ omap_mcbsp_set_tx_threshold(mcbsp_data->bus_id, samples - 1);
+ else
+ omap_mcbsp_set_rx_threshold(mcbsp_data->bus_id, samples - 1);
+}
+
static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
+ int bus_id = mcbsp_data->bus_id;
int err = 0;
- if (cpu_is_omap343x() && mcbsp_data->bus_id == 1) {
+ if (!cpu_dai->active)
+ err = omap_mcbsp_request(bus_id);
+
+ if (cpu_is_omap343x()) {
+ int dma_op_mode = omap_mcbsp_get_dma_op_mode(bus_id);
+ int max_period;
+
/*
* McBSP2 in OMAP3 has 1024 * 32-bit internal audio buffer.
* Set constraint for minimum buffer size to the same than FIFO
* size in order to avoid underruns in playback startup because
* HW is keeping the DMA request active until FIFO is filled.
*/
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4096, UINT_MAX);
- }
+ if (bus_id == 1)
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ 4096, UINT_MAX);
- if (!cpu_dai->active)
- err = omap_mcbsp_request(mcbsp_data->bus_id);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ max_period = omap_mcbsp_get_max_tx_threshold(bus_id);
+ else
+ max_period = omap_mcbsp_get_max_rx_threshold(bus_id);
+
+ max_period++;
+ max_period <<= 1;
+
+ if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD)
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ 32, max_period);
+ }
return err;
}
@@ -183,21 +223,21 @@ static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
- int err = 0;
+ int err = 0, play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!mcbsp_data->active++)
- omap_mcbsp_start(mcbsp_data->bus_id);
+ mcbsp_data->active++;
+ omap_mcbsp_start(mcbsp_data->bus_id, play, !play);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (!--mcbsp_data->active)
- omap_mcbsp_stop(mcbsp_data->bus_id);
+ omap_mcbsp_stop(mcbsp_data->bus_id, play, !play);
+ mcbsp_data->active--;
break;
default:
err = -EINVAL;
@@ -215,7 +255,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
- int wlen, channels, wpf;
+ int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT;
unsigned long port;
unsigned int format;
@@ -231,6 +271,12 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
} else if (cpu_is_omap343x()) {
dma = omap24xx_dma_reqs[bus_id][substream->stream];
port = omap34xx_mcbsp_port[bus_id][substream->stream];
+ omap_mcbsp_dai_dma_params[id][substream->stream].set_threshold =
+ omap_mcbsp_set_threshold;
+ /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
+ if (omap_mcbsp_get_dma_op_mode(bus_id) ==
+ MCBSP_DMA_MODE_THRESHOLD)
+ sync_mode = OMAP_DMA_SYNC_FRAME;
} else {
return -ENODEV;
}
@@ -238,6 +284,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
substream->stream ? "Audio Capture" : "Audio Playback";
omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma;
omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port;
+ omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode;
cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream];
if (mcbsp_data->configured) {
@@ -321,11 +368,14 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
/* Generic McBSP register settings */
regs->spcr2 |= XINTM(3) | FREE;
regs->spcr1 |= RINTM(3);
- regs->rcr2 |= RFIG;
- regs->xcr2 |= XFIG;
+ /* RFIG and XFIG are not defined in 34xx */
+ if (!cpu_is_omap34xx()) {
+ regs->rcr2 |= RFIG;
+ regs->xcr2 |= XFIG;
+ }
if (cpu_is_omap2430() || cpu_is_omap34xx()) {
- regs->xccr = DXENDLY(1) | XDMAEN;
- regs->rccr = RFULL_CYCLE | RDMAEN;
+ regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE;
+ regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -462,6 +512,40 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data,
return 0;
}
+static int omap_mcbsp_dai_set_rcvr_src(struct omap_mcbsp_data *mcbsp_data,
+ int clk_id)
+{
+ int sel_bit, set = 0;
+ u16 reg = OMAP2_CONTROL_DEVCONF0;
+
+ if (cpu_class_is_omap1())
+ return -EINVAL; /* TODO: Can this be implemented for OMAP1? */
+ if (mcbsp_data->bus_id != 0)
+ return -EINVAL;
+
+ switch (clk_id) {
+ case OMAP_MCBSP_CLKR_SRC_CLKX:
+ set = 1;
+ case OMAP_MCBSP_CLKR_SRC_CLKR:
+ sel_bit = 3;
+ break;
+ case OMAP_MCBSP_FSR_SRC_FSX:
+ set = 1;
+ case OMAP_MCBSP_FSR_SRC_FSR:
+ sel_bit = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (set)
+ omap_ctrl_writel(omap_ctrl_readl(reg) | (1 << sel_bit), reg);
+ else
+ omap_ctrl_writel(omap_ctrl_readl(reg) & ~(1 << sel_bit), reg);
+
+ return 0;
+}
+
static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq,
int dir)
@@ -484,6 +568,13 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
case OMAP_MCBSP_SYSCLK_CLKR_EXT:
regs->pcr0 |= SCLKME;
break;
+
+ case OMAP_MCBSP_CLKR_SRC_CLKR:
+ case OMAP_MCBSP_CLKR_SRC_CLKX:
+ case OMAP_MCBSP_FSR_SRC_FSR:
+ case OMAP_MCBSP_FSR_SRC_FSX:
+ err = omap_mcbsp_dai_set_rcvr_src(mcbsp_data, clk_id);
+ break;
default:
err = -ENODEV;
}
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h
index c8147aace81..647d2f981ab 100644
--- a/sound/soc/omap/omap-mcbsp.h
+++ b/sound/soc/omap/omap-mcbsp.h
@@ -32,6 +32,10 @@ enum omap_mcbsp_clksrg_clk {
OMAP_MCBSP_SYSCLK_CLK, /* Internal ICLK */
OMAP_MCBSP_SYSCLK_CLKX_EXT, /* External CLKX pin */
OMAP_MCBSP_SYSCLK_CLKR_EXT, /* External CLKR pin */
+ OMAP_MCBSP_CLKR_SRC_CLKR, /* CLKR from CLKR pin */
+ OMAP_MCBSP_CLKR_SRC_CLKX, /* CLKR from CLKX pin */
+ OMAP_MCBSP_FSR_SRC_FSR, /* FSR from FSR pin */
+ OMAP_MCBSP_FSR_SRC_FSX, /* FSR from FSX pin */
};
/* McBSP dividers */
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 84a1950880e..5735945788b 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -59,16 +59,31 @@ static void omap_pcm_dma_irq(int ch, u16 stat, void *data)
struct omap_runtime_data *prtd = runtime->private_data;
unsigned long flags;
- if (cpu_is_omap1510()) {
+ if ((cpu_is_omap1510()) &&
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) {
/*
- * OMAP1510 doesn't support DMA chaining so have to restart
- * the transfer after all periods are transferred
+ * OMAP1510 doesn't fully support DMA progress counter
+ * and there is no software emulation implemented yet,
+ * so have to maintain our own playback progress counter
+ * that can be used by omap_pcm_pointer() instead.
*/
spin_lock_irqsave(&prtd->lock, flags);
+ if ((stat == OMAP_DMA_LAST_IRQ) &&
+ (prtd->period_index == runtime->periods - 1)) {
+ /* we are in sync, do nothing */
+ spin_unlock_irqrestore(&prtd->lock, flags);
+ return;
+ }
if (prtd->period_index >= 0) {
- if (++prtd->period_index == runtime->periods) {
+ if (stat & OMAP_DMA_BLOCK_IRQ) {
+ /* end of buffer reached, loop back */
+ prtd->period_index = 0;
+ } else if (stat & OMAP_DMA_LAST_IRQ) {
+ /* update the counter for the last period */
+ prtd->period_index = runtime->periods - 1;
+ } else if (++prtd->period_index >= runtime->periods) {
+ /* end of buffer missed? loop back */
prtd->period_index = 0;
- omap_start_dma(prtd->dma_ch);
}
}
spin_unlock_irqrestore(&prtd->lock, flags);
@@ -100,7 +115,7 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
prtd->dma_data = dma_data;
err = omap_request_dma(dma_data->dma_req, dma_data->name,
omap_pcm_dma_irq, substream, &prtd->dma_ch);
- if (!err && !cpu_is_omap1510()) {
+ if (!err) {
/*
* Link channel with itself so DMA doesn't need any
* reprogramming while looping the buffer
@@ -119,8 +134,7 @@ static int omap_pcm_hw_free(struct snd_pcm_substream *substream)
if (prtd->dma_data == NULL)
return 0;
- if (!cpu_is_omap1510())
- omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch);
+ omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch);
omap_free_dma(prtd->dma_ch);
prtd->dma_data = NULL;
@@ -148,7 +162,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
*/
dma_params.data_type = OMAP_DMA_DATA_TYPE_S16;
dma_params.trigger = dma_data->dma_req;
- dma_params.sync_mode = OMAP_DMA_SYNC_ELEMENT;
+ dma_params.sync_mode = dma_data->sync_mode;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dma_params.src_amode = OMAP_DMA_AMODE_POST_INC;
dma_params.dst_amode = OMAP_DMA_AMODE_CONSTANT;
@@ -174,7 +188,15 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
dma_params.frame_count = runtime->periods;
omap_set_dma_params(prtd->dma_ch, &dma_params);
- omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
+ if ((cpu_is_omap1510()) &&
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK))
+ omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ |
+ OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ);
+ else
+ omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
+
+ omap_set_dma_src_burst_mode(prtd->dma_ch, OMAP_DMA_DATA_BURST_16);
+ omap_set_dma_dest_burst_mode(prtd->dma_ch, OMAP_DMA_DATA_BURST_16);
return 0;
}
@@ -183,6 +205,7 @@ static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct omap_runtime_data *prtd = runtime->private_data;
+ struct omap_pcm_dma_data *dma_data = prtd->dma_data;
unsigned long flags;
int ret = 0;
@@ -192,6 +215,10 @@ static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
prtd->period_index = 0;
+ /* Configure McBSP internal buffer usage */
+ if (dma_data->set_threshold)
+ dma_data->set_threshold(substream);
+
omap_start_dma(prtd->dma_ch);
break;
@@ -288,7 +315,7 @@ static struct snd_pcm_ops omap_pcm_ops = {
.mmap = omap_pcm_mmap,
};
-static u64 omap_pcm_dmamask = DMA_BIT_MASK(32);
+static u64 omap_pcm_dmamask = DMA_BIT_MASK(64);
static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
int stream)
@@ -330,7 +357,7 @@ static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
}
}
-int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+static int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
int ret = 0;
@@ -338,7 +365,7 @@ int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
if (!card->dev->dma_mask)
card->dev->dma_mask = &omap_pcm_dmamask;
if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(64);
if (dai->playback.channels_min) {
ret = omap_pcm_preallocate_dma_buffer(pcm,
diff --git a/sound/soc/omap/omap-pcm.h b/sound/soc/omap/omap-pcm.h
index 8d9d26916b0..38a821dd411 100644
--- a/sound/soc/omap/omap-pcm.h
+++ b/sound/soc/omap/omap-pcm.h
@@ -29,6 +29,8 @@ struct omap_pcm_dma_data {
char *name; /* stream identifier */
int dma_req; /* DMA request line */
unsigned long port_addr; /* transmit/receive register */
+ int sync_mode; /* DMA sync mode */
+ void (*set_threshold)(struct snd_pcm_substream *substream);
};
extern struct snd_soc_platform omap_soc_platform;
diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c
index b719e5db4f5..4a3f62d1f29 100644
--- a/sound/soc/omap/sdp3430.c
+++ b/sound/soc/omap/sdp3430.c
@@ -24,6 +24,7 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
@@ -39,6 +40,11 @@
#include "omap-pcm.h"
#include "../codecs/twl4030.h"
+/* TWL4030 PMBR1 Register */
+#define TWL4030_INTBR_PMBR1 0x0D
+/* TWL4030 PMBR1 Register GPIO6 mux bit */
+#define TWL4030_GPIO6_PWM0_MUTE(value) (value << 2)
+
static struct snd_soc_card snd_soc_sdp3430;
static int sdp3430_hw_params(struct snd_pcm_substream *substream,
@@ -96,7 +102,7 @@ static int sdp3430_hw_voice_params(struct snd_pcm_substream *substream,
ret = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_NF |
- SND_SOC_DAIFMT_CBS_CFM);
+ SND_SOC_DAIFMT_CBM_CFM);
if (ret) {
printk(KERN_ERR "can't set codec DAI configuration\n");
return ret;
@@ -280,6 +286,7 @@ static struct snd_soc_card snd_soc_sdp3430 = {
static struct twl4030_setup_data twl4030_setup = {
.ramp_delay_value = 3,
.sysclk = 26000,
+ .hs_extmute = 1,
};
/* Audio subsystem */
@@ -294,6 +301,7 @@ static struct platform_device *sdp3430_snd_device;
static int __init sdp3430_soc_init(void)
{
int ret;
+ u8 pin_mux;
if (!machine_is_omap_3430sdp()) {
pr_debug("Not SDP3430!\n");
@@ -312,6 +320,14 @@ static int __init sdp3430_soc_init(void)
*(unsigned int *)sdp3430_dai[0].cpu_dai->private_data = 1; /* McBSP2 */
*(unsigned int *)sdp3430_dai[1].cpu_dai->private_data = 2; /* McBSP3 */
+ /* Set TWL4030 GPIO6 as EXTMUTE signal */
+ twl4030_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux,
+ TWL4030_INTBR_PMBR1);
+ pin_mux &= ~TWL4030_GPIO6_PWM0_MUTE(0x03);
+ pin_mux |= TWL4030_GPIO6_PWM0_MUTE(0x02);
+ twl4030_i2c_write_u8(TWL4030_MODULE_INTBR, pin_mux,
+ TWL4030_INTBR_PMBR1);
+
ret = platform_device_add(sdp3430_snd_device);
if (ret)
goto err1;
diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c
new file mode 100644
index 00000000000..f90b45f5622
--- /dev/null
+++ b/sound/soc/omap/zoom2.c
@@ -0,0 +1,314 @@
+/*
+ * zoom2.c -- SoC audio for Zoom2
+ *
+ * Author: Misael Lopez Cruz <x0052729@ti.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+#define ZOOM2_HEADSET_MUX_GPIO (OMAP_MAX_GPIO_LINES + 15)
+#define ZOOM2_HEADSET_EXTMUTE_GPIO 153
+
+static int zoom2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec DAI configuration\n");
+ return ret;
+ }
+
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops zoom2_ops = {
+ .hw_params = zoom2_hw_params,
+};
+
+static int zoom2_hw_voice_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_IB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret) {
+ printk(KERN_ERR "can't set codec DAI configuration\n");
+ return ret;
+ }
+
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_IB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops zoom2_voice_ops = {
+ .hw_params = zoom2_hw_voice_params,
+};
+
+/* Zoom2 machine DAPM */
+static const struct snd_soc_dapm_widget zoom2_twl4030_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Ext Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_HP("Headset Stereophone", NULL),
+ SND_SOC_DAPM_LINE("Aux In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* External Mics: MAINMIC, SUBMIC with bias*/
+ {"MAINMIC", NULL, "Mic Bias 1"},
+ {"SUBMIC", NULL, "Mic Bias 2"},
+ {"Mic Bias 1", NULL, "Ext Mic"},
+ {"Mic Bias 2", NULL, "Ext Mic"},
+
+ /* External Speakers: HFL, HFR */
+ {"Ext Spk", NULL, "HFL"},
+ {"Ext Spk", NULL, "HFR"},
+
+ /* Headset Stereophone: HSOL, HSOR */
+ {"Headset Stereophone", NULL, "HSOL"},
+ {"Headset Stereophone", NULL, "HSOR"},
+
+ /* Headset Mic: HSMIC with bias */
+ {"HSMIC", NULL, "Headset Mic Bias"},
+ {"Headset Mic Bias", NULL, "Headset Mic"},
+
+ /* Aux In: AUXL, AUXR */
+ {"Aux In", NULL, "AUXL"},
+ {"Aux In", NULL, "AUXR"},
+};
+
+static int zoom2_twl4030_init(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ /* Add Zoom2 specific widgets */
+ ret = snd_soc_dapm_new_controls(codec, zoom2_twl4030_dapm_widgets,
+ ARRAY_SIZE(zoom2_twl4030_dapm_widgets));
+ if (ret)
+ return ret;
+
+ /* Set up Zoom2 specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ /* Zoom2 connected pins */
+ snd_soc_dapm_enable_pin(codec, "Ext Mic");
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+ snd_soc_dapm_enable_pin(codec, "Headset Mic");
+ snd_soc_dapm_enable_pin(codec, "Headset Stereophone");
+ snd_soc_dapm_enable_pin(codec, "Aux In");
+
+ /* TWL4030 not connected pins */
+ snd_soc_dapm_nc_pin(codec, "CARKITMIC");
+ snd_soc_dapm_nc_pin(codec, "DIGIMIC0");
+ snd_soc_dapm_nc_pin(codec, "DIGIMIC1");
+
+ snd_soc_dapm_nc_pin(codec, "OUTL");
+ snd_soc_dapm_nc_pin(codec, "OUTR");
+ snd_soc_dapm_nc_pin(codec, "EARPIECE");
+ snd_soc_dapm_nc_pin(codec, "PREDRIVEL");
+ snd_soc_dapm_nc_pin(codec, "PREDRIVER");
+ snd_soc_dapm_nc_pin(codec, "CARKITL");
+ snd_soc_dapm_nc_pin(codec, "CARKITR");
+
+ ret = snd_soc_dapm_sync(codec);
+
+ return ret;
+}
+
+static int zoom2_twl4030_voice_init(struct snd_soc_codec *codec)
+{
+ unsigned short reg;
+
+ /* Enable voice interface */
+ reg = codec->read(codec, TWL4030_REG_VOICE_IF);
+ reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN;
+ codec->write(codec, TWL4030_REG_VOICE_IF, reg);
+
+ return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link zoom2_dai[] = {
+ {
+ .name = "TWL4030 I2S",
+ .stream_name = "TWL4030 Audio",
+ .cpu_dai = &omap_mcbsp_dai[0],
+ .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
+ .init = zoom2_twl4030_init,
+ .ops = &zoom2_ops,
+ },
+ {
+ .name = "TWL4030 PCM",
+ .stream_name = "TWL4030 Voice",
+ .cpu_dai = &omap_mcbsp_dai[1],
+ .codec_dai = &twl4030_dai[TWL4030_DAI_VOICE],
+ .init = zoom2_twl4030_voice_init,
+ .ops = &zoom2_voice_ops,
+ },
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_zoom2 = {
+ .name = "Zoom2",
+ .platform = &omap_soc_platform,
+ .dai_link = zoom2_dai,
+ .num_links = ARRAY_SIZE(zoom2_dai),
+};
+
+/* EXTMUTE callback function */
+void zoom2_set_hs_extmute(int mute)
+{
+ gpio_set_value(ZOOM2_HEADSET_EXTMUTE_GPIO, mute);
+}
+
+/* twl4030 setup */
+static struct twl4030_setup_data twl4030_setup = {
+ .ramp_delay_value = 3, /* 161 ms */
+ .sysclk = 26000,
+ .hs_extmute = 1,
+ .set_hs_extmute = zoom2_set_hs_extmute,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device zoom2_snd_devdata = {
+ .card = &snd_soc_zoom2,
+ .codec_dev = &soc_codec_dev_twl4030,
+ .codec_data = &twl4030_setup,
+};
+
+static struct platform_device *zoom2_snd_device;
+
+static int __init zoom2_soc_init(void)
+{
+ int ret;
+
+ if (!machine_is_omap_zoom2()) {
+ pr_debug("Not Zoom2!\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "Zoom2 SoC init\n");
+
+ zoom2_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!zoom2_snd_device) {
+ printk(KERN_ERR "Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(zoom2_snd_device, &zoom2_snd_devdata);
+ zoom2_snd_devdata.dev = &zoom2_snd_device->dev;
+ *(unsigned int *)zoom2_dai[0].cpu_dai->private_data = 1; /* McBSP2 */
+ *(unsigned int *)zoom2_dai[1].cpu_dai->private_data = 2; /* McBSP3 */
+
+ ret = platform_device_add(zoom2_snd_device);
+ if (ret)
+ goto err1;
+
+ BUG_ON(gpio_request(ZOOM2_HEADSET_MUX_GPIO, "hs_mux") < 0);
+ gpio_direction_output(ZOOM2_HEADSET_MUX_GPIO, 0);
+
+ BUG_ON(gpio_request(ZOOM2_HEADSET_EXTMUTE_GPIO, "ext_mute") < 0);
+ gpio_direction_output(ZOOM2_HEADSET_EXTMUTE_GPIO, 0);
+
+ return 0;
+
+err1:
+ printk(KERN_ERR "Unable to add platform device\n");
+ platform_device_put(zoom2_snd_device);
+
+ return ret;
+}
+module_init(zoom2_soc_init);
+
+static void __exit zoom2_soc_exit(void)
+{
+ gpio_free(ZOOM2_HEADSET_MUX_GPIO);
+ gpio_free(ZOOM2_HEADSET_EXTMUTE_GPIO);
+
+ platform_device_unregister(zoom2_snd_device);
+}
+module_exit(zoom2_soc_exit);
+
+MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC Zoom2");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c
index 326955dea36..9f7c61e23da 100644
--- a/sound/soc/pxa/magician.c
+++ b/sound/soc/pxa/magician.c
@@ -20,12 +20,14 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/gpio.h>
+#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include <sound/uda1380.h>
#include <mach/magician.h>
#include <asm/mach-types.h>
@@ -188,7 +190,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 1);
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 0, 1, width);
if (ret < 0)
return ret;
@@ -447,34 +449,47 @@ static struct snd_soc_card snd_soc_card_magician = {
.platform = &pxa2xx_soc_platform,
};
-/* magician audio private data */
-static struct uda1380_setup_data magician_uda1380_setup = {
- .i2c_address = 0x18,
- .dac_clk = UDA1380_DAC_CLK_WSPLL,
-};
-
/* magician audio subsystem */
static struct snd_soc_device magician_snd_devdata = {
.card = &snd_soc_card_magician,
.codec_dev = &soc_codec_dev_uda1380,
- .codec_data = &magician_uda1380_setup,
};
static struct platform_device *magician_snd_device;
+/*
+ * FIXME: move into magician board file once merged into the pxa tree
+ */
+static struct uda1380_platform_data uda1380_info = {
+ .gpio_power = EGPIO_MAGICIAN_CODEC_POWER,
+ .gpio_reset = EGPIO_MAGICIAN_CODEC_RESET,
+ .dac_clk = UDA1380_DAC_CLK_WSPLL,
+};
+
+static struct i2c_board_info i2c_board_info[] = {
+ {
+ I2C_BOARD_INFO("uda1380", 0x18),
+ .platform_data = &uda1380_info,
+ },
+};
+
static int __init magician_init(void)
{
int ret;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
if (!machine_is_magician())
return -ENODEV;
- ret = gpio_request(EGPIO_MAGICIAN_CODEC_POWER, "CODEC_POWER");
- if (ret)
- goto err_request_power;
- ret = gpio_request(EGPIO_MAGICIAN_CODEC_RESET, "CODEC_RESET");
- if (ret)
- goto err_request_reset;
+ adapter = i2c_get_adapter(0);
+ if (!adapter)
+ return -ENODEV;
+ client = i2c_new_device(adapter, i2c_board_info);
+ i2c_put_adapter(adapter);
+ if (!client)
+ return -ENODEV;
+
ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER");
if (ret)
goto err_request_spk;
@@ -491,14 +506,8 @@ static int __init magician_init(void)
if (ret)
goto err_request_in_sel1;
- gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 1);
gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0);
- /* we may need to have the clock running here - pH5 */
- gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 1);
- udelay(5);
- gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 0);
-
magician_snd_device = platform_device_alloc("soc-audio", -1);
if (!magician_snd_device) {
ret = -ENOMEM;
@@ -526,10 +535,6 @@ err_request_mic:
err_request_ep:
gpio_free(EGPIO_MAGICIAN_SPK_POWER);
err_request_spk:
- gpio_free(EGPIO_MAGICIAN_CODEC_RESET);
-err_request_reset:
- gpio_free(EGPIO_MAGICIAN_CODEC_POWER);
-err_request_power:
return ret;
}
@@ -540,15 +545,12 @@ static void __exit magician_exit(void)
gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0);
gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0);
gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0);
- gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 0);
gpio_free(EGPIO_MAGICIAN_IN_SEL1);
gpio_free(EGPIO_MAGICIAN_IN_SEL0);
gpio_free(EGPIO_MAGICIAN_MIC_POWER);
gpio_free(EGPIO_MAGICIAN_EP_POWER);
gpio_free(EGPIO_MAGICIAN_SPK_POWER);
- gpio_free(EGPIO_MAGICIAN_CODEC_RESET);
- gpio_free(EGPIO_MAGICIAN_CODEC_POWER);
}
module_init(magician_init);
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c
index e6102fda0a7..1f96e3227be 100644
--- a/sound/soc/pxa/palm27x.c
+++ b/sound/soc/pxa/palm27x.c
@@ -17,13 +17,12 @@
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/gpio.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include <sound/jack.h>
#include <asm/mach-types.h>
#include <mach/audio.h>
@@ -33,90 +32,31 @@
#include "pxa2xx-pcm.h"
#include "pxa2xx-ac97.h"
-static int palm27x_jack_func = 1;
-static int palm27x_spk_func = 1;
-static int palm27x_ep_gpio = -1;
+static struct snd_soc_jack hs_jack;
-static void palm27x_ext_control(struct snd_soc_codec *codec)
-{
- if (!palm27x_spk_func)
- snd_soc_dapm_enable_pin(codec, "Speaker");
- else
- snd_soc_dapm_disable_pin(codec, "Speaker");
-
- if (!palm27x_jack_func)
- snd_soc_dapm_enable_pin(codec, "Headphone Jack");
- else
- snd_soc_dapm_disable_pin(codec, "Headphone Jack");
-
- snd_soc_dapm_sync(codec);
-}
-
-static int palm27x_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->socdev->card->codec;
-
- /* check the jack status at stream startup */
- palm27x_ext_control(codec);
- return 0;
-}
-
-static struct snd_soc_ops palm27x_ops = {
- .startup = palm27x_startup,
+/* Headphones jack detection DAPM pins */
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
};
-static irqreturn_t palm27x_interrupt(int irq, void *v)
-{
- palm27x_spk_func = gpio_get_value(palm27x_ep_gpio);
- palm27x_jack_func = !palm27x_spk_func;
- return IRQ_HANDLED;
-}
-
-static int palm27x_get_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = palm27x_jack_func;
- return 0;
-}
-
-static int palm27x_set_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-
- if (palm27x_jack_func == ucontrol->value.integer.value[0])
- return 0;
-
- palm27x_jack_func = ucontrol->value.integer.value[0];
- palm27x_ext_control(codec);
- return 1;
-}
-
-static int palm27x_get_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = palm27x_spk_func;
- return 0;
-}
-
-static int palm27x_set_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-
- if (palm27x_spk_func == ucontrol->value.integer.value[0])
- return 0;
-
- palm27x_spk_func = ucontrol->value.integer.value[0];
- palm27x_ext_control(codec);
- return 1;
-}
+/* Headphones jack detection gpios */
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+ [0] = {
+ /* gpio is set on per-platform basis */
+ .name = "hp-gpio",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 200,
+ },
+};
-/* PalmTX machine dapm widgets */
+/* Palm27x machine dapm widgets */
static const struct snd_soc_dapm_widget palm27x_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_SPK("Ext. Speaker", NULL),
+ SND_SOC_DAPM_MIC("Ext. Microphone", NULL),
};
/* PalmTX audio map */
@@ -126,46 +66,66 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Headphone Jack", NULL, "HPOUTR"},
/* ext speaker connected to ROUT2, LOUT2 */
- {"Speaker", NULL, "LOUT2"},
- {"Speaker", NULL, "ROUT2"},
-};
+ {"Ext. Speaker", NULL, "LOUT2"},
+ {"Ext. Speaker", NULL, "ROUT2"},
-static const char *jack_function[] = {"Headphone", "Off"};
-static const char *spk_function[] = {"On", "Off"};
-static const struct soc_enum palm27x_enum[] = {
- SOC_ENUM_SINGLE_EXT(2, jack_function),
- SOC_ENUM_SINGLE_EXT(2, spk_function),
+ /* mic connected to MIC1 */
+ {"Ext. Microphone", NULL, "MIC1"},
};
-static const struct snd_kcontrol_new palm27x_controls[] = {
- SOC_ENUM_EXT("Jack Function", palm27x_enum[0], palm27x_get_jack,
- palm27x_set_jack),
- SOC_ENUM_EXT("Speaker Function", palm27x_enum[1], palm27x_get_spk,
- palm27x_set_spk),
-};
+static struct snd_soc_card palm27x_asoc;
static int palm27x_ac97_init(struct snd_soc_codec *codec)
{
int err;
+ /* add palm27x specific widgets */
+ err = snd_soc_dapm_new_controls(codec, 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(codec, audio_map, ARRAY_SIZE(audio_map));
+ if (err)
+ return err;
+
+ /* connected pins */
+ if (machine_is_palmld())
+ snd_soc_dapm_enable_pin(codec, "MIC1");
+ snd_soc_dapm_enable_pin(codec, "HPOUTL");
+ snd_soc_dapm_enable_pin(codec, "HPOUTR");
+ snd_soc_dapm_enable_pin(codec, "LOUT2");
+ snd_soc_dapm_enable_pin(codec, "ROUT2");
+
+ /* not connected pins */
snd_soc_dapm_nc_pin(codec, "OUT3");
snd_soc_dapm_nc_pin(codec, "MONOOUT");
+ snd_soc_dapm_nc_pin(codec, "LINEINL");
+ snd_soc_dapm_nc_pin(codec, "LINEINR");
+ snd_soc_dapm_nc_pin(codec, "PCBEEP");
+ snd_soc_dapm_nc_pin(codec, "PHONE");
+ snd_soc_dapm_nc_pin(codec, "MIC2");
+
+ err = snd_soc_dapm_sync(codec);
+ if (err)
+ return err;
- /* add palm27x specific controls */
- err = snd_soc_add_controls(codec, palm27x_controls,
- ARRAY_SIZE(palm27x_controls));
- if (err < 0)
+ /* Jack detection API stuff */
+ err = snd_soc_jack_new(&palm27x_asoc, "Headphone Jack",
+ SND_JACK_HEADPHONE, &hs_jack);
+ if (err)
return err;
- /* add palm27x specific widgets */
- snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets,
- ARRAY_SIZE(palm27x_dapm_widgets));
+ err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
+ hs_jack_pins);
+ if (err)
+ return err;
- /* set up palm27x specific audio path audio_map */
- snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+ err = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
- snd_soc_dapm_sync(codec);
- return 0;
+ return err;
}
static struct snd_soc_dai_link palm27x_dai[] = {
@@ -175,14 +135,12 @@ static struct snd_soc_dai_link palm27x_dai[] = {
.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
.codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
.init = palm27x_ac97_init,
- .ops = &palm27x_ops,
},
{
.name = "AC97 Aux",
.stream_name = "AC97 Aux",
.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
.codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
- .ops = &palm27x_ops,
},
};
@@ -208,27 +166,17 @@ static int palm27x_asoc_probe(struct platform_device *pdev)
machine_is_palmld() || machine_is_palmte2()))
return -ENODEV;
- if (pdev->dev.platform_data)
- palm27x_ep_gpio = ((struct palm27x_asoc_info *)
- (pdev->dev.platform_data))->jack_gpio;
-
- ret = gpio_request(palm27x_ep_gpio, "Headphone Jack");
- if (ret)
- return ret;
- ret = gpio_direction_input(palm27x_ep_gpio);
- if (ret)
- goto err_alloc;
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "please supply platform_data\n");
+ return -ENODEV;
+ }
- if (request_irq(gpio_to_irq(palm27x_ep_gpio), palm27x_interrupt,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "Headphone jack", NULL))
- goto err_alloc;
+ 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) {
- ret = -ENOMEM;
- goto err_dev;
- }
+ if (!palm27x_snd_device)
+ return -ENOMEM;
platform_set_drvdata(palm27x_snd_device, &palm27x_snd_devdata);
palm27x_snd_devdata.dev = &palm27x_snd_device->dev;
@@ -241,18 +189,12 @@ static int palm27x_asoc_probe(struct platform_device *pdev)
put_device:
platform_device_put(palm27x_snd_device);
-err_dev:
- free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
-err_alloc:
- gpio_free(palm27x_ep_gpio);
return ret;
}
static int __devexit palm27x_asoc_remove(struct platform_device *pdev)
{
- free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
- gpio_free(palm27x_ep_gpio);
platform_device_unregister(palm27x_snd_device);
return 0;
}
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 19c45409d94..5b9ed646478 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -375,21 +375,34 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai,
* Set the active slots in TDM/Network mode
*/
static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
- unsigned int mask, int slots)
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
{
struct ssp_priv *priv = cpu_dai->private_data;
struct ssp_device *ssp = priv->dev.ssp;
u32 sscr0;
- sscr0 = ssp_read_reg(ssp, SSCR0) & ~SSCR0_SlotsPerFrm(7);
+ sscr0 = ssp_read_reg(ssp, SSCR0);
+ sscr0 &= ~(SSCR0_MOD | SSCR0_SlotsPerFrm(8) | SSCR0_EDSS | SSCR0_DSS);
+
+ /* set slot width */
+ if (slot_width > 16)
+ sscr0 |= SSCR0_EDSS | SSCR0_DataSize(slot_width - 16);
+ else
+ sscr0 |= SSCR0_DataSize(slot_width);
+
+ if (slots > 1) {
+ /* enable network mode */
+ sscr0 |= SSCR0_MOD;
- /* set number of active slots */
- sscr0 |= SSCR0_SlotsPerFrm(slots);
+ /* set number of active slots */
+ sscr0 |= SSCR0_SlotsPerFrm(slots);
+
+ /* set active slot mask */
+ ssp_write_reg(ssp, SSTSA, tx_mask);
+ ssp_write_reg(ssp, SSRSA, rx_mask);
+ }
ssp_write_reg(ssp, SSCR0, sscr0);
- /* set active slot mask */
- ssp_write_reg(ssp, SSTSA, mask);
- ssp_write_reg(ssp, SSRSA, mask);
return 0;
}
@@ -457,31 +470,27 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return -EINVAL;
}
- ssp_write_reg(ssp, SSCR0, sscr0);
- ssp_write_reg(ssp, SSCR1, sscr1);
- ssp_write_reg(ssp, SSPSP, sspsp);
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ sspsp |= SSPSP_SFRMP;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ sspsp |= SSPSP_SCMODE(2);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
+ break;
+ default:
+ return -EINVAL;
+ }
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
sscr0 |= SSCR0_PSP;
sscr1 |= SSCR1_RWOT | SSCR1_TRAIL;
-
/* See hw_params() */
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- sspsp |= SSPSP_SFRMP;
- break;
- case SND_SOC_DAIFMT_NB_IF:
- break;
- case SND_SOC_DAIFMT_IB_IF:
- sspsp |= SSPSP_SCMODE(2);
- break;
- case SND_SOC_DAIFMT_IB_NF:
- sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
- break;
- default:
- return -EINVAL;
- }
break;
case SND_SOC_DAIFMT_DSP_A:
@@ -489,22 +498,6 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
case SND_SOC_DAIFMT_DSP_B:
sscr0 |= SSCR0_MOD | SSCR0_PSP;
sscr1 |= SSCR1_TRAIL | SSCR1_RWOT;
-
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- sspsp |= SSPSP_SFRMP;
- break;
- case SND_SOC_DAIFMT_NB_IF:
- break;
- case SND_SOC_DAIFMT_IB_IF:
- sspsp |= SSPSP_SCMODE(2);
- break;
- case SND_SOC_DAIFMT_IB_NF:
- sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
- break;
- default:
- return -EINVAL;
- }
break;
default:
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index d9c94d71fa6..e9ae7b3a7e0 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -22,6 +22,7 @@
#include <mach/hardware.h>
#include <mach/regs-ac97.h>
#include <mach/dma.h>
+#include <mach/audio.h>
#include "pxa2xx-pcm.h"
#include "pxa2xx-ac97.h"
@@ -241,9 +242,18 @@ EXPORT_SYMBOL_GPL(soc_ac97_ops);
static int __devinit pxa2xx_ac97_dev_probe(struct platform_device *pdev)
{
int i;
+ pxa2xx_audio_ops_t *pdata = pdev->dev.platform_data;
- for (i = 0; i < ARRAY_SIZE(pxa_ac97_dai); i++)
+ if (pdev->id >= 0) {
+ dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n");
+ return -ENXIO;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pxa_ac97_dai); i++) {
pxa_ac97_dai[i].dev = &pdev->dev;
+ if (pdata && pdata->codec_pdata[0])
+ pxa_ac97_dai[i].ac97_pdata = pdata->codec_pdata[0];
+ }
/* Punt most of the init to the SoC probe; we may need the machine
* driver to do interesting things with the clocking to get us up
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index df494d1e346..923428fc1ad 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -1,6 +1,7 @@
config SND_S3C24XX_SOC
tristate "SoC Audio for the Samsung S3CXXXX chips"
- depends on ARCH_S3C2410
+ depends on ARCH_S3C2410 || ARCH_S3C64XX
+ select S3C64XX_DMA if ARCH_S3C64XX
help
Say Y or M if you want to add support for codecs attached to
the S3C24XX AC97 or I2S interfaces. You will also need to
@@ -38,6 +39,15 @@ config SND_S3C24XX_SOC_NEO1973_WM8753
Say Y if you want to add support for SoC audio on smdk2440
with the WM8753.
+config SND_S3C24XX_SOC_NEO1973_GTA02_WM8753
+ tristate "Audio support for the Openmoko Neo FreeRunner (GTA02)"
+ depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA02
+ select SND_S3C24XX_SOC_I2S
+ select SND_SOC_WM8753
+ help
+ This driver provides audio support for the Openmoko Neo FreeRunner
+ smartphone.
+
config SND_S3C24XX_SOC_JIVE_WM8750
tristate "SoC I2S Audio support for Jive"
depends on SND_S3C24XX_SOC && MACH_JIVE
@@ -57,7 +67,7 @@ config SND_S3C24XX_SOC_SMDK2443_WM9710
config SND_S3C24XX_SOC_LN2440SBC_ALC650
tristate "SoC AC97 Audio support for LN2440SBC - ALC650"
- depends on SND_S3C24XX_SOC
+ depends on SND_S3C24XX_SOC && ARCH_S3C2410
select SND_S3C2443_SOC_AC97
select SND_SOC_AC97_CODEC
help
@@ -66,7 +76,26 @@ config SND_S3C24XX_SOC_LN2440SBC_ALC650
config SND_S3C24XX_SOC_S3C24XX_UDA134X
tristate "SoC I2S Audio support UDA134X wired to a S3C24XX"
- depends on SND_S3C24XX_SOC
+ depends on SND_S3C24XX_SOC && ARCH_S3C2410
select SND_S3C24XX_SOC_I2S
select SND_SOC_L3
select SND_SOC_UDA134X
+
+config SND_S3C24XX_SOC_SIMTEC
+ tristate
+ help
+ Internal node for common S3C24XX/Simtec suppor
+
+config SND_S3C24XX_SOC_SIMTEC_TLV320AIC23
+ tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards"
+ depends on SND_S3C24XX_SOC && ARCH_S3C2410
+ select SND_S3C24XX_SOC_I2S
+ select SND_SOC_TLV320AIC23
+ select SND_S3C24XX_SOC_SIMTEC
+
+config SND_S3C24XX_SOC_SIMTEC_HERMES
+ tristate "SoC I2S Audio support for Simtec Hermes board"
+ depends on SND_S3C24XX_SOC && ARCH_S3C2410
+ select SND_S3C24XX_SOC_I2S
+ select SND_SOC_TLV320AIC3X
+ select SND_S3C24XX_SOC_SIMTEC
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index 07a93a2ebe5..99f5a7dd3fc 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -16,12 +16,21 @@ obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
# S3C24XX Machine Support
snd-soc-jive-wm8750-objs := jive_wm8750.o
snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
+snd-soc-neo1973-gta02-wm8753-objs := neo1973_gta02_wm8753.o
snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
+snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o
+snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o
+snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o
obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
+obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_GTA02_WM8753) += snd-soc-neo1973-gta02-wm8753.o
obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
+obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
+obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
+obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
+
diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
new file mode 100644
index 00000000000..0c52e36ddd8
--- /dev/null
+++ b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
@@ -0,0 +1,498 @@
+/*
+ * neo1973_gta02_wm8753.c -- SoC audio for Openmoko Freerunner(GTA02)
+ *
+ * Copyright 2007 Openmoko Inc
+ * Author: Graeme Gregory <graeme@openmoko.org>
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory <linux@wolfsonmicro.com>
+ * Copyright 2009 Wolfson Microelectronics
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+
+#include <plat/regs-iis.h>
+
+#include <mach/regs-clock.h>
+#include <asm/io.h>
+#include <mach/gta02.h>
+#include "../codecs/wm8753.h"
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+
+static struct snd_soc_card neo1973_gta02;
+
+static int neo1973_gta02_hifi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ unsigned int pll_out = 0, bclk = 0;
+ int ret = 0;
+ unsigned long iis_clkrate;
+
+ iis_clkrate = s3c24xx_i2s_get_clockrate();
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ pll_out = 12288000;
+ break;
+ case 48000:
+ bclk = WM8753_BCLK_DIV_4;
+ pll_out = 12288000;
+ break;
+ case 96000:
+ bclk = WM8753_BCLK_DIV_2;
+ pll_out = 12288000;
+ break;
+ case 11025:
+ bclk = WM8753_BCLK_DIV_16;
+ pll_out = 11289600;
+ break;
+ case 22050:
+ bclk = WM8753_BCLK_DIV_8;
+ pll_out = 11289600;
+ break;
+ case 44100:
+ bclk = WM8753_BCLK_DIV_4;
+ pll_out = 11289600;
+ break;
+ case 88200:
+ bclk = WM8753_BCLK_DIV_2;
+ pll_out = 11289600;
+ break;
+ }
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* set MCLK division for sample rate */
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
+ S3C2410_IISMOD_32FS);
+ if (ret < 0)
+ return ret;
+
+ /* set codec BCLK division for sample rate */
+ ret = snd_soc_dai_set_clkdiv(codec_dai,
+ WM8753_BCLKDIV, bclk);
+ if (ret < 0)
+ return ret;
+
+ /* set prescaler division for sample rate */
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+ S3C24XX_PRESCALE(4, 4));
+ if (ret < 0)
+ return ret;
+
+ /* codec PLL input is PCLK/4 */
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+ iis_clkrate / 4, pll_out);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int neo1973_gta02_hifi_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+ /* disable the PLL */
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
+}
+
+/*
+ * Neo1973 WM8753 HiFi DAI opserations.
+ */
+static struct snd_soc_ops neo1973_gta02_hifi_ops = {
+ .hw_params = neo1973_gta02_hifi_hw_params,
+ .hw_free = neo1973_gta02_hifi_hw_free,
+};
+
+static int neo1973_gta02_voice_hw_params(
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ unsigned int pcmdiv = 0;
+ int ret = 0;
+ unsigned long iis_clkrate;
+
+ iis_clkrate = s3c24xx_i2s_get_clockrate();
+
+ if (params_rate(params) != 8000)
+ return -EINVAL;
+ if (params_channels(params) != 1)
+ return -EINVAL;
+
+ pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
+
+ /* todo: gg check mode (DSP_B) against CSR datasheet */
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK,
+ 12288000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ /* set codec PCM division for sample rate */
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV,
+ pcmdiv);
+ if (ret < 0)
+ return ret;
+
+ /* configue and enable PLL for 12.288MHz output */
+ ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
+ iis_clkrate / 4, 12288000);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int neo1973_gta02_voice_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+ /* disable the PLL */
+ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
+}
+
+static struct snd_soc_ops neo1973_gta02_voice_ops = {
+ .hw_params = neo1973_gta02_voice_hw_params,
+ .hw_free = neo1973_gta02_voice_hw_free,
+};
+
+#define LM4853_AMP 1
+#define LM4853_SPK 2
+
+static u8 lm4853_state;
+
+/* This has no effect, it exists only to maintain compatibility with
+ * existing ALSA state files.
+ */
+static int lm4853_set_state(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int val = ucontrol->value.integer.value[0];
+
+ if (val)
+ lm4853_state |= LM4853_AMP;
+ else
+ lm4853_state &= ~LM4853_AMP;
+
+ return 0;
+}
+
+static int lm4853_get_state(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = lm4853_state & LM4853_AMP;
+
+ return 0;
+}
+
+static int lm4853_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int val = ucontrol->value.integer.value[0];
+
+ if (val) {
+ lm4853_state |= LM4853_SPK;
+ gpio_set_value(GTA02_GPIO_HP_IN, 0);
+ } else {
+ lm4853_state &= ~LM4853_SPK;
+ gpio_set_value(GTA02_GPIO_HP_IN, 1);
+ }
+
+ return 0;
+}
+
+static int lm4853_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = (lm4853_state & LM4853_SPK) >> 1;
+
+ return 0;
+}
+
+static int lm4853_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k,
+ int event)
+{
+ gpio_set_value(GTA02_GPIO_AMP_SHUT, SND_SOC_DAPM_EVENT_OFF(value));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Stereo Out", lm4853_event),
+ SND_SOC_DAPM_LINE("GSM Line Out", NULL),
+ SND_SOC_DAPM_LINE("GSM Line In", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Handset Mic", NULL),
+ SND_SOC_DAPM_SPK("Handset Spk", NULL),
+};
+
+
+/* example machine audio_mapnections */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* Connections to the lm4853 amp */
+ {"Stereo Out", NULL, "LOUT1"},
+ {"Stereo Out", NULL, "ROUT1"},
+
+ /* Connections to the GSM Module */
+ {"GSM Line Out", NULL, "MONO1"},
+ {"GSM Line Out", NULL, "MONO2"},
+ {"RXP", NULL, "GSM Line In"},
+ {"RXN", NULL, "GSM Line In"},
+
+ /* Connections to Headset */
+ {"MIC1", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Headset Mic"},
+
+ /* Call Mic */
+ {"MIC2", NULL, "Mic Bias"},
+ {"MIC2N", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Handset Mic"},
+
+ /* Call Speaker */
+ {"Handset Spk", NULL, "LOUT2"},
+ {"Handset Spk", NULL, "ROUT2"},
+
+ /* Connect the ALC pins */
+ {"ACIN", NULL, "ACOP"},
+};
+
+static const struct snd_kcontrol_new wm8753_neo1973_gta02_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Stereo Out"),
+ SOC_DAPM_PIN_SWITCH("GSM Line Out"),
+ SOC_DAPM_PIN_SWITCH("GSM Line In"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Handset Mic"),
+ SOC_DAPM_PIN_SWITCH("Handset Spk"),
+
+ /* This has no effect, it exists only to maintain compatibility with
+ * existing ALSA state files.
+ */
+ SOC_SINGLE_EXT("Amp State Switch", 6, 0, 1, 0,
+ lm4853_get_state,
+ lm4853_set_state),
+ SOC_SINGLE_EXT("Amp Spk Switch", 7, 0, 1, 0,
+ lm4853_get_spk,
+ lm4853_set_spk),
+};
+
+/*
+ * This is an example machine initialisation for a wm8753 connected to a
+ * neo1973 GTA02.
+ */
+static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec)
+{
+ int err;
+
+ /* set up NC codec pins */
+ snd_soc_dapm_nc_pin(codec, "OUT3");
+ snd_soc_dapm_nc_pin(codec, "OUT4");
+ snd_soc_dapm_nc_pin(codec, "LINE1");
+ snd_soc_dapm_nc_pin(codec, "LINE2");
+
+ /* Add neo1973 gta02 specific widgets */
+ snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets,
+ ARRAY_SIZE(wm8753_dapm_widgets));
+
+ /* add neo1973 gta02 specific controls */
+ err = snd_soc_add_controls(codec, wm8753_neo1973_gta02_controls,
+ ARRAY_SIZE(wm8753_neo1973_gta02_controls));
+
+ if (err < 0)
+ return err;
+
+ /* set up neo1973 gta02 specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ /* set endpoints to default off mode */
+ snd_soc_dapm_disable_pin(codec, "Stereo Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+ snd_soc_dapm_disable_pin(codec, "GSM Line In");
+ snd_soc_dapm_disable_pin(codec, "Headset Mic");
+ snd_soc_dapm_disable_pin(codec, "Handset Mic");
+ snd_soc_dapm_disable_pin(codec, "Handset Spk");
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+/*
+ * BT Codec DAI
+ */
+static struct snd_soc_dai bt_dai = {
+ .name = "Bluetooth",
+ .id = 0,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+};
+
+static struct snd_soc_dai_link neo1973_gta02_dai[] = {
+{ /* Hifi Playback - for similatious use with voice below */
+ .name = "WM8753",
+ .stream_name = "WM8753 HiFi",
+ .cpu_dai = &s3c24xx_i2s_dai,
+ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
+ .init = neo1973_gta02_wm8753_init,
+ .ops = &neo1973_gta02_hifi_ops,
+},
+{ /* Voice via BT */
+ .name = "Bluetooth",
+ .stream_name = "Voice",
+ .cpu_dai = &bt_dai,
+ .codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
+ .ops = &neo1973_gta02_voice_ops,
+},
+};
+
+static struct snd_soc_card neo1973_gta02 = {
+ .name = "neo1973-gta02",
+ .platform = &s3c24xx_soc_platform,
+ .dai_link = neo1973_gta02_dai,
+ .num_links = ARRAY_SIZE(neo1973_gta02_dai),
+};
+
+static struct snd_soc_device neo1973_gta02_snd_devdata = {
+ .card = &neo1973_gta02,
+ .codec_dev = &soc_codec_dev_wm8753,
+};
+
+static struct platform_device *neo1973_gta02_snd_device;
+
+static int __init neo1973_gta02_init(void)
+{
+ int ret;
+
+ if (!machine_is_neo1973_gta02()) {
+ printk(KERN_INFO
+ "Only GTA02 is supported by this ASoC driver\n");
+ return -ENODEV;
+ }
+
+ /* register bluetooth DAI here */
+ ret = snd_soc_register_dai(&bt_dai);
+ if (ret)
+ return ret;
+
+ neo1973_gta02_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!neo1973_gta02_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(neo1973_gta02_snd_device,
+ &neo1973_gta02_snd_devdata);
+ neo1973_gta02_snd_devdata.dev = &neo1973_gta02_snd_device->dev;
+ ret = platform_device_add(neo1973_gta02_snd_device);
+
+ if (ret) {
+ platform_device_put(neo1973_gta02_snd_device);
+ return ret;
+ }
+
+ /* Initialise GPIOs used by amp */
+ ret = gpio_request(GTA02_GPIO_HP_IN, "GTA02_HP_IN");
+ if (ret) {
+ pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_HP_IN);
+ goto err_unregister_device;
+ }
+
+ ret = gpio_direction_output(GTA02_GPIO_AMP_HP_IN, 1);
+ if (ret) {
+ pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_HP_IN);
+ goto err_free_gpio_hp_in;
+ }
+
+ ret = gpio_request(GTA02_GPIO_AMP_SHUT, "GTA02_AMP_SHUT");
+ if (ret) {
+ pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_AMP_SHUT);
+ goto err_free_gpio_hp_in;
+ }
+
+ ret = gpio_direction_output(GTA02_GPIO_AMP_SHUT, 1);
+ if (ret) {
+ pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_AMP_SHUT);
+ goto err_free_gpio_amp_shut;
+ }
+
+ return 0;
+
+err_free_gpio_amp_shut:
+ gpio_free(GTA02_GPIO_AMP_SHUT);
+err_free_gpio_hp_in:
+ gpio_free(GTA02_GPIO_HP_IN);
+err_unregister_device:
+ platform_device_unregister(neo1973_gta02_snd_device);
+ return ret;
+}
+module_init(neo1973_gta02_init);
+
+static void __exit neo1973_gta02_exit(void)
+{
+ snd_soc_unregister_dai(&bt_dai);
+ platform_device_unregister(neo1973_gta02_snd_device);
+ gpio_free(GTA02_GPIO_HP_IN);
+ gpio_free(GTA02_GPIO_AMP_SHUT);
+}
+module_exit(neo1973_gta02_exit);
+
+/* Module information */
+MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org");
+MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 GTA02");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c
index 1a283170ca9..aa7af0b8d42 100644
--- a/sound/soc/s3c24xx/s3c-i2s-v2.c
+++ b/sound/soc/s3c24xx/s3c-i2s-v2.c
@@ -36,6 +36,7 @@
#include <mach/dma.h>
#include "s3c-i2s-v2.h"
+#include "s3c24xx-pcm.h"
#undef S3C_IIS_V2_SUPPORTED
@@ -357,19 +358,19 @@ static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
#endif
#ifdef CONFIG_PLAT_S3C64XX
- iismod &= ~0x606;
+ iismod &= ~(S3C64XX_IISMOD_BLC_MASK | S3C2412_IISMOD_BCLK_MASK);
/* Sample size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
/* 8 bit sample, 16fs BCLK */
- iismod |= 0x2004;
+ iismod |= (S3C64XX_IISMOD_BLC_8BIT | S3C2412_IISMOD_BCLK_16FS);
break;
case SNDRV_PCM_FORMAT_S16_LE:
/* 16 bit sample, 32fs BCLK */
break;
case SNDRV_PCM_FORMAT_S24_LE:
/* 24 bit sample, 48fs BCLK */
- iismod |= 0x4002;
+ iismod |= (S3C64XX_IISMOD_BLC_24BIT | S3C2412_IISMOD_BCLK_48FS);
break;
}
#endif
@@ -387,6 +388,8 @@ static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
unsigned long irqs;
int ret = 0;
+ int channel = ((struct s3c24xx_pcm_dma_params *)
+ rtd->dai->cpu_dai->dma_data)->channel;
pr_debug("Entered %s\n", __func__);
@@ -416,6 +419,14 @@ static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
s3c2412_snd_txctrl(i2s, 1);
local_irq_restore(irqs);
+
+ /*
+ * Load the next buffer to DMA to meet the reqirement
+ * of the auto reload mechanism of S3C24XX.
+ * This call won't bother S3C64XX.
+ */
+ s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
+
break;
case SNDRV_PCM_TRIGGER_STOP:
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
index 3f03d5ddfac..fc1beb0930b 100644
--- a/sound/soc/s3c24xx/s3c2443-ac97.c
+++ b/sound/soc/s3c24xx/s3c2443-ac97.c
@@ -47,7 +47,7 @@ static struct s3c24xx_ac97_info s3c24xx_ac97;
static DECLARE_COMPLETION(ac97_completion);
static u32 codec_ready;
-static DECLARE_MUTEX(ac97_mutex);
+static DEFINE_MUTEX(ac97_mutex);
static unsigned short s3c2443_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
@@ -56,7 +56,7 @@ static unsigned short s3c2443_ac97_read(struct snd_ac97 *ac97,
u32 ac_codec_cmd;
u32 stat, addr, data;
- down(&ac97_mutex);
+ mutex_lock(&ac97_mutex);
codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
@@ -79,7 +79,7 @@ static unsigned short s3c2443_ac97_read(struct snd_ac97 *ac97,
printk(KERN_ERR "s3c24xx-ac97: req addr = %02x,"
" rep addr = %02x\n", reg, addr);
- up(&ac97_mutex);
+ mutex_unlock(&ac97_mutex);
return (unsigned short)data;
}
@@ -90,7 +90,7 @@ static void s3c2443_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
u32 ac_glbctrl;
u32 ac_codec_cmd;
- down(&ac97_mutex);
+ mutex_lock(&ac97_mutex);
codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
@@ -109,7 +109,7 @@ static void s3c2443_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
- up(&ac97_mutex);
+ mutex_unlock(&ac97_mutex);
}
@@ -290,6 +290,9 @@ static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
u32 ac_glbctrl;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int channel = ((struct s3c24xx_pcm_dma_params *)
+ rtd->dai->cpu_dai->dma_data)->channel;
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
switch (cmd) {
@@ -312,6 +315,8 @@ static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
}
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+ s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
+
return 0;
}
@@ -334,6 +339,9 @@ static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
u32 ac_glbctrl;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int channel = ((struct s3c24xx_pcm_dma_params *)
+ rtd->dai->cpu_dai->dma_data)->channel;
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
switch (cmd) {
@@ -349,6 +357,8 @@ static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
}
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+ s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
+
return 0;
}
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index 556e35f0ab7..40e2c4790f0 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -279,6 +279,9 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int channel = ((struct s3c24xx_pcm_dma_params *)
+ rtd->dai->cpu_dai->dma_data)->channel;
pr_debug("Entered %s\n", __func__);
@@ -296,6 +299,8 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
s3c24xx_snd_rxctrl(1);
else
s3c24xx_snd_txctrl(1);
+
+ s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index eecfa5eba06..5cbbdc80fde 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -255,7 +255,6 @@ static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
prtd->state |= ST_RUNNING;
s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START);
- s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STARTED);
break;
case SNDRV_PCM_TRIGGER_STOP:
@@ -318,6 +317,7 @@ static int s3c24xx_pcm_open(struct snd_pcm_substream *substream)
pr_debug("Entered %s\n", __func__);
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware);
prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL);
diff --git a/sound/soc/s3c24xx/s3c24xx_simtec.c b/sound/soc/s3c24xx/s3c24xx_simtec.c
new file mode 100644
index 00000000000..1966e0d5652
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c24xx_simtec.c
@@ -0,0 +1,394 @@
+/* sound/soc/s3c24xx/s3c24xx_simtec.c
+ *
+ * Copyright 2009 Simtec Electronics
+ *
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <plat/audio-simtec.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+#include "s3c24xx_simtec.h"
+
+static struct s3c24xx_audio_simtec_pdata *pdata;
+static struct clk *xtal_clk;
+
+static int spk_gain;
+static int spk_unmute;
+
+/**
+ * speaker_gain_get - read the speaker gain setting.
+ * @kcontrol: The control for the speaker gain.
+ * @ucontrol: The value that needs to be updated.
+ *
+ * Read the value for the AMP gain control.
+ */
+static int speaker_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = spk_gain;
+ return 0;
+}
+
+/**
+ * speaker_gain_set - set the value of the speaker amp gain
+ * @value: The value to write.
+ */
+static void speaker_gain_set(int value)
+{
+ gpio_set_value_cansleep(pdata->amp_gain[0], value & 1);
+ gpio_set_value_cansleep(pdata->amp_gain[1], value >> 1);
+}
+
+/**
+ * speaker_gain_put - set the speaker gain setting.
+ * @kcontrol: The control for the speaker gain.
+ * @ucontrol: The value that needs to be set.
+ *
+ * Set the value of the speaker gain from the specified
+ * @ucontrol setting.
+ *
+ * Note, if the speaker amp is muted, then we do not set a gain value
+ * as at-least one of the ICs that is fitted will try and power up even
+ * if the main control is set to off.
+ */
+static int speaker_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int value = ucontrol->value.integer.value[0];
+
+ spk_gain = value;
+
+ if (!spk_unmute)
+ speaker_gain_set(value);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new amp_gain_controls[] = {
+ SOC_SINGLE_EXT("Speaker Gain", 0, 0, 3, 0,
+ speaker_gain_get, speaker_gain_put),
+};
+
+/**
+ * spk_unmute_state - set the unmute state of the speaker
+ * @to: zero to unmute, non-zero to ununmute.
+ */
+static void spk_unmute_state(int to)
+{
+ pr_debug("%s: to=%d\n", __func__, to);
+
+ spk_unmute = to;
+ gpio_set_value(pdata->amp_gpio, to);
+
+ /* if we're umuting, also re-set the gain */
+ if (to && pdata->amp_gain[0] > 0)
+ speaker_gain_set(spk_gain);
+}
+
+/**
+ * speaker_unmute_get - read the speaker unmute setting.
+ * @kcontrol: The control for the speaker gain.
+ * @ucontrol: The value that needs to be updated.
+ *
+ * Read the value for the AMP gain control.
+ */
+static int speaker_unmute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = spk_unmute;
+ return 0;
+}
+
+/**
+ * speaker_unmute_put - set the speaker unmute setting.
+ * @kcontrol: The control for the speaker gain.
+ * @ucontrol: The value that needs to be set.
+ *
+ * Set the value of the speaker gain from the specified
+ * @ucontrol setting.
+ */
+static int speaker_unmute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ spk_unmute_state(ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+/* This is added as a manual control as the speaker amps create clicks
+ * when their power state is changed, which are far more noticeable than
+ * anything produced by the CODEC itself.
+ */
+static const struct snd_kcontrol_new amp_unmute_controls[] = {
+ SOC_SINGLE_EXT("Speaker Switch", 0, 0, 1, 0,
+ speaker_unmute_get, speaker_unmute_put),
+};
+
+void simtec_audio_init(struct snd_soc_codec *codec)
+{
+ if (pdata->amp_gpio > 0) {
+ pr_debug("%s: adding amp routes\n", __func__);
+
+ snd_soc_add_controls(codec, amp_unmute_controls,
+ ARRAY_SIZE(amp_unmute_controls));
+ }
+
+ if (pdata->amp_gain[0] > 0) {
+ pr_debug("%s: adding amp controls\n", __func__);
+ snd_soc_add_controls(codec, amp_gain_controls,
+ ARRAY_SIZE(amp_gain_controls));
+ }
+}
+EXPORT_SYMBOL_GPL(simtec_audio_init);
+
+#define CODEC_CLOCK 12000000
+
+/**
+ * simtec_hw_params - update hardware parameters
+ * @substream: The audio substream instance.
+ * @params: The parameters requested.
+ *
+ * Update the codec data routing and configuration settings
+ * from the supplied data.
+ */
+static int simtec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+ /* Set the CODEC as the bus clock master, I2S */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret) {
+ pr_err("%s: failed set cpu dai format\n", __func__);
+ return ret;
+ }
+
+ /* Set the CODEC as the bus clock master */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret) {
+ pr_err("%s: failed set codec dai format\n", __func__);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+ CODEC_CLOCK, SND_SOC_CLOCK_IN);
+ if (ret) {
+ pr_err( "%s: failed setting codec sysclk\n", __func__);
+ return ret;
+ }
+
+ if (pdata->use_mpllin) {
+ ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_MPLL,
+ 0, SND_SOC_CLOCK_OUT);
+
+ if (ret) {
+ pr_err("%s: failed to set MPLLin as clksrc\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ if (pdata->output_cdclk) {
+ int cdclk_scale;
+
+ cdclk_scale = clk_get_rate(xtal_clk) / CODEC_CLOCK;
+ cdclk_scale--;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+ cdclk_scale);
+ }
+
+ return 0;
+}
+
+static int simtec_call_startup(struct s3c24xx_audio_simtec_pdata *pd)
+{
+ /* call any board supplied startup code, this currently only
+ * covers the bast/vr1000 which have a CPLD in the way of the
+ * LRCLK */
+ if (pd->startup)
+ pd->startup();
+
+ return 0;
+}
+
+static struct snd_soc_ops simtec_snd_ops = {
+ .hw_params = simtec_hw_params,
+};
+
+/**
+ * attach_gpio_amp - get and configure the necessary gpios
+ * @dev: The device we're probing.
+ * @pd: The platform data supplied by the board.
+ *
+ * If there is a GPIO based amplifier attached to the board, claim
+ * the necessary GPIO lines for it, and set default values.
+ */
+static int attach_gpio_amp(struct device *dev,
+ struct s3c24xx_audio_simtec_pdata *pd)
+{
+ int ret;
+
+ /* attach gpio amp gain (if any) */
+ if (pdata->amp_gain[0] > 0) {
+ ret = gpio_request(pd->amp_gain[0], "gpio-amp-gain0");
+ if (ret) {
+ dev_err(dev, "cannot get amp gpio gain0\n");
+ return ret;
+ }
+
+ ret = gpio_request(pd->amp_gain[1], "gpio-amp-gain1");
+ if (ret) {
+ dev_err(dev, "cannot get amp gpio gain1\n");
+ gpio_free(pdata->amp_gain[0]);
+ return ret;
+ }
+
+ gpio_direction_output(pd->amp_gain[0], 0);
+ gpio_direction_output(pd->amp_gain[1], 0);
+ }
+
+ /* note, curently we assume GPA0 isn't valid amp */
+ if (pdata->amp_gpio > 0) {
+ ret = gpio_request(pd->amp_gpio, "gpio-amp");
+ if (ret) {
+ dev_err(dev, "cannot get amp gpio %d (%d)\n",
+ pd->amp_gpio, ret);
+ goto err_amp;
+ }
+
+ /* set the amp off at startup */
+ spk_unmute_state(0);
+ }
+
+ return 0;
+
+err_amp:
+ if (pd->amp_gain[0] > 0) {
+ gpio_free(pd->amp_gain[0]);
+ gpio_free(pd->amp_gain[1]);
+ }
+
+ return ret;
+}
+
+static void detach_gpio_amp(struct s3c24xx_audio_simtec_pdata *pd)
+{
+ if (pd->amp_gain[0] > 0) {
+ gpio_free(pd->amp_gain[0]);
+ gpio_free(pd->amp_gain[1]);
+ }
+
+ if (pd->amp_gpio > 0)
+ gpio_free(pd->amp_gpio);
+}
+
+#ifdef CONFIG_PM
+int simtec_audio_resume(struct device *dev)
+{
+ simtec_call_startup(pdata);
+ return 0;
+}
+
+struct dev_pm_ops simtec_audio_pmops = {
+ .resume = simtec_audio_resume,
+};
+EXPORT_SYMBOL_GPL(simtec_audio_pmops);
+#endif
+
+int __devinit simtec_audio_core_probe(struct platform_device *pdev,
+ struct snd_soc_device *socdev)
+{
+ struct platform_device *snd_dev;
+ int ret;
+
+ socdev->card->dai_link->ops = &simtec_snd_ops;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data supplied\n");
+ return -EINVAL;
+ }
+
+ simtec_call_startup(pdata);
+
+ xtal_clk = clk_get(&pdev->dev, "xtal");
+ if (IS_ERR(xtal_clk)) {
+ dev_err(&pdev->dev, "could not get clkout0\n");
+ return -EINVAL;
+ }
+
+ dev_info(&pdev->dev, "xtal rate is %ld\n", clk_get_rate(xtal_clk));
+
+ ret = attach_gpio_amp(&pdev->dev, pdata);
+ if (ret)
+ goto err_clk;
+
+ snd_dev = platform_device_alloc("soc-audio", -1);
+ if (!snd_dev) {
+ dev_err(&pdev->dev, "failed to alloc soc-audio devicec\n");
+ ret = -ENOMEM;
+ goto err_gpio;
+ }
+
+ platform_set_drvdata(snd_dev, socdev);
+ socdev->dev = &snd_dev->dev;
+
+ ret = platform_device_add(snd_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add soc-audio dev\n");
+ goto err_pdev;
+ }
+
+ platform_set_drvdata(pdev, snd_dev);
+ return 0;
+
+err_pdev:
+ platform_device_put(snd_dev);
+
+err_gpio:
+ detach_gpio_amp(pdata);
+
+err_clk:
+ clk_put(xtal_clk);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(simtec_audio_core_probe);
+
+int __devexit simtec_audio_remove(struct platform_device *pdev)
+{
+ struct platform_device *snd_dev = platform_get_drvdata(pdev);
+
+ platform_device_unregister(snd_dev);
+
+ detach_gpio_amp(pdata);
+ clk_put(xtal_clk);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(simtec_audio_remove);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("ALSA SoC Simtec Audio common support");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c24xx_simtec.h b/sound/soc/s3c24xx/s3c24xx_simtec.h
new file mode 100644
index 00000000000..2714203af16
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c24xx_simtec.h
@@ -0,0 +1,22 @@
+/* sound/soc/s3c24xx/s3c24xx_simtec.h
+ *
+ * Copyright 2009 Simtec Electronics
+ *
+ * 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.
+*/
+
+extern void simtec_audio_init(struct snd_soc_codec *codec);
+
+extern int simtec_audio_core_probe(struct platform_device *pdev,
+ struct snd_soc_device *socdev);
+
+extern int simtec_audio_remove(struct platform_device *pdev);
+
+#ifdef CONFIG_PM
+extern struct dev_pm_ops simtec_audio_pmops;
+#define simtec_audio_pm &simtec_audio_pmops
+#else
+#define simtec_audio_pm NULL
+#endif
diff --git a/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c b/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c
new file mode 100644
index 00000000000..8346bd96eaf
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c24xx_simtec_hermes.c
@@ -0,0 +1,153 @@
+/* sound/soc/s3c24xx/s3c24xx_simtec_hermes.c
+ *
+ * Copyright 2009 Simtec Electronics
+ *
+ * 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/module.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <plat/audio-simtec.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+#include "s3c24xx_simtec.h"
+
+#include "../codecs/tlv320aic3x.h"
+
+static const struct snd_soc_dapm_widget dapm_widgets[] = {
+ SND_SOC_DAPM_LINE("GSM Out", NULL),
+ SND_SOC_DAPM_LINE("GSM In", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_LINE("ZV", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route base_map[] = {
+ /* Headphone connected to HP{L,R}OUT and HP{L,R}COM */
+
+ { "Headphone Jack", NULL, "HPLOUT" },
+ { "Headphone Jack", NULL, "HPLCOM" },
+ { "Headphone Jack", NULL, "HPROUT" },
+ { "Headphone Jack", NULL, "HPRCOM" },
+
+ /* ZV connected to Line1 */
+
+ { "LINE1L", NULL, "ZV" },
+ { "LINE1R", NULL, "ZV" },
+
+ /* Line In connected to Line2 */
+
+ { "LINE2L", NULL, "Line In" },
+ { "LINE2R", NULL, "Line In" },
+
+ /* Microphone connected to MIC3R and MIC_BIAS */
+
+ { "MIC3L", NULL, "Mic Jack" },
+
+ /* GSM connected to MONO_LOUT and MIC3L (in) */
+
+ { "GSM Out", NULL, "MONO_LOUT" },
+ { "MIC3L", NULL, "GSM In" },
+
+ /* Speaker is connected to LINEOUT{LN,LP,RN,RP}, however we are
+ * not using the DAPM to power it up and down as there it makes
+ * a click when powering up. */
+};
+
+/**
+ * simtec_hermes_init - initialise and add controls
+ * @codec; The codec instance to attach to.
+ *
+ * Attach our controls and configure the necessary codec
+ * mappings for our sound card instance.
+*/
+static int simtec_hermes_init(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, dapm_widgets,
+ ARRAY_SIZE(dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, base_map, ARRAY_SIZE(base_map));
+
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_enable_pin(codec, "Line In");
+ snd_soc_dapm_enable_pin(codec, "Line Out");
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
+
+ simtec_audio_init(codec);
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct aic3x_setup_data codec_setup = {
+};
+
+static struct snd_soc_dai_link simtec_dai_aic33 = {
+ .name = "tlv320aic33",
+ .stream_name = "TLV320AIC33",
+ .cpu_dai = &s3c24xx_i2s_dai,
+ .codec_dai = &aic3x_dai,
+ .init = simtec_hermes_init,
+};
+
+/* simtec audio machine driver */
+static struct snd_soc_card snd_soc_machine_simtec_aic33 = {
+ .name = "Simtec-Hermes",
+ .platform = &s3c24xx_soc_platform,
+ .dai_link = &simtec_dai_aic33,
+ .num_links = 1,
+};
+
+/* simtec audio subsystem */
+static struct snd_soc_device simtec_snd_devdata_aic33 = {
+ .card = &snd_soc_machine_simtec_aic33,
+ .codec_dev = &soc_codec_dev_aic3x,
+ .codec_data = &codec_setup,
+};
+
+static int __devinit simtec_audio_hermes_probe(struct platform_device *pd)
+{
+ dev_info(&pd->dev, "probing....\n");
+ return simtec_audio_core_probe(pd, &simtec_snd_devdata_aic33);
+}
+
+static struct platform_driver simtec_audio_hermes_platdrv = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "s3c24xx-simtec-hermes-snd",
+ .pm = simtec_audio_pm,
+ },
+ .probe = simtec_audio_hermes_probe,
+ .remove = __devexit_p(simtec_audio_remove),
+};
+
+MODULE_ALIAS("platform:s3c24xx-simtec-hermes-snd");
+
+static int __init simtec_hermes_modinit(void)
+{
+ return platform_driver_register(&simtec_audio_hermes_platdrv);
+}
+
+static void __exit simtec_hermes_modexit(void)
+{
+ platform_driver_unregister(&simtec_audio_hermes_platdrv);
+}
+
+module_init(simtec_hermes_modinit);
+module_exit(simtec_hermes_modexit);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("ALSA SoC Simtec Audio support");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c b/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
new file mode 100644
index 00000000000..25797e09617
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
@@ -0,0 +1,137 @@
+/* sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
+ *
+ * Copyright 2009 Simtec Electronics
+ *
+ * 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/module.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <plat/audio-simtec.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+#include "s3c24xx_simtec.h"
+
+#include "../codecs/tlv320aic23.h"
+
+/* supported machines:
+ *
+ * Machine Connections AMP
+ * ------- ----------- ---
+ * BAST MIC, HPOUT, LOUT, LIN TPA2001D1 (HPOUTL,R) (gain hardwired)
+ * VR1000 HPOUT, LIN None
+ * VR2000 LIN, LOUT, MIC, HP LM4871 (HPOUTL,R)
+ * DePicture LIN, LOUT, MIC, HP LM4871 (HPOUTL,R)
+ * Anubis LIN, LOUT, MIC, HP TPA2001D1 (HPOUTL,R)
+ */
+
+static const struct snd_soc_dapm_widget dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route base_map[] = {
+ { "Headphone Jack", NULL, "LHPOUT"},
+ { "Headphone Jack", NULL, "RHPOUT"},
+
+ { "Line Out", NULL, "LOUT" },
+ { "Line Out", NULL, "ROUT" },
+
+ { "LLINEIN", NULL, "Line In"},
+ { "RLINEIN", NULL, "Line In"},
+
+ { "MICIN", NULL, "Mic Jack"},
+};
+
+/**
+ * simtec_tlv320aic23_init - initialise and add controls
+ * @codec; The codec instance to attach to.
+ *
+ * Attach our controls and configure the necessary codec
+ * mappings for our sound card instance.
+*/
+static int simtec_tlv320aic23_init(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, dapm_widgets,
+ ARRAY_SIZE(dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, base_map, ARRAY_SIZE(base_map));
+
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_enable_pin(codec, "Line In");
+ snd_soc_dapm_enable_pin(codec, "Line Out");
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
+
+ simtec_audio_init(codec);
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link simtec_dai_aic23 = {
+ .name = "tlv320aic23",
+ .stream_name = "TLV320AIC23",
+ .cpu_dai = &s3c24xx_i2s_dai,
+ .codec_dai = &tlv320aic23_dai,
+ .init = simtec_tlv320aic23_init,
+};
+
+/* simtec audio machine driver */
+static struct snd_soc_card snd_soc_machine_simtec_aic23 = {
+ .name = "Simtec",
+ .platform = &s3c24xx_soc_platform,
+ .dai_link = &simtec_dai_aic23,
+ .num_links = 1,
+};
+
+/* simtec audio subsystem */
+static struct snd_soc_device simtec_snd_devdata_aic23 = {
+ .card = &snd_soc_machine_simtec_aic23,
+ .codec_dev = &soc_codec_dev_tlv320aic23,
+};
+
+static int __devinit simtec_audio_tlv320aic23_probe(struct platform_device *pd)
+{
+ return simtec_audio_core_probe(pd, &simtec_snd_devdata_aic23);
+}
+
+static struct platform_driver simtec_audio_tlv320aic23_platdrv = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "s3c24xx-simtec-tlv320aic23",
+ .pm = simtec_audio_pm,
+ },
+ .probe = simtec_audio_tlv320aic23_probe,
+ .remove = __devexit_p(simtec_audio_remove),
+};
+
+MODULE_ALIAS("platform:s3c24xx-simtec-tlv320aic23");
+
+static int __init simtec_tlv320aic23_modinit(void)
+{
+ return platform_driver_register(&simtec_audio_tlv320aic23_platdrv);
+}
+
+static void __exit simtec_tlv320aic23_modexit(void)
+{
+ platform_driver_unregister(&simtec_audio_tlv320aic23_platdrv);
+}
+
+module_init(simtec_tlv320aic23_modinit);
+module_exit(simtec_tlv320aic23_modexit);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("ALSA SoC Simtec Audio support");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s6000/s6105-ipcam.c b/sound/soc/s6000/s6105-ipcam.c
index b5f95f9781c..c1b40ac22c0 100644
--- a/sound/soc/s6000/s6105-ipcam.c
+++ b/sound/soc/s6000/s6105-ipcam.c
@@ -14,6 +14,7 @@
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
@@ -189,8 +190,6 @@ static struct snd_soc_card snd_soc_card_s6105 = {
/* s6105 audio private data */
static struct aic3x_setup_data s6105_aic3x_setup = {
- .i2c_bus = 0,
- .i2c_address = 0x18,
};
/* s6105 audio subsystem */
@@ -211,10 +210,19 @@ static struct s6000_snd_platform_data __initdata s6105_snd_data = {
static struct platform_device *s6105_snd_device;
+/* temporary i2c device creation until this can be moved into the machine
+ * support file.
+*/
+static struct i2c_board_info i2c_device[] = {
+ { I2C_BOARD_INFO("tlv320aic33", 0x18), }
+};
+
static int __init s6105_init(void)
{
int ret;
+ i2c_register_board_info(0, i2c_device, ARRAY_SIZE(i2c_device));
+
s6105_snd_device = platform_device_alloc("soc-audio", -1);
if (!s6105_snd_device)
return -ENOMEM;
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 54bd604012a..9154b4363db 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -20,7 +20,12 @@ config SND_SOC_SH4_HAC
config SND_SOC_SH4_SSI
tristate
-
+config SND_SOC_SH4_FSI
+ tristate "SH4 FSI support"
+ depends on CPU_SUBTYPE_SH7724
+ select SH_DMA
+ help
+ This option enables FSI sound support
##
## Boards
@@ -35,4 +40,12 @@ config SND_SH7760_AC97
This option enables generic sound support for the first
AC97 unit of the SH7760.
+config SND_FSI_AK4642
+ bool "FSI-AK4642 sound support"
+ depends on SND_SOC_SH4_FSI
+ select SND_SOC_AK4642
+ help
+ This option enables generic sound support for the
+ FSI - AK4642 unit
+
endmenu
diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile
index a8e8ab81cc6..a6997872f24 100644
--- a/sound/soc/sh/Makefile
+++ b/sound/soc/sh/Makefile
@@ -5,10 +5,14 @@ obj-$(CONFIG_SND_SOC_PCM_SH7760) += snd-soc-dma-sh7760.o
## audio units found on some SH-4
snd-soc-hac-objs := hac.o
snd-soc-ssi-objs := ssi.o
+snd-soc-fsi-objs := fsi.o
obj-$(CONFIG_SND_SOC_SH4_HAC) += snd-soc-hac.o
obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o
+obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o
## boards
snd-soc-sh7760-ac97-objs := sh7760-ac97.o
+snd-soc-fsi-ak4642-objs := fsi-ak4642.o
obj-$(CONFIG_SND_SH7760_AC97) += snd-soc-sh7760-ac97.o
+obj-$(CONFIG_SND_FSI_AK4642) += snd-soc-fsi-ak4642.o
diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c
new file mode 100644
index 00000000000..c7af09729c6
--- /dev/null
+++ b/sound/soc/sh/fsi-ak4642.c
@@ -0,0 +1,107 @@
+/*
+ * FSI-AK464x sound support for ms7724se
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <sound/sh_fsi.h>
+#include <../sound/soc/codecs/ak4642.h>
+
+static struct snd_soc_dai_link fsi_dai_link = {
+ .name = "AK4642",
+ .stream_name = "AK4642",
+ .cpu_dai = &fsi_soc_dai[0], /* fsi */
+ .codec_dai = &ak4642_dai,
+ .ops = NULL,
+};
+
+static struct snd_soc_card fsi_soc_card = {
+ .name = "FSI",
+ .platform = &fsi_soc_platform,
+ .dai_link = &fsi_dai_link,
+ .num_links = 1,
+};
+
+static struct snd_soc_device fsi_snd_devdata = {
+ .card = &fsi_soc_card,
+ .codec_dev = &soc_codec_dev_ak4642,
+};
+
+#define AK4642_BUS 0
+#define AK4642_ADR 0x12
+static int ak4642_add_i2c_device(void)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = AK4642_ADR;
+ strlcpy(info.type, "ak4642", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(AK4642_BUS);
+ if (!adapter) {
+ printk(KERN_DEBUG "can't get i2c adapter\n");
+ return -ENODEV;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ printk(KERN_DEBUG "can't add i2c device\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static struct platform_device *fsi_snd_device;
+
+static int __init fsi_ak4642_init(void)
+{
+ int ret = -ENOMEM;
+
+ ak4642_add_i2c_device();
+
+ fsi_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!fsi_snd_device)
+ goto out;
+
+ platform_set_drvdata(fsi_snd_device,
+ &fsi_snd_devdata);
+ fsi_snd_devdata.dev = &fsi_snd_device->dev;
+ ret = platform_device_add(fsi_snd_device);
+
+ if (ret)
+ platform_device_put(fsi_snd_device);
+
+out:
+ return ret;
+}
+
+static void __exit fsi_ak4642_exit(void)
+{
+ platform_device_unregister(fsi_snd_device);
+}
+
+module_init(fsi_ak4642_init);
+module_exit(fsi_ak4642_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic SH4 FSI-AK4642 sound card");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
new file mode 100644
index 00000000000..44123248b63
--- /dev/null
+++ b/sound/soc/sh/fsi.c
@@ -0,0 +1,1004 @@
+/*
+ * Fifo-attached Serial Interface (FSI) support for SH7724
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ssi.c
+ * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/sh_fsi.h>
+#include <asm/atomic.h>
+#include <asm/dma.h>
+#include <asm/dma-sh.h>
+
+#define DO_FMT 0x0000
+#define DOFF_CTL 0x0004
+#define DOFF_ST 0x0008
+#define DI_FMT 0x000C
+#define DIFF_CTL 0x0010
+#define DIFF_ST 0x0014
+#define CKG1 0x0018
+#define CKG2 0x001C
+#define DIDT 0x0020
+#define DODT 0x0024
+#define MUTE_ST 0x0028
+#define REG_END MUTE_ST
+
+#define INT_ST 0x0200
+#define IEMSK 0x0204
+#define IMSK 0x0208
+#define MUTE 0x020C
+#define CLK_RST 0x0210
+#define SOFT_RST 0x0214
+#define MREG_START INT_ST
+#define MREG_END SOFT_RST
+
+/* DO_FMT */
+/* DI_FMT */
+#define CR_FMT(param) ((param) << 4)
+# define CR_MONO 0x0
+# define CR_MONO_D 0x1
+# define CR_PCM 0x2
+# define CR_I2S 0x3
+# define CR_TDM 0x4
+# define CR_TDM_D 0x5
+
+/* DOFF_CTL */
+/* DIFF_CTL */
+#define IRQ_HALF 0x00100000
+#define FIFO_CLR 0x00000001
+
+/* DOFF_ST */
+#define ERR_OVER 0x00000010
+#define ERR_UNDER 0x00000001
+
+/* CLK_RST */
+#define B_CLK 0x00000010
+#define A_CLK 0x00000001
+
+/* INT_ST */
+#define INT_B_IN (1 << 12)
+#define INT_B_OUT (1 << 8)
+#define INT_A_IN (1 << 4)
+#define INT_A_OUT (1 << 0)
+
+#define FSI_RATES SNDRV_PCM_RATE_8000_96000
+
+#define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
+
+/************************************************************************
+
+
+ struct
+
+
+************************************************************************/
+struct fsi_priv {
+ void __iomem *base;
+ struct snd_pcm_substream *substream;
+
+ int fifo_max;
+ int chan;
+ int dma_chan;
+
+ int byte_offset;
+ int period_len;
+ int buffer_len;
+ int periods;
+};
+
+struct fsi_master {
+ void __iomem *base;
+ int irq;
+ struct clk *clk;
+ struct fsi_priv fsia;
+ struct fsi_priv fsib;
+ struct sh_fsi_platform_info *info;
+};
+
+static struct fsi_master *master;
+
+/************************************************************************
+
+
+ basic read write function
+
+
+************************************************************************/
+static int __fsi_reg_write(u32 reg, u32 data)
+{
+ /* valid data area is 24bit */
+ data &= 0x00ffffff;
+
+ return ctrl_outl(data, reg);
+}
+
+static u32 __fsi_reg_read(u32 reg)
+{
+ return ctrl_inl(reg);
+}
+
+static int __fsi_reg_mask_set(u32 reg, u32 mask, u32 data)
+{
+ u32 val = __fsi_reg_read(reg);
+
+ val &= ~mask;
+ val |= data & mask;
+
+ return __fsi_reg_write(reg, val);
+}
+
+static int fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data)
+{
+ if (reg > REG_END)
+ return -1;
+
+ return __fsi_reg_write((u32)(fsi->base + reg), data);
+}
+
+static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg)
+{
+ if (reg > REG_END)
+ return 0;
+
+ return __fsi_reg_read((u32)(fsi->base + reg));
+}
+
+static int fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data)
+{
+ if (reg > REG_END)
+ return -1;
+
+ return __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data);
+}
+
+static int fsi_master_write(u32 reg, u32 data)
+{
+ if ((reg < MREG_START) ||
+ (reg > MREG_END))
+ return -1;
+
+ return __fsi_reg_write((u32)(master->base + reg), data);
+}
+
+static u32 fsi_master_read(u32 reg)
+{
+ if ((reg < MREG_START) ||
+ (reg > MREG_END))
+ return 0;
+
+ return __fsi_reg_read((u32)(master->base + reg));
+}
+
+static int fsi_master_mask_set(u32 reg, u32 mask, u32 data)
+{
+ if ((reg < MREG_START) ||
+ (reg > MREG_END))
+ return -1;
+
+ return __fsi_reg_mask_set((u32)(master->base + reg), mask, data);
+}
+
+/************************************************************************
+
+
+ basic function
+
+
+************************************************************************/
+static struct fsi_priv *fsi_get(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct fsi_priv *fsi = NULL;
+
+ if (!substream || !master)
+ return NULL;
+
+ rtd = substream->private_data;
+ switch (rtd->dai->cpu_dai->id) {
+ case 0:
+ fsi = &master->fsia;
+ break;
+ case 1:
+ fsi = &master->fsib;
+ break;
+ }
+
+ return fsi;
+}
+
+static int fsi_is_port_a(struct fsi_priv *fsi)
+{
+ /* return
+ * 1 : port a
+ * 0 : port b
+ */
+
+ if (fsi == &master->fsia)
+ return 1;
+
+ return 0;
+}
+
+static u32 fsi_get_info_flags(struct fsi_priv *fsi)
+{
+ int is_porta = fsi_is_port_a(fsi);
+
+ return is_porta ? master->info->porta_flags :
+ master->info->portb_flags;
+}
+
+static int fsi_is_master_mode(struct fsi_priv *fsi, int is_play)
+{
+ u32 mode;
+ u32 flags = fsi_get_info_flags(fsi);
+
+ mode = is_play ? SH_FSI_OUT_SLAVE_MODE : SH_FSI_IN_SLAVE_MODE;
+
+ /* return
+ * 1 : master mode
+ * 0 : slave mode
+ */
+
+ return (mode & flags) != mode;
+}
+
+static u32 fsi_port_ab_io_bit(struct fsi_priv *fsi, int is_play)
+{
+ int is_porta = fsi_is_port_a(fsi);
+ u32 data;
+
+ if (is_porta)
+ data = is_play ? (1 << 0) : (1 << 4);
+ else
+ data = is_play ? (1 << 8) : (1 << 12);
+
+ return data;
+}
+
+static void fsi_stream_push(struct fsi_priv *fsi,
+ struct snd_pcm_substream *substream,
+ u32 buffer_len,
+ u32 period_len)
+{
+ fsi->substream = substream;
+ fsi->buffer_len = buffer_len;
+ fsi->period_len = period_len;
+ fsi->byte_offset = 0;
+ fsi->periods = 0;
+}
+
+static void fsi_stream_pop(struct fsi_priv *fsi)
+{
+ fsi->substream = NULL;
+ fsi->buffer_len = 0;
+ fsi->period_len = 0;
+ fsi->byte_offset = 0;
+ fsi->periods = 0;
+}
+
+static int fsi_get_fifo_residue(struct fsi_priv *fsi, int is_play)
+{
+ u32 status;
+ u32 reg = is_play ? DOFF_ST : DIFF_ST;
+ int residue;
+
+ status = fsi_reg_read(fsi, reg);
+ residue = 0x1ff & (status >> 8);
+ residue *= fsi->chan;
+
+ return residue;
+}
+
+static int fsi_get_residue(struct fsi_priv *fsi, int is_play)
+{
+ int residue;
+ int width;
+ struct snd_pcm_runtime *runtime;
+
+ runtime = fsi->substream->runtime;
+
+ /* get 1 channel data width */
+ width = frames_to_bytes(runtime, 1) / fsi->chan;
+
+ if (2 == width)
+ residue = fsi_get_fifo_residue(fsi, is_play);
+ else
+ residue = get_dma_residue(fsi->dma_chan);
+
+ return residue;
+}
+
+/************************************************************************
+
+
+ basic dma function
+
+
+************************************************************************/
+#define PORTA_DMA 0
+#define PORTB_DMA 1
+
+static int fsi_get_dma_chan(void)
+{
+ if (0 != request_dma(PORTA_DMA, "fsia"))
+ return -EIO;
+
+ if (0 != request_dma(PORTB_DMA, "fsib")) {
+ free_dma(PORTA_DMA);
+ return -EIO;
+ }
+
+ master->fsia.dma_chan = PORTA_DMA;
+ master->fsib.dma_chan = PORTB_DMA;
+
+ return 0;
+}
+
+static void fsi_free_dma_chan(void)
+{
+ dma_wait_for_completion(PORTA_DMA);
+ dma_wait_for_completion(PORTB_DMA);
+ free_dma(PORTA_DMA);
+ free_dma(PORTB_DMA);
+
+ master->fsia.dma_chan = -1;
+ master->fsib.dma_chan = -1;
+}
+
+/************************************************************************
+
+
+ ctrl function
+
+
+************************************************************************/
+static void fsi_irq_enable(struct fsi_priv *fsi, int is_play)
+{
+ u32 data = fsi_port_ab_io_bit(fsi, is_play);
+
+ fsi_master_mask_set(IMSK, data, data);
+ fsi_master_mask_set(IEMSK, data, data);
+}
+
+static void fsi_irq_disable(struct fsi_priv *fsi, int is_play)
+{
+ u32 data = fsi_port_ab_io_bit(fsi, is_play);
+
+ fsi_master_mask_set(IMSK, data, 0);
+ fsi_master_mask_set(IEMSK, data, 0);
+}
+
+static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable)
+{
+ u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4);
+
+ if (enable)
+ fsi_master_mask_set(CLK_RST, val, val);
+ else
+ fsi_master_mask_set(CLK_RST, val, 0);
+}
+
+static void fsi_irq_init(struct fsi_priv *fsi, int is_play)
+{
+ u32 data;
+ u32 ctrl;
+
+ data = fsi_port_ab_io_bit(fsi, is_play);
+ ctrl = is_play ? DOFF_CTL : DIFF_CTL;
+
+ /* set IMSK */
+ fsi_irq_disable(fsi, is_play);
+
+ /* set interrupt generation factor */
+ fsi_reg_write(fsi, ctrl, IRQ_HALF);
+
+ /* clear FIFO */
+ fsi_reg_mask_set(fsi, ctrl, FIFO_CLR, FIFO_CLR);
+
+ /* clear interrupt factor */
+ fsi_master_mask_set(INT_ST, data, 0);
+}
+
+static void fsi_soft_all_reset(void)
+{
+ u32 status = fsi_master_read(SOFT_RST);
+
+ /* port AB reset */
+ status &= 0x000000ff;
+ fsi_master_write(SOFT_RST, status);
+ mdelay(10);
+
+ /* soft reset */
+ status &= 0x000000f0;
+ fsi_master_write(SOFT_RST, status);
+ status |= 0x00000001;
+ fsi_master_write(SOFT_RST, status);
+ mdelay(10);
+}
+
+static void fsi_16data_push(struct fsi_priv *fsi,
+ struct snd_pcm_runtime *runtime,
+ int send)
+{
+ u16 *dma_start;
+ u32 snd;
+ int i;
+
+ /* get dma start position for FSI */
+ dma_start = (u16 *)runtime->dma_area;
+ dma_start += fsi->byte_offset / 2;
+
+ /*
+ * soft dma
+ * FSI can not use DMA when 16bpp
+ */
+ for (i = 0; i < send; i++) {
+ snd = (u32)dma_start[i];
+ fsi_reg_write(fsi, DODT, snd << 8);
+ }
+}
+
+static void fsi_32data_push(struct fsi_priv *fsi,
+ struct snd_pcm_runtime *runtime,
+ int send)
+{
+ u32 *dma_start;
+
+ /* get dma start position for FSI */
+ dma_start = (u32 *)runtime->dma_area;
+ dma_start += fsi->byte_offset / 4;
+
+ dma_wait_for_completion(fsi->dma_chan);
+ dma_configure_channel(fsi->dma_chan, (SM_INC|0x400|TS_32|TM_BUR));
+ dma_write(fsi->dma_chan, (u32)dma_start,
+ (u32)(fsi->base + DODT), send * 4);
+}
+
+/* playback interrupt */
+static int fsi_data_push(struct fsi_priv *fsi)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_pcm_substream *substream = NULL;
+ int send;
+ int fifo_free;
+ int width;
+
+ if (!fsi ||
+ !fsi->substream ||
+ !fsi->substream->runtime)
+ return -EINVAL;
+
+ runtime = fsi->substream->runtime;
+
+ /* FSI FIFO has limit.
+ * So, this driver can not send periods data at a time
+ */
+ if (fsi->byte_offset >=
+ fsi->period_len * (fsi->periods + 1)) {
+
+ substream = fsi->substream;
+ fsi->periods = (fsi->periods + 1) % runtime->periods;
+
+ if (0 == fsi->periods)
+ fsi->byte_offset = 0;
+ }
+
+ /* get 1 channel data width */
+ width = frames_to_bytes(runtime, 1) / fsi->chan;
+
+ /* get send size for alsa */
+ send = (fsi->buffer_len - fsi->byte_offset) / width;
+
+ /* get FIFO free size */
+ fifo_free = (fsi->fifo_max * fsi->chan) - fsi_get_fifo_residue(fsi, 1);
+
+ /* size check */
+ if (fifo_free < send)
+ send = fifo_free;
+
+ if (2 == width)
+ fsi_16data_push(fsi, runtime, send);
+ else if (4 == width)
+ fsi_32data_push(fsi, runtime, send);
+ else
+ return -EINVAL;
+
+ fsi->byte_offset += send * width;
+
+ fsi_irq_enable(fsi, 1);
+
+ if (substream)
+ snd_pcm_period_elapsed(substream);
+
+ return 0;
+}
+
+static irqreturn_t fsi_interrupt(int irq, void *data)
+{
+ u32 status = fsi_master_read(SOFT_RST) & ~0x00000010;
+ u32 int_st = fsi_master_read(INT_ST);
+
+ /* clear irq status */
+ fsi_master_write(SOFT_RST, status);
+ fsi_master_write(SOFT_RST, status | 0x00000010);
+
+ if (int_st & INT_A_OUT)
+ fsi_data_push(&master->fsia);
+ if (int_st & INT_B_OUT)
+ fsi_data_push(&master->fsib);
+
+ fsi_master_write(INT_ST, 0x0000000);
+
+ return IRQ_HANDLED;
+}
+
+/************************************************************************
+
+
+ dai ops
+
+
+************************************************************************/
+static int fsi_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsi_priv *fsi = fsi_get(substream);
+ const char *msg;
+ u32 flags = fsi_get_info_flags(fsi);
+ u32 fmt;
+ u32 reg;
+ u32 data;
+ int is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ int is_master;
+ int ret = 0;
+
+ clk_enable(master->clk);
+
+ /* CKG1 */
+ data = is_play ? (1 << 0) : (1 << 4);
+ is_master = fsi_is_master_mode(fsi, is_play);
+ if (is_master)
+ fsi_reg_mask_set(fsi, CKG1, data, data);
+ else
+ fsi_reg_mask_set(fsi, CKG1, data, 0);
+
+ /* clock inversion (CKG2) */
+ data = 0;
+ switch (SH_FSI_INVERSION_MASK & flags) {
+ case SH_FSI_LRM_INV:
+ data = 1 << 12;
+ break;
+ case SH_FSI_BRM_INV:
+ data = 1 << 8;
+ break;
+ case SH_FSI_LRS_INV:
+ data = 1 << 4;
+ break;
+ case SH_FSI_BRS_INV:
+ data = 1 << 0;
+ break;
+ }
+ fsi_reg_write(fsi, CKG2, data);
+
+ /* do fmt, di fmt */
+ data = 0;
+ reg = is_play ? DO_FMT : DI_FMT;
+ fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags);
+ switch (fmt) {
+ case SH_FSI_FMT_MONO:
+ msg = "MONO";
+ data = CR_FMT(CR_MONO);
+ fsi->chan = 1;
+ break;
+ case SH_FSI_FMT_MONO_DELAY:
+ msg = "MONO Delay";
+ data = CR_FMT(CR_MONO_D);
+ fsi->chan = 1;
+ break;
+ case SH_FSI_FMT_PCM:
+ msg = "PCM";
+ data = CR_FMT(CR_PCM);
+ fsi->chan = 2;
+ break;
+ case SH_FSI_FMT_I2S:
+ msg = "I2S";
+ data = CR_FMT(CR_I2S);
+ fsi->chan = 2;
+ break;
+ case SH_FSI_FMT_TDM:
+ msg = "TDM";
+ data = CR_FMT(CR_TDM) | (fsi->chan - 1);
+ fsi->chan = is_play ?
+ SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags);
+ break;
+ case SH_FSI_FMT_TDM_DELAY:
+ msg = "TDM Delay";
+ data = CR_FMT(CR_TDM_D) | (fsi->chan - 1);
+ fsi->chan = is_play ?
+ SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags);
+ break;
+ default:
+ dev_err(dai->dev, "unknown format.\n");
+ return -EINVAL;
+ }
+
+ switch (fsi->chan) {
+ case 1:
+ fsi->fifo_max = 256;
+ break;
+ case 2:
+ fsi->fifo_max = 128;
+ break;
+ case 3:
+ case 4:
+ fsi->fifo_max = 64;
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ fsi->fifo_max = 32;
+ break;
+ default:
+ dev_err(dai->dev, "channel size error.\n");
+ return -EINVAL;
+ }
+
+ fsi_reg_write(fsi, reg, data);
+ dev_dbg(dai->dev, "use %s format (%d channel) use %d DMAC\n",
+ msg, fsi->chan, fsi->dma_chan);
+
+ /*
+ * clear clk reset if master mode
+ */
+ if (is_master)
+ fsi_clk_ctrl(fsi, 1);
+
+ /* irq setting */
+ fsi_irq_init(fsi, is_play);
+
+ return ret;
+}
+
+static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsi_priv *fsi = fsi_get(substream);
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+ fsi_irq_disable(fsi, is_play);
+ fsi_clk_ctrl(fsi, 0);
+
+ clk_disable(master->clk);
+}
+
+static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct fsi_priv *fsi = fsi_get(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int ret = 0;
+
+ /* capture not supported */
+ if (!is_play)
+ return -ENODEV;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ fsi_stream_push(fsi, substream,
+ frames_to_bytes(runtime, runtime->buffer_size),
+ frames_to_bytes(runtime, runtime->period_size));
+ ret = fsi_data_push(fsi);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ fsi_irq_disable(fsi, is_play);
+ fsi_stream_pop(fsi);
+ break;
+ }
+
+ return ret;
+}
+
+static struct snd_soc_dai_ops fsi_dai_ops = {
+ .startup = fsi_dai_startup,
+ .shutdown = fsi_dai_shutdown,
+ .trigger = fsi_dai_trigger,
+};
+
+/************************************************************************
+
+
+ pcm ops
+
+
+************************************************************************/
+static struct snd_pcm_hardware fsi_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE,
+ .formats = FSI_FMTS,
+ .rates = FSI_RATES,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = 64 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 32,
+ .fifo_size = 256,
+};
+
+static int fsi_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret = 0;
+
+ snd_soc_set_runtime_hwparams(substream, &fsi_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+
+ return ret;
+}
+
+static int fsi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int fsi_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsi_priv *fsi = fsi_get(substream);
+ int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ long location;
+
+ location = (fsi->byte_offset - 1) - fsi_get_residue(fsi, is_play);
+ if (location < 0)
+ location = 0;
+
+ return bytes_to_frames(runtime, location);
+}
+
+static struct snd_pcm_ops fsi_pcm_ops = {
+ .open = fsi_pcm_open,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = fsi_hw_params,
+ .hw_free = fsi_hw_free,
+ .pointer = fsi_pointer,
+};
+
+/************************************************************************
+
+
+ snd_soc_platform
+
+
+************************************************************************/
+#define PREALLOC_BUFFER (32 * 1024)
+#define PREALLOC_BUFFER_MAX (32 * 1024)
+
+static void fsi_pcm_free(struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int fsi_pcm_new(struct snd_card *card,
+ struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ /*
+ * dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
+ * in MMAP mode (i.e. aplay -M)
+ */
+ return snd_pcm_lib_preallocate_pages_for_all(
+ pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
+ PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
+}
+
+/************************************************************************
+
+
+ alsa struct
+
+
+************************************************************************/
+struct snd_soc_dai fsi_soc_dai[] = {
+ {
+ .name = "FSIA",
+ .id = 0,
+ .playback = {
+ .rates = FSI_RATES,
+ .formats = FSI_FMTS,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ /* capture not supported */
+ .ops = &fsi_dai_ops,
+ },
+ {
+ .name = "FSIB",
+ .id = 1,
+ .playback = {
+ .rates = FSI_RATES,
+ .formats = FSI_FMTS,
+ .channels_min = 1,
+ .channels_max = 8,
+ },
+ /* capture not supported */
+ .ops = &fsi_dai_ops,
+ },
+};
+EXPORT_SYMBOL_GPL(fsi_soc_dai);
+
+struct snd_soc_platform fsi_soc_platform = {
+ .name = "fsi-pcm",
+ .pcm_ops = &fsi_pcm_ops,
+ .pcm_new = fsi_pcm_new,
+ .pcm_free = fsi_pcm_free,
+};
+EXPORT_SYMBOL_GPL(fsi_soc_platform);
+
+/************************************************************************
+
+
+ platform function
+
+
+************************************************************************/
+static int fsi_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ char clk_name[8];
+ unsigned int irq;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || !irq) {
+ dev_err(&pdev->dev, "Not enough FSI platform resources.\n");
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ master = kzalloc(sizeof(*master), GFP_KERNEL);
+ if (!master) {
+ dev_err(&pdev->dev, "Could not allocate master\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ master->base = ioremap_nocache(res->start, resource_size(res));
+ if (!master->base) {
+ ret = -ENXIO;
+ dev_err(&pdev->dev, "Unable to ioremap FSI registers.\n");
+ goto exit_kfree;
+ }
+
+ master->irq = irq;
+ master->info = pdev->dev.platform_data;
+ master->fsia.base = master->base;
+ master->fsib.base = master->base + 0x40;
+
+ master->fsia.dma_chan = -1;
+ master->fsib.dma_chan = -1;
+
+ ret = fsi_get_dma_chan();
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot get dma api\n");
+ goto exit_iounmap;
+ }
+
+ /* FSI is based on SPU mstp */
+ snprintf(clk_name, sizeof(clk_name), "spu%d", pdev->id);
+ master->clk = clk_get(NULL, clk_name);
+ if (IS_ERR(master->clk)) {
+ dev_err(&pdev->dev, "cannot get %s mstp\n", clk_name);
+ ret = -EIO;
+ goto exit_free_dma;
+ }
+
+ fsi_soc_dai[0].dev = &pdev->dev;
+ fsi_soc_dai[1].dev = &pdev->dev;
+
+ fsi_soft_all_reset();
+
+ ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master);
+ if (ret) {
+ dev_err(&pdev->dev, "irq request err\n");
+ goto exit_free_dma;
+ }
+
+ ret = snd_soc_register_platform(&fsi_soc_platform);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot snd soc register\n");
+ goto exit_free_irq;
+ }
+
+ return snd_soc_register_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai));
+
+exit_free_irq:
+ free_irq(irq, master);
+exit_free_dma:
+ fsi_free_dma_chan();
+exit_iounmap:
+ iounmap(master->base);
+exit_kfree:
+ kfree(master);
+ master = NULL;
+exit:
+ return ret;
+}
+
+static int fsi_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai));
+ snd_soc_unregister_platform(&fsi_soc_platform);
+
+ clk_put(master->clk);
+
+ fsi_free_dma_chan();
+
+ free_irq(master->irq, master);
+
+ iounmap(master->base);
+ kfree(master);
+ master = NULL;
+ return 0;
+}
+
+static struct platform_driver fsi_driver = {
+ .driver = {
+ .name = "sh_fsi",
+ },
+ .probe = fsi_probe,
+ .remove = fsi_remove,
+};
+
+static int __init fsi_mobile_init(void)
+{
+ return platform_driver_register(&fsi_driver);
+}
+
+static void __exit fsi_mobile_exit(void)
+{
+ platform_driver_unregister(&fsi_driver);
+}
+module_init(fsi_mobile_init);
+module_exit(fsi_mobile_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SuperH onchip FSI audio driver");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
new file mode 100644
index 00000000000..c8ceddc2a26
--- /dev/null
+++ b/sound/soc/soc-cache.c
@@ -0,0 +1,218 @@
+/*
+ * soc-cache.c -- ASoC register cache helpers
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= codec->reg_cache_size)
+ return -1;
+ return cache[reg];
+}
+
+static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ u8 data[2];
+ int ret;
+
+ BUG_ON(codec->volatile_register);
+
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
+
+ if (reg < codec->reg_cache_size)
+ cache[reg] = value;
+ ret = codec->hw_write(codec->control_data, data, 2);
+ if (ret == 2)
+ return 0;
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int snd_soc_7_9_spi_write(void *control_data, const char *data,
+ int len)
+{
+ struct spi_device *spi = control_data;
+ struct spi_transfer t;
+ struct spi_message m;
+ u8 msg[2];
+
+ if (len <= 0)
+ return 0;
+
+ msg[0] = data[0];
+ msg[1] = data[1];
+
+ spi_message_init(&m);
+ memset(&t, 0, (sizeof t));
+
+ t.tx_buf = &msg[0];
+ t.len = len;
+
+ spi_message_add_tail(&t, &m);
+ spi_sync(spi, &m);
+
+ return len;
+}
+#else
+#define snd_soc_7_9_spi_write NULL
+#endif
+
+static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u16 *reg_cache = codec->reg_cache;
+ u8 data[3];
+
+ data[0] = reg;
+ data[1] = (value >> 8) & 0xff;
+ data[2] = value & 0xff;
+
+ if (!snd_soc_codec_volatile_register(codec, reg))
+ reg_cache[reg] = value;
+
+ if (codec->hw_write(codec->control_data, data, 3) == 3)
+ return 0;
+ else
+ return -EIO;
+}
+
+static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+
+ if (reg >= codec->reg_cache_size ||
+ snd_soc_codec_volatile_register(codec, reg))
+ return codec->hw_read(codec, reg);
+ else
+ return cache[reg];
+}
+
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
+ unsigned int r)
+{
+ struct i2c_msg xfer[2];
+ u8 reg = r;
+ u16 data;
+ int ret;
+ struct i2c_client *client = codec->control_data;
+
+ /* Write register */
+ xfer[0].addr = client->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 1;
+ xfer[0].buf = &reg;
+
+ /* Read data */
+ xfer[1].addr = client->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = 2;
+ xfer[1].buf = (u8 *)&data;
+
+ ret = i2c_transfer(client->adapter, xfer, 2);
+ if (ret != 2) {
+ dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+ return 0;
+ }
+
+ return (data >> 8) | ((data & 0xff) << 8);
+}
+#else
+#define snd_soc_8_16_read_i2c NULL
+#endif
+
+static struct {
+ int addr_bits;
+ int data_bits;
+ int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int);
+ int (*spi_write)(void *, const char *, int);
+ unsigned int (*read)(struct snd_soc_codec *, unsigned int);
+ unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
+} io_types[] = {
+ { 7, 9, snd_soc_7_9_write, snd_soc_7_9_spi_write, snd_soc_7_9_read },
+ { 8, 16, snd_soc_8_16_write, NULL, snd_soc_8_16_read,
+ snd_soc_8_16_read_i2c },
+};
+
+/**
+ * snd_soc_codec_set_cache_io: Set up standard I/O functions.
+ *
+ * @codec: CODEC to configure.
+ * @type: Type of cache.
+ * @addr_bits: Number of bits of register address data.
+ * @data_bits: Number of bits of data per register.
+ * @control: Control bus used.
+ *
+ * Register formats are frequently shared between many I2C and SPI
+ * devices. In order to promote code reuse the ASoC core provides
+ * some standard implementations of CODEC read and write operations
+ * which can be set up using this function.
+ *
+ * The caller is responsible for allocating and initialising the
+ * actual cache.
+ *
+ * Note that at present this code cannot be used by CODECs with
+ * volatile registers.
+ */
+int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
+ int addr_bits, int data_bits,
+ enum snd_soc_control_type control)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(io_types); i++)
+ if (io_types[i].addr_bits == addr_bits &&
+ io_types[i].data_bits == data_bits)
+ break;
+ if (i == ARRAY_SIZE(io_types)) {
+ printk(KERN_ERR
+ "No I/O functions for %d bit address %d bit data\n",
+ addr_bits, data_bits);
+ return -EINVAL;
+ }
+
+ codec->write = io_types[i].write;
+ codec->read = io_types[i].read;
+
+ switch (control) {
+ case SND_SOC_CUSTOM:
+ break;
+
+ case SND_SOC_I2C:
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+ codec->hw_write = (hw_write_t)i2c_master_send;
+#endif
+ if (io_types[i].i2c_read)
+ codec->hw_read = io_types[i].i2c_read;
+ break;
+
+ case SND_SOC_SPI:
+ if (io_types[i].spi_write)
+ codec->hw_write = io_types[i].spi_write;
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 1d70829464e..7ff04ad2a97 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -28,6 +28,7 @@
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
+#include <sound/ac97_codec.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -619,8 +620,9 @@ static struct snd_pcm_ops soc_pcm_ops = {
#ifdef CONFIG_PM
/* powers down audio subsystem for suspend */
-static int soc_suspend(struct platform_device *pdev, pm_message_t state)
+static int soc_suspend(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_card *card = socdev->card;
struct snd_soc_platform *platform = card->platform;
@@ -656,7 +658,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
snd_pcm_suspend_all(card->dai_link[i].pcm);
if (card->suspend_pre)
- card->suspend_pre(pdev, state);
+ card->suspend_pre(pdev, PMSG_SUSPEND);
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
@@ -682,7 +684,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
}
if (codec_dev->suspend)
- codec_dev->suspend(pdev, state);
+ codec_dev->suspend(pdev, PMSG_SUSPEND);
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
@@ -691,7 +693,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
}
if (card->suspend_post)
- card->suspend_post(pdev, state);
+ card->suspend_post(pdev, PMSG_SUSPEND);
return 0;
}
@@ -765,8 +767,9 @@ static void soc_resume_deferred(struct work_struct *work)
}
/* powers up audio subsystem after a suspend */
-static int soc_resume(struct platform_device *pdev)
+static int soc_resume(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_card *card = socdev->card;
struct snd_soc_dai *cpu_dai = card->dai_link[0].cpu_dai;
@@ -788,6 +791,44 @@ static int soc_resume(struct platform_device *pdev)
return 0;
}
+/**
+ * snd_soc_suspend_device: Notify core of device suspend
+ *
+ * @dev: Device being suspended.
+ *
+ * In order to ensure that the entire audio subsystem is suspended in a
+ * coordinated fashion ASoC devices should suspend themselves when
+ * called by ASoC. When the standard kernel suspend process asks the
+ * device to suspend it should call this function to initiate a suspend
+ * of the entire ASoC card.
+ *
+ * \note Currently this function is stubbed out.
+ */
+int snd_soc_suspend_device(struct device *dev)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_suspend_device);
+
+/**
+ * snd_soc_resume_device: Notify core of device resume
+ *
+ * @dev: Device being resumed.
+ *
+ * In order to ensure that the entire audio subsystem is resumed in a
+ * coordinated fashion ASoC devices should resume themselves when called
+ * by ASoC. When the standard kernel resume process asks the device
+ * to resume it should call this function. Once all the components of
+ * the card have notified that they are ready to be resumed the card
+ * will be resumed.
+ *
+ * \note Currently this function is stubbed out.
+ */
+int snd_soc_resume_device(struct device *dev)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_resume_device);
#else
#define soc_suspend NULL
#define soc_resume NULL
@@ -981,16 +1022,39 @@ static int soc_remove(struct platform_device *pdev)
return 0;
}
+static int soc_poweroff(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_card *card = socdev->card;
+
+ if (!card->instantiated)
+ return 0;
+
+ /* Flush out pmdown_time work - we actually do want to run it
+ * now, we're shutting down so no imminent restart. */
+ run_delayed_work(&card->delayed_work);
+
+ snd_soc_dapm_shutdown(socdev);
+
+ return 0;
+}
+
+static struct dev_pm_ops soc_pm_ops = {
+ .suspend = soc_suspend,
+ .resume = soc_resume,
+ .poweroff = soc_poweroff,
+};
+
/* ASoC platform driver */
static struct platform_driver soc_driver = {
.driver = {
.name = "soc-audio",
.owner = THIS_MODULE,
+ .pm = &soc_pm_ops,
},
.probe = soc_probe,
.remove = soc_remove,
- .suspend = soc_suspend,
- .resume = soc_resume,
};
/* create a new pcm */
@@ -1062,6 +1126,23 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
return ret;
}
+/**
+ * snd_soc_codec_volatile_register: Report if a register is volatile.
+ *
+ * @codec: CODEC to query.
+ * @reg: Register to query.
+ *
+ * Boolean function indiciating if a CODEC register is volatile.
+ */
+int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg)
+{
+ if (codec->volatile_register)
+ return codec->volatile_register(reg);
+ else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register);
+
/* codec register dump */
static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
{
@@ -1075,6 +1156,9 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
count += sprintf(buf, "%s registers\n", codec->name);
for (i = 0; i < codec->reg_cache_size; i += step) {
+ if (codec->readable_register && !codec->readable_register(i))
+ continue;
+
count += sprintf(buf + count, "%2x: ", i);
if (count >= PAGE_SIZE - 1)
break;
@@ -1183,10 +1267,18 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
if (!codec->debugfs_pop_time)
printk(KERN_WARNING
"Failed to create pop time debugfs file\n");
+
+ codec->debugfs_dapm = debugfs_create_dir("dapm", debugfs_root);
+ if (!codec->debugfs_dapm)
+ printk(KERN_WARNING
+ "Failed to create DAPM debugfs directory\n");
+
+ snd_soc_dapm_debugfs_init(codec);
}
static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
{
+ debugfs_remove_recursive(codec->debugfs_dapm);
debugfs_remove(codec->debugfs_pop_time);
debugfs_remove(codec->debugfs_reg);
}
@@ -1264,10 +1356,10 @@ EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
* Returns 1 for change else 0.
*/
int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
- unsigned short mask, unsigned short value)
+ unsigned int mask, unsigned int value)
{
int change;
- unsigned short old, new;
+ unsigned int old, new;
mutex_lock(&io_mutex);
old = snd_soc_read(codec, reg);
@@ -1294,10 +1386,10 @@ EXPORT_SYMBOL_GPL(snd_soc_update_bits);
* Returns 1 for change else 0.
*/
int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
- unsigned short mask, unsigned short value)
+ unsigned int mask, unsigned int value)
{
int change;
- unsigned short old, new;
+ unsigned int old, new;
mutex_lock(&io_mutex);
old = snd_soc_read(codec, reg);
@@ -1381,8 +1473,11 @@ int snd_soc_init_card(struct snd_soc_device *socdev)
continue;
}
}
- if (card->dai_link[i].codec_dai->ac97_control)
+ if (card->dai_link[i].codec_dai->ac97_control) {
ac97 = 1;
+ snd_ac97_dev_add_pdata(codec->ac97,
+ card->dai_link[i].cpu_dai->ac97_pdata);
+ }
}
snprintf(codec->card->shortname, sizeof(codec->card->shortname),
"%s", card->name);
@@ -1586,7 +1681,7 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned short val, bitmask;
+ unsigned int val, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
@@ -1615,8 +1710,8 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned short val;
- unsigned short mask, bitmask;
+ unsigned int val;
+ unsigned int mask, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
@@ -1652,7 +1747,7 @@ int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned short reg_val, val, mux;
+ unsigned int reg_val, val, mux;
reg_val = snd_soc_read(codec, e->reg);
val = (reg_val >> e->shift_l) & e->mask;
@@ -1691,8 +1786,8 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned short val;
- unsigned short mask;
+ unsigned int val;
+ unsigned int mask;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
@@ -1852,7 +1947,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
- unsigned short val, val2, val_mask;
+ unsigned int val, val2, val_mask;
val = (ucontrol->value.integer.value[0] & mask);
if (invert)
@@ -1918,7 +2013,7 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
unsigned int reg2 = mc->rreg;
unsigned int shift = mc->shift;
int max = mc->max;
- unsigned int mask = (1<<fls(max))-1;
+ unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
ucontrol->value.integer.value[0] =
@@ -1958,7 +2053,7 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
int err;
- unsigned short val, val2, val_mask;
+ unsigned int val, val2, val_mask;
val_mask = mask << shift;
val = (ucontrol->value.integer.value[0] & mask);
@@ -2050,7 +2145,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
int min = mc->min;
- unsigned short val;
+ unsigned int val;
val = (ucontrol->value.integer.value[0]+min) & 0xff;
val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
@@ -2136,17 +2231,20 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
/**
* snd_soc_dai_set_tdm_slot - configure DAI TDM.
* @dai: DAI
- * @mask: DAI specific mask representing used slots.
+ * @tx_mask: bitmask representing active TX slots.
+ * @rx_mask: bitmask representing active RX slots.
* @slots: Number of slots in use.
+ * @slot_width: Width in bits for each slot.
*
* Configures a DAI for TDM operation. Both mask and slots are codec and DAI
* specific.
*/
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
- unsigned int mask, int slots)
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
{
if (dai->ops && dai->ops->set_tdm_slot)
- return dai->ops->set_tdm_slot(dai, mask, slots);
+ return dai->ops->set_tdm_slot(dai, tx_mask, rx_mask,
+ slots, slot_width);
else
return -EINVAL;
}
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 21c69074aa1..0d8b08ef873 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -37,6 +37,7 @@
#include <linux/bitops.h>
#include <linux/platform_device.h>
#include <linux/jiffies.h>
+#include <linux/debugfs.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -52,19 +53,41 @@
/* dapm power sequences - make this per codec in the future */
static int dapm_up_seq[] = {
- snd_soc_dapm_pre, snd_soc_dapm_supply, snd_soc_dapm_micbias,
- snd_soc_dapm_mic, snd_soc_dapm_mux, snd_soc_dapm_value_mux,
- snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl,
- snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
- snd_soc_dapm_post
+ [snd_soc_dapm_pre] = 0,
+ [snd_soc_dapm_supply] = 1,
+ [snd_soc_dapm_micbias] = 2,
+ [snd_soc_dapm_aif_in] = 3,
+ [snd_soc_dapm_aif_out] = 3,
+ [snd_soc_dapm_mic] = 4,
+ [snd_soc_dapm_mux] = 5,
+ [snd_soc_dapm_value_mux] = 5,
+ [snd_soc_dapm_dac] = 6,
+ [snd_soc_dapm_mixer] = 7,
+ [snd_soc_dapm_mixer_named_ctl] = 7,
+ [snd_soc_dapm_pga] = 8,
+ [snd_soc_dapm_adc] = 9,
+ [snd_soc_dapm_hp] = 10,
+ [snd_soc_dapm_spk] = 10,
+ [snd_soc_dapm_post] = 11,
};
static int dapm_down_seq[] = {
- snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
- snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer,
- snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias,
- snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_supply,
- snd_soc_dapm_post
+ [snd_soc_dapm_pre] = 0,
+ [snd_soc_dapm_adc] = 1,
+ [snd_soc_dapm_hp] = 2,
+ [snd_soc_dapm_spk] = 2,
+ [snd_soc_dapm_pga] = 4,
+ [snd_soc_dapm_mixer_named_ctl] = 5,
+ [snd_soc_dapm_mixer] = 5,
+ [snd_soc_dapm_dac] = 6,
+ [snd_soc_dapm_mic] = 7,
+ [snd_soc_dapm_micbias] = 8,
+ [snd_soc_dapm_mux] = 9,
+ [snd_soc_dapm_value_mux] = 9,
+ [snd_soc_dapm_aif_in] = 10,
+ [snd_soc_dapm_aif_out] = 10,
+ [snd_soc_dapm_supply] = 11,
+ [snd_soc_dapm_post] = 12,
};
static void pop_wait(u32 pop_time)
@@ -130,8 +153,12 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
if (card->set_bias_level)
ret = card->set_bias_level(card, level);
- if (ret == 0 && codec->set_bias_level)
- ret = codec->set_bias_level(codec, level);
+ if (ret == 0) {
+ if (codec->set_bias_level)
+ ret = codec->set_bias_level(codec, level);
+ else
+ codec->bias_level = level;
+ }
return ret;
}
@@ -206,6 +233,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_micbias:
case snd_soc_dapm_vmid:
case snd_soc_dapm_supply:
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_aif_out:
p->connect = 1;
break;
/* does effect routing - dynamically connected */
@@ -268,7 +297,7 @@ static int dapm_connect_mixer(struct snd_soc_codec *codec,
static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
{
int change, power;
- unsigned short old, new;
+ unsigned int old, new;
struct snd_soc_codec *codec = widget->codec;
/* check for valid widgets */
@@ -479,8 +508,14 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
if (widget->id == snd_soc_dapm_supply)
return 0;
- if (widget->id == snd_soc_dapm_adc && widget->active)
- return 1;
+ switch (widget->id) {
+ case snd_soc_dapm_adc:
+ case snd_soc_dapm_aif_out:
+ if (widget->active)
+ return 1;
+ default:
+ break;
+ }
if (widget->connected) {
/* connected pin ? */
@@ -519,8 +554,14 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
return 0;
/* active stream ? */
- if (widget->id == snd_soc_dapm_dac && widget->active)
- return 1;
+ switch (widget->id) {
+ case snd_soc_dapm_dac:
+ case snd_soc_dapm_aif_in:
+ if (widget->active)
+ return 1;
+ default:
+ break;
+ }
if (widget->connected) {
/* connected pin ? */
@@ -689,53 +730,211 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
return power;
}
-/*
- * Scan a single DAPM widget for a complete audio path and update the
- * power status appropriately.
- */
-static int dapm_power_widget(struct snd_soc_codec *codec, int event,
- struct snd_soc_dapm_widget *w)
+static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
+ struct snd_soc_dapm_widget *b,
+ int sort[])
{
- int ret;
+ if (sort[a->id] != sort[b->id])
+ return sort[a->id] - sort[b->id];
+ if (a->reg != b->reg)
+ return a->reg - b->reg;
- switch (w->id) {
- case snd_soc_dapm_pre:
- if (!w->event)
- return 0;
+ return 0;
+}
- if (event == SND_SOC_DAPM_STREAM_START) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_PRE_PMU);
+/* Insert a widget in order into a DAPM power sequence. */
+static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget,
+ struct list_head *list,
+ int sort[])
+{
+ struct snd_soc_dapm_widget *w;
+
+ list_for_each_entry(w, list, power_list)
+ if (dapm_seq_compare(new_widget, w, sort) < 0) {
+ list_add_tail(&new_widget->power_list, &w->power_list);
+ return;
+ }
+
+ list_add_tail(&new_widget->power_list, list);
+}
+
+/* Apply the coalesced changes from a DAPM sequence */
+static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
+ struct list_head *pending)
+{
+ struct snd_soc_dapm_widget *w;
+ int reg, power, ret;
+ unsigned int value = 0;
+ unsigned int mask = 0;
+ unsigned int cur_mask;
+
+ reg = list_first_entry(pending, struct snd_soc_dapm_widget,
+ power_list)->reg;
+
+ list_for_each_entry(w, pending, power_list) {
+ cur_mask = 1 << w->shift;
+ BUG_ON(reg != w->reg);
+
+ if (w->invert)
+ power = !w->power;
+ else
+ power = w->power;
+
+ mask |= cur_mask;
+ if (power)
+ value |= cur_mask;
+
+ pop_dbg(codec->pop_time,
+ "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
+ w->name, reg, value, mask);
+
+ /* power up pre event */
+ if (w->power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
+ pop_dbg(codec->pop_time, "pop test : %s PRE_PMU\n",
+ w->name);
+ ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
if (ret < 0)
- return ret;
- } else if (event == SND_SOC_DAPM_STREAM_STOP) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_PRE_PMD);
+ pr_err("%s: pre event failed: %d\n",
+ w->name, ret);
+ }
+
+ /* power down pre event */
+ if (!w->power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
+ pop_dbg(codec->pop_time, "pop test : %s PRE_PMD\n",
+ w->name);
+ ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
if (ret < 0)
- return ret;
+ pr_err("%s: pre event failed: %d\n",
+ w->name, ret);
}
- return 0;
- case snd_soc_dapm_post:
- if (!w->event)
- return 0;
+ /* Lower PGA volume to reduce pops */
+ if (w->id == snd_soc_dapm_pga && !w->power)
+ dapm_set_pga(w, w->power);
+ }
- if (event == SND_SOC_DAPM_STREAM_START) {
+ if (reg >= 0) {
+ pop_dbg(codec->pop_time,
+ "pop test : Applying 0x%x/0x%x to %x in %dms\n",
+ value, mask, reg, codec->pop_time);
+ pop_wait(codec->pop_time);
+ snd_soc_update_bits(codec, reg, mask, value);
+ }
+
+ list_for_each_entry(w, pending, power_list) {
+ /* Raise PGA volume to reduce pops */
+ if (w->id == snd_soc_dapm_pga && w->power)
+ dapm_set_pga(w, w->power);
+
+ /* power up post event */
+ if (w->power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
+ pop_dbg(codec->pop_time, "pop test : %s POST_PMU\n",
+ w->name);
ret = w->event(w,
NULL, SND_SOC_DAPM_POST_PMU);
if (ret < 0)
- return ret;
- } else if (event == SND_SOC_DAPM_STREAM_STOP) {
- ret = w->event(w,
- NULL, SND_SOC_DAPM_POST_PMD);
+ pr_err("%s: post event failed: %d\n",
+ w->name, ret);
+ }
+
+ /* power down post event */
+ if (!w->power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
+ pop_dbg(codec->pop_time, "pop test : %s POST_PMD\n",
+ w->name);
+ ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
if (ret < 0)
- return ret;
+ pr_err("%s: post event failed: %d\n",
+ w->name, ret);
}
- return 0;
+ }
+}
- default:
- return dapm_generic_apply_power(w);
+/* Apply a DAPM power sequence.
+ *
+ * We walk over a pre-sorted list of widgets to apply power to. In
+ * order to minimise the number of writes to the device required
+ * multiple widgets will be updated in a single write where possible.
+ * Currently anything that requires more than a single write is not
+ * handled.
+ */
+static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
+ int event, int sort[])
+{
+ struct snd_soc_dapm_widget *w, *n;
+ LIST_HEAD(pending);
+ int cur_sort = -1;
+ int cur_reg = SND_SOC_NOPM;
+ int ret;
+
+ list_for_each_entry_safe(w, n, list, power_list) {
+ ret = 0;
+
+ /* Do we need to apply any queued changes? */
+ if (sort[w->id] != cur_sort || w->reg != cur_reg) {
+ if (!list_empty(&pending))
+ dapm_seq_run_coalesced(codec, &pending);
+
+ INIT_LIST_HEAD(&pending);
+ cur_sort = -1;
+ cur_reg = SND_SOC_NOPM;
+ }
+
+ switch (w->id) {
+ case snd_soc_dapm_pre:
+ if (!w->event)
+ list_for_each_entry_safe_continue(w, n, list,
+ power_list);
+
+ if (event == SND_SOC_DAPM_STREAM_START)
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_PRE_PMU);
+ else if (event == SND_SOC_DAPM_STREAM_STOP)
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_PRE_PMD);
+ break;
+
+ case snd_soc_dapm_post:
+ if (!w->event)
+ list_for_each_entry_safe_continue(w, n, list,
+ power_list);
+
+ if (event == SND_SOC_DAPM_STREAM_START)
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_POST_PMU);
+ else if (event == SND_SOC_DAPM_STREAM_STOP)
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_POST_PMD);
+ break;
+
+ case snd_soc_dapm_input:
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_hp:
+ case snd_soc_dapm_mic:
+ case snd_soc_dapm_line:
+ case snd_soc_dapm_spk:
+ /* No register support currently */
+ ret = dapm_generic_apply_power(w);
+ break;
+
+ default:
+ /* Queue it up for application */
+ cur_sort = sort[w->id];
+ cur_reg = w->reg;
+ list_move(&w->power_list, &pending);
+ break;
+ }
+
+ if (ret < 0)
+ pr_err("Failed to apply widget power: %d\n",
+ ret);
}
+
+ if (!list_empty(&pending))
+ dapm_seq_run_coalesced(codec, &pending);
}
/*
@@ -751,23 +950,22 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
{
struct snd_soc_device *socdev = codec->socdev;
struct snd_soc_dapm_widget *w;
+ LIST_HEAD(up_list);
+ LIST_HEAD(down_list);
int ret = 0;
- int i, power;
+ int power;
int sys_power = 0;
- INIT_LIST_HEAD(&codec->up_list);
- INIT_LIST_HEAD(&codec->down_list);
-
/* Check which widgets we need to power and store them in
* lists indicating if they should be powered up or down.
*/
list_for_each_entry(w, &codec->dapm_widgets, list) {
switch (w->id) {
case snd_soc_dapm_pre:
- list_add_tail(&codec->down_list, &w->power_list);
+ dapm_seq_insert(w, &down_list, dapm_down_seq);
break;
case snd_soc_dapm_post:
- list_add_tail(&codec->up_list, &w->power_list);
+ dapm_seq_insert(w, &up_list, dapm_up_seq);
break;
default:
@@ -782,16 +980,31 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
continue;
if (power)
- list_add_tail(&w->power_list, &codec->up_list);
+ dapm_seq_insert(w, &up_list, dapm_up_seq);
else
- list_add_tail(&w->power_list,
- &codec->down_list);
+ dapm_seq_insert(w, &down_list, dapm_down_seq);
w->power = power;
break;
}
}
+ /* If there are no DAPM widgets then try to figure out power from the
+ * event type.
+ */
+ if (list_empty(&codec->dapm_widgets)) {
+ switch (event) {
+ case SND_SOC_DAPM_STREAM_START:
+ case SND_SOC_DAPM_STREAM_RESUME:
+ sys_power = 1;
+ break;
+ case SND_SOC_DAPM_STREAM_NOP:
+ sys_power = codec->bias_level != SND_SOC_BIAS_STANDBY;
+ default:
+ break;
+ }
+ }
+
/* If we're changing to all on or all off then prepare */
if ((sys_power && codec->bias_level == SND_SOC_BIAS_STANDBY) ||
(!sys_power && codec->bias_level == SND_SOC_BIAS_ON)) {
@@ -802,32 +1015,10 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
}
/* Power down widgets first; try to avoid amplifying pops. */
- for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) {
- list_for_each_entry(w, &codec->down_list, power_list) {
- /* is widget in stream order */
- if (w->id != dapm_down_seq[i])
- continue;
-
- ret = dapm_power_widget(codec, event, w);
- if (ret != 0)
- pr_err("Failed to power down %s: %d\n",
- w->name, ret);
- }
- }
+ dapm_seq_run(codec, &down_list, event, dapm_down_seq);
/* Now power up. */
- for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) {
- list_for_each_entry(w, &codec->up_list, power_list) {
- /* is widget in stream order */
- if (w->id != dapm_up_seq[i])
- continue;
-
- ret = dapm_power_widget(codec, event, w);
- if (ret != 0)
- pr_err("Failed to power up %s: %d\n",
- w->name, ret);
- }
- }
+ dapm_seq_run(codec, &up_list, event, dapm_up_seq);
/* If we just powered the last thing off drop to standby bias */
if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {
@@ -845,6 +1036,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
pr_err("Failed to apply active bias: %d\n", ret);
}
+ pop_dbg(codec->pop_time, "DAPM sequencing finished, waiting %dms\n",
+ codec->pop_time);
+
return 0;
}
@@ -881,6 +1075,8 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
case snd_soc_dapm_supply:
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_aif_out:
if (w->name) {
in = is_connected_input_ep(w);
dapm_clear_walk(w->codec);
@@ -906,6 +1102,92 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
}
#endif
+#ifdef CONFIG_DEBUG_FS
+static int dapm_widget_power_open_file(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t dapm_widget_power_read_file(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct snd_soc_dapm_widget *w = file->private_data;
+ char *buf;
+ int in, out;
+ ssize_t ret;
+ struct snd_soc_dapm_path *p = NULL;
+
+ buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ in = is_connected_input_ep(w);
+ dapm_clear_walk(w->codec);
+ out = is_connected_output_ep(w);
+ dapm_clear_walk(w->codec);
+
+ ret = snprintf(buf, PAGE_SIZE, "%s: %s in %d out %d\n",
+ w->name, w->power ? "On" : "Off", in, out);
+
+ if (w->active && w->sname)
+ ret += snprintf(buf, PAGE_SIZE - ret, " stream %s active\n",
+ w->sname);
+
+ list_for_each_entry(p, &w->sources, list_sink) {
+ if (p->connect)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ " in %s %s\n",
+ p->name ? p->name : "static",
+ p->source->name);
+ }
+ list_for_each_entry(p, &w->sinks, list_source) {
+ if (p->connect)
+ ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ " out %s %s\n",
+ p->name ? p->name : "static",
+ p->sink->name);
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations dapm_widget_power_fops = {
+ .open = dapm_widget_power_open_file,
+ .read = dapm_widget_power_read_file,
+};
+
+void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_widget *w;
+ struct dentry *d;
+
+ if (!codec->debugfs_dapm)
+ return;
+
+ list_for_each_entry(w, &codec->dapm_widgets, list) {
+ if (!w->name)
+ continue;
+
+ d = debugfs_create_file(w->name, 0444,
+ codec->debugfs_dapm, w,
+ &dapm_widget_power_fops);
+ if (!d)
+ printk(KERN_WARNING
+ "ASoC: Failed to create %s debugfs file\n",
+ w->name);
+ }
+}
+#else
+void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec)
+{
+}
+#endif
+
/* test and update the power status of a mux widget */
static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *kcontrol, int mask,
@@ -1138,8 +1420,8 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
if (wsink->id == snd_soc_dapm_input) {
if (wsource->id == snd_soc_dapm_micbias ||
wsource->id == snd_soc_dapm_mic ||
- wsink->id == snd_soc_dapm_line ||
- wsink->id == snd_soc_dapm_output)
+ wsource->id == snd_soc_dapm_line ||
+ wsource->id == snd_soc_dapm_output)
wsink->ext = 1;
}
if (wsource->id == snd_soc_dapm_output) {
@@ -1171,6 +1453,8 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
case snd_soc_dapm_pre:
case snd_soc_dapm_post:
case snd_soc_dapm_supply:
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_aif_out:
list_add(&path->list, &codec->dapm_paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
@@ -1273,9 +1557,11 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
dapm_new_mux(codec, w);
break;
case snd_soc_dapm_adc:
+ case snd_soc_dapm_aif_out:
w->power_check = dapm_adc_check_power;
break;
case snd_soc_dapm_dac:
+ case snd_soc_dapm_aif_in:
w->power_check = dapm_dac_check_power;
break;
case snd_soc_dapm_pga:
@@ -1372,7 +1658,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
- unsigned short val, val2, val_mask;
+ unsigned int val, val2, val_mask;
int ret;
val = (ucontrol->value.integer.value[0] & mask);
@@ -1436,7 +1722,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned short val, bitmask;
+ unsigned int val, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
@@ -1464,8 +1750,8 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned short val, mux;
- unsigned short mask, bitmask;
+ unsigned int val, mux;
+ unsigned int mask, bitmask;
int ret = 0;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
@@ -1523,7 +1809,7 @@ int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned short reg_val, val, mux;
+ unsigned int reg_val, val, mux;
reg_val = snd_soc_read(widget->codec, e->reg);
val = (reg_val >> e->shift_l) & e->mask;
@@ -1563,8 +1849,8 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned short val, mux;
- unsigned short mask;
+ unsigned int val, mux;
+ unsigned int mask;
int ret = 0;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
@@ -1880,6 +2166,36 @@ void snd_soc_dapm_free(struct snd_soc_device *socdev)
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
+/*
+ * snd_soc_dapm_shutdown - callback for system shutdown
+ */
+void snd_soc_dapm_shutdown(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct snd_soc_dapm_widget *w;
+ LIST_HEAD(down_list);
+ int powerdown = 0;
+
+ list_for_each_entry(w, &codec->dapm_widgets, list) {
+ if (w->power) {
+ dapm_seq_insert(w, &down_list, dapm_down_seq);
+ w->power = 0;
+ powerdown = 1;
+ }
+ }
+
+ /* If there were no widgets to power down we're already in
+ * standby.
+ */
+ if (powerdown) {
+ snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_PREPARE);
+ dapm_seq_run(codec, &down_list, 0, dapm_down_seq);
+ snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_STANDBY);
+ }
+
+ snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_OFF);
+}
+
/* Module information */
MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index 28346fb2e70..1d455ab7949 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -73,14 +73,15 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
oldstatus = jack->status;
jack->status &= ~mask;
- jack->status |= status;
+ jack->status |= status & mask;
- /* The DAPM sync is expensive enough to be worth skipping */
- if (jack->status == oldstatus)
+ /* The DAPM sync is expensive enough to be worth skipping.
+ * However, empty mask means pin synchronization is desired. */
+ if (mask && (jack->status == oldstatus))
goto out;
list_for_each_entry(pin, &jack->pins, list) {
- enable = pin->mask & status;
+ enable = pin->mask & jack->status;
if (pin->invert)
enable = !enable;
@@ -220,6 +221,9 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
if (ret)
goto err;
+ INIT_WORK(&gpios[i].work, gpio_work);
+ gpios[i].jack = jack;
+
ret = request_irq(gpio_to_irq(gpios[i].gpio),
gpio_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
@@ -228,8 +232,13 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
if (ret)
goto err;
- INIT_WORK(&gpios[i].work, gpio_work);
- gpios[i].jack = jack;
+#ifdef CONFIG_GPIO_SYSFS
+ /* Expose GPIO value over sysfs for diagnostic purposes */
+ gpio_export(gpios[i].gpio, false);
+#endif
+
+ /* Update initial jack status */
+ snd_soc_jack_gpio_detect(&gpios[i]);
}
return 0;
@@ -258,6 +267,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
int i;
for (i = 0; i < count; i++) {
+#ifdef CONFIG_GPIO_SYSFS
+ gpio_unexport(gpios[i].gpio);
+#endif
free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]);
gpio_free(gpios[i].gpio);
gpios[i].jack = NULL;
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c
index 938a58a5a24..efed64b8b02 100644
--- a/sound/soc/txx9/txx9aclc.c
+++ b/sound/soc/txx9/txx9aclc.c
@@ -297,15 +297,17 @@ static int txx9aclc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
static bool filter(struct dma_chan *chan, void *param)
{
struct txx9aclc_dmadata *dmadata = param;
- char devname[20 + 2]; /* FIXME: old BUS_ID_SIZE + 2 */
+ char *devname;
+ bool found = false;
- snprintf(devname, sizeof(devname), "%s.%d", dmadata->dma_res->name,
+ devname = kasprintf(GFP_KERNEL, "%s.%d", dmadata->dma_res->name,
(int)dmadata->dma_res->start);
if (strcmp(dev_name(chan->device->dev), devname) == 0) {
chan->private = &dmadata->dma_slave;
- return true;
+ found = true;
}
- return false;
+ kfree(devname);
+ return found;
}
static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev,
diff --git a/sound/sound_core.c b/sound/sound_core.c
index a41f8b127f4..bb4b88e606b 100644
--- a/sound/sound_core.c
+++ b/sound/sound_core.c
@@ -128,6 +128,46 @@ extern int msnd_pinnacle_init(void);
#endif
/*
+ * By default, OSS sound_core claims full legacy minor range (0-255)
+ * of SOUND_MAJOR to trap open attempts to any sound minor and
+ * requests modules using custom sound-slot/service-* module aliases.
+ * The only benefit of doing this is allowing use of custom module
+ * aliases instead of the standard char-major-* ones. This behavior
+ * prevents alternative OSS implementation and is scheduled to be
+ * removed.
+ *
+ * CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss kernel
+ * parameter are added to allow distros and developers to try and
+ * switch to alternative implementations without needing to rebuild
+ * the kernel in the meantime. If preclaim_oss is non-zero, the
+ * kernel will behave the same as before. All SOUND_MAJOR minors are
+ * preclaimed and the custom module aliases along with standard chrdev
+ * ones are emitted if a missing device is opened. If preclaim_oss is
+ * zero, sound_core only grabs what's actually in use and for missing
+ * devices only the standard chrdev aliases are requested.
+ *
+ * All these clutters are scheduled to be removed along with
+ * sound-slot/service-* module aliases. Please take a look at
+ * feature-removal-schedule.txt for details.
+ */
+#ifdef CONFIG_SOUND_OSS_CORE_PRECLAIM
+static int preclaim_oss = 1;
+#else
+static int preclaim_oss = 0;
+#endif
+
+module_param(preclaim_oss, int, 0444);
+
+static int soundcore_open(struct inode *, struct file *);
+
+static const struct file_operations soundcore_fops =
+{
+ /* We must have an owner or the module locking fails */
+ .owner = THIS_MODULE,
+ .open = soundcore_open,
+};
+
+/*
* Low level list operator. Scan the ordered list, find a hole and
* join into it. Called with the lock asserted
*/
@@ -219,8 +259,9 @@ static int sound_insert_unit(struct sound_unit **list, const struct file_operati
if (!s)
return -ENOMEM;
-
+
spin_lock(&sound_loader_lock);
+retry:
r = __sound_insert_unit(s, list, fops, index, low, top);
spin_unlock(&sound_loader_lock);
@@ -231,11 +272,31 @@ static int sound_insert_unit(struct sound_unit **list, const struct file_operati
else
sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);
+ if (!preclaim_oss) {
+ /*
+ * Something else might have grabbed the minor. If
+ * first free slot is requested, rescan with @low set
+ * to the next unit; otherwise, -EBUSY.
+ */
+ r = __register_chrdev(SOUND_MAJOR, s->unit_minor, 1, s->name,
+ &soundcore_fops);
+ if (r < 0) {
+ spin_lock(&sound_loader_lock);
+ __sound_remove_unit(list, s->unit_minor);
+ if (index < 0) {
+ low = s->unit_minor + SOUND_STEP;
+ goto retry;
+ }
+ spin_unlock(&sound_loader_lock);
+ return -EBUSY;
+ }
+ }
+
device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),
NULL, s->name+6);
- return r;
+ return s->unit_minor;
- fail:
+fail:
kfree(s);
return r;
}
@@ -254,6 +315,9 @@ static void sound_remove_unit(struct sound_unit **list, int unit)
p = __sound_remove_unit(list, unit);
spin_unlock(&sound_loader_lock);
if (p) {
+ if (!preclaim_oss)
+ __unregister_chrdev(SOUND_MAJOR, p->unit_minor, 1,
+ p->name);
device_destroy(sound_class, MKDEV(SOUND_MAJOR, p->unit_minor));
kfree(p);
}
@@ -491,19 +555,6 @@ void unregister_sound_dsp(int unit)
EXPORT_SYMBOL(unregister_sound_dsp);
-/*
- * Now our file operations
- */
-
-static int soundcore_open(struct inode *, struct file *);
-
-static const struct file_operations soundcore_fops=
-{
- /* We must have an owner or the module locking fails */
- .owner = THIS_MODULE,
- .open = soundcore_open,
-};
-
static struct sound_unit *__look_for_unit(int chain, int unit)
{
struct sound_unit *s;
@@ -539,8 +590,9 @@ static int soundcore_open(struct inode *inode, struct file *file)
s = __look_for_unit(chain, unit);
if (s)
new_fops = fops_get(s->unit_fops);
- if (!new_fops) {
+ if (preclaim_oss && !new_fops) {
spin_unlock(&sound_loader_lock);
+
/*
* Please, don't change this order or code.
* For ALSA slot means soundcard and OSS emulation code
@@ -550,6 +602,17 @@ static int soundcore_open(struct inode *inode, struct file *file)
*/
request_module("sound-slot-%i", unit>>4);
request_module("sound-service-%i-%i", unit>>4, chain);
+
+ /*
+ * sound-slot/service-* module aliases are scheduled
+ * for removal in favor of the standard char-major-*
+ * module aliases. For the time being, generate both
+ * the legacy and standard module aliases to ease
+ * transition.
+ */
+ if (request_module("char-major-%d-%d", SOUND_MAJOR, unit) > 0)
+ request_module("char-major-%d", SOUND_MAJOR);
+
spin_lock(&sound_loader_lock);
s = __look_for_unit(chain, unit);
if (s)
@@ -593,7 +656,8 @@ static void cleanup_oss_soundcore(void)
static int __init init_oss_soundcore(void)
{
- if (register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) {
+ if (preclaim_oss &&
+ register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) == -1) {
printk(KERN_ERR "soundcore: sound device already in use.\n");
return -EBUSY;
}
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 44b9cdc8a83..8db0374e10d 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -1083,6 +1083,8 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
} else
urb_packs = 1;
urb_packs *= packs_per_ms;
+ if (subs->syncpipe)
+ urb_packs = min(urb_packs, 1U << subs->syncinterval);
/* decide how many packets to be used */
if (is_playback) {
@@ -2124,8 +2126,8 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
fp = list_entry(p, struct audioformat, list);
snd_iprintf(buffer, " Interface %d\n", fp->iface);
snd_iprintf(buffer, " Altset %d\n", fp->altsetting);
- snd_iprintf(buffer, " Format: %#x (%d bits)\n",
- fp->format, snd_pcm_format_width(fp->format));
+ snd_iprintf(buffer, " Format: %s\n",
+ snd_pcm_format_name(fp->format));
snd_iprintf(buffer, " Channels: %d\n", fp->channels);
snd_iprintf(buffer, " Endpoint: %d %s (%s)\n",
fp->endpoint & USB_ENDPOINT_NUMBER_MASK,
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 2fb35cc22a3..0eff19ceb7e 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -45,6 +45,7 @@
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/usb.h>
+#include <linux/wait.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
#include <sound/asequencer.h>
@@ -62,6 +63,9 @@
*/
#define ERROR_DELAY_JIFFIES (HZ / 10)
+#define OUTPUT_URBS 7
+#define INPUT_URBS 7
+
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("USB Audio/MIDI helper module");
@@ -90,7 +94,7 @@ struct snd_usb_midi_endpoint;
struct usb_protocol_ops {
void (*input)(struct snd_usb_midi_in_endpoint*, uint8_t*, int);
- void (*output)(struct snd_usb_midi_out_endpoint*);
+ void (*output)(struct snd_usb_midi_out_endpoint *ep, struct urb *urb);
void (*output_packet)(struct urb*, uint8_t, uint8_t, uint8_t, uint8_t);
void (*init_out_endpoint)(struct snd_usb_midi_out_endpoint*);
void (*finish_out_endpoint)(struct snd_usb_midi_out_endpoint*);
@@ -116,11 +120,15 @@ struct snd_usb_midi {
struct snd_usb_midi_out_endpoint {
struct snd_usb_midi* umidi;
- struct urb* urb;
- int urb_active;
+ struct out_urb_context {
+ struct urb *urb;
+ struct snd_usb_midi_out_endpoint *ep;
+ } urbs[OUTPUT_URBS];
+ unsigned int active_urbs;
+ unsigned int drain_urbs;
int max_transfer; /* size of urb buffer */
struct tasklet_struct tasklet;
-
+ unsigned int next_urb;
spinlock_t buffer_lock;
struct usbmidi_out_port {
@@ -139,11 +147,13 @@ struct snd_usb_midi_out_endpoint {
uint8_t data[2];
} ports[0x10];
int current_port;
+
+ wait_queue_head_t drain_wait;
};
struct snd_usb_midi_in_endpoint {
struct snd_usb_midi* umidi;
- struct urb* urb;
+ struct urb* urbs[INPUT_URBS];
struct usbmidi_in_port {
struct snd_rawmidi_substream *substream;
u8 running_status_length;
@@ -251,10 +261,17 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb)
static void snd_usbmidi_out_urb_complete(struct urb* urb)
{
- struct snd_usb_midi_out_endpoint* ep = urb->context;
+ struct out_urb_context *context = urb->context;
+ struct snd_usb_midi_out_endpoint* ep = context->ep;
+ unsigned int urb_index;
spin_lock(&ep->buffer_lock);
- ep->urb_active = 0;
+ urb_index = context - ep->urbs;
+ ep->active_urbs &= ~(1 << urb_index);
+ if (unlikely(ep->drain_urbs)) {
+ ep->drain_urbs &= ~(1 << urb_index);
+ wake_up(&ep->drain_wait);
+ }
spin_unlock(&ep->buffer_lock);
if (urb->status < 0) {
int err = snd_usbmidi_urb_error(urb->status);
@@ -274,24 +291,38 @@ static void snd_usbmidi_out_urb_complete(struct urb* urb)
*/
static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep)
{
- struct urb* urb = ep->urb;
+ unsigned int urb_index;
+ struct urb* urb;
unsigned long flags;
spin_lock_irqsave(&ep->buffer_lock, flags);
- if (ep->urb_active || ep->umidi->chip->shutdown) {
+ if (ep->umidi->chip->shutdown) {
spin_unlock_irqrestore(&ep->buffer_lock, flags);
return;
}
- urb->transfer_buffer_length = 0;
- ep->umidi->usb_protocol_ops->output(ep);
+ urb_index = ep->next_urb;
+ for (;;) {
+ if (!(ep->active_urbs & (1 << urb_index))) {
+ urb = ep->urbs[urb_index].urb;
+ urb->transfer_buffer_length = 0;
+ ep->umidi->usb_protocol_ops->output(ep, urb);
+ if (urb->transfer_buffer_length == 0)
+ break;
- if (urb->transfer_buffer_length > 0) {
- dump_urb("sending", urb->transfer_buffer,
- urb->transfer_buffer_length);
- urb->dev = ep->umidi->chip->dev;
- ep->urb_active = snd_usbmidi_submit_urb(urb, GFP_ATOMIC) >= 0;
+ dump_urb("sending", urb->transfer_buffer,
+ urb->transfer_buffer_length);
+ urb->dev = ep->umidi->chip->dev;
+ if (snd_usbmidi_submit_urb(urb, GFP_ATOMIC) < 0)
+ break;
+ ep->active_urbs |= 1 << urb_index;
+ }
+ if (++urb_index >= OUTPUT_URBS)
+ urb_index = 0;
+ if (urb_index == ep->next_urb)
+ break;
}
+ ep->next_urb = urb_index;
spin_unlock_irqrestore(&ep->buffer_lock, flags);
}
@@ -306,7 +337,7 @@ static void snd_usbmidi_out_tasklet(unsigned long data)
static void snd_usbmidi_error_timer(unsigned long data)
{
struct snd_usb_midi *umidi = (struct snd_usb_midi *)data;
- int i;
+ unsigned int i, j;
spin_lock(&umidi->disc_lock);
if (umidi->disconnected) {
@@ -317,8 +348,10 @@ static void snd_usbmidi_error_timer(unsigned long data)
struct snd_usb_midi_in_endpoint *in = umidi->endpoints[i].in;
if (in && in->error_resubmit) {
in->error_resubmit = 0;
- in->urb->dev = umidi->chip->dev;
- snd_usbmidi_submit_urb(in->urb, GFP_ATOMIC);
+ for (j = 0; j < INPUT_URBS; ++j) {
+ in->urbs[j]->dev = umidi->chip->dev;
+ snd_usbmidi_submit_urb(in->urbs[j], GFP_ATOMIC);
+ }
}
if (umidi->endpoints[i].out)
snd_usbmidi_do_output(umidi->endpoints[i].out);
@@ -330,13 +363,14 @@ static void snd_usbmidi_error_timer(unsigned long data)
static int send_bulk_static_data(struct snd_usb_midi_out_endpoint* ep,
const void *data, int len)
{
- int err;
+ int err = 0;
void *buf = kmemdup(data, len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
dump_urb("sending", buf, len);
- err = usb_bulk_msg(ep->umidi->chip->dev, ep->urb->pipe, buf, len,
- NULL, 250);
+ if (ep->urbs[0].urb)
+ err = usb_bulk_msg(ep->umidi->chip->dev, ep->urbs[0].urb->pipe,
+ buf, len, NULL, 250);
kfree(buf);
return err;
}
@@ -554,9 +588,9 @@ static void snd_usbmidi_transmit_byte(struct usbmidi_out_port* port,
}
}
-static void snd_usbmidi_standard_output(struct snd_usb_midi_out_endpoint* ep)
+static void snd_usbmidi_standard_output(struct snd_usb_midi_out_endpoint* ep,
+ struct urb *urb)
{
- struct urb* urb = ep->urb;
int p;
/* FIXME: lower-numbered ports can starve higher-numbered ports */
@@ -613,14 +647,15 @@ static void snd_usbmidi_novation_input(struct snd_usb_midi_in_endpoint* ep,
snd_usbmidi_input_data(ep, 0, &buffer[2], buffer[0] - 1);
}
-static void snd_usbmidi_novation_output(struct snd_usb_midi_out_endpoint* ep)
+static void snd_usbmidi_novation_output(struct snd_usb_midi_out_endpoint* ep,
+ struct urb *urb)
{
uint8_t* transfer_buffer;
int count;
if (!ep->ports[0].active)
return;
- transfer_buffer = ep->urb->transfer_buffer;
+ transfer_buffer = urb->transfer_buffer;
count = snd_rawmidi_transmit(ep->ports[0].substream,
&transfer_buffer[2],
ep->max_transfer - 2);
@@ -630,7 +665,7 @@ static void snd_usbmidi_novation_output(struct snd_usb_midi_out_endpoint* ep)
}
transfer_buffer[0] = 0;
transfer_buffer[1] = count;
- ep->urb->transfer_buffer_length = 2 + count;
+ urb->transfer_buffer_length = 2 + count;
}
static struct usb_protocol_ops snd_usbmidi_novation_ops = {
@@ -648,20 +683,21 @@ static void snd_usbmidi_raw_input(struct snd_usb_midi_in_endpoint* ep,
snd_usbmidi_input_data(ep, 0, buffer, buffer_length);
}
-static void snd_usbmidi_raw_output(struct snd_usb_midi_out_endpoint* ep)
+static void snd_usbmidi_raw_output(struct snd_usb_midi_out_endpoint* ep,
+ struct urb *urb)
{
int count;
if (!ep->ports[0].active)
return;
count = snd_rawmidi_transmit(ep->ports[0].substream,
- ep->urb->transfer_buffer,
+ urb->transfer_buffer,
ep->max_transfer);
if (count < 1) {
ep->ports[0].active = 0;
return;
}
- ep->urb->transfer_buffer_length = count;
+ urb->transfer_buffer_length = count;
}
static struct usb_protocol_ops snd_usbmidi_raw_ops = {
@@ -681,23 +717,25 @@ static void snd_usbmidi_us122l_input(struct snd_usb_midi_in_endpoint *ep,
snd_usbmidi_input_data(ep, 0, buffer, buffer_length);
}
-static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep)
+static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
+ struct urb *urb)
{
int count;
if (!ep->ports[0].active)
return;
- count = ep->urb->dev->speed == USB_SPEED_HIGH ? 1 : 2;
+ count = snd_usb_get_speed(ep->umidi->chip->dev) == USB_SPEED_HIGH
+ ? 1 : 2;
count = snd_rawmidi_transmit(ep->ports[0].substream,
- ep->urb->transfer_buffer,
+ urb->transfer_buffer,
count);
if (count < 1) {
ep->ports[0].active = 0;
return;
}
- memset(ep->urb->transfer_buffer + count, 0xFD, 9 - count);
- ep->urb->transfer_buffer_length = count;
+ memset(urb->transfer_buffer + count, 0xFD, 9 - count);
+ urb->transfer_buffer_length = count;
}
static struct usb_protocol_ops snd_usbmidi_122l_ops = {
@@ -786,10 +824,11 @@ static void snd_usbmidi_emagic_input(struct snd_usb_midi_in_endpoint* ep,
}
}
-static void snd_usbmidi_emagic_output(struct snd_usb_midi_out_endpoint* ep)
+static void snd_usbmidi_emagic_output(struct snd_usb_midi_out_endpoint* ep,
+ struct urb *urb)
{
int port0 = ep->current_port;
- uint8_t* buf = ep->urb->transfer_buffer;
+ uint8_t* buf = urb->transfer_buffer;
int buf_free = ep->max_transfer;
int length, i;
@@ -829,7 +868,7 @@ static void snd_usbmidi_emagic_output(struct snd_usb_midi_out_endpoint* ep)
*buf = 0xff;
--buf_free;
}
- ep->urb->transfer_buffer_length = ep->max_transfer - buf_free;
+ urb->transfer_buffer_length = ep->max_transfer - buf_free;
}
static struct usb_protocol_ops snd_usbmidi_emagic_ops = {
@@ -884,6 +923,35 @@ static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream,
}
}
+static void snd_usbmidi_output_drain(struct snd_rawmidi_substream *substream)
+{
+ struct usbmidi_out_port* port = substream->runtime->private_data;
+ struct snd_usb_midi_out_endpoint *ep = port->ep;
+ unsigned int drain_urbs;
+ DEFINE_WAIT(wait);
+ long timeout = msecs_to_jiffies(50);
+
+ /*
+ * The substream buffer is empty, but some data might still be in the
+ * currently active URBs, so we have to wait for those to complete.
+ */
+ spin_lock_irq(&ep->buffer_lock);
+ drain_urbs = ep->active_urbs;
+ if (drain_urbs) {
+ ep->drain_urbs |= drain_urbs;
+ do {
+ prepare_to_wait(&ep->drain_wait, &wait,
+ TASK_UNINTERRUPTIBLE);
+ spin_unlock_irq(&ep->buffer_lock);
+ timeout = schedule_timeout(timeout);
+ spin_lock_irq(&ep->buffer_lock);
+ drain_urbs &= ep->drain_urbs;
+ } while (drain_urbs && timeout);
+ finish_wait(&ep->drain_wait, &wait);
+ }
+ spin_unlock_irq(&ep->buffer_lock);
+}
+
static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream)
{
return 0;
@@ -908,6 +976,7 @@ static struct snd_rawmidi_ops snd_usbmidi_output_ops = {
.open = snd_usbmidi_output_open,
.close = snd_usbmidi_output_close,
.trigger = snd_usbmidi_output_trigger,
+ .drain = snd_usbmidi_output_drain,
};
static struct snd_rawmidi_ops snd_usbmidi_input_ops = {
@@ -916,19 +985,26 @@ static struct snd_rawmidi_ops snd_usbmidi_input_ops = {
.trigger = snd_usbmidi_input_trigger
};
+static void free_urb_and_buffer(struct snd_usb_midi *umidi, struct urb *urb,
+ unsigned int buffer_length)
+{
+ usb_buffer_free(umidi->chip->dev, buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
+ usb_free_urb(urb);
+}
+
/*
* Frees an input endpoint.
* May be called when ep hasn't been initialized completely.
*/
static void snd_usbmidi_in_endpoint_delete(struct snd_usb_midi_in_endpoint* ep)
{
- if (ep->urb) {
- usb_buffer_free(ep->umidi->chip->dev,
- ep->urb->transfer_buffer_length,
- ep->urb->transfer_buffer,
- ep->urb->transfer_dma);
- usb_free_urb(ep->urb);
- }
+ unsigned int i;
+
+ for (i = 0; i < INPUT_URBS; ++i)
+ if (ep->urbs[i])
+ free_urb_and_buffer(ep->umidi, ep->urbs[i],
+ ep->urbs[i]->transfer_buffer_length);
kfree(ep);
}
@@ -943,6 +1019,7 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi,
void* buffer;
unsigned int pipe;
int length;
+ unsigned int i;
rep->in = NULL;
ep = kzalloc(sizeof(*ep), GFP_KERNEL);
@@ -950,30 +1027,36 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi* umidi,
return -ENOMEM;
ep->umidi = umidi;
- ep->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!ep->urb) {
- snd_usbmidi_in_endpoint_delete(ep);
- return -ENOMEM;
+ for (i = 0; i < INPUT_URBS; ++i) {
+ ep->urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ep->urbs[i]) {
+ snd_usbmidi_in_endpoint_delete(ep);
+ return -ENOMEM;
+ }
}
if (ep_info->in_interval)
pipe = usb_rcvintpipe(umidi->chip->dev, ep_info->in_ep);
else
pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->in_ep);
length = usb_maxpacket(umidi->chip->dev, pipe, 0);
- buffer = usb_buffer_alloc(umidi->chip->dev, length, GFP_KERNEL,
- &ep->urb->transfer_dma);
- if (!buffer) {
- snd_usbmidi_in_endpoint_delete(ep);
- return -ENOMEM;
+ for (i = 0; i < INPUT_URBS; ++i) {
+ buffer = usb_buffer_alloc(umidi->chip->dev, length, GFP_KERNEL,
+ &ep->urbs[i]->transfer_dma);
+ if (!buffer) {
+ snd_usbmidi_in_endpoint_delete(ep);
+ return -ENOMEM;
+ }
+ if (ep_info->in_interval)
+ usb_fill_int_urb(ep->urbs[i], umidi->chip->dev,
+ pipe, buffer, length,
+ snd_usbmidi_in_urb_complete,
+ ep, ep_info->in_interval);
+ else
+ usb_fill_bulk_urb(ep->urbs[i], umidi->chip->dev,
+ pipe, buffer, length,
+ snd_usbmidi_in_urb_complete, ep);
+ ep->urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
}
- if (ep_info->in_interval)
- usb_fill_int_urb(ep->urb, umidi->chip->dev, pipe, buffer,
- length, snd_usbmidi_in_urb_complete, ep,
- ep_info->in_interval);
- else
- usb_fill_bulk_urb(ep->urb, umidi->chip->dev, pipe, buffer,
- length, snd_usbmidi_in_urb_complete, ep);
- ep->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
rep->in = ep;
return 0;
@@ -994,12 +1077,12 @@ static unsigned int snd_usbmidi_count_bits(unsigned int x)
*/
static void snd_usbmidi_out_endpoint_delete(struct snd_usb_midi_out_endpoint* ep)
{
- if (ep->urb) {
- usb_buffer_free(ep->umidi->chip->dev, ep->max_transfer,
- ep->urb->transfer_buffer,
- ep->urb->transfer_dma);
- usb_free_urb(ep->urb);
- }
+ unsigned int i;
+
+ for (i = 0; i < OUTPUT_URBS; ++i)
+ if (ep->urbs[i].urb)
+ free_urb_and_buffer(ep->umidi, ep->urbs[i].urb,
+ ep->max_transfer);
kfree(ep);
}
@@ -1011,7 +1094,7 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
struct snd_usb_midi_endpoint* rep)
{
struct snd_usb_midi_out_endpoint* ep;
- int i;
+ unsigned int i;
unsigned int pipe;
void* buffer;
@@ -1021,38 +1104,46 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
return -ENOMEM;
ep->umidi = umidi;
- ep->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!ep->urb) {
- snd_usbmidi_out_endpoint_delete(ep);
- return -ENOMEM;
+ for (i = 0; i < OUTPUT_URBS; ++i) {
+ ep->urbs[i].urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ep->urbs[i].urb) {
+ snd_usbmidi_out_endpoint_delete(ep);
+ return -ENOMEM;
+ }
+ ep->urbs[i].ep = ep;
}
if (ep_info->out_interval)
pipe = usb_sndintpipe(umidi->chip->dev, ep_info->out_ep);
else
pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->out_ep);
if (umidi->chip->usb_id == USB_ID(0x0a92, 0x1020)) /* ESI M4U */
- /* FIXME: we need more URBs to get reasonable bandwidth here: */
ep->max_transfer = 4;
else
ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1);
- buffer = usb_buffer_alloc(umidi->chip->dev, ep->max_transfer,
- GFP_KERNEL, &ep->urb->transfer_dma);
- if (!buffer) {
- snd_usbmidi_out_endpoint_delete(ep);
- return -ENOMEM;
+ for (i = 0; i < OUTPUT_URBS; ++i) {
+ buffer = usb_buffer_alloc(umidi->chip->dev,
+ ep->max_transfer, GFP_KERNEL,
+ &ep->urbs[i].urb->transfer_dma);
+ if (!buffer) {
+ snd_usbmidi_out_endpoint_delete(ep);
+ return -ENOMEM;
+ }
+ if (ep_info->out_interval)
+ usb_fill_int_urb(ep->urbs[i].urb, umidi->chip->dev,
+ pipe, buffer, ep->max_transfer,
+ snd_usbmidi_out_urb_complete,
+ &ep->urbs[i], ep_info->out_interval);
+ else
+ usb_fill_bulk_urb(ep->urbs[i].urb, umidi->chip->dev,
+ pipe, buffer, ep->max_transfer,
+ snd_usbmidi_out_urb_complete,
+ &ep->urbs[i]);
+ ep->urbs[i].urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
}
- if (ep_info->out_interval)
- usb_fill_int_urb(ep->urb, umidi->chip->dev, pipe, buffer,
- ep->max_transfer, snd_usbmidi_out_urb_complete,
- ep, ep_info->out_interval);
- else
- usb_fill_bulk_urb(ep->urb, umidi->chip->dev,
- pipe, buffer, ep->max_transfer,
- snd_usbmidi_out_urb_complete, ep);
- ep->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
spin_lock_init(&ep->buffer_lock);
tasklet_init(&ep->tasklet, snd_usbmidi_out_tasklet, (unsigned long)ep);
+ init_waitqueue_head(&ep->drain_wait);
for (i = 0; i < 0x10; ++i)
if (ep_info->out_cables & (1 << i)) {
@@ -1090,7 +1181,7 @@ static void snd_usbmidi_free(struct snd_usb_midi* umidi)
void snd_usbmidi_disconnect(struct list_head* p)
{
struct snd_usb_midi* umidi;
- int i;
+ unsigned int i, j;
umidi = list_entry(p, struct snd_usb_midi, list);
/*
@@ -1105,13 +1196,15 @@ void snd_usbmidi_disconnect(struct list_head* p)
struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
if (ep->out)
tasklet_kill(&ep->out->tasklet);
- if (ep->out && ep->out->urb) {
- usb_kill_urb(ep->out->urb);
+ if (ep->out) {
+ for (j = 0; j < OUTPUT_URBS; ++j)
+ usb_kill_urb(ep->out->urbs[j].urb);
if (umidi->usb_protocol_ops->finish_out_endpoint)
umidi->usb_protocol_ops->finish_out_endpoint(ep->out);
}
if (ep->in)
- usb_kill_urb(ep->in->urb);
+ for (j = 0; j < INPUT_URBS; ++j)
+ usb_kill_urb(ep->in->urbs[j]);
/* free endpoints here; later call can result in Oops */
if (ep->out) {
snd_usbmidi_out_endpoint_delete(ep->out);
@@ -1692,20 +1785,25 @@ static int snd_usbmidi_create_rawmidi(struct snd_usb_midi* umidi,
void snd_usbmidi_input_stop(struct list_head* p)
{
struct snd_usb_midi* umidi;
- int i;
+ unsigned int i, j;
umidi = list_entry(p, struct snd_usb_midi, list);
for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
if (ep->in)
- usb_kill_urb(ep->in->urb);
+ for (j = 0; j < INPUT_URBS; ++j)
+ usb_kill_urb(ep->in->urbs[j]);
}
}
static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep)
{
- if (ep) {
- struct urb* urb = ep->urb;
+ unsigned int i;
+
+ if (!ep)
+ return;
+ for (i = 0; i < INPUT_URBS; ++i) {
+ struct urb* urb = ep->urbs[i];
urb->dev = ep->umidi->chip->dev;
snd_usbmidi_submit_urb(urb, GFP_KERNEL);
}
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index ec9cdf98692..ab5a3ac2ac4 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -86,6 +86,7 @@ struct usb_mixer_interface {
u8 rc_buffer[6];
u8 audigy2nx_leds[3];
+ u8 xonar_u1_status;
};
@@ -461,7 +462,7 @@ static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *_tlv)
{
struct usb_mixer_elem_info *cval = kcontrol->private_data;
- DECLARE_TLV_DB_SCALE(scale, 0, 0, 0);
+ DECLARE_TLV_DB_MINMAX(scale, 0, 0);
if (size < sizeof(scale))
return -ENOMEM;
@@ -469,7 +470,16 @@ static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
* while ALSA TLV contains in 1/100 dB unit
*/
scale[2] = (convert_signed_value(cval, cval->min) * 100) / 256;
- scale[3] = (convert_signed_value(cval, cval->res) * 100) / 256;
+ scale[3] = (convert_signed_value(cval, cval->max) * 100) / 256;
+ if (scale[3] <= scale[2]) {
+ /* something is wrong; assume it's either from/to 0dB */
+ if (scale[2] < 0)
+ scale[3] = 0;
+ else if (scale[2] > 0)
+ scale[2] = 0;
+ else /* totally crap, return an error */
+ return -EINVAL;
+ }
if (copy_to_user(_tlv, scale, sizeof(scale)))
return -EFAULT;
return 0;
@@ -2033,6 +2043,58 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
}
}
+static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02);
+ return 0;
+}
+
+static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+ u8 old_status, new_status;
+ int err, changed;
+
+ old_status = mixer->xonar_u1_status;
+ if (ucontrol->value.integer.value[0])
+ new_status = old_status | 0x02;
+ else
+ new_status = old_status & ~0x02;
+ changed = new_status != old_status;
+ err = snd_usb_ctl_msg(mixer->chip->dev,
+ usb_sndctrlpipe(mixer->chip->dev, 0), 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ 50, 0, &new_status, 1, 100);
+ if (err < 0)
+ return err;
+ mixer->xonar_u1_status = new_status;
+ return changed;
+}
+
+static struct snd_kcontrol_new snd_xonar_u1_output_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_xonar_u1_switch_get,
+ .put = snd_xonar_u1_switch_put,
+};
+
+static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
+{
+ int err;
+
+ err = snd_ctl_add(mixer->chip->card,
+ snd_ctl_new1(&snd_xonar_u1_output_switch, mixer));
+ if (err < 0)
+ return err;
+ mixer->xonar_u1_status = 0x05;
+ return 0;
+}
+
int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
int ignore_error)
{
@@ -2075,6 +2137,13 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
snd_audigy2nx_proc_read);
}
+ if (mixer->chip->usb_id == USB_ID(0x0b05, 0x1739) ||
+ mixer->chip->usb_id == USB_ID(0x0b05, 0x1743)) {
+ err = snd_xonar_u1_controls_create(mixer);
+ if (err < 0)
+ goto _error;
+ }
+
err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops);
if (err < 0)
goto _error;
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile
index 5457192e1b4..bdd3b7ecad0 100644
--- a/tools/perf/Documentation/Makefile
+++ b/tools/perf/Documentation/Makefile
@@ -35,7 +35,7 @@ man7dir=$(mandir)/man7
# DESTDIR=
ASCIIDOC=asciidoc
-ASCIIDOC_EXTRA =
+ASCIIDOC_EXTRA = --unsafe
MANPAGE_XSL = manpage-normal.xsl
XMLTO_EXTRA =
INSTALL?=install
diff --git a/tools/perf/Documentation/examples.txt b/tools/perf/Documentation/examples.txt
new file mode 100644
index 00000000000..8eb6c489fb1
--- /dev/null
+++ b/tools/perf/Documentation/examples.txt
@@ -0,0 +1,225 @@
+
+ ------------------------------
+ ****** perf by examples ******
+ ------------------------------
+
+[ From an e-mail by Ingo Molnar, http://lkml.org/lkml/2009/8/4/346 ]
+
+
+First, discovery/enumeration of available counters can be done via
+'perf list':
+
+titan:~> perf list
+ [...]
+ kmem:kmalloc [Tracepoint event]
+ kmem:kmem_cache_alloc [Tracepoint event]
+ kmem:kmalloc_node [Tracepoint event]
+ kmem:kmem_cache_alloc_node [Tracepoint event]
+ kmem:kfree [Tracepoint event]
+ kmem:kmem_cache_free [Tracepoint event]
+ kmem:mm_page_free_direct [Tracepoint event]
+ kmem:mm_pagevec_free [Tracepoint event]
+ kmem:mm_page_alloc [Tracepoint event]
+ kmem:mm_page_alloc_zone_locked [Tracepoint event]
+ kmem:mm_page_pcpu_drain [Tracepoint event]
+ kmem:mm_page_alloc_extfrag [Tracepoint event]
+
+Then any (or all) of the above event sources can be activated and
+measured. For example the page alloc/free properties of a 'hackbench
+run' are:
+
+ titan:~> perf stat -e kmem:mm_page_pcpu_drain -e kmem:mm_page_alloc
+ -e kmem:mm_pagevec_free -e kmem:mm_page_free_direct ./hackbench 10
+ Time: 0.575
+
+ Performance counter stats for './hackbench 10':
+
+ 13857 kmem:mm_page_pcpu_drain
+ 27576 kmem:mm_page_alloc
+ 6025 kmem:mm_pagevec_free
+ 20934 kmem:mm_page_free_direct
+
+ 0.613972165 seconds time elapsed
+
+You can observe the statistical properties as well, by using the
+'repeat the workload N times' feature of perf stat:
+
+ titan:~> perf stat --repeat 5 -e kmem:mm_page_pcpu_drain -e
+ kmem:mm_page_alloc -e kmem:mm_pagevec_free -e
+ kmem:mm_page_free_direct ./hackbench 10
+ Time: 0.627
+ Time: 0.644
+ Time: 0.564
+ Time: 0.559
+ Time: 0.626
+
+ Performance counter stats for './hackbench 10' (5 runs):
+
+ 12920 kmem:mm_page_pcpu_drain ( +- 3.359% )
+ 25035 kmem:mm_page_alloc ( +- 3.783% )
+ 6104 kmem:mm_pagevec_free ( +- 0.934% )
+ 18376 kmem:mm_page_free_direct ( +- 4.941% )
+
+ 0.643954516 seconds time elapsed ( +- 2.363% )
+
+Furthermore, these tracepoints can be used to sample the workload as
+well. For example the page allocations done by a 'git gc' can be
+captured the following way:
+
+ titan:~/git> perf record -f -e kmem:mm_page_alloc -c 1 ./git gc
+ Counting objects: 1148, done.
+ Delta compression using up to 2 threads.
+ Compressing objects: 100% (450/450), done.
+ Writing objects: 100% (1148/1148), done.
+ Total 1148 (delta 690), reused 1148 (delta 690)
+ [ perf record: Captured and wrote 0.267 MB perf.data (~11679 samples) ]
+
+To check which functions generated page allocations:
+
+ titan:~/git> perf report
+ # Samples: 10646
+ #
+ # Overhead Command Shared Object
+ # ........ ............... ..........................
+ #
+ 23.57% git-repack /lib64/libc-2.5.so
+ 21.81% git /lib64/libc-2.5.so
+ 14.59% git ./git
+ 11.79% git-repack ./git
+ 7.12% git /lib64/ld-2.5.so
+ 3.16% git-repack /lib64/libpthread-2.5.so
+ 2.09% git-repack /bin/bash
+ 1.97% rm /lib64/libc-2.5.so
+ 1.39% mv /lib64/ld-2.5.so
+ 1.37% mv /lib64/libc-2.5.so
+ 1.12% git-repack /lib64/ld-2.5.so
+ 0.95% rm /lib64/ld-2.5.so
+ 0.90% git-update-serv /lib64/libc-2.5.so
+ 0.73% git-update-serv /lib64/ld-2.5.so
+ 0.68% perf /lib64/libpthread-2.5.so
+ 0.64% git-repack /usr/lib64/libz.so.1.2.3
+
+Or to see it on a more finegrained level:
+
+titan:~/git> perf report --sort comm,dso,symbol
+# Samples: 10646
+#
+# Overhead Command Shared Object Symbol
+# ........ ............... .......................... ......
+#
+ 9.35% git-repack ./git [.] insert_obj_hash
+ 9.12% git ./git [.] insert_obj_hash
+ 7.31% git /lib64/libc-2.5.so [.] memcpy
+ 6.34% git-repack /lib64/libc-2.5.so [.] _int_malloc
+ 6.24% git-repack /lib64/libc-2.5.so [.] memcpy
+ 5.82% git-repack /lib64/libc-2.5.so [.] __GI___fork
+ 5.47% git /lib64/libc-2.5.so [.] _int_malloc
+ 2.99% git /lib64/libc-2.5.so [.] memset
+
+Furthermore, call-graph sampling can be done too, of page
+allocations - to see precisely what kind of page allocations there
+are:
+
+ titan:~/git> perf record -f -g -e kmem:mm_page_alloc -c 1 ./git gc
+ Counting objects: 1148, done.
+ Delta compression using up to 2 threads.
+ Compressing objects: 100% (450/450), done.
+ Writing objects: 100% (1148/1148), done.
+ Total 1148 (delta 690), reused 1148 (delta 690)
+ [ perf record: Captured and wrote 0.963 MB perf.data (~42069 samples) ]
+
+ titan:~/git> perf report -g
+ # Samples: 10686
+ #
+ # Overhead Command Shared Object
+ # ........ ............... ..........................
+ #
+ 23.25% git-repack /lib64/libc-2.5.so
+ |
+ |--50.00%-- _int_free
+ |
+ |--37.50%-- __GI___fork
+ | make_child
+ |
+ |--12.50%-- ptmalloc_unlock_all2
+ | make_child
+ |
+ --6.25%-- __GI_strcpy
+ 21.61% git /lib64/libc-2.5.so
+ |
+ |--30.00%-- __GI_read
+ | |
+ | --83.33%-- git_config_from_file
+ | git_config
+ | |
+ [...]
+
+Or you can observe the whole system's page allocations for 10
+seconds:
+
+titan:~/git> perf stat -a -e kmem:mm_page_pcpu_drain -e
+kmem:mm_page_alloc -e kmem:mm_pagevec_free -e
+kmem:mm_page_free_direct sleep 10
+
+ Performance counter stats for 'sleep 10':
+
+ 171585 kmem:mm_page_pcpu_drain
+ 322114 kmem:mm_page_alloc
+ 73623 kmem:mm_pagevec_free
+ 254115 kmem:mm_page_free_direct
+
+ 10.000591410 seconds time elapsed
+
+Or observe how fluctuating the page allocations are, via statistical
+analysis done over ten 1-second intervals:
+
+ titan:~/git> perf stat --repeat 10 -a -e kmem:mm_page_pcpu_drain -e
+ kmem:mm_page_alloc -e kmem:mm_pagevec_free -e
+ kmem:mm_page_free_direct sleep 1
+
+ Performance counter stats for 'sleep 1' (10 runs):
+
+ 17254 kmem:mm_page_pcpu_drain ( +- 3.709% )
+ 34394 kmem:mm_page_alloc ( +- 4.617% )
+ 7509 kmem:mm_pagevec_free ( +- 4.820% )
+ 25653 kmem:mm_page_free_direct ( +- 3.672% )
+
+ 1.058135029 seconds time elapsed ( +- 3.089% )
+
+Or you can annotate the recorded 'git gc' run on a per symbol basis
+and check which instructions/source-code generated page allocations:
+
+ titan:~/git> perf annotate __GI___fork
+ ------------------------------------------------
+ Percent | Source code & Disassembly of libc-2.5.so
+ ------------------------------------------------
+ :
+ :
+ : Disassembly of section .plt:
+ : Disassembly of section .text:
+ :
+ : 00000031a2e95560 <__fork>:
+ [...]
+ 0.00 : 31a2e95602: b8 38 00 00 00 mov $0x38,%eax
+ 0.00 : 31a2e95607: 0f 05 syscall
+ 83.42 : 31a2e95609: 48 3d 00 f0 ff ff cmp $0xfffffffffffff000,%rax
+ 0.00 : 31a2e9560f: 0f 87 4d 01 00 00 ja 31a2e95762 <__fork+0x202>
+ 0.00 : 31a2e95615: 85 c0 test %eax,%eax
+
+( this shows that 83.42% of __GI___fork's page allocations come from
+ the 0x38 system call it performs. )
+
+etc. etc. - a lot more is possible. I could list a dozen of
+other different usecases straight away - neither of which is
+possible via /proc/vmstat.
+
+/proc/vmstat is not in the same league really, in terms of
+expressive power of system analysis and performance
+analysis.
+
+All that the above results needed were those new tracepoints
+in include/tracing/events/kmem.h.
+
+ Ingo
+
+
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index 1dbc1eeb4c0..6be696b0a2b 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -29,13 +29,67 @@ OPTIONS
Select the PMU event. Selection can be a symbolic event name
(use 'perf list' to list all events) or a raw PMU
event (eventsel+umask) in the form of rNNN where NNN is a
- hexadecimal event descriptor.
+ hexadecimal event descriptor.
-a::
- system-wide collection
+ System-wide collection.
-l::
- scale counter values
+ Scale counter values.
+
+-p::
+--pid=::
+ Record events on existing pid.
+
+-r::
+--realtime=::
+ Collect data with this RT SCHED_FIFO priority.
+-A::
+--append::
+ Append to the output file to do incremental profiling.
+
+-f::
+--force::
+ Overwrite existing data file.
+
+-c::
+--count=::
+ Event period to sample.
+
+-o::
+--output=::
+ Output file name.
+
+-i::
+--inherit::
+ Child tasks inherit counters.
+-F::
+--freq=::
+ Profile at this frequency.
+
+-m::
+--mmap-pages=::
+ Number of mmap data pages.
+
+-g::
+--call-graph::
+ Do call-graph (stack chain/backtrace) recording.
+
+-v::
+--verbose::
+ Be more verbose (show counter open errors, etc).
+
+-s::
+--stat::
+ Per thread counts.
+
+-d::
+--data::
+ Sample addresses.
+
+-n::
+--no-samples::
+ Don't sample.
SEE ALSO
--------
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt
index 0d74346d21a..484080dd5b6 100644
--- a/tools/perf/Documentation/perf-stat.txt
+++ b/tools/perf/Documentation/perf-stat.txt
@@ -40,7 +40,7 @@ OPTIONS
-a::
system-wide collection
--S::
+-c::
scale counter values
EXAMPLES
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index 539d0128972..4a7d558dc30 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -3,36 +3,122 @@ perf-top(1)
NAME
----
-perf-top - Run a command and profile it
+perf-top - System profiling tool.
SYNOPSIS
--------
[verse]
-'perf top' [-e <EVENT> | --event=EVENT] [-l] [-a] <command>
+'perf top' [-e <EVENT> | --event=EVENT] [<options>]
DESCRIPTION
-----------
-This command runs a command and gathers a performance counter profile
-from it.
+This command generates and displays a performance counter profile in realtime.
OPTIONS
-------
-<command>...::
- Any command you can specify in a shell.
+-a::
+--all-cpus::
+ System-wide collection. (default)
+
+-c <count>::
+--count=<count>::
+ Event period to sample.
+
+-C <cpu>::
+--CPU=<cpu>::
+ CPU to profile.
+
+-d <seconds>::
+--delay=<seconds>::
+ Number of seconds to delay between refreshes.
--e::
---event=::
+-e <event>::
+--event=<event>::
Select the PMU event. Selection can be a symbolic event name
(use 'perf list' to list all events) or a raw PMU
event (eventsel+umask) in the form of rNNN where NNN is a
- hexadecimal event descriptor.
+ hexadecimal event descriptor.
--a::
- system-wide collection
+-E <entries>::
+--entries=<entries>::
+ Display this many functions.
+
+-f <count>::
+--count-filter=<count>::
+ Only display functions with more events than this.
+
+-F <freq>::
+--freq=<freq>::
+ Profile at this frequency.
+
+-i::
+--inherit::
+ Child tasks inherit counters, only makes sens with -p option.
+
+-k <path>::
+--vmlinux=<path>::
+ Path to vmlinux. Required for annotation functionality.
+
+-m <pages>::
+--mmap-pages=<pages>::
+ Number of mmapped data pages.
+
+-p <pid>::
+--pid=<pid>::
+ Profile events on existing pid.
+
+-r <priority>::
+--realtime=<priority>::
+ Collect data with this RT SCHED_FIFO priority.
+
+-s <symbol>::
+--sym-annotate=<symbol>::
+ Annotate this symbol. Requires -k option.
+
+-v::
+--verbose::
+ Be more verbose (show counter open errors, etc).
+
+-z::
+--zero::
+ Zero history across display updates.
+
+INTERACTIVE PROMPTING KEYS
+--------------------------
+
+[d]::
+ Display refresh delay.
+
+[e]::
+ Number of entries to display.
+
+[E]::
+ Event to display when multiple counters are active.
+
+[f]::
+ Profile display filter (>= hit count).
+
+[F]::
+ Annotation display filter (>= % of total).
+
+[s]::
+ Annotate symbol.
+
+[S]::
+ Stop annotation, return to full profile display.
+
+[w]::
+ Toggle between weighted sum and individual count[E]r profile.
+
+[z]::
+ Toggle event count zeroing across display updates.
+
+[qQ]::
+ Quit.
+
+Pressing any unmapped key displays a menu, and prompts for input.
--l::
- scale counter values
SEE ALSO
--------
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index a5e9b876ca0..c045b4271e5 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -158,8 +158,10 @@ uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
# If we're on a 64-bit kernel, use -m64
-ifneq ($(patsubst %64,%,$(uname_M)),$(uname_M))
- M64 := -m64
+ifndef NO_64BIT
+ ifneq ($(patsubst %64,%,$(uname_M)),$(uname_M))
+ M64 := -m64
+ endif
endif
# CFLAGS and LDFLAGS are for the users to override from the command line.
@@ -345,7 +347,6 @@ BUILTIN_OBJS += builtin-stat.o
BUILTIN_OBJS += builtin-top.o
PERFLIBS = $(LIB_FILE)
-EXTLIBS = -lbfd
#
# Platform specific tweaks
@@ -374,6 +375,39 @@ ifeq ($(uname_S),Darwin)
PTHREAD_LIBS =
endif
+ifneq ($(shell sh -c "(echo '\#include <libelf.h>'; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ_MMAP, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o /dev/null $(ALL_LDFLAGS) > /dev/null 2>&1 && echo y"), y)
+ msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel);
+endif
+
+ifdef NO_DEMANGLE
+ BASIC_CFLAGS += -DNO_DEMANGLE
+else
+ has_bfd := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd > /dev/null 2>&1 && echo y")
+
+ ifeq ($(has_bfd),y)
+ EXTLIBS += -lbfd
+ else
+ has_bfd_iberty := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd -liberty > /dev/null 2>&1 && echo y")
+ ifeq ($(has_bfd_iberty),y)
+ EXTLIBS += -lbfd -liberty
+ else
+ has_bfd_iberty_z := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd -liberty -lz > /dev/null 2>&1 && echo y")
+ ifeq ($(has_bfd_iberty_z),y)
+ EXTLIBS += -lbfd -liberty -lz
+ else
+ has_cplus_demangle := $(shell sh -c "(echo 'extern char *cplus_demangle(const char *, int);'; echo 'int main(void) { cplus_demangle(0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -liberty > /dev/null 2>&1 && echo y")
+ ifeq ($(has_cplus_demangle),y)
+ EXTLIBS += -liberty
+ BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
+ else
+ msg := $(warning No bfd.h/libbfd found, install binutils-dev[el] to gain symbol demangling)
+ BASIC_CFLAGS += -DNO_DEMANGLE
+ endif
+ endif
+ endif
+ endif
+endif
+
ifndef CC_LD_DYNPATH
ifdef NO_R_TO_GCC_LINKER
# Some gcc does not accept and pass -R to the linker to specify
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 1dba568e194..5e17de984dc 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -31,6 +31,7 @@ static char *vmlinux = "vmlinux";
static char default_sort_order[] = "comm,symbol";
static char *sort_order = default_sort_order;
+static int force;
static int input;
static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
@@ -980,6 +981,13 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head)
(void *)(long)(event->header.size),
event->fork.pid, event->fork.ppid);
+ /*
+ * A thread clone will have the same PID for both
+ * parent and child.
+ */
+ if (thread == parent)
+ return 0;
+
if (!thread || !parent || thread__fork(thread, parent)) {
dprintf("problem processing PERF_EVENT_FORK, skipping event.\n");
return -1;
@@ -1327,6 +1335,11 @@ static int __cmd_annotate(void)
exit(-1);
}
+ if (!force && (stat.st_uid != geteuid())) {
+ fprintf(stderr, "file: %s not owned by current user\n", input_name);
+ exit(-1);
+ }
+
if (!stat.st_size) {
fprintf(stderr, "zero-sized file, nothing to do!\n");
exit(0);
@@ -1432,6 +1445,7 @@ static const struct option options[] = {
"input file name"),
OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
"symbol to annotate"),
+ OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show symbol address, etc)"),
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index f990fa8a35c..d88c6961274 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -10,11 +10,12 @@
#include "perf.h"
-#include "util/parse-options.h"
#include "util/parse-events.h"
+#include "util/cache.h"
int cmd_list(int argc __used, const char **argv __used, const char *prefix __used)
{
+ setup_pager();
print_events();
return 0;
}
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 6da09928130..89a5ddcd1de 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -34,7 +34,9 @@ static int output;
static const char *output_name = "perf.data";
static int group = 0;
static unsigned int realtime_prio = 0;
+static int raw_samples = 0;
static int system_wide = 0;
+static int profile_cpu = -1;
static pid_t target_pid = -1;
static int inherit = 1;
static int force = 0;
@@ -203,46 +205,48 @@ static void sig_atexit(void)
kill(getpid(), signr);
}
-static void pid_synthesize_comm_event(pid_t pid, int full)
+static pid_t pid_synthesize_comm_event(pid_t pid, int full)
{
struct comm_event comm_ev;
char filename[PATH_MAX];
char bf[BUFSIZ];
- int fd;
- size_t size;
- char *field, *sep;
+ FILE *fp;
+ size_t size = 0;
DIR *tasks;
struct dirent dirent, *next;
+ pid_t tgid = 0;
- snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
- fd = open(filename, O_RDONLY);
- if (fd < 0) {
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
/*
* We raced with a task exiting - just return:
*/
if (verbose)
fprintf(stderr, "couldn't open %s\n", filename);
- return;
- }
- if (read(fd, bf, sizeof(bf)) < 0) {
- fprintf(stderr, "couldn't read %s\n", filename);
- exit(EXIT_FAILURE);
+ return 0;
}
- close(fd);
- /* 9027 (cat) R 6747 9027 6747 34816 9027 ... */
memset(&comm_ev, 0, sizeof(comm_ev));
- field = strchr(bf, '(');
- if (field == NULL)
- goto out_failure;
- sep = strchr(++field, ')');
- if (sep == NULL)
- goto out_failure;
- size = sep - field;
- memcpy(comm_ev.comm, field, size++);
-
- comm_ev.pid = pid;
+ while (!comm_ev.comm[0] || !comm_ev.pid) {
+ if (fgets(bf, sizeof(bf), fp) == NULL)
+ goto out_failure;
+
+ if (memcmp(bf, "Name:", 5) == 0) {
+ char *name = bf + 5;
+ while (*name && isspace(*name))
+ ++name;
+ size = strlen(name) - 1;
+ memcpy(comm_ev.comm, name, size++);
+ } else if (memcmp(bf, "Tgid:", 5) == 0) {
+ char *tgids = bf + 5;
+ while (*tgids && isspace(*tgids))
+ ++tgids;
+ tgid = comm_ev.pid = atoi(tgids);
+ }
+ }
+
comm_ev.header.type = PERF_EVENT_COMM;
size = ALIGN(size, sizeof(u64));
comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size);
@@ -251,7 +255,7 @@ static void pid_synthesize_comm_event(pid_t pid, int full)
comm_ev.tid = pid;
write_output(&comm_ev, comm_ev.header.size);
- return;
+ goto out_fclose;
}
snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
@@ -268,7 +272,10 @@ static void pid_synthesize_comm_event(pid_t pid, int full)
write_output(&comm_ev, comm_ev.header.size);
}
closedir(tasks);
- return;
+
+out_fclose:
+ fclose(fp);
+ return tgid;
out_failure:
fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n",
@@ -276,7 +283,7 @@ out_failure:
exit(EXIT_FAILURE);
}
-static void pid_synthesize_mmap_samples(pid_t pid)
+static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid)
{
char filename[PATH_MAX];
FILE *fp;
@@ -328,7 +335,7 @@ static void pid_synthesize_mmap_samples(pid_t pid)
mmap_ev.len -= mmap_ev.start;
mmap_ev.header.size = (sizeof(mmap_ev) -
(sizeof(mmap_ev.filename) - size));
- mmap_ev.pid = pid;
+ mmap_ev.pid = tgid;
mmap_ev.tid = pid;
write_output(&mmap_ev, mmap_ev.header.size);
@@ -347,14 +354,14 @@ static void synthesize_all(void)
while (!readdir_r(proc, &dirent, &next) && next) {
char *end;
- pid_t pid;
+ pid_t pid, tgid;
pid = strtol(dirent.d_name, &end, 10);
if (*end) /* only interested in proper numerical dirents */
continue;
- pid_synthesize_comm_event(pid, 1);
- pid_synthesize_mmap_samples(pid);
+ tgid = pid_synthesize_comm_event(pid, 1);
+ pid_synthesize_mmap_samples(pid, tgid);
}
closedir(proc);
@@ -392,7 +399,7 @@ static void create_counter(int counter, int cpu, pid_t pid)
PERF_FORMAT_TOTAL_TIME_RUNNING |
PERF_FORMAT_ID;
- attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
+ attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID;
if (freq) {
attr->sample_type |= PERF_SAMPLE_PERIOD;
@@ -412,6 +419,9 @@ static void create_counter(int counter, int cpu, pid_t pid)
if (call_graph)
attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
+ if (raw_samples)
+ attr->sample_type |= PERF_SAMPLE_RAW;
+
attr->mmap = track;
attr->comm = track;
attr->inherit = (cpu < 0) && inherit;
@@ -425,6 +435,8 @@ try_again:
if (err == EPERM)
die("Permission error - are you root?\n");
+ else if (err == ENODEV && profile_cpu != -1)
+ die("No such device - did you specify an out-of-range profile CPU?\n");
/*
* If it's cycles then fall back to hrtimer
@@ -524,10 +536,14 @@ static int __cmd_record(int argc, const char **argv)
signal(SIGCHLD, sig_handler);
signal(SIGINT, sig_handler);
- if (!stat(output_name, &st) && !force && !append_file) {
- fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n",
- output_name);
- exit(-1);
+ if (!stat(output_name, &st) && st.st_size) {
+ if (!force && !append_file) {
+ fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n",
+ output_name);
+ exit(-1);
+ }
+ } else {
+ append_file = 0;
}
flags = O_CREAT|O_RDWR;
@@ -554,16 +570,22 @@ static int __cmd_record(int argc, const char **argv)
if (pid == -1)
pid = getpid();
- open_counters(-1, pid);
- } else for (i = 0; i < nr_cpus; i++)
- open_counters(i, target_pid);
+ open_counters(profile_cpu, pid);
+ } else {
+ if (profile_cpu != -1) {
+ open_counters(profile_cpu, target_pid);
+ } else {
+ for (i = 0; i < nr_cpus; i++)
+ open_counters(i, target_pid);
+ }
+ }
if (file_new)
perf_header__write(header, output);
if (!system_wide) {
- pid_synthesize_comm_event(pid, 0);
- pid_synthesize_mmap_samples(pid);
+ pid_t tgid = pid_synthesize_comm_event(pid, 0);
+ pid_synthesize_mmap_samples(pid, tgid);
} else
synthesize_all();
@@ -631,10 +653,14 @@ static const struct option options[] = {
"record events on existing pid"),
OPT_INTEGER('r', "realtime", &realtime_prio,
"collect data with this RT SCHED_FIFO priority"),
+ OPT_BOOLEAN('R', "raw-samples", &raw_samples,
+ "collect raw sample records from all opened counters"),
OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"),
OPT_BOOLEAN('A', "append", &append_file,
"append to the output file to do incremental profiling"),
+ OPT_INTEGER('C', "profile_cpu", &profile_cpu,
+ "CPU to profile on"),
OPT_BOOLEAN('f', "force", &force,
"overwrite existing data file"),
OPT_LONG('c', "count", &default_interval,
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index b20a4b6e31b..8b2ec882e6e 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -31,13 +31,14 @@
static char const *input_name = "perf.data";
static char *vmlinux = NULL;
-static char default_sort_order[] = "comm,dso";
+static char default_sort_order[] = "comm,dso,symbol";
static char *sort_order = default_sort_order;
static char *dso_list_str, *comm_list_str, *sym_list_str,
*col_width_list_str;
static struct strlist *dso_list, *comm_list, *sym_list;
static char *field_sep;
+static int force;
static int input;
static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
@@ -68,7 +69,7 @@ static int callchain;
static
struct callchain_param callchain_param = {
- .mode = CHAIN_GRAPH_ABS,
+ .mode = CHAIN_GRAPH_REL,
.min_percent = 0.5
};
@@ -99,6 +100,7 @@ struct comm_event {
struct fork_event {
struct perf_event_header header;
u32 pid, ppid;
+ u32 tid, ptid;
};
struct lost_event {
@@ -111,7 +113,9 @@ struct read_event {
struct perf_event_header header;
u32 pid,tid;
u64 value;
- u64 format[3];
+ u64 time_enabled;
+ u64 time_running;
+ u64 id;
};
typedef union event_union {
@@ -252,7 +256,7 @@ static int strcommon(const char *pathname)
{
int n = 0;
- while (pathname[n] == cwd[n] && n < cwdlen)
+ while (n < cwdlen && pathname[n] == cwd[n])
++n;
return n;
@@ -697,7 +701,8 @@ sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used)
size_t ret = 0;
if (verbose)
- ret += repsep_fprintf(fp, "%#018llx ", (u64)self->ip);
+ ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip,
+ dso__symtab_origin(self->dso));
ret += repsep_fprintf(fp, "[%c] ", self->level);
if (self->sym) {
@@ -887,6 +892,21 @@ ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth,
return ret;
}
+static struct symbol *rem_sq_bracket;
+static struct callchain_list rem_hits;
+
+static void init_rem_hits(void)
+{
+ rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
+ if (!rem_sq_bracket) {
+ fprintf(stderr, "Not enough memory to display remaining hits\n");
+ return;
+ }
+
+ strcpy(rem_sq_bracket->name, "[...]");
+ rem_hits.sym = rem_sq_bracket;
+}
+
static size_t
callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
u64 total_samples, int depth, int depth_mask)
@@ -896,25 +916,34 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
struct callchain_list *chain;
int new_depth_mask = depth_mask;
u64 new_total;
+ u64 remaining;
size_t ret = 0;
int i;
if (callchain_param.mode == CHAIN_GRAPH_REL)
- new_total = self->cumul_hit;
+ new_total = self->children_hit;
else
new_total = total_samples;
+ remaining = new_total;
+
node = rb_first(&self->rb_root);
while (node) {
+ u64 cumul;
+
child = rb_entry(node, struct callchain_node, rb_node);
+ cumul = cumul_hits(child);
+ remaining -= cumul;
/*
* The depth mask manages the output of pipes that show
* the depth. We don't want to keep the pipes of the current
- * level for the last child of this depth
+ * level for the last child of this depth.
+ * Except if we have remaining filtered hits. They will
+ * supersede the last child
*/
next = rb_next(node);
- if (!next)
+ if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
new_depth_mask &= ~(1 << (depth - 1));
/*
@@ -929,7 +958,7 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
ret += ipchain__fprintf_graph(fp, chain, depth,
new_depth_mask, i++,
new_total,
- child->cumul_hit);
+ cumul);
}
ret += callchain__fprintf_graph(fp, child, new_total,
depth + 1,
@@ -937,6 +966,19 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
node = next;
}
+ if (callchain_param.mode == CHAIN_GRAPH_REL &&
+ remaining && remaining != new_total) {
+
+ if (!rem_sq_bracket)
+ return ret;
+
+ new_depth_mask &= ~(1 << (depth - 1));
+
+ ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
+ new_depth_mask, 0, new_total,
+ remaining);
+ }
+
return ret;
}
@@ -1357,6 +1399,8 @@ static size_t output__fprintf(FILE *fp, u64 total_samples)
unsigned int width;
char *col_width = col_width_list_str;
+ init_rem_hits();
+
fprintf(fp, "# Samples: %Ld\n", (u64)total_samples);
fprintf(fp, "#\n");
@@ -1423,11 +1467,13 @@ print_entries:
if (sort_order == default_sort_order &&
parent_pattern == default_parent_pattern) {
fprintf(fp, "#\n");
- fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n");
+ fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n");
fprintf(fp, "#\n");
}
fprintf(fp, "\n");
+ free(rem_sq_bracket);
+
return ret;
}
@@ -1481,11 +1527,11 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
more_data += sizeof(u64);
}
- dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d: %p period: %Ld\n",
+ dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->header.misc,
- event->ip.pid,
+ event->ip.pid, event->ip.tid,
(void *)(long)ip,
(long long)period);
@@ -1545,10 +1591,11 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head)
if (show & show_mask) {
struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);
- if (dso_list && dso && dso->name && !strlist__has_entry(dso_list, dso->name))
+ if (dso_list && (!dso || !dso->name ||
+ !strlist__has_entry(dso_list, dso->name)))
return 0;
- if (sym_list && sym && !strlist__has_entry(sym_list, sym->name))
+ if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name)))
return 0;
if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
@@ -1567,10 +1614,11 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
struct thread *thread = threads__findnew(event->mmap.pid);
struct map *map = map__new(&event->mmap);
- dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n",
+ dprintf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->mmap.pid,
+ event->mmap.tid,
(void *)(long)event->mmap.start,
(void *)(long)event->mmap.len,
(void *)(long)event->mmap.pgoff,
@@ -1608,15 +1656,27 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head)
}
static int
-process_fork_event(event_t *event, unsigned long offset, unsigned long head)
+process_task_event(event_t *event, unsigned long offset, unsigned long head)
{
struct thread *thread = threads__findnew(event->fork.pid);
struct thread *parent = threads__findnew(event->fork.ppid);
- dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n",
+ dprintf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
- event->fork.pid, event->fork.ppid);
+ event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT",
+ event->fork.pid, event->fork.tid,
+ event->fork.ppid, event->fork.ptid);
+
+ /*
+ * A thread clone will have the same PID for both
+ * parent and child.
+ */
+ if (thread == parent)
+ return 0;
+
+ if (event->header.type == PERF_EVENT_EXIT)
+ return 0;
if (!thread || !parent || thread__fork(thread, parent)) {
dprintf("problem processing PERF_EVENT_FORK, skipping event.\n");
@@ -1677,14 +1737,37 @@ static void trace_event(event_t *event)
dprintf(".\n");
}
+static struct perf_header *header;
+
+static struct perf_counter_attr *perf_header__find_attr(u64 id)
+{
+ int i;
+
+ for (i = 0; i < header->attrs; i++) {
+ struct perf_header_attr *attr = header->attr[i];
+ int j;
+
+ for (j = 0; j < attr->ids; j++) {
+ if (attr->id[j] == id)
+ return &attr->attr;
+ }
+ }
+
+ return NULL;
+}
+
static int
process_read_event(event_t *event, unsigned long offset, unsigned long head)
{
- dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n",
+ struct perf_counter_attr *attr = perf_header__find_attr(event->read.id);
+
+ dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n",
(void *)(offset + head),
(void *)(long)(event->header.size),
event->read.pid,
event->read.tid,
+ attr ? __event_name(attr->type, attr->config)
+ : "FAIL",
event->read.value);
return 0;
@@ -1706,7 +1789,8 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
return process_comm_event(event, offset, head);
case PERF_EVENT_FORK:
- return process_fork_event(event, offset, head);
+ case PERF_EVENT_EXIT:
+ return process_task_event(event, offset, head);
case PERF_EVENT_LOST:
return process_lost_event(event, offset, head);
@@ -1729,8 +1813,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
return 0;
}
-static struct perf_header *header;
-
static u64 perf_header__sample_type(void)
{
u64 sample_type = 0;
@@ -1775,6 +1857,11 @@ static int __cmd_report(void)
exit(-1);
}
+ if (!force && (stat.st_uid != geteuid())) {
+ fprintf(stderr, "file: %s not owned by current user\n", input_name);
+ exit(-1);
+ }
+
if (!stat.st_size) {
fprintf(stderr, "zero-sized file, nothing to do!\n");
exit(0);
@@ -1798,6 +1885,13 @@ static int __cmd_report(void)
" -g?\n");
exit(-1);
}
+ } else if (callchain_param.mode != CHAIN_NONE && !callchain) {
+ callchain = 1;
+ if (register_callchain_param(&callchain_param) < 0) {
+ fprintf(stderr, "Can't register callchain"
+ " params\n");
+ exit(-1);
+ }
}
if (load_kernel() < 0) {
@@ -1936,6 +2030,13 @@ parse_callchain_opt(const struct option *opt __used, const char *arg,
else if (!strncmp(tok, "fractal", strlen(arg)))
callchain_param.mode = CHAIN_GRAPH_REL;
+ else if (!strncmp(tok, "none", strlen(arg))) {
+ callchain_param.mode = CHAIN_NONE;
+ callchain = 0;
+
+ return 0;
+ }
+
else
return -1;
@@ -1969,6 +2070,7 @@ static const struct option options[] = {
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"),
OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
+ OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
OPT_BOOLEAN('m', "modules", &modules,
"load module symbols - WARNING: use only with -k and LIVE kernel"),
OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples,
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index f9510eeeb6c..b4b06c7903e 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -496,7 +496,7 @@ static const struct option options[] = {
"stat events on existing pid"),
OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"),
- OPT_BOOLEAN('S', "scale", &scale,
+ OPT_BOOLEAN('c', "scale", &scale,
"scale/normalize counters"),
OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show counter open errors, etc)"),
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index c0a423004e1..7de28ce9ca2 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -31,6 +31,8 @@
#include <fcntl.h>
#include <stdio.h>
+#include <termios.h>
+#include <unistd.h>
#include <errno.h>
#include <time.h>
@@ -54,7 +56,7 @@ static int system_wide = 0;
static int default_interval = 100000;
-static u64 count_filter = 5;
+static int count_filter = 5;
static int print_entries = 15;
static int target_pid = -1;
@@ -69,15 +71,28 @@ static int freq = 0;
static int verbose = 0;
static char *vmlinux = NULL;
-static char *sym_filter;
-static unsigned long filter_start;
-static unsigned long filter_end;
-
static int delay_secs = 2;
static int zero;
static int dump_symtab;
/*
+ * Source
+ */
+
+struct source_line {
+ u64 eip;
+ unsigned long count[MAX_COUNTERS];
+ char *line;
+ struct source_line *next;
+};
+
+static char *sym_filter = NULL;
+struct sym_entry *sym_filter_entry = NULL;
+static int sym_pcnt_filter = 5;
+static int sym_counter = 0;
+static int display_weighted = -1;
+
+/*
* Symbols
*/
@@ -91,9 +106,237 @@ struct sym_entry {
unsigned long snap_count;
double weight;
int skip;
+ struct source_line *source;
+ struct source_line *lines;
+ struct source_line **lines_tail;
+ pthread_mutex_t source_lock;
};
-struct sym_entry *sym_filter_entry;
+/*
+ * Source functions
+ */
+
+static void parse_source(struct sym_entry *syme)
+{
+ struct symbol *sym;
+ struct module *module;
+ struct section *section = NULL;
+ FILE *file;
+ char command[PATH_MAX*2], *path = vmlinux;
+ u64 start, end, len;
+
+ if (!syme)
+ return;
+
+ if (syme->lines) {
+ pthread_mutex_lock(&syme->source_lock);
+ goto out_assign;
+ }
+
+ sym = (struct symbol *)(syme + 1);
+ module = sym->module;
+
+ if (module)
+ path = module->path;
+ if (!path)
+ return;
+
+ start = sym->obj_start;
+ if (!start)
+ start = sym->start;
+
+ if (module) {
+ section = module->sections->find_section(module->sections, ".text");
+ if (section)
+ start -= section->vma;
+ }
+
+ end = start + sym->end - sym->start + 1;
+ len = sym->end - sym->start;
+
+ sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", start, end, path);
+
+ file = popen(command, "r");
+ if (!file)
+ return;
+
+ pthread_mutex_lock(&syme->source_lock);
+ syme->lines_tail = &syme->lines;
+ while (!feof(file)) {
+ struct source_line *src;
+ size_t dummy = 0;
+ char *c;
+
+ src = malloc(sizeof(struct source_line));
+ assert(src != NULL);
+ memset(src, 0, sizeof(struct source_line));
+
+ if (getline(&src->line, &dummy, file) < 0)
+ break;
+ if (!src->line)
+ break;
+
+ c = strchr(src->line, '\n');
+ if (c)
+ *c = 0;
+
+ src->next = NULL;
+ *syme->lines_tail = src;
+ syme->lines_tail = &src->next;
+
+ if (strlen(src->line)>8 && src->line[8] == ':') {
+ src->eip = strtoull(src->line, NULL, 16);
+ if (section)
+ src->eip += section->vma;
+ }
+ if (strlen(src->line)>8 && src->line[16] == ':') {
+ src->eip = strtoull(src->line, NULL, 16);
+ if (section)
+ src->eip += section->vma;
+ }
+ }
+ pclose(file);
+out_assign:
+ sym_filter_entry = syme;
+ pthread_mutex_unlock(&syme->source_lock);
+}
+
+static void __zero_source_counters(struct sym_entry *syme)
+{
+ int i;
+ struct source_line *line;
+
+ line = syme->lines;
+ while (line) {
+ for (i = 0; i < nr_counters; i++)
+ line->count[i] = 0;
+ line = line->next;
+ }
+}
+
+static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
+{
+ struct source_line *line;
+
+ if (syme != sym_filter_entry)
+ return;
+
+ if (pthread_mutex_trylock(&syme->source_lock))
+ return;
+
+ if (!syme->source)
+ goto out_unlock;
+
+ for (line = syme->lines; line; line = line->next) {
+ if (line->eip == ip) {
+ line->count[counter]++;
+ break;
+ }
+ if (line->eip > ip)
+ break;
+ }
+out_unlock:
+ pthread_mutex_unlock(&syme->source_lock);
+}
+
+static void lookup_sym_source(struct sym_entry *syme)
+{
+ struct symbol *symbol = (struct symbol *)(syme + 1);
+ struct source_line *line;
+ char pattern[PATH_MAX];
+ char *idx;
+
+ sprintf(pattern, "<%s>:", symbol->name);
+
+ if (symbol->module) {
+ idx = strstr(pattern, "\t");
+ if (idx)
+ *idx = 0;
+ }
+
+ pthread_mutex_lock(&syme->source_lock);
+ for (line = syme->lines; line; line = line->next) {
+ if (strstr(line->line, pattern)) {
+ syme->source = line;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&syme->source_lock);
+}
+
+static void show_lines(struct source_line *queue, int count, int total)
+{
+ int i;
+ struct source_line *line;
+
+ line = queue;
+ for (i = 0; i < count; i++) {
+ float pcnt = 100.0*(float)line->count[sym_counter]/(float)total;
+
+ printf("%8li %4.1f%%\t%s\n", line->count[sym_counter], pcnt, line->line);
+ line = line->next;
+ }
+}
+
+#define TRACE_COUNT 3
+
+static void show_details(struct sym_entry *syme)
+{
+ struct symbol *symbol;
+ struct source_line *line;
+ struct source_line *line_queue = NULL;
+ int displayed = 0;
+ int line_queue_count = 0, total = 0, more = 0;
+
+ if (!syme)
+ return;
+
+ if (!syme->source)
+ lookup_sym_source(syme);
+
+ if (!syme->source)
+ return;
+
+ symbol = (struct symbol *)(syme + 1);
+ printf("Showing %s for %s\n", event_name(sym_counter), symbol->name);
+ printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter);
+
+ pthread_mutex_lock(&syme->source_lock);
+ line = syme->source;
+ while (line) {
+ total += line->count[sym_counter];
+ line = line->next;
+ }
+
+ line = syme->source;
+ while (line) {
+ float pcnt = 0.0;
+
+ if (!line_queue_count)
+ line_queue = line;
+ line_queue_count++;
+
+ if (line->count[sym_counter])
+ pcnt = 100.0 * line->count[sym_counter] / (float)total;
+ if (pcnt >= (float)sym_pcnt_filter) {
+ if (displayed <= print_entries)
+ show_lines(line_queue, line_queue_count, total);
+ else more++;
+ displayed += line_queue_count;
+ line_queue_count = 0;
+ line_queue = NULL;
+ } else if (line_queue_count > TRACE_COUNT) {
+ line_queue = line_queue->next;
+ line_queue_count--;
+ }
+
+ line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8;
+ line = line->next;
+ }
+ pthread_mutex_unlock(&syme->source_lock);
+ if (more)
+ printf("%d lines not displayed, maybe increase display entries [e]\n", more);
+}
struct dso *kernel_dso;
@@ -112,6 +355,9 @@ static double sym_weight(const struct sym_entry *sym)
double weight = sym->snap_count;
int counter;
+ if (!display_weighted)
+ return weight;
+
for (counter = 1; counter < nr_counters-1; counter++)
weight *= sym->count[counter];
@@ -159,7 +405,7 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se)
static void print_sym_table(void)
{
int printed = 0, j;
- int counter;
+ int counter, snap = !display_weighted ? sym_counter : 0;
float samples_per_sec = samples/delay_secs;
float ksamples_per_sec = (samples-userspace_samples)/delay_secs;
float sum_ksamples = 0.0;
@@ -175,7 +421,7 @@ static void print_sym_table(void)
pthread_mutex_unlock(&active_symbols_lock);
list_for_each_entry_safe_from(syme, n, &active_symbols, node) {
- syme->snap_count = syme->count[0];
+ syme->snap_count = syme->count[snap];
if (syme->snap_count != 0) {
syme->weight = sym_weight(syme);
rb_insert_active_sym(&tmp, syme);
@@ -195,7 +441,7 @@ static void print_sym_table(void)
samples_per_sec,
100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec)));
- if (nr_counters == 1) {
+ if (nr_counters == 1 || !display_weighted) {
printf("%Ld", (u64)attrs[0].sample_period);
if (freq)
printf("Hz ");
@@ -203,7 +449,9 @@ static void print_sym_table(void)
printf(" ");
}
- for (counter = 0; counter < nr_counters; counter++) {
+ if (!display_weighted)
+ printf("%s", event_name(sym_counter));
+ else for (counter = 0; counter < nr_counters; counter++) {
if (counter)
printf("/");
@@ -228,6 +476,11 @@ static void print_sym_table(void)
printf("------------------------------------------------------------------------------\n\n");
+ if (sym_filter_entry) {
+ show_details(sym_filter_entry);
+ return;
+ }
+
if (nr_counters == 1)
printf(" samples pcnt");
else
@@ -242,13 +495,13 @@ static void print_sym_table(void)
struct symbol *sym = (struct symbol *)(syme + 1);
double pcnt;
- if (++printed > print_entries || syme->snap_count < count_filter)
+ if (++printed > print_entries || (int)syme->snap_count < count_filter)
continue;
pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) /
sum_ksamples));
- if (nr_counters == 1)
+ if (nr_counters == 1 || !display_weighted)
printf("%20.2f - ", syme->weight);
else
printf("%9.1f %10ld - ", syme->weight, syme->snap_count);
@@ -261,19 +514,250 @@ static void print_sym_table(void)
}
}
+static void prompt_integer(int *target, const char *msg)
+{
+ char *buf = malloc(0), *p;
+ size_t dummy = 0;
+ int tmp;
+
+ fprintf(stdout, "\n%s: ", msg);
+ if (getline(&buf, &dummy, stdin) < 0)
+ return;
+
+ p = strchr(buf, '\n');
+ if (p)
+ *p = 0;
+
+ p = buf;
+ while(*p) {
+ if (!isdigit(*p))
+ goto out_free;
+ p++;
+ }
+ tmp = strtoul(buf, NULL, 10);
+ *target = tmp;
+out_free:
+ free(buf);
+}
+
+static void prompt_percent(int *target, const char *msg)
+{
+ int tmp = 0;
+
+ prompt_integer(&tmp, msg);
+ if (tmp >= 0 && tmp <= 100)
+ *target = tmp;
+}
+
+static void prompt_symbol(struct sym_entry **target, const char *msg)
+{
+ char *buf = malloc(0), *p;
+ struct sym_entry *syme = *target, *n, *found = NULL;
+ size_t dummy = 0;
+
+ /* zero counters of active symbol */
+ if (syme) {
+ pthread_mutex_lock(&syme->source_lock);
+ __zero_source_counters(syme);
+ *target = NULL;
+ pthread_mutex_unlock(&syme->source_lock);
+ }
+
+ fprintf(stdout, "\n%s: ", msg);
+ if (getline(&buf, &dummy, stdin) < 0)
+ goto out_free;
+
+ p = strchr(buf, '\n');
+ if (p)
+ *p = 0;
+
+ pthread_mutex_lock(&active_symbols_lock);
+ syme = list_entry(active_symbols.next, struct sym_entry, node);
+ pthread_mutex_unlock(&active_symbols_lock);
+
+ list_for_each_entry_safe_from(syme, n, &active_symbols, node) {
+ struct symbol *sym = (struct symbol *)(syme + 1);
+
+ if (!strcmp(buf, sym->name)) {
+ found = syme;
+ break;
+ }
+ }
+
+ if (!found) {
+ fprintf(stderr, "Sorry, %s is not active.\n", sym_filter);
+ sleep(1);
+ return;
+ } else
+ parse_source(found);
+
+out_free:
+ free(buf);
+}
+
+static void print_mapped_keys(void)
+{
+ char *name = NULL;
+
+ if (sym_filter_entry) {
+ struct symbol *sym = (struct symbol *)(sym_filter_entry+1);
+ name = sym->name;
+ }
+
+ fprintf(stdout, "\nMapped keys:\n");
+ fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs);
+ fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries);
+
+ if (nr_counters > 1)
+ fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_counter));
+
+ fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter);
+
+ if (vmlinux) {
+ fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter);
+ fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL");
+ fprintf(stdout, "\t[S] stop annotation.\n");
+ }
+
+ if (nr_counters > 1)
+ fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0);
+
+ fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0);
+ fprintf(stdout, "\t[qQ] quit.\n");
+}
+
+static int key_mapped(int c)
+{
+ switch (c) {
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'z':
+ case 'q':
+ case 'Q':
+ return 1;
+ case 'E':
+ case 'w':
+ return nr_counters > 1 ? 1 : 0;
+ case 'F':
+ case 's':
+ case 'S':
+ return vmlinux ? 1 : 0;
+ }
+
+ return 0;
+}
+
+static void handle_keypress(int c)
+{
+ if (!key_mapped(c)) {
+ struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
+ struct termios tc, save;
+
+ print_mapped_keys();
+ fprintf(stdout, "\nEnter selection, or unmapped key to continue: ");
+ fflush(stdout);
+
+ tcgetattr(0, &save);
+ tc = save;
+ tc.c_lflag &= ~(ICANON | ECHO);
+ tc.c_cc[VMIN] = 0;
+ tc.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &tc);
+
+ poll(&stdin_poll, 1, -1);
+ c = getc(stdin);
+
+ tcsetattr(0, TCSAFLUSH, &save);
+ if (!key_mapped(c))
+ return;
+ }
+
+ switch (c) {
+ case 'd':
+ prompt_integer(&delay_secs, "Enter display delay");
+ break;
+ case 'e':
+ prompt_integer(&print_entries, "Enter display entries (lines)");
+ break;
+ case 'E':
+ if (nr_counters > 1) {
+ int i;
+
+ fprintf(stderr, "\nAvailable events:");
+ for (i = 0; i < nr_counters; i++)
+ fprintf(stderr, "\n\t%d %s", i, event_name(i));
+
+ prompt_integer(&sym_counter, "Enter details event counter");
+
+ if (sym_counter >= nr_counters) {
+ fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(0));
+ sym_counter = 0;
+ sleep(1);
+ }
+ } else sym_counter = 0;
+ break;
+ case 'f':
+ prompt_integer(&count_filter, "Enter display event count filter");
+ break;
+ case 'F':
+ prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)");
+ break;
+ case 'q':
+ case 'Q':
+ printf("exiting.\n");
+ exit(0);
+ case 's':
+ prompt_symbol(&sym_filter_entry, "Enter details symbol");
+ break;
+ case 'S':
+ if (!sym_filter_entry)
+ break;
+ else {
+ struct sym_entry *syme = sym_filter_entry;
+
+ pthread_mutex_lock(&syme->source_lock);
+ sym_filter_entry = NULL;
+ __zero_source_counters(syme);
+ pthread_mutex_unlock(&syme->source_lock);
+ }
+ break;
+ case 'w':
+ display_weighted = ~display_weighted;
+ break;
+ case 'z':
+ zero = ~zero;
+ break;
+ }
+}
+
static void *display_thread(void *arg __used)
{
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
- int delay_msecs = delay_secs * 1000;
+ struct termios tc, save;
+ int delay_msecs, c;
+
+ tcgetattr(0, &save);
+ tc = save;
+ tc.c_lflag &= ~(ICANON | ECHO);
+ tc.c_cc[VMIN] = 0;
+ tc.c_cc[VTIME] = 0;
- printf("PerfTop refresh period: %d seconds\n", delay_secs);
+repeat:
+ delay_msecs = delay_secs * 1000;
+ tcsetattr(0, TCSANOW, &tc);
+ /* trash return*/
+ getc(stdin);
do {
print_sym_table();
} while (!poll(&stdin_poll, 1, delay_msecs) == 1);
- printf("key pressed - exiting.\n");
- exit(0);
+ c = getc(stdin);
+ tcsetattr(0, TCSAFLUSH, &save);
+
+ handle_keypress(c);
+ goto repeat;
return NULL;
}
@@ -285,6 +769,7 @@ static const char *skip_symbols[] = {
"enter_idle",
"exit_idle",
"mwait_idle",
+ "mwait_idle_with_hints",
"ppc64_runlatch_off",
"pseries_dedicated_idle_sleep",
NULL
@@ -292,7 +777,6 @@ static const char *skip_symbols[] = {
static int symbol_filter(struct dso *self, struct symbol *sym)
{
- static int filter_match;
struct sym_entry *syme;
const char *name = sym->name;
int i;
@@ -314,6 +798,10 @@ static int symbol_filter(struct dso *self, struct symbol *sym)
return 1;
syme = dso__sym_priv(self, sym);
+ pthread_mutex_init(&syme->source_lock, NULL);
+ if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter))
+ sym_filter_entry = syme;
+
for (i = 0; skip_symbols[i]; i++) {
if (!strcmp(skip_symbols[i], name)) {
syme->skip = 1;
@@ -321,29 +809,6 @@ static int symbol_filter(struct dso *self, struct symbol *sym)
}
}
- if (filter_match == 1) {
- filter_end = sym->start;
- filter_match = -1;
- if (filter_end - filter_start > 10000) {
- fprintf(stderr,
- "hm, too large filter symbol <%s> - skipping.\n",
- sym_filter);
- fprintf(stderr, "symbol filter start: %016lx\n",
- filter_start);
- fprintf(stderr, " end: %016lx\n",
- filter_end);
- filter_end = filter_start = 0;
- sym_filter = NULL;
- sleep(1);
- }
- }
-
- if (filter_match == 0 && sym_filter && !strcmp(name, sym_filter)) {
- filter_match = 1;
- filter_start = sym->start;
- }
-
-
return 0;
}
@@ -379,8 +844,6 @@ out_delete_dso:
return -1;
}
-#define TRACE_COUNT 3
-
/*
* Binary search in the histogram table and record the hit:
*/
@@ -393,6 +856,7 @@ static void record_ip(u64 ip, int counter)
if (!syme->skip) {
syme->count[counter]++;
+ record_precise_ip(syme, counter, ip);
pthread_mutex_lock(&active_symbols_lock);
if (list_empty(&syme->node) || !syme->node.next)
__list_insert_active_sym(syme);
@@ -689,8 +1153,8 @@ static const struct option options[] = {
"put the counters into a counter group"),
OPT_BOOLEAN('i', "inherit", &inherit,
"child tasks inherit counters"),
- OPT_STRING('s', "sym-filter", &sym_filter, "pattern",
- "only display symbols matchig this pattern"),
+ OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name",
+ "symbol to annotate - requires -k option"),
OPT_BOOLEAN('z', "zero", &zero,
"zero history across updates"),
OPT_INTEGER('F', "freq", &freq,
@@ -733,6 +1197,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
delay_secs = 1;
parse_symbols();
+ parse_source(sym_filter_entry);
/*
* Fill in the ones not specifically initialized via -c:
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 9d3c8141b8c..01147341164 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -13,6 +13,7 @@
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
+#include <math.h>
#include "callchain.h"
@@ -26,10 +27,14 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
struct callchain_node *rnode;
+ u64 chain_cumul = cumul_hits(chain);
while (*p) {
+ u64 rnode_cumul;
+
parent = *p;
rnode = rb_entry(parent, struct callchain_node, rb_node);
+ rnode_cumul = cumul_hits(rnode);
switch (mode) {
case CHAIN_FLAT:
@@ -40,7 +45,7 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
break;
case CHAIN_GRAPH_ABS: /* Falldown */
case CHAIN_GRAPH_REL:
- if (rnode->cumul_hit < chain->cumul_hit)
+ if (rnode_cumul < chain_cumul)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
@@ -87,7 +92,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node,
chain_for_each_child(child, node) {
__sort_chain_graph_abs(child, min_hit);
- if (child->cumul_hit >= min_hit)
+ if (cumul_hits(child) >= min_hit)
rb_insert_callchain(&node->rb_root, child,
CHAIN_GRAPH_ABS);
}
@@ -108,11 +113,11 @@ static void __sort_chain_graph_rel(struct callchain_node *node,
u64 min_hit;
node->rb_root = RB_ROOT;
- min_hit = node->cumul_hit * min_percent / 100.0;
+ min_hit = ceil(node->children_hit * min_percent);
chain_for_each_child(child, node) {
__sort_chain_graph_rel(child, min_percent);
- if (child->cumul_hit >= min_hit)
+ if (cumul_hits(child) >= min_hit)
rb_insert_callchain(&node->rb_root, child,
CHAIN_GRAPH_REL);
}
@@ -122,7 +127,7 @@ static void
sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root,
u64 min_hit __used, struct callchain_param *param)
{
- __sort_chain_graph_rel(chain_root, param->min_percent);
+ __sort_chain_graph_rel(chain_root, param->min_percent / 100.0);
rb_root->rb_node = chain_root->rb_root.rb_node;
}
@@ -211,7 +216,8 @@ add_child(struct callchain_node *parent, struct ip_callchain *chain,
new = create_child(parent, false);
fill_node(new, chain, start, syms);
- new->cumul_hit = new->hit = 1;
+ new->children_hit = 0;
+ new->hit = 1;
}
/*
@@ -241,7 +247,8 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain,
/* split the hits */
new->hit = parent->hit;
- new->cumul_hit = parent->cumul_hit;
+ new->children_hit = parent->children_hit;
+ parent->children_hit = cumul_hits(new);
new->val_nr = parent->val_nr - idx_local;
parent->val_nr = idx_local;
@@ -249,6 +256,7 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain,
if (idx_total < chain->nr) {
parent->hit = 0;
add_child(parent, chain, idx_total, syms);
+ parent->children_hit++;
} else {
parent->hit = 1;
}
@@ -269,13 +277,13 @@ __append_chain_children(struct callchain_node *root, struct ip_callchain *chain,
unsigned int ret = __append_chain(rnode, chain, start, syms);
if (!ret)
- goto cumul;
+ goto inc_children_hit;
}
/* nothing in children, add to the current node */
add_child(root, chain, start, syms);
-cumul:
- root->cumul_hit++;
+inc_children_hit:
+ root->children_hit++;
}
static int
@@ -317,8 +325,6 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain,
/* we match 100% of the path, increment the hit */
if (i - start == root->val_nr && i == chain->nr) {
root->hit++;
- root->cumul_hit++;
-
return 0;
}
@@ -331,5 +337,7 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain,
void append_chain(struct callchain_node *root, struct ip_callchain *chain,
struct symbol **syms)
{
+ if (!chain->nr)
+ return;
__append_chain_children(root, chain, syms, 0);
}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 7812122bea1..a926ae4f5a1 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -7,6 +7,7 @@
#include "symbol.h"
enum chain_mode {
+ CHAIN_NONE,
CHAIN_FLAT,
CHAIN_GRAPH_ABS,
CHAIN_GRAPH_REL
@@ -21,7 +22,7 @@ struct callchain_node {
struct rb_root rb_root; /* sorted tree of children */
unsigned int val_nr;
u64 hit;
- u64 cumul_hit; /* hit + hits of children */
+ u64 children_hit;
};
struct callchain_param;
@@ -48,6 +49,11 @@ static inline void callchain_init(struct callchain_node *node)
INIT_LIST_HEAD(&node->val);
}
+static inline u64 cumul_hits(struct callchain_node *node)
+{
+ return node->hit + node->children_hit;
+}
+
int register_callchain_param(struct callchain_param *param);
void append_chain(struct callchain_node *root, struct ip_callchain *chain,
struct symbol **syms);
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 450384b3bbe..b92a457ca32 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -185,6 +185,8 @@ static void do_read(int fd, void *buf, size_t size)
if (ret < 0)
die("failed to read");
+ if (ret == 0)
+ die("failed to read: missing data");
size -= ret;
buf += ret;
@@ -213,9 +215,10 @@ struct perf_header *perf_header__read(int fd)
for (i = 0; i < nr_attrs; i++) {
struct perf_header_attr *attr;
- off_t tmp = lseek(fd, 0, SEEK_CUR);
+ off_t tmp;
do_read(fd, &f_attr, sizeof(f_attr));
+ tmp = lseek(fd, 0, SEEK_CUR);
attr = perf_header_attr__new(&f_attr.attr);
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 7bdad8df22a..04417840878 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -121,13 +121,29 @@ static unsigned long hw_cache_stat[C(MAX)] = {
(strcmp(sys_dirent.d_name, ".")) && \
(strcmp(sys_dirent.d_name, "..")))
+static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
+{
+ char evt_path[MAXPATHLEN];
+ int fd;
+
+ snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
+ sys_dir->d_name, evt_dir->d_name);
+ fd = open(evt_path, O_RDONLY);
+ if (fd < 0)
+ return -EINVAL;
+ close(fd);
+
+ return 0;
+}
+
#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st) \
while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \
if (snprintf(file, MAXPATHLEN, "%s/%s/%s", debugfs_path, \
sys_dirent.d_name, evt_dirent.d_name) && \
(!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \
(strcmp(evt_dirent.d_name, ".")) && \
- (strcmp(evt_dirent.d_name, "..")))
+ (strcmp(evt_dirent.d_name, "..")) && \
+ (!tp_event_has_id(&sys_dirent, &evt_dirent)))
#define MAX_EVENT_LENGTH 30
@@ -223,9 +239,15 @@ char *event_name(int counter)
{
u64 config = attrs[counter].config;
int type = attrs[counter].type;
+
+ return __event_name(type, config);
+}
+
+char *__event_name(int type, u64 config)
+{
static char buf[32];
- if (attrs[counter].type == PERF_TYPE_RAW) {
+ if (type == PERF_TYPE_RAW) {
sprintf(buf, "raw 0x%llx", config);
return buf;
}
@@ -357,6 +379,7 @@ static int parse_tracepoint_event(const char **strp,
struct perf_counter_attr *attr)
{
const char *evt_name;
+ char *flags;
char sys_name[MAX_EVENT_LENGTH];
char id_buf[4];
int fd;
@@ -378,6 +401,15 @@ static int parse_tracepoint_event(const char **strp,
strncpy(sys_name, *strp, sys_length);
sys_name[sys_length] = '\0';
evt_name = evt_name + 1;
+
+ flags = strchr(evt_name, ':');
+ if (flags) {
+ *flags = '\0';
+ flags++;
+ if (!strncmp(flags, "record", strlen(flags)))
+ attr->sample_type |= PERF_SAMPLE_RAW;
+ }
+
evt_length = strlen(evt_name);
if (evt_length >= MAX_EVENT_LENGTH)
return 0;
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 1ea5d09b6eb..192a962e3a0 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -10,6 +10,7 @@ extern int nr_counters;
extern struct perf_counter_attr attrs[MAX_COUNTERS];
extern char *event_name(int ctr);
+extern char *__event_name(int type, u64 config);
extern int parse_events(const struct option *opt, const char *str, int unset);
diff --git a/tools/perf/util/quote.c b/tools/perf/util/quote.c
index c6e5dc0dc82..2726fe40eb5 100644
--- a/tools/perf/util/quote.c
+++ b/tools/perf/util/quote.c
@@ -318,7 +318,7 @@ char *quote_path_relative(const char *in, int len,
strbuf_addch(out, '"');
if (prefix) {
int off = 0;
- while (prefix[off] && off < len && prefix[off] == in[off])
+ while (off < len && prefix[off] && prefix[off] == in[off])
if (prefix[off] == '/') {
prefix += off + 1;
in += off + 1;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 28106059bf1..5c0f42e6b33 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -6,14 +6,18 @@
#include <libelf.h>
#include <gelf.h>
#include <elf.h>
-#include <bfd.h>
const char *sym_hist_filter;
-#ifndef DMGL_PARAMS
-#define DMGL_PARAMS (1 << 0) /* Include function args */
-#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
-#endif
+enum dso_origin {
+ DSO__ORIG_KERNEL = 0,
+ DSO__ORIG_JAVA_JIT,
+ DSO__ORIG_FEDORA,
+ DSO__ORIG_UBUNTU,
+ DSO__ORIG_BUILDID,
+ DSO__ORIG_DSO,
+ DSO__ORIG_NOT_FOUND,
+};
static struct symbol *symbol__new(u64 start, u64 len,
const char *name, unsigned int priv_size,
@@ -72,6 +76,7 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size)
self->sym_priv_size = sym_priv_size;
self->find_symbol = dso__find_symbol;
self->slen_calculated = 0;
+ self->origin = DSO__ORIG_NOT_FOUND;
}
return self;
@@ -565,7 +570,7 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
goto out_elf_end;
secstrs = elf_getdata(sec_strndx, NULL);
- if (symstrs == NULL)
+ if (secstrs == NULL)
goto out_elf_end;
nr_syms = shdr.sh_size / shdr.sh_entsize;
@@ -652,11 +657,85 @@ out_close:
return err;
}
+#define BUILD_ID_SIZE 128
+
+static char *dso__read_build_id(struct dso *self, int verbose)
+{
+ int i;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ Elf_Data *build_id_data;
+ Elf_Scn *sec;
+ char *build_id = NULL, *bid;
+ unsigned char *raw;
+ Elf *elf;
+ int fd = open(self->name, O_RDONLY);
+
+ if (fd < 0)
+ goto out;
+
+ elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+ if (elf == NULL) {
+ if (verbose)
+ fprintf(stderr, "%s: cannot read %s ELF file.\n",
+ __func__, self->name);
+ goto out_close;
+ }
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ if (verbose)
+ fprintf(stderr, "%s: cannot get elf header.\n", __func__);
+ goto out_elf_end;
+ }
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL);
+ if (sec == NULL)
+ goto out_elf_end;
+
+ build_id_data = elf_getdata(sec, NULL);
+ if (build_id_data == NULL)
+ goto out_elf_end;
+ build_id = malloc(BUILD_ID_SIZE);
+ if (build_id == NULL)
+ goto out_elf_end;
+ raw = build_id_data->d_buf + 16;
+ bid = build_id;
+
+ for (i = 0; i < 20; ++i) {
+ sprintf(bid, "%02x", *raw);
+ ++raw;
+ bid += 2;
+ }
+ if (verbose >= 2)
+ printf("%s(%s): %s\n", __func__, self->name, build_id);
+out_elf_end:
+ elf_end(elf);
+out_close:
+ close(fd);
+out:
+ return build_id;
+}
+
+char dso__symtab_origin(const struct dso *self)
+{
+ static const char origin[] = {
+ [DSO__ORIG_KERNEL] = 'k',
+ [DSO__ORIG_JAVA_JIT] = 'j',
+ [DSO__ORIG_FEDORA] = 'f',
+ [DSO__ORIG_UBUNTU] = 'u',
+ [DSO__ORIG_BUILDID] = 'b',
+ [DSO__ORIG_DSO] = 'd',
+ };
+
+ if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND)
+ return '!';
+ return origin[self->origin];
+}
+
int dso__load(struct dso *self, symbol_filter_t filter, int verbose)
{
- int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug");
- char *name = malloc(size);
- int variant = 0;
+ int size = PATH_MAX;
+ char *name = malloc(size), *build_id = NULL;
int ret = -1;
int fd;
@@ -665,26 +744,43 @@ int dso__load(struct dso *self, symbol_filter_t filter, int verbose)
self->adjust_symbols = 0;
- if (strncmp(self->name, "/tmp/perf-", 10) == 0)
- return dso__load_perf_map(self, filter, verbose);
+ if (strncmp(self->name, "/tmp/perf-", 10) == 0) {
+ ret = dso__load_perf_map(self, filter, verbose);
+ self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT :
+ DSO__ORIG_NOT_FOUND;
+ return ret;
+ }
+
+ self->origin = DSO__ORIG_FEDORA - 1;
more:
do {
- switch (variant) {
- case 0: /* Fedora */
+ self->origin++;
+ switch (self->origin) {
+ case DSO__ORIG_FEDORA:
snprintf(name, size, "/usr/lib/debug%s.debug", self->name);
break;
- case 1: /* Ubuntu */
+ case DSO__ORIG_UBUNTU:
snprintf(name, size, "/usr/lib/debug%s", self->name);
break;
- case 2: /* Sane people */
+ case DSO__ORIG_BUILDID:
+ build_id = dso__read_build_id(self, verbose);
+ if (build_id != NULL) {
+ snprintf(name, size,
+ "/usr/lib/debug/.build-id/%.2s/%s.debug",
+ build_id, build_id + 2);
+ free(build_id);
+ break;
+ }
+ self->origin++;
+ /* Fall thru */
+ case DSO__ORIG_DSO:
snprintf(name, size, "%s", self->name);
break;
default:
goto out;
}
- variant++;
fd = open(name, O_RDONLY);
} while (fd < 0);
@@ -705,6 +801,8 @@ more:
}
out:
free(name);
+ if (ret < 0 && strstr(self->name, " (deleted)") != NULL)
+ return 0;
return ret;
}
@@ -820,6 +918,9 @@ int dso__load_kernel(struct dso *self, const char *vmlinux,
if (err <= 0)
err = dso__load_kallsyms(self, filter, verbose);
+ if (err > 0)
+ self->origin = DSO__ORIG_KERNEL;
+
return err;
}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 2f92b21c712..b53bf0125c1 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -7,6 +7,30 @@
#include <linux/rbtree.h>
#include "module.h"
+#ifdef HAVE_CPLUS_DEMANGLE
+extern char *cplus_demangle(const char *, int);
+
+static inline char *bfd_demangle(void __used *v, const char *c, int i)
+{
+ return cplus_demangle(c, i);
+}
+#else
+#ifdef NO_DEMANGLE
+static inline char *bfd_demangle(void __used *v, const char __used *c,
+ int __used i)
+{
+ return NULL;
+}
+#else
+#include <bfd.h>
+#endif
+#endif
+
+#ifndef DMGL_PARAMS
+#define DMGL_PARAMS (1 << 0) /* Include function args */
+#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
+#endif
+
struct symbol {
struct rb_node rb_node;
u64 start;
@@ -26,6 +50,7 @@ struct dso {
unsigned int sym_priv_size;
unsigned char adjust_symbols;
unsigned char slen_calculated;
+ unsigned char origin;
char name[0];
};
@@ -49,6 +74,7 @@ int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose);
int dso__load(struct dso *self, symbol_filter_t filter, int verbose);
size_t dso__fprintf(struct dso *self, FILE *fp);
+char dso__symtab_origin(const struct dso *self);
void symbol__init(void);
#endif /* _PERF_SYMBOL_ */
diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c
index 1eddae94bab..1150c6d5c7b 100644
--- a/virt/kvm/ioapic.c
+++ b/virt/kvm/ioapic.c
@@ -95,8 +95,6 @@ static int ioapic_service(struct kvm_ioapic *ioapic, unsigned int idx)
if (injected && pent->fields.trig_mode == IOAPIC_LEVEL_TRIG)
pent->fields.remote_irr = 1;
}
- if (!pent->fields.trig_mode)
- ioapic->irr &= ~(1 << idx);
return injected;
}
@@ -136,7 +134,8 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)
mask_after = ioapic->redirtbl[index].fields.mask;
if (mask_before != mask_after)
kvm_fire_mask_notifiers(ioapic->kvm, index, mask_after);
- if (ioapic->irr & (1 << index))
+ if (ioapic->redirtbl[index].fields.trig_mode == IOAPIC_LEVEL_TRIG
+ && ioapic->irr & (1 << index))
ioapic_service(ioapic, index);
break;
}
@@ -184,9 +183,10 @@ int kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level)
if (!level)
ioapic->irr &= ~mask;
else {
+ int edge = (entry.fields.trig_mode == IOAPIC_EDGE_TRIG);
ioapic->irr |= mask;
- if ((!entry.fields.trig_mode && old_irr != ioapic->irr)
- || !entry.fields.remote_irr)
+ if ((edge && old_irr != ioapic->irr) ||
+ (!edge && !entry.fields.remote_irr))
ret = ioapic_service(ioapic, irq);
}
}
diff --git a/virt/kvm/irq_comm.c b/virt/kvm/irq_comm.c
index a8bd466d00c..ddc17f0e2f3 100644
--- a/virt/kvm/irq_comm.c
+++ b/virt/kvm/irq_comm.c
@@ -160,7 +160,8 @@ void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin)
unsigned gsi = pin;
list_for_each_entry(e, &kvm->irq_routing, link)
- if (e->irqchip.irqchip == irqchip &&
+ if (e->type == KVM_IRQ_ROUTING_IRQCHIP &&
+ e->irqchip.irqchip == irqchip &&
e->irqchip.pin == pin) {
gsi = e->gsi;
break;
@@ -259,6 +260,7 @@ static int setup_routing_entry(struct kvm_kernel_irq_routing_entry *e,
int delta;
e->gsi = ue->gsi;
+ e->type = ue->type;
switch (ue->type) {
case KVM_IRQ_ROUTING_IRQCHIP:
delta = 0;