Merge branch 'for-linus' of git://git.kernel.dk/linux-block into next
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 4 Jun 2014 21:26:35 +0000 (14:26 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 4 Jun 2014 21:26:35 +0000 (14:26 -0700)
Pull block follow-up bits from Jens Axboe:
 "A few minor (but important) fixes for blk-mq for the -rc1 window.

   - Hot removal potential oops fix for single queue devices.  From me.

   - Two merged patches in late May meant that we accidentally lost a
     fix for freeing an active queue.  Fix that up.  From me.

   - A change of the blk_mq_tag_to_rq() API, passing in blk_mq_tags, to
     make life considerably easier for scsi-mq.  From me.

   - A schedule-while-atomic fix from Ming Lei, which would hit if the
     tag space was exhausted.

   - Missing __percpu annotation in one place in blk-mq.  Found by the
     magic Wu compile bot due to code being moved around by the previous
     patch, but it's actually an older issue.  From Ming Lei.

   - Clearing of tag of a flush request at end_io time.  From Ming Lei"

* 'for-linus' of git://git.kernel.dk/linux-block:
  block: mq flush: clear flush_rq's tag in flush_end_io()
  blk-mq: let blk_mq_tag_to_rq() take blk_mq_tags as the main parameter
  blk-mq: fix regression from commit 624dbe475416
  blk-mq: handle NULL req return from blk_map_request in single queue mode
  blk-mq: fix sparse warning on missed __percpu annotation
  blk-mq: fix schedule from atomic context
  blk-mq: move blk_mq_get_ctx/blk_mq_put_ctx to mq private header

1173 files changed:
Documentation/ABI/testing/sysfs-devices-system-cpu
Documentation/ABI/testing/sysfs-driver-hid-thingm [deleted file]
Documentation/ABI/testing/sysfs-power
Documentation/Changes
Documentation/DMA-attributes.txt
Documentation/DocBook/media/v4l/io.xml
Documentation/DocBook/media/v4l/media-ioc-enum-links.xml
Documentation/DocBook/media/v4l/pixfmt.xml
Documentation/DocBook/media/v4l/subdev-formats.xml
Documentation/DocBook/media/v4l/vidioc-dqevent.xml
Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml
Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml
Documentation/DocBook/media/v4l/vidioc-subscribe-event.xml
Documentation/cpu-freq/core.txt
Documentation/cpu-freq/cpu-drivers.txt
Documentation/cpu-freq/index.txt
Documentation/devicetree/bindings/arm/omap/omap.txt
Documentation/devicetree/bindings/arm/psci.txt
Documentation/devicetree/bindings/bus/mvebu-mbus.txt
Documentation/devicetree/bindings/dma/dma.txt
Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt
Documentation/devicetree/bindings/gpio/gpio-mcp23s08.txt
Documentation/devicetree/bindings/media/s5p-mfc.txt
Documentation/devicetree/bindings/mfd/mc13xxx.txt
Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt
Documentation/devicetree/bindings/mmc/samsung-sdhci.txt
Documentation/devicetree/bindings/mtd/gpmc-nand.txt
Documentation/devicetree/bindings/mtd/gpmc-nor.txt
Documentation/devicetree/bindings/mtd/gpmc-onenand.txt
Documentation/devicetree/bindings/pinctrl/brcm,bcm11351-pinctrl.txt
Documentation/devicetree/bindings/powerpc/4xx/reboot.txt
Documentation/devicetree/bindings/powerpc/fsl/dcsr.txt
Documentation/devicetree/bindings/regulator/regulator.txt
Documentation/devicetree/bindings/sound/ak4104.txt
Documentation/devicetree/bindings/sound/alc5623.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/cs42l56.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/fsl-sai.txt
Documentation/devicetree/bindings/sound/max98090.txt
Documentation/devicetree/bindings/sound/max98095.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/nokia,rx51.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/renesas,rsnd.txt
Documentation/devicetree/bindings/sound/rt5640.txt
Documentation/devicetree/bindings/sound/simple-card.txt
Documentation/devicetree/bindings/sound/snow.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/st,sta350.txt [new file with mode: 0644]
Documentation/devicetree/bindings/spi/spi-bus.txt
Documentation/devicetree/bindings/usb/dwc2.txt
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/devicetree/bindings/video/hdmi-connector.txt
Documentation/devicetree/bindings/video/lgphilips,lb035q02.txt [new file with mode: 0644]
Documentation/devicetree/bindings/video/panel-dpi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/video/sharp,ls037v7dw01.txt [new file with mode: 0644]
Documentation/devicetree/bindings/video/ti,omap4-dss.txt
Documentation/devicetree/bindings/video/ti,omap5-dss.txt [new file with mode: 0644]
Documentation/devicetree/bindings/video/toppoly,td028ttec1.txt [new file with mode: 0644]
Documentation/devicetree/bindings/video/tpo,td043mtea1.txt [new file with mode: 0644]
Documentation/dma-buf-sharing.txt
Documentation/dynamic-debug-howto.txt
Documentation/edac.txt
Documentation/fb/sm501.txt
Documentation/fb/sstfb.txt
Documentation/filesystems/proc.txt
Documentation/filesystems/sharedsubtree.txt
Documentation/gpio/consumer.txt
Documentation/hid/uhid.txt
Documentation/input/alps.txt
Documentation/input/input.txt
Documentation/kernel-parameters.txt
Documentation/mtd/nand/pxa3xx-nand.txt
Documentation/networking/can.txt
Documentation/networking/dccp.txt
Documentation/power/devices.txt
Documentation/power/opp.txt
Documentation/power/runtime_pm.txt
Documentation/power/states.txt
Documentation/power/swsusp.txt
Documentation/powerpc/transactional_memory.txt
Documentation/printk-formats.txt
Documentation/rbtree.txt
Documentation/rfkill.txt
Documentation/robust-futexes.txt
Documentation/s390/monreader.txt
Documentation/security/Yama.txt
Documentation/sound/alsa/ALSA-Configuration.txt
Documentation/trace/events.txt
Documentation/usb/mass-storage.txt
Documentation/video4linux/CARDLIST.bttv
Documentation/video4linux/CARDLIST.em28xx
Documentation/video4linux/fimc.txt
Documentation/video4linux/v4l2-pci-skeleton.c
Documentation/virtual/kvm/api.txt
Documentation/virtual/kvm/devices/vm.txt [new file with mode: 0644]
Documentation/virtual/kvm/ppc-pv.txt
Documentation/virtual/kvm/s390-diag.txt
Documentation/vm/transhuge.txt
Documentation/x86/earlyprintk.txt
Documentation/x86/i386/IO-APIC.txt
MAINTAINERS
arch/arc/include/asm/sections.h
arch/arc/kernel/devtree.c
arch/arm/include/asm/kvm_host.h
arch/arm/include/asm/kvm_psci.h
arch/arm/include/asm/prom.h
arch/arm/include/asm/psci.h
arch/arm/include/uapi/asm/kvm.h
arch/arm/kernel/devtree.c
arch/arm/kernel/psci.c
arch/arm/kernel/psci_smp.c
arch/arm/kvm/arm.c
arch/arm/kvm/handle_exit.c
arch/arm/kvm/psci.c
arch/arm/mach-davinci/da850.c
arch/arm/mach-exynos/exynos.c
arch/arm/mach-imx/clk-imx35.c
arch/arm/mach-omap2/display.c
arch/arm/mach-s5pv210/mach-goni.c
arch/arm/mm/init.c
arch/arm/plat-samsung/s5p-dev-mfc.c
arch/arm64/include/asm/cpu_ops.h
arch/arm64/include/asm/cputype.h
arch/arm64/include/asm/kvm_host.h
arch/arm64/include/asm/kvm_psci.h
arch/arm64/include/asm/pgtable.h
arch/arm64/include/asm/psci.h
arch/arm64/include/uapi/asm/kvm.h
arch/arm64/kernel/psci.c
arch/arm64/kernel/smp.c
arch/arm64/kvm/guest.c
arch/arm64/kvm/handle_exit.c
arch/arm64/kvm/sys_regs_generic_v8.c
arch/arm64/mm/init.c
arch/c6x/kernel/setup.c
arch/ia64/include/asm/acenv.h [new file with mode: 0644]
arch/ia64/include/asm/acpi.h
arch/ia64/kernel/acpi.c
arch/metag/kernel/setup.c
arch/microblaze/kernel/prom.c
arch/mips/Kconfig
arch/mips/cavium-octeon/setup.c
arch/mips/include/asm/kvm_host.h
arch/mips/include/asm/mach-jz4740/dma.h
arch/mips/include/asm/mips-boards/generic.h
arch/mips/include/asm/prom.h
arch/mips/include/uapi/asm/kvm.h
arch/mips/jz4740/board-qi_lb60.c
arch/mips/kernel/prom.c
arch/mips/kvm/kvm_locore.S
arch/mips/kvm/kvm_mips.c
arch/mips/kvm/kvm_mips_dyntrans.c
arch/mips/kvm/kvm_mips_emul.c
arch/mips/kvm/kvm_tlb.c
arch/mips/kvm/kvm_trap_emul.c
arch/mips/lantiq/prom.c
arch/mips/lantiq/prom.h
arch/mips/loongson/lemote-2f/clock.c
arch/mips/mm/cache.c
arch/mips/mti-malta/malta-time.c
arch/mips/mti-sead3/sead3-setup.c
arch/mips/netlogic/xlp/dt.c
arch/mips/ralink/of.c
arch/openrisc/kernel/vmlinux.h
arch/powerpc/include/asm/dcr-mmio.h
arch/powerpc/include/asm/disassemble.h
arch/powerpc/include/asm/kvm_asm.h
arch/powerpc/include/asm/kvm_book3s.h
arch/powerpc/include/asm/kvm_book3s_64.h
arch/powerpc/include/asm/kvm_book3s_asm.h
arch/powerpc/include/asm/kvm_booke.h
arch/powerpc/include/asm/kvm_host.h
arch/powerpc/include/asm/kvm_ppc.h
arch/powerpc/include/asm/prom.h
arch/powerpc/include/asm/reg.h
arch/powerpc/include/asm/reg_booke.h
arch/powerpc/include/uapi/asm/kvm.h
arch/powerpc/include/uapi/asm/kvm_para.h
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/align.c
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kernel/epapr_paravirt.c
arch/powerpc/kernel/fadump.c
arch/powerpc/kernel/kvm.c
arch/powerpc/kernel/paca.c
arch/powerpc/kernel/prom.c
arch/powerpc/kernel/rtas.c
arch/powerpc/kvm/Kconfig
arch/powerpc/kvm/book3s.c
arch/powerpc/kvm/book3s_32_mmu.c
arch/powerpc/kvm/book3s_32_mmu_host.c
arch/powerpc/kvm/book3s_64_mmu.c
arch/powerpc/kvm/book3s_64_mmu_host.c
arch/powerpc/kvm/book3s_64_mmu_hv.c
arch/powerpc/kvm/book3s_64_slb.S
arch/powerpc/kvm/book3s_emulate.c
arch/powerpc/kvm/book3s_exports.c
arch/powerpc/kvm/book3s_hv.c
arch/powerpc/kvm/book3s_hv_rm_mmu.c
arch/powerpc/kvm/book3s_hv_rmhandlers.S
arch/powerpc/kvm/book3s_interrupts.S
arch/powerpc/kvm/book3s_paired_singles.c
arch/powerpc/kvm/book3s_pr.c
arch/powerpc/kvm/book3s_pr_papr.c
arch/powerpc/kvm/book3s_rtas.c
arch/powerpc/kvm/book3s_segment.S
arch/powerpc/kvm/e500_emulate.c
arch/powerpc/kvm/emulate.c
arch/powerpc/kvm/mpic.c
arch/powerpc/kvm/powerpc.c
arch/powerpc/kvm/trace_pr.h
arch/powerpc/mm/hash_utils_64.c
arch/powerpc/mm/slb.c
arch/powerpc/platforms/52xx/efika.c
arch/powerpc/platforms/chrp/setup.c
arch/powerpc/platforms/powernv/opal.c
arch/powerpc/platforms/pseries/setup.c
arch/powerpc/sysdev/dcr.c
arch/s390/include/asm/ctl_reg.h
arch/s390/include/asm/kvm_host.h
arch/s390/include/asm/lowcore.h
arch/s390/include/asm/mmu.h
arch/s390/include/asm/mmu_context.h
arch/s390/include/asm/pgalloc.h
arch/s390/include/asm/pgtable.h
arch/s390/include/asm/ptrace.h
arch/s390/include/asm/sclp.h
arch/s390/include/uapi/asm/kvm.h
arch/s390/include/uapi/asm/sie.h [new file with mode: 0644]
arch/s390/kernel/asm-offsets.c
arch/s390/kernel/entry.S
arch/s390/kernel/entry64.S
arch/s390/kvm/Makefile
arch/s390/kvm/diag.c
arch/s390/kvm/gaccess.c [new file with mode: 0644]
arch/s390/kvm/gaccess.h
arch/s390/kvm/guestdbg.c [new file with mode: 0644]
arch/s390/kvm/intercept.c
arch/s390/kvm/interrupt.c
arch/s390/kvm/kvm-s390.c
arch/s390/kvm/kvm-s390.h
arch/s390/kvm/priv.c
arch/s390/kvm/sigp.c
arch/s390/kvm/trace-s390.h
arch/s390/kvm/trace.h
arch/s390/mm/pgtable.c
arch/x86/include/asm/acenv.h [new file with mode: 0644]
arch/x86/include/asm/acpi.h
arch/x86/include/asm/kvm_emulate.h
arch/x86/include/asm/kvm_host.h
arch/x86/include/asm/traps.h
arch/x86/kernel/devicetree.c
arch/x86/kernel/kvm.c
arch/x86/kvm/cpuid.c
arch/x86/kvm/cpuid.h
arch/x86/kvm/emulate.c
arch/x86/kvm/irq.c
arch/x86/kvm/lapic.c
arch/x86/kvm/mmu.c
arch/x86/kvm/mmu.h
arch/x86/kvm/paging_tmpl.h
arch/x86/kvm/pmu.c
arch/x86/kvm/svm.c
arch/x86/kvm/trace.h
arch/x86/kvm/vmx.c
arch/x86/kvm/x86.c
arch/xtensa/kernel/setup.c
drivers/acpi/Makefile
drivers/acpi/acpi_cmos_rtc.c
drivers/acpi/acpi_extlog.c
drivers/acpi/acpi_lpss.c
drivers/acpi/acpi_memhotplug.c
drivers/acpi/acpi_pad.c
drivers/acpi/acpi_platform.c
drivers/acpi/acpi_pnp.c [new file with mode: 0644]
drivers/acpi/acpi_processor.c
drivers/acpi/acpica/Makefile
drivers/acpi/acpica/acapps.h [new file with mode: 0644]
drivers/acpi/acpica/acevents.h
drivers/acpi/acpica/acglobal.h
drivers/acpi/acpica/aclocal.h
drivers/acpi/acpica/acpredef.h
drivers/acpi/acpica/actables.h
drivers/acpi/acpica/acutils.h
drivers/acpi/acpica/evgpe.c
drivers/acpi/acpica/evgpeblk.c
drivers/acpi/acpica/evgpeinit.c
drivers/acpi/acpica/evmisc.c
drivers/acpi/acpica/evsci.c
drivers/acpi/acpica/evxface.c
drivers/acpi/acpica/evxfgpe.c
drivers/acpi/acpica/exconfig.c
drivers/acpi/acpica/exdump.c
drivers/acpi/acpica/hwpci.c
drivers/acpi/acpica/rscreate.c
drivers/acpi/acpica/tbdata.c [new file with mode: 0644]
drivers/acpi/acpica/tbfadt.c
drivers/acpi/acpica/tbfind.c
drivers/acpi/acpica/tbinstal.c
drivers/acpi/acpica/tbutils.c
drivers/acpi/acpica/tbxface.c
drivers/acpi/acpica/tbxfload.c
drivers/acpi/acpica/utdecode.c
drivers/acpi/acpica/utglobal.c
drivers/acpi/acpica/utstring.c
drivers/acpi/acpica/utxferror.c
drivers/acpi/apei/einj.c
drivers/acpi/battery.c
drivers/acpi/bus.c
drivers/acpi/container.c
drivers/acpi/device_pm.c
drivers/acpi/internal.h
drivers/acpi/nvs.c
drivers/acpi/osl.c
drivers/acpi/processor_driver.c
drivers/acpi/scan.c
drivers/acpi/sleep.c
drivers/acpi/tables.c
drivers/acpi/thermal.c
drivers/acpi/utils.c
drivers/acpi/video.c
drivers/base/dma-buf.c
drivers/base/platform.c
drivers/base/power/main.c
drivers/base/power/opp.c
drivers/base/power/wakeup.c
drivers/char/tpm/tpm_acpi.c
drivers/clk/Makefile
drivers/clk/clk-fractional-divider.c [new file with mode: 0644]
drivers/clk/rockchip/clk-rockchip.c
drivers/clk/sunxi/clk-sunxi.c
drivers/clk/ti/gate.c
drivers/clocksource/clksrc-of.c
drivers/clocksource/versatile.c
drivers/cpufreq/Kconfig.arm
drivers/cpufreq/Kconfig.x86
drivers/cpufreq/Makefile
drivers/cpufreq/acpi-cpufreq.c
drivers/cpufreq/arm_big_little.c
drivers/cpufreq/cpufreq-nforce2.c
drivers/cpufreq/cpufreq.c
drivers/cpufreq/cpufreq_opp.c [new file with mode: 0644]
drivers/cpufreq/cpufreq_stats.c
drivers/cpufreq/dbx500-cpufreq.c
drivers/cpufreq/elanfreq.c
drivers/cpufreq/exynos-cpufreq.c
drivers/cpufreq/exynos-cpufreq.h
drivers/cpufreq/exynos5440-cpufreq.c
drivers/cpufreq/freq_table.c
drivers/cpufreq/imx6q-cpufreq.c
drivers/cpufreq/intel_pstate.c
drivers/cpufreq/longhaul.c
drivers/cpufreq/pasemi-cpufreq.c
drivers/cpufreq/powernow-k6.c
drivers/cpufreq/powernow-k8.c
drivers/cpufreq/powernow-k8.h
drivers/cpufreq/powernv-cpufreq.c
drivers/cpufreq/ppc_cbe_cpufreq.c
drivers/cpufreq/s3c2416-cpufreq.c
drivers/cpufreq/s3c64xx-cpufreq.c
drivers/cpufreq/s5pv210-cpufreq.c
drivers/cpufreq/speedstep-centrino.c
drivers/cpufreq/tegra-cpufreq.c
drivers/cpuidle/Kconfig.arm
drivers/cpuidle/Makefile
drivers/cpuidle/cpuidle-clps711x.c [new file with mode: 0644]
drivers/devfreq/Kconfig
drivers/devfreq/devfreq.c
drivers/devfreq/exynos/Makefile
drivers/devfreq/exynos/exynos4_bus.c
drivers/devfreq/exynos/exynos5_bus.c
drivers/devfreq/exynos/exynos_ppmu.c
drivers/devfreq/exynos/exynos_ppmu.h
drivers/firewire/Kconfig
drivers/firewire/core.h
drivers/firewire/net.c
drivers/firewire/ohci.c
drivers/gpu/drm/exynos/exynos_drm_ipp.c
drivers/gpu/drm/nouveau/nouveau_backlight.c
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-core.c
drivers/hid/hid-debug.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-rmi.c [new file with mode: 0644]
drivers/hid/hid-saitek.c
drivers/hid/hid-sensor-hub.c
drivers/hid/hid-sony.c
drivers/hid/hid-thingm.c
drivers/hid/i2c-hid/i2c-hid.c
drivers/hid/uhid.c
drivers/hid/usbhid/hid-quirks.c
drivers/irqchip/irq-mxs.c
drivers/irqchip/irq-s3c24xx.c
drivers/irqchip/irqchip.c
drivers/irqchip/irqchip.h
drivers/leds/Kconfig
drivers/media/dvb-core/dvb-usb-ids.h
drivers/media/dvb-frontends/Kconfig
drivers/media/dvb-frontends/Makefile
drivers/media/dvb-frontends/si2168.c [new file with mode: 0644]
drivers/media/dvb-frontends/si2168.h [new file with mode: 0644]
drivers/media/dvb-frontends/si2168_priv.h [new file with mode: 0644]
drivers/media/i2c/ad9389b.c
drivers/media/i2c/adv7180.c
drivers/media/i2c/adv7183.c
drivers/media/i2c/adv7511.c
drivers/media/i2c/adv7604.c
drivers/media/i2c/adv7842.c
drivers/media/i2c/bt819.c
drivers/media/i2c/cx25840/cx25840-core.c
drivers/media/i2c/ks0127.c
drivers/media/i2c/m5mols/m5mols_capture.c
drivers/media/i2c/ml86v7667.c
drivers/media/i2c/msp3400-driver.c
drivers/media/i2c/mt9p031.c
drivers/media/i2c/saa6752hs.c
drivers/media/i2c/saa7110.c
drivers/media/i2c/saa7115.c
drivers/media/i2c/saa717x.c
drivers/media/i2c/saa7191.c
drivers/media/i2c/smiapp-pll.h
drivers/media/i2c/smiapp/smiapp-core.c
drivers/media/i2c/smiapp/smiapp-quirk.c
drivers/media/i2c/smiapp/smiapp-quirk.h
drivers/media/i2c/smiapp/smiapp-reg-defs.h
drivers/media/i2c/smiapp/smiapp-regs.c
drivers/media/i2c/smiapp/smiapp-regs.h
drivers/media/i2c/soc_camera/tw9910.c
drivers/media/i2c/sony-btf-mpx.c
drivers/media/i2c/ths8200.c
drivers/media/i2c/tvaudio.c
drivers/media/i2c/tvp514x.c
drivers/media/i2c/tvp5150.c
drivers/media/i2c/tvp7002.c
drivers/media/i2c/tw2804.c
drivers/media/i2c/tw9903.c
drivers/media/i2c/tw9906.c
drivers/media/i2c/vp27smpx.c
drivers/media/i2c/vpx3220.c
drivers/media/media-device.c
drivers/media/media-devnode.c
drivers/media/parport/bw-qcam.c
drivers/media/pci/bt8xx/bttv-cards.c
drivers/media/pci/bt8xx/bttv-driver.c
drivers/media/pci/bt8xx/bttv.h
drivers/media/pci/bt8xx/dst.c
drivers/media/pci/cx18/cx18-av-core.c
drivers/media/pci/cx18/cx18-fileops.c
drivers/media/pci/cx18/cx18-gpio.c
drivers/media/pci/cx18/cx18-ioctl.c
drivers/media/pci/cx23885/cx23885-video.c
drivers/media/pci/cx88/cx88-core.c
drivers/media/pci/ivtv/ivtv-alsa-pcm.c
drivers/media/pci/ivtv/ivtv-fileops.c
drivers/media/pci/ivtv/ivtv-ioctl.c
drivers/media/pci/saa7134/Kconfig
drivers/media/pci/saa7134/saa7134-alsa.c
drivers/media/pci/saa7134/saa7134-core.c
drivers/media/pci/saa7134/saa7134-dvb.c
drivers/media/pci/saa7134/saa7134-empress.c
drivers/media/pci/saa7134/saa7134-i2c.c
drivers/media/pci/saa7134/saa7134-reg.h
drivers/media/pci/saa7134/saa7134-ts.c
drivers/media/pci/saa7134/saa7134-tvaudio.c
drivers/media/pci/saa7134/saa7134-vbi.c
drivers/media/pci/saa7134/saa7134-video.c
drivers/media/pci/saa7134/saa7134.h
drivers/media/pci/saa7146/mxb.c
drivers/media/pci/sta2x11/sta2x11_vip.c
drivers/media/pci/ttpci/av7110_av.c
drivers/media/pci/zoran/zoran_device.c
drivers/media/pci/zoran/zoran_driver.c
drivers/media/platform/blackfin/bfin_capture.c
drivers/media/platform/coda.c
drivers/media/platform/davinci/vpbe_display.c
drivers/media/platform/davinci/vpfe_capture.c
drivers/media/platform/davinci/vpif_capture.c
drivers/media/platform/davinci/vpif_capture.h
drivers/media/platform/davinci/vpif_display.c
drivers/media/platform/davinci/vpif_display.h
drivers/media/platform/exynos-gsc/gsc-m2m.c
drivers/media/platform/exynos4-is/Kconfig
drivers/media/platform/exynos4-is/common.c
drivers/media/platform/exynos4-is/fimc-capture.c
drivers/media/platform/exynos4-is/fimc-core.c
drivers/media/platform/exynos4-is/fimc-core.h
drivers/media/platform/exynos4-is/fimc-is.c
drivers/media/platform/exynos4-is/fimc-isp-video.c
drivers/media/platform/exynos4-is/fimc-isp.h
drivers/media/platform/exynos4-is/fimc-lite-reg.c
drivers/media/platform/exynos4-is/fimc-lite.c
drivers/media/platform/exynos4-is/fimc-lite.h
drivers/media/platform/exynos4-is/fimc-m2m.c
drivers/media/platform/exynos4-is/fimc-reg.c
drivers/media/platform/exynos4-is/media-dev.c
drivers/media/platform/exynos4-is/media-dev.h
drivers/media/platform/exynos4-is/mipi-csis.c
drivers/media/platform/fsl-viu.c
drivers/media/platform/marvell-ccic/mcam-core.c
drivers/media/platform/mem2mem_testdev.c
drivers/media/platform/mx2_emmaprp.c
drivers/media/platform/s3c-camif/camif-capture.c
drivers/media/platform/s5p-jpeg/jpeg-core.c
drivers/media/platform/s5p-jpeg/jpeg-core.h
drivers/media/platform/s5p-mfc/regs-mfc-v6.h
drivers/media/platform/s5p-mfc/regs-mfc-v7.h
drivers/media/platform/s5p-mfc/regs-mfc-v8.h [new file with mode: 0644]
drivers/media/platform/s5p-mfc/s5p_mfc.c
drivers/media/platform/s5p-mfc/s5p_mfc_common.h
drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h
drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h
drivers/media/platform/s5p-tv/hdmi_drv.c
drivers/media/platform/s5p-tv/hdmiphy_drv.c
drivers/media/platform/s5p-tv/mixer_video.c
drivers/media/platform/soc_camera/atmel-isi.c
drivers/media/platform/soc_camera/mx2_camera.c
drivers/media/platform/soc_camera/mx3_camera.c
drivers/media/platform/soc_camera/rcar_vin.c
drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
drivers/media/platform/soc_camera/soc_camera.c
drivers/media/platform/ti-vpe/csc.c
drivers/media/platform/ti-vpe/sc.c
drivers/media/platform/ti-vpe/vpdma.c
drivers/media/platform/ti-vpe/vpdma.h
drivers/media/platform/ti-vpe/vpe.c
drivers/media/platform/timblogiw.c
drivers/media/platform/vino.c
drivers/media/platform/vivi.c
drivers/media/platform/vsp1/vsp1_video.c
drivers/media/rc/mceusb.c
drivers/media/tuners/Kconfig
drivers/media/tuners/Makefile
drivers/media/tuners/si2157.c [new file with mode: 0644]
drivers/media/tuners/si2157.h [new file with mode: 0644]
drivers/media/tuners/si2157_priv.h [new file with mode: 0644]
drivers/media/tuners/xc5000.c
drivers/media/usb/au0828/au0828-dvb.c
drivers/media/usb/au0828/au0828-video.c
drivers/media/usb/au0828/au0828.h
drivers/media/usb/cx231xx/cx231xx-417.c
drivers/media/usb/cx231xx/cx231xx-video.c
drivers/media/usb/dvb-usb-v2/rtl28xxu.c
drivers/media/usb/dvb-usb/az6027.c
drivers/media/usb/dvb-usb/dib0700.h
drivers/media/usb/dvb-usb/dib0700_core.c
drivers/media/usb/dvb-usb/dib0700_devices.c
drivers/media/usb/dvb-usb/technisat-usb2.c
drivers/media/usb/em28xx/Kconfig
drivers/media/usb/em28xx/em28xx-audio.c
drivers/media/usb/em28xx/em28xx-camera.c
drivers/media/usb/em28xx/em28xx-cards.c
drivers/media/usb/em28xx/em28xx-dvb.c
drivers/media/usb/em28xx/em28xx-i2c.c
drivers/media/usb/em28xx/em28xx-v4l.h
drivers/media/usb/em28xx/em28xx-vbi.c
drivers/media/usb/em28xx/em28xx-video.c
drivers/media/usb/em28xx/em28xx.h
drivers/media/usb/gspca/Kconfig
drivers/media/usb/gspca/Makefile
drivers/media/usb/gspca/dtcs033.c [new file with mode: 0644]
drivers/media/usb/gspca/gl860/gl860-mi2020.c
drivers/media/usb/pvrusb2/pvrusb2-hdw.c
drivers/media/usb/pwc/pwc-if.c
drivers/media/usb/s2255/s2255drv.c
drivers/media/usb/stk1160/stk1160-core.c
drivers/media/usb/stk1160/stk1160-v4l.c
drivers/media/usb/stk1160/stk1160.h
drivers/media/usb/tm6000/tm6000-cards.c
drivers/media/usb/tm6000/tm6000-video.c
drivers/media/usb/usbtv/usbtv-video.c
drivers/media/usb/usbvision/usbvision-video.c
drivers/media/usb/uvc/uvc_video.c
drivers/media/v4l2-core/Kconfig
drivers/media/v4l2-core/Makefile
drivers/media/v4l2-core/tuner-core.c
drivers/media/v4l2-core/v4l2-device.c
drivers/media/v4l2-core/v4l2-dv-timings.c
drivers/media/v4l2-core/v4l2-event.c
drivers/media/v4l2-core/v4l2-ioctl.c
drivers/media/v4l2-core/v4l2-subdev.c
drivers/media/v4l2-core/videobuf-dma-contig.c
drivers/media/v4l2-core/videobuf2-core.c
drivers/media/v4l2-core/videobuf2-dma-sg.c
drivers/media/v4l2-core/videobuf2-dvb.c [new file with mode: 0644]
drivers/mfd/db8500-prcmu.c
drivers/mfd/mc13xxx-core.c
drivers/net/irda/sh_sir.c
drivers/net/wireless/ath/ath10k/wmi.h
drivers/net/wireless/ath/ath6kl/wmi.h
drivers/of/Kconfig
drivers/of/Makefile
drivers/of/address.c
drivers/of/base.c
drivers/of/fdt.c
drivers/of/fdt_address.c [new file with mode: 0644]
drivers/of/irq.c
drivers/of/of_pci_irq.c
drivers/of/of_reserved_mem.c
drivers/of/platform.c
drivers/of/selftest.c
drivers/of/testcase-data/testcases.dtsi
drivers/of/testcase-data/tests-phandle.dtsi
drivers/of/testcase-data/tests-platform.dtsi [new file with mode: 0644]
drivers/pci/pci.c
drivers/platform/x86/acer-wmi.c
drivers/pnp/pnpacpi/core.c
drivers/pnp/resource.c
drivers/power/power_supply_core.c
drivers/powercap/intel_rapl.c
drivers/s390/char/sclp_early.c
drivers/scsi/aic7xxx/aic79xx.h
drivers/scsi/aic7xxx/aic7xxx_osm.c
drivers/sh/clk/core.c
drivers/staging/lustre/lustre/llite/file.c
drivers/staging/media/as102/as102_usb_drv.c
drivers/staging/media/bcm2048/radio-bcm2048.c
drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h
drivers/staging/media/davinci_vpfe/vpfe_video.c
drivers/staging/media/davinci_vpfe/vpfe_video.h
drivers/staging/media/dt3155v4l/dt3155v4l.c
drivers/staging/media/go7007/go7007-v4l2.c
drivers/staging/media/go7007/go7007.txt
drivers/staging/media/go7007/s2250-board.c
drivers/staging/media/go7007/saa7134-go7007.c
drivers/staging/media/msi3101/sdr-msi3101.c
drivers/staging/media/omap24xx/tcm825x.c
drivers/staging/media/omap24xx/tcm825x.h
drivers/staging/media/omap4iss/Kconfig
drivers/staging/media/omap4iss/iss.c
drivers/staging/media/omap4iss/iss.h
drivers/staging/media/omap4iss/iss_csi2.c
drivers/staging/media/omap4iss/iss_video.h
drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
drivers/staging/media/sn9c102/sn9c102.h
drivers/staging/media/sn9c102/sn9c102_core.c
drivers/staging/media/sn9c102/sn9c102_devtable.h
drivers/staging/media/sn9c102/sn9c102_hv7131d.c
drivers/staging/media/sn9c102/sn9c102_hv7131r.c
drivers/staging/media/sn9c102/sn9c102_mi0343.c
drivers/staging/media/sn9c102/sn9c102_mi0360.c
drivers/staging/media/sn9c102/sn9c102_ov7630.c
drivers/staging/media/sn9c102/sn9c102_ov7660.c
drivers/staging/media/sn9c102/sn9c102_pas106b.c
drivers/staging/media/sn9c102/sn9c102_pas202bcb.c
drivers/staging/media/sn9c102/sn9c102_sensor.h
drivers/staging/media/sn9c102/sn9c102_tas5110c1b.c
drivers/staging/media/sn9c102/sn9c102_tas5110d.c
drivers/staging/media/sn9c102/sn9c102_tas5130d1b.c
drivers/staging/media/solo6x10/Kconfig
drivers/staging/media/solo6x10/solo6x10-enc.c
drivers/staging/media/solo6x10/solo6x10-offsets.h
drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c
drivers/staging/media/solo6x10/solo6x10-v4l2.c
drivers/thermal/cpu_cooling.c
drivers/tty/serial/amba-pl011.c
drivers/tty/serial/earlycon.c
drivers/video/backlight/backlight.c
drivers/video/console/fbcon.c
drivers/video/fbdev/Kconfig
drivers/video/fbdev/bf54x-lq043fb.c
drivers/video/fbdev/core/fbmem.c
drivers/video/fbdev/core/fbmon.c
drivers/video/fbdev/da8xx-fb.c
drivers/video/fbdev/exynos/Kconfig
drivers/video/fbdev/fb-puv3.c
drivers/video/fbdev/gbefb.c
drivers/video/fbdev/grvga.c
drivers/video/fbdev/matrox/matroxfb_base.h
drivers/video/fbdev/mbx/Makefile
drivers/video/fbdev/mbx/mbxdebugfs.c
drivers/video/fbdev/mbx/mbxfb.c
drivers/video/fbdev/mmp/Kconfig
drivers/video/fbdev/mmp/fb/mmpfb.c
drivers/video/fbdev/mmp/hw/Kconfig
drivers/video/fbdev/mmp/hw/mmp_ctrl.h
drivers/video/fbdev/mx3fb.c
drivers/video/fbdev/omap/Kconfig
drivers/video/fbdev/omap/Makefile
drivers/video/fbdev/omap/lcdc.c
drivers/video/fbdev/omap/omapfb_main.c
drivers/video/fbdev/omap2/Makefile
drivers/video/fbdev/omap2/displays-new/connector-hdmi.c
drivers/video/fbdev/omap2/displays-new/panel-dpi.c
drivers/video/fbdev/omap2/displays-new/panel-lgphilips-lb035q02.c
drivers/video/fbdev/omap2/displays-new/panel-nec-nl8048hl11.c
drivers/video/fbdev/omap2/displays-new/panel-sharp-ls037v7dw01.c
drivers/video/fbdev/omap2/displays-new/panel-tpo-td028ttec1.c
drivers/video/fbdev/omap2/displays-new/panel-tpo-td043mtea1.c
drivers/video/fbdev/omap2/dss/Kconfig
drivers/video/fbdev/omap2/dss/Makefile
drivers/video/fbdev/omap2/dss/core.c
drivers/video/fbdev/omap2/dss/dispc.c
drivers/video/fbdev/omap2/dss/dpi.c
drivers/video/fbdev/omap2/dss/dsi.c
drivers/video/fbdev/omap2/dss/dss.c
drivers/video/fbdev/omap2/dss/dss.h
drivers/video/fbdev/omap2/dss/dss_features.c
drivers/video/fbdev/omap2/dss/hdmi.h
drivers/video/fbdev/omap2/dss/hdmi4.c
drivers/video/fbdev/omap2/dss/hdmi4_core.c
drivers/video/fbdev/omap2/dss/hdmi5.c [new file with mode: 0644]
drivers/video/fbdev/omap2/dss/hdmi5_core.c [new file with mode: 0644]
drivers/video/fbdev/omap2/dss/hdmi5_core.h [new file with mode: 0644]
drivers/video/fbdev/omap2/dss/hdmi_common.c
drivers/video/fbdev/omap2/dss/hdmi_phy.c
drivers/video/fbdev/omap2/dss/hdmi_pll.c
drivers/video/fbdev/omap2/dss/hdmi_wp.c
drivers/video/fbdev/omap2/dss/omapdss-boot-init.c [new file with mode: 0644]
drivers/video/fbdev/omap2/dss/venc_panel.c [deleted file]
drivers/video/fbdev/pxa3xx-gcu.c
drivers/video/fbdev/s3fb.c
drivers/video/fbdev/wm8505fb.c
drivers/video/of_display_timing.c
fs/9p/vfs_file.c
fs/afs/flock.c
fs/ceph/locks.c
fs/fuse/file.c
fs/gfs2/aops.c
fs/gfs2/bmap.c
fs/gfs2/file.c
fs/gfs2/glops.c
fs/gfs2/glops.h
fs/gfs2/incore.h
fs/gfs2/inode.c
fs/gfs2/log.c
fs/gfs2/log.h
fs/gfs2/lops.c
fs/gfs2/ops_fstype.c
fs/gfs2/quota.c
fs/gfs2/recovery.c
fs/gfs2/rgrp.c
fs/gfs2/super.c
fs/gfs2/sys.c
fs/gfs2/trans.c
fs/jfs/acl.c
fs/jfs/jfs_dmap.c
fs/jfs/jfs_inode.c
fs/jfs/jfs_logmgr.c
fs/jfs/super.c
fs/locks.c
fs/nfs/file.c
include/acpi/acpi.h
include/acpi/acpi_bus.h
include/acpi/acpi_drivers.h
include/acpi/acpi_io.h
include/acpi/acpixf.h
include/acpi/actbl.h
include/acpi/actbl1.h
include/acpi/actbl2.h
include/acpi/actypes.h
include/acpi/platform/acenvex.h [new file with mode: 0644]
include/acpi/platform/acgcc.h
include/acpi/platform/aclinux.h
include/acpi/platform/aclinuxex.h [new file with mode: 0644]
include/acpi/video.h
include/asm-generic/vmlinux.lds.h
include/linux/acpi.h
include/linux/backlight.h
include/linux/clk-provider.h
include/linux/clocksource.h
include/linux/cpufreq.h
include/linux/devfreq.h
include/linux/fb.h
include/linux/firewire.h
include/linux/hid.h
include/linux/kvm_host.h
include/linux/of.h
include/linux/of_address.h
include/linux/of_fdt.h
include/linux/of_irq.h
include/linux/of_pci.h
include/linux/of_platform.h
include/linux/of_reserved_mem.h
include/linux/omap-dma.h
include/linux/platform_data/adau17x1.h [new file with mode: 0644]
include/linux/platform_data/mipi-csis.h [deleted file]
include/linux/pm.h
include/linux/pm_opp.h
include/linux/pm_runtime.h
include/linux/power_supply.h
include/linux/serial_core.h
include/linux/string.h
include/linux/suspend.h
include/media/adv7604.h
include/media/davinci/vpbe_display.h
include/media/davinci/vpfe_capture.h
include/media/exynos-fimc.h [moved from include/media/s5p_fimc.h with 87% similarity]
include/media/media-device.h
include/media/media-devnode.h
include/media/v4l2-device.h
include/media/v4l2-event.h
include/media/v4l2-subdev.h
include/media/videobuf2-core.h
include/media/videobuf2-dvb.h [new file with mode: 0644]
include/net/wimax.h
include/sound/atmel-ac97c.h
include/sound/core.h
include/sound/cs42l56.h [new file with mode: 0644]
include/sound/omap-pcm.h [new file with mode: 0644]
include/sound/rcar_snd.h
include/sound/rt5640.h
include/sound/rt5645.h [new file with mode: 0644]
include/sound/rt5651.h [new file with mode: 0644]
include/sound/rt5677.h [new file with mode: 0644]
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc.h
include/sound/sta350.h [new file with mode: 0644]
include/trace/events/asoc.h
include/trace/events/filelock.h [new file with mode: 0644]
include/uapi/linux/Kbuild
include/uapi/linux/gfs2_ondisk.h
include/uapi/linux/input.h
include/uapi/linux/kvm.h
include/uapi/linux/psci.h [new file with mode: 0644]
include/uapi/linux/v4l2-common.h
include/uapi/linux/v4l2-dv-timings.h
include/uapi/linux/v4l2-mediabus.h
include/uapi/linux/v4l2-subdev.h
include/uapi/linux/videodev2.h
include/uapi/sound/asound.h
include/uapi/sound/firewire.h
include/video/omapdss.h
kernel/power/Kconfig
kernel/power/hibernate.c
kernel/power/main.c
kernel/power/power.h
kernel/power/suspend.c
kernel/power/suspend_test.c
kernel/power/swap.c
lib/Kconfig.debug
lib/string.c
mm/dmapool.c
scripts/mod/modpost.c
sound/aoa/codecs/onyx.c
sound/arm/pxa2xx-pcm.c
sound/arm/pxa2xx-pcm.h
sound/atmel/ac97c.c
sound/core/pcm_lib.c
sound/core/seq/seq_midi.c
sound/firewire/Kconfig
sound/firewire/Makefile
sound/firewire/amdtp.c
sound/firewire/amdtp.h
sound/firewire/bebob/Makefile [new file with mode: 0644]
sound/firewire/bebob/bebob.c [new file with mode: 0644]
sound/firewire/bebob/bebob.h [new file with mode: 0644]
sound/firewire/bebob/bebob_command.c [new file with mode: 0644]
sound/firewire/bebob/bebob_focusrite.c [new file with mode: 0644]
sound/firewire/bebob/bebob_hwdep.c [new file with mode: 0644]
sound/firewire/bebob/bebob_maudio.c [new file with mode: 0644]
sound/firewire/bebob/bebob_midi.c [new file with mode: 0644]
sound/firewire/bebob/bebob_pcm.c [new file with mode: 0644]
sound/firewire/bebob/bebob_proc.c [new file with mode: 0644]
sound/firewire/bebob/bebob_stream.c [new file with mode: 0644]
sound/firewire/bebob/bebob_terratec.c [new file with mode: 0644]
sound/firewire/bebob/bebob_yamaha.c [new file with mode: 0644]
sound/firewire/cmp.c
sound/firewire/cmp.h
sound/firewire/dice.c
sound/firewire/fcp.c
sound/firewire/fcp.h
sound/firewire/fireworks/Makefile [new file with mode: 0644]
sound/firewire/fireworks/fireworks.c [new file with mode: 0644]
sound/firewire/fireworks/fireworks.h [new file with mode: 0644]
sound/firewire/fireworks/fireworks_command.c [new file with mode: 0644]
sound/firewire/fireworks/fireworks_hwdep.c [new file with mode: 0644]
sound/firewire/fireworks/fireworks_midi.c [new file with mode: 0644]
sound/firewire/fireworks/fireworks_pcm.c [new file with mode: 0644]
sound/firewire/fireworks/fireworks_proc.c [new file with mode: 0644]
sound/firewire/fireworks/fireworks_stream.c [new file with mode: 0644]
sound/firewire/fireworks/fireworks_transaction.c [new file with mode: 0644]
sound/firewire/speakers.c
sound/isa/gus/interwave.c
sound/mips/au1x00.c
sound/oss/mpu401.c
sound/oss/swarm_cs4297a.c
sound/pci/fm801.c
sound/pci/hda/Kconfig
sound/pci/hda/Makefile
sound/pci/hda/hda_auto_parser.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_tegra.c [new file with mode: 0644]
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/lola/lola_proc.c
sound/pci/lx6464es/lx_core.c
sound/soc/atmel/Kconfig
sound/soc/atmel/atmel-pcm-pdc.c
sound/soc/atmel/snd-soc-afeb9260.c
sound/soc/blackfin/Kconfig
sound/soc/blackfin/Makefile
sound/soc/blackfin/bfin-eval-adau1x61.c [new file with mode: 0644]
sound/soc/blackfin/bfin-eval-adau1x81.c [new file with mode: 0644]
sound/soc/codecs/88pm860x-codec.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ab8500-codec.c
sound/soc/codecs/ad1980.c
sound/soc/codecs/adau1373.c
sound/soc/codecs/adau1761-i2c.c [new file with mode: 0644]
sound/soc/codecs/adau1761-spi.c [new file with mode: 0644]
sound/soc/codecs/adau1761.c [new file with mode: 0644]
sound/soc/codecs/adau1761.h [new file with mode: 0644]
sound/soc/codecs/adau1781-i2c.c [new file with mode: 0644]
sound/soc/codecs/adau1781-spi.c [new file with mode: 0644]
sound/soc/codecs/adau1781.c [new file with mode: 0644]
sound/soc/codecs/adau1781.h [new file with mode: 0644]
sound/soc/codecs/adau17x1.c [new file with mode: 0644]
sound/soc/codecs/adau17x1.h [new file with mode: 0644]
sound/soc/codecs/adav80x.c
sound/soc/codecs/ak4104.c
sound/soc/codecs/ak4641.c
sound/soc/codecs/ak4642.c
sound/soc/codecs/alc5623.c
sound/soc/codecs/arizona.h
sound/soc/codecs/cq93vc.c
sound/soc/codecs/cs4270.c
sound/soc/codecs/cs4271.c
sound/soc/codecs/cs42l51-i2c.c [new file with mode: 0644]
sound/soc/codecs/cs42l51.c
sound/soc/codecs/cs42l51.h
sound/soc/codecs/cs42l52.c
sound/soc/codecs/cs42l56.c [new file with mode: 0644]
sound/soc/codecs/cs42l56.h [new file with mode: 0644]
sound/soc/codecs/cs42xx8.c
sound/soc/codecs/da7210.c
sound/soc/codecs/da7213.c
sound/soc/codecs/da732x.c
sound/soc/codecs/da9055.c
sound/soc/codecs/hdmi.c
sound/soc/codecs/lm4857.c
sound/soc/codecs/max9768.c
sound/soc/codecs/max98088.c
sound/soc/codecs/max98090.c
sound/soc/codecs/max98090.h
sound/soc/codecs/max98095.c
sound/soc/codecs/mc13783.c
sound/soc/codecs/pcm1681.c
sound/soc/codecs/pcm512x.c
sound/soc/codecs/rl6231.c [new file with mode: 0644]
sound/soc/codecs/rl6231.h [new file with mode: 0644]
sound/soc/codecs/rt5631.c
sound/soc/codecs/rt5640.c
sound/soc/codecs/rt5640.h
sound/soc/codecs/rt5645.c [new file with mode: 0644]
sound/soc/codecs/rt5645.h [new file with mode: 0644]
sound/soc/codecs/rt5651.c [new file with mode: 0644]
sound/soc/codecs/rt5651.h [new file with mode: 0644]
sound/soc/codecs/rt5677.c [new file with mode: 0644]
sound/soc/codecs/rt5677.h [new file with mode: 0644]
sound/soc/codecs/sgtl5000.c
sound/soc/codecs/si476x.c
sound/soc/codecs/sirf-audio-codec.c
sound/soc/codecs/sirf-audio-codec.h
sound/soc/codecs/sta32x.c
sound/soc/codecs/sta350.c [new file with mode: 0644]
sound/soc/codecs/sta350.h [new file with mode: 0644]
sound/soc/codecs/tas5086.c
sound/soc/codecs/tlv320aic23-i2c.c
sound/soc/codecs/tlv320aic23.c
sound/soc/codecs/tlv320aic31xx.c
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/tlv320dac33.c
sound/soc/codecs/tpa6130a2.c
sound/soc/codecs/twl4030.c
sound/soc/codecs/twl6040.c
sound/soc/codecs/wl1273.c
sound/soc/codecs/wm2000.c
sound/soc/codecs/wm2200.c
sound/soc/codecs/wm5100.c
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8400.c
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8804.c
sound/soc/codecs/wm8804.h
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8904.c
sound/soc/codecs/wm8955.c
sound/soc/codecs/wm8958-dsp2.c
sound/soc/codecs/wm8960.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8983.c
sound/soc/codecs/wm8985.c
sound/soc/codecs/wm8988.c
sound/soc/codecs/wm8990.c
sound/soc/codecs/wm8991.c
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8995.c
sound/soc/codecs/wm8996.c
sound/soc/codecs/wm8997.c
sound/soc/codecs/wm9081.c
sound/soc/codecs/wm9713.c
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_hubs.c
sound/soc/davinci/Kconfig
sound/soc/davinci/davinci-evm.c
sound/soc/davinci/davinci-i2s.c
sound/soc/davinci/davinci-mcasp.c
sound/soc/davinci/davinci-mcasp.h
sound/soc/davinci/davinci-pcm.c
sound/soc/davinci/davinci-pcm.h
sound/soc/davinci/davinci-vcif.c
sound/soc/fsl/Kconfig
sound/soc/fsl/Makefile
sound/soc/fsl/fsl_esai.c
sound/soc/fsl/fsl_sai.c
sound/soc/fsl/fsl_sai.h
sound/soc/fsl/fsl_spdif.c
sound/soc/fsl/fsl_spdif.h
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/fsl_ssi.h
sound/soc/fsl/fsl_ssi_dbg.c [new file with mode: 0644]
sound/soc/fsl/imx-pcm-dma.c
sound/soc/generic/simple-card.c
sound/soc/intel/Kconfig
sound/soc/intel/Makefile
sound/soc/intel/byt-max98090.c [new file with mode: 0644]
sound/soc/intel/byt-rt5640.c
sound/soc/intel/haswell.c
sound/soc/intel/sst-acpi.c
sound/soc/intel/sst-baytrail-dsp.c
sound/soc/intel/sst-baytrail-ipc.c
sound/soc/intel/sst-baytrail-ipc.h
sound/soc/intel/sst-baytrail-pcm.c
sound/soc/intel/sst-dsp-priv.h
sound/soc/intel/sst-firmware.c
sound/soc/intel/sst-haswell-ipc.c
sound/soc/intel/sst-haswell-pcm.c
sound/soc/intel/sst-mfld-dsp.h
sound/soc/intel/sst-mfld-platform-compress.c [new file with mode: 0644]
sound/soc/intel/sst-mfld-platform-pcm.c [moved from sound/soc/intel/sst-mfld-platform.c with 67% similarity]
sound/soc/intel/sst-mfld-platform.h
sound/soc/jz4740/Kconfig
sound/soc/jz4740/jz4740-i2s.c
sound/soc/jz4740/qi_lb60.c
sound/soc/kirkwood/kirkwood-t5325.c
sound/soc/nuc900/Kconfig
sound/soc/nuc900/nuc900-ac97.c
sound/soc/omap/Kconfig
sound/soc/omap/am3517evm.c
sound/soc/omap/ams-delta.c
sound/soc/omap/n810.c
sound/soc/omap/omap-abe-twl6040.c
sound/soc/omap/omap-dmic.c
sound/soc/omap/omap-hdmi-card.c
sound/soc/omap/omap-hdmi.c
sound/soc/omap/omap-mcbsp.c
sound/soc/omap/omap-mcbsp.h
sound/soc/omap/omap-mcpdm.c
sound/soc/omap/omap-pcm.c
sound/soc/omap/omap-twl4030.c
sound/soc/omap/omap3pandora.c
sound/soc/omap/osk5912.c
sound/soc/omap/rx51.c
sound/soc/pxa/Kconfig
sound/soc/pxa/brownstone.c
sound/soc/pxa/hx4700.c
sound/soc/pxa/palm27x.c
sound/soc/pxa/poodle.c
sound/soc/pxa/pxa-ssp.c
sound/soc/pxa/pxa2xx-pcm.c
sound/soc/pxa/ttc-dkb.c
sound/soc/samsung/Kconfig
sound/soc/samsung/Makefile
sound/soc/samsung/ac97.c
sound/soc/samsung/bells.c
sound/soc/samsung/dma.c
sound/soc/samsung/dma.h
sound/soc/samsung/dmaengine.c
sound/soc/samsung/goni_wm8994.c
sound/soc/samsung/h1940_uda1380.c
sound/soc/samsung/i2s.c
sound/soc/samsung/i2s.h
sound/soc/samsung/idma.c
sound/soc/samsung/littlemill.c
sound/soc/samsung/lowland.c
sound/soc/samsung/neo1973_wm8753.c
sound/soc/samsung/pcm.c
sound/soc/samsung/rx1950_uda1380.c
sound/soc/samsung/s3c-i2s-v2.c
sound/soc/samsung/s3c2412-i2s.c
sound/soc/samsung/s3c24xx-i2s.c
sound/soc/samsung/s3c24xx_simtec_hermes.c
sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
sound/soc/samsung/smartq_wm8987.c
sound/soc/samsung/smdk_wm8580.c
sound/soc/samsung/smdk_wm8580pcm.c
sound/soc/samsung/smdk_wm8994.c
sound/soc/samsung/smdk_wm8994pcm.c
sound/soc/samsung/snow.c [new file with mode: 0644]
sound/soc/samsung/spdif.c
sound/soc/samsung/speyside.c
sound/soc/samsung/tobermory.c
sound/soc/sh/Kconfig
sound/soc/sh/rcar/Makefile
sound/soc/sh/rcar/adg.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/dvc.c [new file with mode: 0644]
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/src.c
sound/soc/sh/rcar/ssi.c
sound/soc/sirf/sirf-audio-port.c
sound/soc/sirf/sirf-audio-port.h [deleted file]
sound/soc/soc-cache.c
sound/soc/soc-compress.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-devres.c
sound/soc/soc-io.c
sound/soc/soc-jack.c
sound/soc/soc-pcm.c
sound/soc/tegra/tegra_alc5632.c
sound/soc/tegra/tegra_max98090.c
sound/soc/tegra/tegra_rt5640.c
sound/soc/tegra/tegra_wm8903.c
sound/soc/tegra/tegra_wm9712.c
sound/soc/ux500/mop500_ab8500.c
sound/synth/emux/soundfont.c
sound/usb/Kconfig
sound/usb/Makefile
sound/usb/bcd2000/Makefile [new file with mode: 0644]
sound/usb/bcd2000/bcd2000.c [new file with mode: 0644]
sound/usb/mixer.c
tools/power/acpi/Makefile
tools/power/acpi/common/cmfsize.c [new file with mode: 0644]
tools/power/acpi/common/getopt.c [new file with mode: 0644]
tools/power/acpi/man/acpidump.8
tools/power/acpi/os_specific/service_layers/oslinuxtbl.c [new file with mode: 0644]
tools/power/acpi/os_specific/service_layers/osunixdir.c [new file with mode: 0644]
tools/power/acpi/os_specific/service_layers/osunixmap.c [new file with mode: 0644]
tools/power/acpi/tools/acpidump/acpidump.c [deleted file]
tools/power/acpi/tools/acpidump/acpidump.h [new file with mode: 0644]
tools/power/acpi/tools/acpidump/apdump.c [new file with mode: 0644]
tools/power/acpi/tools/acpidump/apfiles.c [new file with mode: 0644]
tools/power/acpi/tools/acpidump/apmain.c [new file with mode: 0644]
tools/power/acpi/tools/ec/Makefile [new file with mode: 0644]
tools/power/acpi/tools/ec/ec_access.c [new file with mode: 0644]
tools/power/cpupower/Makefile
tools/power/cpupower/README
tools/power/cpupower/ToDo
tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c
tools/power/cpupower/man/cpupower-frequency-info.1
tools/power/cpupower/man/cpupower-idle-set.1
tools/power/cpupower/man/cpupower-info.1
tools/power/cpupower/man/cpupower-set.1
tools/power/cpupower/utils/cpufreq-info.c
tools/power/cpupower/utils/cpuidle-set.c
tools/power/cpupower/utils/cpupower-info.c
tools/power/cpupower/utils/cpupower-set.c
tools/power/cpupower/utils/cpupower.c
tools/power/x86/turbostat/turbostat.c
virt/kvm/async_pf.c
virt/kvm/eventfd.c
virt/kvm/irq_comm.c
virt/kvm/irqchip.c
virt/kvm/kvm_main.c

index d5a0d33..acb9bfc 100644 (file)
@@ -128,7 +128,7 @@ Description:        Discover cpuidle policy and mechanism
 
 What:          /sys/devices/system/cpu/cpu#/cpufreq/*
 Date:          pre-git history
-Contact:       cpufreq@vger.kernel.org
+Contact:       linux-pm@vger.kernel.org
 Description:   Discover and change clock speed of CPUs
 
                Clock scaling allows you to change the clock speed of the
@@ -146,7 +146,7 @@ Description:        Discover and change clock speed of CPUs
 
 What:          /sys/devices/system/cpu/cpu#/cpufreq/freqdomain_cpus
 Date:          June 2013
-Contact:       cpufreq@vger.kernel.org
+Contact:       linux-pm@vger.kernel.org
 Description:   Discover CPUs in the same CPU frequency coordination domain
 
                freqdomain_cpus is the list of CPUs (online+offline) that share
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-thingm b/Documentation/ABI/testing/sysfs-driver-hid-thingm
deleted file mode 100644 (file)
index abcffee..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-What:          /sys/class/leds/blink1::<serial>/rgb
-Date:          January 2013
-Contact:       Vivien Didelot <vivien.didelot@savoirfairelinux.com>
-Description:   The ThingM blink1 is an USB RGB LED. The color notation is
-               3-byte hexadecimal. Read this attribute to get the last set
-               color. Write the 24-bit hexadecimal color to change the current
-               LED color. The default color is full white (0xFFFFFF).
-               For instance, set the color to green with: echo 00FF00 > rgb
-
-What:          /sys/class/leds/blink1::<serial>/fade
-Date:          January 2013
-Contact:       Vivien Didelot <vivien.didelot@savoirfairelinux.com>
-Description:   This attribute allows to set a fade time in milliseconds for
-               the next color change. Read the attribute to know the current
-               fade time. The default value is set to 0 (no fade time). For
-               instance, set a fade time of 2 seconds with: echo 2000 > fade
-
-What:          /sys/class/leds/blink1::<serial>/play
-Date:          January 2013
-Contact:       Vivien Didelot <vivien.didelot@savoirfairelinux.com>
-Description:   This attribute is used to play/pause the light patterns. Write 1
-               to start playing, 0 to stop. Reading this attribute returns the
-               current playing status.
index 64c9276..f455181 100644 (file)
@@ -7,19 +7,30 @@ Description:
                subsystem.
 
 What:          /sys/power/state
-Date:          August 2006
+Date:          May 2014
 Contact:       Rafael J. Wysocki <rjw@rjwysocki.net>
 Description:
-               The /sys/power/state file controls the system power state.
-               Reading from this file returns what states are supported,
-               which is hard-coded to 'freeze' (Low-Power Idle), 'standby'
-               (Power-On Suspend), 'mem' (Suspend-to-RAM), and 'disk'
-               (Suspend-to-Disk).
+               The /sys/power/state file controls system sleep states.
+               Reading from this file returns the available sleep state
+               labels, which may be "mem", "standby", "freeze" and "disk"
+               (hibernation).  The meanings of the first three labels depend on
+               the relative_sleep_states command line argument as follows:
+                1) relative_sleep_states = 1
+                   "mem", "standby", "freeze" represent non-hibernation sleep
+                   states from the deepest ("mem", always present) to the
+                   shallowest ("freeze").  "standby" and "freeze" may or may
+                   not be present depending on the capabilities of the
+                   platform.  "freeze" can only be present if "standby" is
+                   present.
+                2) relative_sleep_states = 0 (default)
+                   "mem" - "suspend-to-RAM", present if supported.
+                   "standby" - "power-on suspend", present if supported.
+                   "freeze" - "suspend-to-idle", always present.
 
                Writing to this file one of these strings causes the system to
-               transition into that state. Please see the file
-               Documentation/power/states.txt for a description of each of
-               these states.
+               transition into the corresponding state, if available.  See
+               Documentation/power/states.txt for a description of what
+               "suspend-to-RAM", "power-on suspend" and "suspend-to-idle" mean.
 
 What:          /sys/power/disk
 Date:          September 2006
index 07c75d1..2254db0 100644 (file)
@@ -73,6 +73,11 @@ Perl
 You will need perl 5 and the following modules: Getopt::Long, Getopt::Std,
 File::Basename, and File::Find to build the kernel.
 
+BC
+--
+
+You will need bc to build kernels 3.10 and higher
+
 
 System utilities
 ================
index cc2450d..18dc52c 100644 (file)
@@ -98,5 +98,5 @@ DMA_ATTR_FORCE_CONTIGUOUS
 By default DMA-mapping subsystem is allowed to assemble the buffer
 allocated by dma_alloc_attrs() function from individual pages if it can
 be mapped as contiguous chunk into device dma address space. By
-specifing this attribute the allocated buffer is forced to be contiguous
+specifying this attribute the allocated buffer is forced to be contiguous
 also in physical memory.
index 97a69bf..a086a5d 100644 (file)
@@ -125,7 +125,7 @@ location of the buffers in device memory can be determined with the
 <structfield>m.offset</structfield> and <structfield>length</structfield>
 returned in a &v4l2-buffer; are passed as sixth and second parameter to the
 <function>mmap()</function> function. When using the multi-planar API,
-struct &v4l2-buffer; contains an array of &v4l2-plane; structures, each
+&v4l2-buffer; contains an array of &v4l2-plane; structures, each
 containing its own <structfield>m.offset</structfield> and
 <structfield>length</structfield>. When using the multi-planar API, every
 plane of every buffer has to be mapped separately, so the number of
@@ -699,7 +699,12 @@ linkend="v4l2-buf-type" /></entry>
 buffer. It depends on the negotiated data format and may change with
 each buffer for compressed variable size data like JPEG images.
 Drivers must set this field when <structfield>type</structfield>
-refers to an input stream, applications when it refers to an output stream.</entry>
+refers to an input stream, applications when it refers to an output stream.
+If the application sets this to 0 for an output stream, then
+<structfield>bytesused</structfield> will be set to the size of the
+buffer (see the <structfield>length</structfield> field of this struct) by
+the driver. For multiplanar formats this field is ignored and the
+<structfield>planes</structfield> pointer is used instead.</entry>
          </row>
          <row>
            <entry>__u32</entry>
@@ -861,7 +866,11 @@ should set this to 0.</entry>
            <entry></entry>
            <entry>The number of bytes occupied by data in the plane
              (its payload). Drivers must set this field when <structfield>type</structfield>
-             refers to an input stream, applications when it refers to an output stream.</entry>
+             refers to an input stream, applications when it refers to an output stream.
+             If the application sets this to 0 for an output stream, then
+             <structfield>bytesused</structfield> will be set to the size of the
+             plane (see the <structfield>length</structfield> field of this struct)
+             by the driver.</entry>
          </row>
          <row>
            <entry>__u32</entry>
index cf85485..74fb394 100644 (file)
            <entry>Entity id, set by the application.</entry>
          </row>
          <row>
-           <entry>struct &media-pad-desc;</entry>
+           <entry>&media-pad-desc;</entry>
            <entry>*<structfield>pads</structfield></entry>
            <entry>Pointer to a pads array allocated by the application. Ignored
            if NULL.</entry>
          </row>
          <row>
-           <entry>struct &media-link-desc;</entry>
+           <entry>&media-link-desc;</entry>
            <entry>*<structfield>links</structfield></entry>
            <entry>Pointer to a links array allocated by the application. Ignored
            if NULL.</entry>
         &cs-str;
        <tbody valign="top">
          <row>
-           <entry>struct &media-pad-desc;</entry>
+           <entry>&media-pad-desc;</entry>
            <entry><structfield>source</structfield></entry>
            <entry>Pad at the origin of this link.</entry>
          </row>
          <row>
-           <entry>struct &media-pad-desc;</entry>
+           <entry>&media-pad-desc;</entry>
            <entry><structfield>sink</structfield></entry>
            <entry>Pad at the target of this link.</entry>
          </row>
index ea514d6..91dcbc8 100644 (file)
@@ -772,7 +772,7 @@ extended control <constant>V4L2_CID_MPEG_STREAM_TYPE</constant>, see
          </row>
          <row id="V4L2-PIX-FMT-H264-MVC">
                <entry><constant>V4L2_PIX_FMT_H264_MVC</constant></entry>
-               <entry>'MVC'</entry>
+               <entry>'M264'</entry>
                <entry>H264 MVC video elementary stream.</entry>
          </row>
          <row id="V4L2-PIX-FMT-H263">
@@ -812,7 +812,7 @@ extended control <constant>V4L2_CID_MPEG_STREAM_TYPE</constant>, see
          </row>
          <row id="V4L2-PIX-FMT-VP8">
                <entry><constant>V4L2_PIX_FMT_VP8</constant></entry>
-               <entry>'VP8'</entry>
+               <entry>'VP80'</entry>
                <entry>VP8 video elementary stream.</entry>
          </row>
        </tbody>
index 7331ce1..b2d5a03 100644 (file)
              <entry>y<subscript>1</subscript></entry>
              <entry>y<subscript>0</subscript></entry>
            </row>
+           <row id="V4L2-MBUS-FMT-UYVY10-2X10">
+             <entry>V4L2_MBUS_FMT_UYVY10_2X10</entry>
+             <entry>0x2018</entry>
+             <entry></entry>
+             &dash-ent-22;
+             <entry>u<subscript>9</subscript></entry>
+             <entry>u<subscript>8</subscript></entry>
+             <entry>u<subscript>7</subscript></entry>
+             <entry>u<subscript>6</subscript></entry>
+             <entry>u<subscript>5</subscript></entry>
+             <entry>u<subscript>4</subscript></entry>
+             <entry>u<subscript>3</subscript></entry>
+             <entry>u<subscript>2</subscript></entry>
+             <entry>u<subscript>1</subscript></entry>
+             <entry>u<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-22;
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-22;
+             <entry>v<subscript>9</subscript></entry>
+             <entry>v<subscript>8</subscript></entry>
+             <entry>v<subscript>7</subscript></entry>
+             <entry>v<subscript>6</subscript></entry>
+             <entry>v<subscript>5</subscript></entry>
+             <entry>v<subscript>4</subscript></entry>
+             <entry>v<subscript>3</subscript></entry>
+             <entry>v<subscript>2</subscript></entry>
+             <entry>v<subscript>1</subscript></entry>
+             <entry>v<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-22;
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row id="V4L2-MBUS-FMT-VYUY10-2X10">
+             <entry>V4L2_MBUS_FMT_VYUY10_2X10</entry>
+             <entry>0x2019</entry>
+             <entry></entry>
+             &dash-ent-22;
+             <entry>v<subscript>9</subscript></entry>
+             <entry>v<subscript>8</subscript></entry>
+             <entry>v<subscript>7</subscript></entry>
+             <entry>v<subscript>6</subscript></entry>
+             <entry>v<subscript>5</subscript></entry>
+             <entry>v<subscript>4</subscript></entry>
+             <entry>v<subscript>3</subscript></entry>
+             <entry>v<subscript>2</subscript></entry>
+             <entry>v<subscript>1</subscript></entry>
+             <entry>v<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-22;
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-22;
+             <entry>u<subscript>9</subscript></entry>
+             <entry>u<subscript>8</subscript></entry>
+             <entry>u<subscript>7</subscript></entry>
+             <entry>u<subscript>6</subscript></entry>
+             <entry>u<subscript>5</subscript></entry>
+             <entry>u<subscript>4</subscript></entry>
+             <entry>u<subscript>3</subscript></entry>
+             <entry>u<subscript>2</subscript></entry>
+             <entry>u<subscript>1</subscript></entry>
+             <entry>u<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-22;
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
            <row id="V4L2-MBUS-FMT-YUYV10-2X10">
              <entry>V4L2_MBUS_FMT_YUYV10_2X10</entry>
              <entry>0x200b</entry>
              <entry>v<subscript>1</subscript></entry>
              <entry>v<subscript>0</subscript></entry>
            </row>
+           <row id="V4L2-MBUS-FMT-UYVY10-1X20">
+             <entry>V4L2_MBUS_FMT_UYVY10_1X20</entry>
+             <entry>0x201a</entry>
+             <entry></entry>
+             &dash-ent-12;
+             <entry>u<subscript>9</subscript></entry>
+             <entry>u<subscript>8</subscript></entry>
+             <entry>u<subscript>7</subscript></entry>
+             <entry>u<subscript>6</subscript></entry>
+             <entry>u<subscript>5</subscript></entry>
+             <entry>u<subscript>4</subscript></entry>
+             <entry>u<subscript>3</subscript></entry>
+             <entry>u<subscript>2</subscript></entry>
+             <entry>u<subscript>1</subscript></entry>
+             <entry>u<subscript>0</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-12;
+             <entry>v<subscript>9</subscript></entry>
+             <entry>v<subscript>8</subscript></entry>
+             <entry>v<subscript>7</subscript></entry>
+             <entry>v<subscript>6</subscript></entry>
+             <entry>v<subscript>5</subscript></entry>
+             <entry>v<subscript>4</subscript></entry>
+             <entry>v<subscript>3</subscript></entry>
+             <entry>v<subscript>2</subscript></entry>
+             <entry>v<subscript>1</subscript></entry>
+             <entry>v<subscript>0</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row id="V4L2-MBUS-FMT-VYUY10-1X20">
+             <entry>V4L2_MBUS_FMT_VYUY10_1X20</entry>
+             <entry>0x201b</entry>
+             <entry></entry>
+             &dash-ent-12;
+             <entry>v<subscript>9</subscript></entry>
+             <entry>v<subscript>8</subscript></entry>
+             <entry>v<subscript>7</subscript></entry>
+             <entry>v<subscript>6</subscript></entry>
+             <entry>v<subscript>5</subscript></entry>
+             <entry>v<subscript>4</subscript></entry>
+             <entry>v<subscript>3</subscript></entry>
+             <entry>v<subscript>2</subscript></entry>
+             <entry>v<subscript>1</subscript></entry>
+             <entry>v<subscript>0</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-12;
+             <entry>u<subscript>9</subscript></entry>
+             <entry>u<subscript>8</subscript></entry>
+             <entry>u<subscript>7</subscript></entry>
+             <entry>u<subscript>6</subscript></entry>
+             <entry>u<subscript>5</subscript></entry>
+             <entry>u<subscript>4</subscript></entry>
+             <entry>u<subscript>3</subscript></entry>
+             <entry>u<subscript>2</subscript></entry>
+             <entry>u<subscript>1</subscript></entry>
+             <entry>u<subscript>0</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
            <row id="V4L2-MBUS-FMT-YUYV10-1X20">
              <entry>V4L2_MBUS_FMT_YUYV10_1X20</entry>
              <entry>0x200d</entry>
              <entry>v<subscript>1</subscript></entry>
              <entry>v<subscript>0</subscript></entry>
            </row>
+           <row id="V4L2-MBUS-FMT-UYVY12-2X12">
+             <entry>V4L2_MBUS_FMT_UYVY12_2X12</entry>
+             <entry>0x201c</entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>u<subscript>11</subscript></entry>
+             <entry>u<subscript>10</subscript></entry>
+             <entry>u<subscript>9</subscript></entry>
+             <entry>u<subscript>8</subscript></entry>
+             <entry>u<subscript>7</subscript></entry>
+             <entry>u<subscript>6</subscript></entry>
+             <entry>u<subscript>5</subscript></entry>
+             <entry>u<subscript>4</subscript></entry>
+             <entry>u<subscript>3</subscript></entry>
+             <entry>u<subscript>2</subscript></entry>
+             <entry>u<subscript>1</subscript></entry>
+             <entry>u<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>v<subscript>11</subscript></entry>
+             <entry>v<subscript>10</subscript></entry>
+             <entry>v<subscript>9</subscript></entry>
+             <entry>v<subscript>8</subscript></entry>
+             <entry>v<subscript>7</subscript></entry>
+             <entry>v<subscript>6</subscript></entry>
+             <entry>v<subscript>5</subscript></entry>
+             <entry>v<subscript>4</subscript></entry>
+             <entry>v<subscript>3</subscript></entry>
+             <entry>v<subscript>2</subscript></entry>
+             <entry>v<subscript>1</subscript></entry>
+             <entry>v<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row id="V4L2-MBUS-FMT-VYUY12-2X12">
+             <entry>V4L2_MBUS_FMT_VYUY12_2X12</entry>
+             <entry>0x201d</entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>v<subscript>11</subscript></entry>
+             <entry>v<subscript>10</subscript></entry>
+             <entry>v<subscript>9</subscript></entry>
+             <entry>v<subscript>8</subscript></entry>
+             <entry>v<subscript>7</subscript></entry>
+             <entry>v<subscript>6</subscript></entry>
+             <entry>v<subscript>5</subscript></entry>
+             <entry>v<subscript>4</subscript></entry>
+             <entry>v<subscript>3</subscript></entry>
+             <entry>v<subscript>2</subscript></entry>
+             <entry>v<subscript>1</subscript></entry>
+             <entry>v<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>u<subscript>11</subscript></entry>
+             <entry>u<subscript>10</subscript></entry>
+             <entry>u<subscript>9</subscript></entry>
+             <entry>u<subscript>8</subscript></entry>
+             <entry>u<subscript>7</subscript></entry>
+             <entry>u<subscript>6</subscript></entry>
+             <entry>u<subscript>5</subscript></entry>
+             <entry>u<subscript>4</subscript></entry>
+             <entry>u<subscript>3</subscript></entry>
+             <entry>u<subscript>2</subscript></entry>
+             <entry>u<subscript>1</subscript></entry>
+             <entry>u<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row id="V4L2-MBUS-FMT-YUYV12-2X12">
+             <entry>V4L2_MBUS_FMT_YUYV12_2X12</entry>
+             <entry>0x201e</entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>u<subscript>11</subscript></entry>
+             <entry>u<subscript>10</subscript></entry>
+             <entry>u<subscript>9</subscript></entry>
+             <entry>u<subscript>8</subscript></entry>
+             <entry>u<subscript>7</subscript></entry>
+             <entry>u<subscript>6</subscript></entry>
+             <entry>u<subscript>5</subscript></entry>
+             <entry>u<subscript>4</subscript></entry>
+             <entry>u<subscript>3</subscript></entry>
+             <entry>u<subscript>2</subscript></entry>
+             <entry>u<subscript>1</subscript></entry>
+             <entry>u<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>v<subscript>11</subscript></entry>
+             <entry>v<subscript>10</subscript></entry>
+             <entry>v<subscript>9</subscript></entry>
+             <entry>v<subscript>8</subscript></entry>
+             <entry>v<subscript>7</subscript></entry>
+             <entry>v<subscript>6</subscript></entry>
+             <entry>v<subscript>5</subscript></entry>
+             <entry>v<subscript>4</subscript></entry>
+             <entry>v<subscript>3</subscript></entry>
+             <entry>v<subscript>2</subscript></entry>
+             <entry>v<subscript>1</subscript></entry>
+             <entry>v<subscript>0</subscript></entry>
+           </row>
+           <row id="V4L2-MBUS-FMT-YVYU12-2X12">
+             <entry>V4L2_MBUS_FMT_YVYU12_2X12</entry>
+             <entry>0x201f</entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>v<subscript>11</subscript></entry>
+             <entry>v<subscript>10</subscript></entry>
+             <entry>v<subscript>9</subscript></entry>
+             <entry>v<subscript>8</subscript></entry>
+             <entry>v<subscript>7</subscript></entry>
+             <entry>v<subscript>6</subscript></entry>
+             <entry>v<subscript>5</subscript></entry>
+             <entry>v<subscript>4</subscript></entry>
+             <entry>v<subscript>3</subscript></entry>
+             <entry>v<subscript>2</subscript></entry>
+             <entry>v<subscript>1</subscript></entry>
+             <entry>v<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-20;
+             <entry>u<subscript>11</subscript></entry>
+             <entry>u<subscript>10</subscript></entry>
+             <entry>u<subscript>9</subscript></entry>
+             <entry>u<subscript>8</subscript></entry>
+             <entry>u<subscript>7</subscript></entry>
+             <entry>u<subscript>6</subscript></entry>
+             <entry>u<subscript>5</subscript></entry>
+             <entry>u<subscript>4</subscript></entry>
+             <entry>u<subscript>3</subscript></entry>
+             <entry>u<subscript>2</subscript></entry>
+             <entry>u<subscript>1</subscript></entry>
+             <entry>u<subscript>0</subscript></entry>
+           </row>
+           <row id="V4L2-MBUS-FMT-UYVY12-1X24">
+             <entry>V4L2_MBUS_FMT_UYVY12_1X24</entry>
+             <entry>0x2020</entry>
+             <entry></entry>
+             &dash-ent-8;
+             <entry>u<subscript>11</subscript></entry>
+             <entry>u<subscript>10</subscript></entry>
+             <entry>u<subscript>9</subscript></entry>
+             <entry>u<subscript>8</subscript></entry>
+             <entry>u<subscript>7</subscript></entry>
+             <entry>u<subscript>6</subscript></entry>
+             <entry>u<subscript>5</subscript></entry>
+             <entry>u<subscript>4</subscript></entry>
+             <entry>u<subscript>3</subscript></entry>
+             <entry>u<subscript>2</subscript></entry>
+             <entry>u<subscript>1</subscript></entry>
+             <entry>u<subscript>0</subscript></entry>
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-8;
+             <entry>v<subscript>11</subscript></entry>
+             <entry>v<subscript>10</subscript></entry>
+             <entry>v<subscript>9</subscript></entry>
+             <entry>v<subscript>8</subscript></entry>
+             <entry>v<subscript>7</subscript></entry>
+             <entry>v<subscript>6</subscript></entry>
+             <entry>v<subscript>5</subscript></entry>
+             <entry>v<subscript>4</subscript></entry>
+             <entry>v<subscript>3</subscript></entry>
+             <entry>v<subscript>2</subscript></entry>
+             <entry>v<subscript>1</subscript></entry>
+             <entry>v<subscript>0</subscript></entry>
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row id="V4L2-MBUS-FMT-VYUY12-1X24">
+             <entry>V4L2_MBUS_FMT_VYUY12_1X24</entry>
+             <entry>0x2021</entry>
+             <entry></entry>
+             &dash-ent-8;
+             <entry>v<subscript>11</subscript></entry>
+             <entry>v<subscript>10</subscript></entry>
+             <entry>v<subscript>9</subscript></entry>
+             <entry>v<subscript>8</subscript></entry>
+             <entry>v<subscript>7</subscript></entry>
+             <entry>v<subscript>6</subscript></entry>
+             <entry>v<subscript>5</subscript></entry>
+             <entry>v<subscript>4</subscript></entry>
+             <entry>v<subscript>3</subscript></entry>
+             <entry>v<subscript>2</subscript></entry>
+             <entry>v<subscript>1</subscript></entry>
+             <entry>v<subscript>0</subscript></entry>
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-8;
+             <entry>u<subscript>11</subscript></entry>
+             <entry>u<subscript>10</subscript></entry>
+             <entry>u<subscript>9</subscript></entry>
+             <entry>u<subscript>8</subscript></entry>
+             <entry>u<subscript>7</subscript></entry>
+             <entry>u<subscript>6</subscript></entry>
+             <entry>u<subscript>5</subscript></entry>
+             <entry>u<subscript>4</subscript></entry>
+             <entry>u<subscript>3</subscript></entry>
+             <entry>u<subscript>2</subscript></entry>
+             <entry>u<subscript>1</subscript></entry>
+             <entry>u<subscript>0</subscript></entry>
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+           </row>
+           <row id="V4L2-MBUS-FMT-YUYV12-1X24">
+             <entry>V4L2_MBUS_FMT_YUYV12_1X24</entry>
+             <entry>0x2022</entry>
+             <entry></entry>
+             &dash-ent-8;
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+             <entry>u<subscript>11</subscript></entry>
+             <entry>u<subscript>10</subscript></entry>
+             <entry>u<subscript>9</subscript></entry>
+             <entry>u<subscript>8</subscript></entry>
+             <entry>u<subscript>7</subscript></entry>
+             <entry>u<subscript>6</subscript></entry>
+             <entry>u<subscript>5</subscript></entry>
+             <entry>u<subscript>4</subscript></entry>
+             <entry>u<subscript>3</subscript></entry>
+             <entry>u<subscript>2</subscript></entry>
+             <entry>u<subscript>1</subscript></entry>
+             <entry>u<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-8;
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+             <entry>v<subscript>11</subscript></entry>
+             <entry>v<subscript>10</subscript></entry>
+             <entry>v<subscript>9</subscript></entry>
+             <entry>v<subscript>8</subscript></entry>
+             <entry>v<subscript>7</subscript></entry>
+             <entry>v<subscript>6</subscript></entry>
+             <entry>v<subscript>5</subscript></entry>
+             <entry>v<subscript>4</subscript></entry>
+             <entry>v<subscript>3</subscript></entry>
+             <entry>v<subscript>2</subscript></entry>
+             <entry>v<subscript>1</subscript></entry>
+             <entry>v<subscript>0</subscript></entry>
+           </row>
+           <row id="V4L2-MBUS-FMT-YVYU12-1X24">
+             <entry>V4L2_MBUS_FMT_YVYU12_1X24</entry>
+             <entry>0x2023</entry>
+             <entry></entry>
+             &dash-ent-8;
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+             <entry>v<subscript>11</subscript></entry>
+             <entry>v<subscript>10</subscript></entry>
+             <entry>v<subscript>9</subscript></entry>
+             <entry>v<subscript>8</subscript></entry>
+             <entry>v<subscript>7</subscript></entry>
+             <entry>v<subscript>6</subscript></entry>
+             <entry>v<subscript>5</subscript></entry>
+             <entry>v<subscript>4</subscript></entry>
+             <entry>v<subscript>3</subscript></entry>
+             <entry>v<subscript>2</subscript></entry>
+             <entry>v<subscript>1</subscript></entry>
+             <entry>v<subscript>0</subscript></entry>
+           </row>
+           <row>
+             <entry></entry>
+             <entry></entry>
+             <entry></entry>
+             &dash-ent-8;
+             <entry>y<subscript>11</subscript></entry>
+             <entry>y<subscript>10</subscript></entry>
+             <entry>y<subscript>9</subscript></entry>
+             <entry>y<subscript>8</subscript></entry>
+             <entry>y<subscript>7</subscript></entry>
+             <entry>y<subscript>6</subscript></entry>
+             <entry>y<subscript>5</subscript></entry>
+             <entry>y<subscript>4</subscript></entry>
+             <entry>y<subscript>3</subscript></entry>
+             <entry>y<subscript>2</subscript></entry>
+             <entry>y<subscript>1</subscript></entry>
+             <entry>y<subscript>0</subscript></entry>
+             <entry>u<subscript>11</subscript></entry>
+             <entry>u<subscript>10</subscript></entry>
+             <entry>u<subscript>9</subscript></entry>
+             <entry>u<subscript>8</subscript></entry>
+             <entry>u<subscript>7</subscript></entry>
+             <entry>u<subscript>6</subscript></entry>
+             <entry>u<subscript>5</subscript></entry>
+             <entry>u<subscript>4</subscript></entry>
+             <entry>u<subscript>3</subscript></entry>
+             <entry>u<subscript>2</subscript></entry>
+             <entry>u<subscript>1</subscript></entry>
+             <entry>u<subscript>0</subscript></entry>
+           </row>
          </tbody>
        </tgroup>
       </table>
index 89891ad..820f86e 100644 (file)
       </tgroup>
     </table>
 
+    <table frame="none" pgwide="1" id="v4l2-event-src-change">
+      <title>struct <structname>v4l2_event_src_change</structname></title>
+      <tgroup cols="3">
+       &cs-str;
+       <tbody valign="top">
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>changes</structfield></entry>
+           <entry>
+             A bitmask that tells what has changed. See <xref linkend="src-changes-flags" />.
+           </entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
     <table pgwide="1" frame="none" id="changes-flags">
       <title>Changes</title>
       <tgroup cols="3">
        </tbody>
       </tgroup>
     </table>
+
+    <table pgwide="1" frame="none" id="src-changes-flags">
+      <title>Source Changes</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>V4L2_EVENT_SRC_CH_RESOLUTION</constant></entry>
+           <entry>0x0001</entry>
+           <entry>This event gets triggered when a resolution change is
+           detected at an input. This can come from an input connector or
+           from a video decoder.
+           </entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
   </refsect1>
   <refsect1>
     &return-value;
index cd7720d..28a8c1e 100644 (file)
@@ -1,11 +1,12 @@
 <refentry id="vidioc-dv-timings-cap">
   <refmeta>
-    <refentrytitle>ioctl VIDIOC_DV_TIMINGS_CAP</refentrytitle>
+    <refentrytitle>ioctl VIDIOC_DV_TIMINGS_CAP, VIDIOC_SUBDEV_DV_TIMINGS_CAP</refentrytitle>
     &manvol;
   </refmeta>
 
   <refnamediv>
     <refname>VIDIOC_DV_TIMINGS_CAP</refname>
+    <refname>VIDIOC_SUBDEV_DV_TIMINGS_CAP</refname>
     <refpurpose>The capabilities of the Digital Video receiver/transmitter</refpurpose>
   </refnamediv>
 
@@ -33,7 +34,7 @@
       <varlistentry>
        <term><parameter>request</parameter></term>
        <listitem>
-         <para>VIDIOC_DV_TIMINGS_CAP</para>
+         <para>VIDIOC_DV_TIMINGS_CAP, VIDIOC_SUBDEV_DV_TIMINGS_CAP</para>
        </listitem>
       </varlistentry>
       <varlistentry>
       interface and may change in the future.</para>
     </note>
 
-    <para>To query the capabilities of the DV receiver/transmitter applications can call
-this ioctl and the driver will fill in the structure. Note that drivers may return
+    <para>To query the capabilities of the DV receiver/transmitter applications
+can call the <constant>VIDIOC_DV_TIMINGS_CAP</constant> ioctl on a video node
+and the driver will fill in the structure. Note that drivers may return
 different values after switching the video input or output.</para>
 
+    <para>When implemented by the driver DV capabilities of subdevices can be
+queried by calling the <constant>VIDIOC_SUBDEV_DV_TIMINGS_CAP</constant> ioctl
+directly on a subdevice node. The capabilities are specific to inputs (for DV
+receivers) or outputs (for DV transmitters), applications must specify the
+desired pad number in the &v4l2-dv-timings-cap; <structfield>pad</structfield>
+field. Attempts to query capabilities on a pad that doesn't support them will
+return an &EINVAL;.</para>
+
     <table pgwide="1" frame="none" id="v4l2-bt-timings-cap">
       <title>struct <structname>v4l2_bt_timings_cap</structname></title>
       <tgroup cols="3">
@@ -127,7 +137,14 @@ different values after switching the video input or output.</para>
          </row>
          <row>
            <entry>__u32</entry>
-           <entry><structfield>reserved</structfield>[3]</entry>
+           <entry><structfield>pad</structfield></entry>
+           <entry>Pad number as reported by the media controller API. This field
+           is only used when operating on a subdevice node. When operating on a
+           video node applications must set this field to zero.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>reserved</structfield>[2]</entry>
            <entry>Reserved for future extensions. Drivers must set the array to zero.</entry>
          </row>
          <row>
index b3e17c1..b9fdfea 100644 (file)
@@ -1,11 +1,12 @@
 <refentry id="vidioc-enum-dv-timings">
   <refmeta>
-    <refentrytitle>ioctl VIDIOC_ENUM_DV_TIMINGS</refentrytitle>
+    <refentrytitle>ioctl VIDIOC_ENUM_DV_TIMINGS, VIDIOC_SUBDEV_ENUM_DV_TIMINGS</refentrytitle>
     &manvol;
   </refmeta>
 
   <refnamediv>
     <refname>VIDIOC_ENUM_DV_TIMINGS</refname>
+    <refname>VIDIOC_SUBDEV_ENUM_DV_TIMINGS</refname>
     <refpurpose>Enumerate supported Digital Video timings</refpurpose>
   </refnamediv>
 
@@ -33,7 +34,7 @@
       <varlistentry>
        <term><parameter>request</parameter></term>
        <listitem>
-         <para>VIDIOC_ENUM_DV_TIMINGS</para>
+         <para>VIDIOC_ENUM_DV_TIMINGS, VIDIOC_SUBDEV_ENUM_DV_TIMINGS</para>
        </listitem>
       </varlistentry>
       <varlistentry>
@@ -61,14 +62,21 @@ standards or even custom timings that are not in this list.</para>
 
     <para>To query the available timings, applications initialize the
 <structfield>index</structfield> field and zero the reserved array of &v4l2-enum-dv-timings;
-and call the <constant>VIDIOC_ENUM_DV_TIMINGS</constant> ioctl with a pointer to this
-structure. Drivers fill the rest of the structure or return an
+and call the <constant>VIDIOC_ENUM_DV_TIMINGS</constant> ioctl on a video node with a
+pointer to this structure. Drivers fill the rest of the structure or return an
 &EINVAL; when the index is out of bounds. To enumerate all supported DV timings,
 applications shall begin at index zero, incrementing by one until the
 driver returns <errorcode>EINVAL</errorcode>. Note that drivers may enumerate a
 different set of DV timings after switching the video input or
 output.</para>
 
+    <para>When implemented by the driver DV timings of subdevices can be queried
+by calling the <constant>VIDIOC_SUBDEV_ENUM_DV_TIMINGS</constant> ioctl directly
+on a subdevice node. The DV timings are specific to inputs (for DV receivers) or
+outputs (for DV transmitters), applications must specify the desired pad number
+in the &v4l2-enum-dv-timings; <structfield>pad</structfield> field. Attempts to
+enumerate timings on a pad that doesn't support them will return an &EINVAL;.</para>
+
     <table pgwide="1" frame="none" id="v4l2-enum-dv-timings">
       <title>struct <structname>v4l2_enum_dv_timings</structname></title>
       <tgroup cols="3">
@@ -82,8 +90,16 @@ application.</entry>
          </row>
          <row>
            <entry>__u32</entry>
-           <entry><structfield>reserved</structfield>[3]</entry>
-           <entry>Reserved for future extensions. Drivers must set the array to zero.</entry>
+           <entry><structfield>pad</structfield></entry>
+           <entry>Pad number as reported by the media controller API. This field
+           is only used when operating on a subdevice node. When operating on a
+           video node applications must set this field to zero.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>reserved</structfield>[2]</entry>
+           <entry>Reserved for future extensions. Drivers and applications must
+           set the array to zero.</entry>
          </row>
          <row>
            <entry>&v4l2-dv-timings;</entry>
@@ -103,7 +119,7 @@ application.</entry>
        <term><errorcode>EINVAL</errorcode></term>
        <listitem>
          <para>The &v4l2-enum-dv-timings; <structfield>index</structfield>
-is out of bounds.</para>
+is out of bounds or the <structfield>pad</structfield> number is invalid.</para>
        </listitem>
       </varlistentry>
       <varlistentry>
index 5c70b61..17efa87 100644 (file)
              frame interval in between them.</para>
            </entry>
          </row>
+         <row>
+           <entry><constant>V4L2_EVENT_SOURCE_CHANGE</constant></entry>
+           <entry>5</entry>
+           <entry>
+             <para>This event is triggered when a source parameter change is
+              detected during runtime by the video device. It can be a
+              runtime resolution change triggered by a video decoder or the
+              format change happening on an input connector.
+              This event requires that the <structfield>id</structfield>
+              matches the input index (when used with a video device node)
+              or the pad index (when used with a subdevice node) from which
+              you want to receive events.</para>
+
+              <para>This event has a &v4l2-event-src-change; associated
+             with it. The <structfield>changes</structfield> bitfield denotes
+             what has changed for the subscribed pad. If multiple events
+             occurred before application could dequeue them, then the changes
+             will have the ORed value of all the events generated.</para>
+           </entry>
+         </row>
          <row>
            <entry><constant>V4L2_EVENT_PRIVATE_START</constant></entry>
            <entry>0x08000000</entry>
index 0060d76..70933ea 100644 (file)
@@ -20,6 +20,7 @@ Contents:
 ---------
 1.  CPUFreq core and interfaces
 2.  CPUFreq notifiers
+3.  CPUFreq Table Generation with Operating Performance Point (OPP)
 
 1. General Information
 =======================
@@ -92,3 +93,31 @@ values:
 cpu    - number of the affected CPU
 old    - old frequency
 new    - new frequency
+
+3. CPUFreq Table Generation with Operating Performance Point (OPP)
+==================================================================
+For details about OPP, see Documentation/power/opp.txt
+
+dev_pm_opp_init_cpufreq_table - cpufreq framework typically is initialized with
+       cpufreq_frequency_table_cpuinfo which is provided with the list of
+       frequencies that are available for operation. This function provides
+       a ready to use conversion routine to translate the OPP layer's internal
+       information about the available frequencies into a format readily
+       providable to cpufreq.
+
+       WARNING: Do not use this function in interrupt context.
+
+       Example:
+        soc_pm_init()
+        {
+               /* Do things */
+               r = dev_pm_opp_init_cpufreq_table(dev, &freq_table);
+               if (!r)
+                       cpufreq_frequency_table_cpuinfo(policy, freq_table);
+               /* Do other things */
+        }
+
+       NOTE: This function is available only if CONFIG_CPU_FREQ is enabled in
+       addition to CONFIG_PM_OPP.
+
+dev_pm_opp_free_cpufreq_table - Free up the table allocated by dev_pm_opp_init_cpufreq_table
index 48da5fd..b045fe5 100644 (file)
@@ -228,3 +228,22 @@ is the corresponding frequency table helper for the ->target
 stage. Just pass the values to this function, and the unsigned int
 index returns the number of the frequency table entry which contains
 the frequency the CPU shall be set to.
+
+The following macros can be used as iterators over cpufreq_frequency_table:
+
+cpufreq_for_each_entry(pos, table) - iterates over all entries of frequency
+table.
+
+cpufreq-for_each_valid_entry(pos, table) - iterates over all entries,
+excluding CPUFREQ_ENTRY_INVALID frequencies.
+Use arguments "pos" - a cpufreq_frequency_table * as a loop cursor and
+"table" - the cpufreq_frequency_table * you want to iterate over.
+
+For example:
+
+       struct cpufreq_frequency_table *pos, *driver_freq_table;
+
+       cpufreq_for_each_entry(pos, driver_freq_table) {
+               /* Do something with pos */
+               pos->frequency = ...
+       }
index 3d0b915..dc024ab 100644 (file)
@@ -35,8 +35,8 @@ Mailing List
 ------------
 There is a CPU frequency changing CVS commit and general list where
 you can report bugs, problems or submit patches. To post a message,
-send an email to cpufreq@vger.kernel.org, to subscribe go to
-http://vger.kernel.org/vger-lists.html#cpufreq and follow the
+send an email to linux-pm@vger.kernel.org, to subscribe go to
+http://vger.kernel.org/vger-lists.html#linux-pm and follow the
 instructions there.
 
 Links
index 189baba..d22b216 100644 (file)
@@ -129,7 +129,7 @@ Boards:
 - AM437x GP EVM
   compatible = "ti,am437x-gp-evm", "ti,am4372", "ti,am43"
 
-- DRA742 EVM:  Software Developement Board for DRA742
+- DRA742 EVM:  Software Development Board for DRA742
   compatible = "ti,dra7-evm", "ti,dra742", "ti,dra74", "ti,dra7"
 
 - DRA722 EVM: Software Development Board for DRA722
index 433afe9..b4a58f3 100644 (file)
@@ -21,7 +21,15 @@ to #0.
 
 Main node required properties:
 
- - compatible    : Must be "arm,psci"
+ - compatible    : should contain at least one of:
+
+                                * "arm,psci" : for implementations complying to PSCI versions prior to
+                                       0.2. For these cases function IDs must be provided.
+
+                                * "arm,psci-0.2" : for implementations complying to PSCI 0.2. Function
+                                       IDs are not required and should be ignored by an OS with PSCI 0.2
+                                       support, but are permitted to be present for compatibility with
+                                       existing software when "arm,psci" is later in the compatible list.
 
  - method        : The method of calling the PSCI firmware. Permitted
                    values are:
@@ -45,6 +53,8 @@ Main node optional properties:
 
 Example:
 
+Case 1: PSCI v0.1 only.
+
        psci {
                compatible      = "arm,psci";
                method          = "smc";
@@ -53,3 +63,28 @@ Example:
                cpu_on          = <0x95c10002>;
                migrate         = <0x95c10003>;
        };
+
+
+Case 2: PSCI v0.2 only
+
+       psci {
+               compatible      = "arm,psci-0.2";
+               method          = "smc";
+       };
+
+Case 3: PSCI v0.2 and PSCI v0.1.
+
+       A DTB may provide IDs for use by kernels without PSCI 0.2 support,
+       enabling firmware and hypervisors to support existing and new kernels.
+       These IDs will be ignored by kernels with PSCI 0.2 support, which will
+       use the standard PSCI 0.2 IDs exclusively.
+
+       psci {
+               compatible = "arm,psci-0.2", "arm,psci";
+               method = "hvc";
+
+               cpu_on = < arbitrary value >;
+               cpu_off = < arbitrary value >;
+
+               ...
+       };
index 7586fb6..5fa44f5 100644 (file)
@@ -197,7 +197,7 @@ to be set by the operating system and that are guaranteed to be free of overlaps
 with one another or with the system memory ranges.
 
 Each entry in the property refers to exactly one window. If the operating system
-choses to use a different set of mbus windows, it must ensure that any address
+chooses to use a different set of mbus windows, it must ensure that any address
 translations performed from downstream devices are adapted accordingly.
 
 The operating system may insert additional mbus windows that do not conflict
index 8f504e6..8210427 100644 (file)
@@ -14,7 +14,7 @@ Required property:
 
 Optional properties:
 - dma-channels:        Number of DMA channels supported by the controller.
-- dma-requests:        Number of DMA requests signals supported by the
+- dma-requests:        Number of DMA request signals supported by the
                        controller.
 
 Example:
@@ -44,7 +44,7 @@ Required property:
                          #dma-cells property in the node referenced by phandle
                          containing DMA controller specific information. This
                          typically contains a DMA request line number or a
-                         channel number, but can contain any data that is used
+                         channel number, but can contain any data that is
                          required for configuring a channel.
 - dma-names:           Contains one identifier string for each DMA specifier in
                        the dmas property. The specific strings that can be used
index ee9be99..e577196 100644 (file)
@@ -8,7 +8,7 @@ Required properties:
       "fsl,imx51-sdma"
       "fsl,imx53-sdma"
       "fsl,imx6q-sdma"
-  The -to variants should be preferred since they allow to determnine the
+  The -to variants should be preferred since they allow to determine the
   correct ROM script addresses needed for the driver to work without additional
   firmware.
 - reg : Should contain SDMA registers location and length
index 3ddc7cc..c306a2d 100644 (file)
@@ -54,7 +54,7 @@ Optional device specific properties:
         IO 8-15 are bank 2. These chips have two different interrupt outputs:
         One for bank 1 and another for bank 2. If irq-mirror is set, both
         interrupts are generated regardless of the bank that an input change
-        occured on. If it is not set, the interrupt are only generated for the
+        occurred on. If it is not set, the interrupt are only generated for the
         bank they belong to.
         On devices with only one interrupt output this property is useless.
 
index f418168..3e3c5f3 100644 (file)
@@ -10,7 +10,8 @@ Required properties:
   - compatible : value should be either one among the following
        (a) "samsung,mfc-v5" for MFC v5 present in Exynos4 SoCs
        (b) "samsung,mfc-v6" for MFC v6 present in Exynos5 SoCs
-       (b) "samsung,mfc-v7" for MFC v7 present in Exynos5420 SoC
+       (c) "samsung,mfc-v7" for MFC v7 present in Exynos5420 SoC
+       (d) "samsung,mfc-v8" for MFC v8 present in Exynos5800 SoC
 
   - reg : Physical base address of the IP registers and length of memory
          mapped region.
index 1413f39..8aba488 100644 (file)
@@ -10,6 +10,9 @@ Optional properties:
 - fsl,mc13xxx-uses-touch : Indicate the touchscreen controller is being used
 
 Sub-nodes:
+- codec: Contain the Audio Codec node.
+  - adc-port: Contain PMIC SSI port number used for ADC.
+  - dac-port: Contain PMIC SSI port number used for DAC.
 - leds : Contain the led nodes and initial register values in property
   "led-control". Number of register depends of used IC, for MC13783 is 6,
   for MC13892 is 4, for MC34708 is 1. See datasheet for bits definitions of
index b8653ea..e5bc49f 100644 (file)
@@ -12,7 +12,7 @@ extensions to the Synopsys Designware Mobile Storage Host Controller.
 Required Properties:
 
 * compatible: should be one of the following.
-  - "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extentions.
+  - "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extensions.
 
 Example:
 
index 328e990..42e0a9a 100644 (file)
@@ -3,7 +3,7 @@
 Samsung's SDHCI controller is used as a connectivity interface with external
 MMC, SD and eMMC storage mediums. This file documents differences between the
 core mmc properties described by mmc.txt and the properties used by the
-Samsung implmentation of the SDHCI controller.
+Samsung implementation of the SDHCI controller.
 
 Required SoC Specific Properties:
 - compatible: should be one of the following
index 5e1f31b..eb05255 100644 (file)
@@ -43,7 +43,7 @@ Optional properties:
                ELM hardware engines should specify this device node in .dtsi
                Using ELM for ECC error correction frees some CPU cycles.
 
-For inline partiton table parsing (optional):
+For inline partition table parsing (optional):
 
  - #address-cells: should be set to 1
  - #size-cells: should be set to 1
index 420b3ab..4828c17 100644 (file)
@@ -30,7 +30,7 @@ Optional properties:
 - gpmc,XXX             Additional GPMC timings and settings parameters. See
                        Documentation/devicetree/bindings/bus/ti-gpmc.txt
 
-Optional properties for partiton table parsing:
+Optional properties for partition table parsing:
 - #address-cells: should be set to 1
 - #size-cells: should be set to 1
 
index b752942..5d8fa52 100644 (file)
@@ -17,7 +17,7 @@ Optional properties:
 
  - dma-channel:                DMA Channel index
 
-For inline partiton table parsing (optional):
+For inline partition table parsing (optional):
 
  - #address-cells: should be set to 1
  - #size-cells: should be set to 1
index 67a5db9..4eaae32 100644 (file)
@@ -73,9 +73,9 @@ Optional Properties (for standard pins):
                                Otherwise:
                                        0: fast slew rate
                                        1: normal slew rate
-- input-enable:                        No arguements. Enable input (does not affect
+- input-enable:                        No arguments. Enable input (does not affect
                                output.)
-- input-disable:               No arguements. Disable input (does not affect
+- input-disable:               No arguments. Disable input (does not affect
                                output.)
 - drive-strength:              Integer. Drive strength in mA.  Valid values are
                                2, 4, 6, 8, 10, 12, 14, 16 mA.
@@ -99,9 +99,9 @@ Optional Properties (for I2C pins):
                                Otherwise:
                                        0: fast slew rate
                                        1: normal slew rate
-- input-enable:                        No arguements. Enable input (does not affect
+- input-enable:                        No arguments. Enable input (does not affect
                                output.)
-- input-disable:               No arguements. Disable input (does not affect
+- input-disable:               No arguments. Disable input (does not affect
                                output.)
 
 Optional Properties (for HDMI pins):
@@ -111,9 +111,9 @@ Optional Properties (for HDMI pins):
 - slew-rate:                   Integer. Controls slew rate.
                                        0: Standard(100kbps)& Fast(400kbps) mode
                                        1: Highspeed (3.4Mbps) mode
-- input-enable:                        No arguements. Enable input (does not affect
+- input-enable:                        No arguments. Enable input (does not affect
                                output.)
-- input-disable:               No arguements. Disable input (does not affect
+- input-disable:               No arguments. Disable input (does not affect
                                output.)
 
 Example:
index d721726..5bc6355 100644 (file)
@@ -1,7 +1,7 @@
 Reboot property to control system reboot on PPC4xx systems:
 
 By setting "reset_type" to one of the following values, the default
-software reset mechanism may be overidden. Here the possible values of
+software reset mechanism may be overridden. Here the possible values of
 "reset_type":
 
       1 - PPC4xx core reset
index 9d54eb5..18a8810 100644 (file)
@@ -82,7 +82,7 @@ PROPERTIES
        Which event source asserted the interrupt is captured in an EPU
        Interrupt Status Register (EPISR0,EPISR1).
 
-       Interrupt numbers are lised in order (perfmon, event0, event1).
+       Interrupt numbers are listed in order (perfmon, event0, event1).
 
        - interrupt-parent
        Usage: required
index e2c7f1e..8607433 100644 (file)
@@ -12,7 +12,7 @@ Optional properties:
 - regulator-allow-bypass: allow the regulator to go into bypass mode
 - <name>-supply: phandle to the parent supply/regulator node
 - regulator-ramp-delay: ramp delay for regulator(in uV/uS)
-  For hardwares which support disabling ramp rate, it should be explicitly
+  For hardware which supports disabling ramp rate, it should be explicitly
   intialised to zero (regulator-ramp-delay = <0>) for disabling ramp delay.
 - regulator-enable-ramp-delay: The time taken, in microseconds, for the supply
   rail to reach the target voltage, plus/minus whatever tolerance the board
index b902ee3..deca5e1 100644 (file)
@@ -8,6 +8,8 @@ Required properties:
 
   - reg : The chip select number on the SPI bus
 
+  - vdd-supply : A regulator node, providing 2.7V - 3.6V
+
 Optional properties:
 
   - reset-gpio : a GPIO spec for the reset pin. If specified, it will be
@@ -19,4 +21,5 @@ spdif: ak4104@0 {
        compatible = "asahi-kasei,ak4104";
        reg = <0>;
        spi-max-frequency = <5000000>;
+       vdd-supply = <&vdd_3v3_reg>;
 };
diff --git a/Documentation/devicetree/bindings/sound/alc5623.txt b/Documentation/devicetree/bindings/sound/alc5623.txt
new file mode 100644 (file)
index 0000000..26c86c9
--- /dev/null
@@ -0,0 +1,25 @@
+ALC5621/ALC5622/ALC5623 audio Codec
+
+Required properties:
+
+ - compatible: "realtek,alc5623"
+ - reg:                the I2C address of the device.
+
+Optional properties:
+
+ - add-ctrl:     Default register value for Reg-40h, Additional Control
+                 Register. If absent or has the value of 0, the
+                 register is untouched.
+
+ - jack-det-ctrl: Default register value for Reg-5Ah, Jack Detect
+                 Control Register. If absent or has value 0, the
+                 register is untouched.
+
+Example:
+
+       alc5621: alc5621@1a {
+               compatible = "alc5621";
+               reg = <0x1a>;
+               add-ctrl = <0x3700>;
+               jack-det-ctrl = <0x4810>;
+       };
diff --git a/Documentation/devicetree/bindings/sound/cs42l56.txt b/Documentation/devicetree/bindings/sound/cs42l56.txt
new file mode 100644 (file)
index 0000000..4feb0eb
--- /dev/null
@@ -0,0 +1,63 @@
+CS42L52 audio CODEC
+
+Required properties:
+
+  - compatible : "cirrus,cs42l56"
+
+  - reg : the I2C address of the device for I2C
+
+  - VA-supply, VCP-supply, VLDO-supply : power supplies for the device,
+  as covered in Documentation/devicetree/bindings/regulator/regulator.txt.
+
+Optional properties:
+
+  - cirrus,gpio-nreset : GPIO controller's phandle and the number
+  of the GPIO used to reset the codec.
+
+  - cirrus,chgfreq-divisor : Values used to set the Charge Pump Frequency.
+  Allowable values of 0x00 through 0x0F. These are raw values written to the
+  register, not the actual frequency. The frequency is determined by the following.
+  Frequency = MCLK / 4 * (N+2)
+  N = chgfreq_val
+  MCLK = Where MCLK is the frequency of the mclk signal after the MCLKDIV2 circuit.
+
+  - cirrus,ain1a-ref-cfg, ain1b-ref-cfg : boolean, If present, AIN1A or AIN1B are configured
+  as a pseudo-differential input referenced to AIN1REF/AIN3A.
+
+  - cirrus,ain2a-ref-cfg, ain2b-ref-cfg : boolean, If present, AIN2A or AIN2B are configured
+  as a pseudo-differential input referenced to AIN2REF/AIN3B.
+
+  - cirrus,micbias-lvl: Set the output voltage level on the MICBIAS Pin.
+  0 = 0.5 x VA
+  1 = 0.6 x VA
+  2 = 0.7 x VA
+  3 = 0.8 x VA
+  4 = 0.83 x VA
+  5 = 0.91 x VA
+
+  - cirrus,adaptive-pwr-cfg : Configures how the power to the Headphone and Lineout
+  Amplifiers adapt to the output signal levels.
+  0 = Adapt to Volume Mode. Voltage level determined by the sum of the relevant volume settings.
+  1 = Fixed - Headphone and Line Amp supply = + or - VCP/2.
+  2 = Fixed - Headphone and Line Amp supply = + or - VCP.
+  3 = Adapted to Signal; Voltage level is dynamically determined by the output signal.
+
+  - cirrus,hpf-left-freq, hpf-right-freq : Sets the corner frequency (-3dB point) for the internal High-Pass
+  Filter.
+  0 = 1.8Hz
+  1 = 119Hz
+  2 = 236Hz
+  3 = 464Hz
+
+
+Example:
+
+codec: codec@4b {
+       compatible = "cirrus,cs42l56";
+       reg = <0x4b>;
+       gpio-reset = <&gpio 10 0>;
+       cirrus,chgfreq-divisor = <0x05>;
+       cirrus.ain1_ref_cfg;
+       cirrus,micbias-lvl = <5>;
+       VA-supply = <&reg_audio>;
+};
index 98611a6..0f4e238 100644 (file)
@@ -7,10 +7,11 @@ codec/DSP interfaces.
 
 
 Required properties:
-- compatible: Compatible list, contains "fsl,vf610-sai".
+- compatible: Compatible list, contains "fsl,vf610-sai" or "fsl,imx6sx-sai".
 - reg: Offset and length of the register set for the device.
 - clocks: Must contain an entry for each entry in clock-names.
-- clock-names : Must include the "sai" entry.
+- clock-names : Must include the "bus" for register access and "mclk1" "mclk2"
+  "mclk3" for bit clock and frame clock providing.
 - dmas : Generic dma devicetree binding as described in
   Documentation/devicetree/bindings/dma/dma.txt.
 - dma-names : Two dmas have to be defined, "tx" and "rx".
@@ -30,8 +31,10 @@ sai2: sai@40031000 {
              reg = <0x40031000 0x1000>;
              pinctrl-names = "default";
              pinctrl-0 = <&pinctrl_sai2_1>;
-             clocks = <&clks VF610_CLK_SAI2>;
-             clock-names = "sai";
+             clocks = <&clks VF610_CLK_PLATFORM_BUS>,
+                    <&clks VF610_CLK_SAI2>,
+                    <&clks 0>, <&clks 0>;
+             clock-names = "bus", "mclk1", "mclk2", "mclk3";
              dma-names = "tx", "rx";
              dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>,
                   <&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>;
index e4c8b36..a5e63fa 100644 (file)
@@ -10,6 +10,12 @@ Required properties:
 
 - interrupts : The CODEC's interrupt output.
 
+Optional properties:
+
+- clocks: The phandle of the master clock to the CODEC
+
+- clock-names: Should be "mclk"
+
 Pins on the device (for linking into audio routes):
 
   * MIC1
diff --git a/Documentation/devicetree/bindings/sound/max98095.txt b/Documentation/devicetree/bindings/sound/max98095.txt
new file mode 100644 (file)
index 0000000..318a4c8
--- /dev/null
@@ -0,0 +1,22 @@
+MAX98095 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+- compatible : "maxim,max98095".
+
+- reg : The I2C address of the device.
+
+Optional properties:
+
+- clocks: The phandle of the master clock to the CODEC
+
+- clock-names: Should be "mclk"
+
+Example:
+
+max98095: codec@11 {
+       compatible = "maxim,max98095";
+       reg = <0x11>;
+};
diff --git a/Documentation/devicetree/bindings/sound/nokia,rx51.txt b/Documentation/devicetree/bindings/sound/nokia,rx51.txt
new file mode 100644 (file)
index 0000000..72f93d9
--- /dev/null
@@ -0,0 +1,27 @@
+* Nokia N900 audio setup
+
+Required properties:
+- compatible: Should contain "nokia,n900-audio"
+- nokia,cpu-dai: phandle for the McBSP node
+- nokia,audio-codec: phandles for the main TLV320AIC3X node and the
+                     auxiliary TLV320AIC3X node (in this order)
+- nokia,headphone-amplifier: phandle for the TPA6130A2 node
+- tvout-selection-gpios: GPIO for tvout selection
+- jack-detection-gpios: GPIO for jack detection
+- eci-switch-gpios: GPIO for ECI (Enhancement Control Interface) switch
+- speaker-amplifier-gpios: GPIO for speaker amplifier
+
+Example:
+
+sound {
+       compatible = "nokia,n900-audio";
+
+       nokia,cpu-dai = <&mcbsp2>;
+       nokia,audio-codec = <&tlv320aic3x>, <&tlv320aic3x_aux>;
+       nokia,headphone-amplifier = <&tpa6130a2>;
+
+       tvout-selection-gpios = <&gpio2 8 GPIO_ACTIVE_HIGH>; /* 40 */
+       jack-detection-gpios = <&gpio6 17 GPIO_ACTIVE_HIGH>; /* 177 */
+       eci-switch-gpios = <&gpio6 22 GPIO_ACTIVE_HIGH>; /* 182 */
+       speaker-amplifier-gpios = <&twl_gpio 7 GPIO_ACTIVE_HIGH>;
+};
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.txt
new file mode 100644 (file)
index 0000000..b4730c2
--- /dev/null
@@ -0,0 +1,28 @@
+NVIDIA Tegra30 HDA controller
+
+Required properties:
+- compatible : "nvidia,tegra30-hda"
+- reg : Should contain the HDA registers location and length.
+- interrupts : The interrupt from the HDA controller.
+- clocks : Must contain an entry for each required entry in clock-names.
+  See ../clocks/clock-bindings.txt for details.
+- clock-names : Must include the following entries: hda, hdacodec_2x, hda2hdmi
+- resets : Must contain an entry for each entry in reset-names.
+  See ../reset/reset.txt for details.
+- reset-names : Must include the following entries: hda, hdacodec_2x, hda2hdmi
+
+Example:
+
+hda@0,70030000 {
+       compatible = "nvidia,tegra124-hda", "nvidia,tegra30-hda";
+       reg = <0x0 0x70030000 0x0 0x10000>;
+       interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
+       clocks = <&tegra_car TEGRA124_CLK_HDA>,
+                <&tegra_car TEGRA124_CLK_HDA2HDMI>,
+                <&tegra_car TEGRA124_CLK_HDA2CODEC_2X>;
+       clock-names = "hda", "hda2hdmi", "hda2codec_2x";
+       resets = <&tegra_car 125>, /* hda */
+                <&tegra_car 128>; /* hda2hdmi */
+                <&tegra_car 111>, /* hda2codec_2x */
+       reset-names = "hda", "hda2hdmi", "hda2codec_2x";
+};
index a44e917..8346cab 100644 (file)
@@ -20,6 +20,7 @@ Required properties:
 SSI subnode properties:
 - interrupts                   : Should contain SSI interrupt for PIO transfer
 - shared-pin                   : if shared clock pin
+- pio-transfer                 : use PIO transfer mode
 
 SRC subnode properties:
 no properties at this point
index 068a114..bac4d9a 100644 (file)
@@ -1,10 +1,10 @@
-RT5640 audio CODEC
+RT5640/RT5639 audio CODEC
 
 This device supports I2C only.
 
 Required properties:
 
-- compatible : "realtek,rt5640".
+- compatible : One of "realtek,rt5640" or "realtek,rt5639".
 
 - reg : The I2C address of the device.
 
@@ -18,7 +18,7 @@ Optional properties:
 
 - realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin.
 
-Pins on the device (for linking into audio routes):
+Pins on the device (for linking into audio routes) for RT5639/RT5640:
 
   * DMIC1
   * DMIC2
@@ -31,13 +31,16 @@ Pins on the device (for linking into audio routes):
   * HPOR
   * LOUTL
   * LOUTR
-  * MONOP
-  * MONON
   * SPOLP
   * SPOLN
   * SPORP
   * SPORN
 
+Additional pins on the device for RT5640:
+
+  * MONOP
+  * MONON
+
 Example:
 
 rt5640 {
index 131aa2a..c2e9841 100644 (file)
@@ -1,6 +1,6 @@
 Simple-Card:
 
-Simple-Card specifies audio DAI connection of SoC <-> codec.
+Simple-Card specifies audio DAI connections of SoC <-> codec.
 
 Required properties:
 
@@ -10,26 +10,54 @@ Optional properties:
 
 - simple-audio-card,name               : User specified audio sound card name, one string
                                          property.
-- simple-audio-card,format             : CPU/CODEC common audio format.
-                                         "i2s", "right_j", "left_j" , "dsp_a"
-                                         "dsp_b", "ac97", "pdm", "msb", "lsb"
 - simple-audio-card,widgets            : Please refer to widgets.txt.
 - simple-audio-card,routing            : A list of the connections between audio components.
                                          Each entry is a pair of strings, the first being the
                                          connection's sink, the second being the connection's
                                          source.
-- dai-tdm-slot-num                     : Please refer to tdm-slot.txt.
-- dai-tdm-slot-width                   : Please refer to tdm-slot.txt.
+- simple-audio-card,mclk-fs             : Multiplication factor between stream rate and codec
+                                         mclk.
+
+Optional subnodes:
+
+- simple-audio-card,dai-link           : Container for dai-link level
+                                         properties and the CPU and CODEC
+                                         sub-nodes. This container may be
+                                         omitted when the card has only one
+                                         DAI link. See the examples and the
+                                         section bellow.
+
+Dai-link subnode properties and subnodes:
+
+If dai-link subnode is omitted and the subnode properties are directly
+under "sound"-node the subnode property and subnode names have to be
+prefixed with "simple-audio-card,"-prefix.
 
-Required subnodes:
+Required dai-link subnodes:
 
-- simple-audio-card,dai-link           : container for the CPU and CODEC sub-nodes
-                                         This container may be omitted when the
-                                         card has only one DAI link.
-                                         See the examples.
+- cpu                                  : CPU   sub-node
+- codec                                        : CODEC sub-node
 
-- simple-audio-card,cpu                        : CPU   sub-node
-- simple-audio-card,codec              : CODEC sub-node
+Optional dai-link subnode properties:
+
+- format                               : CPU/CODEC common audio format.
+                                         "i2s", "right_j", "left_j" , "dsp_a"
+                                         "dsp_b", "ac97", "pdm", "msb", "lsb"
+- frame-master                         : Indicates dai-link frame master.
+                                         phandle to a cpu or codec subnode.
+- bitclock-master                      : Indicates dai-link bit clock master.
+                                         phandle to a cpu or codec subnode.
+- bitclock-inversion                   : bool property. Add this if the
+                                         dai-link uses bit clock inversion.
+- frame-inversion                      : bool property. Add this if the
+                                         dai-link uses frame clock inversion.
+
+For backward compatibility the frame-master and bitclock-master
+properties can be used as booleans in codec subnode to indicate if the
+codec is the dai-link frame or bit clock master. In this case there
+should be no dai-link node, the same properties should not be present
+at sound-node level, and the bitclock-inversion and frame-inversion
+properties should also be placed in the codec node if needed.
 
 Required CPU/CODEC subnodes properties:
 
@@ -37,29 +65,21 @@ Required CPU/CODEC subnodes properties:
 
 Optional CPU/CODEC subnodes properties:
 
-- format                               : CPU/CODEC specific audio format if needed.
-                                         see simple-audio-card,format
-- frame-master                         : bool property. add this if subnode is frame master
-- bitclock-master                      : bool property. add this if subnode is bitclock master
-- bitclock-inversion                   : bool property. add this if subnode has clock inversion
-- frame-inversion                      : bool property. add this if subnode has frame inversion
+- dai-tdm-slot-num                     : Please refer to tdm-slot.txt.
+- dai-tdm-slot-width                   : Please refer to tdm-slot.txt.
 - clocks / system-clock-frequency      : specify subnode's clock if needed.
                                          it can be specified via "clocks" if system has
                                          clock node (= common clock), or "system-clock-frequency"
                                          (if system doens't support common clock)
 
-Note:
- * For 'format', 'frame-master', 'bitclock-master', 'bitclock-inversion' and
-   'frame-inversion', the simple card will use the settings of CODEC for both
-   CPU and CODEC sides as we need to keep the settings identical for both ends
-   of the link.
-
 Example 1 - single DAI link:
 
 sound {
        compatible = "simple-audio-card";
        simple-audio-card,name = "VF610-Tower-Sound-Card";
        simple-audio-card,format = "left_j";
+       simple-audio-card,bitclock-master = <&dailink0_master>;
+       simple-audio-card,frame-master = <&dailink0_master>;
        simple-audio-card,widgets =
                "Microphone", "Microphone Jack",
                "Headphone", "Headphone Jack",
@@ -69,17 +89,12 @@ sound {
                "Headphone Jack", "HP_OUT",
                "External Speaker", "LINE_OUT";
 
-       dai-tdm-slot-num = <2>;
-       dai-tdm-slot-width = <8>;
-
        simple-audio-card,cpu {
                sound-dai = <&sh_fsi2 0>;
        };
 
-       simple-audio-card,codec {
+       dailink0_master: simple-audio-card,codec {
                sound-dai = <&ak4648>;
-               bitclock-master;
-               frame-master;
                clocks = <&osc>;
        };
 };
@@ -105,31 +120,31 @@ Example 2 - many DAI links:
 sound {
        compatible = "simple-audio-card";
        simple-audio-card,name = "Cubox Audio";
-       simple-audio-card,format = "i2s";
 
        simple-audio-card,dai-link@0 {          /* I2S - HDMI */
-               simple-audio-card,cpu {
+               format = "i2s";
+               cpu {
                        sound-dai = <&audio1 0>;
                };
-               simple-audio-card,codec {
+               codec {
                        sound-dai = <&tda998x 0>;
                };
        };
 
        simple-audio-card,dai-link@1 {          /* S/PDIF - HDMI */
-               simple-audio-card,cpu {
+               cpu {
                        sound-dai = <&audio1 1>;
                };
-               simple-audio-card,codec {
+               codec {
                        sound-dai = <&tda998x 1>;
                };
        };
 
        simple-audio-card,dai-link@2 {          /* S/PDIF - S/PDIF */
-               simple-audio-card,cpu {
+               cpu {
                        sound-dai = <&audio1 1>;
                };
-               simple-audio-card,codec {
+               codec {
                        sound-dai = <&spdif_codec>;
                };
        };
diff --git a/Documentation/devicetree/bindings/sound/snow.txt b/Documentation/devicetree/bindings/sound/snow.txt
new file mode 100644 (file)
index 0000000..678b191
--- /dev/null
@@ -0,0 +1,17 @@
+Audio Binding for Snow boards
+
+Required properties:
+- compatible : Can be one of the following,
+                       "google,snow-audio-max98090" or
+                       "google,snow-audio-max98095"
+- samsung,i2s-controller: The phandle of the Samsung I2S controller
+- samsung,audio-codec: The phandle of the audio codec
+
+Example:
+
+sound {
+               compatible = "google,snow-audio-max98095";
+
+               samsung,i2s-controller = <&i2s0>;
+               samsung,audio-codec = <&max98095>;
+};
diff --git a/Documentation/devicetree/bindings/sound/st,sta350.txt b/Documentation/devicetree/bindings/sound/st,sta350.txt
new file mode 100644 (file)
index 0000000..b7e71bf
--- /dev/null
@@ -0,0 +1,131 @@
+STA350 audio CODEC
+
+The driver for this device only supports I2C.
+
+Required properties:
+
+  - compatible: "st,sta350"
+  - reg: the I2C address of the device for I2C
+  - reset-gpios: a GPIO spec for the reset pin. If specified, it will be
+                deasserted before communication to the codec starts.
+
+  - power-down-gpios: a GPIO spec for the power down pin. If specified,
+                     it will be deasserted before communication to the codec
+                     starts.
+
+  - vdd-dig-supply: regulator spec, providing 3.3V
+  - vdd-pll-supply: regulator spec, providing 3.3V
+  - vcc-supply: regulator spec, providing 5V - 26V
+
+Optional properties:
+
+  -  st,output-conf: number, Selects the output configuration:
+       0: 2-channel (full-bridge) power, 2-channel data-out
+       1: 2 (half-bridge). 1 (full-bridge) on-board power
+       2: 2 Channel (Full-Bridge) Power, 1 Channel FFX
+       3: 1 Channel Mono-Parallel
+       If parameter is missing, mode 0 will be enabled.
+       This property has to be specified as '/bits/ 8' value.
+
+  -  st,ch1-output-mapping: Channel 1 output mapping
+  -  st,ch2-output-mapping: Channel 2 output mapping
+  -  st,ch3-output-mapping: Channel 3 output mapping
+       0: Channel 1
+       1: Channel 2
+       2: Channel 3
+       If parameter is missing, channel 1 is choosen.
+       This properties have to be specified as '/bits/ 8' values.
+
+  -  st,thermal-warning-recover:
+       If present, thermal warning recovery is enabled.
+
+  -  st,thermal-warning-adjustment:
+       If present, thermal warning adjustment is enabled.
+
+  -  st,fault-detect-recovery:
+       If present, then fault recovery will be enabled.
+
+  -  st,ffx-power-output-mode: string
+       The FFX power output mode selects how the FFX output timing is
+       configured. Must be one of these values:
+         -  "drop-compensation"
+         -  "tapered-compensation"
+         -  "full-power-mode"
+         -  "variable-drop-compensation" (default)
+
+  -  st,drop-compensation-ns: number
+       Only required for "st,ffx-power-output-mode" ==
+       "variable-drop-compensation".
+       Specifies the drop compensation in nanoseconds.
+       The value must be in the range of 0..300, and only
+       multiples of 20 are allowed. Default is 140ns.
+
+  -  st,overcurrent-warning-adjustment:
+       If present, overcurrent warning adjustment is enabled.
+
+  -  st,max-power-use-mpcc:
+       If present, then MPCC bits are used for MPC coefficients,
+       otherwise standard MPC coefficients are used.
+
+  -  st,max-power-corr:
+       If present, power bridge correction for THD reduction near maximum
+       power output is enabled.
+
+  -  st,am-reduction-mode:
+       If present, FFX mode runs in AM reduction mode, otherwise normal
+       FFX mode is used.
+
+  -  st,odd-pwm-speed-mode:
+       If present, PWM speed mode run on odd speed mode (341.3 kHz) on all
+       channels. If not present, normal PWM spped mode (384 kHz) will be used.
+
+  -  st,distortion-compensation:
+       If present, distortion compensation variable uses DCC coefficient.
+       If not present, preset DC coefficient is used.
+
+  -  st,invalid-input-detect-mute:
+       If present, automatic invalid input detect mute is enabled.
+
+  -  st,activate-mute-output:
+       If present, a mute output will be activated in ase the volume will
+       reach a value lower than -76 dBFS.
+
+  -  st,bridge-immediate-off:
+       If present, the bridge will be switched off immediately after the
+       power-down-gpio goes low. Otherwise, the bridge will wait for 13
+       million clock cycles to pass before shutting down.
+
+  -  st,noise-shape-dc-cut:
+       If present, the noise-shaping technique on the DC cutoff filter are
+       enabled.
+
+  -  st,powerdown-master-volume:
+       If present, the power-down pin and I2C power-down functions will
+       act on the master volume. Otherwise, the functions will act on the
+       mute commands.
+
+  -  st,powerdown-delay-divider:
+       If present, the bridge power-down time will be divided by the provided
+       value. If not specified, a divider of 1 will be used. Allowed values
+       are 1, 2, 4, 8, 16, 32, 64 and 128.
+       This property has to be specified as '/bits/ 8' value.
+
+Example:
+
+codec: sta350@38 {
+       compatible = "st,sta350";
+       reg = <0x1c>;
+       reset-gpios = <&gpio1 19 0>;
+       power-down-gpios = <&gpio1 16 0>;
+       st,output-conf = /bits/ 8  <0x3>;       // set output to 2-channel
+                                               // (full-bridge) power,
+                                               // 2-channel data-out
+       st,ch1-output-mapping = /bits/ 8 <0>;   // set channel 1 output ch 1
+       st,ch2-output-mapping = /bits/ 8 <0>;   // set channel 2 output ch 1
+       st,ch3-output-mapping = /bits/ 8 <0>;   // set channel 3 output ch 1
+       st,max-power-correction;                // enables power bridge
+                                               // correction for THD reduction
+                                               // near maximum power output
+       st,invalid-input-detect-mute;           // mute if no valid digital
+                                               // audio signal is provided.
+};
index 22d5740..bbaa857 100644 (file)
@@ -63,7 +63,7 @@ contain the following properties.
                       used for MISO. Defaults to 1 if not present.
 
 Some SPI controllers and devices support Dual and Quad SPI transfer mode.
-It allows data in SPI system transfered in 2 wires(DUAL) or 4 wires(QUAD).
+It allows data in the SPI system to be transferred in 2 wires(DUAL) or 4 wires(QUAD).
 Now the value that spi-tx-bus-width and spi-rx-bus-width can receive is
 only 1(SINGLE), 2(DUAL) and 4(QUAD).
 Dual/Quad mode is not allowed when 3-wire mode is used.
index b8b6871..467ddd1 100644 (file)
@@ -13,7 +13,7 @@ Refer to clk/clock-bindings.txt for generic clock consumer properties
 
 Optional properties:
 - phys: phy provider specifier
-- phy-names: shall be "device"
+- phy-names: shall be "usb2-phy"
 Refer to phy/phy-bindings.txt for generic phy consumer properties
 
 Example:
index 9419804..5261271 100644 (file)
@@ -77,6 +77,7 @@ lsi   LSI Corp. (LSI Logic)
 lltc   Linear Technology Corporation
 marvell        Marvell Technology Group Ltd.
 maxim  Maxim Integrated Products
+micrel Micrel Inc.
 microchip      Microchip Technology Inc.
 mosaixtech     Mosaix Technologies, Inc.
 moxa   Moxa
index ccccc19..acd5668 100644 (file)
@@ -7,6 +7,7 @@ Required properties:
 
 Optional properties:
 - label: a symbolic name for the connector
+- hpd-gpios: HPD GPIO number
 
 Required nodes:
 - Video port for HDMI input
diff --git a/Documentation/devicetree/bindings/video/lgphilips,lb035q02.txt b/Documentation/devicetree/bindings/video/lgphilips,lb035q02.txt
new file mode 100644 (file)
index 0000000..1a1e653
--- /dev/null
@@ -0,0 +1,33 @@
+LG.Philips LB035Q02 Panel
+=========================
+
+Required properties:
+- compatible: "lgphilips,lb035q02"
+- enable-gpios: panel enable gpio
+
+Optional properties:
+- label: a symbolic name for the panel
+
+Required nodes:
+- Video port for DPI input
+
+Example
+-------
+
+lcd-panel: panel@0 {
+       compatible = "lgphilips,lb035q02";
+       reg = <0>;
+       spi-max-frequency = <100000>;
+       spi-cpol;
+       spi-cpha;
+
+       label = "lcd";
+
+       enable-gpios = <&gpio7 7 0>;
+
+       port {
+               lcd_in: endpoint {
+                       remote-endpoint = <&dpi_out>;
+               };
+       };
+};
diff --git a/Documentation/devicetree/bindings/video/panel-dpi.txt b/Documentation/devicetree/bindings/video/panel-dpi.txt
new file mode 100644 (file)
index 0000000..a40180b
--- /dev/null
@@ -0,0 +1,45 @@
+Generic MIPI DPI Panel
+======================
+
+Required properties:
+- compatible: "panel-dpi"
+
+Optional properties:
+- label: a symbolic name for the panel
+- enable-gpios: panel enable gpio
+
+Required nodes:
+- "panel-timing" containing video timings
+  (Documentation/devicetree/bindings/video/display-timing.txt)
+- Video port for DPI input
+
+Example
+-------
+
+lcd0: display@0 {
+        compatible = "samsung,lte430wq-f0c", "panel-dpi";
+        label = "lcd";
+
+        port {
+            lcd_in: endpoint {
+                    remote-endpoint = <&dpi_out>;
+            };
+        };
+
+        panel-timing {
+                clock-frequency = <9200000>;
+                hactive = <480>;
+                vactive = <272>;
+                hfront-porch = <8>;
+                hback-porch = <4>;
+                hsync-len = <41>;
+                vback-porch = <2>;
+                vfront-porch = <4>;
+                vsync-len = <10>;
+
+                hsync-active = <0>;
+                vsync-active = <0>;
+                de-active = <1>;
+                pixelclk-active = <1>;
+        };
+};
diff --git a/Documentation/devicetree/bindings/video/sharp,ls037v7dw01.txt b/Documentation/devicetree/bindings/video/sharp,ls037v7dw01.txt
new file mode 100644 (file)
index 0000000..0cc8981
--- /dev/null
@@ -0,0 +1,43 @@
+SHARP LS037V7DW01 TFT-LCD panel
+===================================
+
+Required properties:
+- compatible: "sharp,ls037v7dw01"
+
+Optional properties:
+- label: a symbolic name for the panel
+- enable-gpios: a GPIO spec for the optional enable pin.
+  This pin is the INI pin as specified in the LS037V7DW01.pdf file.
+- reset-gpios: a GPIO spec for the optional reset pin.
+  This pin is the RESB pin as specified in the LS037V7DW01.pdf file.
+- mode-gpios: a GPIO
+  ordered MO, LR, and UD as specified in the LS037V7DW01.pdf file.
+
+Required nodes:
+- Video port for DPI input
+
+This panel can have zero to five GPIOs to configure to change configuration
+between QVGA and VGA mode and the scan direction. As these pins can be also
+configured with external pulls, all the GPIOs are considered optional with holes
+in the array.
+
+Example
+-------
+
+Example when connected to a omap2+ based device:
+
+lcd0: display {
+       compatible = "sharp,ls037v7dw01";
+       power-supply = <&lcd_3v3>;
+       enable-gpios = <&gpio5 24 GPIO_ACTIVE_HIGH>;    /* gpio152, lcd INI */
+       reset-gpios = <&gpio5 27 GPIO_ACTIVE_HIGH>;     /* gpio155, lcd RESB */
+       mode-gpios = <&gpio5 26 GPIO_ACTIVE_HIGH        /* gpio154, lcd MO */
+                     &gpio1 2 GPIO_ACTIVE_HIGH         /* gpio2, lcd LR */
+                     &gpio1 3 GPIO_ACTIVE_HIGH>;       /* gpio3, lcd UD */
+
+       port {
+               lcd_in: endpoint {
+                       remote-endpoint = <&dpi_out>;
+               };
+       };
+};
index f85d6fc..b8c29fb 100644 (file)
@@ -109,3 +109,7 @@ Required properties:
 
 Optional nodes:
 - Video port for HDMI output
+
+HDMI Endpoint optional properties:
+- lanes: list of 8 pin numbers for the HDMI lanes: CLK+, CLK-, D0+, D0-,
+  D1+, D1-, D2+, D2-. (default: 0,1,2,3,4,5,6,7)
diff --git a/Documentation/devicetree/bindings/video/ti,omap5-dss.txt b/Documentation/devicetree/bindings/video/ti,omap5-dss.txt
new file mode 100644 (file)
index 0000000..38ffc8f
--- /dev/null
@@ -0,0 +1,96 @@
+Texas Instruments OMAP5 Display Subsystem
+=========================================
+
+See Documentation/devicetree/bindings/video/ti,omap-dss.txt for generic
+description about OMAP Display Subsystem bindings.
+
+DSS Core
+--------
+
+Required properties:
+- compatible: "ti,omap5-dss"
+- reg: address and length of the register space
+- ti,hwmods: "dss_core"
+- clocks: handle to fclk
+- clock-names: "fck"
+
+Required nodes:
+- DISPC
+
+Optional nodes:
+- DSS Submodules: RFBI, DSI, HDMI
+- Video port for DPI output
+
+DPI Endpoint required properties:
+- data-lines: number of lines used
+
+
+DISPC
+-----
+
+Required properties:
+- compatible: "ti,omap5-dispc"
+- reg: address and length of the register space
+- ti,hwmods: "dss_dispc"
+- interrupts: the DISPC interrupt
+- clocks: handle to fclk
+- clock-names: "fck"
+
+
+RFBI
+----
+
+Required properties:
+- compatible: "ti,omap5-rfbi"
+- reg: address and length of the register space
+- ti,hwmods: "dss_rfbi"
+- clocks: handles to fclk and iclk
+- clock-names: "fck", "ick"
+
+Optional nodes:
+- Video port for RFBI output
+- RFBI controlled peripherals
+
+
+DSI
+---
+
+Required properties:
+- compatible: "ti,omap5-dsi"
+- reg: addresses and lengths of the register spaces for 'proto', 'phy' and 'pll'
+- reg-names: "proto", "phy", "pll"
+- interrupts: the DSI interrupt line
+- ti,hwmods: "dss_dsi1" or "dss_dsi2"
+- vdd-supply: power supply for DSI
+- clocks: handles to fclk and pll clock
+- clock-names: "fck", "sys_clk"
+
+Optional nodes:
+- Video port for DSI output
+- DSI controlled peripherals
+
+DSI Endpoint required properties:
+- lanes: list of pin numbers for the DSI lanes: CLK+, CLK-, DATA0+, DATA0-,
+  DATA1+, DATA1-, ...
+
+
+HDMI
+----
+
+Required properties:
+- compatible: "ti,omap5-hdmi"
+- reg: addresses and lengths of the register spaces for 'wp', 'pll', 'phy',
+       'core'
+- reg-names: "wp", "pll", "phy", "core"
+- interrupts: the HDMI interrupt line
+- ti,hwmods: "dss_hdmi"
+- vdda-supply: vdda power supply
+- clocks: handles to fclk and pll clock
+- clock-names: "fck", "sys_clk"
+
+Optional nodes:
+- Video port for HDMI output
+
+HDMI Endpoint optional properties:
+- lanes: list of 8 pin numbers for the HDMI lanes: CLK+, CLK-, D0+, D0-,
+  D1+, D1-, D2+, D2-. (default: 0,1,2,3,4,5,6,7)
diff --git a/Documentation/devicetree/bindings/video/toppoly,td028ttec1.txt b/Documentation/devicetree/bindings/video/toppoly,td028ttec1.txt
new file mode 100644 (file)
index 0000000..7175dc3
--- /dev/null
@@ -0,0 +1,30 @@
+Toppoly TD028TTEC1 Panel
+========================
+
+Required properties:
+- compatible: "toppoly,td028ttec1"
+
+Optional properties:
+- label: a symbolic name for the panel
+
+Required nodes:
+- Video port for DPI input
+
+Example
+-------
+
+lcd-panel: td028ttec1@0 {
+       compatible = "toppoly,td028ttec1";
+       reg = <0>;
+       spi-max-frequency = <100000>;
+       spi-cpol;
+       spi-cpha;
+
+       label = "lcd";
+       port {
+               lcd_in: endpoint {
+                       remote-endpoint = <&dpi_out>;
+               };
+       };
+};
+
diff --git a/Documentation/devicetree/bindings/video/tpo,td043mtea1.txt b/Documentation/devicetree/bindings/video/tpo,td043mtea1.txt
new file mode 100644 (file)
index 0000000..ec6d629
--- /dev/null
@@ -0,0 +1,33 @@
+TPO TD043MTEA1 Panel
+====================
+
+Required properties:
+- compatible: "tpo,td043mtea1"
+- reset-gpios: panel reset gpio
+
+Optional properties:
+- label: a symbolic name for the panel
+
+Required nodes:
+- Video port for DPI input
+
+Example
+-------
+
+lcd-panel: panel@0 {
+       compatible = "tpo,td043mtea1";
+       reg = <0>;
+       spi-max-frequency = <100000>;
+       spi-cpol;
+       spi-cpha;
+
+       label = "lcd";
+
+       reset-gpios = <&gpio7 7 0>;
+
+       port {
+               lcd_in: endpoint {
+                       remote-endpoint = <&dpi_out>;
+               };
+       };
+};
index 505e711..67a4087 100644 (file)
@@ -66,7 +66,7 @@ The dma_buf buffer sharing API usage contains the following steps:
 
    Exporting modules which do not wish to provide any specific name may use the
    helper define 'dma_buf_export()', with the same arguments as above, but
-   without the last argument; a __FILE__ pre-processor directive will be
+   without the last argument; a KBUILD_MODNAME pre-processor directive will be
    inserted in place of 'exp_name' instead.
 
 2. Userspace gets a handle to pass around to potential buffer-users
@@ -217,7 +217,7 @@ NOTES:
     and then allow further {map,unmap}_dma_buf operations from any buffer-user
     from the migrated backing-storage.
 
-   If the exporter cannot fulfil the backing-storage constraints of the new
+   If the exporter cannot fulfill the backing-storage constraints of the new
    buffer-user device as requested, dma_buf_attach() would return an error to
    denote non-compatibility of the new buffer-sharing request with the current
    buffer.
@@ -352,7 +352,7 @@ Being able to mmap an export dma-buf buffer object has 2 main use-cases:
 
    No special interfaces, userspace simply calls mmap on the dma-buf fd.
 
-2. Supporting existing mmap interfaces in exporters
+2. Supporting existing mmap interfaces in importers
 
    Similar to the motivation for kernel cpu access it is again important that
    the userspace code of a given importing subsystem can use the same interfaces
index 46325eb..9417871 100644 (file)
@@ -321,7 +321,7 @@ nullarbor:~ # echo -n 'func svc_process -p' >
 nullarbor:~ # echo -n 'format "nfsd: READ" +p' >
                                <debugfs>/dynamic_debug/control
 
-// enable messages in files of which the pathes include string "usb"
+// enable messages in files of which the paths include string "usb"
 nullarbor:~ # echo -n '*usb* +p' > <debugfs>/dynamic_debug/control
 
 // enable all messages
index cb4c2ce..73fff13 100644 (file)
@@ -550,7 +550,7 @@ installs itself as:
        /sys/devices/systm/edac/test-instance
 
 in this directory are various controls, a symlink and one or more 'instance'
-directorys.
+directories.
 
 The standard default controls are:
 
index 8d17aeb..187f3b3 100644 (file)
@@ -3,7 +3,7 @@ Configuration:
 You can pass the following kernel command line options to sm501 videoframebuffer:
 
        sm501fb.bpp=    SM501 Display driver:
-                       Specifiy bits-per-pixel if not specified by 'mode'
+                       Specify bits-per-pixel if not specified by 'mode'
 
        sm501fb.mode=   SM501 Display driver:
                        Specify resolution as
index 550ca77..13db107 100644 (file)
@@ -10,7 +10,7 @@ Introduction
          The main page is located at <http://sstfb.sourceforge.net>, and if
        you want the latest version, check out the CVS, as the driver is a work
        in progress, I feel uncomfortable with releasing tarballs of something
-       not completely working...Don't worry, it's still more than useable
+       not completely working...Don't worry, it's still more than usable
        (I eat my own dog food)
 
          Please read the Bug section, and report any success or failure to me
index 264bcde..ddc531a 100644 (file)
@@ -234,7 +234,7 @@ Table 1-2: Contents of the status files (as of 2.6.30-rc7)
  ShdPnd                      bitmap of shared pending signals for the process
  SigBlk                      bitmap of blocked signals
  SigIgn                      bitmap of ignored signals
- SigCgt                      bitmap of catched signals
+ SigCgt                      bitmap of caught signals
  CapInh                      bitmap of inheritable capabilities
  CapPrm                      bitmap of permitted capabilities
  CapEff                      bitmap of effective capabilities
@@ -300,7 +300,7 @@ Table 1-4: Contents of the stat files (as of 2.6.30-rc7)
   pending       bitmap of pending signals
   blocked       bitmap of blocked signals
   sigign        bitmap of ignored signals
-  sigcatch      bitmap of catched signals
+  sigcatch      bitmap of caught signals
   wchan         address where process went to sleep
   0             (place holder)
   0             (place holder)
@@ -854,7 +854,8 @@ WritebackTmp: Memory used by FUSE for temporary writeback buffers
               if strict overcommit accounting is enabled (mode 2 in
               'vm.overcommit_memory').
               The CommitLimit is calculated with the following formula:
-              CommitLimit = ('vm.overcommit_ratio' * Physical RAM) + Swap
+              CommitLimit = ([total RAM pages] - [total huge TLB pages]) *
+                             overcommit_ratio / 100 + [total swap pages]
               For example, on a system with 1G of physical RAM and 7G
               of swap with a `vm.overcommit_ratio` of 30 it would
               yield a CommitLimit of 7.3G.
index 4ede421..32a173d 100644 (file)
@@ -727,7 +727,7 @@ replicas continue to be exactly same.
                          mkdir -p /tmp/m3
                          mount --rbind /root /tmp/m3
 
-                         I wont' draw the tree..but it has 24 vfsmounts
+                         I won't draw the tree..but it has 24 vfsmounts
 
 
                at step i the number of vfsmounts is V[i] = i*V[i-1].
index 09854fe..d8abfc3 100644 (file)
@@ -41,7 +41,7 @@ Both functions return either a valid GPIO descriptor, or an error code checkable
 with IS_ERR() (they will never return a NULL pointer). -ENOENT will be returned
 if and only if no GPIO has been assigned to the device/function/index triplet,
 other error codes are used for cases where a GPIO has been assigned but an error
-occured while trying to acquire it. This is useful to discriminate between mere
+occurred while trying to acquire it. This is useful to discriminate between mere
 errors and an absence of GPIO for optional GPIO parameters.
 
 Device-managed variants of these functions are also defined:
index ee65936..54c8f97 100644 (file)
@@ -125,7 +125,7 @@ the request was handled successfully.
 
 read()
 ------
-read() will return a queued ouput report. These output reports can be of type
+read() will return a queued output report. These output reports can be of type
 UHID_START, UHID_STOP, UHID_OPEN, UHID_CLOSE, UHID_OUTPUT or UHID_OUTPUT_EV. No
 reaction is required to any of them but you should handle them according to your
 needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads.
index e544c7f..90bca6f 100644 (file)
@@ -94,7 +94,7 @@ PS/2 packet format
 
 Note that the device never signals overflow condition.
 
-ALPS Absolute Mode - Protocol Verion 1
+ALPS Absolute Mode - Protocol Version 1
 --------------------------------------
 
  byte 0:  1    0    0    0    1   x9   x8   x7
index 666c06c..0acfddb 100644 (file)
@@ -226,7 +226,7 @@ And so on up to js31.
 ~~~~~~~~~~~
   evdev is the generic input event interface. It passes the events
 generated in the kernel straight to the program, with timestamps. The
-API is still evolving, but should be useable now. It's described in
+API is still evolving, but should be usable now. It's described in
 section 5. 
 
   This should be the way for GPM and X to get keyboard and mouse
index 4ddcbf9..af55e13 100644 (file)
@@ -214,6 +214,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        unusable.  The "log_buf_len" parameter may be useful
                        if you need to capture more output.
 
+       acpi_force_table_verification   [HW,ACPI]
+                       Enable table checksum verification during early stage.
+                       By default, this is disabled due to x86 early mapping
+                       size limitation.
+
        acpi_irq_balance [HW,ACPI]
                        ACPI will balance active IRQs
                        default in APIC mode
@@ -237,7 +242,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        This feature is enabled by default.
                        This option allows to turn off the feature.
 
-       acpi_no_auto_ssdt       [HW,ACPI] Disable automatic loading of SSDT
+       acpi_no_static_ssdt     [HW,ACPI]
+                       Disable installation of static SSDTs at early boot time
+                       By default, SSDTs contained in the RSDT/XSDT will be
+                       installed automatically and they will appear under
+                       /sys/firmware/acpi/tables.
+                       This option turns off this feature.
+                       Note that specifying this option does not affect
+                       dynamic table installation which will install SSDT
+                       tables to /sys/firmware/acpi/tables/dynamic.
 
        acpica_no_return_repair [HW, ACPI]
                        Disable AML predefined validation mechanism
@@ -2898,6 +2911,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        [KNL, SMP] Set scheduler's default relax_domain_level.
                        See Documentation/cgroups/cpusets.txt.
 
+       relative_sleep_states=
+                       [SUSPEND] Use sleep state labeling where the deepest
+                       state available other than hibernation is always "mem".
+                       Format: { "0" | "1" }
+                       0 -- Traditional sleep state labels.
+                       1 -- Relative sleep state labels.
+
        reserve=        [KNL,BUGS] Force the kernel to ignore some iomem area
 
        reservetop=     [X86-32]
@@ -3470,7 +3490,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        the allocated input device; If set to 0, video driver
                        will only send out the event without touching backlight
                        brightness level.
-                       default: 1
+                       default: 0
 
        virtio_mmio.device=
                        [VMMIO] Memory mapped virtio (platform) device.
index 840fd41..1074cbc 100644 (file)
@@ -48,7 +48,7 @@ configurable between two modes: 1) Hamming, 2) BCH.
 Note that the actual BCH mode: BCH-4 or BCH-8 will depend on the way
 the controller is configured to transfer the data.
 
-In the BCH mode the ECC code will be calculated for each transfered chunk
+In the BCH mode the ECC code will be calculated for each transferred chunk
 and expected to be located (when reading/programming) right after the spare
 bytes as the figure above shows.
 
index 2fa44cb..4f7ae52 100644 (file)
@@ -706,7 +706,7 @@ solution for a couple of reasons:
 
     RX_NO_AUTOTIMER:    Prevent automatically starting the timeout monitor.
 
-    RX_ANNOUNCE_RESUME: If passed at RX_SETUP and a receive timeout occured, a
+    RX_ANNOUNCE_RESUME: If passed at RX_SETUP and a receive timeout occurred, a
       RX_CHANGED message will be generated when the (cyclic) receive restarts.
 
     TX_RESET_MULTI_IDX: Reset the index for the multiple frame transmission.
index bf5dbe3..55c575f 100644 (file)
@@ -86,7 +86,7 @@ built-in CCIDs.
 
 DCCP_SOCKOPT_CCID is write-only and sets both the TX and RX CCIDs at the same
 time, combining the operation of the next two socket options. This option is
-preferrable over the latter two, since often applications will use the same
+preferable over the latter two, since often applications will use the same
 type of CCID for both directions; and mixed use of CCIDs is not currently well
 understood. This socket option takes as argument at least one uint8_t value, or
 an array of uint8_t values, which must match available CCIDS (see above). CCIDs
index 47d46df..d172bce 100644 (file)
@@ -2,6 +2,7 @@ Device Power Management
 
 Copyright (c) 2010-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
 Copyright (c) 2010 Alan Stern <stern@rowland.harvard.edu>
+Copyright (c) 2014 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com>
 
 
 Most of the code in Linux is device drivers, so most of the Linux power
@@ -326,6 +327,20 @@ the phases are:
        driver in some way for the upcoming system power transition, but it
        should not put the device into a low-power state.
 
+       For devices supporting runtime power management, the return value of the
+       prepare callback can be used to indicate to the PM core that it may
+       safely leave the device in runtime suspend (if runtime-suspended
+       already), provided that all of the device's descendants are also left in
+       runtime suspend.  Namely, if the prepare callback returns a positive
+       number and that happens for all of the descendants of the device too,
+       and all of them (including the device itself) are runtime-suspended, the
+       PM core will skip the suspend, suspend_late and suspend_noirq suspend
+       phases as well as the resume_noirq, resume_early and resume phases of
+       the following system resume for all of these devices.   In that case,
+       the complete callback will be called directly after the prepare callback
+       and is entirely responsible for bringing the device back to the
+       functional state as appropriate.
+
     2. The suspend methods should quiesce the device to stop it from performing
        I/O.  They also may save the device registers and put it into the
        appropriate low-power state, depending on the bus type the device is on,
@@ -400,12 +415,23 @@ When resuming from freeze, standby or memory sleep, the phases are:
        the resume callbacks occur; it's not necessary to wait until the
        complete phase.
 
+       Moreover, if the preceding prepare callback returned a positive number,
+       the device may have been left in runtime suspend throughout the whole
+       system suspend and resume (the suspend, suspend_late, suspend_noirq
+       phases of system suspend and the resume_noirq, resume_early, resume
+       phases of system resume may have been skipped for it).  In that case,
+       the complete callback is entirely responsible for bringing the device
+       back to the functional state after system suspend if necessary.  [For
+       example, it may need to queue up a runtime resume request for the device
+       for this purpose.]  To check if that is the case, the complete callback
+       can consult the device's power.direct_complete flag.  Namely, if that
+       flag is set when the complete callback is being run, it has been called
+       directly after the preceding prepare and special action may be required
+       to make the device work correctly afterward.
+
 At the end of these phases, drivers should be as functional as they were before
 suspending: I/O can be performed using DMA and IRQs, and the relevant clocks are
-gated on.  Even if the device was in a low-power state before the system sleep
-because of runtime power management, afterwards it should be back in its
-full-power state.  There are multiple reasons why it's best to do this; they are
-discussed in more detail in Documentation/power/runtime_pm.txt.
+gated on.
 
 However, the details here may again be platform-specific.  For example,
 some systems support multiple "run" states, and the mode in effect at
index b8a907d..a9adad8 100644 (file)
@@ -10,8 +10,7 @@ Contents
 3. OPP Search Functions
 4. OPP Availability Control Functions
 5. OPP Data Retrieval Functions
-6. Cpufreq Table Generation
-7. Data Structures
+6. Data Structures
 
 1. Introduction
 ===============
@@ -72,7 +71,6 @@ operations until that OPP could be re-enabled if possible.
 OPP library facilitates this concept in it's implementation. The following
 operational functions operate only on available opps:
 opp_find_freq_{ceil, floor}, dev_pm_opp_get_voltage, dev_pm_opp_get_freq, dev_pm_opp_get_opp_count
-and dev_pm_opp_init_cpufreq_table
 
 dev_pm_opp_find_freq_exact is meant to be used to find the opp pointer which can then
 be used for dev_pm_opp_enable/disable functions to make an opp available as required.
@@ -96,10 +94,9 @@ using RCU read locks. The opp_find_freq_{exact,ceil,floor},
 opp_get_{voltage, freq, opp_count} fall into this category.
 
 opp_{add,enable,disable} are updaters which use mutex and implement it's own
-RCU locking mechanisms. dev_pm_opp_init_cpufreq_table acts as an updater and uses
-mutex to implment RCU updater strategy. These functions should *NOT* be called
-under RCU locks and other contexts that prevent blocking functions in RCU or
-mutex operations from working.
+RCU locking mechanisms. These functions should *NOT* be called under RCU locks
+and other contexts that prevent blocking functions in RCU or mutex operations
+from working.
 
 2. Initial OPP List Registration
 ================================
@@ -311,34 +308,7 @@ dev_pm_opp_get_opp_count - Retrieve the number of available opps for a device
                /* Do other things */
         }
 
-6. Cpufreq Table Generation
-===========================
-dev_pm_opp_init_cpufreq_table - cpufreq framework typically is initialized with
-       cpufreq_frequency_table_cpuinfo which is provided with the list of
-       frequencies that are available for operation. This function provides
-       a ready to use conversion routine to translate the OPP layer's internal
-       information about the available frequencies into a format readily
-       providable to cpufreq.
-
-       WARNING: Do not use this function in interrupt context.
-
-       Example:
-        soc_pm_init()
-        {
-               /* Do things */
-               r = dev_pm_opp_init_cpufreq_table(dev, &freq_table);
-               if (!r)
-                       cpufreq_frequency_table_cpuinfo(policy, freq_table);
-               /* Do other things */
-        }
-
-       NOTE: This function is available only if CONFIG_CPU_FREQ is enabled in
-       addition to CONFIG_PM as power management feature is required to
-       dynamically scale voltage and frequency in a system.
-
-dev_pm_opp_free_cpufreq_table - Free up the table allocated by dev_pm_opp_init_cpufreq_table
-
-7. Data Structures
+6. Data Structures
 ==================
 Typically an SoC contains multiple voltage domains which are variable. Each
 domain is represented by a device pointer. The relationship to OPP can be
index 5f96daf..f32ce54 100644 (file)
@@ -2,6 +2,7 @@ Runtime Power Management Framework for I/O Devices
 
 (C) 2009-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
 (C) 2010 Alan Stern <stern@rowland.harvard.edu>
+(C) 2014 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com>
 
 1. Introduction
 
@@ -444,6 +445,10 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
   bool pm_runtime_status_suspended(struct device *dev);
     - return true if the device's runtime PM status is 'suspended'
 
+  bool pm_runtime_suspended_if_enabled(struct device *dev);
+    - return true if the device's runtime PM status is 'suspended' and its
+      'power.disable_depth' field is equal to 1
+
   void pm_runtime_allow(struct device *dev);
     - set the power.runtime_auto flag for the device and decrease its usage
       counter (used by the /sys/devices/.../power/control interface to
@@ -644,19 +649,33 @@ place (in particular, if the system is not waking up from hibernation), it may
 be more efficient to leave the devices that had been suspended before the system
 suspend began in the suspended state.
 
+To this end, the PM core provides a mechanism allowing some coordination between
+different levels of device hierarchy.  Namely, if a system suspend .prepare()
+callback returns a positive number for a device, that indicates to the PM core
+that the device appears to be runtime-suspended and its state is fine, so it
+may be left in runtime suspend provided that all of its descendants are also
+left in runtime suspend.  If that happens, the PM core will not execute any
+system suspend and resume callbacks for all of those devices, except for the
+complete callback, which is then entirely responsible for handling the device
+as appropriate.  This only applies to system suspend transitions that are not
+related to hibernation (see Documentation/power/devices.txt for more
+information).
+
 The PM core does its best to reduce the probability of race conditions between
 the runtime PM and system suspend/resume (and hibernation) callbacks by carrying
 out the following operations:
 
-  * During system suspend it calls pm_runtime_get_noresume() and
-    pm_runtime_barrier() for every device right before executing the
-    subsystem-level .suspend() callback for it.  In addition to that it calls
-    __pm_runtime_disable() with 'false' as the second argument for every device
-    right before executing the subsystem-level .suspend_late() callback for it.
-
-  * During system resume it calls pm_runtime_enable() and pm_runtime_put()
-    for every device right after executing the subsystem-level .resume_early()
-    callback and right after executing the subsystem-level .resume() callback
+  * During system suspend pm_runtime_get_noresume() is called for every device
+    right before executing the subsystem-level .prepare() callback for it and
+    pm_runtime_barrier() is called for every device right before executing the
+    subsystem-level .suspend() callback for it.  In addition to that the PM core
+    calls  __pm_runtime_disable() with 'false' as the second argument for every
+    device right before executing the subsystem-level .suspend_late() callback
+    for it.
+
+  * During system resume pm_runtime_enable() and pm_runtime_put() are called for
+    every device right after executing the subsystem-level .resume_early()
+    callback and right after executing the subsystem-level .complete() callback
     for it, respectively.
 
 7. Generic subsystem callbacks
index 442d43d..50f3ef9 100644 (file)
@@ -1,62 +1,87 @@
+System Power Management Sleep States
 
-System Power Management States
+(C) 2014 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com>
 
+The kernel supports up to four system sleep states generically, although three
+of them depend on the platform support code to implement the low-level details
+for each state.
 
-The kernel supports four power management states generically, though
-one is generic and the other three are dependent on platform support
-code to implement the low-level details for each state.
-This file describes each state, what they are
-commonly called, what ACPI state they map to, and what string to write
-to /sys/power/state to enter that state
+The states are represented by strings that can be read or written to the
+/sys/power/state file.  Those strings may be "mem", "standby", "freeze" and
+"disk", where the last one always represents hibernation (Suspend-To-Disk) and
+the meaning of the remaining ones depends on the relative_sleep_states command
+line argument.
 
-state:         Freeze / Low-Power Idle
+For relative_sleep_states=1, the strings "mem", "standby" and "freeze" label the
+available non-hibernation sleep states from the deepest to the shallowest,
+respectively.  In that case, "mem" is always present in /sys/power/state,
+because there is at least one non-hibernation sleep state in every system.  If
+the given system supports two non-hibernation sleep states, "standby" is present
+in /sys/power/state in addition to "mem".  If the system supports three
+non-hibernation sleep states, "freeze" will be present in /sys/power/state in
+addition to "mem" and "standby".
+
+For relative_sleep_states=0, which is the default, the following descriptions
+apply.
+
+state:         Suspend-To-Idle
 ACPI state:    S0
-String:                "freeze"
+Label:         "freeze"
 
-This state is a generic, pure software, light-weight, low-power state.
-It allows more energy to be saved relative to idle by freezing user
+This state is a generic, pure software, light-weight, system sleep state.
+It allows more energy to be saved relative to runtime idle by freezing user
 space and putting all I/O devices into low-power states (possibly
 lower-power than available at run time), such that the processors can
 spend more time in their idle states.
-This state can be used for platforms without Standby/Suspend-to-RAM
+
+This state can be used for platforms without Power-On Suspend/Suspend-to-RAM
 support, or it can be used in addition to Suspend-to-RAM (memory sleep)
-to provide reduced resume latency.
+to provide reduced resume latency.  It is always supported.
 
 
 State:         Standby / Power-On Suspend
 ACPI State:    S1
-String:                "standby"
+Label:         "standby"
 
-This state offers minimal, though real, power savings, while providing
-a very low-latency transition back to a working system. No operating
-state is lost (the CPU retains power), so the system easily starts up
+This state, if supported, offers moderate, though real, power savings, while
+providing a relatively low-latency transition back to a working system.  No
+operating state is lost (the CPU retains power), so the system easily starts up
 again where it left off. 
 
-We try to put devices in a low-power state equivalent to D1, which
-also offers low power savings, but low resume latency. Not all devices
-support D1, and those that don't are left on. 
+In addition to freezing user space and putting all I/O devices into low-power
+states, which is done for Suspend-To-Idle too, nonboot CPUs are taken offline
+and all low-level system functions are suspended during transitions into this
+state.  For this reason, it should allow more energy to be saved relative to
+Suspend-To-Idle, but the resume latency will generally be greater than for that
+state.
 
 
 State:         Suspend-to-RAM
 ACPI State:    S3
-String:                "mem"
+Label:         "mem"
 
-This state offers significant power savings as everything in the
-system is put into a low-power state, except for memory, which is
-placed in self-refresh mode to retain its contents. 
+This state, if supported, offers significant power savings as everything in the
+system is put into a low-power state, except for memory, which should be placed
+into the self-refresh mode to retain its contents.  All of the steps carried out
+when entering Power-On Suspend are also carried out during transitions to STR.
+Additional operations may take place depending on the platform capabilities.  In
+particular, on ACPI systems the kernel passes control to the BIOS (platform
+firmware) as the last step during STR transitions and that usually results in
+powering down some more low-level components that aren't directly controlled by
+the kernel.
 
-System and device state is saved and kept in memory. All devices are
-suspended and put into D3. In many cases, all peripheral buses lose
-power when entering STR, so devices must be able to handle the
-transition back to the On state. 
+System and device state is saved and kept in memory.  All devices are suspended
+and put into low-power states.  In many cases, all peripheral buses lose power
+when entering STR, so devices must be able to handle the transition back to the
+"on" state.
 
-For at least ACPI, STR requires some minimal boot-strapping code to
-resume the system from STR. This may be true on other platforms. 
+For at least ACPI, STR requires some minimal boot-strapping code to resume the
+system from it.  This may be the case on other platforms too.
 
 
 State:         Suspend-to-disk
 ACPI State:    S4
-String:                "disk"
+Label:         "disk"
 
 This state offers the greatest power savings, and can be used even in
 the absence of low-level platform support for power management. This
index 079160e..f732a83 100644 (file)
@@ -220,7 +220,10 @@ Q: After resuming, system is paging heavily, leading to very bad interactivity.
 
 A: Try running
 
-cat `cat /proc/[0-9]*/maps | grep / | sed 's:.* /:/:' | sort -u` > /dev/null
+cat /proc/[0-9]*/maps | grep / | sed 's:.* /:/:' | sort -u | while read file
+do
+  test -f "$file" && cat "$file" > /dev/null
+done
 
 after resume. swapoff -a; swapon -a may also be useful.
 
index dc23e58..9791e98 100644 (file)
@@ -160,7 +160,7 @@ To avoid this, when taking a signal in an active transaction, we need to use
 the stack pointer from the checkpointed state, rather than the speculated
 state.  This ensures that the signal context (written tm suspended) will be
 written below the stack required for the rollback.  The transaction is aborted
-becuase of the treclaim, so any memory written between the tbegin and the
+because of the treclaim, so any memory written between the tbegin and the
 signal will be rolled back anyway.
 
 For signals taken in non-TM or suspended mode, we use the
index 6f4eb32..b449821 100644 (file)
@@ -199,11 +199,11 @@ struct va_format:
        Do not use this feature without some mechanism to verify the
        correctness of the format string and va_list arguments.
 
-u64 SHOULD be printed with %llu/%llx, (unsigned long long):
+u64 SHOULD be printed with %llu/%llx:
 
        printk("%llu", u64_var);
 
-s64 SHOULD be printed with %lld/%llx, (long long):
+s64 SHOULD be printed with %lld/%llx:
 
        printk("%lld", s64_var);
 
index 61b6c48..39873ef 100644 (file)
@@ -255,7 +255,7 @@ However, rbtree can be augmented to store such interval ranges in a structured
 way making it possible to do efficient lookup and exact match.
 
 This "extra information" stored in each node is the maximum hi
-(max_hi) value among all the nodes that are its descendents. This
+(max_hi) value among all the nodes that are its descendants. This
 information can be maintained at each node just be looking at the node
 and its immediate children. And this will be used in O(log n) lookup
 for lowest match (lowest start address among all possible matches)
index f430004..427e897 100644 (file)
@@ -21,7 +21,7 @@ aircraft.
 The rfkill subsystem has a concept of "hard" and "soft" block, which
 differ little in their meaning (block == transmitters off) but rather in
 whether they can be changed or not:
- - hard block: read-only radio block that cannot be overriden by software
+ - hard block: read-only radio block that cannot be overridden by software
  - soft block: writable radio block (need not be readable) that is set by
                the system software.
 
index 0a9446a..af6fce2 100644 (file)
@@ -210,7 +210,7 @@ i386 and x86_64 syscalls are wired up at the moment, and Ulrich has
 tested the new glibc code (on x86_64 and i386), and it works for his
 robust-mutex testcases.
 
-All other architectures should build just fine too - but they wont have
+All other architectures should build just fine too - but they won't have
 the new syscalls yet.
 
 Architectures need to implement the new futex_atomic_cmpxchg_inatomic()
index beeaa4b..d372958 100644 (file)
@@ -10,7 +10,7 @@ Author: Gerald Schaefer (geraldsc@de.ibm.com)
 Description
 ===========
 This item delivers a new Linux API in the form of a misc char device that is
-useable from user space and allows read access to the z/VM Monitor Records
+usable from user space and allows read access to the z/VM Monitor Records
 collected by the *MONITOR System Service of z/VM.
 
 
index dd908cf..227a63f 100644 (file)
@@ -37,7 +37,7 @@ still work as root).
 In mode 1, software that has defined application-specific relationships
 between a debugging process and its inferior (crash handlers, etc),
 prctl(PR_SET_PTRACER, pid, ...) can be used. An inferior can declare which
-other process (and its descendents) are allowed to call PTRACE_ATTACH
+other process (and its descendants) are allowed to call PTRACE_ATTACH
 against it. Only one such declared debugging process can exists for
 each inferior at a time. For example, this is used by KDE, Chromium, and
 Firefox's crash handlers, and by Wine for allowing only Wine processes
index b8dd0df..7ccf933 100644 (file)
@@ -948,7 +948,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     avoided as much as possible...
     
     MORE NOTES ON "azx_get_response timeout" PROBLEMS:
-    On some hardwares, you may need to add a proper probe_mask option
+    On some hardware, you may need to add a proper probe_mask option
     to avoid the "azx_get_response timeout" problem above, instead.
     This occurs when the access to non-existing or non-working codec slot
     (likely a modem one) causes a stall of the communication via HD-audio
@@ -1124,7 +1124,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     buggy_irq     - Enable workaround for buggy interrupts on some
                     motherboards (default yes on nForce chips,
                    otherwise off)
-    buggy_semaphore - Enable workaround for hardwares with buggy
+    buggy_semaphore - Enable workaround for hardware with buggy
                    semaphores (e.g. on some ASUS laptops)
                    (default off)
     spdif_aclink  - Use S/PDIF over AC-link instead of direct connection
index c94435d..75d25a1 100644 (file)
@@ -443,7 +443,7 @@ The following commands are supported:
   The following command creates a snapshot every time a block request
   queue is unplugged with a depth > 1.  If you were tracing a set of
   events or functions at the time, the snapshot trace buffer would
-  capture those events when the trigger event occured:
+  capture those events when the trigger event occurred:
 
   # echo 'snapshot if nr_rq > 1' > \
         /sys/kernel/debug/tracing/events/block/block_unplug/trigger
index 59063ad..e89803a 100644 (file)
@@ -13,7 +13,7 @@
   operation.
 
   Note that the driver is slightly non-portable in that it assumes
-  a single memory/DMA buffer will be useable for bulk-in and bulk-out
+  a single memory/DMA buffer will be usable for bulk-in and bulk-out
   endpoints.  With most device controllers this is not an issue, but
   there may be some with hardware restrictions that prevent a buffer
   from being used by more than one endpoint.
index 2f6e935..b092c0a 100644 (file)
 163 -> Bt848 Capture 14MHz
 164 -> CyberVision CV06 (SV)
 165 -> Kworld V-Stream Xpert TV PVR878
+166 -> PCI-8604PW
index e085b12..5a3ddcd 100644 (file)
@@ -92,3 +92,4 @@
  91 -> SpeedLink Vicious And Devine Laplace webcam (em2765)        [1ae7:9003,1ae7:9004]
  92 -> PCTV DVB-S2 Stick (461e)                 (em28178)
  93 -> KWorld USB ATSC TV Stick UB435-Q V3      (em2874)        [1b80:e34c]
+ 94 -> PCTV tripleStick (292e)                  (em28178)
index 7d6e160..e0c6b8b 100644 (file)
@@ -140,39 +140,9 @@ You can either grep through the kernel log to find relevant information, i.e.
 or retrieve the information from /dev/media? with help of the media-ctl tool:
 # media-ctl -p
 
-6. Platform support
-===================
-
-The machine code (arch/arm/plat-samsung and arch/arm/mach-*) must select
-following options:
-
-CONFIG_S5P_DEV_FIMC0       mandatory
-CONFIG_S5P_DEV_FIMC1  \
-CONFIG_S5P_DEV_FIMC2  |    optional
-CONFIG_S5P_DEV_FIMC3  |
-CONFIG_S5P_SETUP_FIMC /
-CONFIG_S5P_DEV_CSIS0  \    optional for MIPI-CSI interface
-CONFIG_S5P_DEV_CSIS1  /
-
-Except that, relevant s5p_device_fimc? should be registered in the machine code
-in addition to a "s5p-fimc-md" platform device to which the media device driver
-is bound.  The "s5p-fimc-md" device instance is required even if only mem-to-mem
-operation is used.
-
-The description of sensor(s) attached to FIMC/MIPI-CSIS camera inputs should be
-passed as the "s5p-fimc-md" device platform_data.  The platform data structure
-is defined in file include/media/s5p_fimc.h.
-
 7. Build
 ========
 
-This driver depends on following config options:
-PLAT_S5P,
-PM_RUNTIME,
-I2C,
-REGULATOR,
-VIDEO_V4L2_SUBDEV_API,
-
 If the driver is built as a loadable kernel module (CONFIG_VIDEO_SAMSUNG_S5P_FIMC=m)
 two modules are created (in addition to the core v4l2 modules): s5p-fimc.ko and
 optional s5p-csis.ko (MIPI-CSI receiver subdev).
index 3a1c0d2..46904fe 100644 (file)
@@ -77,7 +77,8 @@ struct skeleton {
 
        spinlock_t qlock;
        struct list_head buf_list;
-       unsigned int sequence;
+       unsigned field;
+       unsigned sequence;
 };
 
 struct skel_buffer {
@@ -124,7 +125,7 @@ static const struct v4l2_dv_timings_cap skel_timings_cap = {
  * Interrupt handler: typically interrupts happen after a new frame has been
  * captured. It is the job of the handler to remove the new frame from the
  * internal list and give it back to the vb2 framework, updating the sequence
- * counter and timestamp at the same time.
+ * counter, field and timestamp at the same time.
  */
 static irqreturn_t skeleton_irq(int irq, void *dev_id)
 {
@@ -139,8 +140,15 @@ static irqreturn_t skeleton_irq(int irq, void *dev_id)
                spin_lock(&skel->qlock);
                list_del(&new_buf->list);
                spin_unlock(&skel->qlock);
-               new_buf->vb.v4l2_buf.sequence = skel->sequence++;
                v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp);
+               new_buf->vb.v4l2_buf.sequence = skel->sequence++;
+               new_buf->vb.v4l2_buf.field = skel->field;
+               if (skel->format.field == V4L2_FIELD_ALTERNATE) {
+                       if (skel->field == V4L2_FIELD_BOTTOM)
+                               skel->field = V4L2_FIELD_TOP;
+                       else if (skel->field == V4L2_FIELD_TOP)
+                               skel->field = V4L2_FIELD_BOTTOM;
+               }
                vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE);
        }
 #endif
@@ -160,6 +168,17 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
 {
        struct skeleton *skel = vb2_get_drv_priv(vq);
 
+       skel->field = skel->format.field;
+       if (skel->field == V4L2_FIELD_ALTERNATE) {
+               /*
+                * You cannot use read() with FIELD_ALTERNATE since the field
+                * information (TOP/BOTTOM) cannot be passed back to the user.
+                */
+               if (vb2_fileio_is_active(vq))
+                       return -EINVAL;
+               skel->field = V4L2_FIELD_TOP;
+       }
+
        if (vq->num_buffers + *nbuffers < 3)
                *nbuffers = 3 - vq->num_buffers;
 
@@ -173,10 +192,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
 
 /*
  * Prepare the buffer for queueing to the DMA engine: check and set the
- * payload size and fill in the field. Note: if the format's field is
- * V4L2_FIELD_ALTERNATE, then vb->v4l2_buf.field should be set in the
- * interrupt handler since that's usually where you know if the TOP or
- * BOTTOM field has been captured.
+ * payload size.
  */
 static int buffer_prepare(struct vb2_buffer *vb)
 {
@@ -190,7 +206,6 @@ static int buffer_prepare(struct vb2_buffer *vb)
        }
 
        vb2_set_plane_payload(vb, 0, size);
-       vb->v4l2_buf.field = skel->format.field;
        return 0;
 }
 
@@ -254,7 +269,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
  * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued
  * and passed on to the vb2 framework marked as STATE_ERROR.
  */
-static int stop_streaming(struct vb2_queue *vq)
+static void stop_streaming(struct vb2_queue *vq)
 {
        struct skeleton *skel = vb2_get_drv_priv(vq);
 
@@ -262,7 +277,6 @@ static int stop_streaming(struct vb2_queue *vq)
 
        /* Release all active buffers */
        return_all_buffers(skel, VB2_BUF_STATE_ERROR);
-       return 0;
 }
 
 /*
@@ -319,10 +333,12 @@ static void skeleton_fill_pix_format(struct skeleton *skel,
                /* HDMI input */
                pix->width = skel->timings.bt.width;
                pix->height = skel->timings.bt.height;
-               if (skel->timings.bt.interlaced)
-                       pix->field = V4L2_FIELD_INTERLACED;
-               else
+               if (skel->timings.bt.interlaced) {
+                       pix->field = V4L2_FIELD_ALTERNATE;
+                       pix->height /= 2;
+               } else {
                        pix->field = V4L2_FIELD_NONE;
+               }
                pix->colorspace = V4L2_COLORSPACE_REC709;
        }
 
index b4f5365..0fe3649 100644 (file)
@@ -1794,6 +1794,11 @@ registers, find a list below:
   PPC   | KVM_REG_PPC_MMCR0     | 64
   PPC   | KVM_REG_PPC_MMCR1     | 64
   PPC   | KVM_REG_PPC_MMCRA     | 64
+  PPC   | KVM_REG_PPC_MMCR2     | 64
+  PPC   | KVM_REG_PPC_MMCRS     | 64
+  PPC   | KVM_REG_PPC_SIAR      | 64
+  PPC   | KVM_REG_PPC_SDAR      | 64
+  PPC   | KVM_REG_PPC_SIER      | 64
   PPC   | KVM_REG_PPC_PMC1      | 32
   PPC   | KVM_REG_PPC_PMC2      | 32
   PPC   | KVM_REG_PPC_PMC3      | 32
@@ -1868,6 +1873,7 @@ registers, find a list below:
   PPC   | KVM_REG_PPC_PPR      | 64
   PPC   | KVM_REG_PPC_ARCH_COMPAT 32
   PPC   | KVM_REG_PPC_DABRX     | 32
+  PPC   | KVM_REG_PPC_WORT      | 64
   PPC   | KVM_REG_PPC_TM_GPR0  | 64
           ...
   PPC   | KVM_REG_PPC_TM_GPR31 | 64
@@ -2066,7 +2072,7 @@ the "Server" class MMU emulation supported by KVM.
 This can in turn be used by userspace to generate the appropriate
 device-tree properties for the guest operating system.
 
-The structure contains some global informations, followed by an
+The structure contains some global information, followed by an
 array of supported segment page sizes:
 
       struct kvm_ppc_smmu_info {
@@ -2211,6 +2217,8 @@ KVM_S390_SIGP_STOP (vcpu) - sigp restart
 KVM_S390_PROGRAM_INT (vcpu) - program check; code in parm
 KVM_S390_SIGP_SET_PREFIX (vcpu) - sigp set prefix; prefix address in parm
 KVM_S390_RESTART (vcpu) - restart
+KVM_S390_INT_CLOCK_COMP (vcpu) - clock comparator interrupt
+KVM_S390_INT_CPU_TIMER (vcpu) - CPU timer interrupt
 KVM_S390_INT_VIRTIO (vm) - virtio external interrupt; external interrupt
                           parameters in parm and parm64
 KVM_S390_INT_SERVICE (vm) - sclp external interrupt; sclp parameter in parm
@@ -2314,8 +2322,8 @@ struct kvm_create_device {
 
 4.80 KVM_SET_DEVICE_ATTR/KVM_GET_DEVICE_ATTR
 
-Capability: KVM_CAP_DEVICE_CTRL
-Type: device ioctl
+Capability: KVM_CAP_DEVICE_CTRL, KVM_CAP_VM_ATTRIBUTES for vm device
+Type: device ioctl, vm ioctl
 Parameters: struct kvm_device_attr
 Returns: 0 on success, -1 on error
 Errors:
@@ -2340,8 +2348,8 @@ struct kvm_device_attr {
 
 4.81 KVM_HAS_DEVICE_ATTR
 
-Capability: KVM_CAP_DEVICE_CTRL
-Type: device ioctl
+Capability: KVM_CAP_DEVICE_CTRL, KVM_CAP_VM_ATTRIBUTES for vm device
+Type: device ioctl, vm ioctl
 Parameters: struct kvm_device_attr
 Returns: 0 on success, -1 on error
 Errors:
@@ -2376,6 +2384,8 @@ Possible features:
          Depends on KVM_CAP_ARM_PSCI.
        - KVM_ARM_VCPU_EL1_32BIT: Starts the CPU in a 32bit mode.
          Depends on KVM_CAP_ARM_EL1_32BIT (arm64 only).
+       - KVM_ARM_VCPU_PSCI_0_2: Emulate PSCI v0.2 for the CPU.
+         Depends on KVM_CAP_ARM_PSCI_0_2.
 
 
 4.83 KVM_ARM_PREFERRED_TARGET
@@ -2738,6 +2748,21 @@ It gets triggered whenever both KVM_CAP_PPC_EPR are enabled and an
 external interrupt has just been delivered into the guest. User space
 should put the acknowledged interrupt vector into the 'epr' field.
 
+               /* KVM_EXIT_SYSTEM_EVENT */
+               struct {
+#define KVM_SYSTEM_EVENT_SHUTDOWN       1
+#define KVM_SYSTEM_EVENT_RESET          2
+                       __u32 type;
+                       __u64 flags;
+               } system_event;
+
+If exit_reason is KVM_EXIT_SYSTEM_EVENT then the vcpu has triggered
+a system-level event using some architecture specific mechanism (hypercall
+or some special instruction). In case of ARM/ARM64, this is triggered using
+HVC instruction based PSCI call from the vcpu. The 'type' field describes
+the system-level event type. The 'flags' field describes architecture
+specific flags for the system-level event.
+
                /* Fix the size of the union. */
                char padding[256];
        };
diff --git a/Documentation/virtual/kvm/devices/vm.txt b/Documentation/virtual/kvm/devices/vm.txt
new file mode 100644 (file)
index 0000000..0d16f96
--- /dev/null
@@ -0,0 +1,26 @@
+Generic vm interface
+====================================
+
+The virtual machine "device" also accepts the ioctls KVM_SET_DEVICE_ATTR,
+KVM_GET_DEVICE_ATTR, and KVM_HAS_DEVICE_ATTR. The interface uses the same
+struct kvm_device_attr as other devices, but targets VM-wide settings
+and controls.
+
+The groups and attributes per virtual machine, if any, are architecture
+specific.
+
+1. GROUP: KVM_S390_VM_MEM_CTRL
+Architectures: s390
+
+1.1. ATTRIBUTE: KVM_S390_VM_MEM_CTRL
+Parameters: none
+Returns: -EBUSY if already a vcpus is defined, otherwise 0
+
+Enables CMMA for the virtual machine
+
+1.2. ATTRIBUTE: KVM_S390_VM_CLR_CMMA
+Parameteres: none
+Returns: 0
+
+Clear the CMMA status for all guest pages, so any pages the guest marked
+as unused are again used any may not be reclaimed by the host.
index 4643cde..3195606 100644 (file)
@@ -94,10 +94,24 @@ a bitmap of available features inside the magic page.
 The following enhancements to the magic page are currently available:
 
   KVM_MAGIC_FEAT_SR            Maps SR registers r/w in the magic page
+  KVM_MAGIC_FEAT_MAS0_TO_SPRG7 Maps MASn, ESR, PIR and high SPRGs
 
 For enhanced features in the magic page, please check for the existence of the
 feature before using them!
 
+Magic page flags
+================
+
+In addition to features that indicate whether a host is capable of a particular
+feature we also have a channel for a guest to tell the guest whether it's capable
+of something. This is what we call "flags".
+
+Flags are passed to the host in the low 12 bits of the Effective Address.
+
+The following flags are currently available for a guest to expose:
+
+  MAGIC_PAGE_FLAG_NOT_MAPPED_NX Guest handles NX bits correclty wrt magic page
+
 MSR bits
 ========
 
index f1de4fb..48c4921 100644 (file)
@@ -78,3 +78,5 @@ DIAGNOSE function code 'X'501 - KVM breakpoint
 
 If the function code specifies 0x501, breakpoint functions may be performed.
 This function code is handled by userspace.
+
+This diagnose function code has no subfunctions and uses no parameters.
index 4a63953..6b31cfb 100644 (file)
@@ -360,13 +360,13 @@ on any tail page, would mean having to split all hugepages upfront in
 get_user_pages which is unacceptable as too many gup users are
 performance critical and they must work natively on hugepages like
 they work natively on hugetlbfs already (hugetlbfs is simpler because
-hugetlbfs pages cannot be splitted so there wouldn't be requirement of
+hugetlbfs pages cannot be split so there wouldn't be requirement of
 accounting the pins on the tail pages for hugetlbfs). If we wouldn't
 account the gup refcounts on the tail pages during gup, we won't know
 anymore which tail page is pinned by gup and which is not while we run
 split_huge_page. But we still have to add the gup pin to the head page
 too, to know when we can free the compound page in case it's never
-splitted during its lifetime. That requires changing not just
+split during its lifetime. That requires changing not just
 get_page, but put_page as well so that when put_page runs on a tail
 page (and only on a tail page) it will find its respective head page,
 and then it will decrease the head page refcount in addition to the
index f19802c..688e3ee 100644 (file)
@@ -33,7 +33,7 @@ and two USB cables, connected like this:
  ...
 
 ( If your system does not list a debug port capability then you probably
-  wont be able to use the USB debug key. )
+  won't be able to use the USB debug key. )
 
  b.) You also need a Netchip USB debug cable/key:
 
index 30b4c71..15f5baf 100644 (file)
@@ -87,7 +87,7 @@ your PCI configuration:
 
        echo -n pirq=; echo `scanpci | grep T_L | cut -c56-` | sed 's/ /,/g'
 
-note that this script wont work if you have skipped a few slots or if your
+note that this script won't work if you have skipped a few slots or if your
 board does not do default daisy-chaining. (or the IO-APIC has the PIRQ pins
 connected in some strange way). E.g. if in the above case you have your SCSI
 card (IRQ11) in Slot3, and have Slot1 empty:
index 53feaaf..e433e45 100644 (file)
@@ -2405,7 +2405,6 @@ F:        drivers/net/ethernet/ti/cpmac.c
 CPU FREQUENCY DRIVERS
 M:     Rafael J. Wysocki <rjw@rjwysocki.net>
 M:     Viresh Kumar <viresh.kumar@linaro.org>
-L:     cpufreq@vger.kernel.org
 L:     linux-pm@vger.kernel.org
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
@@ -2416,7 +2415,6 @@ F:        include/linux/cpufreq.h
 CPU FREQUENCY DRIVERS - ARM BIG LITTLE
 M:     Viresh Kumar <viresh.kumar@linaro.org>
 M:     Sudeep Holla <sudeep.holla@arm.com>
-L:     cpufreq@vger.kernel.org
 L:     linux-pm@vger.kernel.org
 W:     http://www.arm.com/products/processors/technologies/biglittleprocessing.php
 S:     Maintained
@@ -6947,7 +6945,6 @@ F:        drivers/power/
 
 PNP SUPPORT
 M:     Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-M:     Bjorn Helgaas <bhelgaas@google.com>
 S:     Maintained
 F:     drivers/pnp/
 
@@ -7671,7 +7668,6 @@ L:        linux-media@vger.kernel.org
 Q:     https://patchwork.linuxtv.org/project/linux-media/list/
 S:     Supported
 F:     drivers/media/platform/exynos4-is/
-F:     include/media/s5p_fimc.h
 
 SAMSUNG S3C24XX/S3C64XX SOC SERIES CAMIF DRIVER
 M:     Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
@@ -7974,6 +7970,26 @@ M:       Robin Holt <robinmholt@gmail.com>
 S:     Maintained
 F:     drivers/misc/sgi-xp/
 
+SI2157 MEDIA DRIVER
+M:     Antti Palosaari <crope@iki.fi>
+L:     linux-media@vger.kernel.org
+W:     http://linuxtv.org/
+W:     http://palosaari.fi/linux/
+Q:     http://patchwork.linuxtv.org/project/linux-media/list/
+T:     git git://linuxtv.org/anttip/media_tree.git
+S:     Maintained
+F:     drivers/media/tuners/si2157*
+
+SI2168 MEDIA DRIVER
+M:     Antti Palosaari <crope@iki.fi>
+L:     linux-media@vger.kernel.org
+W:     http://linuxtv.org/
+W:     http://palosaari.fi/linux/
+Q:     http://patchwork.linuxtv.org/project/linux-media/list/
+T:     git git://linuxtv.org/anttip/media_tree.git
+S:     Maintained
+F:     drivers/media/dvb-frontends/si2168*
+
 SI470X FM RADIO RECEIVER I2C DRIVER
 M:     Hans Verkuil <hverkuil@xs4all.nl>
 L:     linux-media@vger.kernel.org
@@ -8273,6 +8289,7 @@ L:        alsa-devel@alsa-project.org (moderated for non-subscribers)
 W:     http://www.alsa-project.org/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
 T:     git git://git.alsa-project.org/alsa-kernel.git
+Q:     http://patchwork.kernel.org/project/alsa-devel/list/
 S:     Maintained
 F:     Documentation/sound/
 F:     include/sound/
index 764f1e3..09db952 100644 (file)
@@ -12,6 +12,5 @@
 #include <asm-generic/sections.h>
 
 extern char __arc_dccm_base[];
-extern char __dtb_start[];
 
 #endif
index b6dc4e2..0b3ef40 100644 (file)
@@ -42,7 +42,7 @@ const struct machine_desc * __init setup_machine_fdt(void *dt)
        const struct machine_desc *mdesc;
        unsigned long dt_root;
        void *clk;
-       unsigned long len;
+       int len;
 
        if (!early_init_dt_scan(dt))
                return NULL;
index 09af149..193ceaf 100644 (file)
@@ -36,7 +36,7 @@
 #define KVM_COALESCED_MMIO_PAGE_OFFSET 1
 #define KVM_HAVE_ONE_REG
 
-#define KVM_VCPU_MAX_FEATURES 1
+#define KVM_VCPU_MAX_FEATURES 2
 
 #include <kvm/arm_vgic.h>
 
index 9a83d98..6bda945 100644 (file)
 #ifndef __ARM_KVM_PSCI_H__
 #define __ARM_KVM_PSCI_H__
 
-bool kvm_psci_call(struct kvm_vcpu *vcpu);
+#define KVM_ARM_PSCI_0_1       1
+#define KVM_ARM_PSCI_0_2       2
+
+int kvm_psci_version(struct kvm_vcpu *vcpu);
+int kvm_psci_call(struct kvm_vcpu *vcpu);
 
 #endif /* __ARM_KVM_PSCI_H__ */
index b681575..cd94ef2 100644 (file)
@@ -14,7 +14,6 @@
 #ifdef CONFIG_OF
 
 extern const struct machine_desc *setup_machine_fdt(unsigned int dt_phys);
-extern void arm_dt_memblock_reserve(void);
 extern void __init arm_dt_init_cpu_maps(void);
 
 #else /* CONFIG_OF */
@@ -24,7 +23,6 @@ static inline const struct machine_desc *setup_machine_fdt(unsigned int dt_phys)
        return NULL;
 }
 
-static inline void arm_dt_memblock_reserve(void) { }
 static inline void arm_dt_init_cpu_maps(void) { }
 
 #endif /* CONFIG_OF */
index c4ae171..c25ef3e 100644 (file)
@@ -29,16 +29,19 @@ struct psci_operations {
        int (*cpu_off)(struct psci_power_state state);
        int (*cpu_on)(unsigned long cpuid, unsigned long entry_point);
        int (*migrate)(unsigned long cpuid);
+       int (*affinity_info)(unsigned long target_affinity,
+                       unsigned long lowest_affinity_level);
+       int (*migrate_info_type)(void);
 };
 
 extern struct psci_operations psci_ops;
 extern struct smp_operations psci_smp_ops;
 
 #ifdef CONFIG_ARM_PSCI
-void psci_init(void);
+int psci_init(void);
 bool psci_smp_available(void);
 #else
-static inline void psci_init(void) { }
+static inline int psci_init(void) { return 0; }
 static inline bool psci_smp_available(void) { return false; }
 #endif
 
index ef0c878..e6ebdd3 100644 (file)
@@ -20,6 +20,7 @@
 #define __ARM_KVM_H__
 
 #include <linux/types.h>
+#include <linux/psci.h>
 #include <asm/ptrace.h>
 
 #define __KVM_HAVE_GUEST_DEBUG
@@ -83,6 +84,7 @@ struct kvm_regs {
 #define KVM_VGIC_V2_CPU_SIZE           0x2000
 
 #define KVM_ARM_VCPU_POWER_OFF         0 /* CPU is started in OFF state */
+#define KVM_ARM_VCPU_PSCI_0_2          1 /* CPU uses PSCI v0.2 */
 
 struct kvm_vcpu_init {
        __u32 target;
@@ -201,9 +203,9 @@ struct kvm_arch_memory_slot {
 #define KVM_PSCI_FN_CPU_ON             KVM_PSCI_FN(2)
 #define KVM_PSCI_FN_MIGRATE            KVM_PSCI_FN(3)
 
-#define KVM_PSCI_RET_SUCCESS           0
-#define KVM_PSCI_RET_NI                        ((unsigned long)-1)
-#define KVM_PSCI_RET_INVAL             ((unsigned long)-2)
-#define KVM_PSCI_RET_DENIED            ((unsigned long)-3)
+#define KVM_PSCI_RET_SUCCESS           PSCI_RET_SUCCESS
+#define KVM_PSCI_RET_NI                        PSCI_RET_NOT_SUPPORTED
+#define KVM_PSCI_RET_INVAL             PSCI_RET_INVALID_PARAMS
+#define KVM_PSCI_RET_DENIED            PSCI_RET_DENIED
 
 #endif /* __ARM_KVM_H__ */
index c7419a5..ea9ce92 100644 (file)
@@ -32,51 +32,22 @@ void __init early_init_dt_add_memory_arch(u64 base, u64 size)
        arm_add_memory(base, size);
 }
 
-void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
-{
-       return memblock_virt_alloc(size, align);
-}
-
-void __init arm_dt_memblock_reserve(void)
-{
-       u64 *reserve_map, base, size;
-
-       if (!initial_boot_params)
-               return;
-
-       /* Reserve the dtb region */
-       memblock_reserve(virt_to_phys(initial_boot_params),
-                        be32_to_cpu(initial_boot_params->totalsize));
+#ifdef CONFIG_SMP
+extern struct of_cpu_method __cpu_method_of_table[];
 
-       /*
-        * Process the reserve map.  This will probably overlap the initrd
-        * and dtb locations which are already reserved, but overlaping
-        * doesn't hurt anything
-        */
-       reserve_map = ((void*)initial_boot_params) +
-                       be32_to_cpu(initial_boot_params->off_mem_rsvmap);
-       while (1) {
-               base = be64_to_cpup(reserve_map++);
-               size = be64_to_cpup(reserve_map++);
-               if (!size)
-                       break;
-               memblock_reserve(base, size);
-       }
-}
+static const struct of_cpu_method __cpu_method_of_table_sentinel
+       __used __section(__cpu_method_of_table_end);
 
-#ifdef CONFIG_SMP
-extern struct of_cpu_method __cpu_method_of_table_begin[];
-extern struct of_cpu_method __cpu_method_of_table_end[];
 
 static int __init set_smp_ops_by_method(struct device_node *node)
 {
        const char *method;
-       struct of_cpu_method *m = __cpu_method_of_table_begin;
+       struct of_cpu_method *m = __cpu_method_of_table;
 
        if (of_property_read_string(node, "enable-method", &method))
                return 0;
 
-       for (; m < __cpu_method_of_table_end; m++)
+       for (; m->method; m++)
                if (!strcmp(m->method, method)) {
                        smp_set_ops(m->ops);
                        return 1;
@@ -252,7 +223,7 @@ const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
 
        if (!mdesc) {
                const char *prop;
-               long size;
+               int size;
                unsigned long dt_root;
 
                early_print("\nError: unrecognized/unsupported "
index 4693188..f73891b 100644 (file)
 
 #include <linux/init.h>
 #include <linux/of.h>
+#include <linux/reboot.h>
+#include <linux/pm.h>
+#include <uapi/linux/psci.h>
 
 #include <asm/compiler.h>
 #include <asm/errno.h>
 #include <asm/opcodes-sec.h>
 #include <asm/opcodes-virt.h>
 #include <asm/psci.h>
+#include <asm/system_misc.h>
 
 struct psci_operations psci_ops;
 
 static int (*invoke_psci_fn)(u32, u32, u32, u32);
+typedef int (*psci_initcall_t)(const struct device_node *);
 
 enum psci_function {
        PSCI_FN_CPU_SUSPEND,
        PSCI_FN_CPU_ON,
        PSCI_FN_CPU_OFF,
        PSCI_FN_MIGRATE,
+       PSCI_FN_AFFINITY_INFO,
+       PSCI_FN_MIGRATE_INFO_TYPE,
        PSCI_FN_MAX,
 };
 
 static u32 psci_function_id[PSCI_FN_MAX];
 
-#define PSCI_RET_SUCCESS               0
-#define PSCI_RET_EOPNOTSUPP            -1
-#define PSCI_RET_EINVAL                        -2
-#define PSCI_RET_EPERM                 -3
-
 static int psci_to_linux_errno(int errno)
 {
        switch (errno) {
        case PSCI_RET_SUCCESS:
                return 0;
-       case PSCI_RET_EOPNOTSUPP:
+       case PSCI_RET_NOT_SUPPORTED:
                return -EOPNOTSUPP;
-       case PSCI_RET_EINVAL:
+       case PSCI_RET_INVALID_PARAMS:
                return -EINVAL;
-       case PSCI_RET_EPERM:
+       case PSCI_RET_DENIED:
                return -EPERM;
        };
 
        return -EINVAL;
 }
 
-#define PSCI_POWER_STATE_ID_MASK       0xffff
-#define PSCI_POWER_STATE_ID_SHIFT      0
-#define PSCI_POWER_STATE_TYPE_MASK     0x1
-#define PSCI_POWER_STATE_TYPE_SHIFT    16
-#define PSCI_POWER_STATE_AFFL_MASK     0x3
-#define PSCI_POWER_STATE_AFFL_SHIFT    24
-
 static u32 psci_power_state_pack(struct psci_power_state state)
 {
-       return  ((state.id & PSCI_POWER_STATE_ID_MASK)
-                       << PSCI_POWER_STATE_ID_SHIFT)   |
-               ((state.type & PSCI_POWER_STATE_TYPE_MASK)
-                       << PSCI_POWER_STATE_TYPE_SHIFT) |
-               ((state.affinity_level & PSCI_POWER_STATE_AFFL_MASK)
-                       << PSCI_POWER_STATE_AFFL_SHIFT);
+       return ((state.id << PSCI_0_2_POWER_STATE_ID_SHIFT)
+                       & PSCI_0_2_POWER_STATE_ID_MASK) |
+               ((state.type << PSCI_0_2_POWER_STATE_TYPE_SHIFT)
+                & PSCI_0_2_POWER_STATE_TYPE_MASK) |
+               ((state.affinity_level << PSCI_0_2_POWER_STATE_AFFL_SHIFT)
+                & PSCI_0_2_POWER_STATE_AFFL_MASK);
 }
 
 /*
@@ -110,6 +105,14 @@ static noinline int __invoke_psci_fn_smc(u32 function_id, u32 arg0, u32 arg1,
        return function_id;
 }
 
+static int psci_get_version(void)
+{
+       int err;
+
+       err = invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
+       return err;
+}
+
 static int psci_cpu_suspend(struct psci_power_state state,
                            unsigned long entry_point)
 {
@@ -153,26 +156,36 @@ static int psci_migrate(unsigned long cpuid)
        return psci_to_linux_errno(err);
 }
 
-static const struct of_device_id psci_of_match[] __initconst = {
-       { .compatible = "arm,psci",     },
-       {},
-};
+static int psci_affinity_info(unsigned long target_affinity,
+               unsigned long lowest_affinity_level)
+{
+       int err;
+       u32 fn;
+
+       fn = psci_function_id[PSCI_FN_AFFINITY_INFO];
+       err = invoke_psci_fn(fn, target_affinity, lowest_affinity_level, 0);
+       return err;
+}
 
-void __init psci_init(void)
+static int psci_migrate_info_type(void)
 {
-       struct device_node *np;
-       const char *method;
-       u32 id;
+       int err;
+       u32 fn;
 
-       np = of_find_matching_node(NULL, psci_of_match);
-       if (!np)
-               return;
+       fn = psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE];
+       err = invoke_psci_fn(fn, 0, 0, 0);
+       return err;
+}
+
+static int get_set_conduit_method(struct device_node *np)
+{
+       const char *method;
 
-       pr_info("probing function IDs from device-tree\n");
+       pr_info("probing for conduit method from DT.\n");
 
        if (of_property_read_string(np, "method", &method)) {
-               pr_warning("missing \"method\" property\n");
-               goto out_put_node;
+               pr_warn("missing \"method\" property\n");
+               return -ENXIO;
        }
 
        if (!strcmp("hvc", method)) {
@@ -180,10 +193,99 @@ void __init psci_init(void)
        } else if (!strcmp("smc", method)) {
                invoke_psci_fn = __invoke_psci_fn_smc;
        } else {
-               pr_warning("invalid \"method\" property: %s\n", method);
+               pr_warn("invalid \"method\" property: %s\n", method);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd)
+{
+       invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
+}
+
+static void psci_sys_poweroff(void)
+{
+       invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
+}
+
+/*
+ * PSCI Function IDs for v0.2+ are well defined so use
+ * standard values.
+ */
+static int psci_0_2_init(struct device_node *np)
+{
+       int err, ver;
+
+       err = get_set_conduit_method(np);
+
+       if (err)
+               goto out_put_node;
+
+       ver = psci_get_version();
+
+       if (ver == PSCI_RET_NOT_SUPPORTED) {
+               /* PSCI v0.2 mandates implementation of PSCI_ID_VERSION. */
+               pr_err("PSCI firmware does not comply with the v0.2 spec.\n");
+               err = -EOPNOTSUPP;
                goto out_put_node;
+       } else {
+               pr_info("PSCIv%d.%d detected in firmware.\n",
+                               PSCI_VERSION_MAJOR(ver),
+                               PSCI_VERSION_MINOR(ver));
+
+               if (PSCI_VERSION_MAJOR(ver) == 0 &&
+                               PSCI_VERSION_MINOR(ver) < 2) {
+                       err = -EINVAL;
+                       pr_err("Conflicting PSCI version detected.\n");
+                       goto out_put_node;
+               }
        }
 
+       pr_info("Using standard PSCI v0.2 function IDs\n");
+       psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN_CPU_SUSPEND;
+       psci_ops.cpu_suspend = psci_cpu_suspend;
+
+       psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
+       psci_ops.cpu_off = psci_cpu_off;
+
+       psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN_CPU_ON;
+       psci_ops.cpu_on = psci_cpu_on;
+
+       psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN_MIGRATE;
+       psci_ops.migrate = psci_migrate;
+
+       psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN_AFFINITY_INFO;
+       psci_ops.affinity_info = psci_affinity_info;
+
+       psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] =
+               PSCI_0_2_FN_MIGRATE_INFO_TYPE;
+       psci_ops.migrate_info_type = psci_migrate_info_type;
+
+       arm_pm_restart = psci_sys_reset;
+
+       pm_power_off = psci_sys_poweroff;
+
+out_put_node:
+       of_node_put(np);
+       return err;
+}
+
+/*
+ * PSCI < v0.2 get PSCI Function IDs via DT.
+ */
+static int psci_0_1_init(struct device_node *np)
+{
+       u32 id;
+       int err;
+
+       err = get_set_conduit_method(np);
+
+       if (err)
+               goto out_put_node;
+
+       pr_info("Using PSCI v0.1 Function IDs from DT\n");
+
        if (!of_property_read_u32(np, "cpu_suspend", &id)) {
                psci_function_id[PSCI_FN_CPU_SUSPEND] = id;
                psci_ops.cpu_suspend = psci_cpu_suspend;
@@ -206,5 +308,25 @@ void __init psci_init(void)
 
 out_put_node:
        of_node_put(np);
-       return;
+       return err;
+}
+
+static const struct of_device_id psci_of_match[] __initconst = {
+       { .compatible = "arm,psci", .data = psci_0_1_init},
+       { .compatible = "arm,psci-0.2", .data = psci_0_2_init},
+       {},
+};
+
+int __init psci_init(void)
+{
+       struct device_node *np;
+       const struct of_device_id *matched_np;
+       psci_initcall_t init_fn;
+
+       np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np);
+       if (!np)
+               return -ENODEV;
+
+       init_fn = (psci_initcall_t)matched_np->data;
+       return init_fn(np);
 }
index 570a48c..28a1db4 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/init.h>
 #include <linux/smp.h>
 #include <linux/of.h>
+#include <linux/delay.h>
+#include <uapi/linux/psci.h>
 
 #include <asm/psci.h>
 #include <asm/smp_plat.h>
@@ -66,6 +68,36 @@ void __ref psci_cpu_die(unsigned int cpu)
        /* We should never return */
        panic("psci: cpu %d failed to shutdown\n", cpu);
 }
+
+int __ref psci_cpu_kill(unsigned int cpu)
+{
+       int err, i;
+
+       if (!psci_ops.affinity_info)
+               return 1;
+       /*
+        * cpu_kill could race with cpu_die and we can
+        * potentially end up declaring this cpu undead
+        * while it is dying. So, try again a few times.
+        */
+
+       for (i = 0; i < 10; i++) {
+               err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
+               if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
+                       pr_info("CPU%d killed.\n", cpu);
+                       return 1;
+               }
+
+               msleep(10);
+               pr_info("Retrying again to check for CPU kill\n");
+       }
+
+       pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n",
+                       cpu, err);
+       /* Make platform_cpu_kill() fail. */
+       return 0;
+}
+
 #endif
 
 bool __init psci_smp_available(void)
@@ -78,5 +110,6 @@ struct smp_operations __initdata psci_smp_ops = {
        .smp_boot_secondary     = psci_boot_secondary,
 #ifdef CONFIG_HOTPLUG_CPU
        .cpu_die                = psci_cpu_die,
+       .cpu_kill               = psci_cpu_kill,
 #endif
 };
index f0e50a0..3c82b37 100644 (file)
@@ -197,6 +197,7 @@ int kvm_dev_ioctl_check_extension(long ext)
        case KVM_CAP_DESTROY_MEMORY_REGION_WORKS:
        case KVM_CAP_ONE_REG:
        case KVM_CAP_ARM_PSCI:
+       case KVM_CAP_ARM_PSCI_0_2:
                r = 1;
                break;
        case KVM_CAP_COALESCED_MMIO:
index 0de91fc..4c979d4 100644 (file)
@@ -38,14 +38,18 @@ static int handle_svc_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
 
 static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
+       int ret;
+
        trace_kvm_hvc(*vcpu_pc(vcpu), *vcpu_reg(vcpu, 0),
                      kvm_vcpu_hvc_get_imm(vcpu));
 
-       if (kvm_psci_call(vcpu))
+       ret = kvm_psci_call(vcpu);
+       if (ret < 0) {
+               kvm_inject_undefined(vcpu);
                return 1;
+       }
 
-       kvm_inject_undefined(vcpu);
-       return 1;
+       return ret;
 }
 
 static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run)
index 448f60e..09cf377 100644 (file)
  * as described in ARM document number ARM DEN 0022A.
  */
 
+#define AFFINITY_MASK(level)   ~((0x1UL << ((level) * MPIDR_LEVEL_BITS)) - 1)
+
+static unsigned long psci_affinity_mask(unsigned long affinity_level)
+{
+       if (affinity_level <= 3)
+               return MPIDR_HWID_BITMASK & AFFINITY_MASK(affinity_level);
+
+       return 0;
+}
+
+static unsigned long kvm_psci_vcpu_suspend(struct kvm_vcpu *vcpu)
+{
+       /*
+        * NOTE: For simplicity, we make VCPU suspend emulation to be
+        * same-as WFI (Wait-for-interrupt) emulation.
+        *
+        * This means for KVM the wakeup events are interrupts and
+        * this is consistent with intended use of StateID as described
+        * in section 5.4.1 of PSCI v0.2 specification (ARM DEN 0022A).
+        *
+        * Further, we also treat power-down request to be same as
+        * stand-by request as-per section 5.4.2 clause 3 of PSCI v0.2
+        * specification (ARM DEN 0022A). This means all suspend states
+        * for KVM will preserve the register state.
+        */
+       kvm_vcpu_block(vcpu);
+
+       return PSCI_RET_SUCCESS;
+}
+
 static void kvm_psci_vcpu_off(struct kvm_vcpu *vcpu)
 {
        vcpu->arch.pause = true;
@@ -38,6 +68,7 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
        struct kvm_vcpu *vcpu = NULL, *tmp;
        wait_queue_head_t *wq;
        unsigned long cpu_id;
+       unsigned long context_id;
        unsigned long mpidr;
        phys_addr_t target_pc;
        int i;
@@ -58,10 +89,17 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
         * Make sure the caller requested a valid CPU and that the CPU is
         * turned off.
         */
-       if (!vcpu || !vcpu->arch.pause)
-               return KVM_PSCI_RET_INVAL;
+       if (!vcpu)
+               return PSCI_RET_INVALID_PARAMS;
+       if (!vcpu->arch.pause) {
+               if (kvm_psci_version(source_vcpu) != KVM_ARM_PSCI_0_1)
+                       return PSCI_RET_ALREADY_ON;
+               else
+                       return PSCI_RET_INVALID_PARAMS;
+       }
 
        target_pc = *vcpu_reg(source_vcpu, 2);
+       context_id = *vcpu_reg(source_vcpu, 3);
 
        kvm_reset_vcpu(vcpu);
 
@@ -76,26 +114,160 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
                kvm_vcpu_set_be(vcpu);
 
        *vcpu_pc(vcpu) = target_pc;
+       /*
+        * NOTE: We always update r0 (or x0) because for PSCI v0.1
+        * the general puspose registers are undefined upon CPU_ON.
+        */
+       *vcpu_reg(vcpu, 0) = context_id;
        vcpu->arch.pause = false;
        smp_mb();               /* Make sure the above is visible */
 
        wq = kvm_arch_vcpu_wq(vcpu);
        wake_up_interruptible(wq);
 
-       return KVM_PSCI_RET_SUCCESS;
+       return PSCI_RET_SUCCESS;
 }
 
-/**
- * kvm_psci_call - handle PSCI call if r0 value is in range
- * @vcpu: Pointer to the VCPU struct
- *
- * Handle PSCI calls from guests through traps from HVC instructions.
- * The calling convention is similar to SMC calls to the secure world where
- * the function number is placed in r0 and this function returns true if the
- * function number specified in r0 is withing the PSCI range, and false
- * otherwise.
- */
-bool kvm_psci_call(struct kvm_vcpu *vcpu)
+static unsigned long kvm_psci_vcpu_affinity_info(struct kvm_vcpu *vcpu)
+{
+       int i;
+       unsigned long mpidr;
+       unsigned long target_affinity;
+       unsigned long target_affinity_mask;
+       unsigned long lowest_affinity_level;
+       struct kvm *kvm = vcpu->kvm;
+       struct kvm_vcpu *tmp;
+
+       target_affinity = *vcpu_reg(vcpu, 1);
+       lowest_affinity_level = *vcpu_reg(vcpu, 2);
+
+       /* Determine target affinity mask */
+       target_affinity_mask = psci_affinity_mask(lowest_affinity_level);
+       if (!target_affinity_mask)
+               return PSCI_RET_INVALID_PARAMS;
+
+       /* Ignore other bits of target affinity */
+       target_affinity &= target_affinity_mask;
+
+       /*
+        * If one or more VCPU matching target affinity are running
+        * then ON else OFF
+        */
+       kvm_for_each_vcpu(i, tmp, kvm) {
+               mpidr = kvm_vcpu_get_mpidr(tmp);
+               if (((mpidr & target_affinity_mask) == target_affinity) &&
+                   !tmp->arch.pause) {
+                       return PSCI_0_2_AFFINITY_LEVEL_ON;
+               }
+       }
+
+       return PSCI_0_2_AFFINITY_LEVEL_OFF;
+}
+
+static void kvm_prepare_system_event(struct kvm_vcpu *vcpu, u32 type)
+{
+       memset(&vcpu->run->system_event, 0, sizeof(vcpu->run->system_event));
+       vcpu->run->system_event.type = type;
+       vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
+}
+
+static void kvm_psci_system_off(struct kvm_vcpu *vcpu)
+{
+       kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_SHUTDOWN);
+}
+
+static void kvm_psci_system_reset(struct kvm_vcpu *vcpu)
+{
+       kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_RESET);
+}
+
+int kvm_psci_version(struct kvm_vcpu *vcpu)
+{
+       if (test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features))
+               return KVM_ARM_PSCI_0_2;
+
+       return KVM_ARM_PSCI_0_1;
+}
+
+static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
+{
+       int ret = 1;
+       unsigned long psci_fn = *vcpu_reg(vcpu, 0) & ~((u32) 0);
+       unsigned long val;
+
+       switch (psci_fn) {
+       case PSCI_0_2_FN_PSCI_VERSION:
+               /*
+                * Bits[31:16] = Major Version = 0
+                * Bits[15:0] = Minor Version = 2
+                */
+               val = 2;
+               break;
+       case PSCI_0_2_FN_CPU_SUSPEND:
+       case PSCI_0_2_FN64_CPU_SUSPEND:
+               val = kvm_psci_vcpu_suspend(vcpu);
+               break;
+       case PSCI_0_2_FN_CPU_OFF:
+               kvm_psci_vcpu_off(vcpu);
+               val = PSCI_RET_SUCCESS;
+               break;
+       case PSCI_0_2_FN_CPU_ON:
+       case PSCI_0_2_FN64_CPU_ON:
+               val = kvm_psci_vcpu_on(vcpu);
+               break;
+       case PSCI_0_2_FN_AFFINITY_INFO:
+       case PSCI_0_2_FN64_AFFINITY_INFO:
+               val = kvm_psci_vcpu_affinity_info(vcpu);
+               break;
+       case PSCI_0_2_FN_MIGRATE:
+       case PSCI_0_2_FN64_MIGRATE:
+               val = PSCI_RET_NOT_SUPPORTED;
+               break;
+       case PSCI_0_2_FN_MIGRATE_INFO_TYPE:
+               /*
+                * Trusted OS is MP hence does not require migration
+                * or
+                * Trusted OS is not present
+                */
+               val = PSCI_0_2_TOS_MP;
+               break;
+       case PSCI_0_2_FN_MIGRATE_INFO_UP_CPU:
+       case PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU:
+               val = PSCI_RET_NOT_SUPPORTED;
+               break;
+       case PSCI_0_2_FN_SYSTEM_OFF:
+               kvm_psci_system_off(vcpu);
+               /*
+                * We should'nt be going back to guest VCPU after
+                * receiving SYSTEM_OFF request.
+                *
+                * If user space accidently/deliberately resumes
+                * guest VCPU after SYSTEM_OFF request then guest
+                * VCPU should see internal failure from PSCI return
+                * value. To achieve this, we preload r0 (or x0) with
+                * PSCI return value INTERNAL_FAILURE.
+                */
+               val = PSCI_RET_INTERNAL_FAILURE;
+               ret = 0;
+               break;
+       case PSCI_0_2_FN_SYSTEM_RESET:
+               kvm_psci_system_reset(vcpu);
+               /*
+                * Same reason as SYSTEM_OFF for preloading r0 (or x0)
+                * with PSCI return value INTERNAL_FAILURE.
+                */
+               val = PSCI_RET_INTERNAL_FAILURE;
+               ret = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       *vcpu_reg(vcpu, 0) = val;
+       return ret;
+}
+
+static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu)
 {
        unsigned long psci_fn = *vcpu_reg(vcpu, 0) & ~((u32) 0);
        unsigned long val;
@@ -103,20 +275,45 @@ bool kvm_psci_call(struct kvm_vcpu *vcpu)
        switch (psci_fn) {
        case KVM_PSCI_FN_CPU_OFF:
                kvm_psci_vcpu_off(vcpu);
-               val = KVM_PSCI_RET_SUCCESS;
+               val = PSCI_RET_SUCCESS;
                break;
        case KVM_PSCI_FN_CPU_ON:
                val = kvm_psci_vcpu_on(vcpu);
                break;
        case KVM_PSCI_FN_CPU_SUSPEND:
        case KVM_PSCI_FN_MIGRATE:
-               val = KVM_PSCI_RET_NI;
+               val = PSCI_RET_NOT_SUPPORTED;
                break;
-
        default:
-               return false;
+               return -EINVAL;
        }
 
        *vcpu_reg(vcpu, 0) = val;
-       return true;
+       return 1;
+}
+
+/**
+ * kvm_psci_call - handle PSCI call if r0 value is in range
+ * @vcpu: Pointer to the VCPU struct
+ *
+ * Handle PSCI calls from guests through traps from HVC instructions.
+ * The calling convention is similar to SMC calls to the secure world
+ * where the function number is placed in r0.
+ *
+ * This function returns: > 0 (success), 0 (success but exit to user
+ * space), and < 0 (errors)
+ *
+ * Errors:
+ * -EINVAL: Unrecognized PSCI function
+ */
+int kvm_psci_call(struct kvm_vcpu *vcpu)
+{
+       switch (kvm_psci_version(vcpu)) {
+       case KVM_ARM_PSCI_0_2:
+               return kvm_psci_0_2_call(vcpu);
+       case KVM_ARM_PSCI_0_1:
+               return kvm_psci_0_1_call(vcpu);
+       default:
+               return -EINVAL;
+       };
 }
index 85399c9..45ce065 100644 (file)
@@ -1092,20 +1092,21 @@ int da850_register_cpufreq(char *async_clk)
 
 static int da850_round_armrate(struct clk *clk, unsigned long rate)
 {
-       int i, ret = 0, diff;
+       int ret = 0, diff;
        unsigned int best = (unsigned int) -1;
        struct cpufreq_frequency_table *table = cpufreq_info.freq_table;
+       struct cpufreq_frequency_table *pos;
 
        rate /= 1000; /* convert to kHz */
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               diff = table[i].frequency - rate;
+       cpufreq_for_each_entry(pos, table) {
+               diff = pos->frequency - rate;
                if (diff < 0)
                        diff = -diff;
 
                if (diff < best) {
                        best = diff;
-                       ret = table[i].frequency;
+                       ret = pos->frequency;
                }
        }
 
index bc43e22..a56ce45 100644 (file)
@@ -202,7 +202,7 @@ static int __init exynos_fdt_map_chipid(unsigned long node, const char *uname,
 {
        struct map_desc iodesc;
        __be32 *reg;
-       unsigned long len;
+       int len;
 
        if (!of_flat_dt_is_compatible(node, "samsung,exynos4210-chipid") &&
                !of_flat_dt_is_compatible(node, "samsung,exynos5440-clock"))
index a4d5e42..71c86a2 100644 (file)
@@ -289,14 +289,12 @@ int __init mx35_clocks_init(void)
        return 0;
 }
 
-static int __init mx35_clocks_init_dt(struct device_node *ccm_node)
+static void __init mx35_clocks_init_dt(struct device_node *ccm_node)
 {
        clk_data.clks = clk;
        clk_data.clk_num = ARRAY_SIZE(clk);
        of_clk_add_provider(ccm_node, of_clk_src_onecell_get, &clk_data);
 
        mx35_clocks_init();
-
-       return 0;
 }
 CLK_OF_DECLARE(imx35, "fsl,imx35-ccm", mx35_clocks_init_dt);
index 16d33d8..bf852d7 100644 (file)
@@ -279,6 +279,8 @@ static enum omapdss_version __init omap_display_get_version(void)
                return OMAPDSS_VER_OMAP4;
        else if (soc_is_omap54xx())
                return OMAPDSS_VER_OMAP5;
+       else if (soc_is_am43xx())
+               return OMAPDSS_VER_AM43xx;
        else
                return OMAPDSS_VER_UNKNOWN;
 }
@@ -555,65 +557,9 @@ int omap_dss_reset(struct omap_hwmod *oh)
        return r;
 }
 
-/* list of 'compatible' nodes to convert to omapdss specific */
-static const char * const dss_compat_conv_list[] __initconst = {
-       "composite-connector",
-       "dvi-connector",
-       "hdmi-connector",
-       "panel-dpi",
-       "panel-dsi-cm",
-       "sony,acx565akm",
-       "svideo-connector",
-       "ti,tfp410",
-       "ti,tpd12s015",
-};
-
-/* prepend compatible string with "omapdss," */
-static __init void omapdss_omapify_node(struct device_node *node,
-       const char *compat)
-{
-       char *new_compat;
-       struct property *prop;
-
-       new_compat = kasprintf(GFP_KERNEL, "omapdss,%s", compat);
-
-       prop = kzalloc(sizeof(*prop), GFP_KERNEL);
-
-       if (!prop) {
-               pr_err("omapdss_omapify_node: kzalloc failed\n");
-               return;
-       }
-
-       prop->name = "compatible";
-       prop->value = new_compat;
-       prop->length = strlen(new_compat) + 1;
-
-       of_update_property(node, prop);
-}
-
-/*
- * As omapdss panel drivers are omapdss specific, but we want to define the
- * DT-data in generic manner, we convert the compatible strings of the panel
- * nodes from "panel-foo" to "omapdss,panel-foo". This way we can have both
- * correct DT data and omapdss specific drivers.
- *
- * When we get generic panel drivers to the kernel, this will be removed.
- */
 void __init omapdss_early_init_of(void)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(dss_compat_conv_list); ++i) {
-               const char *compat = dss_compat_conv_list[i];
-               struct device_node *node = NULL;
-
-               while ((node = of_find_compatible_node(node, NULL, compat))) {
-                       if (!of_device_is_available(node))
-                               continue;
 
-                       omapdss_omapify_node(node, compat);
-               }
-       }
 }
 
 struct device_node * __init omapdss_find_dss_of_node(void)
@@ -632,6 +578,10 @@ struct device_node * __init omapdss_find_dss_of_node(void)
        if (node)
                return node;
 
+       node = of_find_compatible_node(NULL, NULL, "ti,omap5-dss");
+       if (node)
+               return node;
+
        return NULL;
 }
 
index b41a38a..6c719ec 100644 (file)
 #include <plat/clock.h>
 #include <plat/samsung-time.h>
 #include <plat/mfc.h>
-#include <plat/camport.h>
-
-#include <media/v4l2-mediabus.h>
-#include <media/s5p_fimc.h>
-#include <media/noon010pc30.h>
 
 #include "common.h"
 
@@ -285,14 +280,6 @@ static void __init goni_tsp_init(void)
 /* USB OTG */
 static struct s3c_hsotg_plat goni_hsotg_pdata;
 
-static void goni_camera_init(void)
-{
-       s5pv210_fimc_setup_gpio(S5P_CAMPORT_A);
-
-       /* Set max driver strength on CAM_A_CLKOUT pin. */
-       s5p_gpio_set_drvstr(S5PV210_GPE1(3), S5P_GPIO_DRVSTR_LV4);
-}
-
 /* MAX8998 regulators */
 #if defined(CONFIG_REGULATOR_MAX8998) || defined(CONFIG_REGULATOR_MAX8998_MODULE)
 
@@ -825,34 +812,6 @@ static void goni_setup_sdhci(void)
        s3c_sdhci2_set_platdata(&goni_hsmmc2_data);
 };
 
-static struct noon010pc30_platform_data noon010pc30_pldata = {
-       .clk_rate       = 16000000UL,
-       .gpio_nreset    = S5PV210_GPB(2), /* CAM_CIF_NRST */
-       .gpio_nstby     = S5PV210_GPB(0), /* CAM_CIF_NSTBY */
-};
-
-static struct i2c_board_info noon010pc30_board_info = {
-       I2C_BOARD_INFO("NOON010PC30", 0x60 >> 1),
-       .platform_data = &noon010pc30_pldata,
-};
-
-static struct fimc_source_info goni_camera_sensors[] = {
-       {
-               .mux_id         = 0,
-               .flags          = V4L2_MBUS_PCLK_SAMPLE_FALLING |
-                                 V4L2_MBUS_VSYNC_ACTIVE_LOW,
-               .fimc_bus_type  = FIMC_BUS_TYPE_ITU_601,
-               .board_info     = &noon010pc30_board_info,
-               .i2c_bus_num    = 0,
-               .clk_frequency  = 16000000UL,
-       },
-};
-
-static struct s5p_platform_fimc goni_fimc_md_platdata __initdata = {
-       .source_info    = goni_camera_sensors,
-       .num_clients    = ARRAY_SIZE(goni_camera_sensors),
-};
-
 /* Audio device */
 static struct platform_device goni_device_audio = {
        .name = "smdk-audio",
@@ -874,10 +833,6 @@ static struct platform_device *goni_devices[] __initdata = {
        &s5p_device_mixer,
        &s5p_device_sdo,
        &s3c_device_i2c0,
-       &s5p_device_fimc0,
-       &s5p_device_fimc1,
-       &s5p_device_fimc2,
-       &s5p_device_fimc_md,
        &s3c_device_hsmmc0,
        &s3c_device_hsmmc1,
        &s3c_device_hsmmc2,
@@ -946,14 +901,8 @@ static void __init goni_machine_init(void)
        /* FB */
        s3c_fb_set_platdata(&goni_lcd_pdata);
 
-       /* FIMC */
-       s3c_set_platdata(&goni_fimc_md_platdata, sizeof(goni_fimc_md_platdata),
-                        &s5p_device_fimc_md);
-
        s3c_hsotg_set_platdata(&goni_hsotg_pdata);
 
-       goni_camera_init();
-
        /* SPI */
        spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
 
index 2a77ba8..928d596 100644 (file)
@@ -317,7 +317,6 @@ void __init arm_memblock_init(struct meminfo *mi,
 #endif
 
        arm_mm_memblock_reserve();
-       arm_dt_memblock_reserve();
 
        /* reserve any platform specific memblock areas */
        if (mdesc->reserve)
index 98087b6..469b862 100644 (file)
@@ -125,8 +125,8 @@ device_initcall(s5p_mfc_memory_init);
 int __init s5p_fdt_alloc_mfc_mem(unsigned long node, const char *uname,
                                int depth, void *data)
 {
-       __be32 *prop;
-       unsigned long len;
+       const __be32 *prop;
+       int len;
        struct s5p_mfc_dt_meminfo mfc_mem;
 
        if (!data)
index 1524130..d7b4b38 100644 (file)
@@ -39,6 +39,7 @@ struct device_node;
  *             from the cpu to be killed.
  * @cpu_die:   Makes a cpu leave the kernel. Must not fail. Called from the
  *             cpu being killed.
+ * @cpu_kill:  Ensures a cpu has left the kernel. Called from another cpu.
  * @cpu_suspend: Suspends a cpu and saves the required context. May fail owing
  *               to wrong parameters or error conditions. Called from the
  *               CPU being suspended. Must be called with IRQs disabled.
@@ -52,6 +53,7 @@ struct cpu_operations {
 #ifdef CONFIG_HOTPLUG_CPU
        int             (*cpu_disable)(unsigned int cpu);
        void            (*cpu_die)(unsigned int cpu);
+       int             (*cpu_kill)(unsigned int cpu);
 #endif
 #ifdef CONFIG_ARM64_CPU_SUSPEND
        int             (*cpu_suspend)(unsigned long);
index c404fb0..27f54a7 100644 (file)
@@ -41,6 +41,7 @@
 
 #define ARM_CPU_PART_AEM_V8    0xD0F0
 #define ARM_CPU_PART_FOUNDATION        0xD000
+#define ARM_CPU_PART_CORTEX_A53        0xD030
 #define ARM_CPU_PART_CORTEX_A57        0xD070
 
 #define APM_CPU_PART_POTENZA   0x0000
index 0a1d697..92242ce 100644 (file)
@@ -39,7 +39,7 @@
 #include <kvm/arm_vgic.h>
 #include <kvm/arm_arch_timer.h>
 
-#define KVM_VCPU_MAX_FEATURES 2
+#define KVM_VCPU_MAX_FEATURES 3
 
 struct kvm_vcpu;
 int kvm_target_cpu(void);
index e301a48..bc39e55 100644 (file)
 #ifndef __ARM64_KVM_PSCI_H__
 #define __ARM64_KVM_PSCI_H__
 
-bool kvm_psci_call(struct kvm_vcpu *vcpu);
+#define KVM_ARM_PSCI_0_1       1
+#define KVM_ARM_PSCI_0_2       2
+
+int kvm_psci_version(struct kvm_vcpu *vcpu);
+int kvm_psci_call(struct kvm_vcpu *vcpu);
 
 #endif /* __ARM64_KVM_PSCI_H__ */
index 7b1c67a..e2f9674 100644 (file)
@@ -406,7 +406,7 @@ extern pgd_t idmap_pg_dir[PTRS_PER_PGD];
 
 /*
  * Ensure that there are not more swap files than can be encoded in the kernel
- * the PTEs.
+ * PTEs.
  */
 #define MAX_SWAPFILES_CHECK() BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > __SWP_TYPE_BITS)
 
index d15ab8b..e5312ea 100644 (file)
@@ -14,6 +14,6 @@
 #ifndef __ASM_PSCI_H
 #define __ASM_PSCI_H
 
-void psci_init(void);
+int psci_init(void);
 
 #endif /* __ASM_PSCI_H */
index eaf54a3..e633ff8 100644 (file)
@@ -31,6 +31,7 @@
 #define KVM_NR_SPSR    5
 
 #ifndef __ASSEMBLY__
+#include <linux/psci.h>
 #include <asm/types.h>
 #include <asm/ptrace.h>
 
@@ -56,8 +57,9 @@ struct kvm_regs {
 #define KVM_ARM_TARGET_FOUNDATION_V8   1
 #define KVM_ARM_TARGET_CORTEX_A57      2
 #define KVM_ARM_TARGET_XGENE_POTENZA   3
+#define KVM_ARM_TARGET_CORTEX_A53      4
 
-#define KVM_ARM_NUM_TARGETS            4
+#define KVM_ARM_NUM_TARGETS            5
 
 /* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */
 #define KVM_ARM_DEVICE_TYPE_SHIFT      0
@@ -77,6 +79,7 @@ struct kvm_regs {
 
 #define KVM_ARM_VCPU_POWER_OFF         0 /* CPU is started in OFF state */
 #define KVM_ARM_VCPU_EL1_32BIT         1 /* CPU running a 32bit VM */
+#define KVM_ARM_VCPU_PSCI_0_2          2 /* CPU uses PSCI v0.2 */
 
 struct kvm_vcpu_init {
        __u32 target;
@@ -186,10 +189,10 @@ struct kvm_arch_memory_slot {
 #define KVM_PSCI_FN_CPU_ON             KVM_PSCI_FN(2)
 #define KVM_PSCI_FN_MIGRATE            KVM_PSCI_FN(3)
 
-#define KVM_PSCI_RET_SUCCESS           0
-#define KVM_PSCI_RET_NI                        ((unsigned long)-1)
-#define KVM_PSCI_RET_INVAL             ((unsigned long)-2)
-#define KVM_PSCI_RET_DENIED            ((unsigned long)-3)
+#define KVM_PSCI_RET_SUCCESS           PSCI_RET_SUCCESS
+#define KVM_PSCI_RET_NI                        PSCI_RET_NOT_SUPPORTED
+#define KVM_PSCI_RET_INVAL             PSCI_RET_INVALID_PARAMS
+#define KVM_PSCI_RET_DENIED            PSCI_RET_DENIED
 
 #endif
 
index ea4828a..9e9798f 100644 (file)
 #include <linux/init.h>
 #include <linux/of.h>
 #include <linux/smp.h>
+#include <linux/reboot.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
+#include <uapi/linux/psci.h>
 
 #include <asm/compiler.h>
 #include <asm/cpu_ops.h>
 #include <asm/errno.h>
 #include <asm/psci.h>
 #include <asm/smp_plat.h>
+#include <asm/system_misc.h>
 
 #define PSCI_POWER_STATE_TYPE_STANDBY          0
 #define PSCI_POWER_STATE_TYPE_POWER_DOWN       1
@@ -40,58 +45,52 @@ struct psci_operations {
        int (*cpu_off)(struct psci_power_state state);
        int (*cpu_on)(unsigned long cpuid, unsigned long entry_point);
        int (*migrate)(unsigned long cpuid);
+       int (*affinity_info)(unsigned long target_affinity,
+                       unsigned long lowest_affinity_level);
+       int (*migrate_info_type)(void);
 };
 
 static struct psci_operations psci_ops;
 
 static int (*invoke_psci_fn)(u64, u64, u64, u64);
+typedef int (*psci_initcall_t)(const struct device_node *);
 
 enum psci_function {
        PSCI_FN_CPU_SUSPEND,
        PSCI_FN_CPU_ON,
        PSCI_FN_CPU_OFF,
        PSCI_FN_MIGRATE,
+       PSCI_FN_AFFINITY_INFO,
+       PSCI_FN_MIGRATE_INFO_TYPE,
        PSCI_FN_MAX,
 };
 
 static u32 psci_function_id[PSCI_FN_MAX];
 
-#define PSCI_RET_SUCCESS               0
-#define PSCI_RET_EOPNOTSUPP            -1
-#define PSCI_RET_EINVAL                        -2
-#define PSCI_RET_EPERM                 -3
-
 static int psci_to_linux_errno(int errno)
 {
        switch (errno) {
        case PSCI_RET_SUCCESS:
                return 0;
-       case PSCI_RET_EOPNOTSUPP:
+       case PSCI_RET_NOT_SUPPORTED:
                return -EOPNOTSUPP;
-       case PSCI_RET_EINVAL:
+       case PSCI_RET_INVALID_PARAMS:
                return -EINVAL;
-       case PSCI_RET_EPERM:
+       case PSCI_RET_DENIED:
                return -EPERM;
        };
 
        return -EINVAL;
 }
 
-#define PSCI_POWER_STATE_ID_MASK       0xffff
-#define PSCI_POWER_STATE_ID_SHIFT      0
-#define PSCI_POWER_STATE_TYPE_MASK     0x1
-#define PSCI_POWER_STATE_TYPE_SHIFT    16
-#define PSCI_POWER_STATE_AFFL_MASK     0x3
-#define PSCI_POWER_STATE_AFFL_SHIFT    24
-
 static u32 psci_power_state_pack(struct psci_power_state state)
 {
-       return  ((state.id & PSCI_POWER_STATE_ID_MASK)
-                       << PSCI_POWER_STATE_ID_SHIFT)   |
-               ((state.type & PSCI_POWER_STATE_TYPE_MASK)
-                       << PSCI_POWER_STATE_TYPE_SHIFT) |
-               ((state.affinity_level & PSCI_POWER_STATE_AFFL_MASK)
-                       << PSCI_POWER_STATE_AFFL_SHIFT);
+       return ((state.id << PSCI_0_2_POWER_STATE_ID_SHIFT)
+                       & PSCI_0_2_POWER_STATE_ID_MASK) |
+               ((state.type << PSCI_0_2_POWER_STATE_TYPE_SHIFT)
+                & PSCI_0_2_POWER_STATE_TYPE_MASK) |
+               ((state.affinity_level << PSCI_0_2_POWER_STATE_AFFL_SHIFT)
+                & PSCI_0_2_POWER_STATE_AFFL_MASK);
 }
 
 /*
@@ -128,6 +127,14 @@ static noinline int __invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1,
        return function_id;
 }
 
+static int psci_get_version(void)
+{
+       int err;
+
+       err = invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
+       return err;
+}
+
 static int psci_cpu_suspend(struct psci_power_state state,
                            unsigned long entry_point)
 {
@@ -171,26 +178,36 @@ static int psci_migrate(unsigned long cpuid)
        return psci_to_linux_errno(err);
 }
 
-static const struct of_device_id psci_of_match[] __initconst = {
-       { .compatible = "arm,psci",     },
-       {},
-};
+static int psci_affinity_info(unsigned long target_affinity,
+               unsigned long lowest_affinity_level)
+{
+       int err;
+       u32 fn;
+
+       fn = psci_function_id[PSCI_FN_AFFINITY_INFO];
+       err = invoke_psci_fn(fn, target_affinity, lowest_affinity_level, 0);
+       return err;
+}
 
-void __init psci_init(void)
+static int psci_migrate_info_type(void)
 {
-       struct device_node *np;
-       const char *method;
-       u32 id;
+       int err;
+       u32 fn;
 
-       np = of_find_matching_node(NULL, psci_of_match);
-       if (!np)
-               return;
+       fn = psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE];
+       err = invoke_psci_fn(fn, 0, 0, 0);
+       return err;
+}
 
-       pr_info("probing function IDs from device-tree\n");
+static int get_set_conduit_method(struct device_node *np)
+{
+       const char *method;
+
+       pr_info("probing for conduit method from DT.\n");
 
        if (of_property_read_string(np, "method", &method)) {
-               pr_warning("missing \"method\" property\n");
-               goto out_put_node;
+               pr_warn("missing \"method\" property\n");
+               return -ENXIO;
        }
 
        if (!strcmp("hvc", method)) {
@@ -198,10 +215,99 @@ void __init psci_init(void)
        } else if (!strcmp("smc", method)) {
                invoke_psci_fn = __invoke_psci_fn_smc;
        } else {
-               pr_warning("invalid \"method\" property: %s\n", method);
+               pr_warn("invalid \"method\" property: %s\n", method);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd)
+{
+       invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
+}
+
+static void psci_sys_poweroff(void)
+{
+       invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
+}
+
+/*
+ * PSCI Function IDs for v0.2+ are well defined so use
+ * standard values.
+ */
+static int psci_0_2_init(struct device_node *np)
+{
+       int err, ver;
+
+       err = get_set_conduit_method(np);
+
+       if (err)
+               goto out_put_node;
+
+       ver = psci_get_version();
+
+       if (ver == PSCI_RET_NOT_SUPPORTED) {
+               /* PSCI v0.2 mandates implementation of PSCI_ID_VERSION. */
+               pr_err("PSCI firmware does not comply with the v0.2 spec.\n");
+               err = -EOPNOTSUPP;
                goto out_put_node;
+       } else {
+               pr_info("PSCIv%d.%d detected in firmware.\n",
+                               PSCI_VERSION_MAJOR(ver),
+                               PSCI_VERSION_MINOR(ver));
+
+               if (PSCI_VERSION_MAJOR(ver) == 0 &&
+                               PSCI_VERSION_MINOR(ver) < 2) {
+                       err = -EINVAL;
+                       pr_err("Conflicting PSCI version detected.\n");
+                       goto out_put_node;
+               }
        }
 
+       pr_info("Using standard PSCI v0.2 function IDs\n");
+       psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND;
+       psci_ops.cpu_suspend = psci_cpu_suspend;
+
+       psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
+       psci_ops.cpu_off = psci_cpu_off;
+
+       psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN64_CPU_ON;
+       psci_ops.cpu_on = psci_cpu_on;
+
+       psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE;
+       psci_ops.migrate = psci_migrate;
+
+       psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN64_AFFINITY_INFO;
+       psci_ops.affinity_info = psci_affinity_info;
+
+       psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] =
+               PSCI_0_2_FN_MIGRATE_INFO_TYPE;
+       psci_ops.migrate_info_type = psci_migrate_info_type;
+
+       arm_pm_restart = psci_sys_reset;
+
+       pm_power_off = psci_sys_poweroff;
+
+out_put_node:
+       of_node_put(np);
+       return err;
+}
+
+/*
+ * PSCI < v0.2 get PSCI Function IDs via DT.
+ */
+static int psci_0_1_init(struct device_node *np)
+{
+       u32 id;
+       int err;
+
+       err = get_set_conduit_method(np);
+
+       if (err)
+               goto out_put_node;
+
+       pr_info("Using PSCI v0.1 Function IDs from DT\n");
+
        if (!of_property_read_u32(np, "cpu_suspend", &id)) {
                psci_function_id[PSCI_FN_CPU_SUSPEND] = id;
                psci_ops.cpu_suspend = psci_cpu_suspend;
@@ -224,7 +330,28 @@ void __init psci_init(void)
 
 out_put_node:
        of_node_put(np);
-       return;
+       return err;
+}
+
+static const struct of_device_id psci_of_match[] __initconst = {
+       { .compatible = "arm,psci",     .data = psci_0_1_init},
+       { .compatible = "arm,psci-0.2", .data = psci_0_2_init},
+       {},
+};
+
+int __init psci_init(void)
+{
+       struct device_node *np;
+       const struct of_device_id *matched_np;
+       psci_initcall_t init_fn;
+
+       np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np);
+
+       if (!np)
+               return -ENODEV;
+
+       init_fn = (psci_initcall_t)matched_np->data;
+       return init_fn(np);
 }
 
 #ifdef CONFIG_SMP
@@ -277,6 +404,35 @@ static void cpu_psci_cpu_die(unsigned int cpu)
 
        pr_crit("unable to power off CPU%u (%d)\n", cpu, ret);
 }
+
+static int cpu_psci_cpu_kill(unsigned int cpu)
+{
+       int err, i;
+
+       if (!psci_ops.affinity_info)
+               return 1;
+       /*
+        * cpu_kill could race with cpu_die and we can
+        * potentially end up declaring this cpu undead
+        * while it is dying. So, try again a few times.
+        */
+
+       for (i = 0; i < 10; i++) {
+               err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
+               if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
+                       pr_info("CPU%d killed.\n", cpu);
+                       return 1;
+               }
+
+               msleep(10);
+               pr_info("Retrying again to check for CPU kill\n");
+       }
+
+       pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n",
+                       cpu, err);
+       /* Make op_cpu_kill() fail. */
+       return 0;
+}
 #endif
 
 const struct cpu_operations cpu_psci_ops = {
@@ -287,6 +443,7 @@ const struct cpu_operations cpu_psci_ops = {
 #ifdef CONFIG_HOTPLUG_CPU
        .cpu_disable    = cpu_psci_cpu_disable,
        .cpu_die        = cpu_psci_cpu_die,
+       .cpu_kill       = cpu_psci_cpu_kill,
 #endif
 };
 
index f0a141d..c3cb160 100644 (file)
@@ -228,6 +228,19 @@ int __cpu_disable(void)
        return 0;
 }
 
+static int op_cpu_kill(unsigned int cpu)
+{
+       /*
+        * If we have no means of synchronising with the dying CPU, then assume
+        * that it is really dead. We can only wait for an arbitrary length of
+        * time and hope that it's dead, so let's skip the wait and just hope.
+        */
+       if (!cpu_ops[cpu]->cpu_kill)
+               return 1;
+
+       return cpu_ops[cpu]->cpu_kill(cpu);
+}
+
 static DECLARE_COMPLETION(cpu_died);
 
 /*
@@ -241,6 +254,15 @@ void __cpu_die(unsigned int cpu)
                return;
        }
        pr_notice("CPU%u: shutdown\n", cpu);
+
+       /*
+        * Now that the dying CPU is beyond the point of no return w.r.t.
+        * in-kernel synchronisation, try to get the firwmare to help us to
+        * verify that it has really left the kernel before we consider
+        * clobbering anything it might still be using.
+        */
+       if (!op_cpu_kill(cpu))
+               pr_warn("CPU%d may not have shut down cleanly\n", cpu);
 }
 
 /*
index 0874557..60b5c31 100644 (file)
@@ -214,6 +214,8 @@ int __attribute_const__ kvm_target_cpu(void)
                        return KVM_ARM_TARGET_AEM_V8;
                case ARM_CPU_PART_FOUNDATION:
                        return KVM_ARM_TARGET_FOUNDATION_V8;
+               case ARM_CPU_PART_CORTEX_A53:
+                       return KVM_ARM_TARGET_CORTEX_A53;
                case ARM_CPU_PART_CORTEX_A57:
                        return KVM_ARM_TARGET_CORTEX_A57;
                };
index 7bc41ea..182415e 100644 (file)
@@ -30,11 +30,15 @@ typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *);
 
 static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
-       if (kvm_psci_call(vcpu))
+       int ret;
+
+       ret = kvm_psci_call(vcpu);
+       if (ret < 0) {
+               kvm_inject_undefined(vcpu);
                return 1;
+       }
 
-       kvm_inject_undefined(vcpu);
-       return 1;
+       return ret;
 }
 
 static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run)
index 8fe6f76..475fd29 100644 (file)
@@ -88,6 +88,8 @@ static int __init sys_reg_genericv8_init(void)
                                          &genericv8_target_table);
        kvm_register_target_sys_reg_table(KVM_ARM_TARGET_FOUNDATION_V8,
                                          &genericv8_target_table);
+       kvm_register_target_sys_reg_table(KVM_ARM_TARGET_CORTEX_A53,
+                                         &genericv8_target_table);
        kvm_register_target_sys_reg_table(KVM_ARM_TARGET_CORTEX_A57,
                                          &genericv8_target_table);
        kvm_register_target_sys_reg_table(KVM_ARM_TARGET_XGENE_POTENZA,
index 51d5352..091d428 100644 (file)
@@ -126,8 +126,6 @@ static void arm64_memory_present(void)
 
 void __init arm64_memblock_init(void)
 {
-       u64 *reserve_map, base, size;
-
        /* Register the kernel text, kernel data and initrd with memblock */
        memblock_reserve(__pa(_text), _end - _text);
 #ifdef CONFIG_BLK_DEV_INITRD
@@ -142,25 +140,6 @@ void __init arm64_memblock_init(void)
        memblock_reserve(__pa(swapper_pg_dir), SWAPPER_DIR_SIZE);
        memblock_reserve(__pa(idmap_pg_dir), IDMAP_DIR_SIZE);
 
-       /* Reserve the dtb region */
-       memblock_reserve(virt_to_phys(initial_boot_params),
-                        be32_to_cpu(initial_boot_params->totalsize));
-
-       /*
-        * Process the reserve map.  This will probably overlap the initrd
-        * and dtb locations which are already reserved, but overlapping
-        * doesn't hurt anything
-        */
-       reserve_map = ((void*)initial_boot_params) +
-                       be32_to_cpu(initial_boot_params->off_mem_rsvmap);
-       while (1) {
-               base = be64_to_cpup(reserve_map++);
-               size = be64_to_cpup(reserve_map++);
-               if (!size)
-                       break;
-               memblock_reserve(base, size);
-       }
-
        early_init_fdt_scan_reserved_mem();
        dma_contiguous_reserve(0);
 
index 731db4b..7571288 100644 (file)
@@ -265,8 +265,8 @@ int __init c6x_add_memory(phys_addr_t start, unsigned long size)
  */
 notrace void __init machine_init(unsigned long dt_ptr)
 {
-       struct boot_param_header *dtb = __va(dt_ptr);
-       struct boot_param_header *fdt = (struct boot_param_header *)_fdt_start;
+       const void *dtb = __va(dt_ptr);
+       const void *fdt = _fdt_start;
 
        /* interrupts must be masked */
        set_creg(IER, 2);
diff --git a/arch/ia64/include/asm/acenv.h b/arch/ia64/include/asm/acenv.h
new file mode 100644 (file)
index 0000000..3f9eaee
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * IA64 specific ACPICA environments and implementation
+ *
+ * Copyright (C) 2014, Intel Corporation
+ *   Author: Lv Zheng <lv.zheng@intel.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 _ASM_IA64_ACENV_H
+#define _ASM_IA64_ACENV_H
+
+#include <asm/intrinsics.h>
+
+#define COMPILER_DEPENDENT_INT64       long
+#define COMPILER_DEPENDENT_UINT64      unsigned long
+
+/* Asm macros */
+
+#ifdef CONFIG_ACPI
+
+static inline int
+ia64_acpi_acquire_global_lock(unsigned int *lock)
+{
+       unsigned int old, new, val;
+       do {
+               old = *lock;
+               new = (((old & ~0x3) + 2) + ((old >> 1) & 0x1));
+               val = ia64_cmpxchg4_acq(lock, new, old);
+       } while (unlikely (val != old));
+       return (new < 3) ? -1 : 0;
+}
+
+static inline int
+ia64_acpi_release_global_lock(unsigned int *lock)
+{
+       unsigned int old, new, val;
+       do {
+               old = *lock;
+               new = old & ~0x3;
+               val = ia64_cmpxchg4_acq(lock, new, old);
+       } while (unlikely (val != old));
+       return old & 0x1;
+}
+
+#define ACPI_ACQUIRE_GLOBAL_LOCK(facs, Acq)                            \
+       ((Acq) = ia64_acpi_acquire_global_lock(&facs->global_lock))
+
+#define ACPI_RELEASE_GLOBAL_LOCK(facs, Acq)                            \
+       ((Acq) = ia64_acpi_release_global_lock(&facs->global_lock))
+
+#endif
+
+#endif /* _ASM_IA64_ACENV_H */
index d651102..75dc59a 100644 (file)
 #include <linux/numa.h>
 #include <asm/numa.h>
 
-#define COMPILER_DEPENDENT_INT64       long
-#define COMPILER_DEPENDENT_UINT64      unsigned long
-
-/*
- * Calling conventions:
- *
- * ACPI_SYSTEM_XFACE        - Interfaces to host OS (handlers, threads)
- * ACPI_EXTERNAL_XFACE      - External ACPI interfaces
- * ACPI_INTERNAL_XFACE      - Internal ACPI interfaces
- * ACPI_INTERNAL_VAR_XFACE  - Internal variable-parameter list interfaces
- */
-#define ACPI_SYSTEM_XFACE
-#define ACPI_EXTERNAL_XFACE
-#define ACPI_INTERNAL_XFACE
-#define ACPI_INTERNAL_VAR_XFACE
-
-/* Asm macros */
-
-#define ACPI_FLUSH_CPU_CACHE()
-
-static inline int
-ia64_acpi_acquire_global_lock (unsigned int *lock)
-{
-       unsigned int old, new, val;
-       do {
-               old = *lock;
-               new = (((old & ~0x3) + 2) + ((old >> 1) & 0x1));
-               val = ia64_cmpxchg4_acq(lock, new, old);
-       } while (unlikely (val != old));
-       return (new < 3) ? -1 : 0;
-}
-
-static inline int
-ia64_acpi_release_global_lock (unsigned int *lock)
-{
-       unsigned int old, new, val;
-       do {
-               old = *lock;
-               new = old & ~0x3;
-               val = ia64_cmpxchg4_acq(lock, new, old);
-       } while (unlikely (val != old));
-       return old & 0x1;
-}
-
-#define ACPI_ACQUIRE_GLOBAL_LOCK(facs, Acq)                            \
-       ((Acq) = ia64_acpi_acquire_global_lock(&facs->global_lock))
-
-#define ACPI_RELEASE_GLOBAL_LOCK(facs, Acq)                            \
-       ((Acq) = ia64_acpi_release_global_lock(&facs->global_lock))
-
 #ifdef CONFIG_ACPI
+extern int acpi_lapic;
 #define acpi_disabled 0        /* ACPI always enabled on IA64 */
 #define acpi_noirq 0   /* ACPI always enabled on IA64 */
 #define acpi_pci_disabled 0 /* ACPI PCI always enabled on IA64 */
@@ -92,7 +43,6 @@ ia64_acpi_release_global_lock (unsigned int *lock)
 #endif
 #define acpi_processor_cstate_check(x) (x) /* no idle limits on IA64 :) */
 static inline void disable_acpi(void) { }
-static inline void pci_acpi_crs_quirks(void) { }
 
 #ifdef CONFIG_IA64_GENERIC
 const char *acpi_get_sysname (void);
index 0d407b3..615ef81 100644 (file)
@@ -56,6 +56,7 @@
 
 #define PREFIX                 "ACPI: "
 
+int acpi_lapic;
 unsigned int acpi_cpei_override;
 unsigned int acpi_cpei_phys_cpuid;
 
@@ -676,6 +677,8 @@ int __init early_acpi_boot_init(void)
        if (ret < 1)
                printk(KERN_ERR PREFIX
                       "Error parsing MADT - no LAPIC entries\n");
+       else
+               acpi_lapic = 1;
 
 #ifdef CONFIG_SMP
        if (available_cpus == 0) {
index 129c7cd..31cf53d 100644 (file)
 
 extern char _heap_start[];
 
-#ifdef CONFIG_METAG_BUILTIN_DTB
-extern u32 __dtb_start[];
-#endif
-
 #ifdef CONFIG_DA_CONSOLE
 /* Our early channel based console driver */
 extern struct console dash_console;
index abdfb10..68f0999 100644 (file)
 #include <asm/pci-bridge.h>
 
 #ifdef CONFIG_EARLY_PRINTK
-static char *stdout;
+static const char *stdout;
 
 static int __init early_init_dt_scan_chosen_serial(unsigned long node,
                                const char *uname, int depth, void *data)
 {
-       unsigned long l;
-       char *p;
+       int l;
+       const char *p;
 
        pr_debug("%s: depth: %d, uname: %s\n", __func__, depth, uname);
 
@@ -80,7 +80,7 @@ static int __init early_init_dt_scan_chosen_serial(unsigned long node,
                                (strncmp(p, "xlnx,opb-uartlite", 17) == 0) ||
                                (strncmp(p, "xlnx,axi-uartlite", 17) == 0) ||
                                (strncmp(p, "xlnx,mdm", 8) == 0)) {
-                       unsigned int *addrp;
+                       const unsigned int *addrp;
 
                        *(u32 *)data = UARTLITE;
 
@@ -114,34 +114,3 @@ void __init early_init_devtree(void *params)
 
        pr_debug(" <- early_init_devtree()\n");
 }
-
-/*******
- *
- * New implementation of the OF "find" APIs, return a refcounted
- * object, call of_node_put() when done.  The device tree and list
- * are protected by a rw_lock.
- *
- * Note that property management will need some locking as well,
- * this isn't dealt with yet.
- *
- *******/
-
-#if defined(CONFIG_DEBUG_FS) && defined(DEBUG)
-static struct debugfs_blob_wrapper flat_dt_blob;
-
-static int __init export_flat_device_tree(void)
-{
-       struct dentry *d;
-
-       flat_dt_blob.data = initial_boot_params;
-       flat_dt_blob.size = initial_boot_params->totalsize;
-
-       d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR,
-                               of_debugfs_root, &flat_dt_blob);
-       if (!d)
-               return 1;
-
-       return 0;
-}
-device_initcall(export_flat_device_tree);
-#endif
index 5cd695f..5e0014e 100644 (file)
@@ -1756,14 +1756,14 @@ config KVM_GUEST
        help
          Select this option if building a guest kernel for KVM (Trap & Emulate) mode
 
-config KVM_HOST_FREQ
-       int "KVM Host Processor Frequency (MHz)"
+config KVM_GUEST_TIMER_FREQ
+       int "Count/Compare Timer Frequency (MHz)"
        depends on KVM_GUEST
-       default 500
+       default 100
        help
-         Select this option if building a guest kernel for KVM to skip
-         RTC emulation when determining guest CPU Frequency.  Instead, the guest
-         processor frequency is automatically derived from the host frequency.
+         Set this to non-zero if building a guest kernel for KVM to skip RTC
+         emulation when determining guest CPU Frequency. Instead, the guest's
+         timer frequency is specified directly.
 
 choice
        prompt "Kernel page size"
index 331b837..f1bec00 100644 (file)
@@ -1053,36 +1053,26 @@ void prom_free_prom_memory(void)
 int octeon_prune_device_tree(void);
 
 extern const char __dtb_octeon_3xxx_begin;
-extern const char __dtb_octeon_3xxx_end;
 extern const char __dtb_octeon_68xx_begin;
-extern const char __dtb_octeon_68xx_end;
 void __init device_tree_init(void)
 {
-       int dt_size;
-       struct boot_param_header *fdt;
+       const void *fdt;
        bool do_prune;
 
        if (octeon_bootinfo->minor_version >= 3 && octeon_bootinfo->fdt_addr) {
                fdt = phys_to_virt(octeon_bootinfo->fdt_addr);
                if (fdt_check_header(fdt))
                        panic("Corrupt Device Tree passed to kernel.");
-               dt_size = be32_to_cpu(fdt->totalsize);
                do_prune = false;
        } else if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
-               fdt = (struct boot_param_header *)&__dtb_octeon_68xx_begin;
-               dt_size = &__dtb_octeon_68xx_end - &__dtb_octeon_68xx_begin;
+               fdt = &__dtb_octeon_68xx_begin;
                do_prune = true;
        } else {
-               fdt = (struct boot_param_header *)&__dtb_octeon_3xxx_begin;
-               dt_size = &__dtb_octeon_3xxx_end - &__dtb_octeon_3xxx_begin;
+               fdt = &__dtb_octeon_3xxx_begin;
                do_prune = true;
        }
 
-       /* Copy the default tree from init memory. */
-       initial_boot_params = early_init_dt_alloc_memory_arch(dt_size, 8);
-       if (initial_boot_params == NULL)
-               panic("Could not allocate initial_boot_params");
-       memcpy(initial_boot_params, fdt, dt_size);
+       initial_boot_params = (void *)fdt;
 
        if (do_prune) {
                octeon_prune_device_tree();
@@ -1090,7 +1080,7 @@ void __init device_tree_init(void)
        } else {
                pr_info("Using passed Device Tree.\n");
        }
-       unflatten_device_tree();
+       unflatten_and_copy_device_tree();
 }
 
 static int __initdata disable_octeon_edac_p;
index 060aaa6..b0aa955 100644 (file)
 #include <linux/threads.h>
 #include <linux/spinlock.h>
 
+/* MIPS KVM register ids */
+#define MIPS_CP0_32(_R, _S)                                    \
+       (KVM_REG_MIPS | KVM_REG_SIZE_U32 | 0x10000 | (8 * (_R) + (_S)))
+
+#define MIPS_CP0_64(_R, _S)                                    \
+       (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 0x10000 | (8 * (_R) + (_S)))
+
+#define KVM_REG_MIPS_CP0_INDEX         MIPS_CP0_32(0, 0)
+#define KVM_REG_MIPS_CP0_ENTRYLO0      MIPS_CP0_64(2, 0)
+#define KVM_REG_MIPS_CP0_ENTRYLO1      MIPS_CP0_64(3, 0)
+#define KVM_REG_MIPS_CP0_CONTEXT       MIPS_CP0_64(4, 0)
+#define KVM_REG_MIPS_CP0_USERLOCAL     MIPS_CP0_64(4, 2)
+#define KVM_REG_MIPS_CP0_PAGEMASK      MIPS_CP0_32(5, 0)
+#define KVM_REG_MIPS_CP0_PAGEGRAIN     MIPS_CP0_32(5, 1)
+#define KVM_REG_MIPS_CP0_WIRED         MIPS_CP0_32(6, 0)
+#define KVM_REG_MIPS_CP0_HWRENA                MIPS_CP0_32(7, 0)
+#define KVM_REG_MIPS_CP0_BADVADDR      MIPS_CP0_64(8, 0)
+#define KVM_REG_MIPS_CP0_COUNT         MIPS_CP0_32(9, 0)
+#define KVM_REG_MIPS_CP0_ENTRYHI       MIPS_CP0_64(10, 0)
+#define KVM_REG_MIPS_CP0_COMPARE       MIPS_CP0_32(11, 0)
+#define KVM_REG_MIPS_CP0_STATUS                MIPS_CP0_32(12, 0)
+#define KVM_REG_MIPS_CP0_CAUSE         MIPS_CP0_32(13, 0)
+#define KVM_REG_MIPS_CP0_EPC           MIPS_CP0_64(14, 0)
+#define KVM_REG_MIPS_CP0_EBASE         MIPS_CP0_64(15, 1)
+#define KVM_REG_MIPS_CP0_CONFIG                MIPS_CP0_32(16, 0)
+#define KVM_REG_MIPS_CP0_CONFIG1       MIPS_CP0_32(16, 1)
+#define KVM_REG_MIPS_CP0_CONFIG2       MIPS_CP0_32(16, 2)
+#define KVM_REG_MIPS_CP0_CONFIG3       MIPS_CP0_32(16, 3)
+#define KVM_REG_MIPS_CP0_CONFIG7       MIPS_CP0_32(16, 7)
+#define KVM_REG_MIPS_CP0_XCONTEXT      MIPS_CP0_64(20, 0)
+#define KVM_REG_MIPS_CP0_ERROREPC      MIPS_CP0_64(30, 0)
+
 
 #define KVM_MAX_VCPUS          1
 #define KVM_USER_MEM_SLOTS     8
@@ -372,8 +404,19 @@ struct kvm_vcpu_arch {
 
        u32 io_gpr;             /* GPR used as IO source/target */
 
-       /* Used to calibrate the virutal count register for the guest */
-       int32_t host_cp0_count;
+       struct hrtimer comparecount_timer;
+       /* Count timer control KVM register */
+       uint32_t count_ctl;
+       /* Count bias from the raw time */
+       uint32_t count_bias;
+       /* Frequency of timer in Hz */
+       uint32_t count_hz;
+       /* Dynamic nanosecond bias (multiple of count_period) to avoid overflow */
+       s64 count_dyn_bias;
+       /* Resume time */
+       ktime_t count_resume;
+       /* Period of timer tick in ns */
+       u64 count_period;
 
        /* Bitmask of exceptions that are pending */
        unsigned long pending_exceptions;
@@ -394,8 +437,6 @@ struct kvm_vcpu_arch {
        uint32_t guest_kernel_asid[NR_CPUS];
        struct mm_struct guest_kernel_mm, guest_user_mm;
 
-       struct hrtimer comparecount_timer;
-
        int last_sched_cpu;
 
        /* WAIT executed */
@@ -410,6 +451,7 @@ struct kvm_vcpu_arch {
 #define kvm_read_c0_guest_context(cop0)                (cop0->reg[MIPS_CP0_TLB_CONTEXT][0])
 #define kvm_write_c0_guest_context(cop0, val)  (cop0->reg[MIPS_CP0_TLB_CONTEXT][0] = (val))
 #define kvm_read_c0_guest_userlocal(cop0)      (cop0->reg[MIPS_CP0_TLB_CONTEXT][2])
+#define kvm_write_c0_guest_userlocal(cop0, val)        (cop0->reg[MIPS_CP0_TLB_CONTEXT][2] = (val))
 #define kvm_read_c0_guest_pagemask(cop0)       (cop0->reg[MIPS_CP0_TLB_PG_MASK][0])
 #define kvm_write_c0_guest_pagemask(cop0, val) (cop0->reg[MIPS_CP0_TLB_PG_MASK][0] = (val))
 #define kvm_read_c0_guest_wired(cop0)          (cop0->reg[MIPS_CP0_TLB_WIRED][0])
@@ -449,15 +491,74 @@ struct kvm_vcpu_arch {
 #define kvm_read_c0_guest_errorepc(cop0)       (cop0->reg[MIPS_CP0_ERROR_PC][0])
 #define kvm_write_c0_guest_errorepc(cop0, val) (cop0->reg[MIPS_CP0_ERROR_PC][0] = (val))
 
+/*
+ * Some of the guest registers may be modified asynchronously (e.g. from a
+ * hrtimer callback in hard irq context) and therefore need stronger atomicity
+ * guarantees than other registers.
+ */
+
+static inline void _kvm_atomic_set_c0_guest_reg(unsigned long *reg,
+                                               unsigned long val)
+{
+       unsigned long temp;
+       do {
+               __asm__ __volatile__(
+               "       .set    mips3                           \n"
+               "       " __LL "%0, %1                          \n"
+               "       or      %0, %2                          \n"
+               "       " __SC  "%0, %1                         \n"
+               "       .set    mips0                           \n"
+               : "=&r" (temp), "+m" (*reg)
+               : "r" (val));
+       } while (unlikely(!temp));
+}
+
+static inline void _kvm_atomic_clear_c0_guest_reg(unsigned long *reg,
+                                                 unsigned long val)
+{
+       unsigned long temp;
+       do {
+               __asm__ __volatile__(
+               "       .set    mips3                           \n"
+               "       " __LL "%0, %1                          \n"
+               "       and     %0, %2                          \n"
+               "       " __SC  "%0, %1                         \n"
+               "       .set    mips0                           \n"
+               : "=&r" (temp), "+m" (*reg)
+               : "r" (~val));
+       } while (unlikely(!temp));
+}
+
+static inline void _kvm_atomic_change_c0_guest_reg(unsigned long *reg,
+                                                  unsigned long change,
+                                                  unsigned long val)
+{
+       unsigned long temp;
+       do {
+               __asm__ __volatile__(
+               "       .set    mips3                           \n"
+               "       " __LL "%0, %1                          \n"
+               "       and     %0, %2                          \n"
+               "       or      %0, %3                          \n"
+               "       " __SC  "%0, %1                         \n"
+               "       .set    mips0                           \n"
+               : "=&r" (temp), "+m" (*reg)
+               : "r" (~change), "r" (val & change));
+       } while (unlikely(!temp));
+}
+
 #define kvm_set_c0_guest_status(cop0, val)     (cop0->reg[MIPS_CP0_STATUS][0] |= (val))
 #define kvm_clear_c0_guest_status(cop0, val)   (cop0->reg[MIPS_CP0_STATUS][0] &= ~(val))
-#define kvm_set_c0_guest_cause(cop0, val)      (cop0->reg[MIPS_CP0_CAUSE][0] |= (val))
-#define kvm_clear_c0_guest_cause(cop0, val)    (cop0->reg[MIPS_CP0_CAUSE][0] &= ~(val))
+
+/* Cause can be modified asynchronously from hardirq hrtimer callback */
+#define kvm_set_c0_guest_cause(cop0, val)                              \
+       _kvm_atomic_set_c0_guest_reg(&cop0->reg[MIPS_CP0_CAUSE][0], val)
+#define kvm_clear_c0_guest_cause(cop0, val)                            \
+       _kvm_atomic_clear_c0_guest_reg(&cop0->reg[MIPS_CP0_CAUSE][0], val)
 #define kvm_change_c0_guest_cause(cop0, change, val)                   \
-{                                                                      \
-       kvm_clear_c0_guest_cause(cop0, change);                         \
-       kvm_set_c0_guest_cause(cop0, ((val) & (change)));               \
-}
+       _kvm_atomic_change_c0_guest_reg(&cop0->reg[MIPS_CP0_CAUSE][0],  \
+                                       change, val)
+
 #define kvm_set_c0_guest_ebase(cop0, val)      (cop0->reg[MIPS_CP0_PRID][1] |= (val))
 #define kvm_clear_c0_guest_ebase(cop0, val)    (cop0->reg[MIPS_CP0_PRID][1] &= ~(val))
 #define kvm_change_c0_guest_ebase(cop0, change, val)                   \
@@ -468,29 +569,33 @@ struct kvm_vcpu_arch {
 
 
 struct kvm_mips_callbacks {
-       int (*handle_cop_unusable) (struct kvm_vcpu *vcpu);
-       int (*handle_tlb_mod) (struct kvm_vcpu *vcpu);
-       int (*handle_tlb_ld_miss) (struct kvm_vcpu *vcpu);
-       int (*handle_tlb_st_miss) (struct kvm_vcpu *vcpu);
-       int (*handle_addr_err_st) (struct kvm_vcpu *vcpu);
-       int (*handle_addr_err_ld) (struct kvm_vcpu *vcpu);
-       int (*handle_syscall) (struct kvm_vcpu *vcpu);
-       int (*handle_res_inst) (struct kvm_vcpu *vcpu);
-       int (*handle_break) (struct kvm_vcpu *vcpu);
-       int (*vm_init) (struct kvm *kvm);
-       int (*vcpu_init) (struct kvm_vcpu *vcpu);
-       int (*vcpu_setup) (struct kvm_vcpu *vcpu);
-        gpa_t(*gva_to_gpa) (gva_t gva);
-       void (*queue_timer_int) (struct kvm_vcpu *vcpu);
-       void (*dequeue_timer_int) (struct kvm_vcpu *vcpu);
-       void (*queue_io_int) (struct kvm_vcpu *vcpu,
-                             struct kvm_mips_interrupt *irq);
-       void (*dequeue_io_int) (struct kvm_vcpu *vcpu,
-                               struct kvm_mips_interrupt *irq);
-       int (*irq_deliver) (struct kvm_vcpu *vcpu, unsigned int priority,
-                           uint32_t cause);
-       int (*irq_clear) (struct kvm_vcpu *vcpu, unsigned int priority,
-                         uint32_t cause);
+       int (*handle_cop_unusable)(struct kvm_vcpu *vcpu);
+       int (*handle_tlb_mod)(struct kvm_vcpu *vcpu);
+       int (*handle_tlb_ld_miss)(struct kvm_vcpu *vcpu);
+       int (*handle_tlb_st_miss)(struct kvm_vcpu *vcpu);
+       int (*handle_addr_err_st)(struct kvm_vcpu *vcpu);
+       int (*handle_addr_err_ld)(struct kvm_vcpu *vcpu);
+       int (*handle_syscall)(struct kvm_vcpu *vcpu);
+       int (*handle_res_inst)(struct kvm_vcpu *vcpu);
+       int (*handle_break)(struct kvm_vcpu *vcpu);
+       int (*vm_init)(struct kvm *kvm);
+       int (*vcpu_init)(struct kvm_vcpu *vcpu);
+       int (*vcpu_setup)(struct kvm_vcpu *vcpu);
+       gpa_t (*gva_to_gpa)(gva_t gva);
+       void (*queue_timer_int)(struct kvm_vcpu *vcpu);
+       void (*dequeue_timer_int)(struct kvm_vcpu *vcpu);
+       void (*queue_io_int)(struct kvm_vcpu *vcpu,
+                            struct kvm_mips_interrupt *irq);
+       void (*dequeue_io_int)(struct kvm_vcpu *vcpu,
+                              struct kvm_mips_interrupt *irq);
+       int (*irq_deliver)(struct kvm_vcpu *vcpu, unsigned int priority,
+                          uint32_t cause);
+       int (*irq_clear)(struct kvm_vcpu *vcpu, unsigned int priority,
+                        uint32_t cause);
+       int (*get_one_reg)(struct kvm_vcpu *vcpu,
+                          const struct kvm_one_reg *reg, s64 *v);
+       int (*set_one_reg)(struct kvm_vcpu *vcpu,
+                          const struct kvm_one_reg *reg, s64 v);
 };
 extern struct kvm_mips_callbacks *kvm_mips_callbacks;
 int kvm_mips_emulation_init(struct kvm_mips_callbacks **install_callbacks);
@@ -609,7 +714,16 @@ extern enum emulation_result kvm_mips_emulate_bp_exc(unsigned long cause,
 extern enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu,
                                                         struct kvm_run *run);
 
-enum emulation_result kvm_mips_emulate_count(struct kvm_vcpu *vcpu);
+uint32_t kvm_mips_read_count(struct kvm_vcpu *vcpu);
+void kvm_mips_write_count(struct kvm_vcpu *vcpu, uint32_t count);
+void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare);
+void kvm_mips_init_count(struct kvm_vcpu *vcpu);
+int kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl);
+int kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume);
+int kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz);
+void kvm_mips_count_enable_cause(struct kvm_vcpu *vcpu);
+void kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu);
+enum hrtimer_restart kvm_mips_count_timeout(struct kvm_vcpu *vcpu);
 
 enum emulation_result kvm_mips_check_privilege(unsigned long cause,
                                               uint32_t *opc,
@@ -646,7 +760,6 @@ extern int kvm_mips_trans_mtc0(uint32_t inst, uint32_t *opc,
                               struct kvm_vcpu *vcpu);
 
 /* Misc */
-extern void mips32_SyncICache(unsigned long addr, unsigned long size);
 extern int kvm_mips_dump_stats(struct kvm_vcpu *vcpu);
 extern unsigned long kvm_mips_get_ramsize(struct kvm *kvm);
 
index 509cd58..14ecc53 100644 (file)
@@ -22,8 +22,6 @@ enum jz4740_dma_request_type {
        JZ4740_DMA_TYPE_UART_RECEIVE    = 21,
        JZ4740_DMA_TYPE_SPI_TRANSMIT    = 22,
        JZ4740_DMA_TYPE_SPI_RECEIVE     = 23,
-       JZ4740_DMA_TYPE_AIC_TRANSMIT    = 24,
-       JZ4740_DMA_TYPE_AIC_RECEIVE     = 25,
        JZ4740_DMA_TYPE_MMC_TRANSMIT    = 26,
        JZ4740_DMA_TYPE_MMC_RECEIVE     = 27,
        JZ4740_DMA_TYPE_TCU             = 28,
index 4861681..c904c24 100644 (file)
 
 extern int mips_revision_sconid;
 
-#ifdef CONFIG_OF
-extern struct boot_param_header __dtb_start;
-#endif
-
 #ifdef CONFIG_PCI
 extern void mips_pcibios_init(void);
 #else
index ccd2b75..a9494c0 100644 (file)
@@ -21,13 +21,13 @@ extern void device_tree_init(void);
 
 struct boot_param_header;
 
-extern void __dt_setup_arch(struct boot_param_header *bph);
+extern void __dt_setup_arch(void *bph);
 
 #define dt_setup_arch(sym)                                             \
 ({                                                                     \
-       extern struct boot_param_header __dtb_##sym##_begin;            \
+       extern char __dtb_##sym##_begin[];                              \
                                                                        \
-       __dt_setup_arch(&__dtb_##sym##_begin);                          \
+       __dt_setup_arch(__dtb_##sym##_begin);                           \
 })
 
 #else /* CONFIG_OF */
index f09ff5a..2c04b6d 100644 (file)
@@ -106,6 +106,41 @@ struct kvm_fpu {
 #define KVM_REG_MIPS_LO (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 33)
 #define KVM_REG_MIPS_PC (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 34)
 
+/* KVM specific control registers */
+
+/*
+ * CP0_Count control
+ * DC:    Set 0: Master disable CP0_Count and set COUNT_RESUME to now
+ *        Set 1: Master re-enable CP0_Count with unchanged bias, handling timer
+ *               interrupts since COUNT_RESUME
+ *        This can be used to freeze the timer to get a consistent snapshot of
+ *        the CP0_Count and timer interrupt pending state, while also resuming
+ *        safely without losing time or guest timer interrupts.
+ * Other: Reserved, do not change.
+ */
+#define KVM_REG_MIPS_COUNT_CTL         (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \
+                                        0x20000 | 0)
+#define KVM_REG_MIPS_COUNT_CTL_DC      0x00000001
+
+/*
+ * CP0_Count resume monotonic nanoseconds
+ * The monotonic nanosecond time of the last set of COUNT_CTL.DC (master
+ * disable). Any reads and writes of Count related registers while
+ * COUNT_CTL.DC=1 will appear to occur at this time. When COUNT_CTL.DC is
+ * cleared again (master enable) any timer interrupts since this time will be
+ * emulated.
+ * Modifications to times in the future are rejected.
+ */
+#define KVM_REG_MIPS_COUNT_RESUME      (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \
+                                        0x20000 | 1)
+/*
+ * CP0_Count rate in Hz
+ * Specifies the rate of the CP0_Count timer in Hz. Modifications occur without
+ * discontinuities in CP0_Count.
+ */
+#define KVM_REG_MIPS_COUNT_HZ          (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \
+                                        0x20000 | 2)
+
 /*
  * KVM MIPS specific structures and definitions
  *
index c01900e..088e92a 100644 (file)
@@ -425,6 +425,15 @@ static struct platform_device qi_lb60_audio_device = {
        .id = -1,
 };
 
+static struct gpiod_lookup_table qi_lb60_audio_gpio_table = {
+       .dev_id = "qi-lb60-audio",
+       .table = {
+               GPIO_LOOKUP("Bank B", 29, "snd", 0),
+               GPIO_LOOKUP("Bank D", 4, "amp", 0),
+               { },
+       },
+};
+
 static struct platform_device *jz_platform_devices[] __initdata = {
        &jz4740_udc_device,
        &jz4740_udc_xceiv_device,
@@ -461,6 +470,8 @@ static int __init qi_lb60_init_platform_devices(void)
        jz4740_adc_device.dev.platform_data = &qi_lb60_battery_pdata;
        jz4740_mmc_device.dev.platform_data = &qi_lb60_mmc_pdata;
 
+       gpiod_add_lookup_table(&qi_lb60_audio_gpio_table);
+
        jz4740_serial_device_register();
 
        spi_register_board_info(qi_lb60_spi_board_info,
index 3c3b0df..5d39bb8 100644 (file)
@@ -47,7 +47,7 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
        return __alloc_bootmem(size, align, __pa(MAX_DMA_ADDRESS));
 }
 
-void __init __dt_setup_arch(struct boot_param_header *bph)
+void __init __dt_setup_arch(void *bph)
 {
        if (!early_init_dt_scan(bph))
                return;
index bbace09..033ac34 100644 (file)
@@ -611,35 +611,3 @@ MIPSX(exceptions):
        .word _C_LABEL(MIPSX(GuestException))   # 29
        .word _C_LABEL(MIPSX(GuestException))   # 30
        .word _C_LABEL(MIPSX(GuestException))   # 31
-
-
-/* This routine makes changes to the instruction stream effective to the hardware.
- * It should be called after the instruction stream is written.
- * On return, the new instructions are effective.
- * Inputs:
- * a0 = Start address of new instruction stream
- * a1 = Size, in bytes, of new instruction stream
- */
-
-#define HW_SYNCI_Step       $1
-LEAF(MIPSX(SyncICache))
-       .set    push
-       .set    mips32r2
-       beq     a1, zero, 20f
-        nop
-       REG_ADDU a1, a0, a1
-       rdhwr   v0, HW_SYNCI_Step
-       beq     v0, zero, 20f
-        nop
-10:
-       synci   0(a0)
-       REG_ADDU a0, a0, v0
-       sltu    v1, a0, a1
-       bne     v1, zero, 10b
-        nop
-       sync
-20:
-       jr.hb   ra
-        nop
-       .set    pop
-END(MIPSX(SyncICache))
index da5186f..cd5e4f5 100644 (file)
@@ -61,11 +61,6 @@ static int kvm_mips_reset_vcpu(struct kvm_vcpu *vcpu)
        return 0;
 }
 
-gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
-{
-       return gfn;
-}
-
 /* XXXKYMA: We are simulatoring a processor that has the WII bit set in Config7, so we
  * are "runnable" if interrupts are pending
  */
@@ -130,8 +125,8 @@ static void kvm_mips_init_vm_percpu(void *arg)
 int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 {
        if (atomic_inc_return(&kvm_mips_instance) == 1) {
-               kvm_info("%s: 1st KVM instance, setup host TLB parameters\n",
-                        __func__);
+               kvm_debug("%s: 1st KVM instance, setup host TLB parameters\n",
+                         __func__);
                on_each_cpu(kvm_mips_init_vm_percpu, kvm, 1);
        }
 
@@ -149,9 +144,7 @@ void kvm_mips_free_vcpus(struct kvm *kvm)
                if (kvm->arch.guest_pmap[i] != KVM_INVALID_PAGE)
                        kvm_mips_release_pfn_clean(kvm->arch.guest_pmap[i]);
        }
-
-       if (kvm->arch.guest_pmap)
-               kfree(kvm->arch.guest_pmap);
+       kfree(kvm->arch.guest_pmap);
 
        kvm_for_each_vcpu(i, vcpu, kvm) {
                kvm_arch_vcpu_free(vcpu);
@@ -186,8 +179,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
 
        /* If this is the last instance, restore wired count */
        if (atomic_dec_return(&kvm_mips_instance) == 0) {
-               kvm_info("%s: last KVM instance, restoring TLB parameters\n",
-                        __func__);
+               kvm_debug("%s: last KVM instance, restoring TLB parameters\n",
+                         __func__);
                on_each_cpu(kvm_mips_uninit_tlbs, NULL, 1);
        }
 }
@@ -249,9 +242,8 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
                                goto out;
                        }
 
-                       kvm_info
-                           ("Allocated space for Guest PMAP Table (%ld pages) @ %p\n",
-                            npages, kvm->arch.guest_pmap);
+                       kvm_debug("Allocated space for Guest PMAP Table (%ld pages) @ %p\n",
+                                 npages, kvm->arch.guest_pmap);
 
                        /* Now setup the page table */
                        for (i = 0; i < npages; i++) {
@@ -296,7 +288,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
        if (err)
                goto out_free_cpu;
 
-       kvm_info("kvm @ %p: create cpu %d at %p\n", kvm, id, vcpu);
+       kvm_debug("kvm @ %p: create cpu %d at %p\n", kvm, id, vcpu);
 
        /* Allocate space for host mode exception handlers that handle
         * guest mode exits
@@ -304,7 +296,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
        if (cpu_has_veic || cpu_has_vint) {
                size = 0x200 + VECTORSPACING * 64;
        } else {
-               size = 0x200;
+               size = 0x4000;
        }
 
        /* Save Linux EBASE */
@@ -316,8 +308,8 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
                err = -ENOMEM;
                goto out_free_cpu;
        }
-       kvm_info("Allocated %d bytes for KVM Exception Handlers @ %p\n",
-                ALIGN(size, PAGE_SIZE), gebase);
+       kvm_debug("Allocated %d bytes for KVM Exception Handlers @ %p\n",
+                 ALIGN(size, PAGE_SIZE), gebase);
 
        /* Save new ebase */
        vcpu->arch.guest_ebase = gebase;
@@ -342,15 +334,16 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
 
        /* General handler, relocate to unmapped space for sanity's sake */
        offset = 0x2000;
-       kvm_info("Installing KVM Exception handlers @ %p, %#x bytes\n",
-                gebase + offset,
-                mips32_GuestExceptionEnd - mips32_GuestException);
+       kvm_debug("Installing KVM Exception handlers @ %p, %#x bytes\n",
+                 gebase + offset,
+                 mips32_GuestExceptionEnd - mips32_GuestException);
 
        memcpy(gebase + offset, mips32_GuestException,
               mips32_GuestExceptionEnd - mips32_GuestException);
 
        /* Invalidate the icache for these ranges */
-       mips32_SyncICache((unsigned long) gebase, ALIGN(size, PAGE_SIZE));
+       local_flush_icache_range((unsigned long)gebase,
+                               (unsigned long)gebase + ALIGN(size, PAGE_SIZE));
 
        /* Allocate comm page for guest kernel, a TLB will be reserved for mapping GVA @ 0xFFFF8000 to this page */
        vcpu->arch.kseg0_commpage = kzalloc(PAGE_SIZE << 1, GFP_KERNEL);
@@ -360,14 +353,14 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
                goto out_free_gebase;
        }
 
-       kvm_info("Allocated COMM page @ %p\n", vcpu->arch.kseg0_commpage);
+       kvm_debug("Allocated COMM page @ %p\n", vcpu->arch.kseg0_commpage);
        kvm_mips_commpage_init(vcpu);
 
        /* Init */
        vcpu->arch.last_sched_cpu = -1;
 
        /* Start off the timer */
-       kvm_mips_emulate_count(vcpu);
+       kvm_mips_init_count(vcpu);
 
        return vcpu;
 
@@ -389,12 +382,8 @@ void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
 
        kvm_mips_dump_stats(vcpu);
 
-       if (vcpu->arch.guest_ebase)
-               kfree(vcpu->arch.guest_ebase);
-
-       if (vcpu->arch.kseg0_commpage)
-               kfree(vcpu->arch.kseg0_commpage);
-
+       kfree(vcpu->arch.guest_ebase);
+       kfree(vcpu->arch.kseg0_commpage);
 }
 
 void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
@@ -423,11 +412,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
                vcpu->mmio_needed = 0;
        }
 
+       local_irq_disable();
        /* Check if we have any exceptions/interrupts pending */
        kvm_mips_deliver_interrupts(vcpu,
                                    kvm_read_c0_guest_cause(vcpu->arch.cop0));
 
-       local_irq_disable();
        kvm_guest_enter();
 
        r = __kvm_mips_vcpu_run(run, vcpu);
@@ -490,36 +479,6 @@ kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
        return -ENOIOCTLCMD;
 }
 
-#define MIPS_CP0_32(_R, _S)                                    \
-       (KVM_REG_MIPS | KVM_REG_SIZE_U32 | 0x10000 | (8 * (_R) + (_S)))
-
-#define MIPS_CP0_64(_R, _S)                                    \
-       (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 0x10000 | (8 * (_R) + (_S)))
-
-#define KVM_REG_MIPS_CP0_INDEX         MIPS_CP0_32(0, 0)
-#define KVM_REG_MIPS_CP0_ENTRYLO0      MIPS_CP0_64(2, 0)
-#define KVM_REG_MIPS_CP0_ENTRYLO1      MIPS_CP0_64(3, 0)
-#define KVM_REG_MIPS_CP0_CONTEXT       MIPS_CP0_64(4, 0)
-#define KVM_REG_MIPS_CP0_USERLOCAL     MIPS_CP0_64(4, 2)
-#define KVM_REG_MIPS_CP0_PAGEMASK      MIPS_CP0_32(5, 0)
-#define KVM_REG_MIPS_CP0_PAGEGRAIN     MIPS_CP0_32(5, 1)
-#define KVM_REG_MIPS_CP0_WIRED         MIPS_CP0_32(6, 0)
-#define KVM_REG_MIPS_CP0_HWRENA                MIPS_CP0_32(7, 0)
-#define KVM_REG_MIPS_CP0_BADVADDR      MIPS_CP0_64(8, 0)
-#define KVM_REG_MIPS_CP0_COUNT         MIPS_CP0_32(9, 0)
-#define KVM_REG_MIPS_CP0_ENTRYHI       MIPS_CP0_64(10, 0)
-#define KVM_REG_MIPS_CP0_COMPARE       MIPS_CP0_32(11, 0)
-#define KVM_REG_MIPS_CP0_STATUS                MIPS_CP0_32(12, 0)
-#define KVM_REG_MIPS_CP0_CAUSE         MIPS_CP0_32(13, 0)
-#define KVM_REG_MIPS_CP0_EBASE         MIPS_CP0_64(15, 1)
-#define KVM_REG_MIPS_CP0_CONFIG                MIPS_CP0_32(16, 0)
-#define KVM_REG_MIPS_CP0_CONFIG1       MIPS_CP0_32(16, 1)
-#define KVM_REG_MIPS_CP0_CONFIG2       MIPS_CP0_32(16, 2)
-#define KVM_REG_MIPS_CP0_CONFIG3       MIPS_CP0_32(16, 3)
-#define KVM_REG_MIPS_CP0_CONFIG7       MIPS_CP0_32(16, 7)
-#define KVM_REG_MIPS_CP0_XCONTEXT      MIPS_CP0_64(20, 0)
-#define KVM_REG_MIPS_CP0_ERROREPC      MIPS_CP0_64(30, 0)
-
 static u64 kvm_mips_get_one_regs[] = {
        KVM_REG_MIPS_R0,
        KVM_REG_MIPS_R1,
@@ -560,25 +519,34 @@ static u64 kvm_mips_get_one_regs[] = {
 
        KVM_REG_MIPS_CP0_INDEX,
        KVM_REG_MIPS_CP0_CONTEXT,
+       KVM_REG_MIPS_CP0_USERLOCAL,
        KVM_REG_MIPS_CP0_PAGEMASK,
        KVM_REG_MIPS_CP0_WIRED,
+       KVM_REG_MIPS_CP0_HWRENA,
        KVM_REG_MIPS_CP0_BADVADDR,
+       KVM_REG_MIPS_CP0_COUNT,
        KVM_REG_MIPS_CP0_ENTRYHI,
+       KVM_REG_MIPS_CP0_COMPARE,
        KVM_REG_MIPS_CP0_STATUS,
        KVM_REG_MIPS_CP0_CAUSE,
-       /* EPC set via kvm_regs, et al. */
+       KVM_REG_MIPS_CP0_EPC,
        KVM_REG_MIPS_CP0_CONFIG,
        KVM_REG_MIPS_CP0_CONFIG1,
        KVM_REG_MIPS_CP0_CONFIG2,
        KVM_REG_MIPS_CP0_CONFIG3,
        KVM_REG_MIPS_CP0_CONFIG7,
-       KVM_REG_MIPS_CP0_ERROREPC
+       KVM_REG_MIPS_CP0_ERROREPC,
+
+       KVM_REG_MIPS_COUNT_CTL,
+       KVM_REG_MIPS_COUNT_RESUME,
+       KVM_REG_MIPS_COUNT_HZ,
 };
 
 static int kvm_mips_get_reg(struct kvm_vcpu *vcpu,
                            const struct kvm_one_reg *reg)
 {
        struct mips_coproc *cop0 = vcpu->arch.cop0;
+       int ret;
        s64 v;
 
        switch (reg->id) {
@@ -601,24 +569,36 @@ static int kvm_mips_get_reg(struct kvm_vcpu *vcpu,
        case KVM_REG_MIPS_CP0_CONTEXT:
                v = (long)kvm_read_c0_guest_context(cop0);
                break;
+       case KVM_REG_MIPS_CP0_USERLOCAL:
+               v = (long)kvm_read_c0_guest_userlocal(cop0);
+               break;
        case KVM_REG_MIPS_CP0_PAGEMASK:
                v = (long)kvm_read_c0_guest_pagemask(cop0);
                break;
        case KVM_REG_MIPS_CP0_WIRED:
                v = (long)kvm_read_c0_guest_wired(cop0);
                break;
+       case KVM_REG_MIPS_CP0_HWRENA:
+               v = (long)kvm_read_c0_guest_hwrena(cop0);
+               break;
        case KVM_REG_MIPS_CP0_BADVADDR:
                v = (long)kvm_read_c0_guest_badvaddr(cop0);
                break;
        case KVM_REG_MIPS_CP0_ENTRYHI:
                v = (long)kvm_read_c0_guest_entryhi(cop0);
                break;
+       case KVM_REG_MIPS_CP0_COMPARE:
+               v = (long)kvm_read_c0_guest_compare(cop0);
+               break;
        case KVM_REG_MIPS_CP0_STATUS:
                v = (long)kvm_read_c0_guest_status(cop0);
                break;
        case KVM_REG_MIPS_CP0_CAUSE:
                v = (long)kvm_read_c0_guest_cause(cop0);
                break;
+       case KVM_REG_MIPS_CP0_EPC:
+               v = (long)kvm_read_c0_guest_epc(cop0);
+               break;
        case KVM_REG_MIPS_CP0_ERROREPC:
                v = (long)kvm_read_c0_guest_errorepc(cop0);
                break;
@@ -637,6 +617,15 @@ static int kvm_mips_get_reg(struct kvm_vcpu *vcpu,
        case KVM_REG_MIPS_CP0_CONFIG7:
                v = (long)kvm_read_c0_guest_config7(cop0);
                break;
+       /* registers to be handled specially */
+       case KVM_REG_MIPS_CP0_COUNT:
+       case KVM_REG_MIPS_COUNT_CTL:
+       case KVM_REG_MIPS_COUNT_RESUME:
+       case KVM_REG_MIPS_COUNT_HZ:
+               ret = kvm_mips_callbacks->get_one_reg(vcpu, reg, &v);
+               if (ret)
+                       return ret;
+               break;
        default:
                return -EINVAL;
        }
@@ -697,12 +686,18 @@ static int kvm_mips_set_reg(struct kvm_vcpu *vcpu,
        case KVM_REG_MIPS_CP0_CONTEXT:
                kvm_write_c0_guest_context(cop0, v);
                break;
+       case KVM_REG_MIPS_CP0_USERLOCAL:
+               kvm_write_c0_guest_userlocal(cop0, v);
+               break;
        case KVM_REG_MIPS_CP0_PAGEMASK:
                kvm_write_c0_guest_pagemask(cop0, v);
                break;
        case KVM_REG_MIPS_CP0_WIRED:
                kvm_write_c0_guest_wired(cop0, v);
                break;
+       case KVM_REG_MIPS_CP0_HWRENA:
+               kvm_write_c0_guest_hwrena(cop0, v);
+               break;
        case KVM_REG_MIPS_CP0_BADVADDR:
                kvm_write_c0_guest_badvaddr(cop0, v);
                break;
@@ -712,12 +707,20 @@ static int kvm_mips_set_reg(struct kvm_vcpu *vcpu,
        case KVM_REG_MIPS_CP0_STATUS:
                kvm_write_c0_guest_status(cop0, v);
                break;
-       case KVM_REG_MIPS_CP0_CAUSE:
-               kvm_write_c0_guest_cause(cop0, v);
+       case KVM_REG_MIPS_CP0_EPC:
+               kvm_write_c0_guest_epc(cop0, v);
                break;
        case KVM_REG_MIPS_CP0_ERROREPC:
                kvm_write_c0_guest_errorepc(cop0, v);
                break;
+       /* registers to be handled specially */
+       case KVM_REG_MIPS_CP0_COUNT:
+       case KVM_REG_MIPS_CP0_COMPARE:
+       case KVM_REG_MIPS_CP0_CAUSE:
+       case KVM_REG_MIPS_COUNT_CTL:
+       case KVM_REG_MIPS_COUNT_RESUME:
+       case KVM_REG_MIPS_COUNT_HZ:
+               return kvm_mips_callbacks->set_one_reg(vcpu, reg, v);
        default:
                return -EINVAL;
        }
@@ -920,7 +923,7 @@ int kvm_arch_vcpu_dump_regs(struct kvm_vcpu *vcpu)
                return -1;
 
        printk("VCPU Register Dump:\n");
-       printk("\tpc = 0x%08lx\n", vcpu->arch.pc);;
+       printk("\tpc = 0x%08lx\n", vcpu->arch.pc);
        printk("\texceptions: %08lx\n", vcpu->arch.pending_exceptions);
 
        for (i = 0; i < 32; i += 4) {
@@ -969,7 +972,7 @@ int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
        return 0;
 }
 
-void kvm_mips_comparecount_func(unsigned long data)
+static void kvm_mips_comparecount_func(unsigned long data)
 {
        struct kvm_vcpu *vcpu = (struct kvm_vcpu *)data;
 
@@ -984,15 +987,13 @@ void kvm_mips_comparecount_func(unsigned long data)
 /*
  * low level hrtimer wake routine.
  */
-enum hrtimer_restart kvm_mips_comparecount_wakeup(struct hrtimer *timer)
+static enum hrtimer_restart kvm_mips_comparecount_wakeup(struct hrtimer *timer)
 {
        struct kvm_vcpu *vcpu;
 
        vcpu = container_of(timer, struct kvm_vcpu, arch.comparecount_timer);
        kvm_mips_comparecount_func((unsigned long) vcpu);
-       hrtimer_forward_now(&vcpu->arch.comparecount_timer,
-                           ktime_set(0, MS_TO_NS(10)));
-       return HRTIMER_RESTART;
+       return kvm_mips_count_timeout(vcpu);
 }
 
 int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
index 96528e2..b80e41d 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
 #include <linux/bootmem.h>
+#include <asm/cacheflush.h>
 
 #include "kvm_mips_comm.h"
 
@@ -40,7 +41,7 @@ kvm_mips_trans_cache_index(uint32_t inst, uint32_t *opc,
            CKSEG0ADDR(kvm_mips_translate_guest_kseg0_to_hpa
                       (vcpu, (unsigned long) opc));
        memcpy((void *)kseg0_opc, (void *)&synci_inst, sizeof(uint32_t));
-       mips32_SyncICache(kseg0_opc, 32);
+       local_flush_icache_range(kseg0_opc, kseg0_opc + 32);
 
        return result;
 }
@@ -66,7 +67,7 @@ kvm_mips_trans_cache_va(uint32_t inst, uint32_t *opc,
            CKSEG0ADDR(kvm_mips_translate_guest_kseg0_to_hpa
                       (vcpu, (unsigned long) opc));
        memcpy((void *)kseg0_opc, (void *)&synci_inst, sizeof(uint32_t));
-       mips32_SyncICache(kseg0_opc, 32);
+       local_flush_icache_range(kseg0_opc, kseg0_opc + 32);
 
        return result;
 }
@@ -99,11 +100,12 @@ kvm_mips_trans_mfc0(uint32_t inst, uint32_t *opc, struct kvm_vcpu *vcpu)
                    CKSEG0ADDR(kvm_mips_translate_guest_kseg0_to_hpa
                               (vcpu, (unsigned long) opc));
                memcpy((void *)kseg0_opc, (void *)&mfc0_inst, sizeof(uint32_t));
-               mips32_SyncICache(kseg0_opc, 32);
+               local_flush_icache_range(kseg0_opc, kseg0_opc + 32);
        } else if (KVM_GUEST_KSEGX((unsigned long) opc) == KVM_GUEST_KSEG23) {
                local_irq_save(flags);
                memcpy((void *)opc, (void *)&mfc0_inst, sizeof(uint32_t));
-               mips32_SyncICache((unsigned long) opc, 32);
+               local_flush_icache_range((unsigned long)opc,
+                                        (unsigned long)opc + 32);
                local_irq_restore(flags);
        } else {
                kvm_err("%s: Invalid address: %p\n", __func__, opc);
@@ -134,11 +136,12 @@ kvm_mips_trans_mtc0(uint32_t inst, uint32_t *opc, struct kvm_vcpu *vcpu)
                    CKSEG0ADDR(kvm_mips_translate_guest_kseg0_to_hpa
                               (vcpu, (unsigned long) opc));
                memcpy((void *)kseg0_opc, (void *)&mtc0_inst, sizeof(uint32_t));
-               mips32_SyncICache(kseg0_opc, 32);
+               local_flush_icache_range(kseg0_opc, kseg0_opc + 32);
        } else if (KVM_GUEST_KSEGX((unsigned long) opc) == KVM_GUEST_KSEG23) {
                local_irq_save(flags);
                memcpy((void *)opc, (void *)&mtc0_inst, sizeof(uint32_t));
-               mips32_SyncICache((unsigned long) opc, 32);
+               local_flush_icache_range((unsigned long)opc,
+                                        (unsigned long)opc + 32);
                local_irq_restore(flags);
        } else {
                kvm_err("%s: Invalid address: %p\n", __func__, opc);
index e3fec99..8d48400 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <linux/errno.h>
 #include <linux/err.h>
+#include <linux/ktime.h>
 #include <linux/kvm_host.h>
 #include <linux/module.h>
 #include <linux/vmalloc.h>
@@ -228,25 +229,520 @@ enum emulation_result update_pc(struct kvm_vcpu *vcpu, uint32_t cause)
        return er;
 }
 
-/* Everytime the compare register is written to, we need to decide when to fire
- * the timer that represents timer ticks to the GUEST.
+/**
+ * kvm_mips_count_disabled() - Find whether the CP0_Count timer is disabled.
+ * @vcpu:      Virtual CPU.
  *
+ * Returns:    1 if the CP0_Count timer is disabled by either the guest
+ *             CP0_Cause.DC bit or the count_ctl.DC bit.
+ *             0 otherwise (in which case CP0_Count timer is running).
  */
-enum emulation_result kvm_mips_emulate_count(struct kvm_vcpu *vcpu)
+static inline int kvm_mips_count_disabled(struct kvm_vcpu *vcpu)
 {
        struct mips_coproc *cop0 = vcpu->arch.cop0;
-       enum emulation_result er = EMULATE_DONE;
+       return  (vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC) ||
+               (kvm_read_c0_guest_cause(cop0) & CAUSEF_DC);
+}
+
+/**
+ * kvm_mips_ktime_to_count() - Scale ktime_t to a 32-bit count.
+ *
+ * Caches the dynamic nanosecond bias in vcpu->arch.count_dyn_bias.
+ *
+ * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running).
+ */
+static uint32_t kvm_mips_ktime_to_count(struct kvm_vcpu *vcpu, ktime_t now)
+{
+       s64 now_ns, periods;
+       u64 delta;
+
+       now_ns = ktime_to_ns(now);
+       delta = now_ns + vcpu->arch.count_dyn_bias;
+
+       if (delta >= vcpu->arch.count_period) {
+               /* If delta is out of safe range the bias needs adjusting */
+               periods = div64_s64(now_ns, vcpu->arch.count_period);
+               vcpu->arch.count_dyn_bias = -periods * vcpu->arch.count_period;
+               /* Recalculate delta with new bias */
+               delta = now_ns + vcpu->arch.count_dyn_bias;
+       }
+
+       /*
+        * We've ensured that:
+        *   delta < count_period
+        *
+        * Therefore the intermediate delta*count_hz will never overflow since
+        * at the boundary condition:
+        *   delta = count_period
+        *   delta = NSEC_PER_SEC * 2^32 / count_hz
+        *   delta * count_hz = NSEC_PER_SEC * 2^32
+        */
+       return div_u64(delta * vcpu->arch.count_hz, NSEC_PER_SEC);
+}
+
+/**
+ * kvm_mips_count_time() - Get effective current time.
+ * @vcpu:      Virtual CPU.
+ *
+ * Get effective monotonic ktime. This is usually a straightforward ktime_get(),
+ * except when the master disable bit is set in count_ctl, in which case it is
+ * count_resume, i.e. the time that the count was disabled.
+ *
+ * Returns:    Effective monotonic ktime for CP0_Count.
+ */
+static inline ktime_t kvm_mips_count_time(struct kvm_vcpu *vcpu)
+{
+       if (unlikely(vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC))
+               return vcpu->arch.count_resume;
+
+       return ktime_get();
+}
+
+/**
+ * kvm_mips_read_count_running() - Read the current count value as if running.
+ * @vcpu:      Virtual CPU.
+ * @now:       Kernel time to read CP0_Count at.
+ *
+ * Returns the current guest CP0_Count register at time @now and handles if the
+ * timer interrupt is pending and hasn't been handled yet.
+ *
+ * Returns:    The current value of the guest CP0_Count register.
+ */
+static uint32_t kvm_mips_read_count_running(struct kvm_vcpu *vcpu, ktime_t now)
+{
+       ktime_t expires;
+       int running;
+
+       /* Is the hrtimer pending? */
+       expires = hrtimer_get_expires(&vcpu->arch.comparecount_timer);
+       if (ktime_compare(now, expires) >= 0) {
+               /*
+                * Cancel it while we handle it so there's no chance of
+                * interference with the timeout handler.
+                */
+               running = hrtimer_cancel(&vcpu->arch.comparecount_timer);
+
+               /* Nothing should be waiting on the timeout */
+               kvm_mips_callbacks->queue_timer_int(vcpu);
+
+               /*
+                * Restart the timer if it was running based on the expiry time
+                * we read, so that we don't push it back 2 periods.
+                */
+               if (running) {
+                       expires = ktime_add_ns(expires,
+                                              vcpu->arch.count_period);
+                       hrtimer_start(&vcpu->arch.comparecount_timer, expires,
+                                     HRTIMER_MODE_ABS);
+               }
+       }
+
+       /* Return the biased and scaled guest CP0_Count */
+       return vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now);
+}
+
+/**
+ * kvm_mips_read_count() - Read the current count value.
+ * @vcpu:      Virtual CPU.
+ *
+ * Read the current guest CP0_Count value, taking into account whether the timer
+ * is stopped.
+ *
+ * Returns:    The current guest CP0_Count value.
+ */
+uint32_t kvm_mips_read_count(struct kvm_vcpu *vcpu)
+{
+       struct mips_coproc *cop0 = vcpu->arch.cop0;
+
+       /* If count disabled just read static copy of count */
+       if (kvm_mips_count_disabled(vcpu))
+               return kvm_read_c0_guest_count(cop0);
+
+       return kvm_mips_read_count_running(vcpu, ktime_get());
+}
+
+/**
+ * kvm_mips_freeze_hrtimer() - Safely stop the hrtimer.
+ * @vcpu:      Virtual CPU.
+ * @count:     Output pointer for CP0_Count value at point of freeze.
+ *
+ * Freeze the hrtimer safely and return both the ktime and the CP0_Count value
+ * at the point it was frozen. It is guaranteed that any pending interrupts at
+ * the point it was frozen are handled, and none after that point.
+ *
+ * This is useful where the time/CP0_Count is needed in the calculation of the
+ * new parameters.
+ *
+ * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running).
+ *
+ * Returns:    The ktime at the point of freeze.
+ */
+static ktime_t kvm_mips_freeze_hrtimer(struct kvm_vcpu *vcpu,
+                                      uint32_t *count)
+{
+       ktime_t now;
+
+       /* stop hrtimer before finding time */
+       hrtimer_cancel(&vcpu->arch.comparecount_timer);
+       now = ktime_get();
+
+       /* find count at this point and handle pending hrtimer */
+       *count = kvm_mips_read_count_running(vcpu, now);
+
+       return now;
+}
+
 
-       /* If COUNT is enabled */
-       if (!(kvm_read_c0_guest_cause(cop0) & CAUSEF_DC)) {
-               hrtimer_try_to_cancel(&vcpu->arch.comparecount_timer);
-               hrtimer_start(&vcpu->arch.comparecount_timer,
-                             ktime_set(0, MS_TO_NS(10)), HRTIMER_MODE_REL);
+/**
+ * kvm_mips_resume_hrtimer() - Resume hrtimer, updating expiry.
+ * @vcpu:      Virtual CPU.
+ * @now:       ktime at point of resume.
+ * @count:     CP0_Count at point of resume.
+ *
+ * Resumes the timer and updates the timer expiry based on @now and @count.
+ * This can be used in conjunction with kvm_mips_freeze_timer() when timer
+ * parameters need to be changed.
+ *
+ * It is guaranteed that a timer interrupt immediately after resume will be
+ * handled, but not if CP_Compare is exactly at @count. That case is already
+ * handled by kvm_mips_freeze_timer().
+ *
+ * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running).
+ */
+static void kvm_mips_resume_hrtimer(struct kvm_vcpu *vcpu,
+                                   ktime_t now, uint32_t count)
+{
+       struct mips_coproc *cop0 = vcpu->arch.cop0;
+       uint32_t compare;
+       u64 delta;
+       ktime_t expire;
+
+       /* Calculate timeout (wrap 0 to 2^32) */
+       compare = kvm_read_c0_guest_compare(cop0);
+       delta = (u64)(uint32_t)(compare - count - 1) + 1;
+       delta = div_u64(delta * NSEC_PER_SEC, vcpu->arch.count_hz);
+       expire = ktime_add_ns(now, delta);
+
+       /* Update hrtimer to use new timeout */
+       hrtimer_cancel(&vcpu->arch.comparecount_timer);
+       hrtimer_start(&vcpu->arch.comparecount_timer, expire, HRTIMER_MODE_ABS);
+}
+
+/**
+ * kvm_mips_update_hrtimer() - Update next expiry time of hrtimer.
+ * @vcpu:      Virtual CPU.
+ *
+ * Recalculates and updates the expiry time of the hrtimer. This can be used
+ * after timer parameters have been altered which do not depend on the time that
+ * the change occurs (in those cases kvm_mips_freeze_hrtimer() and
+ * kvm_mips_resume_hrtimer() are used directly).
+ *
+ * It is guaranteed that no timer interrupts will be lost in the process.
+ *
+ * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running).
+ */
+static void kvm_mips_update_hrtimer(struct kvm_vcpu *vcpu)
+{
+       ktime_t now;
+       uint32_t count;
+
+       /*
+        * freeze_hrtimer takes care of a timer interrupts <= count, and
+        * resume_hrtimer the hrtimer takes care of a timer interrupts > count.
+        */
+       now = kvm_mips_freeze_hrtimer(vcpu, &count);
+       kvm_mips_resume_hrtimer(vcpu, now, count);
+}
+
+/**
+ * kvm_mips_write_count() - Modify the count and update timer.
+ * @vcpu:      Virtual CPU.
+ * @count:     Guest CP0_Count value to set.
+ *
+ * Sets the CP0_Count value and updates the timer accordingly.
+ */
+void kvm_mips_write_count(struct kvm_vcpu *vcpu, uint32_t count)
+{
+       struct mips_coproc *cop0 = vcpu->arch.cop0;
+       ktime_t now;
+
+       /* Calculate bias */
+       now = kvm_mips_count_time(vcpu);
+       vcpu->arch.count_bias = count - kvm_mips_ktime_to_count(vcpu, now);
+
+       if (kvm_mips_count_disabled(vcpu))
+               /* The timer's disabled, adjust the static count */
+               kvm_write_c0_guest_count(cop0, count);
+       else
+               /* Update timeout */
+               kvm_mips_resume_hrtimer(vcpu, now, count);
+}
+
+/**
+ * kvm_mips_init_count() - Initialise timer.
+ * @vcpu:      Virtual CPU.
+ *
+ * Initialise the timer to a sensible frequency, namely 100MHz, zero it, and set
+ * it going if it's enabled.
+ */
+void kvm_mips_init_count(struct kvm_vcpu *vcpu)
+{
+       /* 100 MHz */
+       vcpu->arch.count_hz = 100*1000*1000;
+       vcpu->arch.count_period = div_u64((u64)NSEC_PER_SEC << 32,
+                                         vcpu->arch.count_hz);
+       vcpu->arch.count_dyn_bias = 0;
+
+       /* Starting at 0 */
+       kvm_mips_write_count(vcpu, 0);
+}
+
+/**
+ * kvm_mips_set_count_hz() - Update the frequency of the timer.
+ * @vcpu:      Virtual CPU.
+ * @count_hz:  Frequency of CP0_Count timer in Hz.
+ *
+ * Change the frequency of the CP0_Count timer. This is done atomically so that
+ * CP0_Count is continuous and no timer interrupt is lost.
+ *
+ * Returns:    -EINVAL if @count_hz is out of range.
+ *             0 on success.
+ */
+int kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz)
+{
+       struct mips_coproc *cop0 = vcpu->arch.cop0;
+       int dc;
+       ktime_t now;
+       u32 count;
+
+       /* ensure the frequency is in a sensible range... */
+       if (count_hz <= 0 || count_hz > NSEC_PER_SEC)
+               return -EINVAL;
+       /* ... and has actually changed */
+       if (vcpu->arch.count_hz == count_hz)
+               return 0;
+
+       /* Safely freeze timer so we can keep it continuous */
+       dc = kvm_mips_count_disabled(vcpu);
+       if (dc) {
+               now = kvm_mips_count_time(vcpu);
+               count = kvm_read_c0_guest_count(cop0);
        } else {
-               hrtimer_try_to_cancel(&vcpu->arch.comparecount_timer);
+               now = kvm_mips_freeze_hrtimer(vcpu, &count);
        }
 
-       return er;
+       /* Update the frequency */
+       vcpu->arch.count_hz = count_hz;
+       vcpu->arch.count_period = div_u64((u64)NSEC_PER_SEC << 32, count_hz);
+       vcpu->arch.count_dyn_bias = 0;
+
+       /* Calculate adjusted bias so dynamic count is unchanged */
+       vcpu->arch.count_bias = count - kvm_mips_ktime_to_count(vcpu, now);
+
+       /* Update and resume hrtimer */
+       if (!dc)
+               kvm_mips_resume_hrtimer(vcpu, now, count);
+       return 0;
+}
+
+/**
+ * kvm_mips_write_compare() - Modify compare and update timer.
+ * @vcpu:      Virtual CPU.
+ * @compare:   New CP0_Compare value.
+ *
+ * Update CP0_Compare to a new value and update the timeout.
+ */
+void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare)
+{
+       struct mips_coproc *cop0 = vcpu->arch.cop0;
+
+       /* if unchanged, must just be an ack */
+       if (kvm_read_c0_guest_compare(cop0) == compare)
+               return;
+
+       /* Update compare */
+       kvm_write_c0_guest_compare(cop0, compare);
+
+       /* Update timeout if count enabled */
+       if (!kvm_mips_count_disabled(vcpu))
+               kvm_mips_update_hrtimer(vcpu);
+}
+
+/**
+ * kvm_mips_count_disable() - Disable count.
+ * @vcpu:      Virtual CPU.
+ *
+ * Disable the CP0_Count timer. A timer interrupt on or before the final stop
+ * time will be handled but not after.
+ *
+ * Assumes CP0_Count was previously enabled but now Guest.CP0_Cause.DC or
+ * count_ctl.DC has been set (count disabled).
+ *
+ * Returns:    The time that the timer was stopped.
+ */
+static ktime_t kvm_mips_count_disable(struct kvm_vcpu *vcpu)
+{
+       struct mips_coproc *cop0 = vcpu->arch.cop0;
+       uint32_t count;
+       ktime_t now;
+
+       /* Stop hrtimer */
+       hrtimer_cancel(&vcpu->arch.comparecount_timer);
+
+       /* Set the static count from the dynamic count, handling pending TI */
+       now = ktime_get();
+       count = kvm_mips_read_count_running(vcpu, now);
+       kvm_write_c0_guest_count(cop0, count);
+
+       return now;
+}
+
+/**
+ * kvm_mips_count_disable_cause() - Disable count using CP0_Cause.DC.
+ * @vcpu:      Virtual CPU.
+ *
+ * Disable the CP0_Count timer and set CP0_Cause.DC. A timer interrupt on or
+ * before the final stop time will be handled if the timer isn't disabled by
+ * count_ctl.DC, but not after.
+ *
+ * Assumes CP0_Cause.DC is clear (count enabled).
+ */
+void kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu)
+{
+       struct mips_coproc *cop0 = vcpu->arch.cop0;
+
+       kvm_set_c0_guest_cause(cop0, CAUSEF_DC);
+       if (!(vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC))
+               kvm_mips_count_disable(vcpu);
+}
+
+/**
+ * kvm_mips_count_enable_cause() - Enable count using CP0_Cause.DC.
+ * @vcpu:      Virtual CPU.
+ *
+ * Enable the CP0_Count timer and clear CP0_Cause.DC. A timer interrupt after
+ * the start time will be handled if the timer isn't disabled by count_ctl.DC,
+ * potentially before even returning, so the caller should be careful with
+ * ordering of CP0_Cause modifications so as not to lose it.
+ *
+ * Assumes CP0_Cause.DC is set (count disabled).
+ */
+void kvm_mips_count_enable_cause(struct kvm_vcpu *vcpu)
+{
+       struct mips_coproc *cop0 = vcpu->arch.cop0;
+       uint32_t count;
+
+       kvm_clear_c0_guest_cause(cop0, CAUSEF_DC);
+
+       /*
+        * Set the dynamic count to match the static count.
+        * This starts the hrtimer if count_ctl.DC allows it.
+        * Otherwise it conveniently updates the biases.
+        */
+       count = kvm_read_c0_guest_count(cop0);
+       kvm_mips_write_count(vcpu, count);
+}
+
+/**
+ * kvm_mips_set_count_ctl() - Update the count control KVM register.
+ * @vcpu:      Virtual CPU.
+ * @count_ctl: Count control register new value.
+ *
+ * Set the count control KVM register. The timer is updated accordingly.
+ *
+ * Returns:    -EINVAL if reserved bits are set.
+ *             0 on success.
+ */
+int kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl)
+{
+       struct mips_coproc *cop0 = vcpu->arch.cop0;
+       s64 changed = count_ctl ^ vcpu->arch.count_ctl;
+       s64 delta;
+       ktime_t expire, now;
+       uint32_t count, compare;
+
+       /* Only allow defined bits to be changed */
+       if (changed & ~(s64)(KVM_REG_MIPS_COUNT_CTL_DC))
+               return -EINVAL;
+
+       /* Apply new value */
+       vcpu->arch.count_ctl = count_ctl;
+
+       /* Master CP0_Count disable */
+       if (changed & KVM_REG_MIPS_COUNT_CTL_DC) {
+               /* Is CP0_Cause.DC already disabling CP0_Count? */
+               if (kvm_read_c0_guest_cause(cop0) & CAUSEF_DC) {
+                       if (count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)
+                               /* Just record the current time */
+                               vcpu->arch.count_resume = ktime_get();
+               } else if (count_ctl & KVM_REG_MIPS_COUNT_CTL_DC) {
+                       /* disable timer and record current time */
+                       vcpu->arch.count_resume = kvm_mips_count_disable(vcpu);
+               } else {
+                       /*
+                        * Calculate timeout relative to static count at resume
+                        * time (wrap 0 to 2^32).
+                        */
+                       count = kvm_read_c0_guest_count(cop0);
+                       compare = kvm_read_c0_guest_compare(cop0);
+                       delta = (u64)(uint32_t)(compare - count - 1) + 1;
+                       delta = div_u64(delta * NSEC_PER_SEC,
+                                       vcpu->arch.count_hz);
+                       expire = ktime_add_ns(vcpu->arch.count_resume, delta);
+
+                       /* Handle pending interrupt */
+                       now = ktime_get();
+                       if (ktime_compare(now, expire) >= 0)
+                               /* Nothing should be waiting on the timeout */
+                               kvm_mips_callbacks->queue_timer_int(vcpu);
+
+                       /* Resume hrtimer without changing bias */
+                       count = kvm_mips_read_count_running(vcpu, now);
+                       kvm_mips_resume_hrtimer(vcpu, now, count);
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * kvm_mips_set_count_resume() - Update the count resume KVM register.
+ * @vcpu:              Virtual CPU.
+ * @count_resume:      Count resume register new value.
+ *
+ * Set the count resume KVM register.
+ *
+ * Returns:    -EINVAL if out of valid range (0..now).
+ *             0 on success.
+ */
+int kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume)
+{
+       /*
+        * It doesn't make sense for the resume time to be in the future, as it
+        * would be possible for the next interrupt to be more than a full
+        * period in the future.
+        */
+       if (count_resume < 0 || count_resume > ktime_to_ns(ktime_get()))
+               return -EINVAL;
+
+       vcpu->arch.count_resume = ns_to_ktime(count_resume);
+       return 0;
+}
+
+/**
+ * kvm_mips_count_timeout() - Push timer forward on timeout.
+ * @vcpu:      Virtual CPU.
+ *
+ * Handle an hrtimer event by push the hrtimer forward a period.
+ *
+ * Returns:    The hrtimer_restart value to return to the hrtimer subsystem.
+ */
+enum hrtimer_restart kvm_mips_count_timeout(struct kvm_vcpu *vcpu)
+{
+       /* Add the Count period to the current expiry time */
+       hrtimer_add_expires_ns(&vcpu->arch.comparecount_timer,
+                              vcpu->arch.count_period);
+       return HRTIMER_RESTART;
 }
 
 enum emulation_result kvm_mips_emul_eret(struct kvm_vcpu *vcpu)
@@ -471,8 +967,7 @@ kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc, uint32_t cause,
 #endif
                        /* Get reg */
                        if ((rd == MIPS_CP0_COUNT) && (sel == 0)) {
-                               /* XXXKYMA: Run the Guest count register @ 1/4 the rate of the host */
-                               vcpu->arch.gprs[rt] = (read_c0_count() >> 2);
+                               vcpu->arch.gprs[rt] = kvm_mips_read_count(vcpu);
                        } else if ((rd == MIPS_CP0_ERRCTL) && (sel == 0)) {
                                vcpu->arch.gprs[rt] = 0x0;
 #ifdef CONFIG_KVM_MIPS_DYN_TRANS
@@ -539,10 +1034,7 @@ kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc, uint32_t cause,
                        }
                        /* Are we writing to COUNT */
                        else if ((rd == MIPS_CP0_COUNT) && (sel == 0)) {
-                               /* Linux doesn't seem to write into COUNT, we throw an error
-                                * if we notice a write to COUNT
-                                */
-                               /*er = EMULATE_FAIL; */
+                               kvm_mips_write_count(vcpu, vcpu->arch.gprs[rt]);
                                goto done;
                        } else if ((rd == MIPS_CP0_COMPARE) && (sel == 0)) {
                                kvm_debug("[%#x] MTCz, COMPARE %#lx <- %#lx\n",
@@ -552,8 +1044,8 @@ kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc, uint32_t cause,
                                /* If we are writing to COMPARE */
                                /* Clear pending timer interrupt, if any */
                                kvm_mips_callbacks->dequeue_timer_int(vcpu);
-                               kvm_write_c0_guest_compare(cop0,
-                                                          vcpu->arch.gprs[rt]);
+                               kvm_mips_write_compare(vcpu,
+                                                      vcpu->arch.gprs[rt]);
                        } else if ((rd == MIPS_CP0_STATUS) && (sel == 0)) {
                                kvm_write_c0_guest_status(cop0,
                                                          vcpu->arch.gprs[rt]);
@@ -564,6 +1056,20 @@ kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc, uint32_t cause,
 #ifdef CONFIG_KVM_MIPS_DYN_TRANS
                                kvm_mips_trans_mtc0(inst, opc, vcpu);
 #endif
+                       } else if ((rd == MIPS_CP0_CAUSE) && (sel == 0)) {
+                               uint32_t old_cause, new_cause;
+                               old_cause = kvm_read_c0_guest_cause(cop0);
+                               new_cause = vcpu->arch.gprs[rt];
+                               /* Update R/W bits */
+                               kvm_change_c0_guest_cause(cop0, 0x08800300,
+                                                         new_cause);
+                               /* DC bit enabling/disabling timer? */
+                               if ((old_cause ^ new_cause) & CAUSEF_DC) {
+                                       if (new_cause & CAUSEF_DC)
+                                               kvm_mips_count_disable_cause(vcpu);
+                                       else
+                                               kvm_mips_count_enable_cause(vcpu);
+                               }
                        } else {
                                cop0->reg[rd][sel] = vcpu->arch.gprs[rt];
 #ifdef CONFIG_KVM_MIPS_DYN_TRANS
@@ -887,7 +1393,7 @@ int kvm_mips_sync_icache(unsigned long va, struct kvm_vcpu *vcpu)
 
        printk("%s: va: %#lx, unmapped: %#x\n", __func__, va, CKSEG0ADDR(pa));
 
-       mips32_SyncICache(CKSEG0ADDR(pa), 32);
+       local_flush_icache_range(CKSEG0ADDR(pa), 32);
        return 0;
 }
 
@@ -1325,8 +1831,12 @@ kvm_mips_handle_tlbmod(unsigned long cause, uint32_t *opc,
                       struct kvm_run *run, struct kvm_vcpu *vcpu)
 {
        enum emulation_result er = EMULATE_DONE;
-
 #ifdef DEBUG
+       struct mips_coproc *cop0 = vcpu->arch.cop0;
+       unsigned long entryhi = (vcpu->arch.host_cp0_badvaddr & VPN2_MASK) |
+                               (kvm_read_c0_guest_entryhi(cop0) & ASID_MASK);
+       int index;
+
        /*
         * If address not in the guest TLB, then we are in trouble
         */
@@ -1553,8 +2063,7 @@ kvm_mips_handle_ri(unsigned long cause, uint32_t *opc,
                                             current_cpu_data.icache.linesz);
                        break;
                case 2: /* Read count register */
-                       printk("RDHWR: Cont register\n");
-                       arch->gprs[rt] = kvm_read_c0_guest_count(cop0);
+                       arch->gprs[rt] = kvm_mips_read_count(vcpu);
                        break;
                case 3: /* Count register resolution */
                        switch (current_cpu_data.cputype) {
@@ -1810,11 +2319,9 @@ kvm_mips_handle_tlbmiss(unsigned long cause, uint32_t *opc,
                                er = EMULATE_FAIL;
                        }
                } else {
-#ifdef DEBUG
                        kvm_debug
                            ("Injecting hi: %#lx, lo0: %#lx, lo1: %#lx into shadow host TLB\n",
                             tlb->tlb_hi, tlb->tlb_lo0, tlb->tlb_lo1);
-#endif
                        /* OK we have a Guest TLB entry, now inject it into the shadow host TLB */
                        kvm_mips_handle_mapped_seg_tlb_fault(vcpu, tlb, NULL,
                                                             NULL);
index 50ab9c4..8a5a700 100644 (file)
@@ -222,26 +222,19 @@ kvm_mips_host_tlb_write(struct kvm_vcpu *vcpu, unsigned long entryhi,
                return -1;
        }
 
-       if (idx < 0) {
-               idx = read_c0_random() % current_cpu_data.tlbsize;
-               write_c0_index(idx);
-               mtc0_tlbw_hazard();
-       }
        write_c0_entrylo0(entrylo0);
        write_c0_entrylo1(entrylo1);
        mtc0_tlbw_hazard();
 
-       tlb_write_indexed();
+       if (idx < 0)
+               tlb_write_random();
+       else
+               tlb_write_indexed();
        tlbw_use_hazard();
 
-#ifdef DEBUG
-       if (debug) {
-               kvm_debug("@ %#lx idx: %2d [entryhi(R): %#lx] "
-                         "entrylo0(R): 0x%08lx, entrylo1(R): 0x%08lx\n",
-                         vcpu->arch.pc, idx, read_c0_entryhi(),
-                         read_c0_entrylo0(), read_c0_entrylo1());
-       }
-#endif
+       kvm_debug("@ %#lx idx: %2d [entryhi(R): %#lx] entrylo0(R): 0x%08lx, entrylo1(R): 0x%08lx\n",
+                 vcpu->arch.pc, idx, read_c0_entryhi(),
+                 read_c0_entrylo0(), read_c0_entrylo1());
 
        /* Flush D-cache */
        if (flush_dcache_mask) {
@@ -348,11 +341,9 @@ int kvm_mips_handle_commpage_tlb_fault(unsigned long badvaddr,
        mtc0_tlbw_hazard();
        tlbw_use_hazard();
 
-#ifdef DEBUG
        kvm_debug ("@ %#lx idx: %2d [entryhi(R): %#lx] entrylo0 (R): 0x%08lx, entrylo1(R): 0x%08lx\n",
             vcpu->arch.pc, read_c0_index(), read_c0_entryhi(),
             read_c0_entrylo0(), read_c0_entrylo1());
-#endif
 
        /* Restore old ASID */
        write_c0_entryhi(old_entryhi);
@@ -400,10 +391,8 @@ kvm_mips_handle_mapped_seg_tlb_fault(struct kvm_vcpu *vcpu,
        entrylo1 = mips3_paddr_to_tlbpfn(pfn1 << PAGE_SHIFT) | (0x3 << 3) |
                        (tlb->tlb_lo1 & MIPS3_PG_D) | (tlb->tlb_lo1 & MIPS3_PG_V);
 
-#ifdef DEBUG
        kvm_debug("@ %#lx tlb_lo0: 0x%08lx tlb_lo1: 0x%08lx\n", vcpu->arch.pc,
                  tlb->tlb_lo0, tlb->tlb_lo1);
-#endif
 
        return kvm_mips_host_tlb_write(vcpu, entryhi, entrylo0, entrylo1,
                                       tlb->tlb_mask);
@@ -424,10 +413,8 @@ int kvm_mips_guest_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long entryhi)
                }
        }
 
-#ifdef DEBUG
        kvm_debug("%s: entryhi: %#lx, index: %d lo0: %#lx, lo1: %#lx\n",
                  __func__, entryhi, index, tlb[i].tlb_lo0, tlb[i].tlb_lo1);
-#endif
 
        return index;
 }
@@ -461,9 +448,7 @@ int kvm_mips_host_tlb_lookup(struct kvm_vcpu *vcpu, unsigned long vaddr)
 
        local_irq_restore(flags);
 
-#ifdef DEBUG
        kvm_debug("Host TLB lookup, %#lx, idx: %2d\n", vaddr, idx);
-#endif
 
        return idx;
 }
@@ -508,12 +493,9 @@ int kvm_mips_host_tlb_inv(struct kvm_vcpu *vcpu, unsigned long va)
 
        local_irq_restore(flags);
 
-#ifdef DEBUG
-       if (idx > 0) {
+       if (idx > 0)
                kvm_debug("%s: Invalidated entryhi %#lx @ idx %d\n", __func__,
-                         (va & VPN2_MASK) | (vcpu->arch.asid_map[va & ASID_MASK] & ASID_MASK), idx);
-       }
-#endif
+                         (va & VPN2_MASK) | kvm_mips_get_user_asid(vcpu), idx);
 
        return 0;
 }
@@ -658,15 +640,30 @@ void kvm_local_flush_tlb_all(void)
        local_irq_restore(flags);
 }
 
+/**
+ * kvm_mips_migrate_count() - Migrate timer.
+ * @vcpu:      Virtual CPU.
+ *
+ * Migrate CP0_Count hrtimer to the current CPU by cancelling and restarting it
+ * if it was running prior to being cancelled.
+ *
+ * Must be called when the VCPU is migrated to a different CPU to ensure that
+ * timer expiry during guest execution interrupts the guest and causes the
+ * interrupt to be delivered in a timely manner.
+ */
+static void kvm_mips_migrate_count(struct kvm_vcpu *vcpu)
+{
+       if (hrtimer_cancel(&vcpu->arch.comparecount_timer))
+               hrtimer_restart(&vcpu->arch.comparecount_timer);
+}
+
 /* Restore ASID once we are scheduled back after preemption */
 void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 {
        unsigned long flags;
        int newasid = 0;
 
-#ifdef DEBUG
        kvm_debug("%s: vcpu %p, cpu: %d\n", __func__, vcpu, cpu);
-#endif
 
        /* Alocate new kernel and user ASIDs if needed */
 
@@ -682,17 +679,23 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
                    vcpu->arch.guest_user_mm.context.asid[cpu];
                newasid++;
 
-               kvm_info("[%d]: cpu_context: %#lx\n", cpu,
-                        cpu_context(cpu, current->mm));
-               kvm_info("[%d]: Allocated new ASID for Guest Kernel: %#x\n",
-                        cpu, vcpu->arch.guest_kernel_asid[cpu]);
-               kvm_info("[%d]: Allocated new ASID for Guest User: %#x\n", cpu,
-                        vcpu->arch.guest_user_asid[cpu]);
+               kvm_debug("[%d]: cpu_context: %#lx\n", cpu,
+                         cpu_context(cpu, current->mm));
+               kvm_debug("[%d]: Allocated new ASID for Guest Kernel: %#x\n",
+                         cpu, vcpu->arch.guest_kernel_asid[cpu]);
+               kvm_debug("[%d]: Allocated new ASID for Guest User: %#x\n", cpu,
+                         vcpu->arch.guest_user_asid[cpu]);
        }
 
        if (vcpu->arch.last_sched_cpu != cpu) {
-               kvm_info("[%d->%d]KVM VCPU[%d] switch\n",
-                        vcpu->arch.last_sched_cpu, cpu, vcpu->vcpu_id);
+               kvm_debug("[%d->%d]KVM VCPU[%d] switch\n",
+                         vcpu->arch.last_sched_cpu, cpu, vcpu->vcpu_id);
+               /*
+                * Migrate the timer interrupt to the current CPU so that it
+                * always interrupts the guest and synchronously triggers a
+                * guest timer interrupt.
+                */
+               kvm_mips_migrate_count(vcpu);
        }
 
        if (!newasid) {
index 30d7253..693f952 100644 (file)
@@ -32,9 +32,7 @@ static gpa_t kvm_trap_emul_gva_to_gpa_cb(gva_t gva)
                gpa = KVM_INVALID_ADDR;
        }
 
-#ifdef DEBUG
        kvm_debug("%s: gva %#lx, gpa: %#llx\n", __func__, gva, gpa);
-#endif
 
        return gpa;
 }
@@ -85,11 +83,9 @@ static int kvm_trap_emul_handle_tlb_mod(struct kvm_vcpu *vcpu)
 
        if (KVM_GUEST_KSEGX(badvaddr) < KVM_GUEST_KSEG0
            || KVM_GUEST_KSEGX(badvaddr) == KVM_GUEST_KSEG23) {
-#ifdef DEBUG
                kvm_debug
                    ("USER/KSEG23 ADDR TLB MOD fault: cause %#lx, PC: %p, BadVaddr: %#lx\n",
                     cause, opc, badvaddr);
-#endif
                er = kvm_mips_handle_tlbmod(cause, opc, run, vcpu);
 
                if (er == EMULATE_DONE)
@@ -138,11 +134,9 @@ static int kvm_trap_emul_handle_tlb_st_miss(struct kvm_vcpu *vcpu)
                }
        } else if (KVM_GUEST_KSEGX(badvaddr) < KVM_GUEST_KSEG0
                   || KVM_GUEST_KSEGX(badvaddr) == KVM_GUEST_KSEG23) {
-#ifdef DEBUG
                kvm_debug
                    ("USER ADDR TLB LD fault: cause %#lx, PC: %p, BadVaddr: %#lx\n",
                     cause, opc, badvaddr);
-#endif
                er = kvm_mips_handle_tlbmiss(cause, opc, run, vcpu);
                if (er == EMULATE_DONE)
                        ret = RESUME_GUEST;
@@ -188,10 +182,8 @@ static int kvm_trap_emul_handle_tlb_ld_miss(struct kvm_vcpu *vcpu)
                }
        } else if (KVM_GUEST_KSEGX(badvaddr) < KVM_GUEST_KSEG0
                   || KVM_GUEST_KSEGX(badvaddr) == KVM_GUEST_KSEG23) {
-#ifdef DEBUG
                kvm_debug("USER ADDR TLB ST fault: PC: %#lx, BadVaddr: %#lx\n",
                          vcpu->arch.pc, badvaddr);
-#endif
 
                /* User Address (UA) fault, this could happen if
                 * (1) TLB entry not present/valid in both Guest and shadow host TLBs, in this
@@ -236,9 +228,7 @@ static int kvm_trap_emul_handle_addr_err_st(struct kvm_vcpu *vcpu)
 
        if (KVM_GUEST_KERNEL_MODE(vcpu)
            && (KSEGX(badvaddr) == CKSEG0 || KSEGX(badvaddr) == CKSEG1)) {
-#ifdef DEBUG
                kvm_debug("Emulate Store to MMIO space\n");
-#endif
                er = kvm_mips_emulate_inst(cause, opc, run, vcpu);
                if (er == EMULATE_FAIL) {
                        printk("Emulate Store to MMIO space failed\n");
@@ -268,9 +258,7 @@ static int kvm_trap_emul_handle_addr_err_ld(struct kvm_vcpu *vcpu)
        int ret = RESUME_GUEST;
 
        if (KSEGX(badvaddr) == CKSEG0 || KSEGX(badvaddr) == CKSEG1) {
-#ifdef DEBUG
                kvm_debug("Emulate Load from MMIO space @ %#lx\n", badvaddr);
-#endif
                er = kvm_mips_emulate_inst(cause, opc, run, vcpu);
                if (er == EMULATE_FAIL) {
                        printk("Emulate Load from MMIO space failed\n");
@@ -401,6 +389,78 @@ static int kvm_trap_emul_vcpu_setup(struct kvm_vcpu *vcpu)
        return 0;
 }
 
+static int kvm_trap_emul_get_one_reg(struct kvm_vcpu *vcpu,
+                                    const struct kvm_one_reg *reg,
+                                    s64 *v)
+{
+       switch (reg->id) {
+       case KVM_REG_MIPS_CP0_COUNT:
+               *v = kvm_mips_read_count(vcpu);
+               break;
+       case KVM_REG_MIPS_COUNT_CTL:
+               *v = vcpu->arch.count_ctl;
+               break;
+       case KVM_REG_MIPS_COUNT_RESUME:
+               *v = ktime_to_ns(vcpu->arch.count_resume);
+               break;
+       case KVM_REG_MIPS_COUNT_HZ:
+               *v = vcpu->arch.count_hz;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu,
+                                    const struct kvm_one_reg *reg,
+                                    s64 v)
+{
+       struct mips_coproc *cop0 = vcpu->arch.cop0;
+       int ret = 0;
+
+       switch (reg->id) {
+       case KVM_REG_MIPS_CP0_COUNT:
+               kvm_mips_write_count(vcpu, v);
+               break;
+       case KVM_REG_MIPS_CP0_COMPARE:
+               kvm_mips_write_compare(vcpu, v);
+               break;
+       case KVM_REG_MIPS_CP0_CAUSE:
+               /*
+                * If the timer is stopped or started (DC bit) it must look
+                * atomic with changes to the interrupt pending bits (TI, IRQ5).
+                * A timer interrupt should not happen in between.
+                */
+               if ((kvm_read_c0_guest_cause(cop0) ^ v) & CAUSEF_DC) {
+                       if (v & CAUSEF_DC) {
+                               /* disable timer first */
+                               kvm_mips_count_disable_cause(vcpu);
+                               kvm_change_c0_guest_cause(cop0, ~CAUSEF_DC, v);
+                       } else {
+                               /* enable timer last */
+                               kvm_change_c0_guest_cause(cop0, ~CAUSEF_DC, v);
+                               kvm_mips_count_enable_cause(vcpu);
+                       }
+               } else {
+                       kvm_write_c0_guest_cause(cop0, v);
+               }
+               break;
+       case KVM_REG_MIPS_COUNT_CTL:
+               ret = kvm_mips_set_count_ctl(vcpu, v);
+               break;
+       case KVM_REG_MIPS_COUNT_RESUME:
+               ret = kvm_mips_set_count_resume(vcpu, v);
+               break;
+       case KVM_REG_MIPS_COUNT_HZ:
+               ret = kvm_mips_set_count_hz(vcpu, v);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return ret;
+}
+
 static struct kvm_mips_callbacks kvm_trap_emul_callbacks = {
        /* exit handlers */
        .handle_cop_unusable = kvm_trap_emul_handle_cop_unusable,
@@ -423,6 +483,8 @@ static struct kvm_mips_callbacks kvm_trap_emul_callbacks = {
        .dequeue_io_int = kvm_mips_dequeue_io_int_cb,
        .irq_deliver = kvm_mips_irq_deliver_cb,
        .irq_clear = kvm_mips_irq_clear_cb,
+       .get_one_reg = kvm_trap_emul_get_one_reg,
+       .set_one_reg = kvm_trap_emul_set_one_reg,
 };
 
 int kvm_mips_emulation_init(struct kvm_mips_callbacks **install_callbacks)
index 19686c5..7447d32 100644 (file)
@@ -71,23 +71,12 @@ void __init plat_mem_setup(void)
         * Load the builtin devicetree. This causes the chosen node to be
         * parsed resulting in our memory appearing
         */
-       __dt_setup_arch(&__dtb_start);
+       __dt_setup_arch(__dtb_start);
 }
 
 void __init device_tree_init(void)
 {
-       unsigned long base, size;
-
-       if (!initial_boot_params)
-               return;
-
-       base = virt_to_phys((void *)initial_boot_params);
-       size = be32_to_cpu(initial_boot_params->totalsize);
-
-       /* Before we do anything, lets reserve the dt blob */
-       reserve_bootmem(base, size, BOOTMEM_DEFAULT);
-
-       unflatten_device_tree();
+       unflatten_and_copy_device_tree();
 }
 
 void __init prom_init(void)
index 8e07b5f..bfd2d58 100644 (file)
@@ -26,6 +26,4 @@ struct ltq_soc_info {
 extern void ltq_soc_detect(struct ltq_soc_info *i);
 extern void ltq_soc_init(void);
 
-extern struct boot_param_header __dtb_start;
-
 #endif
index 67dd94e..1eed38e 100644 (file)
@@ -91,10 +91,9 @@ EXPORT_SYMBOL(clk_put);
 
 int clk_set_rate(struct clk *clk, unsigned long rate)
 {
-       unsigned int rate_khz = rate / 1000;
+       struct cpufreq_frequency_table *pos;
        int ret = 0;
        int regval;
-       int i;
 
        if (likely(clk->ops && clk->ops->set_rate)) {
                unsigned long flags;
@@ -107,22 +106,16 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
        if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
                propagate_rate(clk);
 
-       for (i = 0; loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END;
-            i++) {
-               if (loongson2_clockmod_table[i].frequency ==
-                   CPUFREQ_ENTRY_INVALID)
-                       continue;
-               if (rate_khz == loongson2_clockmod_table[i].frequency)
+       cpufreq_for_each_valid_entry(pos, loongson2_clockmod_table)
+               if (rate == pos->frequency)
                        break;
-       }
-       if (rate_khz != loongson2_clockmod_table[i].frequency)
+       if (rate != pos->frequency)
                return -ENOTSUPP;
 
        clk->rate = rate;
 
        regval = LOONGSON_CHIPCFG0;
-       regval = (regval & ~0x7) |
-               (loongson2_clockmod_table[i].driver_data - 1);
+       regval = (regval & ~0x7) | (pos->driver_data - 1);
        LOONGSON_CHIPCFG0 = regval;
 
        return ret;
index 9e67cde..f7b91d3 100644 (file)
@@ -31,6 +31,7 @@ void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page,
 void (*flush_icache_range)(unsigned long start, unsigned long end);
 EXPORT_SYMBOL_GPL(flush_icache_range);
 void (*local_flush_icache_range)(unsigned long start, unsigned long end);
+EXPORT_SYMBOL_GPL(local_flush_icache_range);
 
 void (*__flush_cache_vmap)(void);
 void (*__flush_cache_vunmap)(void);
index 3190099..3778a35 100644 (file)
@@ -74,18 +74,8 @@ static void __init estimate_frequencies(void)
        unsigned int giccount = 0, gicstart = 0;
 #endif
 
-#if defined (CONFIG_KVM_GUEST) && defined (CONFIG_KVM_HOST_FREQ)
-       unsigned int prid = read_c0_prid() & (PRID_COMP_MASK | PRID_IMP_MASK);
-
-       /*
-        * XXXKYMA: hardwire the CPU frequency to Host Freq/4
-        */
-       count = (CONFIG_KVM_HOST_FREQ * 1000000) >> 3;
-       if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) &&
-           (prid != (PRID_COMP_MIPS | PRID_IMP_25KF)))
-               count *= 2;
-
-       mips_hpt_frequency = count;
+#if defined(CONFIG_KVM_GUEST) && CONFIG_KVM_GUEST_TIMER_FREQ
+       mips_hpt_frequency = CONFIG_KVM_GUEST_TIMER_FREQ * 1000000;
        return;
 #endif
 
index bf7fe48..e43f480 100644 (file)
@@ -69,17 +69,17 @@ static void __init parse_memsize_param(void)
        if (!memsize)
                return;
 
-       offset = fdt_path_offset(&__dtb_start, "/memory");
+       offset = fdt_path_offset(__dtb_start, "/memory");
        if (offset > 0) {
                uint64_t new_value;
                /*
                 * reg contains 2 32-bits BE values, offset and size. We just
                 * want to replace the size value without affecting the offset
                 */
-               prop_value = fdt_getprop(&__dtb_start, offset, "reg", &prop_len);
+               prop_value = fdt_getprop(__dtb_start, offset, "reg", &prop_len);
                new_value = be64_to_cpu(*prop_value);
                new_value =  (new_value & ~0xffffffffllu) | memsize;
-               fdt_setprop_inplace_u64(&__dtb_start, offset, "reg", new_value);
+               fdt_setprop_inplace_u64(__dtb_start, offset, "reg", new_value);
        }
 }
 
@@ -92,7 +92,7 @@ void __init plat_mem_setup(void)
         * Load the builtin devicetree. This causes the chosen node to be
         * parsed resulting in our memory appearing
         */
-       __dt_setup_arch(&__dtb_start);
+       __dt_setup_arch(__dtb_start);
 }
 
 void __init device_tree_init(void)
index 5754097..bdde331 100644 (file)
@@ -42,7 +42,7 @@
 #include <asm/prom.h>
 
 extern u32 __dtb_xlp_evp_begin[], __dtb_xlp_svp_begin[],
-       __dtb_xlp_fvp_begin[], __dtb_xlp_gvp_begin[], __dtb_start[];
+       __dtb_xlp_fvp_begin[], __dtb_xlp_gvp_begin[];
 static void *xlp_fdt_blob;
 
 void __init *xlp_dt_init(void *fdtp)
@@ -87,22 +87,7 @@ void __init xlp_early_init_devtree(void)
 
 void __init device_tree_init(void)
 {
-       unsigned long base, size;
-       struct boot_param_header *fdtp = xlp_fdt_blob;
-
-       if (!fdtp)
-               return;
-
-       base = virt_to_phys(fdtp);
-       size = be32_to_cpu(fdtp->totalsize);
-
-       /* Before we do anything, lets reserve the dt blob */
-       reserve_bootmem(base, size, BOOTMEM_DEFAULT);
-
-       unflatten_device_tree();
-
-       /* free the space reserved for the dt blob */
-       free_bootmem(base, size);
+       unflatten_and_copy_device_tree();
 }
 
 static struct of_device_id __initdata xlp_ids[] = {
index eccc552..2513952 100644 (file)
@@ -28,8 +28,6 @@
 __iomem void *rt_sysc_membase;
 __iomem void *rt_memc_membase;
 
-extern struct boot_param_header __dtb_start;
-
 __iomem void *plat_of_remap_node(const char *node)
 {
        struct resource res;
@@ -52,30 +50,7 @@ __iomem void *plat_of_remap_node(const char *node)
 
 void __init device_tree_init(void)
 {
-       unsigned long base, size;
-       void *fdt_copy;
-
-       if (!initial_boot_params)
-               return;
-
-       base = virt_to_phys((void *)initial_boot_params);
-       size = be32_to_cpu(initial_boot_params->totalsize);
-
-       /* Before we do anything, lets reserve the dt blob */
-       reserve_bootmem(base, size, BOOTMEM_DEFAULT);
-
-       /* The strings in the flattened tree are referenced directly by the
-        * device tree, so copy the flattened device tree from init memory
-        * to regular memory.
-        */
-       fdt_copy = alloc_bootmem(size);
-       memcpy(fdt_copy, initial_boot_params, size);
-       initial_boot_params = fdt_copy;
-
-       unflatten_device_tree();
-
-       /* free the space reserved for the dt blob */
-       free_bootmem(base, size);
+       unflatten_and_copy_device_tree();
 }
 
 void __init plat_mem_setup(void)
@@ -86,7 +61,7 @@ void __init plat_mem_setup(void)
         * Load the builtin devicetree. This causes the chosen node to be
         * parsed resulting in our memory appearing
         */
-       __dt_setup_arch(&__dtb_start);
+       __dt_setup_arch(__dtb_start);
 
        if (soc_info.mem_size)
                add_memory_region(soc_info.mem_base, soc_info.mem_size * SZ_1M,
index 70b9ce4..bbcdf21 100644 (file)
@@ -5,6 +5,4 @@
 extern char __initrd_start, __initrd_end;
 #endif
 
-extern u32 __dtb_start[];
-
 #endif
index acd491d..93a68b2 100644 (file)
@@ -51,10 +51,6 @@ static inline void dcr_write_mmio(dcr_host_mmio_t host,
        out_be32(host.token + ((host.base + dcr_n) * host.stride), value);
 }
 
-extern u64 of_translate_dcr_address(struct device_node *dev,
-                                   unsigned int dcr_n,
-                                   unsigned int *stride);
-
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_DCR_MMIO_H */
 
index 856f8de..6330a61 100644 (file)
@@ -81,4 +81,38 @@ static inline unsigned int get_oc(u32 inst)
 {
        return (inst >> 11) & 0x7fff;
 }
+
+#define IS_XFORM(inst) (get_op(inst)  == 31)
+#define IS_DSFORM(inst)        (get_op(inst) >= 56)
+
+/*
+ * Create a DSISR value from the instruction
+ */
+static inline unsigned make_dsisr(unsigned instr)
+{
+       unsigned dsisr;
+
+
+       /* bits  6:15 --> 22:31 */
+       dsisr = (instr & 0x03ff0000) >> 16;
+
+       if (IS_XFORM(instr)) {
+               /* bits 29:30 --> 15:16 */
+               dsisr |= (instr & 0x00000006) << 14;
+               /* bit     25 -->    17 */
+               dsisr |= (instr & 0x00000040) << 8;
+               /* bits 21:24 --> 18:21 */
+               dsisr |= (instr & 0x00000780) << 3;
+       } else {
+               /* bit      5 -->    17 */
+               dsisr |= (instr & 0x04000000) >> 12;
+               /* bits  1: 4 --> 18:21 */
+               dsisr |= (instr & 0x78000000) >> 17;
+               /* bits 30:31 --> 12:13 */
+               if (IS_DSFORM(instr))
+                       dsisr |= (instr & 0x00000003) << 18;
+       }
+
+       return dsisr;
+}
 #endif /* __ASM_PPC_DISASSEMBLE_H__ */
index 19eb74a..9601741 100644 (file)
 #define BOOK3S_INTERRUPT_PERFMON       0xf00
 #define BOOK3S_INTERRUPT_ALTIVEC       0xf20
 #define BOOK3S_INTERRUPT_VSX           0xf40
+#define BOOK3S_INTERRUPT_FAC_UNAVAIL   0xf60
 #define BOOK3S_INTERRUPT_H_FAC_UNAVAIL 0xf80
 
 #define BOOK3S_IRQPRIO_SYSTEM_RESET            0
 #define BOOK3S_IRQPRIO_FP_UNAVAIL              7
 #define BOOK3S_IRQPRIO_ALTIVEC                 8
 #define BOOK3S_IRQPRIO_VSX                     9
-#define BOOK3S_IRQPRIO_SYSCALL                 10
-#define BOOK3S_IRQPRIO_MACHINE_CHECK           11
-#define BOOK3S_IRQPRIO_DEBUG                   12
-#define BOOK3S_IRQPRIO_EXTERNAL                        13
-#define BOOK3S_IRQPRIO_DECREMENTER             14
-#define BOOK3S_IRQPRIO_PERFORMANCE_MONITOR     15
-#define BOOK3S_IRQPRIO_EXTERNAL_LEVEL          16
-#define BOOK3S_IRQPRIO_MAX                     17
+#define BOOK3S_IRQPRIO_FAC_UNAVAIL             10
+#define BOOK3S_IRQPRIO_SYSCALL                 11
+#define BOOK3S_IRQPRIO_MACHINE_CHECK           12
+#define BOOK3S_IRQPRIO_DEBUG                   13
+#define BOOK3S_IRQPRIO_EXTERNAL                        14
+#define BOOK3S_IRQPRIO_DECREMENTER             15
+#define BOOK3S_IRQPRIO_PERFORMANCE_MONITOR     16
+#define BOOK3S_IRQPRIO_EXTERNAL_LEVEL          17
+#define BOOK3S_IRQPRIO_MAX                     18
 
 #define BOOK3S_HFLAG_DCBZ32                    0x1
 #define BOOK3S_HFLAG_SLB                       0x2
index bb1e38a..f52f656 100644 (file)
@@ -268,9 +268,10 @@ static inline ulong kvmppc_get_pc(struct kvm_vcpu *vcpu)
        return vcpu->arch.pc;
 }
 
+static inline u64 kvmppc_get_msr(struct kvm_vcpu *vcpu);
 static inline bool kvmppc_need_byteswap(struct kvm_vcpu *vcpu)
 {
-       return (vcpu->arch.shared->msr & MSR_LE) != (MSR_KERNEL & MSR_LE);
+       return (kvmppc_get_msr(vcpu) & MSR_LE) != (MSR_KERNEL & MSR_LE);
 }
 
 static inline u32 kvmppc_get_last_inst_internal(struct kvm_vcpu *vcpu, ulong pc)
index 51388be..fddb72b 100644 (file)
@@ -77,34 +77,122 @@ static inline long try_lock_hpte(unsigned long *hpte, unsigned long bits)
        return old == 0;
 }
 
+static inline int __hpte_actual_psize(unsigned int lp, int psize)
+{
+       int i, shift;
+       unsigned int mask;
+
+       /* start from 1 ignoring MMU_PAGE_4K */
+       for (i = 1; i < MMU_PAGE_COUNT; i++) {
+
+               /* invalid penc */
+               if (mmu_psize_defs[psize].penc[i] == -1)
+                       continue;
+               /*
+                * encoding bits per actual page size
+                *        PTE LP     actual page size
+                *    rrrr rrrz         >=8KB
+                *    rrrr rrzz         >=16KB
+                *    rrrr rzzz         >=32KB
+                *    rrrr zzzz         >=64KB
+                * .......
+                */
+               shift = mmu_psize_defs[i].shift - LP_SHIFT;
+               if (shift > LP_BITS)
+                       shift = LP_BITS;
+               mask = (1 << shift) - 1;
+               if ((lp & mask) == mmu_psize_defs[psize].penc[i])
+                       return i;
+       }
+       return -1;
+}
+
 static inline unsigned long compute_tlbie_rb(unsigned long v, unsigned long r,
                                             unsigned long pte_index)
 {
-       unsigned long rb, va_low;
+       int b_psize, a_psize;
+       unsigned int penc;
+       unsigned long rb = 0, va_low, sllp;
+       unsigned int lp = (r >> LP_SHIFT) & ((1 << LP_BITS) - 1);
+
+       if (!(v & HPTE_V_LARGE)) {
+               /* both base and actual psize is 4k */
+               b_psize = MMU_PAGE_4K;
+               a_psize = MMU_PAGE_4K;
+       } else {
+               for (b_psize = 0; b_psize < MMU_PAGE_COUNT; b_psize++) {
+
+                       /* valid entries have a shift value */
+                       if (!mmu_psize_defs[b_psize].shift)
+                               continue;
 
+                       a_psize = __hpte_actual_psize(lp, b_psize);
+                       if (a_psize != -1)
+                               break;
+               }
+       }
+       /*
+        * Ignore the top 14 bits of va
+        * v have top two bits covering segment size, hence move
+        * by 16 bits, Also clear the lower HPTE_V_AVPN_SHIFT (7) bits.
+        * AVA field in v also have the lower 23 bits ignored.
+        * For base page size 4K we need 14 .. 65 bits (so need to
+        * collect extra 11 bits)
+        * For others we need 14..14+i
+        */
+       /* This covers 14..54 bits of va*/
        rb = (v & ~0x7fUL) << 16;               /* AVA field */
+       /*
+        * AVA in v had cleared lower 23 bits. We need to derive
+        * that from pteg index
+        */
        va_low = pte_index >> 3;
        if (v & HPTE_V_SECONDARY)
                va_low = ~va_low;
-       /* xor vsid from AVA */
+       /*
+        * get the vpn bits from va_low using reverse of hashing.
+        * In v we have va with 23 bits dropped and then left shifted
+        * HPTE_V_AVPN_SHIFT (7) bits. Now to find vsid we need
+        * right shift it with (SID_SHIFT - (23 - 7))
+        */
        if (!(v & HPTE_V_1TB_SEG))
-               va_low ^= v >> 12;
+               va_low ^= v >> (SID_SHIFT - 16);
        else
-               va_low ^= v >> 24;
+               va_low ^= v >> (SID_SHIFT_1T - 16);
        va_low &= 0x7ff;
-       if (v & HPTE_V_LARGE) {
-               rb |= 1;                        /* L field */
-               if (cpu_has_feature(CPU_FTR_ARCH_206) &&
-                   (r & 0xff000)) {
-                       /* non-16MB large page, must be 64k */
-                       /* (masks depend on page size) */
-                       rb |= 0x1000;           /* page encoding in LP field */
-                       rb |= (va_low & 0x7f) << 16; /* 7b of VA in AVA/LP field */
-                       rb |= ((va_low << 4) & 0xf0);   /* AVAL field (P7 doesn't seem to care) */
-               }
-       } else {
-               /* 4kB page */
-               rb |= (va_low & 0x7ff) << 12;   /* remaining 11b of VA */
+
+       switch (b_psize) {
+       case MMU_PAGE_4K:
+               sllp = ((mmu_psize_defs[a_psize].sllp & SLB_VSID_L) >> 6) |
+                       ((mmu_psize_defs[a_psize].sllp & SLB_VSID_LP) >> 4);
+               rb |= sllp << 5;        /*  AP field */
+               rb |= (va_low & 0x7ff) << 12;   /* remaining 11 bits of AVA */
+               break;
+       default:
+       {
+               int aval_shift;
+               /*
+                * remaining 7bits of AVA/LP fields
+                * Also contain the rr bits of LP
+                */
+               rb |= (va_low & 0x7f) << 16;
+               /*
+                * Now clear not needed LP bits based on actual psize
+                */
+               rb &= ~((1ul << mmu_psize_defs[a_psize].shift) - 1);
+               /*
+                * AVAL field 58..77 - base_page_shift bits of va
+                * we have space for 58..64 bits, Missing bits should
+                * be zero filled. +1 is to take care of L bit shift
+                */
+               aval_shift = 64 - (77 - mmu_psize_defs[b_psize].shift) + 1;
+               rb |= ((va_low << aval_shift) & 0xfe);
+
+               rb |= 1;                /* L field */
+               penc = mmu_psize_defs[b_psize].penc[a_psize];
+               rb |= penc << 12;       /* LP field */
+               break;
+       }
        }
        rb |= (v >> 54) & 0x300;                /* B field */
        return rb;
@@ -112,14 +200,26 @@ static inline unsigned long compute_tlbie_rb(unsigned long v, unsigned long r,
 
 static inline unsigned long hpte_page_size(unsigned long h, unsigned long l)
 {
+       int size, a_psize;
+       /* Look at the 8 bit LP value */
+       unsigned int lp = (l >> LP_SHIFT) & ((1 << LP_BITS) - 1);
+
        /* only handle 4k, 64k and 16M pages for now */
        if (!(h & HPTE_V_LARGE))
-               return 1ul << 12;               /* 4k page */
-       if ((l & 0xf000) == 0x1000 && cpu_has_feature(CPU_FTR_ARCH_206))
-               return 1ul << 16;               /* 64k page */
-       if ((l & 0xff000) == 0)
-               return 1ul << 24;               /* 16M page */
-       return 0;                               /* error */
+               return 1ul << 12;
+       else {
+               for (size = 0; size < MMU_PAGE_COUNT; size++) {
+                       /* valid entries have a shift value */
+                       if (!mmu_psize_defs[size].shift)
+                               continue;
+
+                       a_psize = __hpte_actual_psize(lp, size);
+                       if (a_psize != -1)
+                               return 1ul << mmu_psize_defs[a_psize].shift;
+               }
+
+       }
+       return 0;
 }
 
 static inline unsigned long hpte_rpn(unsigned long ptel, unsigned long psize)
index 821725c..5bdfb5d 100644 (file)
@@ -104,6 +104,7 @@ struct kvmppc_host_state {
 #ifdef CONFIG_PPC_BOOK3S_64
        u64 cfar;
        u64 ppr;
+       u64 host_fscr;
 #endif
 };
 
@@ -133,6 +134,7 @@ struct kvmppc_book3s_shadow_vcpu {
                u64     esid;
                u64     vsid;
        } slb[64];                      /* guest SLB */
+       u64 shadow_fscr;
 #endif
 };
 
index 80d46b5..c7aed61 100644 (file)
@@ -108,9 +108,4 @@ static inline ulong kvmppc_get_fault_dar(struct kvm_vcpu *vcpu)
 {
        return vcpu->arch.fault_dear;
 }
-
-static inline ulong kvmppc_get_msr(struct kvm_vcpu *vcpu)
-{
-       return vcpu->arch.shared->msr;
-}
 #endif /* __ASM_KVM_BOOKE_H__ */
index 1eaea2d..bb66d8b 100644 (file)
@@ -449,7 +449,9 @@ struct kvm_vcpu_arch {
        ulong pc;
        ulong ctr;
        ulong lr;
+#ifdef CONFIG_PPC_BOOK3S
        ulong tar;
+#endif
 
        ulong xer;
        u32 cr;
@@ -475,6 +477,7 @@ struct kvm_vcpu_arch {
        ulong ppr;
        ulong pspb;
        ulong fscr;
+       ulong shadow_fscr;
        ulong ebbhr;
        ulong ebbrr;
        ulong bescr;
@@ -562,6 +565,7 @@ struct kvm_vcpu_arch {
 #ifdef CONFIG_PPC_BOOK3S
        ulong fault_dar;
        u32 fault_dsisr;
+       unsigned long intr_msr;
 #endif
 
 #ifdef CONFIG_BOOKE
@@ -622,8 +626,12 @@ struct kvm_vcpu_arch {
        wait_queue_head_t cpu_run;
 
        struct kvm_vcpu_arch_shared *shared;
+#if defined(CONFIG_PPC_BOOK3S_64) && defined(CONFIG_KVM_BOOK3S_PR_POSSIBLE)
+       bool shared_big_endian;
+#endif
        unsigned long magic_page_pa; /* phys addr to map the magic page to */
        unsigned long magic_page_ea; /* effect. addr to map the magic page to */
+       bool disable_kernel_nx;
 
        int irq_type;           /* one of KVM_IRQ_* */
        int irq_cpu_id;
@@ -654,7 +662,6 @@ struct kvm_vcpu_arch {
        spinlock_t tbacct_lock;
        u64 busy_stolen;
        u64 busy_preempt;
-       unsigned long intr_msr;
 #endif
 };
 
index 4096f16..4a7cc45 100644 (file)
@@ -448,6 +448,84 @@ static inline void kvmppc_mmu_flush_icache(pfn_t pfn)
        }
 }
 
+/*
+ * Shared struct helpers. The shared struct can be little or big endian,
+ * depending on the guest endianness. So expose helpers to all of them.
+ */
+static inline bool kvmppc_shared_big_endian(struct kvm_vcpu *vcpu)
+{
+#if defined(CONFIG_PPC_BOOK3S_64) && defined(CONFIG_KVM_BOOK3S_PR_POSSIBLE)
+       /* Only Book3S_64 PR supports bi-endian for now */
+       return vcpu->arch.shared_big_endian;
+#elif defined(CONFIG_PPC_BOOK3S_64) && defined(__LITTLE_ENDIAN__)
+       /* Book3s_64 HV on little endian is always little endian */
+       return false;
+#else
+       return true;
+#endif
+}
+
+#define SHARED_WRAPPER_GET(reg, size)                                  \
+static inline u##size kvmppc_get_##reg(struct kvm_vcpu *vcpu)  \
+{                                                                      \
+       if (kvmppc_shared_big_endian(vcpu))                             \
+              return be##size##_to_cpu(vcpu->arch.shared->reg);        \
+       else                                                            \
+              return le##size##_to_cpu(vcpu->arch.shared->reg);        \
+}                                                                      \
+
+#define SHARED_WRAPPER_SET(reg, size)                                  \
+static inline void kvmppc_set_##reg(struct kvm_vcpu *vcpu, u##size val)        \
+{                                                                      \
+       if (kvmppc_shared_big_endian(vcpu))                             \
+              vcpu->arch.shared->reg = cpu_to_be##size(val);           \
+       else                                                            \
+              vcpu->arch.shared->reg = cpu_to_le##size(val);           \
+}                                                                      \
+
+#define SHARED_WRAPPER(reg, size)                                      \
+       SHARED_WRAPPER_GET(reg, size)                                   \
+       SHARED_WRAPPER_SET(reg, size)                                   \
+
+SHARED_WRAPPER(critical, 64)
+SHARED_WRAPPER(sprg0, 64)
+SHARED_WRAPPER(sprg1, 64)
+SHARED_WRAPPER(sprg2, 64)
+SHARED_WRAPPER(sprg3, 64)
+SHARED_WRAPPER(srr0, 64)
+SHARED_WRAPPER(srr1, 64)
+SHARED_WRAPPER(dar, 64)
+SHARED_WRAPPER_GET(msr, 64)
+static inline void kvmppc_set_msr_fast(struct kvm_vcpu *vcpu, u64 val)
+{
+       if (kvmppc_shared_big_endian(vcpu))
+              vcpu->arch.shared->msr = cpu_to_be64(val);
+       else
+              vcpu->arch.shared->msr = cpu_to_le64(val);
+}
+SHARED_WRAPPER(dsisr, 32)
+SHARED_WRAPPER(int_pending, 32)
+SHARED_WRAPPER(sprg4, 64)
+SHARED_WRAPPER(sprg5, 64)
+SHARED_WRAPPER(sprg6, 64)
+SHARED_WRAPPER(sprg7, 64)
+
+static inline u32 kvmppc_get_sr(struct kvm_vcpu *vcpu, int nr)
+{
+       if (kvmppc_shared_big_endian(vcpu))
+              return be32_to_cpu(vcpu->arch.shared->sr[nr]);
+       else
+              return le32_to_cpu(vcpu->arch.shared->sr[nr]);
+}
+
+static inline void kvmppc_set_sr(struct kvm_vcpu *vcpu, int nr, u32 val)
+{
+       if (kvmppc_shared_big_endian(vcpu))
+              vcpu->arch.shared->sr[nr] = cpu_to_be32(val);
+       else
+              vcpu->arch.shared->sr[nr] = cpu_to_le32(val);
+}
+
 /*
  * Please call after prepare_to_enter. This function puts the lazy ee and irq
  * disabled tracking state back to normal mode, without actually enabling
@@ -485,7 +563,7 @@ static inline ulong kvmppc_get_ea_indexed(struct kvm_vcpu *vcpu, int ra, int rb)
        msr_64bit = MSR_SF;
 #endif
 
-       if (!(vcpu->arch.shared->msr & msr_64bit))
+       if (!(kvmppc_get_msr(vcpu) & msr_64bit))
                ea = (uint32_t)ea;
 
        return ea;
index d977b9b..74b79f0 100644 (file)
 #include <linux/of_irq.h>
 #include <linux/platform_device.h>
 
+#define OF_DT_BEGIN_NODE       0x1             /* Start of node, full name */
+#define OF_DT_END_NODE         0x2             /* End node */
+#define OF_DT_PROP             0x3             /* Property: name off, size,
+                                                * content */
+#define OF_DT_NOP              0x4             /* nop */
+#define OF_DT_END              0x9
+
+#define OF_DT_VERSION          0x10
+
+/*
+ * This is what gets passed to the kernel by prom_init or kexec
+ *
+ * The dt struct contains the device tree structure, full pathes and
+ * property contents. The dt strings contain a separate block with just
+ * the strings for the property names, and is fully page aligned and
+ * self contained in a page, so that it can be kept around by the kernel,
+ * each property name appears only once in this page (cheap compression)
+ *
+ * the mem_rsvmap contains a map of reserved ranges of physical memory,
+ * passing it here instead of in the device-tree itself greatly simplifies
+ * the job of everybody. It's just a list of u64 pairs (base/size) that
+ * ends when size is 0
+ */
+struct boot_param_header {
+       __be32  magic;                  /* magic word OF_DT_HEADER */
+       __be32  totalsize;              /* total size of DT block */
+       __be32  off_dt_struct;          /* offset to structure */
+       __be32  off_dt_strings;         /* offset to strings */
+       __be32  off_mem_rsvmap;         /* offset to memory reserve map */
+       __be32  version;                /* format version */
+       __be32  last_comp_version;      /* last compatible version */
+       /* version 2 fields below */
+       __be32  boot_cpuid_phys;        /* Physical CPU id we're booting on */
+       /* version 3 fields below */
+       __be32  dt_strings_size;        /* size of the DT strings block */
+       /* version 17 fields below */
+       __be32  dt_struct_size;         /* size of the DT structure block */
+};
+
 /*
  * OF address retreival & translation
  */
index e5d2e0b..4852bcf 100644 (file)
 #define   MMCR0_PROBLEM_DISABLE MMCR0_FCP
 #define   MMCR0_FCM1   0x10000000UL /* freeze counters while MSR mark = 1 */
 #define   MMCR0_FCM0   0x08000000UL /* freeze counters while MSR mark = 0 */
-#define   MMCR0_PMXE   0x04000000UL /* performance monitor exception enable */
-#define   MMCR0_FCECE  0x02000000UL /* freeze ctrs on enabled cond or event */
+#define   MMCR0_PMXE   ASM_CONST(0x04000000) /* perf mon exception enable */
+#define   MMCR0_FCECE  ASM_CONST(0x02000000) /* freeze ctrs on enabled cond or event */
 #define   MMCR0_TBEE   0x00400000UL /* time base exception enable */
 #define   MMCR0_BHRBA  0x00200000UL /* BHRB Access allowed in userspace */
 #define   MMCR0_EBE    0x00100000UL /* Event based branch enable */
 #define   MMCR0_PMCC   0x000c0000UL /* PMC control */
 #define   MMCR0_PMCC_U6        0x00080000UL /* PMC1-6 are R/W by user (PR) */
 #define   MMCR0_PMC1CE 0x00008000UL /* PMC1 count enable*/
-#define   MMCR0_PMCjCE 0x00004000UL /* PMCj count enable*/
+#define   MMCR0_PMCjCE ASM_CONST(0x00004000) /* PMCj count enable*/
 #define   MMCR0_TRIGGER        0x00002000UL /* TRIGGER enable */
-#define   MMCR0_PMAO_SYNC 0x00000800UL /* PMU interrupt is synchronous */
-#define   MMCR0_PMAO   0x00000080UL /* performance monitor alert has occurred, set to 0 after handling exception */
+#define   MMCR0_PMAO_SYNC ASM_CONST(0x00000800) /* PMU intr is synchronous */
+#define   MMCR0_C56RUN ASM_CONST(0x00000100) /* PMC5/6 count when RUN=0 */
+/* performance monitor alert has occurred, set to 0 after handling exception */
+#define   MMCR0_PMAO   ASM_CONST(0x00000080)
 #define   MMCR0_SHRFC  0x00000040UL /* SHRre freeze conditions between threads */
 #define   MMCR0_FC56   0x00000010UL /* freeze counters 5 and 6 */
 #define   MMCR0_FCTI   0x00000008UL /* freeze counters in tags inactive mode */
index 163c3b0..464f108 100644 (file)
 
 /* Bit definitions for L1CSR0. */
 #define L1CSR0_CPE     0x00010000      /* Data Cache Parity Enable */
+#define L1CSR0_CUL     0x00000400      /* Data Cache Unable to Lock */
 #define L1CSR0_CLFC    0x00000100      /* Cache Lock Bits Flash Clear */
 #define L1CSR0_DCFI    0x00000002      /* Data Cache Flash Invalidate */
 #define L1CSR0_CFI     0x00000002      /* Cache Flash Invalidate */
index a6665be..2bc4a94 100644 (file)
@@ -545,7 +545,6 @@ struct kvm_get_htab_header {
 #define KVM_REG_PPC_TCSCR      (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb1)
 #define KVM_REG_PPC_PID                (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb2)
 #define KVM_REG_PPC_ACOP       (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb3)
-#define KVM_REG_PPC_WORT       (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb4)
 
 #define KVM_REG_PPC_VRSAVE     (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb4)
 #define KVM_REG_PPC_LPCR       (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb5)
@@ -555,6 +554,7 @@ struct kvm_get_htab_header {
 #define KVM_REG_PPC_ARCH_COMPAT        (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb7)
 
 #define KVM_REG_PPC_DABRX      (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xb8)
+#define KVM_REG_PPC_WORT       (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb9)
 
 /* Transactional Memory checkpointed state:
  * This is all GPRs, all VSX regs and a subset of SPRs
index e3af328..91e42f0 100644 (file)
@@ -82,10 +82,16 @@ struct kvm_vcpu_arch_shared {
 
 #define KVM_FEATURE_MAGIC_PAGE 1
 
+/* Magic page flags from host to guest */
+
 #define KVM_MAGIC_FEAT_SR              (1 << 0)
 
 /* MASn, ESR, PIR, and high SPRGs */
 #define KVM_MAGIC_FEAT_MAS0_TO_SPRG7   (1 << 1)
 
+/* Magic page flags from guest to host */
+
+#define MAGIC_PAGE_FLAG_NOT_MAPPED_NX  (1 << 0)
+
 
 #endif /* _UAPI__POWERPC_KVM_PARA_H__ */
index fcc9a89..fab19ec 100644 (file)
@@ -2,6 +2,7 @@
 # Makefile for the linux kernel.
 #
 
+CFLAGS_prom.o          = -I$(src)/../../../scripts/dtc/libfdt
 CFLAGS_ptrace.o                += -DUTS_MACHINE='"$(UTS_MACHINE)"'
 
 subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
index 94908af..34f5552 100644 (file)
 #include <asm/cputable.h>
 #include <asm/emulated_ops.h>
 #include <asm/switch_to.h>
+#include <asm/disassemble.h>
 
 struct aligninfo {
        unsigned char len;
        unsigned char flags;
 };
 
-#define IS_XFORM(inst) (((inst) >> 26) == 31)
-#define IS_DSFORM(inst)        (((inst) >> 26) >= 56)
 
 #define INVALID        { 0, 0 }
 
@@ -191,37 +190,6 @@ static struct aligninfo aligninfo[128] = {
        INVALID,                /* 11 1 1111 */
 };
 
-/*
- * Create a DSISR value from the instruction
- */
-static inline unsigned make_dsisr(unsigned instr)
-{
-       unsigned dsisr;
-
-
-       /* bits  6:15 --> 22:31 */
-       dsisr = (instr & 0x03ff0000) >> 16;
-
-       if (IS_XFORM(instr)) {
-               /* bits 29:30 --> 15:16 */
-               dsisr |= (instr & 0x00000006) << 14;
-               /* bit     25 -->    17 */
-               dsisr |= (instr & 0x00000040) << 8;
-               /* bits 21:24 --> 18:21 */
-               dsisr |= (instr & 0x00000780) << 3;
-       } else {
-               /* bit      5 -->    17 */
-               dsisr |= (instr & 0x04000000) >> 12;
-               /* bits  1: 4 --> 18:21 */
-               dsisr |= (instr & 0x78000000) >> 17;
-               /* bits 30:31 --> 12:13 */
-               if (IS_DSFORM(instr))
-                       dsisr |= (instr & 0x00000003) << 18;
-       }
-
-       return dsisr;
-}
-
 /*
  * The dcbz (data cache block zero) instruction
  * gives an alignment fault if used on non-cacheable
index dba8140..93e1465 100644 (file)
@@ -54,6 +54,7 @@
 #endif
 #if defined(CONFIG_KVM) && defined(CONFIG_PPC_BOOK3S)
 #include <asm/kvm_book3s.h>
+#include <asm/kvm_ppc.h>
 #endif
 
 #ifdef CONFIG_PPC32
@@ -445,7 +446,9 @@ int main(void)
        DEFINE(VCPU_XER, offsetof(struct kvm_vcpu, arch.xer));
        DEFINE(VCPU_CTR, offsetof(struct kvm_vcpu, arch.ctr));
        DEFINE(VCPU_LR, offsetof(struct kvm_vcpu, arch.lr));
+#ifdef CONFIG_PPC_BOOK3S
        DEFINE(VCPU_TAR, offsetof(struct kvm_vcpu, arch.tar));
+#endif
        DEFINE(VCPU_CR, offsetof(struct kvm_vcpu, arch.cr));
        DEFINE(VCPU_PC, offsetof(struct kvm_vcpu, arch.pc));
 #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
@@ -467,6 +470,9 @@ int main(void)
        DEFINE(VCPU_SHARED, offsetof(struct kvm_vcpu, arch.shared));
        DEFINE(VCPU_SHARED_MSR, offsetof(struct kvm_vcpu_arch_shared, msr));
        DEFINE(VCPU_SHADOW_MSR, offsetof(struct kvm_vcpu, arch.shadow_msr));
+#if defined(CONFIG_PPC_BOOK3S_64) && defined(CONFIG_KVM_BOOK3S_PR_POSSIBLE)
+       DEFINE(VCPU_SHAREDBE, offsetof(struct kvm_vcpu, arch.shared_big_endian));
+#endif
 
        DEFINE(VCPU_SHARED_MAS0, offsetof(struct kvm_vcpu_arch_shared, mas0));
        DEFINE(VCPU_SHARED_MAS1, offsetof(struct kvm_vcpu_arch_shared, mas1));
@@ -493,7 +499,6 @@ int main(void)
        DEFINE(VCPU_DAR, offsetof(struct kvm_vcpu, arch.shregs.dar));
        DEFINE(VCPU_VPA, offsetof(struct kvm_vcpu, arch.vpa.pinned_addr));
        DEFINE(VCPU_VPA_DIRTY, offsetof(struct kvm_vcpu, arch.vpa.dirty));
-       DEFINE(VCPU_INTR_MSR, offsetof(struct kvm_vcpu, arch.intr_msr));
 #endif
 #ifdef CONFIG_PPC_BOOK3S
        DEFINE(VCPU_VCPUID, offsetof(struct kvm_vcpu, vcpu_id));
@@ -528,11 +533,13 @@ int main(void)
        DEFINE(VCPU_SLB_NR, offsetof(struct kvm_vcpu, arch.slb_nr));
        DEFINE(VCPU_FAULT_DSISR, offsetof(struct kvm_vcpu, arch.fault_dsisr));
        DEFINE(VCPU_FAULT_DAR, offsetof(struct kvm_vcpu, arch.fault_dar));
+       DEFINE(VCPU_INTR_MSR, offsetof(struct kvm_vcpu, arch.intr_msr));
        DEFINE(VCPU_LAST_INST, offsetof(struct kvm_vcpu, arch.last_inst));
        DEFINE(VCPU_TRAP, offsetof(struct kvm_vcpu, arch.trap));
        DEFINE(VCPU_CFAR, offsetof(struct kvm_vcpu, arch.cfar));
        DEFINE(VCPU_PPR, offsetof(struct kvm_vcpu, arch.ppr));
        DEFINE(VCPU_FSCR, offsetof(struct kvm_vcpu, arch.fscr));
+       DEFINE(VCPU_SHADOW_FSCR, offsetof(struct kvm_vcpu, arch.shadow_fscr));
        DEFINE(VCPU_PSPB, offsetof(struct kvm_vcpu, arch.pspb));
        DEFINE(VCPU_EBBHR, offsetof(struct kvm_vcpu, arch.ebbhr));
        DEFINE(VCPU_EBBRR, offsetof(struct kvm_vcpu, arch.ebbrr));
@@ -614,6 +621,7 @@ int main(void)
 #ifdef CONFIG_PPC64
        SVCPU_FIELD(SVCPU_SLB, slb);
        SVCPU_FIELD(SVCPU_SLB_MAX, slb_max);
+       SVCPU_FIELD(SVCPU_SHADOW_FSCR, shadow_fscr);
 #endif
 
        HSTATE_FIELD(HSTATE_HOST_R1, host_r1);
@@ -649,6 +657,7 @@ int main(void)
 #ifdef CONFIG_PPC_BOOK3S_64
        HSTATE_FIELD(HSTATE_CFAR, cfar);
        HSTATE_FIELD(HSTATE_PPR, ppr);
+       HSTATE_FIELD(HSTATE_HOST_FSCR, host_fscr);
 #endif /* CONFIG_PPC_BOOK3S_64 */
 
 #else /* CONFIG_PPC_BOOK3S */
index 7898be9..60d1a22 100644 (file)
@@ -36,7 +36,7 @@ static int __init early_init_dt_scan_epapr(unsigned long node,
                                           int depth, void *data)
 {
        const u32 *insts;
-       unsigned long len;
+       int len;
        int i;
 
        insts = of_get_flat_dt_prop(node, "hcall-instructions", &len);
@@ -47,9 +47,10 @@ static int __init early_init_dt_scan_epapr(unsigned long node,
                return -1;
 
        for (i = 0; i < (len / 4); i++) {
-               patch_instruction(epapr_hypercall_start + i, insts[i]);
+               u32 inst = be32_to_cpu(insts[i]);
+               patch_instruction(epapr_hypercall_start + i, inst);
 #if !defined(CONFIG_64BIT) || defined(CONFIG_PPC_BOOK3E_64)
-               patch_instruction(epapr_ev_idle_start + i, insts[i]);
+               patch_instruction(epapr_ev_idle_start + i, inst);
 #endif
        }
 
index 2230fd0..7213d93 100644 (file)
@@ -55,9 +55,9 @@ int crash_mem_ranges;
 int __init early_init_dt_scan_fw_dump(unsigned long node,
                        const char *uname, int depth, void *data)
 {
-       __be32 *sections;
+       const __be32 *sections;
        int i, num_sections;
-       unsigned long size;
+       int size;
        const int *token;
 
        if (depth != 1 || strcmp(uname, "rtas") != 0)
index dd8695f..33aa4dd 100644 (file)
@@ -417,7 +417,7 @@ static void kvm_map_magic_page(void *data)
        ulong out[8];
 
        in[0] = KVM_MAGIC_PAGE;
-       in[1] = KVM_MAGIC_PAGE;
+       in[1] = KVM_MAGIC_PAGE | MAGIC_PAGE_FLAG_NOT_MAPPED_NX;
 
        epapr_hypercall(in, out, KVM_HCALL_TOKEN(KVM_HC_PPC_MAP_MAGIC_PAGE));
 
index ad302f8..d6e195e 100644 (file)
@@ -98,6 +98,9 @@ static inline void free_lppacas(void) { }
 /*
  * 3 persistent SLBs are registered here.  The buffer will be zero
  * initially, hence will all be invaild until we actually write them.
+ *
+ * If you make the number of persistent SLB entries dynamic, please also
+ * update PR KVM to flush and restore them accordingly.
  */
 static struct slb_shadow *slb_shadow;
 
index 668aa47..613a860 100644 (file)
 #include <linux/bitops.h>
 #include <linux/export.h>
 #include <linux/kexec.h>
-#include <linux/debugfs.h>
 #include <linux/irq.h>
 #include <linux/memblock.h>
 #include <linux/of.h>
 #include <linux/of_fdt.h>
+#include <linux/libfdt.h>
 
 #include <asm/prom.h>
 #include <asm/rtas.h>
@@ -118,14 +118,14 @@ static void __init move_device_tree(void)
        DBG("-> move_device_tree\n");
 
        start = __pa(initial_boot_params);
-       size = be32_to_cpu(initial_boot_params->totalsize);
+       size = fdt_totalsize(initial_boot_params);
 
        if ((memory_limit && (start + size) > PHYSICAL_START + memory_limit) ||
                        overlaps_crashkernel(start, size) ||
                        overlaps_initrd(start, size)) {
                p = __va(memblock_alloc(size, PAGE_SIZE));
                memcpy(p, initial_boot_params, size);
-               initial_boot_params = (struct boot_param_header *)p;
+               initial_boot_params = p;
                DBG("Moved device tree to 0x%p\n", p);
        }
 
@@ -163,7 +163,7 @@ static struct ibm_pa_feature {
        {CPU_FTR_REAL_LE, PPC_FEATURE_TRUE_LE, 5, 0, 0},
 };
 
-static void __init scan_features(unsigned long node, unsigned char *ftrs,
+static void __init scan_features(unsigned long node, const unsigned char *ftrs,
                                 unsigned long tablelen,
                                 struct ibm_pa_feature *fp,
                                 unsigned long ft_size)
@@ -202,8 +202,8 @@ static void __init scan_features(unsigned long node, unsigned char *ftrs,
 
 static void __init check_cpu_pa_features(unsigned long node)
 {
-       unsigned char *pa_ftrs;
-       unsigned long tablelen;
+       const unsigned char *pa_ftrs;
+       int tablelen;
 
        pa_ftrs = of_get_flat_dt_prop(node, "ibm,pa-features", &tablelen);
        if (pa_ftrs == NULL)
@@ -216,7 +216,7 @@ static void __init check_cpu_pa_features(unsigned long node)
 #ifdef CONFIG_PPC_STD_MMU_64
 static void __init check_cpu_slb_size(unsigned long node)
 {
-       __be32 *slb_size_ptr;
+       const __be32 *slb_size_ptr;
 
        slb_size_ptr = of_get_flat_dt_prop(node, "slb-size", NULL);
        if (slb_size_ptr != NULL) {
@@ -257,7 +257,7 @@ static struct feature_property {
 static inline void identical_pvr_fixup(unsigned long node)
 {
        unsigned int pvr;
-       char *model = of_get_flat_dt_prop(node, "model", NULL);
+       const char *model = of_get_flat_dt_prop(node, "model", NULL);
 
        /*
         * Since 440GR(x)/440EP(x) processors have the same pvr,
@@ -295,11 +295,11 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
                                          const char *uname, int depth,
                                          void *data)
 {
-       char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+       const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
        const __be32 *prop;
        const __be32 *intserv;
        int i, nthreads;
-       unsigned long len;
+       int len;
        int found = -1;
        int found_thread = 0;
 
@@ -325,9 +325,9 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
                 * version 2 of the kexec param format adds the phys cpuid of
                 * booted proc.
                 */
-               if (be32_to_cpu(initial_boot_params->version) >= 2) {
+               if (fdt_version(initial_boot_params) >= 2) {
                        if (be32_to_cpu(intserv[i]) ==
-                           be32_to_cpu(initial_boot_params->boot_cpuid_phys)) {
+                           fdt_boot_cpuid_phys(initial_boot_params)) {
                                found = boot_cpu_count;
                                found_thread = i;
                        }
@@ -392,7 +392,7 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
 int __init early_init_dt_scan_chosen_ppc(unsigned long node, const char *uname,
                                         int depth, void *data)
 {
-       unsigned long *lprop; /* All these set by kernel, so no need to convert endian */
+       const unsigned long *lprop; /* All these set by kernel, so no need to convert endian */
 
        /* Use common scan routine to determine if this is the chosen node */
        if (early_init_dt_scan_chosen(node, uname, depth, data) == 0)
@@ -443,8 +443,9 @@ int __init early_init_dt_scan_chosen_ppc(unsigned long node, const char *uname,
  */
 static int __init early_init_dt_scan_drconf_memory(unsigned long node)
 {
-       __be32 *dm, *ls, *usm;
-       unsigned long l, n, flags;
+       const __be32 *dm, *ls, *usm;
+       int l;
+       unsigned long n, flags;
        u64 base, size, memblock_size;
        unsigned int is_kexec_kdump = 0, rngs;
 
@@ -564,9 +565,12 @@ void __init early_init_dt_add_memory_arch(u64 base, u64 size)
 
 static void __init early_reserve_mem_dt(void)
 {
-       unsigned long i, len, dt_root;
+       unsigned long i, dt_root;
+       int len;
        const __be32 *prop;
 
+       early_init_fdt_scan_reserved_mem();
+
        dt_root = of_get_flat_dt_root();
 
        prop = of_get_flat_dt_prop(dt_root, "reserved-ranges", &len);
@@ -589,24 +593,14 @@ static void __init early_reserve_mem_dt(void)
                        memblock_reserve(base, size);
                }
        }
-
-       early_init_fdt_scan_reserved_mem();
 }
 
 static void __init early_reserve_mem(void)
 {
-       u64 base, size;
        __be64 *reserve_map;
-       unsigned long self_base;
-       unsigned long self_size;
 
        reserve_map = (__be64 *)(((unsigned long)initial_boot_params) +
-                       be32_to_cpu(initial_boot_params->off_mem_rsvmap));
-
-       /* before we do anything, lets reserve the dt blob */
-       self_base = __pa((unsigned long)initial_boot_params);
-       self_size = be32_to_cpu(initial_boot_params->totalsize);
-       memblock_reserve(self_base, self_size);
+                       fdt_off_mem_rsvmap(initial_boot_params));
 
        /* Look for the new "reserved-regions" property in the DT */
        early_reserve_mem_dt();
@@ -636,26 +630,12 @@ static void __init early_reserve_mem(void)
                        size_32 = be32_to_cpup(reserve_map_32++);
                        if (size_32 == 0)
                                break;
-                       /* skip if the reservation is for the blob */
-                       if (base_32 == self_base && size_32 == self_size)
-                               continue;
                        DBG("reserving: %x -> %x\n", base_32, size_32);
                        memblock_reserve(base_32, size_32);
                }
                return;
        }
 #endif
-       DBG("Processing reserve map\n");
-
-       /* Handle the reserve map in the fdt blob if it exists */
-       while (1) {
-               base = be64_to_cpup(reserve_map++);
-               size = be64_to_cpup(reserve_map++);
-               if (size == 0)
-                       break;
-               DBG("reserving: %llx -> %llx\n", base, size);
-               memblock_reserve(base, size);
-       }
 }
 
 void __init early_init_devtree(void *params)
@@ -922,23 +902,3 @@ bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
 {
        return (int)phys_id == get_hard_smp_processor_id(cpu);
 }
-
-#if defined(CONFIG_DEBUG_FS) && defined(DEBUG)
-static struct debugfs_blob_wrapper flat_dt_blob;
-
-static int __init export_flat_device_tree(void)
-{
-       struct dentry *d;
-
-       flat_dt_blob.data = initial_boot_params;
-       flat_dt_blob.size = be32_to_cpu(initial_boot_params->totalsize);
-
-       d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR,
-                               powerpc_debugfs_root, &flat_dt_blob);
-       if (!d)
-               return 1;
-
-       return 0;
-}
-__initcall(export_flat_device_tree);
-#endif
index 8cd5ed0..8b4c857 100644 (file)
@@ -1142,7 +1142,7 @@ void __init rtas_initialize(void)
 int __init early_init_dt_scan_rtas(unsigned long node,
                const char *uname, int depth, void *data)
 {
-       u32 *basep, *entryp, *sizep;
+       const u32 *basep, *entryp, *sizep;
 
        if (depth != 1 || strcmp(uname, "rtas") != 0)
                return 0;
index 141b202..d6a53b9 100644 (file)
@@ -6,7 +6,6 @@ source "virt/kvm/Kconfig"
 
 menuconfig VIRTUALIZATION
        bool "Virtualization"
-       depends on !CPU_LITTLE_ENDIAN
        ---help---
          Say Y here to get to see options for using your Linux host to run
          other operating systems inside virtual machines (guests).
@@ -76,6 +75,7 @@ config KVM_BOOK3S_64
 config KVM_BOOK3S_64_HV
        tristate "KVM support for POWER7 and PPC970 using hypervisor mode in host"
        depends on KVM_BOOK3S_64
+       depends on !CPU_LITTLE_ENDIAN
        select KVM_BOOK3S_HV_POSSIBLE
        select MMU_NOTIFIER
        select CMA
index 7af190a..c254c27 100644 (file)
@@ -85,9 +85,9 @@ static inline void kvmppc_update_int_pending(struct kvm_vcpu *vcpu,
        if (is_kvmppc_hv_enabled(vcpu->kvm))
                return;
        if (pending_now)
-               vcpu->arch.shared->int_pending = 1;
+               kvmppc_set_int_pending(vcpu, 1);
        else if (old_pending)
-               vcpu->arch.shared->int_pending = 0;
+               kvmppc_set_int_pending(vcpu, 0);
 }
 
 static inline bool kvmppc_critical_section(struct kvm_vcpu *vcpu)
@@ -99,11 +99,11 @@ static inline bool kvmppc_critical_section(struct kvm_vcpu *vcpu)
        if (is_kvmppc_hv_enabled(vcpu->kvm))
                return false;
 
-       crit_raw = vcpu->arch.shared->critical;
+       crit_raw = kvmppc_get_critical(vcpu);
        crit_r1 = kvmppc_get_gpr(vcpu, 1);
 
        /* Truncate crit indicators in 32 bit mode */
-       if (!(vcpu->arch.shared->msr & MSR_SF)) {
+       if (!(kvmppc_get_msr(vcpu) & MSR_SF)) {
                crit_raw &= 0xffffffff;
                crit_r1 &= 0xffffffff;
        }
@@ -111,15 +111,15 @@ static inline bool kvmppc_critical_section(struct kvm_vcpu *vcpu)
        /* Critical section when crit == r1 */
        crit = (crit_raw == crit_r1);
        /* ... and we're in supervisor mode */
-       crit = crit && !(vcpu->arch.shared->msr & MSR_PR);
+       crit = crit && !(kvmppc_get_msr(vcpu) & MSR_PR);
 
        return crit;
 }
 
 void kvmppc_inject_interrupt(struct kvm_vcpu *vcpu, int vec, u64 flags)
 {
-       vcpu->arch.shared->srr0 = kvmppc_get_pc(vcpu);
-       vcpu->arch.shared->srr1 = vcpu->arch.shared->msr | flags;
+       kvmppc_set_srr0(vcpu, kvmppc_get_pc(vcpu));
+       kvmppc_set_srr1(vcpu, kvmppc_get_msr(vcpu) | flags);
        kvmppc_set_pc(vcpu, kvmppc_interrupt_offset(vcpu) + vec);
        vcpu->arch.mmu.reset_msr(vcpu);
 }
@@ -145,6 +145,7 @@ static int kvmppc_book3s_vec2irqprio(unsigned int vec)
        case 0xd00: prio = BOOK3S_IRQPRIO_DEBUG;                break;
        case 0xf20: prio = BOOK3S_IRQPRIO_ALTIVEC;              break;
        case 0xf40: prio = BOOK3S_IRQPRIO_VSX;                  break;
+       case 0xf60: prio = BOOK3S_IRQPRIO_FAC_UNAVAIL;          break;
        default:    prio = BOOK3S_IRQPRIO_MAX;                  break;
        }
 
@@ -225,12 +226,12 @@ int kvmppc_book3s_irqprio_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
 
        switch (priority) {
        case BOOK3S_IRQPRIO_DECREMENTER:
-               deliver = (vcpu->arch.shared->msr & MSR_EE) && !crit;
+               deliver = (kvmppc_get_msr(vcpu) & MSR_EE) && !crit;
                vec = BOOK3S_INTERRUPT_DECREMENTER;
                break;
        case BOOK3S_IRQPRIO_EXTERNAL:
        case BOOK3S_IRQPRIO_EXTERNAL_LEVEL:
-               deliver = (vcpu->arch.shared->msr & MSR_EE) && !crit;
+               deliver = (kvmppc_get_msr(vcpu) & MSR_EE) && !crit;
                vec = BOOK3S_INTERRUPT_EXTERNAL;
                break;
        case BOOK3S_IRQPRIO_SYSTEM_RESET:
@@ -275,6 +276,9 @@ int kvmppc_book3s_irqprio_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
        case BOOK3S_IRQPRIO_PERFORMANCE_MONITOR:
                vec = BOOK3S_INTERRUPT_PERFMON;
                break;
+       case BOOK3S_IRQPRIO_FAC_UNAVAIL:
+               vec = BOOK3S_INTERRUPT_FAC_UNAVAIL;
+               break;
        default:
                deliver = 0;
                printk(KERN_ERR "KVM: Unknown interrupt: 0x%x\n", priority);
@@ -343,7 +347,7 @@ pfn_t kvmppc_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn, bool writing,
 {
        ulong mp_pa = vcpu->arch.magic_page_pa;
 
-       if (!(vcpu->arch.shared->msr & MSR_SF))
+       if (!(kvmppc_get_msr(vcpu) & MSR_SF))
                mp_pa = (uint32_t)mp_pa;
 
        /* Magic page override */
@@ -367,7 +371,7 @@ EXPORT_SYMBOL_GPL(kvmppc_gfn_to_pfn);
 static int kvmppc_xlate(struct kvm_vcpu *vcpu, ulong eaddr, bool data,
                        bool iswrite, struct kvmppc_pte *pte)
 {
-       int relocated = (vcpu->arch.shared->msr & (data ? MSR_DR : MSR_IR));
+       int relocated = (kvmppc_get_msr(vcpu) & (data ? MSR_DR : MSR_IR));
        int r;
 
        if (relocated) {
@@ -498,18 +502,18 @@ int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
        regs->ctr = kvmppc_get_ctr(vcpu);
        regs->lr = kvmppc_get_lr(vcpu);
        regs->xer = kvmppc_get_xer(vcpu);
-       regs->msr = vcpu->arch.shared->msr;
-       regs->srr0 = vcpu->arch.shared->srr0;
-       regs->srr1 = vcpu->arch.shared->srr1;
+       regs->msr = kvmppc_get_msr(vcpu);
+       regs->srr0 = kvmppc_get_srr0(vcpu);
+       regs->srr1 = kvmppc_get_srr1(vcpu);
        regs->pid = vcpu->arch.pid;
-       regs->sprg0 = vcpu->arch.shared->sprg0;
-       regs->sprg1 = vcpu->arch.shared->sprg1;
-       regs->sprg2 = vcpu->arch.shared->sprg2;
-       regs->sprg3 = vcpu->arch.shared->sprg3;
-       regs->sprg4 = vcpu->arch.shared->sprg4;
-       regs->sprg5 = vcpu->arch.shared->sprg5;
-       regs->sprg6 = vcpu->arch.shared->sprg6;
-       regs->sprg7 = vcpu->arch.shared->sprg7;
+       regs->sprg0 = kvmppc_get_sprg0(vcpu);
+       regs->sprg1 = kvmppc_get_sprg1(vcpu);
+       regs->sprg2 = kvmppc_get_sprg2(vcpu);
+       regs->sprg3 = kvmppc_get_sprg3(vcpu);
+       regs->sprg4 = kvmppc_get_sprg4(vcpu);
+       regs->sprg5 = kvmppc_get_sprg5(vcpu);
+       regs->sprg6 = kvmppc_get_sprg6(vcpu);
+       regs->sprg7 = kvmppc_get_sprg7(vcpu);
 
        for (i = 0; i < ARRAY_SIZE(regs->gpr); i++)
                regs->gpr[i] = kvmppc_get_gpr(vcpu, i);
@@ -527,16 +531,16 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
        kvmppc_set_lr(vcpu, regs->lr);
        kvmppc_set_xer(vcpu, regs->xer);
        kvmppc_set_msr(vcpu, regs->msr);
-       vcpu->arch.shared->srr0 = regs->srr0;
-       vcpu->arch.shared->srr1 = regs->srr1;
-       vcpu->arch.shared->sprg0 = regs->sprg0;
-       vcpu->arch.shared->sprg1 = regs->sprg1;
-       vcpu->arch.shared->sprg2 = regs->sprg2;
-       vcpu->arch.shared->sprg3 = regs->sprg3;
-       vcpu->arch.shared->sprg4 = regs->sprg4;
-       vcpu->arch.shared->sprg5 = regs->sprg5;
-       vcpu->arch.shared->sprg6 = regs->sprg6;
-       vcpu->arch.shared->sprg7 = regs->sprg7;
+       kvmppc_set_srr0(vcpu, regs->srr0);
+       kvmppc_set_srr1(vcpu, regs->srr1);
+       kvmppc_set_sprg0(vcpu, regs->sprg0);
+       kvmppc_set_sprg1(vcpu, regs->sprg1);
+       kvmppc_set_sprg2(vcpu, regs->sprg2);
+       kvmppc_set_sprg3(vcpu, regs->sprg3);
+       kvmppc_set_sprg4(vcpu, regs->sprg4);
+       kvmppc_set_sprg5(vcpu, regs->sprg5);
+       kvmppc_set_sprg6(vcpu, regs->sprg6);
+       kvmppc_set_sprg7(vcpu, regs->sprg7);
 
        for (i = 0; i < ARRAY_SIZE(regs->gpr); i++)
                kvmppc_set_gpr(vcpu, i, regs->gpr[i]);
@@ -570,10 +574,10 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
                r = 0;
                switch (reg->id) {
                case KVM_REG_PPC_DAR:
-                       val = get_reg_val(reg->id, vcpu->arch.shared->dar);
+                       val = get_reg_val(reg->id, kvmppc_get_dar(vcpu));
                        break;
                case KVM_REG_PPC_DSISR:
-                       val = get_reg_val(reg->id, vcpu->arch.shared->dsisr);
+                       val = get_reg_val(reg->id, kvmppc_get_dsisr(vcpu));
                        break;
                case KVM_REG_PPC_FPR0 ... KVM_REG_PPC_FPR31:
                        i = reg->id - KVM_REG_PPC_FPR0;
@@ -627,6 +631,21 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
                        val = get_reg_val(reg->id, kvmppc_xics_get_icp(vcpu));
                        break;
 #endif /* CONFIG_KVM_XICS */
+               case KVM_REG_PPC_FSCR:
+                       val = get_reg_val(reg->id, vcpu->arch.fscr);
+                       break;
+               case KVM_REG_PPC_TAR:
+                       val = get_reg_val(reg->id, vcpu->arch.tar);
+                       break;
+               case KVM_REG_PPC_EBBHR:
+                       val = get_reg_val(reg->id, vcpu->arch.ebbhr);
+                       break;
+               case KVM_REG_PPC_EBBRR:
+                       val = get_reg_val(reg->id, vcpu->arch.ebbrr);
+                       break;
+               case KVM_REG_PPC_BESCR:
+                       val = get_reg_val(reg->id, vcpu->arch.bescr);
+                       break;
                default:
                        r = -EINVAL;
                        break;
@@ -660,10 +679,10 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
                r = 0;
                switch (reg->id) {
                case KVM_REG_PPC_DAR:
-                       vcpu->arch.shared->dar = set_reg_val(reg->id, val);
+                       kvmppc_set_dar(vcpu, set_reg_val(reg->id, val));
                        break;
                case KVM_REG_PPC_DSISR:
-                       vcpu->arch.shared->dsisr = set_reg_val(reg->id, val);
+                       kvmppc_set_dsisr(vcpu, set_reg_val(reg->id, val));
                        break;
                case KVM_REG_PPC_FPR0 ... KVM_REG_PPC_FPR31:
                        i = reg->id - KVM_REG_PPC_FPR0;
@@ -716,6 +735,21 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
                                                set_reg_val(reg->id, val));
                        break;
 #endif /* CONFIG_KVM_XICS */
+               case KVM_REG_PPC_FSCR:
+                       vcpu->arch.fscr = set_reg_val(reg->id, val);
+                       break;
+               case KVM_REG_PPC_TAR:
+                       vcpu->arch.tar = set_reg_val(reg->id, val);
+                       break;
+               case KVM_REG_PPC_EBBHR:
+                       vcpu->arch.ebbhr = set_reg_val(reg->id, val);
+                       break;
+               case KVM_REG_PPC_EBBRR:
+                       vcpu->arch.ebbrr = set_reg_val(reg->id, val);
+                       break;
+               case KVM_REG_PPC_BESCR:
+                       vcpu->arch.bescr = set_reg_val(reg->id, val);
+                       break;
                default:
                        r = -EINVAL;
                        break;
index 76a64ce..93503bb 100644 (file)
@@ -91,7 +91,7 @@ static int kvmppc_mmu_book3s_32_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,
 
 static u32 find_sr(struct kvm_vcpu *vcpu, gva_t eaddr)
 {
-       return vcpu->arch.shared->sr[(eaddr >> 28) & 0xf];
+       return kvmppc_get_sr(vcpu, (eaddr >> 28) & 0xf);
 }
 
 static u64 kvmppc_mmu_book3s_32_ea_to_vp(struct kvm_vcpu *vcpu, gva_t eaddr,
@@ -131,7 +131,7 @@ static hva_t kvmppc_mmu_book3s_32_get_pteg(struct kvm_vcpu *vcpu,
        pteg = (vcpu_book3s->sdr1 & 0xffff0000) | hash;
 
        dprintk("MMU: pc=0x%lx eaddr=0x%lx sdr1=0x%llx pteg=0x%x vsid=0x%x\n",
-               kvmppc_get_pc(&vcpu_book3s->vcpu), eaddr, vcpu_book3s->sdr1, pteg,
+               kvmppc_get_pc(vcpu), eaddr, vcpu_book3s->sdr1, pteg,
                sr_vsid(sre));
 
        r = gfn_to_hva(vcpu->kvm, pteg >> PAGE_SHIFT);
@@ -160,7 +160,7 @@ static int kvmppc_mmu_book3s_32_xlate_bat(struct kvm_vcpu *vcpu, gva_t eaddr,
                else
                        bat = &vcpu_book3s->ibat[i];
 
-               if (vcpu->arch.shared->msr & MSR_PR) {
+               if (kvmppc_get_msr(vcpu) & MSR_PR) {
                        if (!bat->vp)
                                continue;
                } else {
@@ -208,6 +208,7 @@ static int kvmppc_mmu_book3s_32_xlate_pte(struct kvm_vcpu *vcpu, gva_t eaddr,
        u32 sre;
        hva_t ptegp;
        u32 pteg[16];
+       u32 pte0, pte1;
        u32 ptem = 0;
        int i;
        int found = 0;
@@ -233,14 +234,16 @@ static int kvmppc_mmu_book3s_32_xlate_pte(struct kvm_vcpu *vcpu, gva_t eaddr,
        }
 
        for (i=0; i<16; i+=2) {
-               if (ptem == pteg[i]) {
+               pte0 = be32_to_cpu(pteg[i]);
+               pte1 = be32_to_cpu(pteg[i + 1]);
+               if (ptem == pte0) {
                        u8 pp;
 
-                       pte->raddr = (pteg[i+1] & ~(0xFFFULL)) | (eaddr & 0xFFF);
-                       pp = pteg[i+1] & 3;
+                       pte->raddr = (pte1 & ~(0xFFFULL)) | (eaddr & 0xFFF);
+                       pp = pte1 & 3;
 
-                       if ((sr_kp(sre) &&  (vcpu->arch.shared->msr & MSR_PR)) ||
-                           (sr_ks(sre) && !(vcpu->arch.shared->msr & MSR_PR)))
+                       if ((sr_kp(sre) &&  (kvmppc_get_msr(vcpu) & MSR_PR)) ||
+                           (sr_ks(sre) && !(kvmppc_get_msr(vcpu) & MSR_PR)))
                                pp |= 4;
 
                        pte->may_write = false;
@@ -260,7 +263,7 @@ static int kvmppc_mmu_book3s_32_xlate_pte(struct kvm_vcpu *vcpu, gva_t eaddr,
                        }
 
                        dprintk_pte("MMU: Found PTE -> %x %x - %x\n",
-                                   pteg[i], pteg[i+1], pp);
+                                   pte0, pte1, pp);
                        found = 1;
                        break;
                }
@@ -269,8 +272,8 @@ static int kvmppc_mmu_book3s_32_xlate_pte(struct kvm_vcpu *vcpu, gva_t eaddr,
        /* Update PTE C and A bits, so the guest's swapper knows we used the
           page */
        if (found) {
-               u32 pte_r = pteg[i+1];
-               char __user *addr = (char __user *) &pteg[i+1];
+               u32 pte_r = pte1;
+               char __user *addr = (char __user *) (ptegp + (i+1) * sizeof(u32));
 
                /*
                 * Use single-byte writes to update the HPTE, to
@@ -296,7 +299,8 @@ no_page_found:
                            to_book3s(vcpu)->sdr1, ptegp);
                for (i=0; i<16; i+=2) {
                        dprintk_pte("   %02d: 0x%x - 0x%x (0x%x)\n",
-                                   i, pteg[i], pteg[i+1], ptem);
+                                   i, be32_to_cpu(pteg[i]),
+                                   be32_to_cpu(pteg[i+1]), ptem);
                }
        }
 
@@ -316,7 +320,7 @@ static int kvmppc_mmu_book3s_32_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
        /* Magic page override */
        if (unlikely(mp_ea) &&
            unlikely((eaddr & ~0xfffULL) == (mp_ea & ~0xfffULL)) &&
-           !(vcpu->arch.shared->msr & MSR_PR)) {
+           !(kvmppc_get_msr(vcpu) & MSR_PR)) {
                pte->vpage = kvmppc_mmu_book3s_32_ea_to_vp(vcpu, eaddr, data);
                pte->raddr = vcpu->arch.magic_page_pa | (pte->raddr & 0xfff);
                pte->raddr &= KVM_PAM;
@@ -341,13 +345,13 @@ static int kvmppc_mmu_book3s_32_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
 
 static u32 kvmppc_mmu_book3s_32_mfsrin(struct kvm_vcpu *vcpu, u32 srnum)
 {
-       return vcpu->arch.shared->sr[srnum];
+       return kvmppc_get_sr(vcpu, srnum);
 }
 
 static void kvmppc_mmu_book3s_32_mtsrin(struct kvm_vcpu *vcpu, u32 srnum,
                                        ulong value)
 {
-       vcpu->arch.shared->sr[srnum] = value;
+       kvmppc_set_sr(vcpu, srnum, value);
        kvmppc_mmu_map_segment(vcpu, srnum << SID_SHIFT);
 }
 
@@ -367,8 +371,9 @@ static int kvmppc_mmu_book3s_32_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,
        ulong ea = esid << SID_SHIFT;
        u32 sr;
        u64 gvsid = esid;
+       u64 msr = kvmppc_get_msr(vcpu);
 
-       if (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) {
+       if (msr & (MSR_DR|MSR_IR)) {
                sr = find_sr(vcpu, ea);
                if (sr_valid(sr))
                        gvsid = sr_vsid(sr);
@@ -377,7 +382,7 @@ static int kvmppc_mmu_book3s_32_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,
        /* In case we only have one of MSR_IR or MSR_DR set, let's put
           that in the real-mode context (and hope RM doesn't access
           high memory) */
-       switch (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) {
+       switch (msr & (MSR_DR|MSR_IR)) {
        case 0:
                *vsid = VSID_REAL | esid;
                break;
@@ -397,7 +402,7 @@ static int kvmppc_mmu_book3s_32_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,
                BUG();
        }
 
-       if (vcpu->arch.shared->msr & MSR_PR)
+       if (msr & MSR_PR)
                *vsid |= VSID_PR;
 
        return 0;
index 5fac89d..678e753 100644 (file)
@@ -92,7 +92,7 @@ static struct kvmppc_sid_map *find_sid_vsid(struct kvm_vcpu *vcpu, u64 gvsid)
        struct kvmppc_sid_map *map;
        u16 sid_map_mask;
 
-       if (vcpu->arch.shared->msr & MSR_PR)
+       if (kvmppc_get_msr(vcpu) & MSR_PR)
                gvsid |= VSID_PR;
 
        sid_map_mask = kvmppc_sid_hash(vcpu, gvsid);
@@ -279,7 +279,7 @@ static struct kvmppc_sid_map *create_sid_map(struct kvm_vcpu *vcpu, u64 gvsid)
        u16 sid_map_mask;
        static int backwards_map = 0;
 
-       if (vcpu->arch.shared->msr & MSR_PR)
+       if (kvmppc_get_msr(vcpu) & MSR_PR)
                gvsid |= VSID_PR;
 
        /* We might get collisions that trap in preceding order, so let's
index 83da1f8..774a253 100644 (file)
@@ -38,7 +38,7 @@
 
 static void kvmppc_mmu_book3s_64_reset_msr(struct kvm_vcpu *vcpu)
 {
-       kvmppc_set_msr(vcpu, MSR_SF);
+       kvmppc_set_msr(vcpu, vcpu->arch.intr_msr);
 }
 
 static struct kvmppc_slb *kvmppc_mmu_book3s_64_find_slbe(
@@ -226,7 +226,7 @@ static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
        /* Magic page override */
        if (unlikely(mp_ea) &&
            unlikely((eaddr & ~0xfffULL) == (mp_ea & ~0xfffULL)) &&
-           !(vcpu->arch.shared->msr & MSR_PR)) {
+           !(kvmppc_get_msr(vcpu) & MSR_PR)) {
                gpte->eaddr = eaddr;
                gpte->vpage = kvmppc_mmu_book3s_64_ea_to_vp(vcpu, eaddr, data);
                gpte->raddr = vcpu->arch.magic_page_pa | (gpte->raddr & 0xfff);
@@ -269,18 +269,21 @@ do_second:
                goto no_page_found;
        }
 
-       if ((vcpu->arch.shared->msr & MSR_PR) && slbe->Kp)
+       if ((kvmppc_get_msr(vcpu) & MSR_PR) && slbe->Kp)
                key = 4;
-       else if (!(vcpu->arch.shared->msr & MSR_PR) && slbe->Ks)
+       else if (!(kvmppc_get_msr(vcpu) & MSR_PR) && slbe->Ks)
                key = 4;
 
        for (i=0; i<16; i+=2) {
+               u64 pte0 = be64_to_cpu(pteg[i]);
+               u64 pte1 = be64_to_cpu(pteg[i + 1]);
+
                /* Check all relevant fields of 1st dword */
-               if ((pteg[i] & v_mask) == v_val) {
+               if ((pte0 & v_mask) == v_val) {
                        /* If large page bit is set, check pgsize encoding */
                        if (slbe->large &&
                            (vcpu->arch.hflags & BOOK3S_HFLAG_MULTI_PGSIZE)) {
-                               pgsize = decode_pagesize(slbe, pteg[i+1]);
+                               pgsize = decode_pagesize(slbe, pte1);
                                if (pgsize < 0)
                                        continue;
                        }
@@ -297,8 +300,8 @@ do_second:
                goto do_second;
        }
 
-       v = pteg[i];
-       r = pteg[i+1];
+       v = be64_to_cpu(pteg[i]);
+       r = be64_to_cpu(pteg[i+1]);
        pp = (r & HPTE_R_PP) | key;
        if (r & HPTE_R_PP0)
                pp |= 8;
@@ -310,6 +313,9 @@ do_second:
        gpte->raddr = (r & HPTE_R_RPN & ~eaddr_mask) | (eaddr & eaddr_mask);
        gpte->page_size = pgsize;
        gpte->may_execute = ((r & HPTE_R_N) ? false : true);
+       if (unlikely(vcpu->arch.disable_kernel_nx) &&
+           !(kvmppc_get_msr(vcpu) & MSR_PR))
+               gpte->may_execute = true;
        gpte->may_read = false;
        gpte->may_write = false;
 
@@ -342,14 +348,14 @@ do_second:
                 * non-PAPR platforms such as mac99, and this is
                 * what real hardware does.
                 */
-               char __user *addr = (char __user *) &pteg[i+1];
+                char __user *addr = (char __user *) (ptegp + (i + 1) * sizeof(u64));
                r |= HPTE_R_R;
                put_user(r >> 8, addr + 6);
        }
        if (iswrite && gpte->may_write && !(r & HPTE_R_C)) {
                /* Set the dirty flag */
                /* Use a single byte write */
-               char __user *addr = (char __user *) &pteg[i+1];
+                char __user *addr = (char __user *) (ptegp + (i + 1) * sizeof(u64));
                r |= HPTE_R_C;
                put_user(r, addr + 7);
        }
@@ -479,7 +485,7 @@ static void kvmppc_mmu_book3s_64_slbia(struct kvm_vcpu *vcpu)
                vcpu->arch.slb[i].origv = 0;
        }
 
-       if (vcpu->arch.shared->msr & MSR_IR) {
+       if (kvmppc_get_msr(vcpu) & MSR_IR) {
                kvmppc_mmu_flush_segments(vcpu);
                kvmppc_mmu_map_segment(vcpu, kvmppc_get_pc(vcpu));
        }
@@ -563,7 +569,7 @@ static int segment_contains_magic_page(struct kvm_vcpu *vcpu, ulong esid)
 {
        ulong mp_ea = vcpu->arch.magic_page_ea;
 
-       return mp_ea && !(vcpu->arch.shared->msr & MSR_PR) &&
+       return mp_ea && !(kvmppc_get_msr(vcpu) & MSR_PR) &&
                (mp_ea >> SID_SHIFT) == esid;
 }
 #endif
@@ -576,8 +582,9 @@ static int kvmppc_mmu_book3s_64_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,
        u64 gvsid = esid;
        ulong mp_ea = vcpu->arch.magic_page_ea;
        int pagesize = MMU_PAGE_64K;
+       u64 msr = kvmppc_get_msr(vcpu);
 
-       if (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) {
+       if (msr & (MSR_DR|MSR_IR)) {
                slb = kvmppc_mmu_book3s_64_find_slbe(vcpu, ea);
                if (slb) {
                        gvsid = slb->vsid;
@@ -590,7 +597,7 @@ static int kvmppc_mmu_book3s_64_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,
                }
        }
 
-       switch (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) {
+       switch (msr & (MSR_DR|MSR_IR)) {
        case 0:
                gvsid = VSID_REAL | esid;
                break;
@@ -623,7 +630,7 @@ static int kvmppc_mmu_book3s_64_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid,
                gvsid |= VSID_64K;
 #endif
 
-       if (vcpu->arch.shared->msr & MSR_PR)
+       if (kvmppc_get_msr(vcpu) & MSR_PR)
                gvsid |= VSID_PR;
 
        *vsid = gvsid;
@@ -633,7 +640,7 @@ no_slb:
        /* Catch magic page case */
        if (unlikely(mp_ea) &&
            unlikely(esid == (mp_ea >> SID_SHIFT)) &&
-           !(vcpu->arch.shared->msr & MSR_PR)) {
+           !(kvmppc_get_msr(vcpu) & MSR_PR)) {
                *vsid = VSID_REAL | esid;
                return 0;
        }
index 0d513af..0ac9839 100644 (file)
@@ -58,7 +58,7 @@ static struct kvmppc_sid_map *find_sid_vsid(struct kvm_vcpu *vcpu, u64 gvsid)
        struct kvmppc_sid_map *map;
        u16 sid_map_mask;
 
-       if (vcpu->arch.shared->msr & MSR_PR)
+       if (kvmppc_get_msr(vcpu) & MSR_PR)
                gvsid |= VSID_PR;
 
        sid_map_mask = kvmppc_sid_hash(vcpu, gvsid);
@@ -230,7 +230,7 @@ static struct kvmppc_sid_map *create_sid_map(struct kvm_vcpu *vcpu, u64 gvsid)
        u16 sid_map_mask;
        static int backwards_map = 0;
 
-       if (vcpu->arch.shared->msr & MSR_PR)
+       if (kvmppc_get_msr(vcpu) & MSR_PR)
                gvsid |= VSID_PR;
 
        /* We might get collisions that trap in preceding order, so let's
@@ -271,11 +271,8 @@ static int kvmppc_mmu_next_segment(struct kvm_vcpu *vcpu, ulong esid)
        int found_inval = -1;
        int r;
 
-       if (!svcpu->slb_max)
-               svcpu->slb_max = 1;
-
        /* Are we overwriting? */
-       for (i = 1; i < svcpu->slb_max; i++) {
+       for (i = 0; i < svcpu->slb_max; i++) {
                if (!(svcpu->slb[i].esid & SLB_ESID_V))
                        found_inval = i;
                else if ((svcpu->slb[i].esid & ESID_MASK) == esid) {
@@ -285,7 +282,7 @@ static int kvmppc_mmu_next_segment(struct kvm_vcpu *vcpu, ulong esid)
        }
 
        /* Found a spare entry that was invalidated before */
-       if (found_inval > 0) {
+       if (found_inval >= 0) {
                r = found_inval;
                goto out;
        }
@@ -359,7 +356,7 @@ void kvmppc_mmu_flush_segment(struct kvm_vcpu *vcpu, ulong ea, ulong seg_size)
        ulong seg_mask = -seg_size;
        int i;
 
-       for (i = 1; i < svcpu->slb_max; i++) {
+       for (i = 0; i < svcpu->slb_max; i++) {
                if ((svcpu->slb[i].esid & SLB_ESID_V) &&
                    (svcpu->slb[i].esid & seg_mask) == ea) {
                        /* Invalidate this entry */
@@ -373,7 +370,7 @@ void kvmppc_mmu_flush_segment(struct kvm_vcpu *vcpu, ulong ea, ulong seg_size)
 void kvmppc_mmu_flush_segments(struct kvm_vcpu *vcpu)
 {
        struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
-       svcpu->slb_max = 1;
+       svcpu->slb_max = 0;
        svcpu->slb[0].esid = 0;
        svcpu_put(svcpu);
 }
index fb25ebc..8056107 100644 (file)
@@ -52,7 +52,7 @@ static void kvmppc_rmap_reset(struct kvm *kvm);
 
 long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp)
 {
-       unsigned long hpt;
+       unsigned long hpt = 0;
        struct revmap_entry *rev;
        struct page *page = NULL;
        long order = KVM_DEFAULT_HPT_ORDER;
@@ -64,22 +64,11 @@ long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp)
        }
 
        kvm->arch.hpt_cma_alloc = 0;
-       /*
-        * try first to allocate it from the kernel page allocator.
-        * We keep the CMA reserved for failed allocation.
-        */
-       hpt = __get_free_pages(GFP_KERNEL | __GFP_ZERO | __GFP_REPEAT |
-                              __GFP_NOWARN, order - PAGE_SHIFT);
-
-       /* Next try to allocate from the preallocated pool */
-       if (!hpt) {
-               VM_BUG_ON(order < KVM_CMA_CHUNK_ORDER);
-               page = kvm_alloc_hpt(1 << (order - PAGE_SHIFT));
-               if (page) {
-                       hpt = (unsigned long)pfn_to_kaddr(page_to_pfn(page));
-                       kvm->arch.hpt_cma_alloc = 1;
-               } else
-                       --order;
+       VM_BUG_ON(order < KVM_CMA_CHUNK_ORDER);
+       page = kvm_alloc_hpt(1 << (order - PAGE_SHIFT));
+       if (page) {
+               hpt = (unsigned long)pfn_to_kaddr(page_to_pfn(page));
+               kvm->arch.hpt_cma_alloc = 1;
        }
 
        /* Lastly try successively smaller sizes from the page allocator */
@@ -596,6 +585,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
        struct kvm *kvm = vcpu->kvm;
        unsigned long *hptep, hpte[3], r;
        unsigned long mmu_seq, psize, pte_size;
+       unsigned long gpa_base, gfn_base;
        unsigned long gpa, gfn, hva, pfn;
        struct kvm_memory_slot *memslot;
        unsigned long *rmap;
@@ -634,7 +624,9 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
 
        /* Translate the logical address and get the page */
        psize = hpte_page_size(hpte[0], r);
-       gpa = (r & HPTE_R_RPN & ~(psize - 1)) | (ea & (psize - 1));
+       gpa_base = r & HPTE_R_RPN & ~(psize - 1);
+       gfn_base = gpa_base >> PAGE_SHIFT;
+       gpa = gpa_base | (ea & (psize - 1));
        gfn = gpa >> PAGE_SHIFT;
        memslot = gfn_to_memslot(kvm, gfn);
 
@@ -646,6 +638,13 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
        if (!kvm->arch.using_mmu_notifiers)
                return -EFAULT;         /* should never get here */
 
+       /*
+        * This should never happen, because of the slot_is_aligned()
+        * check in kvmppc_do_h_enter().
+        */
+       if (gfn_base < memslot->base_gfn)
+               return -EFAULT;
+
        /* used to check for invalidations in progress */
        mmu_seq = kvm->mmu_notifier_seq;
        smp_rmb();
@@ -738,7 +737,8 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
                goto out_unlock;
        hpte[0] = (hpte[0] & ~HPTE_V_ABSENT) | HPTE_V_VALID;
 
-       rmap = &memslot->arch.rmap[gfn - memslot->base_gfn];
+       /* Always put the HPTE in the rmap chain for the page base address */
+       rmap = &memslot->arch.rmap[gfn_base - memslot->base_gfn];
        lock_rmap(rmap);
 
        /* Check if we might have been invalidated; let the guest retry if so */
@@ -1060,22 +1060,33 @@ void kvm_set_spte_hva_hv(struct kvm *kvm, unsigned long hva, pte_t pte)
        kvm_handle_hva(kvm, hva, kvm_unmap_rmapp);
 }
 
-static int kvm_test_clear_dirty(struct kvm *kvm, unsigned long *rmapp)
+static int vcpus_running(struct kvm *kvm)
+{
+       return atomic_read(&kvm->arch.vcpus_running) != 0;
+}
+
+/*
+ * Returns the number of system pages that are dirty.
+ * This can be more than 1 if we find a huge-page HPTE.
+ */
+static int kvm_test_clear_dirty_npages(struct kvm *kvm, unsigned long *rmapp)
 {
        struct revmap_entry *rev = kvm->arch.revmap;
        unsigned long head, i, j;
+       unsigned long n;
+       unsigned long v, r;
        unsigned long *hptep;
-       int ret = 0;
+       int npages_dirty = 0;
 
  retry:
        lock_rmap(rmapp);
        if (*rmapp & KVMPPC_RMAP_CHANGED) {
                *rmapp &= ~KVMPPC_RMAP_CHANGED;
-               ret = 1;
+               npages_dirty = 1;
        }
        if (!(*rmapp & KVMPPC_RMAP_PRESENT)) {
                unlock_rmap(rmapp);
-               return ret;
+               return npages_dirty;
        }
 
        i = head = *rmapp & KVMPPC_RMAP_INDEX;
@@ -1083,7 +1094,22 @@ static int kvm_test_clear_dirty(struct kvm *kvm, unsigned long *rmapp)
                hptep = (unsigned long *) (kvm->arch.hpt_virt + (i << 4));
                j = rev[i].forw;
 
-               if (!(hptep[1] & HPTE_R_C))
+               /*
+                * Checking the C (changed) bit here is racy since there
+                * is no guarantee about when the hardware writes it back.
+                * If the HPTE is not writable then it is stable since the
+                * page can't be written to, and we would have done a tlbie
+                * (which forces the hardware to complete any writeback)
+                * when making the HPTE read-only.
+                * If vcpus are running then this call is racy anyway
+                * since the page could get dirtied subsequently, so we
+                * expect there to be a further call which would pick up
+                * any delayed C bit writeback.
+                * Otherwise we need to do the tlbie even if C==0 in
+                * order to pick up any delayed writeback of C.
+                */
+               if (!(hptep[1] & HPTE_R_C) &&
+                   (!hpte_is_writable(hptep[1]) || vcpus_running(kvm)))
                        continue;
 
                if (!try_lock_hpte(hptep, HPTE_V_HVLOCK)) {
@@ -1095,24 +1121,33 @@ static int kvm_test_clear_dirty(struct kvm *kvm, unsigned long *rmapp)
                }
 
                /* Now check and modify the HPTE */
-               if ((hptep[0] & HPTE_V_VALID) && (hptep[1] & HPTE_R_C)) {
-                       /* need to make it temporarily absent to clear C */
-                       hptep[0] |= HPTE_V_ABSENT;
-                       kvmppc_invalidate_hpte(kvm, hptep, i);
-                       hptep[1] &= ~HPTE_R_C;
-                       eieio();
-                       hptep[0] = (hptep[0] & ~HPTE_V_ABSENT) | HPTE_V_VALID;
+               if (!(hptep[0] & HPTE_V_VALID))
+                       continue;
+
+               /* need to make it temporarily absent so C is stable */
+               hptep[0] |= HPTE_V_ABSENT;
+               kvmppc_invalidate_hpte(kvm, hptep, i);
+               v = hptep[0];
+               r = hptep[1];
+               if (r & HPTE_R_C) {
+                       hptep[1] = r & ~HPTE_R_C;
                        if (!(rev[i].guest_rpte & HPTE_R_C)) {
                                rev[i].guest_rpte |= HPTE_R_C;
                                note_hpte_modification(kvm, &rev[i]);
                        }
-                       ret = 1;
+                       n = hpte_page_size(v, r);
+                       n = (n + PAGE_SIZE - 1) >> PAGE_SHIFT;
+                       if (n > npages_dirty)
+                               npages_dirty = n;
+                       eieio();
                }
-               hptep[0] &= ~HPTE_V_HVLOCK;
+               v &= ~(HPTE_V_ABSENT | HPTE_V_HVLOCK);
+               v |= HPTE_V_VALID;
+               hptep[0] = v;
        } while ((i = j) != head);
 
        unlock_rmap(rmapp);
-       return ret;
+       return npages_dirty;
 }
 
 static void harvest_vpa_dirty(struct kvmppc_vpa *vpa,
@@ -1136,15 +1171,22 @@ static void harvest_vpa_dirty(struct kvmppc_vpa *vpa,
 long kvmppc_hv_get_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot,
                             unsigned long *map)
 {
-       unsigned long i;
+       unsigned long i, j;
        unsigned long *rmapp;
        struct kvm_vcpu *vcpu;
 
        preempt_disable();
        rmapp = memslot->arch.rmap;
        for (i = 0; i < memslot->npages; ++i) {
-               if (kvm_test_clear_dirty(kvm, rmapp) && map)
-                       __set_bit_le(i, map);
+               int npages = kvm_test_clear_dirty_npages(kvm, rmapp);
+               /*
+                * Note that if npages > 0 then i must be a multiple of npages,
+                * since we always put huge-page HPTEs in the rmap chain
+                * corresponding to their page base address.
+                */
+               if (npages && map)
+                       for (j = i; npages; ++j, --npages)
+                               __set_bit_le(j, map);
                ++rmapp;
        }
 
index 4f12e8f..3589c4e 100644 (file)
  * Authors: Alexander Graf <agraf@suse.de>
  */
 
-#ifdef __LITTLE_ENDIAN__
-#error Need to fix SLB shadow accesses in little endian mode
-#endif
-
-#define SHADOW_SLB_ESID(num)   (SLBSHADOW_SAVEAREA + (num * 0x10))
-#define SHADOW_SLB_VSID(num)   (SLBSHADOW_SAVEAREA + (num * 0x10) + 0x8)
-#define UNBOLT_SLB_ENTRY(num) \
-       ld      r9, SHADOW_SLB_ESID(num)(r12); \
-       /* Invalid? Skip. */; \
-       rldicl. r0, r9, 37, 63; \
-       beq     slb_entry_skip_ ## num; \
-       xoris   r9, r9, SLB_ESID_V@h; \
-       std     r9, SHADOW_SLB_ESID(num)(r12); \
-  slb_entry_skip_ ## num:
-
-#define REBOLT_SLB_ENTRY(num) \
-       ld      r10, SHADOW_SLB_ESID(num)(r11); \
-       cmpdi   r10, 0; \
-       beq     slb_exit_skip_ ## num; \
-       oris    r10, r10, SLB_ESID_V@h; \
-       ld      r9, SHADOW_SLB_VSID(num)(r11); \
-       slbmte  r9, r10; \
-       std     r10, SHADOW_SLB_ESID(num)(r11); \
-slb_exit_skip_ ## num:
+#define SHADOW_SLB_ENTRY_LEN   0x10
+#define OFFSET_ESID(x)         (SHADOW_SLB_ENTRY_LEN * x)
+#define OFFSET_VSID(x)         ((SHADOW_SLB_ENTRY_LEN * x) + 8)
 
 /******************************************************************************
  *                                                                            *
@@ -64,20 +43,15 @@ slb_exit_skip_ ## num:
         * SVCPU[LR]  = guest LR
         */
 
-       /* Remove LPAR shadow entries */
+BEGIN_FW_FTR_SECTION
 
-#if SLB_NUM_BOLTED == 3
+       /* Declare SLB shadow as 0 entries big */
 
-       ld      r12, PACA_SLBSHADOWPTR(r13)
+       ld      r11, PACA_SLBSHADOWPTR(r13)
+       li      r8, 0
+       stb     r8, 3(r11)
 
-       /* Remove bolted entries */
-       UNBOLT_SLB_ENTRY(0)
-       UNBOLT_SLB_ENTRY(1)
-       UNBOLT_SLB_ENTRY(2)
-       
-#else
-#error unknown number of bolted entries
-#endif
+END_FW_FTR_SECTION_IFSET(FW_FEATURE_LPAR)
 
        /* Flush SLB */
 
@@ -100,7 +74,7 @@ slb_loop_enter:
 
        ld      r10, 0(r11)
 
-       rldicl. r0, r10, 37, 63
+       andis.  r9, r10, SLB_ESID_V@h
        beq     slb_loop_enter_skip
 
        ld      r9, 8(r11)
@@ -137,23 +111,42 @@ slb_do_enter:
         *
         */
 
-       /* Restore bolted entries from the shadow and fix it along the way */
+       /* Remove all SLB entries that are in use. */
 
-       /* We don't store anything in entry 0, so we don't need to take care of it */
+       li      r0, r0
+       slbmte  r0, r0
        slbia
-       isync
 
-#if SLB_NUM_BOLTED == 3
+       /* Restore bolted entries from the shadow */
 
        ld      r11, PACA_SLBSHADOWPTR(r13)
 
-       REBOLT_SLB_ENTRY(0)
-       REBOLT_SLB_ENTRY(1)
-       REBOLT_SLB_ENTRY(2)
-       
-#else
-#error unknown number of bolted entries
-#endif
+BEGIN_FW_FTR_SECTION
+
+       /* Declare SLB shadow as SLB_NUM_BOLTED entries big */
+
+       li      r8, SLB_NUM_BOLTED
+       stb     r8, 3(r11)
+
+END_FW_FTR_SECTION_IFSET(FW_FEATURE_LPAR)
+
+       /* Manually load all entries from shadow SLB */
+
+       li      r8, SLBSHADOW_SAVEAREA
+       li      r7, SLBSHADOW_SAVEAREA + 8
+
+       .rept   SLB_NUM_BOLTED
+       LDX_BE  r10, r11, r8
+       cmpdi   r10, 0
+       beq     1f
+       LDX_BE  r9, r11, r7
+       slbmte  r9, r10
+1:     addi    r7, r7, SHADOW_SLB_ENTRY_LEN
+       addi    r8, r8, SHADOW_SLB_ENTRY_LEN
+       .endr
+
+       isync
+       sync
 
 slb_do_exit:
 
index 99d40f8..3f29526 100644 (file)
@@ -80,7 +80,7 @@ static bool spr_allowed(struct kvm_vcpu *vcpu, enum priv_level level)
                return false;
 
        /* Limit user space to its own small SPR set */
-       if ((vcpu->arch.shared->msr & MSR_PR) && level > PRIV_PROBLEM)
+       if ((kvmppc_get_msr(vcpu) & MSR_PR) && level > PRIV_PROBLEM)
                return false;
 
        return true;
@@ -94,14 +94,31 @@ int kvmppc_core_emulate_op_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
        int rs = get_rs(inst);
        int ra = get_ra(inst);
        int rb = get_rb(inst);
+       u32 inst_sc = 0x44000002;
 
        switch (get_op(inst)) {
+       case 0:
+               emulated = EMULATE_FAIL;
+               if ((kvmppc_get_msr(vcpu) & MSR_LE) &&
+                   (inst == swab32(inst_sc))) {
+                       /*
+                        * This is the byte reversed syscall instruction of our
+                        * hypercall handler. Early versions of LE Linux didn't
+                        * swap the instructions correctly and ended up in
+                        * illegal instructions.
+                        * Just always fail hypercalls on these broken systems.
+                        */
+                       kvmppc_set_gpr(vcpu, 3, EV_UNIMPLEMENTED);
+                       kvmppc_set_pc(vcpu, kvmppc_get_pc(vcpu) + 4);
+                       emulated = EMULATE_DONE;
+               }
+               break;
        case 19:
                switch (get_xop(inst)) {
                case OP_19_XOP_RFID:
                case OP_19_XOP_RFI:
-                       kvmppc_set_pc(vcpu, vcpu->arch.shared->srr0);
-                       kvmppc_set_msr(vcpu, vcpu->arch.shared->srr1);
+                       kvmppc_set_pc(vcpu, kvmppc_get_srr0(vcpu));
+                       kvmppc_set_msr(vcpu, kvmppc_get_srr1(vcpu));
                        *advance = 0;
                        break;
 
@@ -113,16 +130,16 @@ int kvmppc_core_emulate_op_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
        case 31:
                switch (get_xop(inst)) {
                case OP_31_XOP_MFMSR:
-                       kvmppc_set_gpr(vcpu, rt, vcpu->arch.shared->msr);
+                       kvmppc_set_gpr(vcpu, rt, kvmppc_get_msr(vcpu));
                        break;
                case OP_31_XOP_MTMSRD:
                {
                        ulong rs_val = kvmppc_get_gpr(vcpu, rs);
                        if (inst & 0x10000) {
-                               ulong new_msr = vcpu->arch.shared->msr;
+                               ulong new_msr = kvmppc_get_msr(vcpu);
                                new_msr &= ~(MSR_RI | MSR_EE);
                                new_msr |= rs_val & (MSR_RI | MSR_EE);
-                               vcpu->arch.shared->msr = new_msr;
+                               kvmppc_set_msr_fast(vcpu, new_msr);
                        } else
                                kvmppc_set_msr(vcpu, rs_val);
                        break;
@@ -179,7 +196,7 @@ int kvmppc_core_emulate_op_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        ulong cmd = kvmppc_get_gpr(vcpu, 3);
                        int i;
 
-                       if ((vcpu->arch.shared->msr & MSR_PR) ||
+                       if ((kvmppc_get_msr(vcpu) & MSR_PR) ||
                            !vcpu->arch.papr_enabled) {
                                emulated = EMULATE_FAIL;
                                break;
@@ -261,14 +278,14 @@ int kvmppc_core_emulate_op_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
                                ra_val = kvmppc_get_gpr(vcpu, ra);
 
                        addr = (ra_val + rb_val) & ~31ULL;
-                       if (!(vcpu->arch.shared->msr & MSR_SF))
+                       if (!(kvmppc_get_msr(vcpu) & MSR_SF))
                                addr &= 0xffffffff;
                        vaddr = addr;
 
                        r = kvmppc_st(vcpu, &addr, 32, zeros, true);
                        if ((r == -ENOENT) || (r == -EPERM)) {
                                *advance = 0;
-                               vcpu->arch.shared->dar = vaddr;
+                               kvmppc_set_dar(vcpu, vaddr);
                                vcpu->arch.fault_dar = vaddr;
 
                                dsisr = DSISR_ISSTORE;
@@ -277,7 +294,7 @@ int kvmppc_core_emulate_op_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
                                else if (r == -EPERM)
                                        dsisr |= DSISR_PROTFAULT;
 
-                               vcpu->arch.shared->dsisr = dsisr;
+                               kvmppc_set_dsisr(vcpu, dsisr);
                                vcpu->arch.fault_dsisr = dsisr;
 
                                kvmppc_book3s_queue_irqprio(vcpu,
@@ -356,10 +373,10 @@ int kvmppc_core_emulate_mtspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
                to_book3s(vcpu)->sdr1 = spr_val;
                break;
        case SPRN_DSISR:
-               vcpu->arch.shared->dsisr = spr_val;
+               kvmppc_set_dsisr(vcpu, spr_val);
                break;
        case SPRN_DAR:
-               vcpu->arch.shared->dar = spr_val;
+               kvmppc_set_dar(vcpu, spr_val);
                break;
        case SPRN_HIOR:
                to_book3s(vcpu)->hior = spr_val;
@@ -438,6 +455,31 @@ int kvmppc_core_emulate_mtspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
        case SPRN_GQR7:
                to_book3s(vcpu)->gqr[sprn - SPRN_GQR0] = spr_val;
                break;
+       case SPRN_FSCR:
+               vcpu->arch.fscr = spr_val;
+               break;
+#ifdef CONFIG_PPC_BOOK3S_64
+       case SPRN_BESCR:
+               vcpu->arch.bescr = spr_val;
+               break;
+       case SPRN_EBBHR:
+               vcpu->arch.ebbhr = spr_val;
+               break;
+       case SPRN_EBBRR:
+               vcpu->arch.ebbrr = spr_val;
+               break;
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+       case SPRN_TFHAR:
+               vcpu->arch.tfhar = spr_val;
+               break;
+       case SPRN_TEXASR:
+               vcpu->arch.texasr = spr_val;
+               break;
+       case SPRN_TFIAR:
+               vcpu->arch.tfiar = spr_val;
+               break;
+#endif
+#endif
        case SPRN_ICTC:
        case SPRN_THRM1:
        case SPRN_THRM2:
@@ -455,6 +497,13 @@ int kvmppc_core_emulate_mtspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
        case SPRN_WPAR_GEKKO:
        case SPRN_MSSSR0:
        case SPRN_DABR:
+#ifdef CONFIG_PPC_BOOK3S_64
+       case SPRN_MMCRS:
+       case SPRN_MMCRA:
+       case SPRN_MMCR0:
+       case SPRN_MMCR1:
+       case SPRN_MMCR2:
+#endif
                break;
 unprivileged:
        default:
@@ -493,10 +542,10 @@ int kvmppc_core_emulate_mfspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val
                *spr_val = to_book3s(vcpu)->sdr1;
                break;
        case SPRN_DSISR:
-               *spr_val = vcpu->arch.shared->dsisr;
+               *spr_val = kvmppc_get_dsisr(vcpu);
                break;
        case SPRN_DAR:
-               *spr_val = vcpu->arch.shared->dar;
+               *spr_val = kvmppc_get_dar(vcpu);
                break;
        case SPRN_HIOR:
                *spr_val = to_book3s(vcpu)->hior;
@@ -538,6 +587,31 @@ int kvmppc_core_emulate_mfspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val
        case SPRN_GQR7:
                *spr_val = to_book3s(vcpu)->gqr[sprn - SPRN_GQR0];
                break;
+       case SPRN_FSCR:
+               *spr_val = vcpu->arch.fscr;
+               break;
+#ifdef CONFIG_PPC_BOOK3S_64
+       case SPRN_BESCR:
+               *spr_val = vcpu->arch.bescr;
+               break;
+       case SPRN_EBBHR:
+               *spr_val = vcpu->arch.ebbhr;
+               break;
+       case SPRN_EBBRR:
+               *spr_val = vcpu->arch.ebbrr;
+               break;
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+       case SPRN_TFHAR:
+               *spr_val = vcpu->arch.tfhar;
+               break;
+       case SPRN_TEXASR:
+               *spr_val = vcpu->arch.texasr;
+               break;
+       case SPRN_TFIAR:
+               *spr_val = vcpu->arch.tfiar;
+               break;
+#endif
+#endif
        case SPRN_THRM1:
        case SPRN_THRM2:
        case SPRN_THRM3:
@@ -553,6 +627,14 @@ int kvmppc_core_emulate_mfspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val
        case SPRN_WPAR_GEKKO:
        case SPRN_MSSSR0:
        case SPRN_DABR:
+#ifdef CONFIG_PPC_BOOK3S_64
+       case SPRN_MMCRS:
+       case SPRN_MMCRA:
+       case SPRN_MMCR0:
+       case SPRN_MMCR1:
+       case SPRN_MMCR2:
+       case SPRN_TIR:
+#endif
                *spr_val = 0;
                break;
        default:
@@ -569,48 +651,17 @@ unprivileged:
 
 u32 kvmppc_alignment_dsisr(struct kvm_vcpu *vcpu, unsigned int inst)
 {
-       u32 dsisr = 0;
-
-       /*
-        * This is what the spec says about DSISR bits (not mentioned = 0):
-        *
-        * 12:13                [DS]    Set to bits 30:31
-        * 15:16                [X]     Set to bits 29:30
-        * 17                   [X]     Set to bit 25
-        *                      [D/DS]  Set to bit 5
-        * 18:21                [X]     Set to bits 21:24
-        *                      [D/DS]  Set to bits 1:4
-        * 22:26                        Set to bits 6:10 (RT/RS/FRT/FRS)
-        * 27:31                        Set to bits 11:15 (RA)
-        */
-
-       switch (get_op(inst)) {
-       /* D-form */
-       case OP_LFS:
-       case OP_LFD:
-       case OP_STFD:
-       case OP_STFS:
-               dsisr |= (inst >> 12) & 0x4000; /* bit 17 */
-               dsisr |= (inst >> 17) & 0x3c00; /* bits 18:21 */
-               break;
-       /* X-form */
-       case 31:
-               dsisr |= (inst << 14) & 0x18000; /* bits 15:16 */
-               dsisr |= (inst << 8)  & 0x04000; /* bit 17 */
-               dsisr |= (inst << 3)  & 0x03c00; /* bits 18:21 */
-               break;
-       default:
-               printk(KERN_INFO "KVM: Unaligned instruction 0x%x\n", inst);
-               break;
-       }
-
-       dsisr |= (inst >> 16) & 0x03ff; /* bits 22:31 */
-
-       return dsisr;
+       return make_dsisr(inst);
 }
 
 ulong kvmppc_alignment_dar(struct kvm_vcpu *vcpu, unsigned int inst)
 {
+#ifdef CONFIG_PPC_BOOK3S_64
+       /*
+        * Linux's fix_alignment() assumes that DAR is valid, so can we
+        */
+       return vcpu->arch.fault_dar;
+#else
        ulong dar = 0;
        ulong ra = get_ra(inst);
        ulong rb = get_rb(inst);
@@ -635,4 +686,5 @@ ulong kvmppc_alignment_dar(struct kvm_vcpu *vcpu, unsigned int inst)
        }
 
        return dar;
+#endif
 }
index 20d4ea8..0d013fb 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <linux/export.h>
+#include <asm/kvm_ppc.h>
 #include <asm/kvm_book3s.h>
 
 #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
index 8227dba..aba05bb 100644 (file)
@@ -879,24 +879,9 @@ static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
        case KVM_REG_PPC_IAMR:
                *val = get_reg_val(id, vcpu->arch.iamr);
                break;
-       case KVM_REG_PPC_FSCR:
-               *val = get_reg_val(id, vcpu->arch.fscr);
-               break;
        case KVM_REG_PPC_PSPB:
                *val = get_reg_val(id, vcpu->arch.pspb);
                break;
-       case KVM_REG_PPC_EBBHR:
-               *val = get_reg_val(id, vcpu->arch.ebbhr);
-               break;
-       case KVM_REG_PPC_EBBRR:
-               *val = get_reg_val(id, vcpu->arch.ebbrr);
-               break;
-       case KVM_REG_PPC_BESCR:
-               *val = get_reg_val(id, vcpu->arch.bescr);
-               break;
-       case KVM_REG_PPC_TAR:
-               *val = get_reg_val(id, vcpu->arch.tar);
-               break;
        case KVM_REG_PPC_DPDES:
                *val = get_reg_val(id, vcpu->arch.vcore->dpdes);
                break;
@@ -1091,24 +1076,9 @@ static int kvmppc_set_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
        case KVM_REG_PPC_IAMR:
                vcpu->arch.iamr = set_reg_val(id, *val);
                break;
-       case KVM_REG_PPC_FSCR:
-               vcpu->arch.fscr = set_reg_val(id, *val);
-               break;
        case KVM_REG_PPC_PSPB:
                vcpu->arch.pspb = set_reg_val(id, *val);
                break;
-       case KVM_REG_PPC_EBBHR:
-               vcpu->arch.ebbhr = set_reg_val(id, *val);
-               break;
-       case KVM_REG_PPC_EBBRR:
-               vcpu->arch.ebbrr = set_reg_val(id, *val);
-               break;
-       case KVM_REG_PPC_BESCR:
-               vcpu->arch.bescr = set_reg_val(id, *val);
-               break;
-       case KVM_REG_PPC_TAR:
-               vcpu->arch.tar = set_reg_val(id, *val);
-               break;
        case KVM_REG_PPC_DPDES:
                vcpu->arch.vcore->dpdes = set_reg_val(id, *val);
                break;
@@ -1280,6 +1250,17 @@ static struct kvm_vcpu *kvmppc_core_vcpu_create_hv(struct kvm *kvm,
                goto free_vcpu;
 
        vcpu->arch.shared = &vcpu->arch.shregs;
+#ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
+       /*
+        * The shared struct is never shared on HV,
+        * so we can always use host endianness
+        */
+#ifdef __BIG_ENDIAN__
+       vcpu->arch.shared_big_endian = true;
+#else
+       vcpu->arch.shared_big_endian = false;
+#endif
+#endif
        vcpu->arch.mmcr[0] = MMCR0_FC;
        vcpu->arch.ctrl = CTRL_RUNLATCH;
        /* default to host PVR, since we can't spoof it */
@@ -1949,6 +1930,13 @@ static void kvmppc_add_seg_page_size(struct kvm_ppc_one_seg_page_size **sps,
         * support pte_enc here
         */
        (*sps)->enc[0].pte_enc = def->penc[linux_psize];
+       /*
+        * Add 16MB MPSS support if host supports it
+        */
+       if (linux_psize != MMU_PAGE_16M && def->penc[MMU_PAGE_16M] != -1) {
+               (*sps)->enc[1].page_shift = 24;
+               (*sps)->enc[1].pte_enc = def->penc[MMU_PAGE_16M];
+       }
        (*sps)++;
 }
 
index 8fcc363..6e62243 100644 (file)
@@ -42,13 +42,14 @@ static int global_invalidates(struct kvm *kvm, unsigned long flags)
 
        /*
         * If there is only one vcore, and it's currently running,
+        * as indicated by local_paca->kvm_hstate.kvm_vcpu being set,
         * we can use tlbiel as long as we mark all other physical
         * cores as potentially having stale TLB entries for this lpid.
         * If we're not using MMU notifiers, we never take pages away
         * from the guest, so we can use tlbiel if requested.
         * Otherwise, don't use tlbiel.
         */
-       if (kvm->arch.online_vcores == 1 && local_paca->kvm_hstate.kvm_vcore)
+       if (kvm->arch.online_vcores == 1 && local_paca->kvm_hstate.kvm_vcpu)
                global = 0;
        else if (kvm->arch.using_mmu_notifiers)
                global = 1;
index 07c8b5b..9747934 100644 (file)
@@ -86,6 +86,12 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
        lbz     r4, LPPACA_PMCINUSE(r3)
        cmpwi   r4, 0
        beq     23f                     /* skip if not */
+BEGIN_FTR_SECTION
+       ld      r3, HSTATE_MMCR(r13)
+       andi.   r4, r3, MMCR0_PMAO_SYNC | MMCR0_PMAO
+       cmpwi   r4, MMCR0_PMAO
+       beql    kvmppc_fix_pmao
+END_FTR_SECTION_IFSET(CPU_FTR_PMAO_BUG)
        lwz     r3, HSTATE_PMC(r13)
        lwz     r4, HSTATE_PMC + 4(r13)
        lwz     r5, HSTATE_PMC + 8(r13)
@@ -737,6 +743,12 @@ skip_tm:
        sldi    r3, r3, 31              /* MMCR0_FC (freeze counters) bit */
        mtspr   SPRN_MMCR0, r3          /* freeze all counters, disable ints */
        isync
+BEGIN_FTR_SECTION
+       ld      r3, VCPU_MMCR(r4)
+       andi.   r5, r3, MMCR0_PMAO_SYNC | MMCR0_PMAO
+       cmpwi   r5, MMCR0_PMAO
+       beql    kvmppc_fix_pmao
+END_FTR_SECTION_IFSET(CPU_FTR_PMAO_BUG)
        lwz     r3, VCPU_PMC(r4)        /* always load up guest PMU registers */
        lwz     r5, VCPU_PMC + 4(r4)    /* to prevent information leak */
        lwz     r6, VCPU_PMC + 8(r4)
@@ -1439,6 +1451,30 @@ END_FTR_SECTION_IFCLR(CPU_FTR_TM)
 25:
        /* Save PMU registers if requested */
        /* r8 and cr0.eq are live here */
+BEGIN_FTR_SECTION
+       /*
+        * POWER8 seems to have a hardware bug where setting
+        * MMCR0[PMAE] along with MMCR0[PMC1CE] and/or MMCR0[PMCjCE]
+        * when some counters are already negative doesn't seem
+        * to cause a performance monitor alert (and hence interrupt).
+        * The effect of this is that when saving the PMU state,
+        * if there is no PMU alert pending when we read MMCR0
+        * before freezing the counters, but one becomes pending
+        * before we read the counters, we lose it.
+        * To work around this, we need a way to freeze the counters
+        * before reading MMCR0.  Normally, freezing the counters
+        * is done by writing MMCR0 (to set MMCR0[FC]) which
+        * unavoidably writes MMCR0[PMA0] as well.  On POWER8,
+        * we can also freeze the counters using MMCR2, by writing
+        * 1s to all the counter freeze condition bits (there are
+        * 9 bits each for 6 counters).
+        */
+       li      r3, -1                  /* set all freeze bits */
+       clrrdi  r3, r3, 10
+       mfspr   r10, SPRN_MMCR2
+       mtspr   SPRN_MMCR2, r3
+       isync
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
        li      r3, 1
        sldi    r3, r3, 31              /* MMCR0_FC (freeze counters) bit */
        mfspr   r4, SPRN_MMCR0          /* save MMCR0 */
@@ -1462,6 +1498,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
        std     r4, VCPU_MMCR(r9)
        std     r5, VCPU_MMCR + 8(r9)
        std     r6, VCPU_MMCR + 16(r9)
+BEGIN_FTR_SECTION
+       std     r10, VCPU_MMCR + 24(r9)
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
        std     r7, VCPU_SIAR(r9)
        std     r8, VCPU_SDAR(r9)
        mfspr   r3, SPRN_PMC1
@@ -1485,12 +1524,10 @@ BEGIN_FTR_SECTION
        stw     r11, VCPU_PMC + 28(r9)
 END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
 BEGIN_FTR_SECTION
-       mfspr   r4, SPRN_MMCR2
        mfspr   r5, SPRN_SIER
        mfspr   r6, SPRN_SPMC1
        mfspr   r7, SPRN_SPMC2
        mfspr   r8, SPRN_MMCRS
-       std     r4, VCPU_MMCR + 24(r9)
        std     r5, VCPU_SIER(r9)
        stw     r6, VCPU_PMC + 24(r9)
        stw     r7, VCPU_PMC + 28(r9)
@@ -2227,6 +2264,7 @@ machine_check_realmode:
        beq     mc_cont
        /* If not, deliver a machine check.  SRR0/1 are already set */
        li      r10, BOOK3S_INTERRUPT_MACHINE_CHECK
+       ld      r11, VCPU_MSR(r9)
        bl      kvmppc_msr_interrupt
        b       fast_interrupt_c_return
 
@@ -2431,3 +2469,21 @@ kvmppc_msr_interrupt:
        li      r0, 1
 1:     rldimi  r11, r0, MSR_TS_S_LG, 63 - MSR_TS_T_LG
        blr
+
+/*
+ * This works around a hardware bug on POWER8E processors, where
+ * writing a 1 to the MMCR0[PMAO] bit doesn't generate a
+ * performance monitor interrupt.  Instead, when we need to have
+ * an interrupt pending, we have to arrange for a counter to overflow.
+ */
+kvmppc_fix_pmao:
+       li      r3, 0
+       mtspr   SPRN_MMCR2, r3
+       lis     r3, (MMCR0_PMXE | MMCR0_FCECE)@h
+       ori     r3, r3, MMCR0_PMCjCE | MMCR0_C56RUN
+       mtspr   SPRN_MMCR0, r3
+       lis     r3, 0x7fff
+       ori     r3, r3, 0xffff
+       mtspr   SPRN_PMC6, r3
+       isync
+       blr
index 3533c99..e2c29e3 100644 (file)
@@ -104,8 +104,27 @@ kvm_start_lightweight:
        stb     r3, HSTATE_RESTORE_HID5(r13)
 
        /* Load up guest SPRG3 value, since it's user readable */
-       ld      r3, VCPU_SHARED(r4)
-       ld      r3, VCPU_SHARED_SPRG3(r3)
+       lwz     r3, VCPU_SHAREDBE(r4)
+       cmpwi   r3, 0
+       ld      r5, VCPU_SHARED(r4)
+       beq     sprg3_little_endian
+sprg3_big_endian:
+#ifdef __BIG_ENDIAN__
+       ld      r3, VCPU_SHARED_SPRG3(r5)
+#else
+       addi    r5, r5, VCPU_SHARED_SPRG3
+       ldbrx   r3, 0, r5
+#endif
+       b       after_sprg3_load
+sprg3_little_endian:
+#ifdef __LITTLE_ENDIAN__
+       ld      r3, VCPU_SHARED_SPRG3(r5)
+#else
+       addi    r5, r5, VCPU_SHARED_SPRG3
+       ldbrx   r3, 0, r5
+#endif
+
+after_sprg3_load:
        mtspr   SPRN_SPRG3, r3
 #endif /* CONFIG_PPC_BOOK3S_64 */
 
index c1abd95..6c8011f 100644 (file)
@@ -165,16 +165,18 @@ static inline void kvmppc_sync_qpr(struct kvm_vcpu *vcpu, int rt)
 
 static void kvmppc_inject_pf(struct kvm_vcpu *vcpu, ulong eaddr, bool is_store)
 {
-       u64 dsisr;
-       struct kvm_vcpu_arch_shared *shared = vcpu->arch.shared;
+       u32 dsisr;
+       u64 msr = kvmppc_get_msr(vcpu);
 
-       shared->msr = kvmppc_set_field(shared->msr, 33, 36, 0);
-       shared->msr = kvmppc_set_field(shared->msr, 42, 47, 0);
-       shared->dar = eaddr;
+       msr = kvmppc_set_field(msr, 33, 36, 0);
+       msr = kvmppc_set_field(msr, 42, 47, 0);
+       kvmppc_set_msr(vcpu, msr);
+       kvmppc_set_dar(vcpu, eaddr);
        /* Page Fault */
        dsisr = kvmppc_set_field(0, 33, 33, 1);
        if (is_store)
-               shared->dsisr = kvmppc_set_field(dsisr, 38, 38, 1);
+               dsisr = kvmppc_set_field(dsisr, 38, 38, 1);
+       kvmppc_set_dsisr(vcpu, dsisr);
        kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_DATA_STORAGE);
 }
 
@@ -660,7 +662,7 @@ int kvmppc_emulate_paired_single(struct kvm_run *run, struct kvm_vcpu *vcpu)
        if (!kvmppc_inst_is_paired_single(vcpu, inst))
                return EMULATE_FAIL;
 
-       if (!(vcpu->arch.shared->msr & MSR_FP)) {
+       if (!(kvmppc_get_msr(vcpu) & MSR_FP)) {
                kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_FP_UNAVAIL);
                return EMULATE_AGAIN;
        }
index 02f1def..8eef1e5 100644 (file)
@@ -53,6 +53,7 @@
 
 static int kvmppc_handle_ext(struct kvm_vcpu *vcpu, unsigned int exit_nr,
                             ulong msr);
+static void kvmppc_giveup_fac(struct kvm_vcpu *vcpu, ulong fac);
 
 /* Some compatibility defines */
 #ifdef CONFIG_PPC_BOOK3S_32
@@ -89,6 +90,7 @@ static void kvmppc_core_vcpu_put_pr(struct kvm_vcpu *vcpu)
 #endif
 
        kvmppc_giveup_ext(vcpu, MSR_FP | MSR_VEC | MSR_VSX);
+       kvmppc_giveup_fac(vcpu, FSCR_TAR_LG);
        vcpu->cpu = -1;
 }
 
@@ -115,6 +117,9 @@ void kvmppc_copy_to_svcpu(struct kvmppc_book3s_shadow_vcpu *svcpu,
        svcpu->ctr = vcpu->arch.ctr;
        svcpu->lr  = vcpu->arch.lr;
        svcpu->pc  = vcpu->arch.pc;
+#ifdef CONFIG_PPC_BOOK3S_64
+       svcpu->shadow_fscr = vcpu->arch.shadow_fscr;
+#endif
        svcpu->in_use = true;
 }
 
@@ -158,6 +163,9 @@ void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu,
        vcpu->arch.fault_dar   = svcpu->fault_dar;
        vcpu->arch.fault_dsisr = svcpu->fault_dsisr;
        vcpu->arch.last_inst   = svcpu->last_inst;
+#ifdef CONFIG_PPC_BOOK3S_64
+       vcpu->arch.shadow_fscr = svcpu->shadow_fscr;
+#endif
        svcpu->in_use = false;
 
 out:
@@ -246,14 +254,15 @@ static void kvm_set_spte_hva_pr(struct kvm *kvm, unsigned long hva, pte_t pte)
 
 static void kvmppc_recalc_shadow_msr(struct kvm_vcpu *vcpu)
 {
-       ulong smsr = vcpu->arch.shared->msr;
+       ulong guest_msr = kvmppc_get_msr(vcpu);
+       ulong smsr = guest_msr;
 
        /* Guest MSR values */
-       smsr &= MSR_FE0 | MSR_FE1 | MSR_SF | MSR_SE | MSR_BE;
+       smsr &= MSR_FE0 | MSR_FE1 | MSR_SF | MSR_SE | MSR_BE | MSR_LE;
        /* Process MSR values */
        smsr |= MSR_ME | MSR_RI | MSR_IR | MSR_DR | MSR_PR | MSR_EE;
        /* External providers the guest reserved */
-       smsr |= (vcpu->arch.shared->msr & vcpu->arch.guest_owned_ext);
+       smsr |= (guest_msr & vcpu->arch.guest_owned_ext);
        /* 64-bit Process MSR values */
 #ifdef CONFIG_PPC_BOOK3S_64
        smsr |= MSR_ISF | MSR_HV;
@@ -263,14 +272,14 @@ static void kvmppc_recalc_shadow_msr(struct kvm_vcpu *vcpu)
 
 static void kvmppc_set_msr_pr(struct kvm_vcpu *vcpu, u64 msr)
 {
-       ulong old_msr = vcpu->arch.shared->msr;
+       ulong old_msr = kvmppc_get_msr(vcpu);
 
 #ifdef EXIT_DEBUG
        printk(KERN_INFO "KVM: Set MSR to 0x%llx\n", msr);
 #endif
 
        msr &= to_book3s(vcpu)->msr_mask;
-       vcpu->arch.shared->msr = msr;
+       kvmppc_set_msr_fast(vcpu, msr);
        kvmppc_recalc_shadow_msr(vcpu);
 
        if (msr & MSR_POW) {
@@ -281,11 +290,11 @@ static void kvmppc_set_msr_pr(struct kvm_vcpu *vcpu, u64 msr)
 
                        /* Unset POW bit after we woke up */
                        msr &= ~MSR_POW;
-                       vcpu->arch.shared->msr = msr;
+                       kvmppc_set_msr_fast(vcpu, msr);
                }
        }
 
-       if ((vcpu->arch.shared->msr & (MSR_PR|MSR_IR|MSR_DR)) !=
+       if ((kvmppc_get_msr(vcpu) & (MSR_PR|MSR_IR|MSR_DR)) !=
                   (old_msr & (MSR_PR|MSR_IR|MSR_DR))) {
                kvmppc_mmu_flush_segments(vcpu);
                kvmppc_mmu_map_segment(vcpu, kvmppc_get_pc(vcpu));
@@ -317,7 +326,7 @@ static void kvmppc_set_msr_pr(struct kvm_vcpu *vcpu, u64 msr)
        }
 
        /* Preload FPU if it's enabled */
-       if (vcpu->arch.shared->msr & MSR_FP)
+       if (kvmppc_get_msr(vcpu) & MSR_FP)
                kvmppc_handle_ext(vcpu, BOOK3S_INTERRUPT_FP_UNAVAIL, MSR_FP);
 }
 
@@ -427,8 +436,8 @@ static void kvmppc_patch_dcbz(struct kvm_vcpu *vcpu, struct kvmppc_pte *pte)
 
        /* patch dcbz into reserved instruction, so we trap */
        for (i=hpage_offset; i < hpage_offset + (HW_PAGE_SIZE / 4); i++)
-               if ((page[i] & 0xff0007ff) == INS_DCBZ)
-                       page[i] &= 0xfffffff7;
+               if ((be32_to_cpu(page[i]) & 0xff0007ff) == INS_DCBZ)
+                       page[i] &= cpu_to_be32(0xfffffff7);
 
        kunmap_atomic(page);
        put_page(hpage);
@@ -438,7 +447,7 @@ static int kvmppc_visible_gfn(struct kvm_vcpu *vcpu, gfn_t gfn)
 {
        ulong mp_pa = vcpu->arch.magic_page_pa;
 
-       if (!(vcpu->arch.shared->msr & MSR_SF))
+       if (!(kvmppc_get_msr(vcpu) & MSR_SF))
                mp_pa = (uint32_t)mp_pa;
 
        if (unlikely(mp_pa) &&
@@ -459,8 +468,8 @@ int kvmppc_handle_pagefault(struct kvm_run *run, struct kvm_vcpu *vcpu,
        int page_found = 0;
        struct kvmppc_pte pte;
        bool is_mmio = false;
-       bool dr = (vcpu->arch.shared->msr & MSR_DR) ? true : false;
-       bool ir = (vcpu->arch.shared->msr & MSR_IR) ? true : false;
+       bool dr = (kvmppc_get_msr(vcpu) & MSR_DR) ? true : false;
+       bool ir = (kvmppc_get_msr(vcpu) & MSR_IR) ? true : false;
        u64 vsid;
 
        relocated = data ? dr : ir;
@@ -480,7 +489,7 @@ int kvmppc_handle_pagefault(struct kvm_run *run, struct kvm_vcpu *vcpu,
                pte.page_size = MMU_PAGE_64K;
        }
 
-       switch (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) {
+       switch (kvmppc_get_msr(vcpu) & (MSR_DR|MSR_IR)) {
        case 0:
                pte.vpage |= ((u64)VSID_REAL << (SID_SHIFT - 12));
                break;
@@ -488,7 +497,7 @@ int kvmppc_handle_pagefault(struct kvm_run *run, struct kvm_vcpu *vcpu,
        case MSR_IR:
                vcpu->arch.mmu.esid_to_vsid(vcpu, eaddr >> SID_SHIFT, &vsid);
 
-               if ((vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) == MSR_DR)
+               if ((kvmppc_get_msr(vcpu) & (MSR_DR|MSR_IR)) == MSR_DR)
                        pte.vpage |= ((u64)VSID_REAL_DR << (SID_SHIFT - 12));
                else
                        pte.vpage |= ((u64)VSID_REAL_IR << (SID_SHIFT - 12));
@@ -511,22 +520,25 @@ int kvmppc_handle_pagefault(struct kvm_run *run, struct kvm_vcpu *vcpu,
 
        if (page_found == -ENOENT) {
                /* Page not found in guest PTE entries */
-               vcpu->arch.shared->dar = kvmppc_get_fault_dar(vcpu);
-               vcpu->arch.shared->dsisr = vcpu->arch.fault_dsisr;
-               vcpu->arch.shared->msr |=
-                       vcpu->arch.shadow_srr1 & 0x00000000f8000000ULL;
+               u64 ssrr1 = vcpu->arch.shadow_srr1;
+               u64 msr = kvmppc_get_msr(vcpu);
+               kvmppc_set_dar(vcpu, kvmppc_get_fault_dar(vcpu));
+               kvmppc_set_dsisr(vcpu, vcpu->arch.fault_dsisr);
+               kvmppc_set_msr_fast(vcpu, msr | (ssrr1 & 0xf8000000ULL));
                kvmppc_book3s_queue_irqprio(vcpu, vec);
        } else if (page_found == -EPERM) {
                /* Storage protection */
-               vcpu->arch.shared->dar = kvmppc_get_fault_dar(vcpu);
-               vcpu->arch.shared->dsisr = vcpu->arch.fault_dsisr & ~DSISR_NOHPTE;
-               vcpu->arch.shared->dsisr |= DSISR_PROTFAULT;
-               vcpu->arch.shared->msr |=
-                       vcpu->arch.shadow_srr1 & 0x00000000f8000000ULL;
+               u32 dsisr = vcpu->arch.fault_dsisr;
+               u64 ssrr1 = vcpu->arch.shadow_srr1;
+               u64 msr = kvmppc_get_msr(vcpu);
+               kvmppc_set_dar(vcpu, kvmppc_get_fault_dar(vcpu));
+               dsisr = (dsisr & ~DSISR_NOHPTE) | DSISR_PROTFAULT;
+               kvmppc_set_dsisr(vcpu, dsisr);
+               kvmppc_set_msr_fast(vcpu, msr | (ssrr1 & 0xf8000000ULL));
                kvmppc_book3s_queue_irqprio(vcpu, vec);
        } else if (page_found == -EINVAL) {
                /* Page not found in guest SLB */
-               vcpu->arch.shared->dar = kvmppc_get_fault_dar(vcpu);
+               kvmppc_set_dar(vcpu, kvmppc_get_fault_dar(vcpu));
                kvmppc_book3s_queue_irqprio(vcpu, vec + 0x80);
        } else if (!is_mmio &&
                   kvmppc_visible_gfn(vcpu, pte.raddr >> PAGE_SHIFT)) {
@@ -606,6 +618,25 @@ void kvmppc_giveup_ext(struct kvm_vcpu *vcpu, ulong msr)
        kvmppc_recalc_shadow_msr(vcpu);
 }
 
+/* Give up facility (TAR / EBB / DSCR) */
+static void kvmppc_giveup_fac(struct kvm_vcpu *vcpu, ulong fac)
+{
+#ifdef CONFIG_PPC_BOOK3S_64
+       if (!(vcpu->arch.shadow_fscr & (1ULL << fac))) {
+               /* Facility not available to the guest, ignore giveup request*/
+               return;
+       }
+
+       switch (fac) {
+       case FSCR_TAR_LG:
+               vcpu->arch.tar = mfspr(SPRN_TAR);
+               mtspr(SPRN_TAR, current->thread.tar);
+               vcpu->arch.shadow_fscr &= ~FSCR_TAR;
+               break;
+       }
+#endif
+}
+
 static int kvmppc_read_inst(struct kvm_vcpu *vcpu)
 {
        ulong srr0 = kvmppc_get_pc(vcpu);
@@ -614,11 +645,12 @@ static int kvmppc_read_inst(struct kvm_vcpu *vcpu)
 
        ret = kvmppc_ld(vcpu, &srr0, sizeof(u32), &last_inst, false);
        if (ret == -ENOENT) {
-               ulong msr = vcpu->arch.shared->msr;
+               ulong msr = kvmppc_get_msr(vcpu);
 
                msr = kvmppc_set_field(msr, 33, 33, 1);
                msr = kvmppc_set_field(msr, 34, 36, 0);
-               vcpu->arch.shared->msr = kvmppc_set_field(msr, 42, 47, 0);
+               msr = kvmppc_set_field(msr, 42, 47, 0);
+               kvmppc_set_msr_fast(vcpu, msr);
                kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_INST_STORAGE);
                return EMULATE_AGAIN;
        }
@@ -651,7 +683,7 @@ static int kvmppc_handle_ext(struct kvm_vcpu *vcpu, unsigned int exit_nr,
        if (vcpu->arch.hflags & BOOK3S_HFLAG_PAIRED_SINGLE)
                return RESUME_GUEST;
 
-       if (!(vcpu->arch.shared->msr & msr)) {
+       if (!(kvmppc_get_msr(vcpu) & msr)) {
                kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
                return RESUME_GUEST;
        }
@@ -683,16 +715,20 @@ static int kvmppc_handle_ext(struct kvm_vcpu *vcpu, unsigned int exit_nr,
 #endif
 
        if (msr & MSR_FP) {
+               preempt_disable();
                enable_kernel_fp();
                load_fp_state(&vcpu->arch.fp);
                t->fp_save_area = &vcpu->arch.fp;
+               preempt_enable();
        }
 
        if (msr & MSR_VEC) {
 #ifdef CONFIG_ALTIVEC
+               preempt_disable();
                enable_kernel_altivec();
                load_vr_state(&vcpu->arch.vr);
                t->vr_save_area = &vcpu->arch.vr;
+               preempt_enable();
 #endif
        }
 
@@ -716,18 +752,90 @@ static void kvmppc_handle_lost_ext(struct kvm_vcpu *vcpu)
                return;
 
        if (lost_ext & MSR_FP) {
+               preempt_disable();
                enable_kernel_fp();
                load_fp_state(&vcpu->arch.fp);
+               preempt_enable();
        }
 #ifdef CONFIG_ALTIVEC
        if (lost_ext & MSR_VEC) {
+               preempt_disable();
                enable_kernel_altivec();
                load_vr_state(&vcpu->arch.vr);
+               preempt_enable();
        }
 #endif
        current->thread.regs->msr |= lost_ext;
 }
 
+#ifdef CONFIG_PPC_BOOK3S_64
+
+static void kvmppc_trigger_fac_interrupt(struct kvm_vcpu *vcpu, ulong fac)
+{
+       /* Inject the Interrupt Cause field and trigger a guest interrupt */
+       vcpu->arch.fscr &= ~(0xffULL << 56);
+       vcpu->arch.fscr |= (fac << 56);
+       kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_FAC_UNAVAIL);
+}
+
+static void kvmppc_emulate_fac(struct kvm_vcpu *vcpu, ulong fac)
+{
+       enum emulation_result er = EMULATE_FAIL;
+
+       if (!(kvmppc_get_msr(vcpu) & MSR_PR))
+               er = kvmppc_emulate_instruction(vcpu->run, vcpu);
+
+       if ((er != EMULATE_DONE) && (er != EMULATE_AGAIN)) {
+               /* Couldn't emulate, trigger interrupt in guest */
+               kvmppc_trigger_fac_interrupt(vcpu, fac);
+       }
+}
+
+/* Enable facilities (TAR, EBB, DSCR) for the guest */
+static int kvmppc_handle_fac(struct kvm_vcpu *vcpu, ulong fac)
+{
+       bool guest_fac_enabled;
+       BUG_ON(!cpu_has_feature(CPU_FTR_ARCH_207S));
+
+       /*
+        * Not every facility is enabled by FSCR bits, check whether the
+        * guest has this facility enabled at all.
+        */
+       switch (fac) {
+       case FSCR_TAR_LG:
+       case FSCR_EBB_LG:
+               guest_fac_enabled = (vcpu->arch.fscr & (1ULL << fac));
+               break;
+       case FSCR_TM_LG:
+               guest_fac_enabled = kvmppc_get_msr(vcpu) & MSR_TM;
+               break;
+       default:
+               guest_fac_enabled = false;
+               break;
+       }
+
+       if (!guest_fac_enabled) {
+               /* Facility not enabled by the guest */
+               kvmppc_trigger_fac_interrupt(vcpu, fac);
+               return RESUME_GUEST;
+       }
+
+       switch (fac) {
+       case FSCR_TAR_LG:
+               /* TAR switching isn't lazy in Linux yet */
+               current->thread.tar = mfspr(SPRN_TAR);
+               mtspr(SPRN_TAR, vcpu->arch.tar);
+               vcpu->arch.shadow_fscr |= FSCR_TAR;
+               break;
+       default:
+               kvmppc_emulate_fac(vcpu, fac);
+               break;
+       }
+
+       return RESUME_GUEST;
+}
+#endif
+
 int kvmppc_handle_exit_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
                          unsigned int exit_nr)
 {
@@ -784,7 +892,9 @@ int kvmppc_handle_exit_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        kvmppc_mmu_pte_flush(vcpu, kvmppc_get_pc(vcpu), ~0xFFFUL);
                        r = RESUME_GUEST;
                } else {
-                       vcpu->arch.shared->msr |= shadow_srr1 & 0x58000000;
+                       u64 msr = kvmppc_get_msr(vcpu);
+                       msr |= shadow_srr1 & 0x58000000;
+                       kvmppc_set_msr_fast(vcpu, msr);
                        kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
                        r = RESUME_GUEST;
                }
@@ -824,8 +934,8 @@ int kvmppc_handle_exit_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        r = kvmppc_handle_pagefault(run, vcpu, dar, exit_nr);
                        srcu_read_unlock(&vcpu->kvm->srcu, idx);
                } else {
-                       vcpu->arch.shared->dar = dar;
-                       vcpu->arch.shared->dsisr = fault_dsisr;
+                       kvmppc_set_dar(vcpu, dar);
+                       kvmppc_set_dsisr(vcpu, fault_dsisr);
                        kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
                        r = RESUME_GUEST;
                }
@@ -833,7 +943,7 @@ int kvmppc_handle_exit_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
        }
        case BOOK3S_INTERRUPT_DATA_SEGMENT:
                if (kvmppc_mmu_map_segment(vcpu, kvmppc_get_fault_dar(vcpu)) < 0) {
-                       vcpu->arch.shared->dar = kvmppc_get_fault_dar(vcpu);
+                       kvmppc_set_dar(vcpu, kvmppc_get_fault_dar(vcpu));
                        kvmppc_book3s_queue_irqprio(vcpu,
                                BOOK3S_INTERRUPT_DATA_SEGMENT);
                }
@@ -871,7 +981,7 @@ int kvmppc_handle_exit_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
 program_interrupt:
                flags = vcpu->arch.shadow_srr1 & 0x1f0000ull;
 
-               if (vcpu->arch.shared->msr & MSR_PR) {
+               if (kvmppc_get_msr(vcpu) & MSR_PR) {
 #ifdef EXIT_DEBUG
                        printk(KERN_INFO "Userspace triggered 0x700 exception at 0x%lx (0x%x)\n", kvmppc_get_pc(vcpu), kvmppc_get_last_inst(vcpu));
 #endif
@@ -913,7 +1023,7 @@ program_interrupt:
        case BOOK3S_INTERRUPT_SYSCALL:
                if (vcpu->arch.papr_enabled &&
                    (kvmppc_get_last_sc(vcpu) == 0x44000022) &&
-                   !(vcpu->arch.shared->msr & MSR_PR)) {
+                   !(kvmppc_get_msr(vcpu) & MSR_PR)) {
                        /* SC 1 papr hypercalls */
                        ulong cmd = kvmppc_get_gpr(vcpu, 3);
                        int i;
@@ -945,7 +1055,7 @@ program_interrupt:
                                gprs[i] = kvmppc_get_gpr(vcpu, i);
                        vcpu->arch.osi_needed = 1;
                        r = RESUME_HOST_NV;
-               } else if (!(vcpu->arch.shared->msr & MSR_PR) &&
+               } else if (!(kvmppc_get_msr(vcpu) & MSR_PR) &&
                    (((u32)kvmppc_get_gpr(vcpu, 0)) == KVM_SC_MAGIC_R0)) {
                        /* KVM PV hypercalls */
                        kvmppc_set_gpr(vcpu, 3, kvmppc_kvm_pv(vcpu));
@@ -986,14 +1096,26 @@ program_interrupt:
        }
        case BOOK3S_INTERRUPT_ALIGNMENT:
                if (kvmppc_read_inst(vcpu) == EMULATE_DONE) {
-                       vcpu->arch.shared->dsisr = kvmppc_alignment_dsisr(vcpu,
-                               kvmppc_get_last_inst(vcpu));
-                       vcpu->arch.shared->dar = kvmppc_alignment_dar(vcpu,
-                               kvmppc_get_last_inst(vcpu));
+                       u32 last_inst = kvmppc_get_last_inst(vcpu);
+                       u32 dsisr;
+                       u64 dar;
+
+                       dsisr = kvmppc_alignment_dsisr(vcpu, last_inst);
+                       dar = kvmppc_alignment_dar(vcpu, last_inst);
+
+                       kvmppc_set_dsisr(vcpu, dsisr);
+                       kvmppc_set_dar(vcpu, dar);
+
                        kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
                }
                r = RESUME_GUEST;
                break;
+#ifdef CONFIG_PPC_BOOK3S_64
+       case BOOK3S_INTERRUPT_FAC_UNAVAIL:
+               kvmppc_handle_fac(vcpu, vcpu->arch.shadow_fscr >> 56);
+               r = RESUME_GUEST;
+               break;
+#endif
        case BOOK3S_INTERRUPT_MACHINE_CHECK:
        case BOOK3S_INTERRUPT_TRACE:
                kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
@@ -1054,7 +1176,7 @@ static int kvm_arch_vcpu_ioctl_get_sregs_pr(struct kvm_vcpu *vcpu,
                }
        } else {
                for (i = 0; i < 16; i++)
-                       sregs->u.s.ppc32.sr[i] = vcpu->arch.shared->sr[i];
+                       sregs->u.s.ppc32.sr[i] = kvmppc_get_sr(vcpu, i);
 
                for (i = 0; i < 8; i++) {
                        sregs->u.s.ppc32.ibat[i] = vcpu3s->ibat[i].raw;
@@ -1110,6 +1232,15 @@ static int kvmppc_get_one_reg_pr(struct kvm_vcpu *vcpu, u64 id,
        case KVM_REG_PPC_HIOR:
                *val = get_reg_val(id, to_book3s(vcpu)->hior);
                break;
+       case KVM_REG_PPC_LPCR:
+               /*
+                * We are only interested in the LPCR_ILE bit
+                */
+               if (vcpu->arch.intr_msr & MSR_LE)
+                       *val = get_reg_val(id, LPCR_ILE);
+               else
+                       *val = get_reg_val(id, 0);
+               break;
        default:
                r = -EINVAL;
                break;
@@ -1118,6 +1249,14 @@ static int kvmppc_get_one_reg_pr(struct kvm_vcpu *vcpu, u64 id,
        return r;
 }
 
+static void kvmppc_set_lpcr_pr(struct kvm_vcpu *vcpu, u64 new_lpcr)
+{
+       if (new_lpcr & LPCR_ILE)
+               vcpu->arch.intr_msr |= MSR_LE;
+       else
+               vcpu->arch.intr_msr &= ~MSR_LE;
+}
+
 static int kvmppc_set_one_reg_pr(struct kvm_vcpu *vcpu, u64 id,
                                 union kvmppc_one_reg *val)
 {
@@ -1128,6 +1267,9 @@ static int kvmppc_set_one_reg_pr(struct kvm_vcpu *vcpu, u64 id,
                to_book3s(vcpu)->hior = set_reg_val(id, *val);
                to_book3s(vcpu)->hior_explicit = true;
                break;
+       case KVM_REG_PPC_LPCR:
+               kvmppc_set_lpcr_pr(vcpu, set_reg_val(id, *val));
+               break;
        default:
                r = -EINVAL;
                break;
@@ -1170,8 +1312,14 @@ static struct kvm_vcpu *kvmppc_core_vcpu_create_pr(struct kvm *kvm,
                goto uninit_vcpu;
        /* the real shared page fills the last 4k of our page */
        vcpu->arch.shared = (void *)(p + PAGE_SIZE - 4096);
-
 #ifdef CONFIG_PPC_BOOK3S_64
+       /* Always start the shared struct in native endian mode */
+#ifdef __BIG_ENDIAN__
+        vcpu->arch.shared_big_endian = true;
+#else
+        vcpu->arch.shared_big_endian = false;
+#endif
+
        /*
         * Default to the same as the host if we're on sufficiently
         * recent machine that we have 1TB segments;
@@ -1180,6 +1328,7 @@ static struct kvm_vcpu *kvmppc_core_vcpu_create_pr(struct kvm *kvm,
        vcpu->arch.pvr = 0x3C0301;
        if (mmu_has_feature(MMU_FTR_1T_SEGMENT))
                vcpu->arch.pvr = mfspr(SPRN_PVR);
+       vcpu->arch.intr_msr = MSR_SF;
 #else
        /* default to book3s_32 (750) */
        vcpu->arch.pvr = 0x84202;
@@ -1187,7 +1336,7 @@ static struct kvm_vcpu *kvmppc_core_vcpu_create_pr(struct kvm *kvm,
        kvmppc_set_pvr_pr(vcpu, vcpu->arch.pvr);
        vcpu->arch.slb_nr = 64;
 
-       vcpu->arch.shadow_msr = MSR_USER64;
+       vcpu->arch.shadow_msr = MSR_USER64 & ~MSR_LE;
 
        err = kvmppc_mmu_init(vcpu);
        if (err < 0)
@@ -1264,7 +1413,7 @@ static int kvmppc_vcpu_run_pr(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
 #endif
 
        /* Preload FPU if it's enabled */
-       if (vcpu->arch.shared->msr & MSR_FP)
+       if (kvmppc_get_msr(vcpu) & MSR_FP)
                kvmppc_handle_ext(vcpu, BOOK3S_INTERRUPT_FP_UNAVAIL, MSR_FP);
 
        kvmppc_fix_ee_before_entry();
@@ -1277,6 +1426,9 @@ static int kvmppc_vcpu_run_pr(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
        /* Make sure we save the guest FPU/Altivec/VSX state */
        kvmppc_giveup_ext(vcpu, MSR_FP | MSR_VEC | MSR_VSX);
 
+       /* Make sure we save the guest TAR/EBB/DSCR state */
+       kvmppc_giveup_fac(vcpu, FSCR_TAR_LG);
+
 out:
        vcpu->mode = OUTSIDE_GUEST_MODE;
        return ret;
index 5efa97b..52a63bf 100644 (file)
@@ -57,7 +57,7 @@ static int kvmppc_h_pr_enter(struct kvm_vcpu *vcpu)
                for (i = 0; ; ++i) {
                        if (i == 8)
                                goto done;
-                       if ((*hpte & HPTE_V_VALID) == 0)
+                       if ((be64_to_cpu(*hpte) & HPTE_V_VALID) == 0)
                                break;
                        hpte += 2;
                }
@@ -67,8 +67,8 @@ static int kvmppc_h_pr_enter(struct kvm_vcpu *vcpu)
                        goto done;
        }
 
-       hpte[0] = kvmppc_get_gpr(vcpu, 6);
-       hpte[1] = kvmppc_get_gpr(vcpu, 7);
+       hpte[0] = cpu_to_be64(kvmppc_get_gpr(vcpu, 6));
+       hpte[1] = cpu_to_be64(kvmppc_get_gpr(vcpu, 7));
        pteg_addr += i * HPTE_SIZE;
        copy_to_user((void __user *)pteg_addr, hpte, HPTE_SIZE);
        kvmppc_set_gpr(vcpu, 4, pte_index | i);
@@ -93,6 +93,8 @@ static int kvmppc_h_pr_remove(struct kvm_vcpu *vcpu)
        pteg = get_pteg_addr(vcpu, pte_index);
        mutex_lock(&vcpu->kvm->arch.hpt_mutex);
        copy_from_user(pte, (void __user *)pteg, sizeof(pte));
+       pte[0] = be64_to_cpu(pte[0]);
+       pte[1] = be64_to_cpu(pte[1]);
 
        ret = H_NOT_FOUND;
        if ((pte[0] & HPTE_V_VALID) == 0 ||
@@ -169,6 +171,8 @@ static int kvmppc_h_pr_bulk_remove(struct kvm_vcpu *vcpu)
 
                pteg = get_pteg_addr(vcpu, tsh & H_BULK_REMOVE_PTEX);
                copy_from_user(pte, (void __user *)pteg, sizeof(pte));
+               pte[0] = be64_to_cpu(pte[0]);
+               pte[1] = be64_to_cpu(pte[1]);
 
                /* tsl = AVPN */
                flags = (tsh & H_BULK_REMOVE_FLAGS) >> 26;
@@ -207,6 +211,8 @@ static int kvmppc_h_pr_protect(struct kvm_vcpu *vcpu)
        pteg = get_pteg_addr(vcpu, pte_index);
        mutex_lock(&vcpu->kvm->arch.hpt_mutex);
        copy_from_user(pte, (void __user *)pteg, sizeof(pte));
+       pte[0] = be64_to_cpu(pte[0]);
+       pte[1] = be64_to_cpu(pte[1]);
 
        ret = H_NOT_FOUND;
        if ((pte[0] & HPTE_V_VALID) == 0 ||
@@ -225,6 +231,8 @@ static int kvmppc_h_pr_protect(struct kvm_vcpu *vcpu)
 
        rb = compute_tlbie_rb(v, r, pte_index);
        vcpu->arch.mmu.tlbie(vcpu, rb, rb & 1 ? true : false);
+       pte[0] = cpu_to_be64(pte[0]);
+       pte[1] = cpu_to_be64(pte[1]);
        copy_to_user((void __user *)pteg, pte, sizeof(pte));
        ret = H_SUCCESS;
 
@@ -270,7 +278,7 @@ int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd)
        case H_PUT_TCE:
                return kvmppc_h_pr_put_tce(vcpu);
        case H_CEDE:
-               vcpu->arch.shared->msr |= MSR_EE;
+               kvmppc_set_msr_fast(vcpu, kvmppc_get_msr(vcpu) | MSR_EE);
                kvm_vcpu_block(vcpu);
                clear_bit(KVM_REQ_UNHALT, &vcpu->requests);
                vcpu->stat.halt_wakeup++;
index 7a05315..edb14ba 100644 (file)
@@ -205,6 +205,32 @@ int kvm_vm_ioctl_rtas_define_token(struct kvm *kvm, void __user *argp)
        return rc;
 }
 
+static void kvmppc_rtas_swap_endian_in(struct rtas_args *args)
+{
+#ifdef __LITTLE_ENDIAN__
+       int i;
+
+       args->token = be32_to_cpu(args->token);
+       args->nargs = be32_to_cpu(args->nargs);
+       args->nret = be32_to_cpu(args->nret);
+       for (i = 0; i < args->nargs; i++)
+               args->args[i] = be32_to_cpu(args->args[i]);
+#endif
+}
+
+static void kvmppc_rtas_swap_endian_out(struct rtas_args *args)
+{
+#ifdef __LITTLE_ENDIAN__
+       int i;
+
+       for (i = 0; i < args->nret; i++)
+               args->args[i] = cpu_to_be32(args->args[i]);
+       args->token = cpu_to_be32(args->token);
+       args->nargs = cpu_to_be32(args->nargs);
+       args->nret = cpu_to_be32(args->nret);
+#endif
+}
+
 int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu)
 {
        struct rtas_token_definition *d;
@@ -223,6 +249,8 @@ int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu)
        if (rc)
                goto fail;
 
+       kvmppc_rtas_swap_endian_in(&args);
+
        /*
         * args->rets is a pointer into args->args. Now that we've
         * copied args we need to fix it up to point into our copy,
@@ -247,6 +275,7 @@ int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu)
 
        if (rc == 0) {
                args.rets = orig_rets;
+               kvmppc_rtas_swap_endian_out(&args);
                rc = kvm_write_guest(vcpu->kvm, args_phys, &args, sizeof(args));
                if (rc)
                        goto fail;
index 1e0cc2a..acee37c 100644 (file)
@@ -90,6 +90,15 @@ kvmppc_handler_trampoline_enter:
        LOAD_GUEST_SEGMENTS
 
 #ifdef CONFIG_PPC_BOOK3S_64
+BEGIN_FTR_SECTION
+       /* Save host FSCR */
+       mfspr   r8, SPRN_FSCR
+       std     r8, HSTATE_HOST_FSCR(r13)
+       /* Set FSCR during guest execution */
+       ld      r9, SVCPU_SHADOW_FSCR(r13)
+       mtspr   SPRN_FSCR, r9
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
+
        /* Some guests may need to have dcbz set to 32 byte length.
         *
         * Usually we ensure that by patching the guest's instructions
@@ -255,6 +264,10 @@ BEGIN_FTR_SECTION
        cmpwi   r12, BOOK3S_INTERRUPT_H_EMUL_ASSIST
        beq-    ld_last_inst
 END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
+BEGIN_FTR_SECTION
+       cmpwi   r12, BOOK3S_INTERRUPT_FAC_UNAVAIL
+       beq-    ld_last_inst
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
 #endif
 
        b       no_ld_last_inst
@@ -311,6 +324,18 @@ no_ld_last_inst:
 
 no_dcbz32_off:
 
+BEGIN_FTR_SECTION
+       /* Save guest FSCR on a FAC_UNAVAIL interrupt */
+       cmpwi   r12, BOOK3S_INTERRUPT_FAC_UNAVAIL
+       bne+    no_fscr_save
+       mfspr   r7, SPRN_FSCR
+       std     r7, SVCPU_SHADOW_FSCR(r13)
+no_fscr_save:
+       /* Restore host FSCR */
+       ld      r8, HSTATE_HOST_FSCR(r13)
+       mtspr   SPRN_FSCR, r8
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
+
 #endif /* CONFIG_PPC_BOOK3S_64 */
 
        /*
index 89b7f82..002d517 100644 (file)
@@ -19,6 +19,7 @@
 #include "booke.h"
 #include "e500.h"
 
+#define XOP_DCBTLS  166
 #define XOP_MSGSND  206
 #define XOP_MSGCLR  238
 #define XOP_TLBIVAX 786
@@ -103,6 +104,15 @@ static int kvmppc_e500_emul_ehpriv(struct kvm_run *run, struct kvm_vcpu *vcpu,
        return emulated;
 }
 
+static int kvmppc_e500_emul_dcbtls(struct kvm_vcpu *vcpu)
+{
+       struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
+
+       /* Always fail to lock the cache */
+       vcpu_e500->l1csr0 |= L1CSR0_CUL;
+       return EMULATE_DONE;
+}
+
 int kvmppc_core_emulate_op_e500(struct kvm_run *run, struct kvm_vcpu *vcpu,
                                unsigned int inst, int *advance)
 {
@@ -116,6 +126,10 @@ int kvmppc_core_emulate_op_e500(struct kvm_run *run, struct kvm_vcpu *vcpu,
        case 31:
                switch (get_xop(inst)) {
 
+               case XOP_DCBTLS:
+                       emulated = kvmppc_e500_emul_dcbtls(vcpu);
+                       break;
+
 #ifdef CONFIG_KVM_E500MC
                case XOP_MSGSND:
                        emulated = kvmppc_e500_emul_msgsnd(vcpu, rb);
@@ -222,6 +236,7 @@ int kvmppc_core_emulate_mtspr_e500(struct kvm_vcpu *vcpu, int sprn, ulong spr_va
                break;
        case SPRN_L1CSR1:
                vcpu_e500->l1csr1 = spr_val;
+               vcpu_e500->l1csr1 &= ~(L1CSR1_ICFI | L1CSR1_ICLFR);
                break;
        case SPRN_HID0:
                vcpu_e500->hid0 = spr_val;
index c2b887b..da86d9b 100644 (file)
@@ -97,10 +97,10 @@ static int kvmppc_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
 
        switch (sprn) {
        case SPRN_SRR0:
-               vcpu->arch.shared->srr0 = spr_val;
+               kvmppc_set_srr0(vcpu, spr_val);
                break;
        case SPRN_SRR1:
-               vcpu->arch.shared->srr1 = spr_val;
+               kvmppc_set_srr1(vcpu, spr_val);
                break;
 
        /* XXX We need to context-switch the timebase for
@@ -114,16 +114,16 @@ static int kvmppc_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
                break;
 
        case SPRN_SPRG0:
-               vcpu->arch.shared->sprg0 = spr_val;
+               kvmppc_set_sprg0(vcpu, spr_val);
                break;
        case SPRN_SPRG1:
-               vcpu->arch.shared->sprg1 = spr_val;
+               kvmppc_set_sprg1(vcpu, spr_val);
                break;
        case SPRN_SPRG2:
-               vcpu->arch.shared->sprg2 = spr_val;
+               kvmppc_set_sprg2(vcpu, spr_val);
                break;
        case SPRN_SPRG3:
-               vcpu->arch.shared->sprg3 = spr_val;
+               kvmppc_set_sprg3(vcpu, spr_val);
                break;
 
        /* PIR can legally be written, but we ignore it */
@@ -150,10 +150,10 @@ static int kvmppc_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt)
 
        switch (sprn) {
        case SPRN_SRR0:
-               spr_val = vcpu->arch.shared->srr0;
+               spr_val = kvmppc_get_srr0(vcpu);
                break;
        case SPRN_SRR1:
-               spr_val = vcpu->arch.shared->srr1;
+               spr_val = kvmppc_get_srr1(vcpu);
                break;
        case SPRN_PVR:
                spr_val = vcpu->arch.pvr;
@@ -173,16 +173,16 @@ static int kvmppc_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt)
                break;
 
        case SPRN_SPRG0:
-               spr_val = vcpu->arch.shared->sprg0;
+               spr_val = kvmppc_get_sprg0(vcpu);
                break;
        case SPRN_SPRG1:
-               spr_val = vcpu->arch.shared->sprg1;
+               spr_val = kvmppc_get_sprg1(vcpu);
                break;
        case SPRN_SPRG2:
-               spr_val = vcpu->arch.shared->sprg2;
+               spr_val = kvmppc_get_sprg2(vcpu);
                break;
        case SPRN_SPRG3:
-               spr_val = vcpu->arch.shared->sprg3;
+               spr_val = kvmppc_get_sprg3(vcpu);
                break;
        /* Note: SPRG4-7 are user-readable, so we don't get
         * a trap. */
index efbd996..b68d0dc 100644 (file)
@@ -126,6 +126,8 @@ static int openpic_cpu_write_internal(void *opaque, gpa_t addr,
                                      u32 val, int idx);
 static int openpic_cpu_read_internal(void *opaque, gpa_t addr,
                                     u32 *ptr, int idx);
+static inline void write_IRQreg_idr(struct openpic *opp, int n_IRQ,
+                                   uint32_t val);
 
 enum irq_type {
        IRQ_TYPE_NORMAL = 0,
@@ -528,7 +530,6 @@ static void openpic_reset(struct openpic *opp)
        /* Initialise IRQ sources */
        for (i = 0; i < opp->max_irq; i++) {
                opp->src[i].ivpr = opp->ivpr_reset;
-               opp->src[i].idr = opp->idr_reset;
 
                switch (opp->src[i].type) {
                case IRQ_TYPE_NORMAL:
@@ -543,6 +544,8 @@ static void openpic_reset(struct openpic *opp)
                case IRQ_TYPE_FSLSPECIAL:
                        break;
                }
+
+               write_IRQreg_idr(opp, i, opp->idr_reset);
        }
        /* Initialise IRQ destinations */
        for (i = 0; i < MAX_CPU; i++) {
index 3cf541a..bab20f4 100644 (file)
@@ -125,6 +125,27 @@ int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu)
 }
 EXPORT_SYMBOL_GPL(kvmppc_prepare_to_enter);
 
+#if defined(CONFIG_PPC_BOOK3S_64) && defined(CONFIG_KVM_BOOK3S_PR_POSSIBLE)
+static void kvmppc_swab_shared(struct kvm_vcpu *vcpu)
+{
+       struct kvm_vcpu_arch_shared *shared = vcpu->arch.shared;
+       int i;
+
+       shared->sprg0 = swab64(shared->sprg0);
+       shared->sprg1 = swab64(shared->sprg1);
+       shared->sprg2 = swab64(shared->sprg2);
+       shared->sprg3 = swab64(shared->sprg3);
+       shared->srr0 = swab64(shared->srr0);
+       shared->srr1 = swab64(shared->srr1);
+       shared->dar = swab64(shared->dar);
+       shared->msr = swab64(shared->msr);
+       shared->dsisr = swab32(shared->dsisr);
+       shared->int_pending = swab32(shared->int_pending);
+       for (i = 0; i < ARRAY_SIZE(shared->sr); i++)
+               shared->sr[i] = swab32(shared->sr[i]);
+}
+#endif
+
 int kvmppc_kvm_pv(struct kvm_vcpu *vcpu)
 {
        int nr = kvmppc_get_gpr(vcpu, 11);
@@ -135,7 +156,7 @@ int kvmppc_kvm_pv(struct kvm_vcpu *vcpu)
        unsigned long __maybe_unused param4 = kvmppc_get_gpr(vcpu, 6);
        unsigned long r2 = 0;
 
-       if (!(vcpu->arch.shared->msr & MSR_SF)) {
+       if (!(kvmppc_get_msr(vcpu) & MSR_SF)) {
                /* 32 bit mode */
                param1 &= 0xffffffff;
                param2 &= 0xffffffff;
@@ -146,8 +167,28 @@ int kvmppc_kvm_pv(struct kvm_vcpu *vcpu)
        switch (nr) {
        case KVM_HCALL_TOKEN(KVM_HC_PPC_MAP_MAGIC_PAGE):
        {
-               vcpu->arch.magic_page_pa = param1;
-               vcpu->arch.magic_page_ea = param2;
+#if defined(CONFIG_PPC_BOOK3S_64) && defined(CONFIG_KVM_BOOK3S_PR_POSSIBLE)
+               /* Book3S can be little endian, find it out here */
+               int shared_big_endian = true;
+               if (vcpu->arch.intr_msr & MSR_LE)
+                       shared_big_endian = false;
+               if (shared_big_endian != vcpu->arch.shared_big_endian)
+                       kvmppc_swab_shared(vcpu);
+               vcpu->arch.shared_big_endian = shared_big_endian;
+#endif
+
+               if (!(param2 & MAGIC_PAGE_FLAG_NOT_MAPPED_NX)) {
+                       /*
+                        * Older versions of the Linux magic page code had
+                        * a bug where they would map their trampoline code
+                        * NX. If that's the case, remove !PR NX capability.
+                        */
+                       vcpu->arch.disable_kernel_nx = true;
+                       kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
+               }
+
+               vcpu->arch.magic_page_pa = param1 & ~0xfffULL;
+               vcpu->arch.magic_page_ea = param2 & ~0xfffULL;
 
                r2 = KVM_MAGIC_FEAT_SR | KVM_MAGIC_FEAT_MAS0_TO_SPRG7;
 
@@ -375,6 +416,7 @@ int kvm_dev_ioctl_check_extension(long ext)
        case KVM_CAP_SPAPR_TCE:
        case KVM_CAP_PPC_ALLOC_HTAB:
        case KVM_CAP_PPC_RTAS:
+       case KVM_CAP_PPC_FIXUP_HCALL:
 #ifdef CONFIG_KVM_XICS
        case KVM_CAP_IRQ_XICS:
 #endif
@@ -1015,10 +1057,10 @@ static int kvm_vm_ioctl_get_pvinfo(struct kvm_ppc_pvinfo *pvinfo)
        u32 inst_nop = 0x60000000;
 #ifdef CONFIG_KVM_BOOKE_HV
        u32 inst_sc1 = 0x44000022;
-       pvinfo->hcall[0] = inst_sc1;
-       pvinfo->hcall[1] = inst_nop;
-       pvinfo->hcall[2] = inst_nop;
-       pvinfo->hcall[3] = inst_nop;
+       pvinfo->hcall[0] = cpu_to_be32(inst_sc1);
+       pvinfo->hcall[1] = cpu_to_be32(inst_nop);
+       pvinfo->hcall[2] = cpu_to_be32(inst_nop);
+       pvinfo->hcall[3] = cpu_to_be32(inst_nop);
 #else
        u32 inst_lis = 0x3c000000;
        u32 inst_ori = 0x60000000;
@@ -1034,10 +1076,10 @@ static int kvm_vm_ioctl_get_pvinfo(struct kvm_ppc_pvinfo *pvinfo)
         *    sc
         *    nop
         */
-       pvinfo->hcall[0] = inst_lis | ((KVM_SC_MAGIC_R0 >> 16) & inst_imm_mask);
-       pvinfo->hcall[1] = inst_ori | (KVM_SC_MAGIC_R0 & inst_imm_mask);
-       pvinfo->hcall[2] = inst_sc;
-       pvinfo->hcall[3] = inst_nop;
+       pvinfo->hcall[0] = cpu_to_be32(inst_lis | ((KVM_SC_MAGIC_R0 >> 16) & inst_imm_mask));
+       pvinfo->hcall[1] = cpu_to_be32(inst_ori | (KVM_SC_MAGIC_R0 & inst_imm_mask));
+       pvinfo->hcall[2] = cpu_to_be32(inst_sc);
+       pvinfo->hcall[3] = cpu_to_be32(inst_nop);
 #endif
 
        pvinfo->flags = KVM_PPC_PVINFO_FLAGS_EV_IDLE;
index 8b22e47..e1357cd 100644 (file)
@@ -255,7 +255,7 @@ TRACE_EVENT(kvm_exit,
                __entry->exit_nr        = exit_nr;
                __entry->pc             = kvmppc_get_pc(vcpu);
                __entry->dar            = kvmppc_get_fault_dar(vcpu);
-               __entry->msr            = vcpu->arch.shared->msr;
+               __entry->msr            = kvmppc_get_msr(vcpu);
                __entry->srr1           = vcpu->arch.shadow_srr1;
                __entry->last_inst      = vcpu->arch.last_inst;
        ),
index 06ba83b..350aa58 100644 (file)
@@ -269,9 +269,9 @@ static int __init htab_dt_scan_seg_sizes(unsigned long node,
                                         const char *uname, int depth,
                                         void *data)
 {
-       char *type = of_get_flat_dt_prop(node, "device_type", NULL);
-       __be32 *prop;
-       unsigned long size = 0;
+       const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+       const __be32 *prop;
+       int size = 0;
 
        /* We are scanning "cpu" nodes only */
        if (type == NULL || strcmp(type, "cpu") != 0)
@@ -324,9 +324,9 @@ static int __init htab_dt_scan_page_sizes(unsigned long node,
                                          const char *uname, int depth,
                                          void *data)
 {
-       char *type = of_get_flat_dt_prop(node, "device_type", NULL);
-       __be32 *prop;
-       unsigned long size = 0;
+       const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+       const __be32 *prop;
+       int size = 0;
 
        /* We are scanning "cpu" nodes only */
        if (type == NULL || strcmp(type, "cpu") != 0)
@@ -406,9 +406,9 @@ static int __init htab_dt_scan_page_sizes(unsigned long node,
 static int __init htab_dt_scan_hugepage_blocks(unsigned long node,
                                        const char *uname, int depth,
                                        void *data) {
-       char *type = of_get_flat_dt_prop(node, "device_type", NULL);
-       __be64 *addr_prop;
-       __be32 *page_count_prop;
+       const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+       const __be64 *addr_prop;
+       const __be32 *page_count_prop;
        unsigned int expected_pages;
        long unsigned int phys_addr;
        long unsigned int block_size;
@@ -550,8 +550,8 @@ static int __init htab_dt_scan_pftsize(unsigned long node,
                                       const char *uname, int depth,
                                       void *data)
 {
-       char *type = of_get_flat_dt_prop(node, "device_type", NULL);
-       __be32 *prop;
+       const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+       const __be32 *prop;
 
        /* We are scanning "cpu" nodes only */
        if (type == NULL || strcmp(type, "cpu") != 0)
index 9d1d33c..964a5f6 100644 (file)
@@ -97,7 +97,7 @@ static inline void create_shadowed_slbe(unsigned long ea, int ssize,
 static void __slb_flush_and_rebolt(void)
 {
        /* If you change this make sure you change SLB_NUM_BOLTED
-        * appropriately too. */
+        * and PR KVM appropriately too. */
        unsigned long linear_llp, vmalloc_llp, lflags, vflags;
        unsigned long ksp_esid_data, ksp_vsid_data;
 
index 18c1048..6e19b0a 100644 (file)
@@ -199,8 +199,8 @@ static void __init efika_setup_arch(void)
 
 static int __init efika_probe(void)
 {
-       char *model = of_get_flat_dt_prop(of_get_flat_dt_root(),
-                                         "model", NULL);
+       const char *model = of_get_flat_dt_prop(of_get_flat_dt_root(),
+                                               "model", NULL);
 
        if (model == NULL)
                return 0;
index c665d7d..7044fd3 100644 (file)
@@ -574,8 +574,8 @@ chrp_init2(void)
 
 static int __init chrp_probe(void)
 {
-       char *dtype = of_get_flat_dt_prop(of_get_flat_dt_root(),
-                                         "device_type", NULL);
+       const char *dtype = of_get_flat_dt_prop(of_get_flat_dt_root(),
+                                               "device_type", NULL);
        if (dtype == NULL)
                return 0;
        if (strcmp(dtype, "chrp"))
index 360ad80..f343183 100644 (file)
@@ -61,7 +61,7 @@ int __init early_init_dt_scan_opal(unsigned long node,
                                   const char *uname, int depth, void *data)
 {
        const void *basep, *entryp, *sizep;
-       unsigned long basesz, entrysz, runtimesz;
+       int basesz, entrysz, runtimesz;
 
        if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
                return 0;
@@ -77,11 +77,11 @@ int __init early_init_dt_scan_opal(unsigned long node,
        opal.entry = of_read_number(entryp, entrysz/4);
        opal.size = of_read_number(sizep, runtimesz/4);
 
-       pr_debug("OPAL Base  = 0x%llx (basep=%p basesz=%ld)\n",
+       pr_debug("OPAL Base  = 0x%llx (basep=%p basesz=%d)\n",
                 opal.base, basep, basesz);
-       pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n",
+       pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%d)\n",
                 opal.entry, entryp, entrysz);
-       pr_debug("OPAL Entry = 0x%llx (sizep=%p runtimesz=%ld)\n",
+       pr_debug("OPAL Entry = 0x%llx (sizep=%p runtimesz=%d)\n",
                 opal.size, sizep, runtimesz);
 
        powerpc_firmware_features |= FW_FEATURE_OPAL;
@@ -102,7 +102,7 @@ int __init early_init_dt_scan_opal(unsigned long node,
 int __init early_init_dt_scan_recoverable_ranges(unsigned long node,
                                   const char *uname, int depth, void *data)
 {
-       unsigned long i, psize, size;
+       int i, psize, size;
        const __be32 *prop;
 
        if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
@@ -359,7 +359,7 @@ int opal_get_chars(uint32_t vtermno, char *buf, int count)
        if ((be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_INPUT) == 0)
                return 0;
        len = cpu_to_be64(count);
-       rc = opal_console_read(vtermno, &len, buf);     
+       rc = opal_console_read(vtermno, &len, buf);
        if (rc == OPAL_SUCCESS)
                return be64_to_cpu(len);
        return 0;
index 2db8cc6..099d2df 100644 (file)
@@ -665,7 +665,7 @@ static int __init pseries_probe_fw_features(unsigned long node,
                                            void *data)
 {
        const char *prop;
-       unsigned long len;
+       int len;
        static int hypertas_found;
        static int vec5_found;
 
@@ -698,7 +698,7 @@ static int __init pseries_probe_fw_features(unsigned long node,
 static int __init pSeries_probe(void)
 {
        unsigned long root = of_get_flat_dt_root();
-       char *dtype = of_get_flat_dt_prop(root, "device_type", NULL);
+       const char *dtype = of_get_flat_dt_prop(root, "device_type", NULL);
 
        if (dtype == NULL)
                return 0;
index 1bd0eba..e9056e4 100644 (file)
@@ -152,9 +152,9 @@ EXPORT_SYMBOL_GPL(dcr_resource_len);
 
 #ifdef CONFIG_PPC_DCR_MMIO
 
-u64 of_translate_dcr_address(struct device_node *dev,
-                            unsigned int dcr_n,
-                            unsigned int *out_stride)
+static u64 of_translate_dcr_address(struct device_node *dev,
+                                   unsigned int dcr_n,
+                                   unsigned int *out_stride)
 {
        struct device_node *dp;
        const u32 *p;
index 4e63f1a..31ab9f3 100644 (file)
@@ -57,6 +57,20 @@ static inline void __ctl_clear_bit(unsigned int cr, unsigned int bit)
 void smp_ctl_set_bit(int cr, int bit);
 void smp_ctl_clear_bit(int cr, int bit);
 
+union ctlreg0 {
+       unsigned long val;
+       struct {
+#ifdef CONFIG_64BIT
+               unsigned long      : 32;
+#endif
+               unsigned long      : 3;
+               unsigned long lap  : 1; /* Low-address-protection control */
+               unsigned long      : 4;
+               unsigned long edat : 1; /* Enhanced-DAT-enablement control */
+               unsigned long      : 23;
+       };
+};
+
 #ifdef CONFIG_SMP
 # define ctl_set_bit(cr, bit) smp_ctl_set_bit(cr, bit)
 # define ctl_clear_bit(cr, bit) smp_ctl_clear_bit(cr, bit)
index 154b600..4181d7b 100644 (file)
 #define KVM_NR_IRQCHIPS 1
 #define KVM_IRQCHIP_NUM_PINS 4096
 
+#define SIGP_CTRL_C    0x00800000
+
 struct sca_entry {
-       atomic_t scn;
+       atomic_t ctrl;
        __u32   reserved;
        __u64   sda;
        __u64   reserved2[2];
 } __attribute__((packed));
 
+union ipte_control {
+       unsigned long val;
+       struct {
+               unsigned long k  : 1;
+               unsigned long kh : 31;
+               unsigned long kg : 32;
+       };
+};
 
 struct sca_block {
-       __u64   ipte_control;
+       union ipte_control ipte_control;
        __u64   reserved[5];
        __u64   mcn;
        __u64   reserved2;
@@ -64,6 +74,7 @@ struct sca_block {
 #define CPUSTAT_ZARCH      0x00000800
 #define CPUSTAT_MCDS       0x00000100
 #define CPUSTAT_SM         0x00000080
+#define CPUSTAT_IBS        0x00000040
 #define CPUSTAT_G          0x00000008
 #define CPUSTAT_GED        0x00000004
 #define CPUSTAT_J          0x00000002
@@ -71,7 +82,9 @@ struct sca_block {
 
 struct kvm_s390_sie_block {
        atomic_t cpuflags;              /* 0x0000 */
-       __u32   prefix;                 /* 0x0004 */
+       __u32 : 1;                      /* 0x0004 */
+       __u32 prefix : 18;
+       __u32 : 13;
        __u8    reserved08[4];          /* 0x0008 */
 #define PROG_IN_SIE (1<<0)
        __u32   prog0c;                 /* 0x000c */
@@ -85,12 +98,27 @@ struct kvm_s390_sie_block {
        __u8    reserved40[4];          /* 0x0040 */
 #define LCTL_CR0       0x8000
 #define LCTL_CR6       0x0200
+#define LCTL_CR9       0x0040
+#define LCTL_CR10      0x0020
+#define LCTL_CR11      0x0010
 #define LCTL_CR14      0x0002
        __u16   lctl;                   /* 0x0044 */
        __s16   icpua;                  /* 0x0046 */
-#define ICTL_LPSW 0x00400000
+#define ICTL_PINT      0x20000000
+#define ICTL_LPSW      0x00400000
+#define ICTL_STCTL     0x00040000
+#define ICTL_ISKE      0x00004000
+#define ICTL_SSKE      0x00002000
+#define ICTL_RRBE      0x00001000
+#define ICTL_TPROT     0x00000200
        __u32   ictl;                   /* 0x0048 */
        __u32   eca;                    /* 0x004c */
+#define ICPT_INST      0x04
+#define ICPT_PROGI     0x08
+#define ICPT_INSTPROGI 0x0C
+#define ICPT_OPEREXC   0x2C
+#define ICPT_PARTEXEC  0x38
+#define ICPT_IOINST    0x40
        __u8    icptcode;               /* 0x0050 */
        __u8    reserved51;             /* 0x0051 */
        __u16   ihcpu;                  /* 0x0052 */
@@ -109,9 +137,24 @@ struct kvm_s390_sie_block {
        psw_t   gpsw;                   /* 0x0090 */
        __u64   gg14;                   /* 0x00a0 */
        __u64   gg15;                   /* 0x00a8 */
-       __u8    reservedb0[30];         /* 0x00b0 */
-       __u16   iprcc;                  /* 0x00ce */
-       __u8    reservedd0[48];         /* 0x00d0 */
+       __u8    reservedb0[20];         /* 0x00b0 */
+       __u16   extcpuaddr;             /* 0x00c4 */
+       __u16   eic;                    /* 0x00c6 */
+       __u32   reservedc8;             /* 0x00c8 */
+       __u16   pgmilc;                 /* 0x00cc */
+       __u16   iprcc;                  /* 0x00ce */
+       __u32   dxc;                    /* 0x00d0 */
+       __u16   mcn;                    /* 0x00d4 */
+       __u8    perc;                   /* 0x00d6 */
+       __u8    peratmid;               /* 0x00d7 */
+       __u64   peraddr;                /* 0x00d8 */
+       __u8    eai;                    /* 0x00e0 */
+       __u8    peraid;                 /* 0x00e1 */
+       __u8    oai;                    /* 0x00e2 */
+       __u8    armid;                  /* 0x00e3 */
+       __u8    reservede4[4];          /* 0x00e4 */
+       __u64   tecmc;                  /* 0x00e8 */
+       __u8    reservedf0[16];         /* 0x00f0 */
        __u64   gcr[16];                /* 0x0100 */
        __u64   gbea;                   /* 0x0180 */
        __u8    reserved188[24];        /* 0x0188 */
@@ -146,6 +189,8 @@ struct kvm_vcpu_stat {
        u32 exit_instruction;
        u32 instruction_lctl;
        u32 instruction_lctlg;
+       u32 instruction_stctl;
+       u32 instruction_stctg;
        u32 exit_program_interruption;
        u32 exit_instr_and_program;
        u32 deliver_external_call;
@@ -164,6 +209,7 @@ struct kvm_vcpu_stat {
        u32 instruction_stpx;
        u32 instruction_stap;
        u32 instruction_storage_key;
+       u32 instruction_ipte_interlock;
        u32 instruction_stsch;
        u32 instruction_chsc;
        u32 instruction_stsi;
@@ -183,13 +229,58 @@ struct kvm_vcpu_stat {
        u32 diagnose_9c;
 };
 
-#define PGM_OPERATION            0x01
-#define PGM_PRIVILEGED_OP       0x02
-#define PGM_EXECUTE              0x03
-#define PGM_PROTECTION           0x04
-#define PGM_ADDRESSING           0x05
-#define PGM_SPECIFICATION        0x06
-#define PGM_DATA                 0x07
+#define PGM_OPERATION                  0x01
+#define PGM_PRIVILEGED_OP              0x02
+#define PGM_EXECUTE                    0x03
+#define PGM_PROTECTION                 0x04
+#define PGM_ADDRESSING                 0x05
+#define PGM_SPECIFICATION              0x06
+#define PGM_DATA                       0x07
+#define PGM_FIXED_POINT_OVERFLOW       0x08
+#define PGM_FIXED_POINT_DIVIDE         0x09
+#define PGM_DECIMAL_OVERFLOW           0x0a
+#define PGM_DECIMAL_DIVIDE             0x0b
+#define PGM_HFP_EXPONENT_OVERFLOW      0x0c
+#define PGM_HFP_EXPONENT_UNDERFLOW     0x0d
+#define PGM_HFP_SIGNIFICANCE           0x0e
+#define PGM_HFP_DIVIDE                 0x0f
+#define PGM_SEGMENT_TRANSLATION                0x10
+#define PGM_PAGE_TRANSLATION           0x11
+#define PGM_TRANSLATION_SPEC           0x12
+#define PGM_SPECIAL_OPERATION          0x13
+#define PGM_OPERAND                    0x15
+#define PGM_TRACE_TABEL                        0x16
+#define PGM_SPACE_SWITCH               0x1c
+#define PGM_HFP_SQUARE_ROOT            0x1d
+#define PGM_PC_TRANSLATION_SPEC                0x1f
+#define PGM_AFX_TRANSLATION            0x20
+#define PGM_ASX_TRANSLATION            0x21
+#define PGM_LX_TRANSLATION             0x22
+#define PGM_EX_TRANSLATION             0x23
+#define PGM_PRIMARY_AUTHORITY          0x24
+#define PGM_SECONDARY_AUTHORITY                0x25
+#define PGM_LFX_TRANSLATION            0x26
+#define PGM_LSX_TRANSLATION            0x27
+#define PGM_ALET_SPECIFICATION         0x28
+#define PGM_ALEN_TRANSLATION           0x29
+#define PGM_ALE_SEQUENCE               0x2a
+#define PGM_ASTE_VALIDITY              0x2b
+#define PGM_ASTE_SEQUENCE              0x2c
+#define PGM_EXTENDED_AUTHORITY         0x2d
+#define PGM_LSTE_SEQUENCE              0x2e
+#define PGM_ASTE_INSTANCE              0x2f
+#define PGM_STACK_FULL                 0x30
+#define PGM_STACK_EMPTY                        0x31
+#define PGM_STACK_SPECIFICATION                0x32
+#define PGM_STACK_TYPE                 0x33
+#define PGM_STACK_OPERATION            0x34
+#define PGM_ASCE_TYPE                  0x38
+#define PGM_REGION_FIRST_TRANS         0x39
+#define PGM_REGION_SECOND_TRANS                0x3a
+#define PGM_REGION_THIRD_TRANS         0x3b
+#define PGM_MONITOR                    0x40
+#define PGM_PER                                0x80
+#define PGM_CRYPTO_OPERATION           0x119
 
 struct kvm_s390_interrupt_info {
        struct list_head list;
@@ -229,6 +320,45 @@ struct kvm_s390_float_interrupt {
        unsigned int irq_count;
 };
 
+struct kvm_hw_wp_info_arch {
+       unsigned long addr;
+       unsigned long phys_addr;
+       int len;
+       char *old_data;
+};
+
+struct kvm_hw_bp_info_arch {
+       unsigned long addr;
+       int len;
+};
+
+/*
+ * Only the upper 16 bits of kvm_guest_debug->control are arch specific.
+ * Further KVM_GUESTDBG flags which an be used from userspace can be found in
+ * arch/s390/include/uapi/asm/kvm.h
+ */
+#define KVM_GUESTDBG_EXIT_PENDING 0x10000000
+
+#define guestdbg_enabled(vcpu) \
+               (vcpu->guest_debug & KVM_GUESTDBG_ENABLE)
+#define guestdbg_sstep_enabled(vcpu) \
+               (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
+#define guestdbg_hw_bp_enabled(vcpu) \
+               (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
+#define guestdbg_exit_pending(vcpu) (guestdbg_enabled(vcpu) && \
+               (vcpu->guest_debug & KVM_GUESTDBG_EXIT_PENDING))
+
+struct kvm_guestdbg_info_arch {
+       unsigned long cr0;
+       unsigned long cr9;
+       unsigned long cr10;
+       unsigned long cr11;
+       struct kvm_hw_bp_info_arch *hw_bp_info;
+       struct kvm_hw_wp_info_arch *hw_wp_info;
+       int nr_hw_bp;
+       int nr_hw_wp;
+       unsigned long last_bp;
+};
 
 struct kvm_vcpu_arch {
        struct kvm_s390_sie_block *sie_block;
@@ -238,11 +368,13 @@ struct kvm_vcpu_arch {
        struct kvm_s390_local_interrupt local_int;
        struct hrtimer    ckc_timer;
        struct tasklet_struct tasklet;
+       struct kvm_s390_pgm_info pgm;
        union  {
                struct cpuid    cpu_id;
                u64             stidp_data;
        };
        struct gmap *gmap;
+       struct kvm_guestdbg_info_arch guestdbg;
 #define KVM_S390_PFAULT_TOKEN_INVALID  (-1UL)
        unsigned long pfault_token;
        unsigned long pfault_select;
@@ -285,7 +417,10 @@ struct kvm_arch{
        struct gmap *gmap;
        int css_support;
        int use_irqchip;
+       int use_cmma;
        struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS];
+       wait_queue_head_t ipte_wq;
+       spinlock_t start_stop_lock;
 };
 
 #define KVM_HVA_ERR_BAD                (-1UL)
index 2070cad..4349197 100644 (file)
@@ -56,13 +56,14 @@ struct _lowcore {
        __u16   pgm_code;                       /* 0x008e */
        __u32   trans_exc_code;                 /* 0x0090 */
        __u16   mon_class_num;                  /* 0x0094 */
-       __u16   per_perc_atmid;                 /* 0x0096 */
+       __u8    per_code;                       /* 0x0096 */
+       __u8    per_atmid;                      /* 0x0097 */
        __u32   per_address;                    /* 0x0098 */
        __u32   monitor_code;                   /* 0x009c */
        __u8    exc_access_id;                  /* 0x00a0 */
        __u8    per_access_id;                  /* 0x00a1 */
        __u8    op_access_id;                   /* 0x00a2 */
-       __u8    ar_access_id;                   /* 0x00a3 */
+       __u8    ar_mode_id;                     /* 0x00a3 */
        __u8    pad_0x00a4[0x00b8-0x00a4];      /* 0x00a4 */
        __u16   subchannel_id;                  /* 0x00b8 */
        __u16   subchannel_nr;                  /* 0x00ba */
@@ -195,12 +196,13 @@ struct _lowcore {
        __u16   pgm_code;                       /* 0x008e */
        __u32   data_exc_code;                  /* 0x0090 */
        __u16   mon_class_num;                  /* 0x0094 */
-       __u16   per_perc_atmid;                 /* 0x0096 */
+       __u8    per_code;                       /* 0x0096 */
+       __u8    per_atmid;                      /* 0x0097 */
        __u64   per_address;                    /* 0x0098 */
        __u8    exc_access_id;                  /* 0x00a0 */
        __u8    per_access_id;                  /* 0x00a1 */
        __u8    op_access_id;                   /* 0x00a2 */
-       __u8    ar_access_id;                   /* 0x00a3 */
+       __u8    ar_mode_id;                     /* 0x00a3 */
        __u8    pad_0x00a4[0x00a8-0x00a4];      /* 0x00a4 */
        __u64   trans_exc_code;                 /* 0x00a8 */
        __u64   monitor_code;                   /* 0x00b0 */
index f77695a..a5e6562 100644 (file)
@@ -16,6 +16,8 @@ typedef struct {
        unsigned long vdso_base;
        /* The mmu context has extended page tables. */
        unsigned int has_pgste:1;
+       /* The mmu context uses storage keys. */
+       unsigned int use_skey:1;
 } mm_context_t;
 
 #define INIT_MM_CONTEXT(name)                                                \
index 056d7ef..c28f32a 100644 (file)
@@ -23,6 +23,7 @@ static inline int init_new_context(struct task_struct *tsk,
        mm->context.asce_bits |= _ASCE_TYPE_REGION3;
 #endif
        mm->context.has_pgste = 0;
+       mm->context.use_skey = 0;
        mm->context.asce_limit = STACK_TOP_MAX;
        crst_table_init((unsigned long *) mm->pgd, pgd_entry_type(mm));
        return 0;
index 884017c..9e18a61 100644 (file)
@@ -22,7 +22,8 @@ unsigned long *page_table_alloc(struct mm_struct *, unsigned long);
 void page_table_free(struct mm_struct *, unsigned long *);
 void page_table_free_rcu(struct mmu_gather *, unsigned long *);
 
-void page_table_reset_pgste(struct mm_struct *, unsigned long, unsigned long);
+void page_table_reset_pgste(struct mm_struct *, unsigned long, unsigned long,
+                           bool init_skey);
 int set_guest_storage_key(struct mm_struct *mm, unsigned long addr,
                          unsigned long key, bool nq);
 
index 12f7531..fcba5e0 100644 (file)
@@ -309,7 +309,8 @@ extern unsigned long MODULES_END;
 #define PGSTE_HC_BIT   0x00200000UL
 #define PGSTE_GR_BIT   0x00040000UL
 #define PGSTE_GC_BIT   0x00020000UL
-#define PGSTE_IN_BIT   0x00008000UL    /* IPTE notify bit */
+#define PGSTE_UC_BIT   0x00008000UL    /* user dirty (migration) */
+#define PGSTE_IN_BIT   0x00004000UL    /* IPTE notify bit */
 
 #else /* CONFIG_64BIT */
 
@@ -391,7 +392,8 @@ extern unsigned long MODULES_END;
 #define PGSTE_HC_BIT   0x0020000000000000UL
 #define PGSTE_GR_BIT   0x0004000000000000UL
 #define PGSTE_GC_BIT   0x0002000000000000UL
-#define PGSTE_IN_BIT   0x0000800000000000UL    /* IPTE notify bit */
+#define PGSTE_UC_BIT   0x0000800000000000UL    /* user dirty (migration) */
+#define PGSTE_IN_BIT   0x0000400000000000UL    /* IPTE notify bit */
 
 #endif /* CONFIG_64BIT */
 
@@ -466,6 +468,16 @@ static inline int mm_has_pgste(struct mm_struct *mm)
 #endif
        return 0;
 }
+
+static inline int mm_use_skey(struct mm_struct *mm)
+{
+#ifdef CONFIG_PGSTE
+       if (mm->context.use_skey)
+               return 1;
+#endif
+       return 0;
+}
+
 /*
  * pgd/pmd/pte query functions
  */
@@ -699,26 +711,17 @@ static inline void pgste_set(pte_t *ptep, pgste_t pgste)
 #endif
 }
 
-static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste)
+static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste,
+                                      struct mm_struct *mm)
 {
 #ifdef CONFIG_PGSTE
        unsigned long address, bits, skey;
 
-       if (pte_val(*ptep) & _PAGE_INVALID)
+       if (!mm_use_skey(mm) || pte_val(*ptep) & _PAGE_INVALID)
                return pgste;
        address = pte_val(*ptep) & PAGE_MASK;
        skey = (unsigned long) page_get_storage_key(address);
        bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED);
-       if (!(pgste_val(pgste) & PGSTE_HC_BIT) && (bits & _PAGE_CHANGED)) {
-               /* Transfer dirty + referenced bit to host bits in pgste */
-               pgste_val(pgste) |= bits << 52;
-               page_set_storage_key(address, skey ^ bits, 0);
-       } else if (!(pgste_val(pgste) & PGSTE_HR_BIT) &&
-                  (bits & _PAGE_REFERENCED)) {
-               /* Transfer referenced bit to host bit in pgste */
-               pgste_val(pgste) |= PGSTE_HR_BIT;
-               page_reset_referenced(address);
-       }
        /* Transfer page changed & referenced bit to guest bits in pgste */
        pgste_val(pgste) |= bits << 48;         /* GR bit & GC bit */
        /* Copy page access key and fetch protection bit to pgste */
@@ -729,25 +732,14 @@ static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste)
 
 }
 
-static inline pgste_t pgste_update_young(pte_t *ptep, pgste_t pgste)
-{
-#ifdef CONFIG_PGSTE
-       if (pte_val(*ptep) & _PAGE_INVALID)
-               return pgste;
-       /* Get referenced bit from storage key */
-       if (page_reset_referenced(pte_val(*ptep) & PAGE_MASK))
-               pgste_val(pgste) |= PGSTE_HR_BIT | PGSTE_GR_BIT;
-#endif
-       return pgste;
-}
-
-static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry)
+static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry,
+                                struct mm_struct *mm)
 {
 #ifdef CONFIG_PGSTE
        unsigned long address;
        unsigned long nkey;
 
-       if (pte_val(entry) & _PAGE_INVALID)
+       if (!mm_use_skey(mm) || pte_val(entry) & _PAGE_INVALID)
                return;
        VM_BUG_ON(!(pte_val(*ptep) & _PAGE_INVALID));
        address = pte_val(entry) & PAGE_MASK;
@@ -757,23 +749,30 @@ static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry)
         * key C/R to 0.
         */
        nkey = (pgste_val(pgste) & (PGSTE_ACC_BITS | PGSTE_FP_BIT)) >> 56;
+       nkey |= (pgste_val(pgste) & (PGSTE_GR_BIT | PGSTE_GC_BIT)) >> 48;
        page_set_storage_key(address, nkey, 0);
 #endif
 }
 
-static inline void pgste_set_pte(pte_t *ptep, pte_t entry)
+static inline pgste_t pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry)
 {
-       if (!MACHINE_HAS_ESOP &&
-           (pte_val(entry) & _PAGE_PRESENT) &&
-           (pte_val(entry) & _PAGE_WRITE)) {
-               /*
-                * Without enhanced suppression-on-protection force
-                * the dirty bit on for all writable ptes.
-                */
-               pte_val(entry) |= _PAGE_DIRTY;
-               pte_val(entry) &= ~_PAGE_PROTECT;
+       if ((pte_val(entry) & _PAGE_PRESENT) &&
+           (pte_val(entry) & _PAGE_WRITE) &&
+           !(pte_val(entry) & _PAGE_INVALID)) {
+               if (!MACHINE_HAS_ESOP) {
+                       /*
+                        * Without enhanced suppression-on-protection force
+                        * the dirty bit on for all writable ptes.
+                        */
+                       pte_val(entry) |= _PAGE_DIRTY;
+                       pte_val(entry) &= ~_PAGE_PROTECT;
+               }
+               if (!(pte_val(entry) & _PAGE_PROTECT))
+                       /* This pte allows write access, set user-dirty */
+                       pgste_val(pgste) |= PGSTE_UC_BIT;
        }
        *ptep = entry;
+       return pgste;
 }
 
 /**
@@ -839,6 +838,8 @@ unsigned long __gmap_fault(unsigned long address, struct gmap *);
 unsigned long gmap_fault(unsigned long address, struct gmap *);
 void gmap_discard(unsigned long from, unsigned long to, struct gmap *);
 void __gmap_zap(unsigned long address, struct gmap *);
+bool gmap_test_and_clear_dirty(unsigned long address, struct gmap *);
+
 
 void gmap_register_ipte_notifier(struct gmap_notifier *);
 void gmap_unregister_ipte_notifier(struct gmap_notifier *);
@@ -870,8 +871,8 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
        if (mm_has_pgste(mm)) {
                pgste = pgste_get_lock(ptep);
                pgste_val(pgste) &= ~_PGSTE_GPS_ZERO;
-               pgste_set_key(ptep, pgste, entry);
-               pgste_set_pte(ptep, entry);
+               pgste_set_key(ptep, pgste, entry, mm);
+               pgste = pgste_set_pte(ptep, pgste, entry);
                pgste_set_unlock(ptep, pgste);
        } else {
                if (!(pte_val(entry) & _PAGE_INVALID) && MACHINE_HAS_EDAT1)
@@ -1017,45 +1018,6 @@ static inline pte_t pte_mkhuge(pte_t pte)
 }
 #endif
 
-/*
- * Get (and clear) the user dirty bit for a pte.
- */
-static inline int ptep_test_and_clear_user_dirty(struct mm_struct *mm,
-                                                pte_t *ptep)
-{
-       pgste_t pgste;
-       int dirty = 0;
-
-       if (mm_has_pgste(mm)) {
-               pgste = pgste_get_lock(ptep);
-               pgste = pgste_update_all(ptep, pgste);
-               dirty = !!(pgste_val(pgste) & PGSTE_HC_BIT);
-               pgste_val(pgste) &= ~PGSTE_HC_BIT;
-               pgste_set_unlock(ptep, pgste);
-               return dirty;
-       }
-       return dirty;
-}
-
-/*
- * Get (and clear) the user referenced bit for a pte.
- */
-static inline int ptep_test_and_clear_user_young(struct mm_struct *mm,
-                                                pte_t *ptep)
-{
-       pgste_t pgste;
-       int young = 0;
-
-       if (mm_has_pgste(mm)) {
-               pgste = pgste_get_lock(ptep);
-               pgste = pgste_update_young(ptep, pgste);
-               young = !!(pgste_val(pgste) & PGSTE_HR_BIT);
-               pgste_val(pgste) &= ~PGSTE_HR_BIT;
-               pgste_set_unlock(ptep, pgste);
-       }
-       return young;
-}
-
 static inline void __ptep_ipte(unsigned long address, pte_t *ptep)
 {
        unsigned long pto = (unsigned long) ptep;
@@ -1118,6 +1080,36 @@ static inline void ptep_flush_lazy(struct mm_struct *mm,
        atomic_sub(0x10000, &mm->context.attach_count);
 }
 
+/*
+ * Get (and clear) the user dirty bit for a pte.
+ */
+static inline int ptep_test_and_clear_user_dirty(struct mm_struct *mm,
+                                                unsigned long addr,
+                                                pte_t *ptep)
+{
+       pgste_t pgste;
+       pte_t pte;
+       int dirty;
+
+       if (!mm_has_pgste(mm))
+               return 0;
+       pgste = pgste_get_lock(ptep);
+       dirty = !!(pgste_val(pgste) & PGSTE_UC_BIT);
+       pgste_val(pgste) &= ~PGSTE_UC_BIT;
+       pte = *ptep;
+       if (dirty && (pte_val(pte) & _PAGE_PRESENT)) {
+               pgste = pgste_ipte_notify(mm, ptep, pgste);
+               __ptep_ipte(addr, ptep);
+               if (MACHINE_HAS_ESOP || !(pte_val(pte) & _PAGE_WRITE))
+                       pte_val(pte) |= _PAGE_PROTECT;
+               else
+                       pte_val(pte) |= _PAGE_INVALID;
+               *ptep = pte;
+       }
+       pgste_set_unlock(ptep, pgste);
+       return dirty;
+}
+
 #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
 static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
                                            unsigned long addr, pte_t *ptep)
@@ -1137,7 +1129,7 @@ static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
        pte = pte_mkold(pte);
 
        if (mm_has_pgste(vma->vm_mm)) {
-               pgste_set_pte(ptep, pte);
+               pgste = pgste_set_pte(ptep, pgste, pte);
                pgste_set_unlock(ptep, pgste);
        } else
                *ptep = pte;
@@ -1182,7 +1174,7 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
        pte_val(*ptep) = _PAGE_INVALID;
 
        if (mm_has_pgste(mm)) {
-               pgste = pgste_update_all(&pte, pgste);
+               pgste = pgste_update_all(&pte, pgste, mm);
                pgste_set_unlock(ptep, pgste);
        }
        return pte;
@@ -1205,7 +1197,7 @@ static inline pte_t ptep_modify_prot_start(struct mm_struct *mm,
        ptep_flush_lazy(mm, address, ptep);
 
        if (mm_has_pgste(mm)) {
-               pgste = pgste_update_all(&pte, pgste);
+               pgste = pgste_update_all(&pte, pgste, mm);
                pgste_set(ptep, pgste);
        }
        return pte;
@@ -1219,8 +1211,8 @@ static inline void ptep_modify_prot_commit(struct mm_struct *mm,
 
        if (mm_has_pgste(mm)) {
                pgste = pgste_get(ptep);
-               pgste_set_key(ptep, pgste, pte);
-               pgste_set_pte(ptep, pte);
+               pgste_set_key(ptep, pgste, pte, mm);
+               pgste = pgste_set_pte(ptep, pgste, pte);
                pgste_set_unlock(ptep, pgste);
        } else
                *ptep = pte;
@@ -1246,7 +1238,7 @@ static inline pte_t ptep_clear_flush(struct vm_area_struct *vma,
                if ((pgste_val(pgste) & _PGSTE_GPS_USAGE_MASK) ==
                    _PGSTE_GPS_USAGE_UNUSED)
                        pte_val(pte) |= _PAGE_UNUSED;
-               pgste = pgste_update_all(&pte, pgste);
+               pgste = pgste_update_all(&pte, pgste, vma->vm_mm);
                pgste_set_unlock(ptep, pgste);
        }
        return pte;
@@ -1278,7 +1270,7 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
        pte_val(*ptep) = _PAGE_INVALID;
 
        if (!full && mm_has_pgste(mm)) {
-               pgste = pgste_update_all(&pte, pgste);
+               pgste = pgste_update_all(&pte, pgste, mm);
                pgste_set_unlock(ptep, pgste);
        }
        return pte;
@@ -1301,7 +1293,7 @@ static inline pte_t ptep_set_wrprotect(struct mm_struct *mm,
                pte = pte_wrprotect(pte);
 
                if (mm_has_pgste(mm)) {
-                       pgste_set_pte(ptep, pte);
+                       pgste = pgste_set_pte(ptep, pgste, pte);
                        pgste_set_unlock(ptep, pgste);
                } else
                        *ptep = pte;
@@ -1326,7 +1318,7 @@ static inline int ptep_set_access_flags(struct vm_area_struct *vma,
        ptep_flush_direct(vma->vm_mm, address, ptep);
 
        if (mm_has_pgste(vma->vm_mm)) {
-               pgste_set_pte(ptep, entry);
+               pgste = pgste_set_pte(ptep, pgste, entry);
                pgste_set_unlock(ptep, pgste);
        } else
                *ptep = entry;
@@ -1734,6 +1726,7 @@ static inline pte_t mk_swap_pte(unsigned long type, unsigned long offset)
 extern int vmem_add_mapping(unsigned long start, unsigned long size);
 extern int vmem_remove_mapping(unsigned long start, unsigned long size);
 extern int s390_enable_sie(void);
+extern void s390_enable_skey(void);
 
 /*
  * No page table caches to initialise
index 1b5300c..55d69dd 100644 (file)
                         PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_MASK_MCHECK | \
                         PSW_MASK_PSTATE | PSW_ASC_PRIMARY)
 
+struct psw_bits {
+       unsigned long long      : 1;
+       unsigned long long r    : 1; /* PER-Mask */
+       unsigned long long      : 3;
+       unsigned long long t    : 1; /* DAT Mode */
+       unsigned long long i    : 1; /* Input/Output Mask */
+       unsigned long long e    : 1; /* External Mask */
+       unsigned long long key  : 4; /* PSW Key */
+       unsigned long long      : 1;
+       unsigned long long m    : 1; /* Machine-Check Mask */
+       unsigned long long w    : 1; /* Wait State */
+       unsigned long long p    : 1; /* Problem State */
+       unsigned long long as   : 2; /* Address Space Control */
+       unsigned long long cc   : 2; /* Condition Code */
+       unsigned long long pm   : 4; /* Program Mask */
+       unsigned long long ri   : 1; /* Runtime Instrumentation */
+       unsigned long long      : 6;
+       unsigned long long eaba : 2; /* Addressing Mode */
+#ifdef CONFIG_64BIT
+       unsigned long long      : 31;
+       unsigned long long ia   : 64;/* Instruction Address */
+#else
+       unsigned long long ia   : 31;/* Instruction Address */
+#endif
+};
+
+enum {
+       PSW_AMODE_24BIT = 0,
+       PSW_AMODE_31BIT = 1,
+       PSW_AMODE_64BIT = 3
+};
+
+enum {
+       PSW_AS_PRIMARY   = 0,
+       PSW_AS_ACCREG    = 1,
+       PSW_AS_SECONDARY = 2,
+       PSW_AS_HOME      = 3
+};
+
+#define psw_bits(__psw) (*({                   \
+       typecheck(psw_t, __psw);                \
+       &(*(struct psw_bits *)(&(__psw)));      \
+}))
+
 /*
  * The pt_regs struct defines the way the registers are stored on
  * the stack during a system call.
index 2f5e993..1aba89b 100644 (file)
@@ -28,7 +28,11 @@ struct sclp_ipl_info {
 
 struct sclp_cpu_entry {
        u8 address;
-       u8 reserved0[13];
+       u8 reserved0[2];
+       u8 : 3;
+       u8 siif : 1;
+       u8 : 4;
+       u8 reserved2[10];
        u8 type;
        u8 reserved1;
 } __attribute__((packed));
@@ -61,5 +65,7 @@ int sclp_pci_deconfigure(u32 fid);
 int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode);
 unsigned long sclp_get_hsa_size(void);
 void sclp_early_detect(void);
+int sclp_has_siif(void);
+unsigned int sclp_get_ibc(void);
 
 #endif /* _ASM_S390_SCLP_H */
index c003c6a..0fc2643 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/types.h>
 
 #define __KVM_S390
+#define __KVM_HAVE_GUEST_DEBUG
 
 /* Device control API: s390-specific devices */
 #define KVM_DEV_FLIC_GET_ALL_IRQS      1
@@ -54,6 +55,13 @@ struct kvm_s390_io_adapter_req {
        __u64 addr;
 };
 
+/* kvm attr_group  on vm fd */
+#define KVM_S390_VM_MEM_CTRL           0
+
+/* kvm attributes for mem_ctrl */
+#define KVM_S390_VM_MEM_ENABLE_CMMA    0
+#define KVM_S390_VM_MEM_CLR_CMMA       1
+
 /* for KVM_GET_REGS and KVM_SET_REGS */
 struct kvm_regs {
        /* general purpose regs for s390 */
@@ -72,11 +80,31 @@ struct kvm_fpu {
        __u64 fprs[16];
 };
 
+#define KVM_GUESTDBG_USE_HW_BP         0x00010000
+
+#define KVM_HW_BP                      1
+#define KVM_HW_WP_WRITE                        2
+#define KVM_SINGLESTEP                 4
+
 struct kvm_debug_exit_arch {
+       __u64 addr;
+       __u8 type;
+       __u8 pad[7]; /* Should be set to 0 */
+};
+
+struct kvm_hw_breakpoint {
+       __u64 addr;
+       __u64 phys_addr;
+       __u64 len;
+       __u8 type;
+       __u8 pad[7]; /* Should be set to 0 */
 };
 
 /* for KVM_SET_GUEST_DEBUG */
 struct kvm_guest_debug_arch {
+       __u32 nr_hw_bp;
+       __u32 pad; /* Should be set to 0 */
+       struct kvm_hw_breakpoint __user *hw_bp;
 };
 
 #define KVM_SYNC_PREFIX (1UL << 0)
diff --git a/arch/s390/include/uapi/asm/sie.h b/arch/s390/include/uapi/asm/sie.h
new file mode 100644 (file)
index 0000000..3d97f61
--- /dev/null
@@ -0,0 +1,245 @@
+#ifndef _UAPI_ASM_S390_SIE_H
+#define _UAPI_ASM_S390_SIE_H
+
+#include <asm/sigp.h>
+
+#define diagnose_codes                                         \
+       { 0x10, "DIAG (0x10) release pages" },                  \
+       { 0x44, "DIAG (0x44) time slice end" },                 \
+       { 0x9c, "DIAG (0x9c) time slice end directed" },        \
+       { 0x204, "DIAG (0x204) logical-cpu utilization" },      \
+       { 0x258, "DIAG (0x258) page-reference services" },      \
+       { 0x308, "DIAG (0x308) ipl functions" },                \
+       { 0x500, "DIAG (0x500) KVM virtio functions" },         \
+       { 0x501, "DIAG (0x501) KVM breakpoint" }
+
+#define sigp_order_codes                                               \
+       { SIGP_SENSE, "SIGP sense" },                                   \
+       { SIGP_EXTERNAL_CALL, "SIGP external call" },                   \
+       { SIGP_EMERGENCY_SIGNAL, "SIGP emergency signal" },             \
+       { SIGP_STOP, "SIGP stop" },                                     \
+       { SIGP_STOP_AND_STORE_STATUS, "SIGP stop and store status" },   \
+       { SIGP_SET_ARCHITECTURE, "SIGP set architecture" },             \
+       { SIGP_SET_PREFIX, "SIGP set prefix" },                         \
+       { SIGP_SENSE_RUNNING, "SIGP sense running" },                   \
+       { SIGP_RESTART, "SIGP restart" },                               \
+       { SIGP_INITIAL_CPU_RESET, "SIGP initial cpu reset" },           \
+       { SIGP_STORE_STATUS_AT_ADDRESS, "SIGP store status at address" }
+
+#define icpt_prog_codes                                                \
+       { 0x0001, "Prog Operation" },                           \
+       { 0x0002, "Prog Privileged Operation" },                \
+       { 0x0003, "Prog Execute" },                             \
+       { 0x0004, "Prog Protection" },                          \
+       { 0x0005, "Prog Addressing" },                          \
+       { 0x0006, "Prog Specification" },                       \
+       { 0x0007, "Prog Data" },                                \
+       { 0x0008, "Prog Fixedpoint overflow" },                 \
+       { 0x0009, "Prog Fixedpoint divide" },                   \
+       { 0x000A, "Prog Decimal overflow" },                    \
+       { 0x000B, "Prog Decimal divide" },                      \
+       { 0x000C, "Prog HFP exponent overflow" },               \
+       { 0x000D, "Prog HFP exponent underflow" },              \
+       { 0x000E, "Prog HFP significance" },                    \
+       { 0x000F, "Prog HFP divide" },                          \
+       { 0x0010, "Prog Segment translation" },                 \
+       { 0x0011, "Prog Page translation" },                    \
+       { 0x0012, "Prog Translation specification" },           \
+       { 0x0013, "Prog Special operation" },                   \
+       { 0x0015, "Prog Operand" },                             \
+       { 0x0016, "Prog Trace table" },                         \
+       { 0x0017, "Prog ASNtranslation specification" },        \
+       { 0x001C, "Prog Spaceswitch event" },                   \
+       { 0x001D, "Prog HFP square root" },                     \
+       { 0x001F, "Prog PCtranslation specification" },         \
+       { 0x0020, "Prog AFX translation" },                     \
+       { 0x0021, "Prog ASX translation" },                     \
+       { 0x0022, "Prog LX translation" },                      \
+       { 0x0023, "Prog EX translation" },                      \
+       { 0x0024, "Prog Primary authority" },                   \
+       { 0x0025, "Prog Secondary authority" },                 \
+       { 0x0026, "Prog LFXtranslation exception" },            \
+       { 0x0027, "Prog LSXtranslation exception" },            \
+       { 0x0028, "Prog ALET specification" },                  \
+       { 0x0029, "Prog ALEN translation" },                    \
+       { 0x002A, "Prog ALE sequence" },                        \
+       { 0x002B, "Prog ASTE validity" },                       \
+       { 0x002C, "Prog ASTE sequence" },                       \
+       { 0x002D, "Prog Extended authority" },                  \
+       { 0x002E, "Prog LSTE sequence" },                       \
+       { 0x002F, "Prog ASTE instance" },                       \
+       { 0x0030, "Prog Stack full" },                          \
+       { 0x0031, "Prog Stack empty" },                         \
+       { 0x0032, "Prog Stack specification" },                 \
+       { 0x0033, "Prog Stack type" },                          \
+       { 0x0034, "Prog Stack operation" },                     \
+       { 0x0039, "Prog Region first translation" },            \
+       { 0x003A, "Prog Region second translation" },           \
+       { 0x003B, "Prog Region third translation" },            \
+       { 0x0040, "Prog Monitor event" },                       \
+       { 0x0080, "Prog PER event" },                           \
+       { 0x0119, "Prog Crypto operation" }
+
+#define exit_code_ipa0(ipa0, opcode, mnemonic)         \
+       { (ipa0 << 8 | opcode), #ipa0 " " mnemonic }
+#define exit_code(opcode, mnemonic)                    \
+       { opcode, mnemonic }
+
+#define icpt_insn_codes                                \
+       exit_code_ipa0(0x01, 0x01, "PR"),       \
+       exit_code_ipa0(0x01, 0x04, "PTFF"),     \
+       exit_code_ipa0(0x01, 0x07, "SCKPF"),    \
+       exit_code_ipa0(0xAA, 0x00, "RINEXT"),   \
+       exit_code_ipa0(0xAA, 0x01, "RION"),     \
+       exit_code_ipa0(0xAA, 0x02, "TRIC"),     \
+       exit_code_ipa0(0xAA, 0x03, "RIOFF"),    \
+       exit_code_ipa0(0xAA, 0x04, "RIEMIT"),   \
+       exit_code_ipa0(0xB2, 0x02, "STIDP"),    \
+       exit_code_ipa0(0xB2, 0x04, "SCK"),      \
+       exit_code_ipa0(0xB2, 0x05, "STCK"),     \
+       exit_code_ipa0(0xB2, 0x06, "SCKC"),     \
+       exit_code_ipa0(0xB2, 0x07, "STCKC"),    \
+       exit_code_ipa0(0xB2, 0x08, "SPT"),      \
+       exit_code_ipa0(0xB2, 0x09, "STPT"),     \
+       exit_code_ipa0(0xB2, 0x0d, "PTLB"),     \
+       exit_code_ipa0(0xB2, 0x10, "SPX"),      \
+       exit_code_ipa0(0xB2, 0x11, "STPX"),     \
+       exit_code_ipa0(0xB2, 0x12, "STAP"),     \
+       exit_code_ipa0(0xB2, 0x14, "SIE"),      \
+       exit_code_ipa0(0xB2, 0x16, "SETR"),     \
+       exit_code_ipa0(0xB2, 0x17, "STETR"),    \
+       exit_code_ipa0(0xB2, 0x18, "PC"),       \
+       exit_code_ipa0(0xB2, 0x20, "SERVC"),    \
+       exit_code_ipa0(0xB2, 0x28, "PT"),       \
+       exit_code_ipa0(0xB2, 0x29, "ISKE"),     \
+       exit_code_ipa0(0xB2, 0x2a, "RRBE"),     \
+       exit_code_ipa0(0xB2, 0x2b, "SSKE"),     \
+       exit_code_ipa0(0xB2, 0x2c, "TB"),       \
+       exit_code_ipa0(0xB2, 0x2e, "PGIN"),     \
+       exit_code_ipa0(0xB2, 0x2f, "PGOUT"),    \
+       exit_code_ipa0(0xB2, 0x30, "CSCH"),     \
+       exit_code_ipa0(0xB2, 0x31, "HSCH"),     \
+       exit_code_ipa0(0xB2, 0x32, "MSCH"),     \
+       exit_code_ipa0(0xB2, 0x33, "SSCH"),     \
+       exit_code_ipa0(0xB2, 0x34, "STSCH"),    \
+       exit_code_ipa0(0xB2, 0x35, "TSCH"),     \
+       exit_code_ipa0(0xB2, 0x36, "TPI"),      \
+       exit_code_ipa0(0xB2, 0x37, "SAL"),      \
+       exit_code_ipa0(0xB2, 0x38, "RSCH"),     \
+       exit_code_ipa0(0xB2, 0x39, "STCRW"),    \
+       exit_code_ipa0(0xB2, 0x3a, "STCPS"),    \
+       exit_code_ipa0(0xB2, 0x3b, "RCHP"),     \
+       exit_code_ipa0(0xB2, 0x3c, "SCHM"),     \
+       exit_code_ipa0(0xB2, 0x40, "BAKR"),     \
+       exit_code_ipa0(0xB2, 0x48, "PALB"),     \
+       exit_code_ipa0(0xB2, 0x4c, "TAR"),      \
+       exit_code_ipa0(0xB2, 0x50, "CSP"),      \
+       exit_code_ipa0(0xB2, 0x54, "MVPG"),     \
+       exit_code_ipa0(0xB2, 0x58, "BSG"),      \
+       exit_code_ipa0(0xB2, 0x5a, "BSA"),      \
+       exit_code_ipa0(0xB2, 0x5f, "CHSC"),     \
+       exit_code_ipa0(0xB2, 0x74, "SIGA"),     \
+       exit_code_ipa0(0xB2, 0x76, "XSCH"),     \
+       exit_code_ipa0(0xB2, 0x78, "STCKE"),    \
+       exit_code_ipa0(0xB2, 0x7c, "STCKF"),    \
+       exit_code_ipa0(0xB2, 0x7d, "STSI"),     \
+       exit_code_ipa0(0xB2, 0xb0, "STFLE"),    \
+       exit_code_ipa0(0xB2, 0xb1, "STFL"),     \
+       exit_code_ipa0(0xB2, 0xb2, "LPSWE"),    \
+       exit_code_ipa0(0xB2, 0xf8, "TEND"),     \
+       exit_code_ipa0(0xB2, 0xfc, "TABORT"),   \
+       exit_code_ipa0(0xB9, 0x1e, "KMAC"),     \
+       exit_code_ipa0(0xB9, 0x28, "PCKMO"),    \
+       exit_code_ipa0(0xB9, 0x2a, "KMF"),      \
+       exit_code_ipa0(0xB9, 0x2b, "KMO"),      \
+       exit_code_ipa0(0xB9, 0x2d, "KMCTR"),    \
+       exit_code_ipa0(0xB9, 0x2e, "KM"),       \
+       exit_code_ipa0(0xB9, 0x2f, "KMC"),      \
+       exit_code_ipa0(0xB9, 0x3e, "KIMD"),     \
+       exit_code_ipa0(0xB9, 0x3f, "KLMD"),     \
+       exit_code_ipa0(0xB9, 0x8a, "CSPG"),     \
+       exit_code_ipa0(0xB9, 0x8d, "EPSW"),     \
+       exit_code_ipa0(0xB9, 0x8e, "IDTE"),     \
+       exit_code_ipa0(0xB9, 0x8f, "CRDTE"),    \
+       exit_code_ipa0(0xB9, 0x9c, "EQBS"),     \
+       exit_code_ipa0(0xB9, 0xa2, "PTF"),      \
+       exit_code_ipa0(0xB9, 0xab, "ESSA"),     \
+       exit_code_ipa0(0xB9, 0xae, "RRBM"),     \
+       exit_code_ipa0(0xB9, 0xaf, "PFMF"),     \
+       exit_code_ipa0(0xE3, 0x03, "LRAG"),     \
+       exit_code_ipa0(0xE3, 0x13, "LRAY"),     \
+       exit_code_ipa0(0xE3, 0x25, "NTSTG"),    \
+       exit_code_ipa0(0xE5, 0x00, "LASP"),     \
+       exit_code_ipa0(0xE5, 0x01, "TPROT"),    \
+       exit_code_ipa0(0xE5, 0x60, "TBEGIN"),   \
+       exit_code_ipa0(0xE5, 0x61, "TBEGINC"),  \
+       exit_code_ipa0(0xEB, 0x25, "STCTG"),    \
+       exit_code_ipa0(0xEB, 0x2f, "LCTLG"),    \
+       exit_code_ipa0(0xEB, 0x60, "LRIC"),     \
+       exit_code_ipa0(0xEB, 0x61, "STRIC"),    \
+       exit_code_ipa0(0xEB, 0x62, "MRIC"),     \
+       exit_code_ipa0(0xEB, 0x8a, "SQBS"),     \
+       exit_code_ipa0(0xC8, 0x01, "ECTG"),     \
+       exit_code(0x0a, "SVC"),                 \
+       exit_code(0x80, "SSM"),                 \
+       exit_code(0x82, "LPSW"),                \
+       exit_code(0x83, "DIAG"),                \
+       exit_code(0xae, "SIGP"),                \
+       exit_code(0xac, "STNSM"),               \
+       exit_code(0xad, "STOSM"),               \
+       exit_code(0xb1, "LRA"),                 \
+       exit_code(0xb6, "STCTL"),               \
+       exit_code(0xb7, "LCTL"),                \
+       exit_code(0xee, "PLO")
+
+#define sie_intercept_code                                     \
+       { 0x00, "Host interruption" },                          \
+       { 0x04, "Instruction" },                                \
+       { 0x08, "Program interruption" },                       \
+       { 0x0c, "Instruction and program interruption" },       \
+       { 0x10, "External request" },                           \
+       { 0x14, "External interruption" },                      \
+       { 0x18, "I/O request" },                                \
+       { 0x1c, "Wait state" },                                 \
+       { 0x20, "Validity" },                                   \
+       { 0x28, "Stop request" },                               \
+       { 0x2c, "Operation exception" },                        \
+       { 0x38, "Partial-execution" },                          \
+       { 0x3c, "I/O interruption" },                           \
+       { 0x40, "I/O instruction" },                            \
+       { 0x48, "Timing subset" }
+
+/*
+ * This is the simple interceptable instructions decoder.
+ *
+ * It will be used as userspace interface and it can be used in places
+ * that does not allow to use general decoder functions,
+ * such as trace events declarations.
+ *
+ * Some userspace tools may want to parse this code
+ * and would be confused by switch(), if() and other statements,
+ * but they can understand conditional operator.
+ */
+#define INSN_DECODE_IPA0(ipa0, insn, rshift, mask)             \
+       (insn >> 56) == (ipa0) ?                                \
+               ((ipa0 << 8) | ((insn >> rshift) & mask)) :
+
+#define INSN_DECODE(insn) (insn >> 56)
+
+/*
+ * The macro icpt_insn_decoder() takes an intercepted instruction
+ * and returns a key, which can be used to find a mnemonic name
+ * of the instruction in the icpt_insn_codes table.
+ */
+#define icpt_insn_decoder(insn)                        \
+       INSN_DECODE_IPA0(0x01, insn, 48, 0xff)  \
+       INSN_DECODE_IPA0(0xaa, insn, 48, 0x0f)  \
+       INSN_DECODE_IPA0(0xb2, insn, 48, 0xff)  \
+       INSN_DECODE_IPA0(0xb9, insn, 48, 0xff)  \
+       INSN_DECODE_IPA0(0xe3, insn, 48, 0xff)  \
+       INSN_DECODE_IPA0(0xe5, insn, 48, 0xff)  \
+       INSN_DECODE_IPA0(0xeb, insn, 16, 0xff)  \
+       INSN_DECODE_IPA0(0xc8, insn, 48, 0x0f)  \
+       INSN_DECODE(insn)
+
+#endif /* _UAPI_ASM_S390_SIE_H */
index 0c070c4..afe1715 100644 (file)
@@ -90,16 +90,22 @@ int main(void)
        DEFINE(__LC_PGM_ILC, offsetof(struct _lowcore, pgm_ilc));
        DEFINE(__LC_PGM_INT_CODE, offsetof(struct _lowcore, pgm_code));
        DEFINE(__LC_TRANS_EXC_CODE, offsetof(struct _lowcore, trans_exc_code));
-       DEFINE(__LC_PER_CAUSE, offsetof(struct _lowcore, per_perc_atmid));
+       DEFINE(__LC_MON_CLASS_NR, offsetof(struct _lowcore, mon_class_num));
+       DEFINE(__LC_PER_CODE, offsetof(struct _lowcore, per_code));
+       DEFINE(__LC_PER_ATMID, offsetof(struct _lowcore, per_atmid));
        DEFINE(__LC_PER_ADDRESS, offsetof(struct _lowcore, per_address));
-       DEFINE(__LC_PER_PAID, offsetof(struct _lowcore, per_access_id));
-       DEFINE(__LC_AR_MODE_ID, offsetof(struct _lowcore, ar_access_id));
+       DEFINE(__LC_EXC_ACCESS_ID, offsetof(struct _lowcore, exc_access_id));
+       DEFINE(__LC_PER_ACCESS_ID, offsetof(struct _lowcore, per_access_id));
+       DEFINE(__LC_OP_ACCESS_ID, offsetof(struct _lowcore, op_access_id));
+       DEFINE(__LC_AR_MODE_ID, offsetof(struct _lowcore, ar_mode_id));
+       DEFINE(__LC_MON_CODE, offsetof(struct _lowcore, monitor_code));
        DEFINE(__LC_SUBCHANNEL_ID, offsetof(struct _lowcore, subchannel_id));
        DEFINE(__LC_SUBCHANNEL_NR, offsetof(struct _lowcore, subchannel_nr));
        DEFINE(__LC_IO_INT_PARM, offsetof(struct _lowcore, io_int_parm));
        DEFINE(__LC_IO_INT_WORD, offsetof(struct _lowcore, io_int_word));
        DEFINE(__LC_STFL_FAC_LIST, offsetof(struct _lowcore, stfl_fac_list));
        DEFINE(__LC_MCCK_CODE, offsetof(struct _lowcore, mcck_interruption_code));
+       DEFINE(__LC_MCCK_EXT_DAM_CODE, offsetof(struct _lowcore, external_damage_code));
        DEFINE(__LC_RST_OLD_PSW, offsetof(struct _lowcore, restart_old_psw));
        DEFINE(__LC_EXT_OLD_PSW, offsetof(struct _lowcore, external_old_psw));
        DEFINE(__LC_SVC_OLD_PSW, offsetof(struct _lowcore, svc_old_psw));
@@ -157,6 +163,8 @@ int main(void)
 #ifdef CONFIG_32BIT
        DEFINE(SAVE_AREA_BASE, offsetof(struct _lowcore, extended_save_area_addr));
 #else /* CONFIG_32BIT */
+       DEFINE(__LC_DATA_EXC_CODE, offsetof(struct _lowcore, data_exc_code));
+       DEFINE(__LC_MCCK_FAIL_STOR_ADDR, offsetof(struct _lowcore, failing_storage_address));
        DEFINE(__LC_EXT_PARAMS2, offsetof(struct _lowcore, ext_params2));
        DEFINE(SAVE_AREA_BASE, offsetof(struct _lowcore, floating_pt_save_area));
        DEFINE(__LC_PASTE, offsetof(struct _lowcore, paste));
index 18e5af8..7020326 100644 (file)
@@ -389,8 +389,8 @@ ENTRY(pgm_check_handler)
        jz      pgm_kprobe
        oi      __PT_FLAGS+3(%r11),_PIF_PER_TRAP
        mvc     __THREAD_per_address(4,%r1),__LC_PER_ADDRESS
-       mvc     __THREAD_per_cause(2,%r1),__LC_PER_CAUSE
-       mvc     __THREAD_per_paid(1,%r1),__LC_PER_PAID
+       mvc     __THREAD_per_cause(2,%r1),__LC_PER_CODE
+       mvc     __THREAD_per_paid(1,%r1),__LC_PER_ACCESS_ID
 0:     REENABLE_IRQS
        xc      __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
        l       %r1,BASED(.Ljump_table)
index c41f3f9..f2e674c 100644 (file)
@@ -420,8 +420,8 @@ ENTRY(pgm_check_handler)
        jz      pgm_kprobe
        oi      __PT_FLAGS+7(%r11),_PIF_PER_TRAP
        mvc     __THREAD_per_address(8,%r14),__LC_PER_ADDRESS
-       mvc     __THREAD_per_cause(2,%r14),__LC_PER_CAUSE
-       mvc     __THREAD_per_paid(1,%r14),__LC_PER_PAID
+       mvc     __THREAD_per_cause(2,%r14),__LC_PER_CODE
+       mvc     __THREAD_per_paid(1,%r14),__LC_PER_ACCESS_ID
 0:     REENABLE_IRQS
        xc      __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
        larl    %r1,pgm_check_table
index d3adb37..b3b5534 100644 (file)
@@ -11,5 +11,7 @@ common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o  $(KVM)/async_pf.o $(KVM)/irqch
 
 ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
 
-kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o diag.o
+kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o
+kvm-objs += diag.o gaccess.o guestdbg.o
+
 obj-$(CONFIG_KVM) += kvm.o
index 08dfc83..0161675 100644 (file)
@@ -23,7 +23,7 @@
 static int diag_release_pages(struct kvm_vcpu *vcpu)
 {
        unsigned long start, end;
-       unsigned long prefix  = vcpu->arch.sie_block->prefix;
+       unsigned long prefix  = kvm_s390_get_prefix(vcpu);
 
        start = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4];
        end = vcpu->run->s.regs.gprs[vcpu->arch.sie_block->ipa & 0xf] + 4096;
@@ -64,12 +64,12 @@ static int __diag_page_ref_service(struct kvm_vcpu *vcpu)
        int rc;
        u16 rx = (vcpu->arch.sie_block->ipa & 0xf0) >> 4;
        u16 ry = (vcpu->arch.sie_block->ipa & 0x0f);
-       unsigned long hva_token = KVM_HVA_ERR_BAD;
 
        if (vcpu->run->s.regs.gprs[rx] & 7)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
-       if (copy_from_guest(vcpu, &parm, vcpu->run->s.regs.gprs[rx], sizeof(parm)))
-               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       rc = read_guest(vcpu, vcpu->run->s.regs.gprs[rx], &parm, sizeof(parm));
+       if (rc)
+               return kvm_s390_inject_prog_cond(vcpu, rc);
        if (parm.parm_version != 2 || parm.parm_len < 5 || parm.code != 0x258)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
@@ -89,8 +89,7 @@ static int __diag_page_ref_service(struct kvm_vcpu *vcpu)
                    parm.token_addr & 7 || parm.zarch != 0x8000000000000000ULL)
                        return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
-               hva_token = gfn_to_hva(vcpu->kvm, gpa_to_gfn(parm.token_addr));
-               if (kvm_is_error_hva(hva_token))
+               if (kvm_is_error_gpa(vcpu->kvm, parm.token_addr))
                        return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 
                vcpu->arch.pfault_token = parm.token_addr;
@@ -167,23 +166,17 @@ static int __diag_ipl_functions(struct kvm_vcpu *vcpu)
 
        VCPU_EVENT(vcpu, 5, "diag ipl functions, subcode %lx", subcode);
        switch (subcode) {
-       case 0:
-       case 1:
-               page_table_reset_pgste(current->mm, 0, TASK_SIZE);
-               return -EOPNOTSUPP;
        case 3:
                vcpu->run->s390_reset_flags = KVM_S390_RESET_CLEAR;
-               page_table_reset_pgste(current->mm, 0, TASK_SIZE);
                break;
        case 4:
                vcpu->run->s390_reset_flags = 0;
-               page_table_reset_pgste(current->mm, 0, TASK_SIZE);
                break;
        default:
                return -EOPNOTSUPP;
        }
 
-       atomic_set_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
+       kvm_s390_vcpu_stop(vcpu);
        vcpu->run->s390_reset_flags |= KVM_S390_RESET_SUBSYSTEM;
        vcpu->run->s390_reset_flags |= KVM_S390_RESET_IPL;
        vcpu->run->s390_reset_flags |= KVM_S390_RESET_CPU_INIT;
diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c
new file mode 100644 (file)
index 0000000..4653ac6
--- /dev/null
@@ -0,0 +1,726 @@
+/*
+ * guest access functions
+ *
+ * Copyright IBM Corp. 2014
+ *
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/err.h>
+#include <asm/pgtable.h>
+#include "kvm-s390.h"
+#include "gaccess.h"
+
+union asce {
+       unsigned long val;
+       struct {
+               unsigned long origin : 52; /* Region- or Segment-Table Origin */
+               unsigned long    : 2;
+               unsigned long g  : 1; /* Subspace Group Control */
+               unsigned long p  : 1; /* Private Space Control */
+               unsigned long s  : 1; /* Storage-Alteration-Event Control */
+               unsigned long x  : 1; /* Space-Switch-Event Control */
+               unsigned long r  : 1; /* Real-Space Control */
+               unsigned long    : 1;
+               unsigned long dt : 2; /* Designation-Type Control */
+               unsigned long tl : 2; /* Region- or Segment-Table Length */
+       };
+};
+
+enum {
+       ASCE_TYPE_SEGMENT = 0,
+       ASCE_TYPE_REGION3 = 1,
+       ASCE_TYPE_REGION2 = 2,
+       ASCE_TYPE_REGION1 = 3
+};
+
+union region1_table_entry {
+       unsigned long val;
+       struct {
+               unsigned long rto: 52;/* Region-Table Origin */
+               unsigned long    : 2;
+               unsigned long p  : 1; /* DAT-Protection Bit */
+               unsigned long    : 1;
+               unsigned long tf : 2; /* Region-Second-Table Offset */
+               unsigned long i  : 1; /* Region-Invalid Bit */
+               unsigned long    : 1;
+               unsigned long tt : 2; /* Table-Type Bits */
+               unsigned long tl : 2; /* Region-Second-Table Length */
+       };
+};
+
+union region2_table_entry {
+       unsigned long val;
+       struct {
+               unsigned long rto: 52;/* Region-Table Origin */
+               unsigned long    : 2;
+               unsigned long p  : 1; /* DAT-Protection Bit */
+               unsigned long    : 1;
+               unsigned long tf : 2; /* Region-Third-Table Offset */
+               unsigned long i  : 1; /* Region-Invalid Bit */
+               unsigned long    : 1;
+               unsigned long tt : 2; /* Table-Type Bits */
+               unsigned long tl : 2; /* Region-Third-Table Length */
+       };
+};
+
+struct region3_table_entry_fc0 {
+       unsigned long sto: 52;/* Segment-Table Origin */
+       unsigned long    : 1;
+       unsigned long fc : 1; /* Format-Control */
+       unsigned long p  : 1; /* DAT-Protection Bit */
+       unsigned long    : 1;
+       unsigned long tf : 2; /* Segment-Table Offset */
+       unsigned long i  : 1; /* Region-Invalid Bit */
+       unsigned long cr : 1; /* Common-Region Bit */
+       unsigned long tt : 2; /* Table-Type Bits */
+       unsigned long tl : 2; /* Segment-Table Length */
+};
+
+struct region3_table_entry_fc1 {
+       unsigned long rfaa : 33; /* Region-Frame Absolute Address */
+       unsigned long    : 14;
+       unsigned long av : 1; /* ACCF-Validity Control */
+       unsigned long acc: 4; /* Access-Control Bits */
+       unsigned long f  : 1; /* Fetch-Protection Bit */
+       unsigned long fc : 1; /* Format-Control */
+       unsigned long p  : 1; /* DAT-Protection Bit */
+       unsigned long co : 1; /* Change-Recording Override */
+       unsigned long    : 2;
+       unsigned long i  : 1; /* Region-Invalid Bit */
+       unsigned long cr : 1; /* Common-Region Bit */
+       unsigned long tt : 2; /* Table-Type Bits */
+       unsigned long    : 2;
+};
+
+union region3_table_entry {
+       unsigned long val;
+       struct region3_table_entry_fc0 fc0;
+       struct region3_table_entry_fc1 fc1;
+       struct {
+               unsigned long    : 53;
+               unsigned long fc : 1; /* Format-Control */
+               unsigned long    : 4;
+               unsigned long i  : 1; /* Region-Invalid Bit */
+               unsigned long cr : 1; /* Common-Region Bit */
+               unsigned long tt : 2; /* Table-Type Bits */
+               unsigned long    : 2;
+       };
+};
+
+struct segment_entry_fc0 {
+       unsigned long pto: 53;/* Page-Table Origin */
+       unsigned long fc : 1; /* Format-Control */
+       unsigned long p  : 1; /* DAT-Protection Bit */
+       unsigned long    : 3;
+       unsigned long i  : 1; /* Segment-Invalid Bit */
+       unsigned long cs : 1; /* Common-Segment Bit */
+       unsigned long tt : 2; /* Table-Type Bits */
+       unsigned long    : 2;
+};
+
+struct segment_entry_fc1 {
+       unsigned long sfaa : 44; /* Segment-Frame Absolute Address */
+       unsigned long    : 3;
+       unsigned long av : 1; /* ACCF-Validity Control */
+       unsigned long acc: 4; /* Access-Control Bits */
+       unsigned long f  : 1; /* Fetch-Protection Bit */
+       unsigned long fc : 1; /* Format-Control */
+       unsigned long p  : 1; /* DAT-Protection Bit */
+       unsigned long co : 1; /* Change-Recording Override */
+       unsigned long    : 2;
+       unsigned long i  : 1; /* Segment-Invalid Bit */
+       unsigned long cs : 1; /* Common-Segment Bit */
+       unsigned long tt : 2; /* Table-Type Bits */
+       unsigned long    : 2;
+};
+
+union segment_table_entry {
+       unsigned long val;
+       struct segment_entry_fc0 fc0;
+       struct segment_entry_fc1 fc1;
+       struct {
+               unsigned long    : 53;
+               unsigned long fc : 1; /* Format-Control */
+               unsigned long    : 4;
+               unsigned long i  : 1; /* Segment-Invalid Bit */
+               unsigned long cs : 1; /* Common-Segment Bit */
+               unsigned long tt : 2; /* Table-Type Bits */
+               unsigned long    : 2;
+       };
+};
+
+enum {
+       TABLE_TYPE_SEGMENT = 0,
+       TABLE_TYPE_REGION3 = 1,
+       TABLE_TYPE_REGION2 = 2,
+       TABLE_TYPE_REGION1 = 3
+};
+
+union page_table_entry {
+       unsigned long val;
+       struct {
+               unsigned long pfra : 52; /* Page-Frame Real Address */
+               unsigned long z  : 1; /* Zero Bit */
+               unsigned long i  : 1; /* Page-Invalid Bit */
+               unsigned long p  : 1; /* DAT-Protection Bit */
+               unsigned long co : 1; /* Change-Recording Override */
+               unsigned long    : 8;
+       };
+};
+
+/*
+ * vaddress union in order to easily decode a virtual address into its
+ * region first index, region second index etc. parts.
+ */
+union vaddress {
+       unsigned long addr;
+       struct {
+               unsigned long rfx : 11;
+               unsigned long rsx : 11;
+               unsigned long rtx : 11;
+               unsigned long sx  : 11;
+               unsigned long px  : 8;
+               unsigned long bx  : 12;
+       };
+       struct {
+               unsigned long rfx01 : 2;
+               unsigned long       : 9;
+               unsigned long rsx01 : 2;
+               unsigned long       : 9;
+               unsigned long rtx01 : 2;
+               unsigned long       : 9;
+               unsigned long sx01  : 2;
+               unsigned long       : 29;
+       };
+};
+
+/*
+ * raddress union which will contain the result (real or absolute address)
+ * after a page table walk. The rfaa, sfaa and pfra members are used to
+ * simply assign them the value of a region, segment or page table entry.
+ */
+union raddress {
+       unsigned long addr;
+       unsigned long rfaa : 33; /* Region-Frame Absolute Address */
+       unsigned long sfaa : 44; /* Segment-Frame Absolute Address */
+       unsigned long pfra : 52; /* Page-Frame Real Address */
+};
+
+static int ipte_lock_count;
+static DEFINE_MUTEX(ipte_mutex);
+
+int ipte_lock_held(struct kvm_vcpu *vcpu)
+{
+       union ipte_control *ic = &vcpu->kvm->arch.sca->ipte_control;
+
+       if (vcpu->arch.sie_block->eca & 1)
+               return ic->kh != 0;
+       return ipte_lock_count != 0;
+}
+
+static void ipte_lock_simple(struct kvm_vcpu *vcpu)
+{
+       union ipte_control old, new, *ic;
+
+       mutex_lock(&ipte_mutex);
+       ipte_lock_count++;
+       if (ipte_lock_count > 1)
+               goto out;
+       ic = &vcpu->kvm->arch.sca->ipte_control;
+       do {
+               old = ACCESS_ONCE(*ic);
+               while (old.k) {
+                       cond_resched();
+                       old = ACCESS_ONCE(*ic);
+               }
+               new = old;
+               new.k = 1;
+       } while (cmpxchg(&ic->val, old.val, new.val) != old.val);
+out:
+       mutex_unlock(&ipte_mutex);
+}
+
+static void ipte_unlock_simple(struct kvm_vcpu *vcpu)
+{
+       union ipte_control old, new, *ic;
+
+       mutex_lock(&ipte_mutex);
+       ipte_lock_count--;
+       if (ipte_lock_count)
+               goto out;
+       ic = &vcpu->kvm->arch.sca->ipte_control;
+       do {
+               new = old = ACCESS_ONCE(*ic);
+               new.k = 0;
+       } while (cmpxchg(&ic->val, old.val, new.val) != old.val);
+       if (!ipte_lock_count)
+               wake_up(&vcpu->kvm->arch.ipte_wq);
+out:
+       mutex_unlock(&ipte_mutex);
+}
+
+static void ipte_lock_siif(struct kvm_vcpu *vcpu)
+{
+       union ipte_control old, new, *ic;
+
+       ic = &vcpu->kvm->arch.sca->ipte_control;
+       do {
+               old = ACCESS_ONCE(*ic);
+               while (old.kg) {
+                       cond_resched();
+                       old = ACCESS_ONCE(*ic);
+               }
+               new = old;
+               new.k = 1;
+               new.kh++;
+       } while (cmpxchg(&ic->val, old.val, new.val) != old.val);
+}
+
+static void ipte_unlock_siif(struct kvm_vcpu *vcpu)
+{
+       union ipte_control old, new, *ic;
+
+       ic = &vcpu->kvm->arch.sca->ipte_control;
+       do {
+               new = old = ACCESS_ONCE(*ic);
+               new.kh--;
+               if (!new.kh)
+                       new.k = 0;
+       } while (cmpxchg(&ic->val, old.val, new.val) != old.val);
+       if (!new.kh)
+               wake_up(&vcpu->kvm->arch.ipte_wq);
+}
+
+void ipte_lock(struct kvm_vcpu *vcpu)
+{
+       if (vcpu->arch.sie_block->eca & 1)
+               ipte_lock_siif(vcpu);
+       else
+               ipte_lock_simple(vcpu);
+}
+
+void ipte_unlock(struct kvm_vcpu *vcpu)
+{
+       if (vcpu->arch.sie_block->eca & 1)
+               ipte_unlock_siif(vcpu);
+       else
+               ipte_unlock_simple(vcpu);
+}
+
+static unsigned long get_vcpu_asce(struct kvm_vcpu *vcpu)
+{
+       switch (psw_bits(vcpu->arch.sie_block->gpsw).as) {
+       case PSW_AS_PRIMARY:
+               return vcpu->arch.sie_block->gcr[1];
+       case PSW_AS_SECONDARY:
+               return vcpu->arch.sie_block->gcr[7];
+       case PSW_AS_HOME:
+               return vcpu->arch.sie_block->gcr[13];
+       }
+       return 0;
+}
+
+static int deref_table(struct kvm *kvm, unsigned long gpa, unsigned long *val)
+{
+       return kvm_read_guest(kvm, gpa, val, sizeof(*val));
+}
+
+/**
+ * guest_translate - translate a guest virtual into a guest absolute address
+ * @vcpu: virtual cpu
+ * @gva: guest virtual address
+ * @gpa: points to where guest physical (absolute) address should be stored
+ * @write: indicates if access is a write access
+ *
+ * Translate a guest virtual address into a guest absolute address by means
+ * of dynamic address translation as specified by the architecuture.
+ * If the resulting absolute address is not available in the configuration
+ * an addressing exception is indicated and @gpa will not be changed.
+ *
+ * Returns: - zero on success; @gpa contains the resulting absolute address
+ *         - a negative value if guest access failed due to e.g. broken
+ *           guest mapping
+ *         - a positve value if an access exception happened. In this case
+ *           the returned value is the program interruption code as defined
+ *           by the architecture
+ */
+static unsigned long guest_translate(struct kvm_vcpu *vcpu, unsigned long gva,
+                                    unsigned long *gpa, int write)
+{
+       union vaddress vaddr = {.addr = gva};
+       union raddress raddr = {.addr = gva};
+       union page_table_entry pte;
+       int dat_protection = 0;
+       union ctlreg0 ctlreg0;
+       unsigned long ptr;
+       int edat1, edat2;
+       union asce asce;
+
+       ctlreg0.val = vcpu->arch.sie_block->gcr[0];
+       edat1 = ctlreg0.edat && test_vfacility(8);
+       edat2 = edat1 && test_vfacility(78);
+       asce.val = get_vcpu_asce(vcpu);
+       if (asce.r)
+               goto real_address;
+       ptr = asce.origin * 4096;
+       switch (asce.dt) {
+       case ASCE_TYPE_REGION1:
+               if (vaddr.rfx01 > asce.tl)
+                       return PGM_REGION_FIRST_TRANS;
+               ptr += vaddr.rfx * 8;
+               break;
+       case ASCE_TYPE_REGION2:
+               if (vaddr.rfx)
+                       return PGM_ASCE_TYPE;
+               if (vaddr.rsx01 > asce.tl)
+                       return PGM_REGION_SECOND_TRANS;
+               ptr += vaddr.rsx * 8;
+               break;
+       case ASCE_TYPE_REGION3:
+               if (vaddr.rfx || vaddr.rsx)
+                       return PGM_ASCE_TYPE;
+               if (vaddr.rtx01 > asce.tl)
+                       return PGM_REGION_THIRD_TRANS;
+               ptr += vaddr.rtx * 8;
+               break;
+       case ASCE_TYPE_SEGMENT:
+               if (vaddr.rfx || vaddr.rsx || vaddr.rtx)
+                       return PGM_ASCE_TYPE;
+               if (vaddr.sx01 > asce.tl)
+                       return PGM_SEGMENT_TRANSLATION;
+               ptr += vaddr.sx * 8;
+               break;
+       }
+       switch (asce.dt) {
+       case ASCE_TYPE_REGION1: {
+               union region1_table_entry rfte;
+
+               if (kvm_is_error_gpa(vcpu->kvm, ptr))
+                       return PGM_ADDRESSING;
+               if (deref_table(vcpu->kvm, ptr, &rfte.val))
+                       return -EFAULT;
+               if (rfte.i)
+                       return PGM_REGION_FIRST_TRANS;
+               if (rfte.tt != TABLE_TYPE_REGION1)
+                       return PGM_TRANSLATION_SPEC;
+               if (vaddr.rsx01 < rfte.tf || vaddr.rsx01 > rfte.tl)
+                       return PGM_REGION_SECOND_TRANS;
+               if (edat1)
+                       dat_protection |= rfte.p;
+               ptr = rfte.rto * 4096 + vaddr.rsx * 8;
+       }
+               /* fallthrough */
+       case ASCE_TYPE_REGION2: {
+               union region2_table_entry rste;
+
+               if (kvm_is_error_gpa(vcpu->kvm, ptr))
+                       return PGM_ADDRESSING;
+               if (deref_table(vcpu->kvm, ptr, &rste.val))
+                       return -EFAULT;
+               if (rste.i)
+                       return PGM_REGION_SECOND_TRANS;
+               if (rste.tt != TABLE_TYPE_REGION2)
+                       return PGM_TRANSLATION_SPEC;
+               if (vaddr.rtx01 < rste.tf || vaddr.rtx01 > rste.tl)
+                       return PGM_REGION_THIRD_TRANS;
+               if (edat1)
+                       dat_protection |= rste.p;
+               ptr = rste.rto * 4096 + vaddr.rtx * 8;
+       }
+               /* fallthrough */
+       case ASCE_TYPE_REGION3: {
+               union region3_table_entry rtte;
+
+               if (kvm_is_error_gpa(vcpu->kvm, ptr))
+                       return PGM_ADDRESSING;
+               if (deref_table(vcpu->kvm, ptr, &rtte.val))
+                       return -EFAULT;
+               if (rtte.i)
+                       return PGM_REGION_THIRD_TRANS;
+               if (rtte.tt != TABLE_TYPE_REGION3)
+                       return PGM_TRANSLATION_SPEC;
+               if (rtte.cr && asce.p && edat2)
+                       return PGM_TRANSLATION_SPEC;
+               if (rtte.fc && edat2) {
+                       dat_protection |= rtte.fc1.p;
+                       raddr.rfaa = rtte.fc1.rfaa;
+                       goto absolute_address;
+               }
+               if (vaddr.sx01 < rtte.fc0.tf)
+                       return PGM_SEGMENT_TRANSLATION;
+               if (vaddr.sx01 > rtte.fc0.tl)
+                       return PGM_SEGMENT_TRANSLATION;
+               if (edat1)
+                       dat_protection |= rtte.fc0.p;
+               ptr = rtte.fc0.sto * 4096 + vaddr.sx * 8;
+       }
+               /* fallthrough */
+       case ASCE_TYPE_SEGMENT: {
+               union segment_table_entry ste;
+
+               if (kvm_is_error_gpa(vcpu->kvm, ptr))
+                       return PGM_ADDRESSING;
+               if (deref_table(vcpu->kvm, ptr, &ste.val))
+                       return -EFAULT;
+               if (ste.i)
+                       return PGM_SEGMENT_TRANSLATION;
+               if (ste.tt != TABLE_TYPE_SEGMENT)
+                       return PGM_TRANSLATION_SPEC;
+               if (ste.cs && asce.p)
+                       return PGM_TRANSLATION_SPEC;
+               if (ste.fc && edat1) {
+                       dat_protection |= ste.fc1.p;
+                       raddr.sfaa = ste.fc1.sfaa;
+                       goto absolute_address;
+               }
+               dat_protection |= ste.fc0.p;
+               ptr = ste.fc0.pto * 2048 + vaddr.px * 8;
+       }
+       }
+       if (kvm_is_error_gpa(vcpu->kvm, ptr))
+               return PGM_ADDRESSING;
+       if (deref_table(vcpu->kvm, ptr, &pte.val))
+               return -EFAULT;
+       if (pte.i)
+               return PGM_PAGE_TRANSLATION;
+       if (pte.z)
+               return PGM_TRANSLATION_SPEC;
+       if (pte.co && !edat1)
+               return PGM_TRANSLATION_SPEC;
+       dat_protection |= pte.p;
+       raddr.pfra = pte.pfra;
+real_address:
+       raddr.addr = kvm_s390_real_to_abs(vcpu, raddr.addr);
+absolute_address:
+       if (write && dat_protection)
+               return PGM_PROTECTION;
+       if (kvm_is_error_gpa(vcpu->kvm, raddr.addr))
+               return PGM_ADDRESSING;
+       *gpa = raddr.addr;
+       return 0;
+}
+
+static inline int is_low_address(unsigned long ga)
+{
+       /* Check for address ranges 0..511 and 4096..4607 */
+       return (ga & ~0x11fful) == 0;
+}
+
+static int low_address_protection_enabled(struct kvm_vcpu *vcpu)
+{
+       union ctlreg0 ctlreg0 = {.val = vcpu->arch.sie_block->gcr[0]};
+       psw_t *psw = &vcpu->arch.sie_block->gpsw;
+       union asce asce;
+
+       if (!ctlreg0.lap)
+               return 0;
+       asce.val = get_vcpu_asce(vcpu);
+       if (psw_bits(*psw).t && asce.p)
+               return 0;
+       return 1;
+}
+
+struct trans_exc_code_bits {
+       unsigned long addr : 52; /* Translation-exception Address */
+       unsigned long fsi  : 2;  /* Access Exception Fetch/Store Indication */
+       unsigned long      : 7;
+       unsigned long b61  : 1;
+       unsigned long as   : 2;  /* ASCE Identifier */
+};
+
+enum {
+       FSI_UNKNOWN = 0, /* Unknown wether fetch or store */
+       FSI_STORE   = 1, /* Exception was due to store operation */
+       FSI_FETCH   = 2  /* Exception was due to fetch operation */
+};
+
+static int guest_page_range(struct kvm_vcpu *vcpu, unsigned long ga,
+                           unsigned long *pages, unsigned long nr_pages,
+                           int write)
+{
+       struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm;
+       psw_t *psw = &vcpu->arch.sie_block->gpsw;
+       struct trans_exc_code_bits *tec_bits;
+       int lap_enabled, rc;
+
+       memset(pgm, 0, sizeof(*pgm));
+       tec_bits = (struct trans_exc_code_bits *)&pgm->trans_exc_code;
+       tec_bits->fsi = write ? FSI_STORE : FSI_FETCH;
+       tec_bits->as = psw_bits(*psw).as;
+       lap_enabled = low_address_protection_enabled(vcpu);
+       while (nr_pages) {
+               ga = kvm_s390_logical_to_effective(vcpu, ga);
+               tec_bits->addr = ga >> PAGE_SHIFT;
+               if (write && lap_enabled && is_low_address(ga)) {
+                       pgm->code = PGM_PROTECTION;
+                       return pgm->code;
+               }
+               ga &= PAGE_MASK;
+               if (psw_bits(*psw).t) {
+                       rc = guest_translate(vcpu, ga, pages, write);
+                       if (rc < 0)
+                               return rc;
+                       if (rc == PGM_PROTECTION)
+                               tec_bits->b61 = 1;
+                       if (rc)
+                               pgm->code = rc;
+               } else {
+                       *pages = kvm_s390_real_to_abs(vcpu, ga);
+                       if (kvm_is_error_gpa(vcpu->kvm, *pages))
+                               pgm->code = PGM_ADDRESSING;
+               }
+               if (pgm->code)
+                       return pgm->code;
+               ga += PAGE_SIZE;
+               pages++;
+               nr_pages--;
+       }
+       return 0;
+}
+
+int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, void *data,
+                unsigned long len, int write)
+{
+       psw_t *psw = &vcpu->arch.sie_block->gpsw;
+       unsigned long _len, nr_pages, gpa, idx;
+       unsigned long pages_array[2];
+       unsigned long *pages;
+       int need_ipte_lock;
+       union asce asce;
+       int rc;
+
+       if (!len)
+               return 0;
+       /* Access register mode is not supported yet. */
+       if (psw_bits(*psw).t && psw_bits(*psw).as == PSW_AS_ACCREG)
+               return -EOPNOTSUPP;
+       nr_pages = (((ga & ~PAGE_MASK) + len - 1) >> PAGE_SHIFT) + 1;
+       pages = pages_array;
+       if (nr_pages > ARRAY_SIZE(pages_array))
+               pages = vmalloc(nr_pages * sizeof(unsigned long));
+       if (!pages)
+               return -ENOMEM;
+       asce.val = get_vcpu_asce(vcpu);
+       need_ipte_lock = psw_bits(*psw).t && !asce.r;
+       if (need_ipte_lock)
+               ipte_lock(vcpu);
+       rc = guest_page_range(vcpu, ga, pages, nr_pages, write);
+       for (idx = 0; idx < nr_pages && !rc; idx++) {
+               gpa = *(pages + idx) + (ga & ~PAGE_MASK);
+               _len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len);
+               if (write)
+                       rc = kvm_write_guest(vcpu->kvm, gpa, data, _len);
+               else
+                       rc = kvm_read_guest(vcpu->kvm, gpa, data, _len);
+               len -= _len;
+               ga += _len;
+               data += _len;
+       }
+       if (need_ipte_lock)
+               ipte_unlock(vcpu);
+       if (nr_pages > ARRAY_SIZE(pages_array))
+               vfree(pages);
+       return rc;
+}
+
+int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
+                     void *data, unsigned long len, int write)
+{
+       unsigned long _len, gpa;
+       int rc = 0;
+
+       while (len && !rc) {
+               gpa = kvm_s390_real_to_abs(vcpu, gra);
+               _len = min(PAGE_SIZE - (gpa & ~PAGE_MASK), len);
+               if (write)
+                       rc = write_guest_abs(vcpu, gpa, data, _len);
+               else
+                       rc = read_guest_abs(vcpu, gpa, data, _len);
+               len -= _len;
+               gra += _len;
+               data += _len;
+       }
+       return rc;
+}
+
+/**
+ * guest_translate_address - translate guest logical into guest absolute address
+ *
+ * Parameter semantics are the same as the ones from guest_translate.
+ * The memory contents at the guest address are not changed.
+ *
+ * Note: The IPTE lock is not taken during this function, so the caller
+ * has to take care of this.
+ */
+int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva,
+                           unsigned long *gpa, int write)
+{
+       struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm;
+       psw_t *psw = &vcpu->arch.sie_block->gpsw;
+       struct trans_exc_code_bits *tec;
+       union asce asce;
+       int rc;
+
+       /* Access register mode is not supported yet. */
+       if (psw_bits(*psw).t && psw_bits(*psw).as == PSW_AS_ACCREG)
+               return -EOPNOTSUPP;
+
+       gva = kvm_s390_logical_to_effective(vcpu, gva);
+       memset(pgm, 0, sizeof(*pgm));
+       tec = (struct trans_exc_code_bits *)&pgm->trans_exc_code;
+       tec->as = psw_bits(*psw).as;
+       tec->fsi = write ? FSI_STORE : FSI_FETCH;
+       tec->addr = gva >> PAGE_SHIFT;
+       if (is_low_address(gva) && low_address_protection_enabled(vcpu)) {
+               if (write) {
+                       rc = pgm->code = PGM_PROTECTION;
+                       return rc;
+               }
+       }
+
+       asce.val = get_vcpu_asce(vcpu);
+       if (psw_bits(*psw).t && !asce.r) {      /* Use DAT? */
+               rc = guest_translate(vcpu, gva, gpa, write);
+               if (rc > 0) {
+                       if (rc == PGM_PROTECTION)
+                               tec->b61 = 1;
+                       pgm->code = rc;
+               }
+       } else {
+               rc = 0;
+               *gpa = kvm_s390_real_to_abs(vcpu, gva);
+               if (kvm_is_error_gpa(vcpu->kvm, *gpa))
+                       rc = pgm->code = PGM_ADDRESSING;
+       }
+
+       return rc;
+}
+
+/**
+ * kvm_s390_check_low_addr_protection - check for low-address protection
+ * @ga: Guest address
+ *
+ * Checks whether an address is subject to low-address protection and set
+ * up vcpu->arch.pgm accordingly if necessary.
+ *
+ * Return: 0 if no protection exception, or PGM_PROTECTION if protected.
+ */
+int kvm_s390_check_low_addr_protection(struct kvm_vcpu *vcpu, unsigned long ga)
+{
+       struct kvm_s390_pgm_info *pgm = &vcpu->arch.pgm;
+       psw_t *psw = &vcpu->arch.sie_block->gpsw;
+       struct trans_exc_code_bits *tec_bits;
+
+       if (!is_low_address(ga) || !low_address_protection_enabled(vcpu))
+               return 0;
+
+       memset(pgm, 0, sizeof(*pgm));
+       tec_bits = (struct trans_exc_code_bits *)&pgm->trans_exc_code;
+       tec_bits->fsi = FSI_STORE;
+       tec_bits->as = psw_bits(*psw).as;
+       tec_bits->addr = ga >> PAGE_SHIFT;
+       pgm->code = PGM_PROTECTION;
+
+       return pgm->code;
+}
index 374a439..0149cf1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * access guest memory
  *
- * Copyright IBM Corp. 2008, 2009
+ * Copyright IBM Corp. 2008, 2014
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License (version 2 only)
 
 #include <linux/compiler.h>
 #include <linux/kvm_host.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
+#include <linux/ptrace.h>
 #include "kvm-s390.h"
 
-/* Convert real to absolute address by applying the prefix of the CPU */
+/**
+ * kvm_s390_real_to_abs - convert guest real address to guest absolute address
+ * @vcpu - guest virtual cpu
+ * @gra - guest real address
+ *
+ * Returns the guest absolute address that corresponds to the passed guest real
+ * address @gra of a virtual guest cpu by applying its prefix.
+ */
 static inline unsigned long kvm_s390_real_to_abs(struct kvm_vcpu *vcpu,
-                                                unsigned long gaddr)
+                                                unsigned long gra)
 {
-       unsigned long prefix  = vcpu->arch.sie_block->prefix;
-       if (gaddr < 2 * PAGE_SIZE)
-               gaddr += prefix;
-       else if (gaddr >= prefix && gaddr < prefix + 2 * PAGE_SIZE)
-               gaddr -= prefix;
-       return gaddr;
+       unsigned long prefix  = kvm_s390_get_prefix(vcpu);
+
+       if (gra < 2 * PAGE_SIZE)
+               gra += prefix;
+       else if (gra >= prefix && gra < prefix + 2 * PAGE_SIZE)
+               gra -= prefix;
+       return gra;
 }
 
-static inline void __user *__gptr_to_uptr(struct kvm_vcpu *vcpu,
-                                         void __user *gptr,
-                                         int prefixing)
+/**
+ * kvm_s390_logical_to_effective - convert guest logical to effective address
+ * @vcpu: guest virtual cpu
+ * @ga: guest logical address
+ *
+ * Convert a guest vcpu logical address to a guest vcpu effective address by
+ * applying the rules of the vcpu's addressing mode defined by PSW bits 31
+ * and 32 (extendended/basic addressing mode).
+ *
+ * Depending on the vcpu's addressing mode the upper 40 bits (24 bit addressing
+ * mode), 33 bits (31 bit addressing mode) or no bits (64 bit addressing mode)
+ * of @ga will be zeroed and the remaining bits will be returned.
+ */
+static inline unsigned long kvm_s390_logical_to_effective(struct kvm_vcpu *vcpu,
+                                                         unsigned long ga)
 {
-       unsigned long gaddr = (unsigned long) gptr;
-       unsigned long uaddr;
-
-       if (prefixing)
-               gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
-       uaddr = gmap_fault(gaddr, vcpu->arch.gmap);
-       if (IS_ERR_VALUE(uaddr))
-               uaddr = -EFAULT;
-       return (void __user *)uaddr;
+       psw_t *psw = &vcpu->arch.sie_block->gpsw;
+
+       if (psw_bits(*psw).eaba == PSW_AMODE_64BIT)
+               return ga;
+       if (psw_bits(*psw).eaba == PSW_AMODE_31BIT)
+               return ga & ((1UL << 31) - 1);
+       return ga & ((1UL << 24) - 1);
 }
 
-#define get_guest(vcpu, x, gptr)                               \
-({                                                             \
-       __typeof__(gptr) __uptr = __gptr_to_uptr(vcpu, gptr, 1);\
-       int __mask = sizeof(__typeof__(*(gptr))) - 1;           \
-       int __ret;                                              \
-                                                               \
-       if (IS_ERR((void __force *)__uptr)) {                   \
-               __ret = PTR_ERR((void __force *)__uptr);        \
-       } else {                                                \
-               BUG_ON((unsigned long)__uptr & __mask);         \
-               __ret = get_user(x, __uptr);                    \
-       }                                                       \
-       __ret;                                                  \
-})
+/*
+ * put_guest_lc, read_guest_lc and write_guest_lc are guest access functions
+ * which shall only be used to access the lowcore of a vcpu.
+ * These functions should be used for e.g. interrupt handlers where no
+ * guest memory access protection facilities, like key or low address
+ * protection, are applicable.
+ * At a later point guest vcpu lowcore access should happen via pinned
+ * prefix pages, so that these pages can be accessed directly via the
+ * kernel mapping. All of these *_lc functions can be removed then.
+ */
 
-#define put_guest(vcpu, x, gptr)                               \
+/**
+ * put_guest_lc - write a simple variable to a guest vcpu's lowcore
+ * @vcpu: virtual cpu
+ * @x: value to copy to guest
+ * @gra: vcpu's destination guest real address
+ *
+ * Copies a simple value from kernel space to a guest vcpu's lowcore.
+ * The size of the variable may be 1, 2, 4 or 8 bytes. The destination
+ * must be located in the vcpu's lowcore. Otherwise the result is undefined.
+ *
+ * Returns zero on success or -EFAULT on error.
+ *
+ * Note: an error indicates that either the kernel is out of memory or
+ *      the guest memory mapping is broken. In any case the best solution
+ *      would be to terminate the guest.
+ *      It is wrong to inject a guest exception.
+ */
+#define put_guest_lc(vcpu, x, gra)                             \
 ({                                                             \
-       __typeof__(gptr) __uptr = __gptr_to_uptr(vcpu, gptr, 1);\
-       int __mask = sizeof(__typeof__(*(gptr))) - 1;           \
-       int __ret;                                              \
+       struct kvm_vcpu *__vcpu = (vcpu);                       \
+       __typeof__(*(gra)) __x = (x);                           \
+       unsigned long __gpa;                                    \
                                                                \
-       if (IS_ERR((void __force *)__uptr)) {                   \
-               __ret = PTR_ERR((void __force *)__uptr);        \
-       } else {                                                \
-               BUG_ON((unsigned long)__uptr & __mask);         \
-               __ret = put_user(x, __uptr);                    \
-       }                                                       \
-       __ret;                                                  \
+       __gpa = (unsigned long)(gra);                           \
+       __gpa += kvm_s390_get_prefix(__vcpu);                   \
+       kvm_write_guest(__vcpu->kvm, __gpa, &__x, sizeof(__x)); \
 })
 
-static inline int __copy_guest(struct kvm_vcpu *vcpu, unsigned long to,
-                              unsigned long from, unsigned long len,
-                              int to_guest, int prefixing)
+/**
+ * write_guest_lc - copy data from kernel space to guest vcpu's lowcore
+ * @vcpu: virtual cpu
+ * @gra: vcpu's source guest real address
+ * @data: source address in kernel space
+ * @len: number of bytes to copy
+ *
+ * Copy data from kernel space to guest vcpu's lowcore. The entire range must
+ * be located within the vcpu's lowcore, otherwise the result is undefined.
+ *
+ * Returns zero on success or -EFAULT on error.
+ *
+ * Note: an error indicates that either the kernel is out of memory or
+ *      the guest memory mapping is broken. In any case the best solution
+ *      would be to terminate the guest.
+ *      It is wrong to inject a guest exception.
+ */
+static inline __must_check
+int write_guest_lc(struct kvm_vcpu *vcpu, unsigned long gra, void *data,
+                  unsigned long len)
+{
+       unsigned long gpa = gra + kvm_s390_get_prefix(vcpu);
+
+       return kvm_write_guest(vcpu->kvm, gpa, data, len);
+}
+
+/**
+ * read_guest_lc - copy data from guest vcpu's lowcore to kernel space
+ * @vcpu: virtual cpu
+ * @gra: vcpu's source guest real address
+ * @data: destination address in kernel space
+ * @len: number of bytes to copy
+ *
+ * Copy data from guest vcpu's lowcore to kernel space. The entire range must
+ * be located within the vcpu's lowcore, otherwise the result is undefined.
+ *
+ * Returns zero on success or -EFAULT on error.
+ *
+ * Note: an error indicates that either the kernel is out of memory or
+ *      the guest memory mapping is broken. In any case the best solution
+ *      would be to terminate the guest.
+ *      It is wrong to inject a guest exception.
+ */
+static inline __must_check
+int read_guest_lc(struct kvm_vcpu *vcpu, unsigned long gra, void *data,
+                 unsigned long len)
+{
+       unsigned long gpa = gra + kvm_s390_get_prefix(vcpu);
+
+       return kvm_read_guest(vcpu->kvm, gpa, data, len);
+}
+
+int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva,
+                           unsigned long *gpa, int write);
+
+int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, void *data,
+                unsigned long len, int write);
+
+int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
+                     void *data, unsigned long len, int write);
+
+/**
+ * write_guest - copy data from kernel space to guest space
+ * @vcpu: virtual cpu
+ * @ga: guest address
+ * @data: source address in kernel space
+ * @len: number of bytes to copy
+ *
+ * Copy @len bytes from @data (kernel space) to @ga (guest address).
+ * In order to copy data to guest space the PSW of the vcpu is inspected:
+ * If DAT is off data will be copied to guest real or absolute memory.
+ * If DAT is on data will be copied to the address space as specified by
+ * the address space bits of the PSW:
+ * Primary, secondory or home space (access register mode is currently not
+ * implemented).
+ * The addressing mode of the PSW is also inspected, so that address wrap
+ * around is taken into account for 24-, 31- and 64-bit addressing mode,
+ * if the to be copied data crosses page boundaries in guest address space.
+ * In addition also low address and DAT protection are inspected before
+ * copying any data (key protection is currently not implemented).
+ *
+ * This function modifies the 'struct kvm_s390_pgm_info pgm' member of @vcpu.
+ * In case of an access exception (e.g. protection exception) pgm will contain
+ * all data necessary so that a subsequent call to 'kvm_s390_inject_prog_vcpu()'
+ * will inject a correct exception into the guest.
+ * If no access exception happened, the contents of pgm are undefined when
+ * this function returns.
+ *
+ * Returns:  - zero on success
+ *          - a negative value if e.g. the guest mapping is broken or in
+ *            case of out-of-memory. In this case the contents of pgm are
+ *            undefined. Also parts of @data may have been copied to guest
+ *            space.
+ *          - a positive value if an access exception happened. In this case
+ *            the returned value is the program interruption code and the
+ *            contents of pgm may be used to inject an exception into the
+ *            guest. No data has been copied to guest space.
+ *
+ * Note: in case an access exception is recognized no data has been copied to
+ *      guest space (this is also true, if the to be copied data would cross
+ *      one or more page boundaries in guest space).
+ *      Therefore this function may be used for nullifying and suppressing
+ *      instruction emulation.
+ *      It may also be used for terminating instructions, if it is undefined
+ *      if data has been changed in guest space in case of an exception.
+ */
+static inline __must_check
+int write_guest(struct kvm_vcpu *vcpu, unsigned long ga, void *data,
+               unsigned long len)
+{
+       return access_guest(vcpu, ga, data, len, 1);
+}
+
+/**
+ * read_guest - copy data from guest space to kernel space
+ * @vcpu: virtual cpu
+ * @ga: guest address
+ * @data: destination address in kernel space
+ * @len: number of bytes to copy
+ *
+ * Copy @len bytes from @ga (guest address) to @data (kernel space).
+ *
+ * The behaviour of read_guest is identical to write_guest, except that
+ * data will be copied from guest space to kernel space.
+ */
+static inline __must_check
+int read_guest(struct kvm_vcpu *vcpu, unsigned long ga, void *data,
+              unsigned long len)
+{
+       return access_guest(vcpu, ga, data, len, 0);
+}
+
+/**
+ * write_guest_abs - copy data from kernel space to guest space absolute
+ * @vcpu: virtual cpu
+ * @gpa: guest physical (absolute) address
+ * @data: source address in kernel space
+ * @len: number of bytes to copy
+ *
+ * Copy @len bytes from @data (kernel space) to @gpa (guest absolute address).
+ * It is up to the caller to ensure that the entire guest memory range is
+ * valid memory before calling this function.
+ * Guest low address and key protection are not checked.
+ *
+ * Returns zero on success or -EFAULT on error.
+ *
+ * If an error occurs data may have been copied partially to guest memory.
+ */
+static inline __must_check
+int write_guest_abs(struct kvm_vcpu *vcpu, unsigned long gpa, void *data,
+                   unsigned long len)
+{
+       return kvm_write_guest(vcpu->kvm, gpa, data, len);
+}
+
+/**
+ * read_guest_abs - copy data from guest space absolute to kernel space
+ * @vcpu: virtual cpu
+ * @gpa: guest physical (absolute) address
+ * @data: destination address in kernel space
+ * @len: number of bytes to copy
+ *
+ * Copy @len bytes from @gpa (guest absolute address) to @data (kernel space).
+ * It is up to the caller to ensure that the entire guest memory range is
+ * valid memory before calling this function.
+ * Guest key protection is not checked.
+ *
+ * Returns zero on success or -EFAULT on error.
+ *
+ * If an error occurs data may have been copied partially to kernel space.
+ */
+static inline __must_check
+int read_guest_abs(struct kvm_vcpu *vcpu, unsigned long gpa, void *data,
+                  unsigned long len)
+{
+       return kvm_read_guest(vcpu->kvm, gpa, data, len);
+}
+
+/**
+ * write_guest_real - copy data from kernel space to guest space real
+ * @vcpu: virtual cpu
+ * @gra: guest real address
+ * @data: source address in kernel space
+ * @len: number of bytes to copy
+ *
+ * Copy @len bytes from @data (kernel space) to @gra (guest real address).
+ * It is up to the caller to ensure that the entire guest memory range is
+ * valid memory before calling this function.
+ * Guest low address and key protection are not checked.
+ *
+ * Returns zero on success or -EFAULT on error.
+ *
+ * If an error occurs data may have been copied partially to guest memory.
+ */
+static inline __must_check
+int write_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, void *data,
+                    unsigned long len)
+{
+       return access_guest_real(vcpu, gra, data, len, 1);
+}
+
+/**
+ * read_guest_real - copy data from guest space real to kernel space
+ * @vcpu: virtual cpu
+ * @gra: guest real address
+ * @data: destination address in kernel space
+ * @len: number of bytes to copy
+ *
+ * Copy @len bytes from @gra (guest real address) to @data (kernel space).
+ * It is up to the caller to ensure that the entire guest memory range is
+ * valid memory before calling this function.
+ * Guest key protection is not checked.
+ *
+ * Returns zero on success or -EFAULT on error.
+ *
+ * If an error occurs data may have been copied partially to kernel space.
+ */
+static inline __must_check
+int read_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, void *data,
+                   unsigned long len)
 {
-       unsigned long _len, rc;
-       void __user *uptr;
-
-       while (len) {
-               uptr = to_guest ? (void __user *)to : (void __user *)from;
-               uptr = __gptr_to_uptr(vcpu, uptr, prefixing);
-               if (IS_ERR((void __force *)uptr))
-                       return -EFAULT;
-               _len = PAGE_SIZE - ((unsigned long)uptr & (PAGE_SIZE - 1));
-               _len = min(_len, len);
-               if (to_guest)
-                       rc = copy_to_user((void __user *) uptr, (void *)from, _len);
-               else
-                       rc = copy_from_user((void *)to, (void __user *)uptr, _len);
-               if (rc)
-                       return -EFAULT;
-               len -= _len;
-               from += _len;
-               to += _len;
-       }
-       return 0;
+       return access_guest_real(vcpu, gra, data, len, 0);
 }
 
-#define copy_to_guest(vcpu, to, from, size) \
-       __copy_guest(vcpu, to, (unsigned long)from, size, 1, 1)
-#define copy_from_guest(vcpu, to, from, size) \
-       __copy_guest(vcpu, (unsigned long)to, from, size, 0, 1)
-#define copy_to_guest_absolute(vcpu, to, from, size) \
-       __copy_guest(vcpu, to, (unsigned long)from, size, 1, 0)
-#define copy_from_guest_absolute(vcpu, to, from, size) \
-       __copy_guest(vcpu, (unsigned long)to, from, size, 0, 0)
+void ipte_lock(struct kvm_vcpu *vcpu);
+void ipte_unlock(struct kvm_vcpu *vcpu);
+int ipte_lock_held(struct kvm_vcpu *vcpu);
+int kvm_s390_check_low_addr_protection(struct kvm_vcpu *vcpu, unsigned long ga);
 
 #endif /* __KVM_S390_GACCESS_H */
diff --git a/arch/s390/kvm/guestdbg.c b/arch/s390/kvm/guestdbg.c
new file mode 100644 (file)
index 0000000..3e8d409
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * kvm guest debug support
+ *
+ * Copyright IBM Corp. 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ *
+ *    Author(s): David Hildenbrand <dahi@linux.vnet.ibm.com>
+ */
+#include <linux/kvm_host.h>
+#include <linux/errno.h>
+#include "kvm-s390.h"
+#include "gaccess.h"
+
+/*
+ * Extends the address range given by *start and *stop to include the address
+ * range starting with estart and the length len. Takes care of overflowing
+ * intervals and tries to minimize the overall intervall size.
+ */
+static void extend_address_range(u64 *start, u64 *stop, u64 estart, int len)
+{
+       u64 estop;
+
+       if (len > 0)
+               len--;
+       else
+               len = 0;
+
+       estop = estart + len;
+
+       /* 0-0 range represents "not set" */
+       if ((*start == 0) && (*stop == 0)) {
+               *start = estart;
+               *stop = estop;
+       } else if (*start <= *stop) {
+               /* increase the existing range */
+               if (estart < *start)
+                       *start = estart;
+               if (estop > *stop)
+                       *stop = estop;
+       } else {
+               /* "overflowing" interval, whereby *stop > *start */
+               if (estart <= *stop) {
+                       if (estop > *stop)
+                               *stop = estop;
+               } else if (estop > *start) {
+                       if (estart < *start)
+                               *start = estart;
+               }
+               /* minimize the range */
+               else if ((estop - *stop) < (*start - estart))
+                       *stop = estop;
+               else
+                       *start = estart;
+       }
+}
+
+#define MAX_INST_SIZE 6
+
+static void enable_all_hw_bp(struct kvm_vcpu *vcpu)
+{
+       unsigned long start, len;
+       u64 *cr9 = &vcpu->arch.sie_block->gcr[9];
+       u64 *cr10 = &vcpu->arch.sie_block->gcr[10];
+       u64 *cr11 = &vcpu->arch.sie_block->gcr[11];
+       int i;
+
+       if (vcpu->arch.guestdbg.nr_hw_bp <= 0 ||
+           vcpu->arch.guestdbg.hw_bp_info == NULL)
+               return;
+
+       /*
+        * If the guest is not interrested in branching events, we can savely
+        * limit them to the PER address range.
+        */
+       if (!(*cr9 & PER_EVENT_BRANCH))
+               *cr9 |= PER_CONTROL_BRANCH_ADDRESS;
+       *cr9 |= PER_EVENT_IFETCH | PER_EVENT_BRANCH;
+
+       for (i = 0; i < vcpu->arch.guestdbg.nr_hw_bp; i++) {
+               start = vcpu->arch.guestdbg.hw_bp_info[i].addr;
+               len = vcpu->arch.guestdbg.hw_bp_info[i].len;
+
+               /*
+                * The instruction in front of the desired bp has to
+                * report instruction-fetching events
+                */
+               if (start < MAX_INST_SIZE) {
+                       len += start;
+                       start = 0;
+               } else {
+                       start -= MAX_INST_SIZE;
+                       len += MAX_INST_SIZE;
+               }
+
+               extend_address_range(cr10, cr11, start, len);
+       }
+}
+
+static void enable_all_hw_wp(struct kvm_vcpu *vcpu)
+{
+       unsigned long start, len;
+       u64 *cr9 = &vcpu->arch.sie_block->gcr[9];
+       u64 *cr10 = &vcpu->arch.sie_block->gcr[10];
+       u64 *cr11 = &vcpu->arch.sie_block->gcr[11];
+       int i;
+
+       if (vcpu->arch.guestdbg.nr_hw_wp <= 0 ||
+           vcpu->arch.guestdbg.hw_wp_info == NULL)
+               return;
+
+       /* if host uses storage alternation for special address
+        * spaces, enable all events and give all to the guest */
+       if (*cr9 & PER_EVENT_STORE && *cr9 & PER_CONTROL_ALTERATION) {
+               *cr9 &= ~PER_CONTROL_ALTERATION;
+               *cr10 = 0;
+               *cr11 = PSW_ADDR_INSN;
+       } else {
+               *cr9 &= ~PER_CONTROL_ALTERATION;
+               *cr9 |= PER_EVENT_STORE;
+
+               for (i = 0; i < vcpu->arch.guestdbg.nr_hw_wp; i++) {
+                       start = vcpu->arch.guestdbg.hw_wp_info[i].addr;
+                       len = vcpu->arch.guestdbg.hw_wp_info[i].len;
+
+                       extend_address_range(cr10, cr11, start, len);
+               }
+       }
+}
+
+void kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu)
+{
+       vcpu->arch.guestdbg.cr0 = vcpu->arch.sie_block->gcr[0];
+       vcpu->arch.guestdbg.cr9 = vcpu->arch.sie_block->gcr[9];
+       vcpu->arch.guestdbg.cr10 = vcpu->arch.sie_block->gcr[10];
+       vcpu->arch.guestdbg.cr11 = vcpu->arch.sie_block->gcr[11];
+}
+
+void kvm_s390_restore_guest_per_regs(struct kvm_vcpu *vcpu)
+{
+       vcpu->arch.sie_block->gcr[0] = vcpu->arch.guestdbg.cr0;
+       vcpu->arch.sie_block->gcr[9] = vcpu->arch.guestdbg.cr9;
+       vcpu->arch.sie_block->gcr[10] = vcpu->arch.guestdbg.cr10;
+       vcpu->arch.sie_block->gcr[11] = vcpu->arch.guestdbg.cr11;
+}
+
+void kvm_s390_patch_guest_per_regs(struct kvm_vcpu *vcpu)
+{
+       /*
+        * TODO: if guest psw has per enabled, otherwise 0s!
+        * This reduces the amount of reported events.
+        * Need to intercept all psw changes!
+        */
+
+       if (guestdbg_sstep_enabled(vcpu)) {
+               /* disable timer (clock-comparator) interrupts */
+               vcpu->arch.sie_block->gcr[0] &= ~0x800ul;
+               vcpu->arch.sie_block->gcr[9] |= PER_EVENT_IFETCH;
+               vcpu->arch.sie_block->gcr[10] = 0;
+               vcpu->arch.sie_block->gcr[11] = PSW_ADDR_INSN;
+       }
+
+       if (guestdbg_hw_bp_enabled(vcpu)) {
+               enable_all_hw_bp(vcpu);
+               enable_all_hw_wp(vcpu);
+       }
+
+       /* TODO: Instruction-fetching-nullification not allowed for now */
+       if (vcpu->arch.sie_block->gcr[9] & PER_EVENT_NULLIFICATION)
+               vcpu->arch.sie_block->gcr[9] &= ~PER_EVENT_NULLIFICATION;
+}
+
+#define MAX_WP_SIZE 100
+
+static int __import_wp_info(struct kvm_vcpu *vcpu,
+                           struct kvm_hw_breakpoint *bp_data,
+                           struct kvm_hw_wp_info_arch *wp_info)
+{
+       int ret = 0;
+       wp_info->len = bp_data->len;
+       wp_info->addr = bp_data->addr;
+       wp_info->phys_addr = bp_data->phys_addr;
+       wp_info->old_data = NULL;
+
+       if (wp_info->len < 0 || wp_info->len > MAX_WP_SIZE)
+               return -EINVAL;
+
+       wp_info->old_data = kmalloc(bp_data->len, GFP_KERNEL);
+       if (!wp_info->old_data)
+               return -ENOMEM;
+       /* try to backup the original value */
+       ret = read_guest(vcpu, wp_info->phys_addr, wp_info->old_data,
+                        wp_info->len);
+       if (ret) {
+               kfree(wp_info->old_data);
+               wp_info->old_data = NULL;
+       }
+
+       return ret;
+}
+
+#define MAX_BP_COUNT 50
+
+int kvm_s390_import_bp_data(struct kvm_vcpu *vcpu,
+                           struct kvm_guest_debug *dbg)
+{
+       int ret = 0, nr_wp = 0, nr_bp = 0, i, size;
+       struct kvm_hw_breakpoint *bp_data = NULL;
+       struct kvm_hw_wp_info_arch *wp_info = NULL;
+       struct kvm_hw_bp_info_arch *bp_info = NULL;
+
+       if (dbg->arch.nr_hw_bp <= 0 || !dbg->arch.hw_bp)
+               return 0;
+       else if (dbg->arch.nr_hw_bp > MAX_BP_COUNT)
+               return -EINVAL;
+
+       size = dbg->arch.nr_hw_bp * sizeof(struct kvm_hw_breakpoint);
+       bp_data = kmalloc(size, GFP_KERNEL);
+       if (!bp_data) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       if (copy_from_user(bp_data, dbg->arch.hw_bp, size)) {
+               ret = -EFAULT;
+               goto error;
+       }
+
+       for (i = 0; i < dbg->arch.nr_hw_bp; i++) {
+               switch (bp_data[i].type) {
+               case KVM_HW_WP_WRITE:
+                       nr_wp++;
+                       break;
+               case KVM_HW_BP:
+                       nr_bp++;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       size = nr_wp * sizeof(struct kvm_hw_wp_info_arch);
+       if (size > 0) {
+               wp_info = kmalloc(size, GFP_KERNEL);
+               if (!wp_info) {
+                       ret = -ENOMEM;
+                       goto error;
+               }
+       }
+       size = nr_bp * sizeof(struct kvm_hw_bp_info_arch);
+       if (size > 0) {
+               bp_info = kmalloc(size, GFP_KERNEL);
+               if (!bp_info) {
+                       ret = -ENOMEM;
+                       goto error;
+               }
+       }
+
+       for (nr_wp = 0, nr_bp = 0, i = 0; i < dbg->arch.nr_hw_bp; i++) {
+               switch (bp_data[i].type) {
+               case KVM_HW_WP_WRITE:
+                       ret = __import_wp_info(vcpu, &bp_data[i],
+                                              &wp_info[nr_wp]);
+                       if (ret)
+                               goto error;
+                       nr_wp++;
+                       break;
+               case KVM_HW_BP:
+                       bp_info[nr_bp].len = bp_data[i].len;
+                       bp_info[nr_bp].addr = bp_data[i].addr;
+                       nr_bp++;
+                       break;
+               }
+       }
+
+       vcpu->arch.guestdbg.nr_hw_bp = nr_bp;
+       vcpu->arch.guestdbg.hw_bp_info = bp_info;
+       vcpu->arch.guestdbg.nr_hw_wp = nr_wp;
+       vcpu->arch.guestdbg.hw_wp_info = wp_info;
+       return 0;
+error:
+       kfree(bp_data);
+       kfree(wp_info);
+       kfree(bp_info);
+       return ret;
+}
+
+void kvm_s390_clear_bp_data(struct kvm_vcpu *vcpu)
+{
+       int i;
+       struct kvm_hw_wp_info_arch *hw_wp_info = NULL;
+
+       for (i = 0; i < vcpu->arch.guestdbg.nr_hw_wp; i++) {
+               hw_wp_info = &vcpu->arch.guestdbg.hw_wp_info[i];
+               kfree(hw_wp_info->old_data);
+               hw_wp_info->old_data = NULL;
+       }
+       kfree(vcpu->arch.guestdbg.hw_wp_info);
+       vcpu->arch.guestdbg.hw_wp_info = NULL;
+
+       kfree(vcpu->arch.guestdbg.hw_bp_info);
+       vcpu->arch.guestdbg.hw_bp_info = NULL;
+
+       vcpu->arch.guestdbg.nr_hw_wp = 0;
+       vcpu->arch.guestdbg.nr_hw_bp = 0;
+}
+
+static inline int in_addr_range(u64 addr, u64 a, u64 b)
+{
+       if (a <= b)
+               return (addr >= a) && (addr <= b);
+       else
+               /* "overflowing" interval */
+               return (addr <= a) && (addr >= b);
+}
+
+#define end_of_range(bp_info) (bp_info->addr + bp_info->len - 1)
+
+static struct kvm_hw_bp_info_arch *find_hw_bp(struct kvm_vcpu *vcpu,
+                                             unsigned long addr)
+{
+       struct kvm_hw_bp_info_arch *bp_info = vcpu->arch.guestdbg.hw_bp_info;
+       int i;
+
+       if (vcpu->arch.guestdbg.nr_hw_bp == 0)
+               return NULL;
+
+       for (i = 0; i < vcpu->arch.guestdbg.nr_hw_bp; i++) {
+               /* addr is directly the start or in the range of a bp */
+               if (addr == bp_info->addr)
+                       goto found;
+               if (bp_info->len > 0 &&
+                   in_addr_range(addr, bp_info->addr, end_of_range(bp_info)))
+                       goto found;
+
+               bp_info++;
+       }
+
+       return NULL;
+found:
+       return bp_info;
+}
+
+static struct kvm_hw_wp_info_arch *any_wp_changed(struct kvm_vcpu *vcpu)
+{
+       int i;
+       struct kvm_hw_wp_info_arch *wp_info = NULL;
+       void *temp = NULL;
+
+       if (vcpu->arch.guestdbg.nr_hw_wp == 0)
+               return NULL;
+
+       for (i = 0; i < vcpu->arch.guestdbg.nr_hw_wp; i++) {
+               wp_info = &vcpu->arch.guestdbg.hw_wp_info[i];
+               if (!wp_info || !wp_info->old_data || wp_info->len <= 0)
+                       continue;
+
+               temp = kmalloc(wp_info->len, GFP_KERNEL);
+               if (!temp)
+                       continue;
+
+               /* refetch the wp data and compare it to the old value */
+               if (!read_guest(vcpu, wp_info->phys_addr, temp,
+                               wp_info->len)) {
+                       if (memcmp(temp, wp_info->old_data, wp_info->len)) {
+                               kfree(temp);
+                               return wp_info;
+                       }
+               }
+               kfree(temp);
+               temp = NULL;
+       }
+
+       return NULL;
+}
+
+void kvm_s390_prepare_debug_exit(struct kvm_vcpu *vcpu)
+{
+       vcpu->run->exit_reason = KVM_EXIT_DEBUG;
+       vcpu->guest_debug &= ~KVM_GUESTDBG_EXIT_PENDING;
+}
+
+#define per_bp_event(code) \
+                       (code & (PER_EVENT_IFETCH | PER_EVENT_BRANCH))
+#define per_write_wp_event(code) \
+                       (code & (PER_EVENT_STORE | PER_EVENT_STORE_REAL))
+
+static int debug_exit_required(struct kvm_vcpu *vcpu)
+{
+       u32 perc = (vcpu->arch.sie_block->perc << 24);
+       struct kvm_debug_exit_arch *debug_exit = &vcpu->run->debug.arch;
+       struct kvm_hw_wp_info_arch *wp_info = NULL;
+       struct kvm_hw_bp_info_arch *bp_info = NULL;
+       unsigned long addr = vcpu->arch.sie_block->gpsw.addr;
+       unsigned long peraddr = vcpu->arch.sie_block->peraddr;
+
+       if (guestdbg_hw_bp_enabled(vcpu)) {
+               if (per_write_wp_event(perc) &&
+                   vcpu->arch.guestdbg.nr_hw_wp > 0) {
+                       wp_info = any_wp_changed(vcpu);
+                       if (wp_info) {
+                               debug_exit->addr = wp_info->addr;
+                               debug_exit->type = KVM_HW_WP_WRITE;
+                               goto exit_required;
+                       }
+               }
+               if (per_bp_event(perc) &&
+                        vcpu->arch.guestdbg.nr_hw_bp > 0) {
+                       bp_info = find_hw_bp(vcpu, addr);
+                       /* remove duplicate events if PC==PER address */
+                       if (bp_info && (addr != peraddr)) {
+                               debug_exit->addr = addr;
+                               debug_exit->type = KVM_HW_BP;
+                               vcpu->arch.guestdbg.last_bp = addr;
+                               goto exit_required;
+                       }
+                       /* breakpoint missed */
+                       bp_info = find_hw_bp(vcpu, peraddr);
+                       if (bp_info && vcpu->arch.guestdbg.last_bp != peraddr) {
+                               debug_exit->addr = peraddr;
+                               debug_exit->type = KVM_HW_BP;
+                               goto exit_required;
+                       }
+               }
+       }
+       if (guestdbg_sstep_enabled(vcpu) && per_bp_event(perc)) {
+               debug_exit->addr = addr;
+               debug_exit->type = KVM_SINGLESTEP;
+               goto exit_required;
+       }
+
+       return 0;
+exit_required:
+       return 1;
+}
+
+#define guest_per_enabled(vcpu) \
+                            (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER)
+
+static void filter_guest_per_event(struct kvm_vcpu *vcpu)
+{
+       u32 perc = vcpu->arch.sie_block->perc << 24;
+       u64 peraddr = vcpu->arch.sie_block->peraddr;
+       u64 addr = vcpu->arch.sie_block->gpsw.addr;
+       u64 cr9 = vcpu->arch.sie_block->gcr[9];
+       u64 cr10 = vcpu->arch.sie_block->gcr[10];
+       u64 cr11 = vcpu->arch.sie_block->gcr[11];
+       /* filter all events, demanded by the guest */
+       u32 guest_perc = perc & cr9 & PER_EVENT_MASK;
+
+       if (!guest_per_enabled(vcpu))
+               guest_perc = 0;
+
+       /* filter "successful-branching" events */
+       if (guest_perc & PER_EVENT_BRANCH &&
+           cr9 & PER_CONTROL_BRANCH_ADDRESS &&
+           !in_addr_range(addr, cr10, cr11))
+               guest_perc &= ~PER_EVENT_BRANCH;
+
+       /* filter "instruction-fetching" events */
+       if (guest_perc & PER_EVENT_IFETCH &&
+           !in_addr_range(peraddr, cr10, cr11))
+               guest_perc &= ~PER_EVENT_IFETCH;
+
+       /* All other PER events will be given to the guest */
+       /* TODO: Check alterated address/address space */
+
+       vcpu->arch.sie_block->perc = guest_perc >> 24;
+
+       if (!guest_perc)
+               vcpu->arch.sie_block->iprcc &= ~PGM_PER;
+}
+
+void kvm_s390_handle_per_event(struct kvm_vcpu *vcpu)
+{
+       if (debug_exit_required(vcpu))
+               vcpu->guest_debug |= KVM_GUESTDBG_EXIT_PENDING;
+
+       filter_guest_per_event(vcpu);
+}
index eeb1ac7..a0b586c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * in-kernel handling for sie intercepts
  *
- * Copyright IBM Corp. 2008, 2009
+ * Copyright IBM Corp. 2008, 2014
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License (version 2 only)
@@ -16,6 +16,8 @@
 #include <linux/pagemap.h>
 
 #include <asm/kvm_host.h>
+#include <asm/asm-offsets.h>
+#include <asm/irq.h>
 
 #include "kvm-s390.h"
 #include "gaccess.h"
@@ -29,6 +31,7 @@ static const intercept_handler_t instruction_handlers[256] = {
        [0x83] = kvm_s390_handle_diag,
        [0xae] = kvm_s390_handle_sigp,
        [0xb2] = kvm_s390_handle_b2,
+       [0xb6] = kvm_s390_handle_stctl,
        [0xb7] = kvm_s390_handle_lctl,
        [0xb9] = kvm_s390_handle_b9,
        [0xe5] = kvm_s390_handle_e5,
@@ -44,9 +47,6 @@ static int handle_noop(struct kvm_vcpu *vcpu)
        case 0x10:
                vcpu->stat.exit_external_request++;
                break;
-       case 0x14:
-               vcpu->stat.exit_external_interrupt++;
-               break;
        default:
                break; /* nothing */
        }
@@ -63,8 +63,7 @@ static int handle_stop(struct kvm_vcpu *vcpu)
        trace_kvm_s390_stop_request(vcpu->arch.local_int.action_bits);
 
        if (vcpu->arch.local_int.action_bits & ACTION_STOP_ON_STOP) {
-               atomic_set_mask(CPUSTAT_STOPPED,
-                               &vcpu->arch.sie_block->cpuflags);
+               kvm_s390_vcpu_stop(vcpu);
                vcpu->arch.local_int.action_bits &= ~ACTION_STOP_ON_STOP;
                VCPU_EVENT(vcpu, 3, "%s", "cpu stopped");
                rc = -EOPNOTSUPP;
@@ -109,22 +108,120 @@ static int handle_instruction(struct kvm_vcpu *vcpu)
        return -EOPNOTSUPP;
 }
 
+static void __extract_prog_irq(struct kvm_vcpu *vcpu,
+                              struct kvm_s390_pgm_info *pgm_info)
+{
+       memset(pgm_info, 0, sizeof(struct kvm_s390_pgm_info));
+       pgm_info->code = vcpu->arch.sie_block->iprcc;
+
+       switch (vcpu->arch.sie_block->iprcc & ~PGM_PER) {
+       case PGM_AFX_TRANSLATION:
+       case PGM_ASX_TRANSLATION:
+       case PGM_EX_TRANSLATION:
+       case PGM_LFX_TRANSLATION:
+       case PGM_LSTE_SEQUENCE:
+       case PGM_LSX_TRANSLATION:
+       case PGM_LX_TRANSLATION:
+       case PGM_PRIMARY_AUTHORITY:
+       case PGM_SECONDARY_AUTHORITY:
+       case PGM_SPACE_SWITCH:
+               pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc;
+               break;
+       case PGM_ALEN_TRANSLATION:
+       case PGM_ALE_SEQUENCE:
+       case PGM_ASTE_INSTANCE:
+       case PGM_ASTE_SEQUENCE:
+       case PGM_ASTE_VALIDITY:
+       case PGM_EXTENDED_AUTHORITY:
+               pgm_info->exc_access_id = vcpu->arch.sie_block->eai;
+               break;
+       case PGM_ASCE_TYPE:
+       case PGM_PAGE_TRANSLATION:
+       case PGM_REGION_FIRST_TRANS:
+       case PGM_REGION_SECOND_TRANS:
+       case PGM_REGION_THIRD_TRANS:
+       case PGM_SEGMENT_TRANSLATION:
+               pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc;
+               pgm_info->exc_access_id  = vcpu->arch.sie_block->eai;
+               pgm_info->op_access_id  = vcpu->arch.sie_block->oai;
+               break;
+       case PGM_MONITOR:
+               pgm_info->mon_class_nr = vcpu->arch.sie_block->mcn;
+               pgm_info->mon_code = vcpu->arch.sie_block->tecmc;
+               break;
+       case PGM_DATA:
+               pgm_info->data_exc_code = vcpu->arch.sie_block->dxc;
+               break;
+       case PGM_PROTECTION:
+               pgm_info->trans_exc_code = vcpu->arch.sie_block->tecmc;
+               pgm_info->exc_access_id  = vcpu->arch.sie_block->eai;
+               break;
+       default:
+               break;
+       }
+
+       if (vcpu->arch.sie_block->iprcc & PGM_PER) {
+               pgm_info->per_code = vcpu->arch.sie_block->perc;
+               pgm_info->per_atmid = vcpu->arch.sie_block->peratmid;
+               pgm_info->per_address = vcpu->arch.sie_block->peraddr;
+               pgm_info->per_access_id = vcpu->arch.sie_block->peraid;
+       }
+}
+
+/*
+ * restore ITDB to program-interruption TDB in guest lowcore
+ * and set TX abort indication if required
+*/
+static int handle_itdb(struct kvm_vcpu *vcpu)
+{
+       struct kvm_s390_itdb *itdb;
+       int rc;
+
+       if (!IS_TE_ENABLED(vcpu) || !IS_ITDB_VALID(vcpu))
+               return 0;
+       if (current->thread.per_flags & PER_FLAG_NO_TE)
+               return 0;
+       itdb = (struct kvm_s390_itdb *)vcpu->arch.sie_block->itdba;
+       rc = write_guest_lc(vcpu, __LC_PGM_TDB, itdb, sizeof(*itdb));
+       if (rc)
+               return rc;
+       memset(itdb, 0, sizeof(*itdb));
+
+       return 0;
+}
+
+#define per_event(vcpu) (vcpu->arch.sie_block->iprcc & PGM_PER)
+
 static int handle_prog(struct kvm_vcpu *vcpu)
 {
+       struct kvm_s390_pgm_info pgm_info;
+       psw_t psw;
+       int rc;
+
        vcpu->stat.exit_program_interruption++;
 
-       /* Restore ITDB to Program-Interruption TDB in guest memory */
-       if (IS_TE_ENABLED(vcpu) &&
-           !(current->thread.per_flags & PER_FLAG_NO_TE) &&
-           IS_ITDB_VALID(vcpu)) {
-               copy_to_guest(vcpu, TDB_ADDR, vcpu->arch.sie_block->itdba,
-                             sizeof(struct kvm_s390_itdb));
-               memset((void *) vcpu->arch.sie_block->itdba, 0,
-                      sizeof(struct kvm_s390_itdb));
+       if (guestdbg_enabled(vcpu) && per_event(vcpu)) {
+               kvm_s390_handle_per_event(vcpu);
+               /* the interrupt might have been filtered out completely */
+               if (vcpu->arch.sie_block->iprcc == 0)
+                       return 0;
        }
 
        trace_kvm_s390_intercept_prog(vcpu, vcpu->arch.sie_block->iprcc);
-       return kvm_s390_inject_program_int(vcpu, vcpu->arch.sie_block->iprcc);
+       if (vcpu->arch.sie_block->iprcc == PGM_SPECIFICATION) {
+               rc = read_guest_lc(vcpu, __LC_PGM_NEW_PSW, &psw, sizeof(psw_t));
+               if (rc)
+                       return rc;
+               /* Avoid endless loops of specification exceptions */
+               if (!is_valid_psw(&psw))
+                       return -EOPNOTSUPP;
+       }
+       rc = handle_itdb(vcpu);
+       if (rc)
+               return rc;
+
+       __extract_prog_irq(vcpu, &pgm_info);
+       return kvm_s390_inject_prog_irq(vcpu, &pgm_info);
 }
 
 static int handle_instruction_and_prog(struct kvm_vcpu *vcpu)
@@ -142,17 +239,110 @@ static int handle_instruction_and_prog(struct kvm_vcpu *vcpu)
        return rc2;
 }
 
+/**
+ * handle_external_interrupt - used for external interruption interceptions
+ *
+ * This interception only occurs if the CPUSTAT_EXT_INT bit was set, or if
+ * the new PSW does not have external interrupts disabled. In the first case,
+ * we've got to deliver the interrupt manually, and in the second case, we
+ * drop to userspace to handle the situation there.
+ */
+static int handle_external_interrupt(struct kvm_vcpu *vcpu)
+{
+       u16 eic = vcpu->arch.sie_block->eic;
+       struct kvm_s390_interrupt irq;
+       psw_t newpsw;
+       int rc;
+
+       vcpu->stat.exit_external_interrupt++;
+
+       rc = read_guest_lc(vcpu, __LC_EXT_NEW_PSW, &newpsw, sizeof(psw_t));
+       if (rc)
+               return rc;
+       /* We can not handle clock comparator or timer interrupt with bad PSW */
+       if ((eic == EXT_IRQ_CLK_COMP || eic == EXT_IRQ_CPU_TIMER) &&
+           (newpsw.mask & PSW_MASK_EXT))
+               return -EOPNOTSUPP;
+
+       switch (eic) {
+       case EXT_IRQ_CLK_COMP:
+               irq.type = KVM_S390_INT_CLOCK_COMP;
+               break;
+       case EXT_IRQ_CPU_TIMER:
+               irq.type = KVM_S390_INT_CPU_TIMER;
+               break;
+       case EXT_IRQ_EXTERNAL_CALL:
+               if (kvm_s390_si_ext_call_pending(vcpu))
+                       return 0;
+               irq.type = KVM_S390_INT_EXTERNAL_CALL;
+               irq.parm = vcpu->arch.sie_block->extcpuaddr;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return kvm_s390_inject_vcpu(vcpu, &irq);
+}
+
+/**
+ * Handle MOVE PAGE partial execution interception.
+ *
+ * This interception can only happen for guests with DAT disabled and
+ * addresses that are currently not mapped in the host. Thus we try to
+ * set up the mappings for the corresponding user pages here (or throw
+ * addressing exceptions in case of illegal guest addresses).
+ */
+static int handle_mvpg_pei(struct kvm_vcpu *vcpu)
+{
+       psw_t *psw = &vcpu->arch.sie_block->gpsw;
+       unsigned long srcaddr, dstaddr;
+       int reg1, reg2, rc;
+
+       kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
+
+       /* Make sure that the source is paged-in */
+       srcaddr = kvm_s390_real_to_abs(vcpu, vcpu->run->s.regs.gprs[reg2]);
+       if (kvm_is_error_gpa(vcpu->kvm, srcaddr))
+               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       rc = kvm_arch_fault_in_page(vcpu, srcaddr, 0);
+       if (rc != 0)
+               return rc;
+
+       /* Make sure that the destination is paged-in */
+       dstaddr = kvm_s390_real_to_abs(vcpu, vcpu->run->s.regs.gprs[reg1]);
+       if (kvm_is_error_gpa(vcpu->kvm, dstaddr))
+               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       rc = kvm_arch_fault_in_page(vcpu, dstaddr, 1);
+       if (rc != 0)
+               return rc;
+
+       psw->addr = __rewind_psw(*psw, 4);
+
+       return 0;
+}
+
+static int handle_partial_execution(struct kvm_vcpu *vcpu)
+{
+       if (vcpu->arch.sie_block->ipa == 0xb254)        /* MVPG */
+               return handle_mvpg_pei(vcpu);
+       if (vcpu->arch.sie_block->ipa >> 8 == 0xae)     /* SIGP */
+               return kvm_s390_handle_sigp_pei(vcpu);
+
+       return -EOPNOTSUPP;
+}
+
 static const intercept_handler_t intercept_funcs[] = {
        [0x00 >> 2] = handle_noop,
        [0x04 >> 2] = handle_instruction,
        [0x08 >> 2] = handle_prog,
        [0x0C >> 2] = handle_instruction_and_prog,
        [0x10 >> 2] = handle_noop,
-       [0x14 >> 2] = handle_noop,
+       [0x14 >> 2] = handle_external_interrupt,
        [0x18 >> 2] = handle_noop,
        [0x1C >> 2] = kvm_s390_handle_wait,
        [0x20 >> 2] = handle_validity,
        [0x28 >> 2] = handle_stop,
+       [0x38 >> 2] = handle_partial_execution,
 };
 
 int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
index 200a8f9..90c8de2 100644 (file)
@@ -27,6 +27,8 @@
 #define IOINT_CSSID_MASK 0x03fc0000
 #define IOINT_AI_MASK 0x04000000
 
+static void deliver_ckc_interrupt(struct kvm_vcpu *vcpu);
+
 static int is_ioint(u64 type)
 {
        return ((type & 0xfffe0000u) != 0xfffe0000u);
@@ -56,6 +58,17 @@ static int psw_interrupts_disabled(struct kvm_vcpu *vcpu)
        return 1;
 }
 
+static int ckc_interrupts_enabled(struct kvm_vcpu *vcpu)
+{
+       if (psw_extint_disabled(vcpu) ||
+           !(vcpu->arch.sie_block->gcr[0] & 0x800ul))
+               return 0;
+       if (guestdbg_enabled(vcpu) && guestdbg_sstep_enabled(vcpu))
+               /* No timer interrupts when single stepping */
+               return 0;
+       return 1;
+}
+
 static u64 int_word_to_isc_bits(u32 int_word)
 {
        u8 isc = (int_word & 0x38000000) >> 27;
@@ -78,6 +91,14 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu,
                if (vcpu->arch.sie_block->gcr[0] & 0x4000ul)
                        return 1;
                return 0;
+       case KVM_S390_INT_CLOCK_COMP:
+               return ckc_interrupts_enabled(vcpu);
+       case KVM_S390_INT_CPU_TIMER:
+               if (psw_extint_disabled(vcpu))
+                       return 0;
+               if (vcpu->arch.sie_block->gcr[0] & 0x400ul)
+                       return 1;
+               return 0;
        case KVM_S390_INT_SERVICE:
        case KVM_S390_INT_PFAULT_INIT:
        case KVM_S390_INT_PFAULT_DONE:
@@ -127,11 +148,16 @@ static void __unset_cpu_idle(struct kvm_vcpu *vcpu)
 
 static void __reset_intercept_indicators(struct kvm_vcpu *vcpu)
 {
-       atomic_clear_mask(CPUSTAT_ECALL_PEND |
-               CPUSTAT_IO_INT | CPUSTAT_EXT_INT | CPUSTAT_STOP_INT,
-               &vcpu->arch.sie_block->cpuflags);
+       atomic_clear_mask(CPUSTAT_IO_INT | CPUSTAT_EXT_INT | CPUSTAT_STOP_INT,
+                         &vcpu->arch.sie_block->cpuflags);
        vcpu->arch.sie_block->lctl = 0x0000;
-       vcpu->arch.sie_block->ictl &= ~ICTL_LPSW;
+       vcpu->arch.sie_block->ictl &= ~(ICTL_LPSW | ICTL_STCTL | ICTL_PINT);
+
+       if (guestdbg_enabled(vcpu)) {
+               vcpu->arch.sie_block->lctl |= (LCTL_CR0 | LCTL_CR9 |
+                                              LCTL_CR10 | LCTL_CR11);
+               vcpu->arch.sie_block->ictl |= (ICTL_STCTL | ICTL_PINT);
+       }
 }
 
 static void __set_cpuflag(struct kvm_vcpu *vcpu, u32 flag)
@@ -149,6 +175,8 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu,
        case KVM_S390_INT_PFAULT_INIT:
        case KVM_S390_INT_PFAULT_DONE:
        case KVM_S390_INT_VIRTIO:
+       case KVM_S390_INT_CLOCK_COMP:
+       case KVM_S390_INT_CPU_TIMER:
                if (psw_extint_disabled(vcpu))
                        __set_cpuflag(vcpu, CPUSTAT_EXT_INT);
                else
@@ -174,6 +202,106 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu,
        }
 }
 
+static int __deliver_prog_irq(struct kvm_vcpu *vcpu,
+                             struct kvm_s390_pgm_info *pgm_info)
+{
+       const unsigned short table[] = { 2, 4, 4, 6 };
+       int rc = 0;
+
+       switch (pgm_info->code & ~PGM_PER) {
+       case PGM_AFX_TRANSLATION:
+       case PGM_ASX_TRANSLATION:
+       case PGM_EX_TRANSLATION:
+       case PGM_LFX_TRANSLATION:
+       case PGM_LSTE_SEQUENCE:
+       case PGM_LSX_TRANSLATION:
+       case PGM_LX_TRANSLATION:
+       case PGM_PRIMARY_AUTHORITY:
+       case PGM_SECONDARY_AUTHORITY:
+       case PGM_SPACE_SWITCH:
+               rc = put_guest_lc(vcpu, pgm_info->trans_exc_code,
+                                 (u64 *)__LC_TRANS_EXC_CODE);
+               break;
+       case PGM_ALEN_TRANSLATION:
+       case PGM_ALE_SEQUENCE:
+       case PGM_ASTE_INSTANCE:
+       case PGM_ASTE_SEQUENCE:
+       case PGM_ASTE_VALIDITY:
+       case PGM_EXTENDED_AUTHORITY:
+               rc = put_guest_lc(vcpu, pgm_info->exc_access_id,
+                                 (u8 *)__LC_EXC_ACCESS_ID);
+               break;
+       case PGM_ASCE_TYPE:
+       case PGM_PAGE_TRANSLATION:
+       case PGM_REGION_FIRST_TRANS:
+       case PGM_REGION_SECOND_TRANS:
+       case PGM_REGION_THIRD_TRANS:
+       case PGM_SEGMENT_TRANSLATION:
+               rc = put_guest_lc(vcpu, pgm_info->trans_exc_code,
+                                 (u64 *)__LC_TRANS_EXC_CODE);
+               rc |= put_guest_lc(vcpu, pgm_info->exc_access_id,
+                                  (u8 *)__LC_EXC_ACCESS_ID);
+               rc |= put_guest_lc(vcpu, pgm_info->op_access_id,
+                                  (u8 *)__LC_OP_ACCESS_ID);
+               break;
+       case PGM_MONITOR:
+               rc = put_guest_lc(vcpu, pgm_info->mon_class_nr,
+                                 (u64 *)__LC_MON_CLASS_NR);
+               rc |= put_guest_lc(vcpu, pgm_info->mon_code,
+                                  (u64 *)__LC_MON_CODE);
+               break;
+       case PGM_DATA:
+               rc = put_guest_lc(vcpu, pgm_info->data_exc_code,
+                                 (u32 *)__LC_DATA_EXC_CODE);
+               break;
+       case PGM_PROTECTION:
+               rc = put_guest_lc(vcpu, pgm_info->trans_exc_code,
+                                 (u64 *)__LC_TRANS_EXC_CODE);
+               rc |= put_guest_lc(vcpu, pgm_info->exc_access_id,
+                                  (u8 *)__LC_EXC_ACCESS_ID);
+               break;
+       }
+
+       if (pgm_info->code & PGM_PER) {
+               rc |= put_guest_lc(vcpu, pgm_info->per_code,
+                                  (u8 *) __LC_PER_CODE);
+               rc |= put_guest_lc(vcpu, pgm_info->per_atmid,
+                                  (u8 *)__LC_PER_ATMID);
+               rc |= put_guest_lc(vcpu, pgm_info->per_address,
+                                  (u64 *) __LC_PER_ADDRESS);
+               rc |= put_guest_lc(vcpu, pgm_info->per_access_id,
+                                  (u8 *) __LC_PER_ACCESS_ID);
+       }
+
+       switch (vcpu->arch.sie_block->icptcode) {
+       case ICPT_INST:
+       case ICPT_INSTPROGI:
+       case ICPT_OPEREXC:
+       case ICPT_PARTEXEC:
+       case ICPT_IOINST:
+               /* last instruction only stored for these icptcodes */
+               rc |= put_guest_lc(vcpu, table[vcpu->arch.sie_block->ipa >> 14],
+                                  (u16 *) __LC_PGM_ILC);
+               break;
+       case ICPT_PROGI:
+               rc |= put_guest_lc(vcpu, vcpu->arch.sie_block->pgmilc,
+                                  (u16 *) __LC_PGM_ILC);
+               break;
+       default:
+               rc |= put_guest_lc(vcpu, 0,
+                                  (u16 *) __LC_PGM_ILC);
+       }
+
+       rc |= put_guest_lc(vcpu, pgm_info->code,
+                          (u16 *)__LC_PGM_INT_CODE);
+       rc |= write_guest_lc(vcpu, __LC_PGM_OLD_PSW,
+                            &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+       rc |= read_guest_lc(vcpu, __LC_PGM_NEW_PSW,
+                           &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+
+       return rc;
+}
+
 static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
                                   struct kvm_s390_interrupt_info *inti)
 {
@@ -186,26 +314,46 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
                vcpu->stat.deliver_emergency_signal++;
                trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
                                                 inti->emerg.code, 0);
-               rc  = put_guest(vcpu, 0x1201, (u16 __user *)__LC_EXT_INT_CODE);
-               rc |= put_guest(vcpu, inti->emerg.code,
-                               (u16 __user *)__LC_EXT_CPU_ADDR);
-               rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW,
+               rc  = put_guest_lc(vcpu, 0x1201, (u16 *)__LC_EXT_INT_CODE);
+               rc |= put_guest_lc(vcpu, inti->emerg.code,
+                                  (u16 *)__LC_EXT_CPU_ADDR);
+               rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
+                                    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+               rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
                                    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-               rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
-                                     __LC_EXT_NEW_PSW, sizeof(psw_t));
                break;
        case KVM_S390_INT_EXTERNAL_CALL:
                VCPU_EVENT(vcpu, 4, "%s", "interrupt: sigp ext call");
                vcpu->stat.deliver_external_call++;
                trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
                                                 inti->extcall.code, 0);
-               rc  = put_guest(vcpu, 0x1202, (u16 __user *)__LC_EXT_INT_CODE);
-               rc |= put_guest(vcpu, inti->extcall.code,
-                               (u16 __user *)__LC_EXT_CPU_ADDR);
-               rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW,
+               rc  = put_guest_lc(vcpu, 0x1202, (u16 *)__LC_EXT_INT_CODE);
+               rc |= put_guest_lc(vcpu, inti->extcall.code,
+                                  (u16 *)__LC_EXT_CPU_ADDR);
+               rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
+                                    &vcpu->arch.sie_block->gpsw,
+                                    sizeof(psw_t));
+               rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
+                                   &vcpu->arch.sie_block->gpsw,
+                                   sizeof(psw_t));
+               break;
+       case KVM_S390_INT_CLOCK_COMP:
+               trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
+                                                inti->ext.ext_params, 0);
+               deliver_ckc_interrupt(vcpu);
+               break;
+       case KVM_S390_INT_CPU_TIMER:
+               trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
+                                                inti->ext.ext_params, 0);
+               rc  = put_guest_lc(vcpu, EXT_IRQ_CPU_TIMER,
+                                  (u16 *)__LC_EXT_INT_CODE);
+               rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
+                                    &vcpu->arch.sie_block->gpsw,
+                                    sizeof(psw_t));
+               rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
                                    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-               rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
-                                     __LC_EXT_NEW_PSW, sizeof(psw_t));
+               rc |= put_guest_lc(vcpu, inti->ext.ext_params,
+                                  (u32 *)__LC_EXT_PARAMS);
                break;
        case KVM_S390_INT_SERVICE:
                VCPU_EVENT(vcpu, 4, "interrupt: sclp parm:%x",
@@ -213,37 +361,39 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
                vcpu->stat.deliver_service_signal++;
                trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
                                                 inti->ext.ext_params, 0);
-               rc  = put_guest(vcpu, 0x2401, (u16 __user *)__LC_EXT_INT_CODE);
-               rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW,
+               rc  = put_guest_lc(vcpu, 0x2401, (u16 *)__LC_EXT_INT_CODE);
+               rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
+                                    &vcpu->arch.sie_block->gpsw,
+                                    sizeof(psw_t));
+               rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
                                    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-               rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
-                                     __LC_EXT_NEW_PSW, sizeof(psw_t));
-               rc |= put_guest(vcpu, inti->ext.ext_params,
-                               (u32 __user *)__LC_EXT_PARAMS);
+               rc |= put_guest_lc(vcpu, inti->ext.ext_params,
+                                  (u32 *)__LC_EXT_PARAMS);
                break;
        case KVM_S390_INT_PFAULT_INIT:
                trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, 0,
                                                 inti->ext.ext_params2);
-               rc  = put_guest(vcpu, 0x2603, (u16 __user *) __LC_EXT_INT_CODE);
-               rc |= put_guest(vcpu, 0x0600, (u16 __user *) __LC_EXT_CPU_ADDR);
-               rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW,
+               rc  = put_guest_lc(vcpu, 0x2603, (u16 *) __LC_EXT_INT_CODE);
+               rc |= put_guest_lc(vcpu, 0x0600, (u16 *) __LC_EXT_CPU_ADDR);
+               rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
+                                    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+               rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
                                    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-               rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
-                                     __LC_EXT_NEW_PSW, sizeof(psw_t));
-               rc |= put_guest(vcpu, inti->ext.ext_params2,
-                               (u64 __user *) __LC_EXT_PARAMS2);
+               rc |= put_guest_lc(vcpu, inti->ext.ext_params2,
+                                  (u64 *) __LC_EXT_PARAMS2);
                break;
        case KVM_S390_INT_PFAULT_DONE:
                trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, 0,
                                                 inti->ext.ext_params2);
-               rc  = put_guest(vcpu, 0x2603, (u16 __user *) __LC_EXT_INT_CODE);
-               rc |= put_guest(vcpu, 0x0680, (u16 __user *) __LC_EXT_CPU_ADDR);
-               rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW,
+               rc  = put_guest_lc(vcpu, 0x2603, (u16 *)__LC_EXT_INT_CODE);
+               rc |= put_guest_lc(vcpu, 0x0680, (u16 *)__LC_EXT_CPU_ADDR);
+               rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
+                                    &vcpu->arch.sie_block->gpsw,
+                                    sizeof(psw_t));
+               rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
                                    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-               rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
-                                     __LC_EXT_NEW_PSW, sizeof(psw_t));
-               rc |= put_guest(vcpu, inti->ext.ext_params2,
-                               (u64 __user *) __LC_EXT_PARAMS2);
+               rc |= put_guest_lc(vcpu, inti->ext.ext_params2,
+                                  (u64 *)__LC_EXT_PARAMS2);
                break;
        case KVM_S390_INT_VIRTIO:
                VCPU_EVENT(vcpu, 4, "interrupt: virtio parm:%x,parm64:%llx",
@@ -252,16 +402,17 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
                trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
                                                 inti->ext.ext_params,
                                                 inti->ext.ext_params2);
-               rc  = put_guest(vcpu, 0x2603, (u16 __user *)__LC_EXT_INT_CODE);
-               rc |= put_guest(vcpu, 0x0d00, (u16 __user *)__LC_EXT_CPU_ADDR);
-               rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW,
+               rc  = put_guest_lc(vcpu, 0x2603, (u16 *)__LC_EXT_INT_CODE);
+               rc |= put_guest_lc(vcpu, 0x0d00, (u16 *)__LC_EXT_CPU_ADDR);
+               rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
+                                    &vcpu->arch.sie_block->gpsw,
+                                    sizeof(psw_t));
+               rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
                                    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-               rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
-                                     __LC_EXT_NEW_PSW, sizeof(psw_t));
-               rc |= put_guest(vcpu, inti->ext.ext_params,
-                               (u32 __user *)__LC_EXT_PARAMS);
-               rc |= put_guest(vcpu, inti->ext.ext_params2,
-                               (u64 __user *)__LC_EXT_PARAMS2);
+               rc |= put_guest_lc(vcpu, inti->ext.ext_params,
+                                  (u32 *)__LC_EXT_PARAMS);
+               rc |= put_guest_lc(vcpu, inti->ext.ext_params2,
+                                  (u64 *)__LC_EXT_PARAMS2);
                break;
        case KVM_S390_SIGP_STOP:
                VCPU_EVENT(vcpu, 4, "%s", "interrupt: cpu stop");
@@ -285,13 +436,12 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
                vcpu->stat.deliver_restart_signal++;
                trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
                                                 0, 0);
-               rc  = copy_to_guest(vcpu,
-                                   offsetof(struct _lowcore, restart_old_psw),
-                                   &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-               rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
-                                     offsetof(struct _lowcore, restart_psw),
-                                     sizeof(psw_t));
-               atomic_clear_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
+               rc  = write_guest_lc(vcpu,
+                                    offsetof(struct _lowcore, restart_old_psw),
+                                    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+               rc |= read_guest_lc(vcpu, offsetof(struct _lowcore, restart_psw),
+                                   &vcpu->arch.sie_block->gpsw,
+                                   sizeof(psw_t));
                break;
        case KVM_S390_PROGRAM_INT:
                VCPU_EVENT(vcpu, 4, "interrupt: pgm check code:%x, ilc:%x",
@@ -300,13 +450,7 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
                vcpu->stat.deliver_program_int++;
                trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
                                                 inti->pgm.code, 0);
-               rc  = put_guest(vcpu, inti->pgm.code, (u16 __user *)__LC_PGM_INT_CODE);
-               rc |= put_guest(vcpu, table[vcpu->arch.sie_block->ipa >> 14],
-                               (u16 __user *)__LC_PGM_ILC);
-               rc |= copy_to_guest(vcpu, __LC_PGM_OLD_PSW,
-                                   &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-               rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
-                                     __LC_PGM_NEW_PSW, sizeof(psw_t));
+               rc = __deliver_prog_irq(vcpu, &inti->pgm);
                break;
 
        case KVM_S390_MCHK:
@@ -317,11 +461,12 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
                                                 inti->mchk.mcic);
                rc  = kvm_s390_vcpu_store_status(vcpu,
                                                 KVM_S390_STORE_STATUS_PREFIXED);
-               rc |= put_guest(vcpu, inti->mchk.mcic, (u64 __user *) __LC_MCCK_CODE);
-               rc |= copy_to_guest(vcpu, __LC_MCK_OLD_PSW,
+               rc |= put_guest_lc(vcpu, inti->mchk.mcic, (u64 *)__LC_MCCK_CODE);
+               rc |= write_guest_lc(vcpu, __LC_MCK_OLD_PSW,
+                                    &vcpu->arch.sie_block->gpsw,
+                                    sizeof(psw_t));
+               rc |= read_guest_lc(vcpu, __LC_MCK_NEW_PSW,
                                    &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-               rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
-                                     __LC_MCK_NEW_PSW, sizeof(psw_t));
                break;
 
        case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
@@ -334,18 +479,20 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
                vcpu->stat.deliver_io_int++;
                trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
                                                 param0, param1);
-               rc  = put_guest(vcpu, inti->io.subchannel_id,
-                               (u16 __user *) __LC_SUBCHANNEL_ID);
-               rc |= put_guest(vcpu, inti->io.subchannel_nr,
-                               (u16 __user *) __LC_SUBCHANNEL_NR);
-               rc |= put_guest(vcpu, inti->io.io_int_parm,
-                               (u32 __user *) __LC_IO_INT_PARM);
-               rc |= put_guest(vcpu, inti->io.io_int_word,
-                               (u32 __user *) __LC_IO_INT_WORD);
-               rc |= copy_to_guest(vcpu, __LC_IO_OLD_PSW,
-                                   &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-               rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
-                                     __LC_IO_NEW_PSW, sizeof(psw_t));
+               rc  = put_guest_lc(vcpu, inti->io.subchannel_id,
+                                  (u16 *)__LC_SUBCHANNEL_ID);
+               rc |= put_guest_lc(vcpu, inti->io.subchannel_nr,
+                                  (u16 *)__LC_SUBCHANNEL_NR);
+               rc |= put_guest_lc(vcpu, inti->io.io_int_parm,
+                                  (u32 *)__LC_IO_INT_PARM);
+               rc |= put_guest_lc(vcpu, inti->io.io_int_word,
+                                  (u32 *)__LC_IO_INT_WORD);
+               rc |= write_guest_lc(vcpu, __LC_IO_OLD_PSW,
+                                    &vcpu->arch.sie_block->gpsw,
+                                    sizeof(psw_t));
+               rc |= read_guest_lc(vcpu, __LC_IO_NEW_PSW,
+                                   &vcpu->arch.sie_block->gpsw,
+                                   sizeof(psw_t));
                break;
        }
        default:
@@ -358,25 +505,35 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
        }
 }
 
-static int __try_deliver_ckc_interrupt(struct kvm_vcpu *vcpu)
+static void deliver_ckc_interrupt(struct kvm_vcpu *vcpu)
 {
        int rc;
 
-       if (psw_extint_disabled(vcpu))
-               return 0;
-       if (!(vcpu->arch.sie_block->gcr[0] & 0x800ul))
-               return 0;
-       rc  = put_guest(vcpu, 0x1004, (u16 __user *)__LC_EXT_INT_CODE);
-       rc |= copy_to_guest(vcpu, __LC_EXT_OLD_PSW,
-                           &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
-       rc |= copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
-                             __LC_EXT_NEW_PSW, sizeof(psw_t));
+       rc  = put_guest_lc(vcpu, 0x1004, (u16 __user *)__LC_EXT_INT_CODE);
+       rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW,
+                            &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+       rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW,
+                           &vcpu->arch.sie_block->gpsw,
+                           sizeof(psw_t));
        if (rc) {
                printk("kvm: The guest lowcore is not mapped during interrupt "
                        "delivery, killing userspace\n");
                do_exit(SIGKILL);
        }
-       return 1;
+}
+
+/* Check whether SIGP interpretation facility has an external call pending */
+int kvm_s390_si_ext_call_pending(struct kvm_vcpu *vcpu)
+{
+       atomic_t *sigp_ctrl = &vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].ctrl;
+
+       if (!psw_extint_disabled(vcpu) &&
+           (vcpu->arch.sie_block->gcr[0] & 0x2000ul) &&
+           (atomic_read(sigp_ctrl) & SIGP_CTRL_C) &&
+           (atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_ECALL_PEND))
+               return 1;
+
+       return 0;
 }
 
 int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
@@ -406,19 +563,23 @@ int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
                spin_unlock(&fi->lock);
        }
 
-       if ((!rc) && (vcpu->arch.sie_block->ckc <
-               get_tod_clock_fast() + vcpu->arch.sie_block->epoch)) {
-               if ((!psw_extint_disabled(vcpu)) &&
-                       (vcpu->arch.sie_block->gcr[0] & 0x800ul))
-                       rc = 1;
-       }
+       if (!rc && kvm_cpu_has_pending_timer(vcpu))
+               rc = 1;
+
+       if (!rc && kvm_s390_si_ext_call_pending(vcpu))
+               rc = 1;
 
        return rc;
 }
 
 int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
 {
-       return 0;
+       if (!(vcpu->arch.sie_block->ckc <
+             get_tod_clock_fast() + vcpu->arch.sie_block->epoch))
+               return 0;
+       if (!ckc_interrupts_enabled(vcpu))
+               return 0;
+       return 1;
 }
 
 int kvm_s390_handle_wait(struct kvm_vcpu *vcpu)
@@ -441,8 +602,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu)
                return -EOPNOTSUPP; /* disabled wait */
        }
 
-       if (psw_extint_disabled(vcpu) ||
-           (!(vcpu->arch.sie_block->gcr[0] & 0x800ul))) {
+       if (!ckc_interrupts_enabled(vcpu)) {
                VCPU_EVENT(vcpu, 3, "%s", "enabled wait w/o timer");
                goto no_timer;
        }
@@ -465,7 +625,8 @@ no_timer:
        while (list_empty(&vcpu->arch.local_int.list) &&
                list_empty(&vcpu->arch.local_int.float_int->list) &&
                (!vcpu->arch.local_int.timer_due) &&
-               !signal_pending(current)) {
+               !signal_pending(current) &&
+               !kvm_s390_si_ext_call_pending(vcpu)) {
                set_current_state(TASK_INTERRUPTIBLE);
                spin_unlock_bh(&vcpu->arch.local_int.lock);
                spin_unlock(&vcpu->arch.local_int.float_int->lock);
@@ -522,6 +683,11 @@ void kvm_s390_clear_local_irqs(struct kvm_vcpu *vcpu)
        }
        atomic_set(&li->active, 0);
        spin_unlock_bh(&li->lock);
+
+       /* clear pending external calls set by sigp interpretation facility */
+       atomic_clear_mask(CPUSTAT_ECALL_PEND, &vcpu->arch.sie_block->cpuflags);
+       atomic_clear_mask(SIGP_CTRL_C,
+                         &vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].ctrl);
 }
 
 void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
@@ -554,9 +720,8 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
                } while (deliver);
        }
 
-       if ((vcpu->arch.sie_block->ckc <
-               get_tod_clock_fast() + vcpu->arch.sie_block->epoch))
-               __try_deliver_ckc_interrupt(vcpu);
+       if (kvm_cpu_has_pending_timer(vcpu))
+               deliver_ckc_interrupt(vcpu);
 
        if (atomic_read(&fi->active)) {
                do {
@@ -660,6 +825,31 @@ int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code)
        return 0;
 }
 
+int kvm_s390_inject_prog_irq(struct kvm_vcpu *vcpu,
+                            struct kvm_s390_pgm_info *pgm_info)
+{
+       struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
+       struct kvm_s390_interrupt_info *inti;
+
+       inti = kzalloc(sizeof(*inti), GFP_KERNEL);
+       if (!inti)
+               return -ENOMEM;
+
+       VCPU_EVENT(vcpu, 3, "inject: prog irq %d (from kernel)",
+                  pgm_info->code);
+       trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_PROGRAM_INT,
+                                  pgm_info->code, 0, 1);
+
+       inti->type = KVM_S390_PROGRAM_INT;
+       memcpy(&inti->pgm, pgm_info, sizeof(inti->pgm));
+       spin_lock_bh(&li->lock);
+       list_add(&inti->list, &li->list);
+       atomic_set(&li->active, 1);
+       BUG_ON(waitqueue_active(li->wq));
+       spin_unlock_bh(&li->lock);
+       return 0;
+}
+
 struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm,
                                                    u64 cr6, u64 schid)
 {
@@ -810,6 +1000,12 @@ int kvm_s390_inject_vm(struct kvm *kvm,
        return __inject_vm(kvm, inti);
 }
 
+void kvm_s390_reinject_io_int(struct kvm *kvm,
+                             struct kvm_s390_interrupt_info *inti)
+{
+       __inject_vm(kvm, inti);
+}
+
 int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
                         struct kvm_s390_interrupt *s390int)
 {
@@ -839,6 +1035,8 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
                break;
        case KVM_S390_SIGP_STOP:
        case KVM_S390_RESTART:
+       case KVM_S390_INT_CLOCK_COMP:
+       case KVM_S390_INT_CPU_TIMER:
                VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type);
                inti->type = s390int->type;
                break;
@@ -900,7 +1098,7 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
        return 0;
 }
 
-static void clear_floating_interrupts(struct kvm *kvm)
+void kvm_s390_clear_float_irqs(struct kvm *kvm)
 {
        struct kvm_s390_float_interrupt *fi;
        struct kvm_s390_interrupt_info  *n, *inti = NULL;
@@ -1246,7 +1444,7 @@ static int flic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
                break;
        case KVM_DEV_FLIC_CLEAR_IRQS:
                r = 0;
-               clear_floating_interrupts(dev->kvm);
+               kvm_s390_clear_float_irqs(dev->kvm);
                break;
        case KVM_DEV_FLIC_APF_ENABLE:
                dev->kvm->arch.gmap->pfault_enabled = 1;
index 825fe7b..2f3e14f 100644 (file)
@@ -11,6 +11,7 @@
  *               Christian Borntraeger <borntraeger@de.ibm.com>
  *               Heiko Carstens <heiko.carstens@de.ibm.com>
  *               Christian Ehrhardt <ehrhardt@de.ibm.com>
+ *               Jason J. Herne <jjherne@us.ibm.com>
  */
 
 #include <linux/compiler.h>
@@ -51,6 +52,8 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
        { "exit_instr_and_program_int", VCPU_STAT(exit_instr_and_program) },
        { "instruction_lctlg", VCPU_STAT(instruction_lctlg) },
        { "instruction_lctl", VCPU_STAT(instruction_lctl) },
+       { "instruction_stctl", VCPU_STAT(instruction_stctl) },
+       { "instruction_stctg", VCPU_STAT(instruction_stctg) },
        { "deliver_emergency_signal", VCPU_STAT(deliver_emergency_signal) },
        { "deliver_external_call", VCPU_STAT(deliver_external_call) },
        { "deliver_service_signal", VCPU_STAT(deliver_service_signal) },
@@ -66,6 +69,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
        { "instruction_stpx", VCPU_STAT(instruction_stpx) },
        { "instruction_stap", VCPU_STAT(instruction_stap) },
        { "instruction_storage_key", VCPU_STAT(instruction_storage_key) },
+       { "instruction_ipte_interlock", VCPU_STAT(instruction_ipte_interlock) },
        { "instruction_stsch", VCPU_STAT(instruction_stsch) },
        { "instruction_chsc", VCPU_STAT(instruction_chsc) },
        { "instruction_essa", VCPU_STAT(instruction_essa) },
@@ -90,7 +94,7 @@ unsigned long *vfacilities;
 static struct gmap_notifier gmap_notifier;
 
 /* test availability of vfacility */
-static inline int test_vfacility(unsigned long nr)
+int test_vfacility(unsigned long nr)
 {
        return __test_facility(nr, (void *) vfacilities);
 }
@@ -162,6 +166,7 @@ int kvm_dev_ioctl_check_extension(long ext)
        case KVM_CAP_IOEVENTFD:
        case KVM_CAP_DEVICE_CTRL:
        case KVM_CAP_ENABLE_CAP_VM:
+       case KVM_CAP_VM_ATTRIBUTES:
                r = 1;
                break;
        case KVM_CAP_NR_VCPUS:
@@ -180,6 +185,25 @@ int kvm_dev_ioctl_check_extension(long ext)
        return r;
 }
 
+static void kvm_s390_sync_dirty_log(struct kvm *kvm,
+                                       struct kvm_memory_slot *memslot)
+{
+       gfn_t cur_gfn, last_gfn;
+       unsigned long address;
+       struct gmap *gmap = kvm->arch.gmap;
+
+       down_read(&gmap->mm->mmap_sem);
+       /* Loop over all guest pages */
+       last_gfn = memslot->base_gfn + memslot->npages;
+       for (cur_gfn = memslot->base_gfn; cur_gfn <= last_gfn; cur_gfn++) {
+               address = gfn_to_hva_memslot(memslot, cur_gfn);
+
+               if (gmap_test_and_clear_dirty(address, gmap))
+                       mark_page_dirty(kvm, cur_gfn);
+       }
+       up_read(&gmap->mm->mmap_sem);
+}
+
 /* Section: vm related */
 /*
  * Get (and clear) the dirty memory log for a memory slot.
@@ -187,7 +211,36 @@ int kvm_dev_ioctl_check_extension(long ext)
 int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
                               struct kvm_dirty_log *log)
 {
-       return 0;
+       int r;
+       unsigned long n;
+       struct kvm_memory_slot *memslot;
+       int is_dirty = 0;
+
+       mutex_lock(&kvm->slots_lock);
+
+       r = -EINVAL;
+       if (log->slot >= KVM_USER_MEM_SLOTS)
+               goto out;
+
+       memslot = id_to_memslot(kvm->memslots, log->slot);
+       r = -ENOENT;
+       if (!memslot->dirty_bitmap)
+               goto out;
+
+       kvm_s390_sync_dirty_log(kvm, memslot);
+       r = kvm_get_dirty_log(kvm, log, &is_dirty);
+       if (r)
+               goto out;
+
+       /* Clear the dirty log */
+       if (is_dirty) {
+               n = kvm_dirty_bitmap_bytes(memslot);
+               memset(memslot->dirty_bitmap, 0, n);
+       }
+       r = 0;
+out:
+       mutex_unlock(&kvm->slots_lock);
+       return r;
 }
 
 static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap)
@@ -209,11 +262,86 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap)
        return r;
 }
 
+static int kvm_s390_mem_control(struct kvm *kvm, struct kvm_device_attr *attr)
+{
+       int ret;
+       unsigned int idx;
+       switch (attr->attr) {
+       case KVM_S390_VM_MEM_ENABLE_CMMA:
+               ret = -EBUSY;
+               mutex_lock(&kvm->lock);
+               if (atomic_read(&kvm->online_vcpus) == 0) {
+                       kvm->arch.use_cmma = 1;
+                       ret = 0;
+               }
+               mutex_unlock(&kvm->lock);
+               break;
+       case KVM_S390_VM_MEM_CLR_CMMA:
+               mutex_lock(&kvm->lock);
+               idx = srcu_read_lock(&kvm->srcu);
+               page_table_reset_pgste(kvm->arch.gmap->mm, 0, TASK_SIZE, false);
+               srcu_read_unlock(&kvm->srcu, idx);
+               mutex_unlock(&kvm->lock);
+               ret = 0;
+               break;
+       default:
+               ret = -ENXIO;
+               break;
+       }
+       return ret;
+}
+
+static int kvm_s390_vm_set_attr(struct kvm *kvm, struct kvm_device_attr *attr)
+{
+       int ret;
+
+       switch (attr->group) {
+       case KVM_S390_VM_MEM_CTRL:
+               ret = kvm_s390_mem_control(kvm, attr);
+               break;
+       default:
+               ret = -ENXIO;
+               break;
+       }
+
+       return ret;
+}
+
+static int kvm_s390_vm_get_attr(struct kvm *kvm, struct kvm_device_attr *attr)
+{
+       return -ENXIO;
+}
+
+static int kvm_s390_vm_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
+{
+       int ret;
+
+       switch (attr->group) {
+       case KVM_S390_VM_MEM_CTRL:
+               switch (attr->attr) {
+               case KVM_S390_VM_MEM_ENABLE_CMMA:
+               case KVM_S390_VM_MEM_CLR_CMMA:
+                       ret = 0;
+                       break;
+               default:
+                       ret = -ENXIO;
+                       break;
+               }
+               break;
+       default:
+               ret = -ENXIO;
+               break;
+       }
+
+       return ret;
+}
+
 long kvm_arch_vm_ioctl(struct file *filp,
                       unsigned int ioctl, unsigned long arg)
 {
        struct kvm *kvm = filp->private_data;
        void __user *argp = (void __user *)arg;
+       struct kvm_device_attr attr;
        int r;
 
        switch (ioctl) {
@@ -246,6 +374,27 @@ long kvm_arch_vm_ioctl(struct file *filp,
                }
                break;
        }
+       case KVM_SET_DEVICE_ATTR: {
+               r = -EFAULT;
+               if (copy_from_user(&attr, (void __user *)arg, sizeof(attr)))
+                       break;
+               r = kvm_s390_vm_set_attr(kvm, &attr);
+               break;
+       }
+       case KVM_GET_DEVICE_ATTR: {
+               r = -EFAULT;
+               if (copy_from_user(&attr, (void __user *)arg, sizeof(attr)))
+                       break;
+               r = kvm_s390_vm_get_attr(kvm, &attr);
+               break;
+       }
+       case KVM_HAS_DEVICE_ATTR: {
+               r = -EFAULT;
+               if (copy_from_user(&attr, (void __user *)arg, sizeof(attr)))
+                       break;
+               r = kvm_s390_vm_has_attr(kvm, &attr);
+               break;
+       }
        default:
                r = -ENOTTY;
        }
@@ -292,6 +441,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 
        spin_lock_init(&kvm->arch.float_int.lock);
        INIT_LIST_HEAD(&kvm->arch.float_int.list);
+       init_waitqueue_head(&kvm->arch.ipte_wq);
 
        debug_register_view(kvm->arch.dbf, &debug_sprintf_view);
        VM_EVENT(kvm, 3, "%s", "vm created");
@@ -309,6 +459,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
        kvm->arch.css_support = 0;
        kvm->arch.use_irqchip = 0;
 
+       spin_lock_init(&kvm->arch.start_stop_lock);
+
        return 0;
 out_nogmap:
        debug_unregister(kvm->arch.dbf);
@@ -322,6 +474,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
 {
        VCPU_EVENT(vcpu, 3, "%s", "free cpu");
        trace_kvm_s390_destroy_vcpu(vcpu->vcpu_id);
+       kvm_s390_clear_local_irqs(vcpu);
        kvm_clear_async_pf_completion_queue(vcpu);
        if (!kvm_is_ucontrol(vcpu->kvm)) {
                clear_bit(63 - vcpu->vcpu_id,
@@ -335,9 +488,8 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
        if (kvm_is_ucontrol(vcpu->kvm))
                gmap_free(vcpu->arch.gmap);
 
-       if (vcpu->arch.sie_block->cbrlo)
-               __free_page(__pfn_to_page(
-                               vcpu->arch.sie_block->cbrlo >> PAGE_SHIFT));
+       if (kvm_s390_cmma_enabled(vcpu->kvm))
+               kvm_s390_vcpu_unsetup_cmma(vcpu);
        free_page((unsigned long)(vcpu->arch.sie_block));
 
        kvm_vcpu_uninit(vcpu);
@@ -372,6 +524,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
        if (!kvm_is_ucontrol(kvm))
                gmap_free(kvm->arch.gmap);
        kvm_s390_destroy_adapters(kvm);
+       kvm_s390_clear_float_irqs(kvm);
 }
 
 /* Section: vcpu related */
@@ -442,7 +595,7 @@ static void kvm_s390_vcpu_initial_reset(struct kvm_vcpu *vcpu)
        vcpu->arch.sie_block->pp = 0;
        vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID;
        kvm_clear_async_pf_completion_queue(vcpu);
-       atomic_set_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
+       kvm_s390_vcpu_stop(vcpu);
        kvm_s390_clear_local_irqs(vcpu);
 }
 
@@ -451,9 +604,26 @@ int kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
        return 0;
 }
 
+void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu)
+{
+       free_page(vcpu->arch.sie_block->cbrlo);
+       vcpu->arch.sie_block->cbrlo = 0;
+}
+
+int kvm_s390_vcpu_setup_cmma(struct kvm_vcpu *vcpu)
+{
+       vcpu->arch.sie_block->cbrlo = get_zeroed_page(GFP_KERNEL);
+       if (!vcpu->arch.sie_block->cbrlo)
+               return -ENOMEM;
+
+       vcpu->arch.sie_block->ecb2 |= 0x80;
+       vcpu->arch.sie_block->ecb2 &= ~0x08;
+       return 0;
+}
+
 int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
 {
-       struct page *cbrl;
+       int rc = 0;
 
        atomic_set(&vcpu->arch.sie_block->cpuflags, CPUSTAT_ZARCH |
                                                    CPUSTAT_SM |
@@ -464,15 +634,17 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
                vcpu->arch.sie_block->ecb |= 0x10;
 
        vcpu->arch.sie_block->ecb2  = 8;
-       vcpu->arch.sie_block->eca   = 0xC1002001U;
+       vcpu->arch.sie_block->eca   = 0xD1002000U;
+       if (sclp_has_siif())
+               vcpu->arch.sie_block->eca |= 1;
        vcpu->arch.sie_block->fac   = (int) (long) vfacilities;
-       if (kvm_enabled_cmma()) {
-               cbrl = alloc_page(GFP_KERNEL | __GFP_ZERO);
-               if (cbrl) {
-                       vcpu->arch.sie_block->ecb2 |= 0x80;
-                       vcpu->arch.sie_block->ecb2 &= ~0x08;
-                       vcpu->arch.sie_block->cbrlo = page_to_phys(cbrl);
-               }
+       vcpu->arch.sie_block->ictl |= ICTL_ISKE | ICTL_SSKE | ICTL_RRBE |
+                                     ICTL_TPROT;
+
+       if (kvm_s390_cmma_enabled(vcpu->kvm)) {
+               rc = kvm_s390_vcpu_setup_cmma(vcpu);
+               if (rc)
+                       return rc;
        }
        hrtimer_init(&vcpu->arch.ckc_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
        tasklet_init(&vcpu->arch.tasklet, kvm_s390_tasklet,
@@ -480,7 +652,7 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
        vcpu->arch.ckc_timer.function = kvm_s390_idle_wakeup;
        get_cpu_id(&vcpu->arch.cpu_id);
        vcpu->arch.cpu_id.version = 0xff;
-       return 0;
+       return rc;
 }
 
 struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
@@ -584,7 +756,7 @@ static void kvm_gmap_notifier(struct gmap *gmap, unsigned long address)
 
        kvm_for_each_vcpu(i, vcpu, kvm) {
                /* match against both prefix pages */
-               if (vcpu->arch.sie_block->prefix == (address & ~0x1000UL)) {
+               if (kvm_s390_get_prefix(vcpu) == (address & ~0x1000UL)) {
                        VCPU_EVENT(vcpu, 2, "gmap notifier for %lx", address);
                        kvm_make_request(KVM_REQ_MMU_RELOAD, vcpu);
                        exit_sie_sync(vcpu);
@@ -769,10 +941,40 @@ int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
        return -EINVAL; /* not implemented yet */
 }
 
+#define VALID_GUESTDBG_FLAGS (KVM_GUESTDBG_SINGLESTEP | \
+                             KVM_GUESTDBG_USE_HW_BP | \
+                             KVM_GUESTDBG_ENABLE)
+
 int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
                                        struct kvm_guest_debug *dbg)
 {
-       return -EINVAL; /* not implemented yet */
+       int rc = 0;
+
+       vcpu->guest_debug = 0;
+       kvm_s390_clear_bp_data(vcpu);
+
+       if (dbg->control & ~VALID_GUESTDBG_FLAGS)
+               return -EINVAL;
+
+       if (dbg->control & KVM_GUESTDBG_ENABLE) {
+               vcpu->guest_debug = dbg->control;
+               /* enforce guest PER */
+               atomic_set_mask(CPUSTAT_P, &vcpu->arch.sie_block->cpuflags);
+
+               if (dbg->control & KVM_GUESTDBG_USE_HW_BP)
+                       rc = kvm_s390_import_bp_data(vcpu, dbg);
+       } else {
+               atomic_clear_mask(CPUSTAT_P, &vcpu->arch.sie_block->cpuflags);
+               vcpu->arch.guestdbg.last_bp = 0;
+       }
+
+       if (rc) {
+               vcpu->guest_debug = 0;
+               kvm_s390_clear_bp_data(vcpu);
+               atomic_clear_mask(CPUSTAT_P, &vcpu->arch.sie_block->cpuflags);
+       }
+
+       return rc;
 }
 
 int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
@@ -787,8 +989,27 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
        return -EINVAL; /* not implemented yet */
 }
 
+bool kvm_s390_cmma_enabled(struct kvm *kvm)
+{
+       if (!MACHINE_IS_LPAR)
+               return false;
+       /* only enable for z10 and later */
+       if (!MACHINE_HAS_EDAT1)
+               return false;
+       if (!kvm->arch.use_cmma)
+               return false;
+       return true;
+}
+
+static bool ibs_enabled(struct kvm_vcpu *vcpu)
+{
+       return atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_IBS;
+}
+
 static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu)
 {
+retry:
+       s390_vcpu_unblock(vcpu);
        /*
         * We use MMU_RELOAD just to re-arm the ipte notifier for the
         * guest prefix page. gmap_ipte_notify will wait on the ptl lock.
@@ -796,27 +1017,61 @@ static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu)
         * already finished. We might race against a second unmapper that
         * wants to set the blocking bit. Lets just retry the request loop.
         */
-       while (kvm_check_request(KVM_REQ_MMU_RELOAD, vcpu)) {
+       if (kvm_check_request(KVM_REQ_MMU_RELOAD, vcpu)) {
                int rc;
                rc = gmap_ipte_notify(vcpu->arch.gmap,
-                                     vcpu->arch.sie_block->prefix,
+                                     kvm_s390_get_prefix(vcpu),
                                      PAGE_SIZE * 2);
                if (rc)
                        return rc;
-               s390_vcpu_unblock(vcpu);
+               goto retry;
+       }
+
+       if (kvm_check_request(KVM_REQ_ENABLE_IBS, vcpu)) {
+               if (!ibs_enabled(vcpu)) {
+                       trace_kvm_s390_enable_disable_ibs(vcpu->vcpu_id, 1);
+                       atomic_set_mask(CPUSTAT_IBS,
+                                       &vcpu->arch.sie_block->cpuflags);
+               }
+               goto retry;
        }
+
+       if (kvm_check_request(KVM_REQ_DISABLE_IBS, vcpu)) {
+               if (ibs_enabled(vcpu)) {
+                       trace_kvm_s390_enable_disable_ibs(vcpu->vcpu_id, 0);
+                       atomic_clear_mask(CPUSTAT_IBS,
+                                         &vcpu->arch.sie_block->cpuflags);
+               }
+               goto retry;
+       }
+
        return 0;
 }
 
-static long kvm_arch_fault_in_sync(struct kvm_vcpu *vcpu)
+/**
+ * kvm_arch_fault_in_page - fault-in guest page if necessary
+ * @vcpu: The corresponding virtual cpu
+ * @gpa: Guest physical address
+ * @writable: Whether the page should be writable or not
+ *
+ * Make sure that a guest page has been faulted-in on the host.
+ *
+ * Return: Zero on success, negative error code otherwise.
+ */
+long kvm_arch_fault_in_page(struct kvm_vcpu *vcpu, gpa_t gpa, int writable)
 {
-       long rc;
-       hva_t fault = gmap_fault(current->thread.gmap_addr, vcpu->arch.gmap);
        struct mm_struct *mm = current->mm;
+       hva_t hva;
+       long rc;
+
+       hva = gmap_fault(gpa, vcpu->arch.gmap);
+       if (IS_ERR_VALUE(hva))
+               return (long)hva;
        down_read(&mm->mmap_sem);
-       rc = get_user_pages(current, mm, fault, 1, 1, 0, NULL, NULL);
+       rc = get_user_pages(current, mm, hva, 1, writable, 0, NULL, NULL);
        up_read(&mm->mmap_sem);
-       return rc;
+
+       return rc < 0 ? rc : 0;
 }
 
 static void __kvm_inject_pfault_token(struct kvm_vcpu *vcpu, bool start_token,
@@ -883,8 +1138,9 @@ static int kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu)
        if (!vcpu->arch.gmap->pfault_enabled)
                return 0;
 
-       hva = gmap_fault(current->thread.gmap_addr, vcpu->arch.gmap);
-       if (copy_from_guest(vcpu, &arch.pfault_token, vcpu->arch.pfault_token, 8))
+       hva = gfn_to_hva(vcpu->kvm, gpa_to_gfn(current->thread.gmap_addr));
+       hva += current->thread.gmap_addr & ~PAGE_MASK;
+       if (read_guest_real(vcpu, vcpu->arch.pfault_token, &arch.pfault_token, 8))
                return 0;
 
        rc = kvm_setup_async_pf(vcpu, current->thread.gmap_addr, hva, &arch);
@@ -917,6 +1173,11 @@ static int vcpu_pre_run(struct kvm_vcpu *vcpu)
        if (rc)
                return rc;
 
+       if (guestdbg_enabled(vcpu)) {
+               kvm_s390_backup_guest_per_regs(vcpu);
+               kvm_s390_patch_guest_per_regs(vcpu);
+       }
+
        vcpu->arch.sie_block->icptcode = 0;
        cpuflags = atomic_read(&vcpu->arch.sie_block->cpuflags);
        VCPU_EVENT(vcpu, 6, "entering sie flags %x", cpuflags);
@@ -933,6 +1194,9 @@ static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason)
                   vcpu->arch.sie_block->icptcode);
        trace_kvm_s390_sie_exit(vcpu, vcpu->arch.sie_block->icptcode);
 
+       if (guestdbg_enabled(vcpu))
+               kvm_s390_restore_guest_per_regs(vcpu);
+
        if (exit_reason >= 0) {
                rc = 0;
        } else if (kvm_is_ucontrol(vcpu->kvm)) {
@@ -945,9 +1209,12 @@ static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason)
        } else if (current->thread.gmap_pfault) {
                trace_kvm_s390_major_guest_pfault(vcpu);
                current->thread.gmap_pfault = 0;
-               if (kvm_arch_setup_async_pf(vcpu) ||
-                   (kvm_arch_fault_in_sync(vcpu) >= 0))
+               if (kvm_arch_setup_async_pf(vcpu)) {
                        rc = 0;
+               } else {
+                       gpa_t gpa = current->thread.gmap_addr;
+                       rc = kvm_arch_fault_in_page(vcpu, gpa, 1);
+               }
        }
 
        if (rc == -1) {
@@ -969,16 +1236,6 @@ static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason)
        return rc;
 }
 
-bool kvm_enabled_cmma(void)
-{
-       if (!MACHINE_IS_LPAR)
-               return false;
-       /* only enable for z10 and later */
-       if (!MACHINE_HAS_EDAT1)
-               return false;
-       return true;
-}
-
 static int __vcpu_run(struct kvm_vcpu *vcpu)
 {
        int rc, exit_reason;
@@ -1008,7 +1265,7 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
                vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
 
                rc = vcpu_post_run(vcpu, exit_reason);
-       } while (!signal_pending(current) && !rc);
+       } while (!signal_pending(current) && !guestdbg_exit_pending(vcpu) && !rc);
 
        srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
        return rc;
@@ -1019,10 +1276,15 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
        int rc;
        sigset_t sigsaved;
 
+       if (guestdbg_exit_pending(vcpu)) {
+               kvm_s390_prepare_debug_exit(vcpu);
+               return 0;
+       }
+
        if (vcpu->sigset_active)
                sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
 
-       atomic_clear_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
+       kvm_s390_vcpu_start(vcpu);
 
        switch (kvm_run->exit_reason) {
        case KVM_EXIT_S390_SIEIC:
@@ -1031,6 +1293,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
        case KVM_EXIT_S390_RESET:
        case KVM_EXIT_S390_UCONTROL:
        case KVM_EXIT_S390_TSCH:
+       case KVM_EXIT_DEBUG:
                break;
        default:
                BUG();
@@ -1056,6 +1319,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
                rc = -EINTR;
        }
 
+       if (guestdbg_exit_pending(vcpu) && !rc)  {
+               kvm_s390_prepare_debug_exit(vcpu);
+               rc = 0;
+       }
+
        if (rc == -EOPNOTSUPP) {
                /* intercept cannot be handled in-kernel, prepare kvm-run */
                kvm_run->exit_reason         = KVM_EXIT_S390_SIEIC;
@@ -1073,7 +1341,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 
        kvm_run->psw_mask     = vcpu->arch.sie_block->gpsw.mask;
        kvm_run->psw_addr     = vcpu->arch.sie_block->gpsw.addr;
-       kvm_run->s.regs.prefix = vcpu->arch.sie_block->prefix;
+       kvm_run->s.regs.prefix = kvm_s390_get_prefix(vcpu);
        memcpy(&kvm_run->s.regs.crs, &vcpu->arch.sie_block->gcr, 128);
 
        if (vcpu->sigset_active)
@@ -1083,83 +1351,52 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
        return rc;
 }
 
-static int __guestcopy(struct kvm_vcpu *vcpu, u64 guestdest, void *from,
-                      unsigned long n, int prefix)
-{
-       if (prefix)
-               return copy_to_guest(vcpu, guestdest, from, n);
-       else
-               return copy_to_guest_absolute(vcpu, guestdest, from, n);
-}
-
 /*
  * store status at address
  * we use have two special cases:
  * KVM_S390_STORE_STATUS_NOADDR: -> 0x1200 on 64 bit
  * KVM_S390_STORE_STATUS_PREFIXED: -> prefix
  */
-int kvm_s390_store_status_unloaded(struct kvm_vcpu *vcpu, unsigned long addr)
+int kvm_s390_store_status_unloaded(struct kvm_vcpu *vcpu, unsigned long gpa)
 {
        unsigned char archmode = 1;
-       int prefix;
+       unsigned int px;
        u64 clkcomp;
+       int rc;
 
-       if (addr == KVM_S390_STORE_STATUS_NOADDR) {
-               if (copy_to_guest_absolute(vcpu, 163ul, &archmode, 1))
+       if (gpa == KVM_S390_STORE_STATUS_NOADDR) {
+               if (write_guest_abs(vcpu, 163, &archmode, 1))
                        return -EFAULT;
-               addr = SAVE_AREA_BASE;
-               prefix = 0;
-       } else if (addr == KVM_S390_STORE_STATUS_PREFIXED) {
-               if (copy_to_guest(vcpu, 163ul, &archmode, 1))
+               gpa = SAVE_AREA_BASE;
+       } else if (gpa == KVM_S390_STORE_STATUS_PREFIXED) {
+               if (write_guest_real(vcpu, 163, &archmode, 1))
                        return -EFAULT;
-               addr = SAVE_AREA_BASE;
-               prefix = 1;
-       } else
-               prefix = 0;
-
-       if (__guestcopy(vcpu, addr + offsetof(struct save_area, fp_regs),
-                       vcpu->arch.guest_fpregs.fprs, 128, prefix))
-               return -EFAULT;
-
-       if (__guestcopy(vcpu, addr + offsetof(struct save_area, gp_regs),
-                       vcpu->run->s.regs.gprs, 128, prefix))
-               return -EFAULT;
-
-       if (__guestcopy(vcpu, addr + offsetof(struct save_area, psw),
-                       &vcpu->arch.sie_block->gpsw, 16, prefix))
-               return -EFAULT;
-
-       if (__guestcopy(vcpu, addr + offsetof(struct save_area, pref_reg),
-                       &vcpu->arch.sie_block->prefix, 4, prefix))
-               return -EFAULT;
-
-       if (__guestcopy(vcpu,
-                       addr + offsetof(struct save_area, fp_ctrl_reg),
-                       &vcpu->arch.guest_fpregs.fpc, 4, prefix))
-               return -EFAULT;
-
-       if (__guestcopy(vcpu, addr + offsetof(struct save_area, tod_reg),
-                       &vcpu->arch.sie_block->todpr, 4, prefix))
-               return -EFAULT;
-
-       if (__guestcopy(vcpu, addr + offsetof(struct save_area, timer),
-                       &vcpu->arch.sie_block->cputm, 8, prefix))
-               return -EFAULT;
-
+               gpa = kvm_s390_real_to_abs(vcpu, SAVE_AREA_BASE);
+       }
+       rc = write_guest_abs(vcpu, gpa + offsetof(struct save_area, fp_regs),
+                            vcpu->arch.guest_fpregs.fprs, 128);
+       rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, gp_regs),
+                             vcpu->run->s.regs.gprs, 128);
+       rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, psw),
+                             &vcpu->arch.sie_block->gpsw, 16);
+       px = kvm_s390_get_prefix(vcpu);
+       rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, pref_reg),
+                             &px, 4);
+       rc |= write_guest_abs(vcpu,
+                             gpa + offsetof(struct save_area, fp_ctrl_reg),
+                             &vcpu->arch.guest_fpregs.fpc, 4);
+       rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, tod_reg),
+                             &vcpu->arch.sie_block->todpr, 4);
+       rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, timer),
+                             &vcpu->arch.sie_block->cputm, 8);
        clkcomp = vcpu->arch.sie_block->ckc >> 8;
-       if (__guestcopy(vcpu, addr + offsetof(struct save_area, clk_cmp),
-                       &clkcomp, 8, prefix))
-               return -EFAULT;
-
-       if (__guestcopy(vcpu, addr + offsetof(struct save_area, acc_regs),
-                       &vcpu->run->s.regs.acrs, 64, prefix))
-               return -EFAULT;
-
-       if (__guestcopy(vcpu,
-                       addr + offsetof(struct save_area, ctrl_regs),
-                       &vcpu->arch.sie_block->gcr, 128, prefix))
-               return -EFAULT;
-       return 0;
+       rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, clk_cmp),
+                             &clkcomp, 8);
+       rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, acc_regs),
+                             &vcpu->run->s.regs.acrs, 64);
+       rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, ctrl_regs),
+                             &vcpu->arch.sie_block->gcr, 128);
+       return rc ? -EFAULT : 0;
 }
 
 int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr)
@@ -1176,6 +1413,109 @@ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr)
        return kvm_s390_store_status_unloaded(vcpu, addr);
 }
 
+static inline int is_vcpu_stopped(struct kvm_vcpu *vcpu)
+{
+       return atomic_read(&(vcpu)->arch.sie_block->cpuflags) & CPUSTAT_STOPPED;
+}
+
+static void __disable_ibs_on_vcpu(struct kvm_vcpu *vcpu)
+{
+       kvm_check_request(KVM_REQ_ENABLE_IBS, vcpu);
+       kvm_make_request(KVM_REQ_DISABLE_IBS, vcpu);
+       exit_sie_sync(vcpu);
+}
+
+static void __disable_ibs_on_all_vcpus(struct kvm *kvm)
+{
+       unsigned int i;
+       struct kvm_vcpu *vcpu;
+
+       kvm_for_each_vcpu(i, vcpu, kvm) {
+               __disable_ibs_on_vcpu(vcpu);
+       }
+}
+
+static void __enable_ibs_on_vcpu(struct kvm_vcpu *vcpu)
+{
+       kvm_check_request(KVM_REQ_DISABLE_IBS, vcpu);
+       kvm_make_request(KVM_REQ_ENABLE_IBS, vcpu);
+       exit_sie_sync(vcpu);
+}
+
+void kvm_s390_vcpu_start(struct kvm_vcpu *vcpu)
+{
+       int i, online_vcpus, started_vcpus = 0;
+
+       if (!is_vcpu_stopped(vcpu))
+               return;
+
+       trace_kvm_s390_vcpu_start_stop(vcpu->vcpu_id, 1);
+       /* Only one cpu at a time may enter/leave the STOPPED state. */
+       spin_lock_bh(&vcpu->kvm->arch.start_stop_lock);
+       online_vcpus = atomic_read(&vcpu->kvm->online_vcpus);
+
+       for (i = 0; i < online_vcpus; i++) {
+               if (!is_vcpu_stopped(vcpu->kvm->vcpus[i]))
+                       started_vcpus++;
+       }
+
+       if (started_vcpus == 0) {
+               /* we're the only active VCPU -> speed it up */
+               __enable_ibs_on_vcpu(vcpu);
+       } else if (started_vcpus == 1) {
+               /*
+                * As we are starting a second VCPU, we have to disable
+                * the IBS facility on all VCPUs to remove potentially
+                * oustanding ENABLE requests.
+                */
+               __disable_ibs_on_all_vcpus(vcpu->kvm);
+       }
+
+       atomic_clear_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
+       /*
+        * Another VCPU might have used IBS while we were offline.
+        * Let's play safe and flush the VCPU at startup.
+        */
+       vcpu->arch.sie_block->ihcpu  = 0xffff;
+       spin_unlock_bh(&vcpu->kvm->arch.start_stop_lock);
+       return;
+}
+
+void kvm_s390_vcpu_stop(struct kvm_vcpu *vcpu)
+{
+       int i, online_vcpus, started_vcpus = 0;
+       struct kvm_vcpu *started_vcpu = NULL;
+
+       if (is_vcpu_stopped(vcpu))
+               return;
+
+       trace_kvm_s390_vcpu_start_stop(vcpu->vcpu_id, 0);
+       /* Only one cpu at a time may enter/leave the STOPPED state. */
+       spin_lock_bh(&vcpu->kvm->arch.start_stop_lock);
+       online_vcpus = atomic_read(&vcpu->kvm->online_vcpus);
+
+       atomic_set_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
+       __disable_ibs_on_vcpu(vcpu);
+
+       for (i = 0; i < online_vcpus; i++) {
+               if (!is_vcpu_stopped(vcpu->kvm->vcpus[i])) {
+                       started_vcpus++;
+                       started_vcpu = vcpu->kvm->vcpus[i];
+               }
+       }
+
+       if (started_vcpus == 1) {
+               /*
+                * As we only have one VCPU left, we want to enable the
+                * IBS facility for that VCPU to speed it up.
+                */
+               __enable_ibs_on_vcpu(started_vcpu);
+       }
+
+       spin_unlock_bh(&vcpu->kvm->arch.start_stop_lock);
+       return;
+}
+
 static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
                                     struct kvm_enable_cap *cap)
 {
index 3c1e227..a8655ed 100644 (file)
@@ -28,7 +28,6 @@ int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu);
 
 /* Transactional Memory Execution related macros */
 #define IS_TE_ENABLED(vcpu)    ((vcpu->arch.sie_block->ecb & 0x10))
-#define TDB_ADDR               0x1800UL
 #define TDB_FORMAT1            1
 #define IS_ITDB_VALID(vcpu)    ((*(char *)vcpu->arch.sie_block->itdba == TDB_FORMAT1))
 
@@ -62,9 +61,15 @@ static inline int kvm_is_ucontrol(struct kvm *kvm)
 #endif
 }
 
+#define GUEST_PREFIX_SHIFT 13
+static inline u32 kvm_s390_get_prefix(struct kvm_vcpu *vcpu)
+{
+       return vcpu->arch.sie_block->prefix << GUEST_PREFIX_SHIFT;
+}
+
 static inline void kvm_s390_set_prefix(struct kvm_vcpu *vcpu, u32 prefix)
 {
-       vcpu->arch.sie_block->prefix = prefix & 0x7fffe000u;
+       vcpu->arch.sie_block->prefix = prefix >> GUEST_PREFIX_SHIFT;
        vcpu->arch.sie_block->ihcpu  = 0xffff;
        kvm_make_request(KVM_REQ_MMU_RELOAD, vcpu);
 }
@@ -130,6 +135,7 @@ void kvm_s390_tasklet(unsigned long parm);
 void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu);
 void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu);
 void kvm_s390_clear_local_irqs(struct kvm_vcpu *vcpu);
+void kvm_s390_clear_float_irqs(struct kvm *kvm);
 int __must_check kvm_s390_inject_vm(struct kvm *kvm,
                                    struct kvm_s390_interrupt *s390int);
 int __must_check kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
@@ -137,35 +143,94 @@ int __must_check kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
 int __must_check kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code);
 struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm,
                                                    u64 cr6, u64 schid);
+void kvm_s390_reinject_io_int(struct kvm *kvm,
+                             struct kvm_s390_interrupt_info *inti);
 int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked);
 
 /* implemented in priv.c */
+int is_valid_psw(psw_t *psw);
 int kvm_s390_handle_b2(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_e5(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_01(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_b9(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu);
+int kvm_s390_handle_stctl(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_lctl(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_eb(struct kvm_vcpu *vcpu);
 
 /* implemented in sigp.c */
 int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu);
+int kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu);
 
 /* implemented in kvm-s390.c */
+long kvm_arch_fault_in_page(struct kvm_vcpu *vcpu, gpa_t gpa, int writable);
 int kvm_s390_store_status_unloaded(struct kvm_vcpu *vcpu, unsigned long addr);
 int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr);
+void kvm_s390_vcpu_start(struct kvm_vcpu *vcpu);
+void kvm_s390_vcpu_stop(struct kvm_vcpu *vcpu);
 void s390_vcpu_block(struct kvm_vcpu *vcpu);
 void s390_vcpu_unblock(struct kvm_vcpu *vcpu);
 void exit_sie(struct kvm_vcpu *vcpu);
 void exit_sie_sync(struct kvm_vcpu *vcpu);
-/* are we going to support cmma? */
-bool kvm_enabled_cmma(void);
+int kvm_s390_vcpu_setup_cmma(struct kvm_vcpu *vcpu);
+void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu);
+/* is cmma enabled */
+bool kvm_s390_cmma_enabled(struct kvm *kvm);
+int test_vfacility(unsigned long nr);
+
 /* implemented in diag.c */
 int kvm_s390_handle_diag(struct kvm_vcpu *vcpu);
+/* implemented in interrupt.c */
+int kvm_s390_inject_prog_irq(struct kvm_vcpu *vcpu,
+                            struct kvm_s390_pgm_info *pgm_info);
+
+/**
+ * kvm_s390_inject_prog_cond - conditionally inject a program check
+ * @vcpu: virtual cpu
+ * @rc: original return/error code
+ *
+ * This function is supposed to be used after regular guest access functions
+ * failed, to conditionally inject a program check to a vcpu. The typical
+ * pattern would look like
+ *
+ * rc = write_guest(vcpu, addr, data, len);
+ * if (rc)
+ *     return kvm_s390_inject_prog_cond(vcpu, rc);
+ *
+ * A negative return code from guest access functions implies an internal error
+ * like e.g. out of memory. In these cases no program check should be injected
+ * to the guest.
+ * A positive value implies that an exception happened while accessing a guest's
+ * memory. In this case all data belonging to the corresponding program check
+ * has been stored in vcpu->arch.pgm and can be injected with
+ * kvm_s390_inject_prog_irq().
+ *
+ * Returns: - the original @rc value if @rc was negative (internal error)
+ *         - zero if @rc was already zero
+ *         - zero or error code from injecting if @rc was positive
+ *           (program check injected to @vcpu)
+ */
+static inline int kvm_s390_inject_prog_cond(struct kvm_vcpu *vcpu, int rc)
+{
+       if (rc <= 0)
+               return rc;
+       return kvm_s390_inject_prog_irq(vcpu, &vcpu->arch.pgm);
+}
 
 /* implemented in interrupt.c */
 int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu);
 int psw_extint_disabled(struct kvm_vcpu *vcpu);
 void kvm_s390_destroy_adapters(struct kvm *kvm);
+int kvm_s390_si_ext_call_pending(struct kvm_vcpu *vcpu);
+
+/* implemented in guestdbg.c */
+void kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu);
+void kvm_s390_restore_guest_per_regs(struct kvm_vcpu *vcpu);
+void kvm_s390_patch_guest_per_regs(struct kvm_vcpu *vcpu);
+int kvm_s390_import_bp_data(struct kvm_vcpu *vcpu,
+                           struct kvm_guest_debug *dbg);
+void kvm_s390_clear_bp_data(struct kvm_vcpu *vcpu);
+void kvm_s390_prepare_debug_exit(struct kvm_vcpu *vcpu);
+void kvm_s390_handle_per_event(struct kvm_vcpu *vcpu);
 
 #endif
index 476e9e2..f89c1cd 100644 (file)
@@ -35,8 +35,8 @@ static int handle_set_clock(struct kvm_vcpu *vcpu)
 {
        struct kvm_vcpu *cpup;
        s64 hostclk, val;
+       int i, rc;
        u64 op2;
-       int i;
 
        if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
                return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
@@ -44,8 +44,9 @@ static int handle_set_clock(struct kvm_vcpu *vcpu)
        op2 = kvm_s390_get_base_disp_s(vcpu);
        if (op2 & 7)    /* Operand must be on a doubleword boundary */
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
-       if (get_guest(vcpu, val, (u64 __user *) op2))
-               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       rc = read_guest(vcpu, op2, &val, sizeof(val));
+       if (rc)
+               return kvm_s390_inject_prog_cond(vcpu, rc);
 
        if (store_tod_clock(&hostclk)) {
                kvm_s390_set_psw_cc(vcpu, 3);
@@ -65,8 +66,8 @@ static int handle_set_clock(struct kvm_vcpu *vcpu)
 static int handle_set_prefix(struct kvm_vcpu *vcpu)
 {
        u64 operand2;
-       u32 address = 0;
-       u8 tmp;
+       u32 address;
+       int rc;
 
        vcpu->stat.instruction_spx++;
 
@@ -80,14 +81,18 @@ static int handle_set_prefix(struct kvm_vcpu *vcpu)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
        /* get the value */
-       if (get_guest(vcpu, address, (u32 __user *) operand2))
-               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       rc = read_guest(vcpu, operand2, &address, sizeof(address));
+       if (rc)
+               return kvm_s390_inject_prog_cond(vcpu, rc);
 
-       address = address & 0x7fffe000u;
+       address &= 0x7fffe000u;
 
-       /* make sure that the new value is valid memory */
-       if (copy_from_guest_absolute(vcpu, &tmp, address, 1) ||
-          (copy_from_guest_absolute(vcpu, &tmp, address + PAGE_SIZE, 1)))
+       /*
+        * Make sure the new value is valid memory. We only need to check the
+        * first page, since address is 8k aligned and memory pieces are always
+        * at least 1MB aligned and have at least a size of 1MB.
+        */
+       if (kvm_is_error_gpa(vcpu->kvm, address))
                return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 
        kvm_s390_set_prefix(vcpu, address);
@@ -101,6 +106,7 @@ static int handle_store_prefix(struct kvm_vcpu *vcpu)
 {
        u64 operand2;
        u32 address;
+       int rc;
 
        vcpu->stat.instruction_stpx++;
 
@@ -113,12 +119,12 @@ static int handle_store_prefix(struct kvm_vcpu *vcpu)
        if (operand2 & 3)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
-       address = vcpu->arch.sie_block->prefix;
-       address = address & 0x7fffe000u;
+       address = kvm_s390_get_prefix(vcpu);
 
        /* get the value */
-       if (put_guest(vcpu, address, (u32 __user *)operand2))
-               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       rc = write_guest(vcpu, operand2, &address, sizeof(address));
+       if (rc)
+               return kvm_s390_inject_prog_cond(vcpu, rc);
 
        VCPU_EVENT(vcpu, 5, "storing prefix to %x", address);
        trace_kvm_s390_handle_prefix(vcpu, 0, address);
@@ -127,28 +133,44 @@ static int handle_store_prefix(struct kvm_vcpu *vcpu)
 
 static int handle_store_cpu_address(struct kvm_vcpu *vcpu)
 {
-       u64 useraddr;
+       u16 vcpu_id = vcpu->vcpu_id;
+       u64 ga;
+       int rc;
 
        vcpu->stat.instruction_stap++;
 
        if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
                return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
 
-       useraddr = kvm_s390_get_base_disp_s(vcpu);
+       ga = kvm_s390_get_base_disp_s(vcpu);
 
-       if (useraddr & 1)
+       if (ga & 1)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
-       if (put_guest(vcpu, vcpu->vcpu_id, (u16 __user *)useraddr))
-               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       rc = write_guest(vcpu, ga, &vcpu_id, sizeof(vcpu_id));
+       if (rc)
+               return kvm_s390_inject_prog_cond(vcpu, rc);
 
-       VCPU_EVENT(vcpu, 5, "storing cpu address to %llx", useraddr);
-       trace_kvm_s390_handle_stap(vcpu, useraddr);
+       VCPU_EVENT(vcpu, 5, "storing cpu address to %llx", ga);
+       trace_kvm_s390_handle_stap(vcpu, ga);
        return 0;
 }
 
+static void __skey_check_enable(struct kvm_vcpu *vcpu)
+{
+       if (!(vcpu->arch.sie_block->ictl & (ICTL_ISKE | ICTL_SSKE | ICTL_RRBE)))
+               return;
+
+       s390_enable_skey();
+       trace_kvm_s390_skey_related_inst(vcpu);
+       vcpu->arch.sie_block->ictl &= ~(ICTL_ISKE | ICTL_SSKE | ICTL_RRBE);
+}
+
+
 static int handle_skey(struct kvm_vcpu *vcpu)
 {
+       __skey_check_enable(vcpu);
+
        vcpu->stat.instruction_storage_key++;
 
        if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
@@ -160,9 +182,21 @@ static int handle_skey(struct kvm_vcpu *vcpu)
        return 0;
 }
 
+static int handle_ipte_interlock(struct kvm_vcpu *vcpu)
+{
+       psw_t *psw = &vcpu->arch.sie_block->gpsw;
+
+       vcpu->stat.instruction_ipte_interlock++;
+       if (psw_bits(*psw).p)
+               return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
+       wait_event(vcpu->kvm->arch.ipte_wq, !ipte_lock_held(vcpu));
+       psw->addr = __rewind_psw(*psw, 4);
+       VCPU_EVENT(vcpu, 4, "%s", "retrying ipte interlock operation");
+       return 0;
+}
+
 static int handle_test_block(struct kvm_vcpu *vcpu)
 {
-       unsigned long hva;
        gpa_t addr;
        int reg2;
 
@@ -171,16 +205,18 @@ static int handle_test_block(struct kvm_vcpu *vcpu)
 
        kvm_s390_get_regs_rre(vcpu, NULL, &reg2);
        addr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
+       addr = kvm_s390_logical_to_effective(vcpu, addr);
+       if (kvm_s390_check_low_addr_protection(vcpu, addr))
+               return kvm_s390_inject_prog_irq(vcpu, &vcpu->arch.pgm);
        addr = kvm_s390_real_to_abs(vcpu, addr);
 
-       hva = gfn_to_hva(vcpu->kvm, gpa_to_gfn(addr));
-       if (kvm_is_error_hva(hva))
+       if (kvm_is_error_gpa(vcpu->kvm, addr))
                return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
        /*
         * We don't expect errors on modern systems, and do not care
         * about storage keys (yet), so let's just clear the page.
         */
-       if (clear_user((void __user *)hva, PAGE_SIZE) != 0)
+       if (kvm_clear_guest(vcpu->kvm, addr, PAGE_SIZE))
                return -EFAULT;
        kvm_s390_set_psw_cc(vcpu, 0);
        vcpu->run->s.regs.gprs[0] = 0;
@@ -190,9 +226,12 @@ static int handle_test_block(struct kvm_vcpu *vcpu)
 static int handle_tpi(struct kvm_vcpu *vcpu)
 {
        struct kvm_s390_interrupt_info *inti;
+       unsigned long len;
+       u32 tpi_data[3];
+       int cc, rc;
        u64 addr;
-       int cc;
 
+       rc = 0;
        addr = kvm_s390_get_base_disp_s(vcpu);
        if (addr & 3)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
@@ -201,30 +240,41 @@ static int handle_tpi(struct kvm_vcpu *vcpu)
        if (!inti)
                goto no_interrupt;
        cc = 1;
+       tpi_data[0] = inti->io.subchannel_id << 16 | inti->io.subchannel_nr;
+       tpi_data[1] = inti->io.io_int_parm;
+       tpi_data[2] = inti->io.io_int_word;
        if (addr) {
                /*
                 * Store the two-word I/O interruption code into the
                 * provided area.
                 */
-               if (put_guest(vcpu, inti->io.subchannel_id, (u16 __user *)addr)
-                   || put_guest(vcpu, inti->io.subchannel_nr, (u16 __user *)(addr + 2))
-                   || put_guest(vcpu, inti->io.io_int_parm, (u32 __user *)(addr + 4)))
-                       return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+               len = sizeof(tpi_data) - 4;
+               rc = write_guest(vcpu, addr, &tpi_data, len);
+               if (rc)
+                       return kvm_s390_inject_prog_cond(vcpu, rc);
        } else {
                /*
                 * Store the three-word I/O interruption code into
                 * the appropriate lowcore area.
                 */
-               put_guest(vcpu, inti->io.subchannel_id, (u16 __user *) __LC_SUBCHANNEL_ID);
-               put_guest(vcpu, inti->io.subchannel_nr, (u16 __user *) __LC_SUBCHANNEL_NR);
-               put_guest(vcpu, inti->io.io_int_parm, (u32 __user *) __LC_IO_INT_PARM);
-               put_guest(vcpu, inti->io.io_int_word, (u32 __user *) __LC_IO_INT_WORD);
+               len = sizeof(tpi_data);
+               if (write_guest_lc(vcpu, __LC_SUBCHANNEL_ID, &tpi_data, len))
+                       rc = -EFAULT;
        }
-       kfree(inti);
+       /*
+        * If we encounter a problem storing the interruption code, the
+        * instruction is suppressed from the guest's view: reinject the
+        * interrupt.
+        */
+       if (!rc)
+               kfree(inti);
+       else
+               kvm_s390_reinject_io_int(vcpu->kvm, inti);
 no_interrupt:
        /* Set condition code and we're done. */
-       kvm_s390_set_psw_cc(vcpu, cc);
-       return 0;
+       if (!rc)
+               kvm_s390_set_psw_cc(vcpu, cc);
+       return rc ? -EFAULT : 0;
 }
 
 static int handle_tsch(struct kvm_vcpu *vcpu)
@@ -292,10 +342,10 @@ static int handle_stfl(struct kvm_vcpu *vcpu)
        if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
                return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
 
-       rc = copy_to_guest(vcpu, offsetof(struct _lowcore, stfl_fac_list),
-                          vfacilities, 4);
+       rc = write_guest_lc(vcpu, offsetof(struct _lowcore, stfl_fac_list),
+                           vfacilities, 4);
        if (rc)
-               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+               return rc;
        VCPU_EVENT(vcpu, 5, "store facility list value %x",
                   *(unsigned int *) vfacilities);
        trace_kvm_s390_handle_stfl(vcpu, *(unsigned int *) vfacilities);
@@ -314,7 +364,8 @@ static void handle_new_psw(struct kvm_vcpu *vcpu)
 #define PSW_ADDR_24 0x0000000000ffffffUL
 #define PSW_ADDR_31 0x000000007fffffffUL
 
-static int is_valid_psw(psw_t *psw) {
+int is_valid_psw(psw_t *psw)
+{
        if (psw->mask & PSW_MASK_UNASSIGNED)
                return 0;
        if ((psw->mask & PSW_MASK_ADDR_MODE) == PSW_MASK_BA) {
@@ -325,6 +376,8 @@ static int is_valid_psw(psw_t *psw) {
                return 0;
        if ((psw->mask & PSW_MASK_ADDR_MODE) ==  PSW_MASK_EA)
                return 0;
+       if (psw->addr & 1)
+               return 0;
        return 1;
 }
 
@@ -333,6 +386,7 @@ int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu)
        psw_t *gpsw = &vcpu->arch.sie_block->gpsw;
        psw_compat_t new_psw;
        u64 addr;
+       int rc;
 
        if (gpsw->mask & PSW_MASK_PSTATE)
                return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
@@ -340,8 +394,10 @@ int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu)
        addr = kvm_s390_get_base_disp_s(vcpu);
        if (addr & 7)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
-       if (copy_from_guest(vcpu, &new_psw, addr, sizeof(new_psw)))
-               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+
+       rc = read_guest(vcpu, addr, &new_psw, sizeof(new_psw));
+       if (rc)
+               return kvm_s390_inject_prog_cond(vcpu, rc);
        if (!(new_psw.mask & PSW32_MASK_BASE))
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
        gpsw->mask = (new_psw.mask & ~PSW32_MASK_BASE) << 32;
@@ -357,6 +413,7 @@ static int handle_lpswe(struct kvm_vcpu *vcpu)
 {
        psw_t new_psw;
        u64 addr;
+       int rc;
 
        if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
                return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
@@ -364,8 +421,9 @@ static int handle_lpswe(struct kvm_vcpu *vcpu)
        addr = kvm_s390_get_base_disp_s(vcpu);
        if (addr & 7)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
-       if (copy_from_guest(vcpu, &new_psw, addr, sizeof(new_psw)))
-               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       rc = read_guest(vcpu, addr, &new_psw, sizeof(new_psw));
+       if (rc)
+               return kvm_s390_inject_prog_cond(vcpu, rc);
        vcpu->arch.sie_block->gpsw = new_psw;
        if (!is_valid_psw(&vcpu->arch.sie_block->gpsw))
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
@@ -375,7 +433,9 @@ static int handle_lpswe(struct kvm_vcpu *vcpu)
 
 static int handle_stidp(struct kvm_vcpu *vcpu)
 {
+       u64 stidp_data = vcpu->arch.stidp_data;
        u64 operand2;
+       int rc;
 
        vcpu->stat.instruction_stidp++;
 
@@ -387,8 +447,9 @@ static int handle_stidp(struct kvm_vcpu *vcpu)
        if (operand2 & 7)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
-       if (put_guest(vcpu, vcpu->arch.stidp_data, (u64 __user *)operand2))
-               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       rc = write_guest(vcpu, operand2, &stidp_data, sizeof(stidp_data));
+       if (rc)
+               return kvm_s390_inject_prog_cond(vcpu, rc);
 
        VCPU_EVENT(vcpu, 5, "%s", "store cpu id");
        return 0;
@@ -474,9 +535,10 @@ static int handle_stsi(struct kvm_vcpu *vcpu)
                break;
        }
 
-       if (copy_to_guest_absolute(vcpu, operand2, (void *) mem, PAGE_SIZE)) {
-               rc = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
-               goto out_exception;
+       rc = write_guest(vcpu, operand2, (void *)mem, PAGE_SIZE);
+       if (rc) {
+               rc = kvm_s390_inject_prog_cond(vcpu, rc);
+               goto out;
        }
        trace_kvm_s390_handle_stsi(vcpu, fc, sel1, sel2, operand2);
        free_page(mem);
@@ -485,7 +547,7 @@ static int handle_stsi(struct kvm_vcpu *vcpu)
        return 0;
 out_no_data:
        kvm_s390_set_psw_cc(vcpu, 3);
-out_exception:
+out:
        free_page(mem);
        return rc;
 }
@@ -496,6 +558,7 @@ static const intercept_handler_t b2_handlers[256] = {
        [0x10] = handle_set_prefix,
        [0x11] = handle_store_prefix,
        [0x12] = handle_store_cpu_address,
+       [0x21] = handle_ipte_interlock,
        [0x29] = handle_skey,
        [0x2a] = handle_skey,
        [0x2b] = handle_skey,
@@ -513,6 +576,7 @@ static const intercept_handler_t b2_handlers[256] = {
        [0x3a] = handle_io_inst,
        [0x3b] = handle_io_inst,
        [0x3c] = handle_io_inst,
+       [0x50] = handle_ipte_interlock,
        [0x5f] = handle_io_inst,
        [0x74] = handle_io_inst,
        [0x76] = handle_io_inst,
@@ -591,6 +655,11 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
        start = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
+       if (vcpu->run->s.regs.gprs[reg1] & PFMF_CF) {
+               if (kvm_s390_check_low_addr_protection(vcpu, start))
+                       return kvm_s390_inject_prog_irq(vcpu, &vcpu->arch.pgm);
+       }
+
        switch (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) {
        case 0x00000000:
                end = (start + (1UL << 12)) & ~((1UL << 12) - 1);
@@ -606,10 +675,15 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
        }
        while (start < end) {
-               unsigned long useraddr;
-
-               useraddr = gmap_translate(start, vcpu->arch.gmap);
-               if (IS_ERR((void *)useraddr))
+               unsigned long useraddr, abs_addr;
+
+               /* Translate guest address to host address */
+               if ((vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) == 0)
+                       abs_addr = kvm_s390_real_to_abs(vcpu, start);
+               else
+                       abs_addr = start;
+               useraddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(abs_addr));
+               if (kvm_is_error_hva(useraddr))
                        return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 
                if (vcpu->run->s.regs.gprs[reg1] & PFMF_CF) {
@@ -618,6 +692,7 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
                }
 
                if (vcpu->run->s.regs.gprs[reg1] & PFMF_SK) {
+                       __skey_check_enable(vcpu);
                        if (set_guest_storage_key(current->mm, useraddr,
                                        vcpu->run->s.regs.gprs[reg1] & PFMF_KEY,
                                        vcpu->run->s.regs.gprs[reg1] & PFMF_NQ))
@@ -642,7 +717,7 @@ static int handle_essa(struct kvm_vcpu *vcpu)
        VCPU_EVENT(vcpu, 5, "cmma release %d pages", entries);
        gmap = vcpu->arch.gmap;
        vcpu->stat.instruction_essa++;
-       if (!kvm_enabled_cmma() || !vcpu->arch.sie_block->cbrlo)
+       if (!kvm_s390_cmma_enabled(vcpu->kvm))
                return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
 
        if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
@@ -672,7 +747,10 @@ static int handle_essa(struct kvm_vcpu *vcpu)
 }
 
 static const intercept_handler_t b9_handlers[256] = {
+       [0x8a] = handle_ipte_interlock,
        [0x8d] = handle_epsw,
+       [0x8e] = handle_ipte_interlock,
+       [0x8f] = handle_ipte_interlock,
        [0xab] = handle_essa,
        [0xaf] = handle_pfmf,
 };
@@ -693,32 +771,67 @@ int kvm_s390_handle_lctl(struct kvm_vcpu *vcpu)
 {
        int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4;
        int reg3 = vcpu->arch.sie_block->ipa & 0x000f;
-       u64 useraddr;
        u32 val = 0;
        int reg, rc;
+       u64 ga;
 
        vcpu->stat.instruction_lctl++;
 
        if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
                return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
 
-       useraddr = kvm_s390_get_base_disp_rs(vcpu);
+       ga = kvm_s390_get_base_disp_rs(vcpu);
 
-       if (useraddr & 3)
+       if (ga & 3)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
-       VCPU_EVENT(vcpu, 5, "lctl r1:%x, r3:%x, addr:%llx", reg1, reg3,
-                  useraddr);
-       trace_kvm_s390_handle_lctl(vcpu, 0, reg1, reg3, useraddr);
+       VCPU_EVENT(vcpu, 5, "lctl r1:%x, r3:%x, addr:%llx", reg1, reg3, ga);
+       trace_kvm_s390_handle_lctl(vcpu, 0, reg1, reg3, ga);
 
        reg = reg1;
        do {
-               rc = get_guest(vcpu, val, (u32 __user *) useraddr);
+               rc = read_guest(vcpu, ga, &val, sizeof(val));
                if (rc)
-                       return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+                       return kvm_s390_inject_prog_cond(vcpu, rc);
                vcpu->arch.sie_block->gcr[reg] &= 0xffffffff00000000ul;
                vcpu->arch.sie_block->gcr[reg] |= val;
-               useraddr += 4;
+               ga += 4;
+               if (reg == reg3)
+                       break;
+               reg = (reg + 1) % 16;
+       } while (1);
+
+       return 0;
+}
+
+int kvm_s390_handle_stctl(struct kvm_vcpu *vcpu)
+{
+       int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4;
+       int reg3 = vcpu->arch.sie_block->ipa & 0x000f;
+       u64 ga;
+       u32 val;
+       int reg, rc;
+
+       vcpu->stat.instruction_stctl++;
+
+       if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
+               return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
+
+       ga = kvm_s390_get_base_disp_rs(vcpu);
+
+       if (ga & 3)
+               return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
+       VCPU_EVENT(vcpu, 5, "stctl r1:%x, r3:%x, addr:%llx", reg1, reg3, ga);
+       trace_kvm_s390_handle_stctl(vcpu, 0, reg1, reg3, ga);
+
+       reg = reg1;
+       do {
+               val = vcpu->arch.sie_block->gcr[reg] &  0x00000000fffffffful;
+               rc = write_guest(vcpu, ga, &val, sizeof(val));
+               if (rc)
+                       return kvm_s390_inject_prog_cond(vcpu, rc);
+               ga += 4;
                if (reg == reg3)
                        break;
                reg = (reg + 1) % 16;
@@ -731,7 +844,7 @@ static int handle_lctlg(struct kvm_vcpu *vcpu)
 {
        int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4;
        int reg3 = vcpu->arch.sie_block->ipa & 0x000f;
-       u64 useraddr;
+       u64 ga, val;
        int reg, rc;
 
        vcpu->stat.instruction_lctlg++;
@@ -739,23 +852,58 @@ static int handle_lctlg(struct kvm_vcpu *vcpu)
        if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
                return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
 
-       useraddr = kvm_s390_get_base_disp_rsy(vcpu);
+       ga = kvm_s390_get_base_disp_rsy(vcpu);
 
-       if (useraddr & 7)
+       if (ga & 7)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
        reg = reg1;
 
-       VCPU_EVENT(vcpu, 5, "lctlg r1:%x, r3:%x, addr:%llx", reg1, reg3,
-                  useraddr);
-       trace_kvm_s390_handle_lctl(vcpu, 1, reg1, reg3, useraddr);
+       VCPU_EVENT(vcpu, 5, "lctlg r1:%x, r3:%x, addr:%llx", reg1, reg3, ga);
+       trace_kvm_s390_handle_lctl(vcpu, 1, reg1, reg3, ga);
 
        do {
-               rc = get_guest(vcpu, vcpu->arch.sie_block->gcr[reg],
-                              (u64 __user *) useraddr);
+               rc = read_guest(vcpu, ga, &val, sizeof(val));
                if (rc)
-                       return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
-               useraddr += 8;
+                       return kvm_s390_inject_prog_cond(vcpu, rc);
+               vcpu->arch.sie_block->gcr[reg] = val;
+               ga += 8;
+               if (reg == reg3)
+                       break;
+               reg = (reg + 1) % 16;
+       } while (1);
+
+       return 0;
+}
+
+static int handle_stctg(struct kvm_vcpu *vcpu)
+{
+       int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4;
+       int reg3 = vcpu->arch.sie_block->ipa & 0x000f;
+       u64 ga, val;
+       int reg, rc;
+
+       vcpu->stat.instruction_stctg++;
+
+       if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
+               return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
+
+       ga = kvm_s390_get_base_disp_rsy(vcpu);
+
+       if (ga & 7)
+               return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
+       reg = reg1;
+
+       VCPU_EVENT(vcpu, 5, "stctg r1:%x, r3:%x, addr:%llx", reg1, reg3, ga);
+       trace_kvm_s390_handle_stctl(vcpu, 1, reg1, reg3, ga);
+
+       do {
+               val = vcpu->arch.sie_block->gcr[reg];
+               rc = write_guest(vcpu, ga, &val, sizeof(val));
+               if (rc)
+                       return kvm_s390_inject_prog_cond(vcpu, rc);
+               ga += 8;
                if (reg == reg3)
                        break;
                reg = (reg + 1) % 16;
@@ -766,6 +914,7 @@ static int handle_lctlg(struct kvm_vcpu *vcpu)
 
 static const intercept_handler_t eb_handlers[256] = {
        [0x2f] = handle_lctlg,
+       [0x25] = handle_stctg,
 };
 
 int kvm_s390_handle_eb(struct kvm_vcpu *vcpu)
@@ -781,8 +930,9 @@ int kvm_s390_handle_eb(struct kvm_vcpu *vcpu)
 static int handle_tprot(struct kvm_vcpu *vcpu)
 {
        u64 address1, address2;
-       struct vm_area_struct *vma;
-       unsigned long user_address;
+       unsigned long hva, gpa;
+       int ret = 0, cc = 0;
+       bool writable;
 
        vcpu->stat.instruction_tprot++;
 
@@ -793,32 +943,41 @@ static int handle_tprot(struct kvm_vcpu *vcpu)
 
        /* we only handle the Linux memory detection case:
         * access key == 0
-        * guest DAT == off
         * everything else goes to userspace. */
        if (address2 & 0xf0)
                return -EOPNOTSUPP;
        if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT)
-               return -EOPNOTSUPP;
-
-       down_read(&current->mm->mmap_sem);
-       user_address = __gmap_translate(address1, vcpu->arch.gmap);
-       if (IS_ERR_VALUE(user_address))
-               goto out_inject;
-       vma = find_vma(current->mm, user_address);
-       if (!vma)
-               goto out_inject;
-       vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
-       if (!(vma->vm_flags & VM_WRITE) && (vma->vm_flags & VM_READ))
-               vcpu->arch.sie_block->gpsw.mask |= (1ul << 44);
-       if (!(vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_READ))
-               vcpu->arch.sie_block->gpsw.mask |= (2ul << 44);
-
-       up_read(&current->mm->mmap_sem);
-       return 0;
+               ipte_lock(vcpu);
+       ret = guest_translate_address(vcpu, address1, &gpa, 1);
+       if (ret == PGM_PROTECTION) {
+               /* Write protected? Try again with read-only... */
+               cc = 1;
+               ret = guest_translate_address(vcpu, address1, &gpa, 0);
+       }
+       if (ret) {
+               if (ret == PGM_ADDRESSING || ret == PGM_TRANSLATION_SPEC) {
+                       ret = kvm_s390_inject_program_int(vcpu, ret);
+               } else if (ret > 0) {
+                       /* Translation not available */
+                       kvm_s390_set_psw_cc(vcpu, 3);
+                       ret = 0;
+               }
+               goto out_unlock;
+       }
 
-out_inject:
-       up_read(&current->mm->mmap_sem);
-       return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       hva = gfn_to_hva_prot(vcpu->kvm, gpa_to_gfn(gpa), &writable);
+       if (kvm_is_error_hva(hva)) {
+               ret = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       } else {
+               if (!writable)
+                       cc = 1;         /* Write not permitted ==> read-only */
+               kvm_s390_set_psw_cc(vcpu, cc);
+               /* Note: CC2 only occurs for storage keys (not supported yet) */
+       }
+out_unlock:
+       if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT)
+               ipte_unlock(vcpu);
+       return ret;
 }
 
 int kvm_s390_handle_e5(struct kvm_vcpu *vcpu)
index 26caeb5..43079a4 100644 (file)
@@ -54,33 +54,23 @@ static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr,
 
 static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr)
 {
-       struct kvm_s390_local_interrupt *li;
-       struct kvm_s390_interrupt_info *inti;
+       struct kvm_s390_interrupt s390int = {
+               .type = KVM_S390_INT_EMERGENCY,
+               .parm = vcpu->vcpu_id,
+       };
        struct kvm_vcpu *dst_vcpu = NULL;
+       int rc = 0;
 
        if (cpu_addr < KVM_MAX_VCPUS)
                dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
        if (!dst_vcpu)
                return SIGP_CC_NOT_OPERATIONAL;
 
-       inti = kzalloc(sizeof(*inti), GFP_KERNEL);
-       if (!inti)
-               return -ENOMEM;
-
-       inti->type = KVM_S390_INT_EMERGENCY;
-       inti->emerg.code = vcpu->vcpu_id;
-
-       li = &dst_vcpu->arch.local_int;
-       spin_lock_bh(&li->lock);
-       list_add_tail(&inti->list, &li->list);
-       atomic_set(&li->active, 1);
-       atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
-       if (waitqueue_active(li->wq))
-               wake_up_interruptible(li->wq);
-       spin_unlock_bh(&li->lock);
-       VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr);
+       rc = kvm_s390_inject_vcpu(dst_vcpu, &s390int);
+       if (!rc)
+               VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr);
 
-       return SIGP_CC_ORDER_CODE_ACCEPTED;
+       return rc ? rc : SIGP_CC_ORDER_CODE_ACCEPTED;
 }
 
 static int __sigp_conditional_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr,
@@ -116,33 +106,23 @@ static int __sigp_conditional_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr,
 
 static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr)
 {
-       struct kvm_s390_local_interrupt *li;
-       struct kvm_s390_interrupt_info *inti;
+       struct kvm_s390_interrupt s390int = {
+               .type = KVM_S390_INT_EXTERNAL_CALL,
+               .parm = vcpu->vcpu_id,
+       };
        struct kvm_vcpu *dst_vcpu = NULL;
+       int rc;
 
        if (cpu_addr < KVM_MAX_VCPUS)
                dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
        if (!dst_vcpu)
                return SIGP_CC_NOT_OPERATIONAL;
 
-       inti = kzalloc(sizeof(*inti), GFP_KERNEL);
-       if (!inti)
-               return -ENOMEM;
+       rc = kvm_s390_inject_vcpu(dst_vcpu, &s390int);
+       if (!rc)
+               VCPU_EVENT(vcpu, 4, "sent sigp ext call to cpu %x", cpu_addr);
 
-       inti->type = KVM_S390_INT_EXTERNAL_CALL;
-       inti->extcall.code = vcpu->vcpu_id;
-
-       li = &dst_vcpu->arch.local_int;
-       spin_lock_bh(&li->lock);
-       list_add_tail(&inti->list, &li->list);
-       atomic_set(&li->active, 1);
-       atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
-       if (waitqueue_active(li->wq))
-               wake_up_interruptible(li->wq);
-       spin_unlock_bh(&li->lock);
-       VCPU_EVENT(vcpu, 4, "sent sigp ext call to cpu %x", cpu_addr);
-
-       return SIGP_CC_ORDER_CODE_ACCEPTED;
+       return rc ? rc : SIGP_CC_ORDER_CODE_ACCEPTED;
 }
 
 static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action)
@@ -235,7 +215,6 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
        struct kvm_vcpu *dst_vcpu = NULL;
        struct kvm_s390_interrupt_info *inti;
        int rc;
-       u8 tmp;
 
        if (cpu_addr < KVM_MAX_VCPUS)
                dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
@@ -243,10 +222,13 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
                return SIGP_CC_NOT_OPERATIONAL;
        li = &dst_vcpu->arch.local_int;
 
-       /* make sure that the new value is valid memory */
-       address = address & 0x7fffe000u;
-       if (copy_from_guest_absolute(vcpu, &tmp, address, 1) ||
-          copy_from_guest_absolute(vcpu, &tmp, address + PAGE_SIZE, 1)) {
+       /*
+        * Make sure the new value is valid memory. We only need to check the
+        * first page, since address is 8k aligned and memory pieces are always
+        * at least 1MB aligned and have at least a size of 1MB.
+        */
+       address &= 0x7fffe000u;
+       if (kvm_is_error_gpa(vcpu->kvm, address)) {
                *reg &= 0xffffffff00000000UL;
                *reg |= SIGP_STATUS_INVALID_PARAMETER;
                return SIGP_CC_STATUS_STORED;
@@ -456,3 +438,38 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
        kvm_s390_set_psw_cc(vcpu, rc);
        return 0;
 }
+
+/*
+ * Handle SIGP partial execution interception.
+ *
+ * This interception will occur at the source cpu when a source cpu sends an
+ * external call to a target cpu and the target cpu has the WAIT bit set in
+ * its cpuflags. Interception will occurr after the interrupt indicator bits at
+ * the target cpu have been set. All error cases will lead to instruction
+ * interception, therefore nothing is to be checked or prepared.
+ */
+int kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu)
+{
+       int r3 = vcpu->arch.sie_block->ipa & 0x000f;
+       u16 cpu_addr = vcpu->run->s.regs.gprs[r3];
+       struct kvm_vcpu *dest_vcpu;
+       u8 order_code = kvm_s390_get_base_disp_rs(vcpu);
+
+       trace_kvm_s390_handle_sigp_pei(vcpu, order_code, cpu_addr);
+
+       if (order_code == SIGP_EXTERNAL_CALL) {
+               dest_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
+               BUG_ON(dest_vcpu == NULL);
+
+               spin_lock_bh(&dest_vcpu->arch.local_int.lock);
+               if (waitqueue_active(&dest_vcpu->wq))
+                       wake_up_interruptible(&dest_vcpu->wq);
+               dest_vcpu->preempted = true;
+               spin_unlock_bh(&dest_vcpu->arch.local_int.lock);
+
+               kvm_s390_set_psw_cc(vcpu, SIGP_CC_ORDER_CODE_ACCEPTED);
+               return 0;
+       }
+
+       return -EOPNOTSUPP;
+}
index 13f30f5..647e9d6 100644 (file)
@@ -67,6 +67,27 @@ TRACE_EVENT(kvm_s390_destroy_vcpu,
            TP_printk("destroy cpu %d", __entry->id)
        );
 
+/*
+ * Trace point for start and stop of vpcus.
+ */
+TRACE_EVENT(kvm_s390_vcpu_start_stop,
+           TP_PROTO(unsigned int id, int state),
+           TP_ARGS(id, state),
+
+           TP_STRUCT__entry(
+                   __field(unsigned int, id)
+                   __field(int, state)
+                   ),
+
+           TP_fast_assign(
+                   __entry->id = id;
+                   __entry->state = state;
+                   ),
+
+           TP_printk("%s cpu %d", __entry->state ? "starting" : "stopping",
+                     __entry->id)
+       );
+
 /*
  * Trace points for injection of interrupts, either per machine or
  * per vcpu.
@@ -223,6 +244,28 @@ TRACE_EVENT(kvm_s390_enable_css,
                      __entry->kvm)
        );
 
+/*
+ * Trace point for enabling and disabling interlocking-and-broadcasting
+ * suppression.
+ */
+TRACE_EVENT(kvm_s390_enable_disable_ibs,
+           TP_PROTO(unsigned int id, int state),
+           TP_ARGS(id, state),
+
+           TP_STRUCT__entry(
+                   __field(unsigned int, id)
+                   __field(int, state)
+                   ),
+
+           TP_fast_assign(
+                   __entry->id = id;
+                   __entry->state = state;
+                   ),
+
+           TP_printk("%s ibs on cpu %d",
+                     __entry->state ? "enabling" : "disabling", __entry->id)
+       );
+
 
 #endif /* _TRACE_KVMS390_H */
 
index e8e7213..916834d 100644 (file)
@@ -2,7 +2,7 @@
 #define _TRACE_KVM_H
 
 #include <linux/tracepoint.h>
-#include <asm/sigp.h>
+#include <asm/sie.h>
 #include <asm/debug.h>
 #include <asm/dis.h>
 
        TP_printk("%02d[%016lx-%016lx]: " p_str, __entry->id,           \
                  __entry->pswmask, __entry->pswaddr, p_args)
 
+TRACE_EVENT(kvm_s390_skey_related_inst,
+           TP_PROTO(VCPU_PROTO_COMMON),
+           TP_ARGS(VCPU_ARGS_COMMON),
+
+           TP_STRUCT__entry(
+                   VCPU_FIELD_COMMON
+                   ),
+
+           TP_fast_assign(
+                   VCPU_ASSIGN_COMMON
+                   ),
+           VCPU_TP_PRINTK("%s", "first instruction related to skeys on vcpu")
+       );
+
 TRACE_EVENT(kvm_s390_major_guest_pfault,
            TP_PROTO(VCPU_PROTO_COMMON),
            TP_ARGS(VCPU_ARGS_COMMON),
@@ -111,17 +125,6 @@ TRACE_EVENT(kvm_s390_sie_fault,
            VCPU_TP_PRINTK("%s", "fault in sie instruction")
        );
 
-#define sie_intercept_code                             \
-       {0x04, "Instruction"},                          \
-       {0x08, "Program interruption"},                 \
-       {0x0C, "Instruction and program interruption"}, \
-       {0x10, "External request"},                     \
-       {0x14, "External interruption"},                \
-       {0x18, "I/O request"},                          \
-       {0x1C, "Wait state"},                           \
-       {0x20, "Validity"},                             \
-       {0x28, "Stop request"}
-
 TRACE_EVENT(kvm_s390_sie_exit,
            TP_PROTO(VCPU_PROTO_COMMON, u8 icptcode),
            TP_ARGS(VCPU_ARGS_COMMON, icptcode),
@@ -151,7 +154,6 @@ TRACE_EVENT(kvm_s390_intercept_instruction,
            TP_STRUCT__entry(
                    VCPU_FIELD_COMMON
                    __field(__u64, instruction)
-                   __field(char, insn[8])
                    ),
 
            TP_fast_assign(
@@ -162,10 +164,8 @@ TRACE_EVENT(kvm_s390_intercept_instruction,
 
            VCPU_TP_PRINTK("intercepted instruction %016llx (%s)",
                           __entry->instruction,
-                          insn_to_mnemonic((unsigned char *)
-                                           &__entry->instruction,
-                                        __entry->insn, sizeof(__entry->insn)) ?
-                          "unknown" : __entry->insn)
+                          __print_symbolic(icpt_insn_decoder(__entry->instruction),
+                                           icpt_insn_codes))
        );
 
 /*
@@ -213,18 +213,6 @@ TRACE_EVENT(kvm_s390_intercept_validity,
  * Trace points for instructions that are of special interest.
  */
 
-#define sigp_order_codes                                       \
-       {SIGP_SENSE, "sense"},                                  \
-       {SIGP_EXTERNAL_CALL, "external call"},                  \
-       {SIGP_EMERGENCY_SIGNAL, "emergency signal"},            \
-       {SIGP_STOP, "stop"},                                    \
-       {SIGP_STOP_AND_STORE_STATUS, "stop and store status"},  \
-       {SIGP_SET_ARCHITECTURE, "set architecture"},            \
-       {SIGP_SET_PREFIX, "set prefix"},                        \
-       {SIGP_STORE_STATUS_AT_ADDRESS, "store status at addr"}, \
-       {SIGP_SENSE_RUNNING, "sense running"},                  \
-       {SIGP_RESTART, "restart"}
-
 TRACE_EVENT(kvm_s390_handle_sigp,
            TP_PROTO(VCPU_PROTO_COMMON, __u8 order_code, __u16 cpu_addr, \
                     __u32 parameter),
@@ -251,12 +239,28 @@ TRACE_EVENT(kvm_s390_handle_sigp,
                           __entry->cpu_addr, __entry->parameter)
        );
 
-#define diagnose_codes                         \
-       {0x10, "release pages"},                \
-       {0x44, "time slice end"},               \
-       {0x308, "ipl functions"},               \
-       {0x500, "kvm hypercall"},               \
-       {0x501, "kvm breakpoint"}
+TRACE_EVENT(kvm_s390_handle_sigp_pei,
+           TP_PROTO(VCPU_PROTO_COMMON, __u8 order_code, __u16 cpu_addr),
+           TP_ARGS(VCPU_ARGS_COMMON, order_code, cpu_addr),
+
+           TP_STRUCT__entry(
+                   VCPU_FIELD_COMMON
+                   __field(__u8, order_code)
+                   __field(__u16, cpu_addr)
+                   ),
+
+           TP_fast_assign(
+                   VCPU_ASSIGN_COMMON
+                   __entry->order_code = order_code;
+                   __entry->cpu_addr = cpu_addr;
+                   ),
+
+           VCPU_TP_PRINTK("handle sigp pei order %02x (%s), cpu address %04x",
+                          __entry->order_code,
+                          __print_symbolic(__entry->order_code,
+                                           sigp_order_codes),
+                          __entry->cpu_addr)
+       );
 
 TRACE_EVENT(kvm_s390_handle_diag,
            TP_PROTO(VCPU_PROTO_COMMON, __u16 code),
@@ -301,6 +305,31 @@ TRACE_EVENT(kvm_s390_handle_lctl,
                           __entry->reg1, __entry->reg3, __entry->addr)
        );
 
+TRACE_EVENT(kvm_s390_handle_stctl,
+           TP_PROTO(VCPU_PROTO_COMMON, int g, int reg1, int reg3, u64 addr),
+           TP_ARGS(VCPU_ARGS_COMMON, g, reg1, reg3, addr),
+
+           TP_STRUCT__entry(
+                   VCPU_FIELD_COMMON
+                   __field(int, g)
+                   __field(int, reg1)
+                   __field(int, reg3)
+                   __field(u64, addr)
+                   ),
+
+           TP_fast_assign(
+                   VCPU_ASSIGN_COMMON
+                   __entry->g = g;
+                   __entry->reg1 = reg1;
+                   __entry->reg3 = reg3;
+                   __entry->addr = addr;
+                   ),
+
+           VCPU_TP_PRINTK("%s: storing cr %x-%x to %016llx",
+                          __entry->g ? "stctg" : "stctl",
+                          __entry->reg1, __entry->reg3, __entry->addr)
+       );
+
 TRACE_EVENT(kvm_s390_handle_prefix,
            TP_PROTO(VCPU_PROTO_COMMON, int set, u32 address),
            TP_ARGS(VCPU_ARGS_COMMON, set, address),
index 7881d4e..37b8241 100644 (file)
@@ -834,6 +834,7 @@ void gmap_do_ipte_notify(struct mm_struct *mm, pte_t *pte)
        }
        spin_unlock(&gmap_notifier_lock);
 }
+EXPORT_SYMBOL_GPL(gmap_do_ipte_notify);
 
 static inline int page_table_with_pgste(struct page *page)
 {
@@ -866,8 +867,7 @@ static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,
        atomic_set(&page->_mapcount, 0);
        table = (unsigned long *) page_to_phys(page);
        clear_table(table, _PAGE_INVALID, PAGE_SIZE/2);
-       clear_table(table + PTRS_PER_PTE, PGSTE_HR_BIT | PGSTE_HC_BIT,
-                   PAGE_SIZE/2);
+       clear_table(table + PTRS_PER_PTE, 0, PAGE_SIZE/2);
        return table;
 }
 
@@ -885,8 +885,8 @@ static inline void page_table_free_pgste(unsigned long *table)
        __free_page(page);
 }
 
-static inline unsigned long page_table_reset_pte(struct mm_struct *mm,
-                       pmd_t *pmd, unsigned long addr, unsigned long end)
+static inline unsigned long page_table_reset_pte(struct mm_struct *mm, pmd_t *pmd,
+                       unsigned long addr, unsigned long end, bool init_skey)
 {
        pte_t *start_pte, *pte;
        spinlock_t *ptl;
@@ -897,6 +897,22 @@ static inline unsigned long page_table_reset_pte(struct mm_struct *mm,
        do {
                pgste = pgste_get_lock(pte);
                pgste_val(pgste) &= ~_PGSTE_GPS_USAGE_MASK;
+               if (init_skey) {
+                       unsigned long address;
+
+                       pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT |
+                                             PGSTE_GR_BIT | PGSTE_GC_BIT);
+
+                       /* skip invalid and not writable pages */
+                       if (pte_val(*pte) & _PAGE_INVALID ||
+                           !(pte_val(*pte) & _PAGE_WRITE)) {
+                               pgste_set_unlock(pte, pgste);
+                               continue;
+                       }
+
+                       address = pte_val(*pte) & PAGE_MASK;
+                       page_set_storage_key(address, PAGE_DEFAULT_KEY, 1);
+               }
                pgste_set_unlock(pte, pgste);
        } while (pte++, addr += PAGE_SIZE, addr != end);
        pte_unmap_unlock(start_pte, ptl);
@@ -904,8 +920,8 @@ static inline unsigned long page_table_reset_pte(struct mm_struct *mm,
        return addr;
 }
 
-static inline unsigned long page_table_reset_pmd(struct mm_struct *mm,
-                       pud_t *pud, unsigned long addr, unsigned long end)
+static inline unsigned long page_table_reset_pmd(struct mm_struct *mm, pud_t *pud,
+                       unsigned long addr, unsigned long end, bool init_skey)
 {
        unsigned long next;
        pmd_t *pmd;
@@ -915,14 +931,14 @@ static inline unsigned long page_table_reset_pmd(struct mm_struct *mm,
                next = pmd_addr_end(addr, end);
                if (pmd_none_or_clear_bad(pmd))
                        continue;
-               next = page_table_reset_pte(mm, pmd, addr, next);
+               next = page_table_reset_pte(mm, pmd, addr, next, init_skey);
        } while (pmd++, addr = next, addr != end);
 
        return addr;
 }
 
-static inline unsigned long page_table_reset_pud(struct mm_struct *mm,
-                       pgd_t *pgd, unsigned long addr, unsigned long end)
+static inline unsigned long page_table_reset_pud(struct mm_struct *mm, pgd_t *pgd,
+                       unsigned long addr, unsigned long end, bool init_skey)
 {
        unsigned long next;
        pud_t *pud;
@@ -932,28 +948,33 @@ static inline unsigned long page_table_reset_pud(struct mm_struct *mm,
                next = pud_addr_end(addr, end);
                if (pud_none_or_clear_bad(pud))
                        continue;
-               next = page_table_reset_pmd(mm, pud, addr, next);
+               next = page_table_reset_pmd(mm, pud, addr, next, init_skey);
        } while (pud++, addr = next, addr != end);
 
        return addr;
 }
 
-void page_table_reset_pgste(struct mm_struct *mm,
-                       unsigned long start, unsigned long end)
+void page_table_reset_pgste(struct mm_struct *mm, unsigned long start,
+                           unsigned long end, bool init_skey)
 {
        unsigned long addr, next;
        pgd_t *pgd;
 
+       down_write(&mm->mmap_sem);
+       if (init_skey && mm_use_skey(mm))
+               goto out_up;
        addr = start;
-       down_read(&mm->mmap_sem);
        pgd = pgd_offset(mm, addr);
        do {
                next = pgd_addr_end(addr, end);
                if (pgd_none_or_clear_bad(pgd))
                        continue;
-               next = page_table_reset_pud(mm, pgd, addr, next);
+               next = page_table_reset_pud(mm, pgd, addr, next, init_skey);
        } while (pgd++, addr = next, addr != end);
-       up_read(&mm->mmap_sem);
+       if (init_skey)
+               current->mm->context.use_skey = 1;
+out_up:
+       up_write(&mm->mmap_sem);
 }
 EXPORT_SYMBOL(page_table_reset_pgste);
 
@@ -991,7 +1012,7 @@ int set_guest_storage_key(struct mm_struct *mm, unsigned long addr,
        /* changing the guest storage key is considered a change of the page */
        if ((pgste_val(new) ^ pgste_val(old)) &
            (PGSTE_ACC_BITS | PGSTE_FP_BIT | PGSTE_GR_BIT | PGSTE_GC_BIT))
-               pgste_val(new) |= PGSTE_HC_BIT;
+               pgste_val(new) |= PGSTE_UC_BIT;
 
        pgste_set_unlock(ptep, new);
        pte_unmap_unlock(*ptep, ptl);
@@ -1013,6 +1034,11 @@ static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,
        return NULL;
 }
 
+void page_table_reset_pgste(struct mm_struct *mm, unsigned long start,
+                           unsigned long end, bool init_skey)
+{
+}
+
 static inline void page_table_free_pgste(unsigned long *table)
 {
 }
@@ -1359,6 +1385,37 @@ int s390_enable_sie(void)
 }
 EXPORT_SYMBOL_GPL(s390_enable_sie);
 
+/*
+ * Enable storage key handling from now on and initialize the storage
+ * keys with the default key.
+ */
+void s390_enable_skey(void)
+{
+       page_table_reset_pgste(current->mm, 0, TASK_SIZE, true);
+}
+EXPORT_SYMBOL_GPL(s390_enable_skey);
+
+/*
+ * Test and reset if a guest page is dirty
+ */
+bool gmap_test_and_clear_dirty(unsigned long address, struct gmap *gmap)
+{
+       pte_t *pte;
+       spinlock_t *ptl;
+       bool dirty = false;
+
+       pte = get_locked_pte(gmap->mm, address, &ptl);
+       if (unlikely(!pte))
+               return false;
+
+       if (ptep_test_and_clear_user_dirty(gmap->mm, address, pte))
+               dirty = true;
+
+       spin_unlock(ptl);
+       return dirty;
+}
+EXPORT_SYMBOL_GPL(gmap_test_and_clear_dirty);
+
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 int pmdp_clear_flush_young(struct vm_area_struct *vma, unsigned long address,
                           pmd_t *pmdp)
diff --git a/arch/x86/include/asm/acenv.h b/arch/x86/include/asm/acenv.h
new file mode 100644 (file)
index 0000000..6687329
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * X86 specific ACPICA environments and implementation
+ *
+ * Copyright (C) 2014, Intel Corporation
+ *   Author: Lv Zheng <lv.zheng@intel.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 _ASM_X86_ACENV_H
+#define _ASM_X86_ACENV_H
+
+#include <asm/special_insns.h>
+
+/* Asm macros */
+
+#define ACPI_FLUSH_CPU_CACHE() wbinvd()
+
+#ifdef CONFIG_ACPI
+
+int __acpi_acquire_global_lock(unsigned int *lock);
+int __acpi_release_global_lock(unsigned int *lock);
+
+#define ACPI_ACQUIRE_GLOBAL_LOCK(facs, Acq) \
+       ((Acq) = __acpi_acquire_global_lock(&facs->global_lock))
+
+#define ACPI_RELEASE_GLOBAL_LOCK(facs, Acq) \
+       ((Acq) = __acpi_release_global_lock(&facs->global_lock))
+
+/*
+ * Math helper asm macros
+ */
+#define ACPI_DIV_64_BY_32(n_hi, n_lo, d32, q32, r32) \
+       asm("divl %2;"                               \
+           : "=a"(q32), "=d"(r32)                   \
+           : "r"(d32),                              \
+            "0"(n_lo), "1"(n_hi))
+
+#define ACPI_SHIFT_RIGHT_64(n_hi, n_lo) \
+       asm("shrl   $1,%2       ;"      \
+           "rcrl   $1,%3;"             \
+           : "=r"(n_hi), "=r"(n_lo)    \
+           : "0"(n_hi), "1"(n_lo))
+
+#endif
+
+#endif /* _ASM_X86_ACENV_H */
index c8c1e70..e06225e 100644 (file)
 #include <asm/mpspec.h>
 #include <asm/realmode.h>
 
-#define COMPILER_DEPENDENT_INT64   long long
-#define COMPILER_DEPENDENT_UINT64  unsigned long long
-
-/*
- * Calling conventions:
- *
- * ACPI_SYSTEM_XFACE        - Interfaces to host OS (handlers, threads)
- * ACPI_EXTERNAL_XFACE      - External ACPI interfaces
- * ACPI_INTERNAL_XFACE      - Internal ACPI interfaces
- * ACPI_INTERNAL_VAR_XFACE  - Internal variable-parameter list interfaces
- */
-#define ACPI_SYSTEM_XFACE
-#define ACPI_EXTERNAL_XFACE
-#define ACPI_INTERNAL_XFACE
-#define ACPI_INTERNAL_VAR_XFACE
-
-/* Asm macros */
-
-#define ACPI_FLUSH_CPU_CACHE() wbinvd()
-
-int __acpi_acquire_global_lock(unsigned int *lock);
-int __acpi_release_global_lock(unsigned int *lock);
-
-#define ACPI_ACQUIRE_GLOBAL_LOCK(facs, Acq) \
-       ((Acq) = __acpi_acquire_global_lock(&facs->global_lock))
-
-#define ACPI_RELEASE_GLOBAL_LOCK(facs, Acq) \
-       ((Acq) = __acpi_release_global_lock(&facs->global_lock))
-
-/*
- * Math helper asm macros
- */
-#define ACPI_DIV_64_BY_32(n_hi, n_lo, d32, q32, r32) \
-       asm("divl %2;"                               \
-           : "=a"(q32), "=d"(r32)                   \
-           : "r"(d32),                              \
-            "0"(n_lo), "1"(n_hi))
-
-
-#define ACPI_SHIFT_RIGHT_64(n_hi, n_lo) \
-       asm("shrl   $1,%2       ;"      \
-           "rcrl   $1,%3;"             \
-           : "=r"(n_hi), "=r"(n_lo)    \
-           : "0"(n_hi), "1"(n_lo))
-
 #ifdef CONFIG_ACPI
 extern int acpi_lapic;
 extern int acpi_ioapic;
index 24ec121..a04fe4e 100644 (file)
@@ -189,7 +189,6 @@ struct x86_emulate_ops {
        void (*set_idt)(struct x86_emulate_ctxt *ctxt, struct desc_ptr *dt);
        ulong (*get_cr)(struct x86_emulate_ctxt *ctxt, int cr);
        int (*set_cr)(struct x86_emulate_ctxt *ctxt, int cr, ulong val);
-       void (*set_rflags)(struct x86_emulate_ctxt *ctxt, ulong val);
        int (*cpl)(struct x86_emulate_ctxt *ctxt);
        int (*get_dr)(struct x86_emulate_ctxt *ctxt, int dr, ulong *dest);
        int (*set_dr)(struct x86_emulate_ctxt *ctxt, int dr, ulong value);
index 7de069a..4931415 100644 (file)
                          | X86_CR0_ET | X86_CR0_NE | X86_CR0_WP | X86_CR0_AM \
                          | X86_CR0_NW | X86_CR0_CD | X86_CR0_PG))
 
-#define CR3_PAE_RESERVED_BITS ((X86_CR3_PWT | X86_CR3_PCD) - 1)
-#define CR3_NONPAE_RESERVED_BITS ((PAGE_SIZE-1) & ~(X86_CR3_PWT | X86_CR3_PCD))
-#define CR3_PCID_ENABLED_RESERVED_BITS 0xFFFFFF0000000000ULL
-#define CR3_L_MODE_RESERVED_BITS (CR3_NONPAE_RESERVED_BITS |   \
-                                 0xFFFFFF0000000000ULL)
+#define CR3_L_MODE_RESERVED_BITS 0xFFFFFF0000000000ULL
 #define CR4_RESERVED_BITS                                               \
        (~(unsigned long)(X86_CR4_VME | X86_CR4_PVI | X86_CR4_TSD | X86_CR4_DE\
                          | X86_CR4_PSE | X86_CR4_PAE | X86_CR4_MCE     \
@@ -134,7 +130,6 @@ enum kvm_reg_ex {
        VCPU_EXREG_PDPTR = NR_VCPU_REGS,
        VCPU_EXREG_CR3,
        VCPU_EXREG_RFLAGS,
-       VCPU_EXREG_CPL,
        VCPU_EXREG_SEGMENTS,
 };
 
index 58d66fe..8ba1884 100644 (file)
@@ -74,6 +74,11 @@ dotraplinkage void do_general_protection(struct pt_regs *, long);
 dotraplinkage void do_page_fault(struct pt_regs *, unsigned long);
 #ifdef CONFIG_TRACING
 dotraplinkage void trace_do_page_fault(struct pt_regs *, unsigned long);
+#else
+static inline void trace_do_page_fault(struct pt_regs *regs, unsigned long error)
+{
+       do_page_fault(regs, error);
+}
 #endif
 dotraplinkage void do_spurious_interrupt_bug(struct pt_regs *, long);
 dotraplinkage void do_coprocessor_error(struct pt_regs *, long);
index d35078e..7db54b5 100644 (file)
@@ -206,23 +206,21 @@ static void __init dtb_apic_setup(void)
 static void __init x86_flattree_get_config(void)
 {
        u32 size, map_len;
-       struct boot_param_header *dt;
+       void *dt;
 
        if (!initial_dtb)
                return;
 
-       map_len = max(PAGE_SIZE - (initial_dtb & ~PAGE_MASK),
-                       (u64)sizeof(struct boot_param_header));
+       map_len = max(PAGE_SIZE - (initial_dtb & ~PAGE_MASK), (u64)128);
 
-       dt = early_memremap(initial_dtb, map_len);
-       size = be32_to_cpu(dt->totalsize);
+       initial_boot_params = dt = early_memremap(initial_dtb, map_len);
+       size = of_get_flat_dt_size();
        if (map_len < size) {
                early_iounmap(dt, map_len);
-               dt = early_memremap(initial_dtb, size);
+               initial_boot_params = dt = early_memremap(initial_dtb, size);
                map_len = size;
        }
 
-       initial_boot_params = dt;
        unflatten_and_copy_device_tree();
        early_iounmap(dt, map_len);
 }
index 0331cb3..7e97371 100644 (file)
@@ -259,7 +259,7 @@ do_async_page_fault(struct pt_regs *regs, unsigned long error_code)
 
        switch (kvm_read_and_reset_pf_reason()) {
        default:
-               do_page_fault(regs, error_code);
+               trace_do_page_fault(regs, error_code);
                break;
        case KVM_PV_REASON_PAGE_NOT_PRESENT:
                /* page is swapped out by the host. */
index f47a104..38a0afe 100644 (file)
@@ -283,6 +283,8 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
                0 /* Reserved */ | f_lm | F(3DNOWEXT) | F(3DNOW);
        /* cpuid 1.ecx */
        const u32 kvm_supported_word4_x86_features =
+               /* NOTE: MONITOR (and MWAIT) are emulated as NOP,
+                * but *not* advertised to guests via CPUID ! */
                F(XMM3) | F(PCLMULQDQ) | 0 /* DTES64, MONITOR */ |
                0 /* DS-CPL, VMX, SMX, EST */ |
                0 /* TM2 */ | F(SSSE3) | 0 /* CNXT-ID */ | 0 /* Reserved */ |
@@ -495,6 +497,13 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
                entry->ecx &= kvm_supported_word6_x86_features;
                cpuid_mask(&entry->ecx, 6);
                break;
+       case 0x80000007: /* Advanced power management */
+               /* invariant TSC is CPUID.80000007H:EDX[8] */
+               entry->edx &= (1 << 8);
+               /* mask against host */
+               entry->edx &= boot_cpu_data.x86_power;
+               entry->eax = entry->ebx = entry->ecx = 0;
+               break;
        case 0x80000008: {
                unsigned g_phys_as = (entry->eax >> 16) & 0xff;
                unsigned virt_as = max((entry->eax >> 8) & 0xff, 48U);
@@ -525,7 +534,6 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
        case 3: /* Processor serial number */
        case 5: /* MONITOR/MWAIT */
        case 6: /* Thermal management */
-       case 0x80000007: /* Advanced power management */
        case 0xC0000002:
        case 0xC0000003:
        case 0xC0000004:
@@ -726,6 +734,7 @@ int cpuid_maxphyaddr(struct kvm_vcpu *vcpu)
 not_found:
        return 36;
 }
+EXPORT_SYMBOL_GPL(cpuid_maxphyaddr);
 
 /*
  * If no match is found, check whether we exceed the vCPU's limit
index eeecbed..f908731 100644 (file)
@@ -88,4 +88,11 @@ static inline bool guest_cpuid_has_x2apic(struct kvm_vcpu *vcpu)
        return best && (best->ecx & bit(X86_FEATURE_X2APIC));
 }
 
+static inline bool guest_cpuid_has_gbpages(struct kvm_vcpu *vcpu)
+{
+       struct kvm_cpuid_entry2 *best;
+
+       best = kvm_find_cpuid_entry(vcpu, 0x80000001, 0);
+       return best && (best->edx & bit(X86_FEATURE_GBPAGES));
+}
 #endif
index 205b17e..e4e833d 100644 (file)
 #define Fastop      ((u64)1 << 44)  /* Use opcode::u.fastop */
 #define NoWrite     ((u64)1 << 45)  /* No writeback */
 #define SrcWrite    ((u64)1 << 46)  /* Write back src operand */
+#define NoMod      ((u64)1 << 47)  /* Mod field is ignored */
 
 #define DstXacc     (DstAccLo | SrcAccHi | SrcWrite)
 
@@ -1077,7 +1078,7 @@ static int decode_modrm(struct x86_emulate_ctxt *ctxt,
        ctxt->modrm_rm |= (ctxt->modrm & 0x07);
        ctxt->modrm_seg = VCPU_SREG_DS;
 
-       if (ctxt->modrm_mod == 3) {
+       if (ctxt->modrm_mod == 3 || (ctxt->d & NoMod)) {
                op->type = OP_REG;
                op->bytes = (ctxt->d & ByteOp) ? 1 : ctxt->op_bytes;
                op->addr.reg = decode_register(ctxt, ctxt->modrm_rm,
@@ -1324,7 +1325,8 @@ static int pio_in_emulated(struct x86_emulate_ctxt *ctxt,
                rc->end = n * size;
        }
 
-       if (ctxt->rep_prefix && !(ctxt->eflags & EFLG_DF)) {
+       if (ctxt->rep_prefix && (ctxt->d & String) &&
+           !(ctxt->eflags & EFLG_DF)) {
                ctxt->dst.data = rc->data + rc->pos;
                ctxt->dst.type = OP_MEM_STR;
                ctxt->dst.count = (rc->end - rc->pos) / size;
@@ -1409,11 +1411,11 @@ static int write_segment_descriptor(struct x86_emulate_ctxt *ctxt,
 }
 
 /* Does not support long mode */
-static int load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
-                                  u16 selector, int seg)
+static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
+                                    u16 selector, int seg, u8 cpl, bool in_task_switch)
 {
        struct desc_struct seg_desc, old_desc;
-       u8 dpl, rpl, cpl;
+       u8 dpl, rpl;
        unsigned err_vec = GP_VECTOR;
        u32 err_code = 0;
        bool null_selector = !(selector & ~0x3); /* 0000-0003 are null */
@@ -1441,7 +1443,6 @@ static int load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
        }
 
        rpl = selector & 3;
-       cpl = ctxt->ops->cpl(ctxt);
 
        /* NULL selector is not valid for TR, CS and SS (except for long mode) */
        if ((seg == VCPU_SREG_CS
@@ -1486,6 +1487,9 @@ static int load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
                        goto exception;
                break;
        case VCPU_SREG_CS:
+               if (in_task_switch && rpl != dpl)
+                       goto exception;
+
                if (!(seg_desc.type & 8))
                        goto exception;
 
@@ -1543,6 +1547,13 @@ exception:
        return X86EMUL_PROPAGATE_FAULT;
 }
 
+static int load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
+                                  u16 selector, int seg)
+{
+       u8 cpl = ctxt->ops->cpl(ctxt);
+       return __load_segment_descriptor(ctxt, selector, seg, cpl, false);
+}
+
 static void write_register_operand(struct operand *op)
 {
        /* The 4-byte case *is* correct: in 64-bit mode we zero-extend. */
@@ -2404,6 +2415,7 @@ static int load_state_from_tss16(struct x86_emulate_ctxt *ctxt,
                                 struct tss_segment_16 *tss)
 {
        int ret;
+       u8 cpl;
 
        ctxt->_eip = tss->ip;
        ctxt->eflags = tss->flag | 2;
@@ -2426,23 +2438,25 @@ static int load_state_from_tss16(struct x86_emulate_ctxt *ctxt,
        set_segment_selector(ctxt, tss->ss, VCPU_SREG_SS);
        set_segment_selector(ctxt, tss->ds, VCPU_SREG_DS);
 
+       cpl = tss->cs & 3;
+
        /*
         * Now load segment descriptors. If fault happens at this stage
         * it is handled in a context of new task
         */
-       ret = load_segment_descriptor(ctxt, tss->ldt, VCPU_SREG_LDTR);
+       ret = __load_segment_descriptor(ctxt, tss->ldt, VCPU_SREG_LDTR, cpl, true);
        if (ret != X86EMUL_CONTINUE)
                return ret;
-       ret = load_segment_descriptor(ctxt, tss->es, VCPU_SREG_ES);
+       ret = __load_segment_descriptor(ctxt, tss->es, VCPU_SREG_ES, cpl, true);
        if (ret != X86EMUL_CONTINUE)
                return ret;
-       ret = load_segment_descriptor(ctxt, tss->cs, VCPU_SREG_CS);
+       ret = __load_segment_descriptor(ctxt, tss->cs, VCPU_SREG_CS, cpl, true);
        if (ret != X86EMUL_CONTINUE)
                return ret;
-       ret = load_segment_descriptor(ctxt, tss->ss, VCPU_SREG_SS);
+       ret = __load_segment_descriptor(ctxt, tss->ss, VCPU_SREG_SS, cpl, true);
        if (ret != X86EMUL_CONTINUE)
                return ret;
-       ret = load_segment_descriptor(ctxt, tss->ds, VCPU_SREG_DS);
+       ret = __load_segment_descriptor(ctxt, tss->ds, VCPU_SREG_DS, cpl, true);
        if (ret != X86EMUL_CONTINUE)
                return ret;
 
@@ -2496,7 +2510,7 @@ static int task_switch_16(struct x86_emulate_ctxt *ctxt,
 static void save_state_to_tss32(struct x86_emulate_ctxt *ctxt,
                                struct tss_segment_32 *tss)
 {
-       tss->cr3 = ctxt->ops->get_cr(ctxt, 3);
+       /* CR3 and ldt selector are not saved intentionally */
        tss->eip = ctxt->_eip;
        tss->eflags = ctxt->eflags;
        tss->eax = reg_read(ctxt, VCPU_REGS_RAX);
@@ -2514,13 +2528,13 @@ static void save_state_to_tss32(struct x86_emulate_ctxt *ctxt,
        tss->ds = get_segment_selector(ctxt, VCPU_SREG_DS);
        tss->fs = get_segment_selector(ctxt, VCPU_SREG_FS);
        tss->gs = get_segment_selector(ctxt, VCPU_SREG_GS);
-       tss->ldt_selector = get_segment_selector(ctxt, VCPU_SREG_LDTR);
 }
 
 static int load_state_from_tss32(struct x86_emulate_ctxt *ctxt,
                                 struct tss_segment_32 *tss)
 {
        int ret;
+       u8 cpl;
 
        if (ctxt->ops->set_cr(ctxt, 3, tss->cr3))
                return emulate_gp(ctxt, 0);
@@ -2539,7 +2553,8 @@ static int load_state_from_tss32(struct x86_emulate_ctxt *ctxt,
 
        /*
         * SDM says that segment selectors are loaded before segment
-        * descriptors
+        * descriptors.  This is important because CPL checks will
+        * use CS.RPL.
         */
        set_segment_selector(ctxt, tss->ldt_selector, VCPU_SREG_LDTR);
        set_segment_selector(ctxt, tss->es, VCPU_SREG_ES);
@@ -2553,43 +2568,38 @@ static int load_state_from_tss32(struct x86_emulate_ctxt *ctxt,
         * If we're switching between Protected Mode and VM86, we need to make
         * sure to update the mode before loading the segment descriptors so
         * that the selectors are interpreted correctly.
-        *
-        * Need to get rflags to the vcpu struct immediately because it
-        * influences the CPL which is checked at least when loading the segment
-        * descriptors and when pushing an error code to the new kernel stack.
-        *
-        * TODO Introduce a separate ctxt->ops->set_cpl callback
         */
-       if (ctxt->eflags & X86_EFLAGS_VM)
+       if (ctxt->eflags & X86_EFLAGS_VM) {
                ctxt->mode = X86EMUL_MODE_VM86;
-       else
+               cpl = 3;
+       } else {
                ctxt->mode = X86EMUL_MODE_PROT32;
-
-       ctxt->ops->set_rflags(ctxt, ctxt->eflags);
+               cpl = tss->cs & 3;
+       }
 
        /*
         * Now load segment descriptors. If fault happenes at this stage
         * it is handled in a context of new task
         */
-       ret = load_segment_descriptor(ctxt, tss->ldt_selector, VCPU_SREG_LDTR);
+       ret = __load_segment_descriptor(ctxt, tss->ldt_selector, VCPU_SREG_LDTR, cpl, true);
        if (ret != X86EMUL_CONTINUE)
                return ret;
-       ret = load_segment_descriptor(ctxt, tss->es, VCPU_SREG_ES);
+       ret = __load_segment_descriptor(ctxt, tss->es, VCPU_SREG_ES, cpl, true);
        if (ret != X86EMUL_CONTINUE)
                return ret;
-       ret = load_segment_descriptor(ctxt, tss->cs, VCPU_SREG_CS);
+       ret = __load_segment_descriptor(ctxt, tss->cs, VCPU_SREG_CS, cpl, true);
        if (ret != X86EMUL_CONTINUE)
                return ret;
-       ret = load_segment_descriptor(ctxt, tss->ss, VCPU_SREG_SS);
+       ret = __load_segment_descriptor(ctxt, tss->ss, VCPU_SREG_SS, cpl, true);
        if (ret != X86EMUL_CONTINUE)
                return ret;
-       ret = load_segment_descriptor(ctxt, tss->ds, VCPU_SREG_DS);
+       ret = __load_segment_descriptor(ctxt, tss->ds, VCPU_SREG_DS, cpl, true);
        if (ret != X86EMUL_CONTINUE)
                return ret;
-       ret = load_segment_descriptor(ctxt, tss->fs, VCPU_SREG_FS);
+       ret = __load_segment_descriptor(ctxt, tss->fs, VCPU_SREG_FS, cpl, true);
        if (ret != X86EMUL_CONTINUE)
                return ret;
-       ret = load_segment_descriptor(ctxt, tss->gs, VCPU_SREG_GS);
+       ret = __load_segment_descriptor(ctxt, tss->gs, VCPU_SREG_GS, cpl, true);
        if (ret != X86EMUL_CONTINUE)
                return ret;
 
@@ -2604,6 +2614,8 @@ static int task_switch_32(struct x86_emulate_ctxt *ctxt,
        struct tss_segment_32 tss_seg;
        int ret;
        u32 new_tss_base = get_desc_base(new_desc);
+       u32 eip_offset = offsetof(struct tss_segment_32, eip);
+       u32 ldt_sel_offset = offsetof(struct tss_segment_32, ldt_selector);
 
        ret = ops->read_std(ctxt, old_tss_base, &tss_seg, sizeof tss_seg,
                            &ctxt->exception);
@@ -2613,8 +2625,9 @@ static int task_switch_32(struct x86_emulate_ctxt *ctxt,
 
        save_state_to_tss32(ctxt, &tss_seg);
 
-       ret = ops->write_std(ctxt, old_tss_base, &tss_seg, sizeof tss_seg,
-                            &ctxt->exception);
+       /* Only GP registers and segment selectors are saved */
+       ret = ops->write_std(ctxt, old_tss_base + eip_offset, &tss_seg.eip,
+                            ldt_sel_offset - eip_offset, &ctxt->exception);
        if (ret != X86EMUL_CONTINUE)
                /* FIXME: need to provide precise fault address */
                return ret;
@@ -3386,10 +3399,6 @@ static int check_cr_write(struct x86_emulate_ctxt *ctxt)
                ctxt->ops->get_msr(ctxt, MSR_EFER, &efer);
                if (efer & EFER_LMA)
                        rsvd = CR3_L_MODE_RESERVED_BITS;
-               else if (ctxt->ops->get_cr(ctxt, 4) & X86_CR4_PAE)
-                       rsvd = CR3_PAE_RESERVED_BITS;
-               else if (ctxt->ops->get_cr(ctxt, 0) & X86_CR0_PG)
-                       rsvd = CR3_NONPAE_RESERVED_BITS;
 
                if (new_val & rsvd)
                        return emulate_gp(ctxt, 0);
@@ -3869,10 +3878,12 @@ static const struct opcode twobyte_table[256] = {
        N, N, N, N, N, N, N, N,
        D(ImplicitOps | ModRM), N, N, N, N, N, N, D(ImplicitOps | ModRM),
        /* 0x20 - 0x2F */
-       DIP(ModRM | DstMem | Priv | Op3264, cr_read, check_cr_read),
-       DIP(ModRM | DstMem | Priv | Op3264, dr_read, check_dr_read),
-       IIP(ModRM | SrcMem | Priv | Op3264, em_cr_write, cr_write, check_cr_write),
-       IIP(ModRM | SrcMem | Priv | Op3264, em_dr_write, dr_write, check_dr_write),
+       DIP(ModRM | DstMem | Priv | Op3264 | NoMod, cr_read, check_cr_read),
+       DIP(ModRM | DstMem | Priv | Op3264 | NoMod, dr_read, check_dr_read),
+       IIP(ModRM | SrcMem | Priv | Op3264 | NoMod, em_cr_write, cr_write,
+                                               check_cr_write),
+       IIP(ModRM | SrcMem | Priv | Op3264 | NoMod, em_dr_write, dr_write,
+                                               check_dr_write),
        N, N, N, N,
        GP(ModRM | DstReg | SrcMem | Mov | Sse, &pfx_0f_28_0f_29),
        GP(ModRM | DstMem | SrcReg | Mov | Sse, &pfx_0f_28_0f_29),
index 484bc87..bd0da43 100644 (file)
@@ -113,6 +113,7 @@ int kvm_cpu_get_interrupt(struct kvm_vcpu *v)
 
        return kvm_get_apic_interrupt(v);       /* APIC */
 }
+EXPORT_SYMBOL_GPL(kvm_cpu_get_interrupt);
 
 void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu)
 {
index 9736529..0069118 100644 (file)
@@ -360,6 +360,8 @@ static inline void apic_clear_irr(int vec, struct kvm_lapic *apic)
 
 static inline void apic_set_isr(int vec, struct kvm_lapic *apic)
 {
+       /* Note that we never get here with APIC virtualization enabled.  */
+
        if (!__apic_test_and_set_vector(vec, apic->regs + APIC_ISR))
                ++apic->isr_count;
        BUG_ON(apic->isr_count > MAX_APIC_VECTOR);
@@ -371,12 +373,48 @@ static inline void apic_set_isr(int vec, struct kvm_lapic *apic)
        apic->highest_isr_cache = vec;
 }
 
+static inline int apic_find_highest_isr(struct kvm_lapic *apic)
+{
+       int result;
+
+       /*
+        * Note that isr_count is always 1, and highest_isr_cache
+        * is always -1, with APIC virtualization enabled.
+        */
+       if (!apic->isr_count)
+               return -1;
+       if (likely(apic->highest_isr_cache != -1))
+               return apic->highest_isr_cache;
+
+       result = find_highest_vector(apic->regs + APIC_ISR);
+       ASSERT(result == -1 || result >= 16);
+
+       return result;
+}
+
 static inline void apic_clear_isr(int vec, struct kvm_lapic *apic)
 {
-       if (__apic_test_and_clear_vector(vec, apic->regs + APIC_ISR))
+       struct kvm_vcpu *vcpu;
+       if (!__apic_test_and_clear_vector(vec, apic->regs + APIC_ISR))
+               return;
+
+       vcpu = apic->vcpu;
+
+       /*
+        * We do get here for APIC virtualization enabled if the guest
+        * uses the Hyper-V APIC enlightenment.  In this case we may need
+        * to trigger a new interrupt delivery by writing the SVI field;
+        * on the other hand isr_count and highest_isr_cache are unused
+        * and must be left alone.
+        */
+       if (unlikely(kvm_apic_vid_enabled(vcpu->kvm)))
+               kvm_x86_ops->hwapic_isr_update(vcpu->kvm,
+                                              apic_find_highest_isr(apic));
+       else {
                --apic->isr_count;
-       BUG_ON(apic->isr_count < 0);
-       apic->highest_isr_cache = -1;
+               BUG_ON(apic->isr_count < 0);
+               apic->highest_isr_cache = -1;
+       }
 }
 
 int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu)
@@ -456,22 +494,6 @@ static void pv_eoi_clr_pending(struct kvm_vcpu *vcpu)
        __clear_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention);
 }
 
-static inline int apic_find_highest_isr(struct kvm_lapic *apic)
-{
-       int result;
-
-       /* Note that isr_count is always 1 with vid enabled */
-       if (!apic->isr_count)
-               return -1;
-       if (likely(apic->highest_isr_cache != -1))
-               return apic->highest_isr_cache;
-
-       result = find_highest_vector(apic->regs + APIC_ISR);
-       ASSERT(result == -1 || result >= 16);
-
-       return result;
-}
-
 void kvm_apic_update_tmr(struct kvm_vcpu *vcpu, u32 *tmr)
 {
        struct kvm_lapic *apic = vcpu->arch.apic;
@@ -1605,6 +1627,8 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu)
        int vector = kvm_apic_has_interrupt(vcpu);
        struct kvm_lapic *apic = vcpu->arch.apic;
 
+       /* Note that we never get here with APIC virtualization enabled.  */
+
        if (vector == -1)
                return -1;
 
index 813d310..9314678 100644 (file)
@@ -22,6 +22,7 @@
 #include "mmu.h"
 #include "x86.h"
 #include "kvm_cache_regs.h"
+#include "cpuid.h"
 
 #include <linux/kvm_host.h>
 #include <linux/types.h>
@@ -595,7 +596,8 @@ static bool mmu_spte_update(u64 *sptep, u64 new_spte)
         * we always atomicly update it, see the comments in
         * spte_has_volatile_bits().
         */
-       if (is_writable_pte(old_spte) && !is_writable_pte(new_spte))
+       if (spte_is_locklessly_modifiable(old_spte) &&
+             !is_writable_pte(new_spte))
                ret = true;
 
        if (!shadow_accessed_mask)
@@ -1176,8 +1178,7 @@ static void drop_large_spte(struct kvm_vcpu *vcpu, u64 *sptep)
 
 /*
  * Write-protect on the specified @sptep, @pt_protect indicates whether
- * spte writ-protection is caused by protecting shadow page table.
- * @flush indicates whether tlb need be flushed.
+ * spte write-protection is caused by protecting shadow page table.
  *
  * Note: write protection is difference between drity logging and spte
  * protection:
@@ -1186,10 +1187,9 @@ static void drop_large_spte(struct kvm_vcpu *vcpu, u64 *sptep)
  * - for spte protection, the spte can be writable only after unsync-ing
  *   shadow page.
  *
- * Return true if the spte is dropped.
+ * Return true if tlb need be flushed.
  */
-static bool
-spte_write_protect(struct kvm *kvm, u64 *sptep, bool *flush, bool pt_protect)
+static bool spte_write_protect(struct kvm *kvm, u64 *sptep, bool pt_protect)
 {
        u64 spte = *sptep;
 
@@ -1199,17 +1199,11 @@ spte_write_protect(struct kvm *kvm, u64 *sptep, bool *flush, bool pt_protect)
 
        rmap_printk("rmap_write_protect: spte %p %llx\n", sptep, *sptep);
 
-       if (__drop_large_spte(kvm, sptep)) {
-               *flush |= true;
-               return true;
-       }
-
        if (pt_protect)
                spte &= ~SPTE_MMU_WRITEABLE;
        spte = spte & ~PT_WRITABLE_MASK;
 
-       *flush |= mmu_spte_update(sptep, spte);
-       return false;
+       return mmu_spte_update(sptep, spte);
 }
 
 static bool __rmap_write_protect(struct kvm *kvm, unsigned long *rmapp,
@@ -1221,11 +1215,8 @@ static bool __rmap_write_protect(struct kvm *kvm, unsigned long *rmapp,
 
        for (sptep = rmap_get_first(*rmapp, &iter); sptep;) {
                BUG_ON(!(*sptep & PT_PRESENT_MASK));
-               if (spte_write_protect(kvm, sptep, &flush, pt_protect)) {
-                       sptep = rmap_get_first(*rmapp, &iter);
-                       continue;
-               }
 
+               flush |= spte_write_protect(kvm, sptep, pt_protect);
                sptep = rmap_get_next(&iter);
        }
 
@@ -2802,9 +2793,9 @@ static bool page_fault_can_be_fast(u32 error_code)
 }
 
 static bool
-fast_pf_fix_direct_spte(struct kvm_vcpu *vcpu, u64 *sptep, u64 spte)
+fast_pf_fix_direct_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
+                       u64 *sptep, u64 spte)
 {
-       struct kvm_mmu_page *sp = page_header(__pa(sptep));
        gfn_t gfn;
 
        WARN_ON(!sp->role.direct);
@@ -2830,6 +2821,7 @@ static bool fast_page_fault(struct kvm_vcpu *vcpu, gva_t gva, int level,
                            u32 error_code)
 {
        struct kvm_shadow_walk_iterator iterator;
+       struct kvm_mmu_page *sp;
        bool ret = false;
        u64 spte = 0ull;
 
@@ -2853,7 +2845,8 @@ static bool fast_page_fault(struct kvm_vcpu *vcpu, gva_t gva, int level,
                goto exit;
        }
 
-       if (!is_last_spte(spte, level))
+       sp = page_header(__pa(iterator.sptep));
+       if (!is_last_spte(spte, sp->role.level))
                goto exit;
 
        /*
@@ -2874,12 +2867,25 @@ static bool fast_page_fault(struct kvm_vcpu *vcpu, gva_t gva, int level,
        if (!spte_is_locklessly_modifiable(spte))
                goto exit;
 
+       /*
+        * Do not fix write-permission on the large spte since we only dirty
+        * the first page into the dirty-bitmap in fast_pf_fix_direct_spte()
+        * that means other pages are missed if its slot is dirty-logged.
+        *
+        * Instead, we let the slow page fault path create a normal spte to
+        * fix the access.
+        *
+        * See the comments in kvm_arch_commit_memory_region().
+        */
+       if (sp->role.level > PT_PAGE_TABLE_LEVEL)
+               goto exit;
+
        /*
         * Currently, fast page fault only works for direct mapping since
         * the gfn is not stable for indirect shadow page.
         * See Documentation/virtual/kvm/locking.txt to get more detail.
         */
-       ret = fast_pf_fix_direct_spte(vcpu, iterator.sptep, spte);
+       ret = fast_pf_fix_direct_spte(vcpu, sp, iterator.sptep, spte);
 exit:
        trace_fast_page_fault(vcpu, gva, error_code, iterator.sptep,
                              spte, ret);
@@ -3511,11 +3517,14 @@ static void reset_rsvds_bits_mask(struct kvm_vcpu *vcpu,
 {
        int maxphyaddr = cpuid_maxphyaddr(vcpu);
        u64 exb_bit_rsvd = 0;
+       u64 gbpages_bit_rsvd = 0;
 
        context->bad_mt_xwr = 0;
 
        if (!context->nx)
                exb_bit_rsvd = rsvd_bits(63, 63);
+       if (!guest_cpuid_has_gbpages(vcpu))
+               gbpages_bit_rsvd = rsvd_bits(7, 7);
        switch (context->root_level) {
        case PT32_ROOT_LEVEL:
                /* no rsvd bits for 2 level 4K page table entries */
@@ -3538,7 +3547,7 @@ static void reset_rsvds_bits_mask(struct kvm_vcpu *vcpu,
        case PT32E_ROOT_LEVEL:
                context->rsvd_bits_mask[0][2] =
                        rsvd_bits(maxphyaddr, 63) |
-                       rsvd_bits(7, 8) | rsvd_bits(1, 2);      /* PDPTE */
+                       rsvd_bits(5, 8) | rsvd_bits(1, 2);      /* PDPTE */
                context->rsvd_bits_mask[0][1] = exb_bit_rsvd |
                        rsvd_bits(maxphyaddr, 62);      /* PDE */
                context->rsvd_bits_mask[0][0] = exb_bit_rsvd |
@@ -3550,16 +3559,16 @@ static void reset_rsvds_bits_mask(struct kvm_vcpu *vcpu,
                break;
        case PT64_ROOT_LEVEL:
                context->rsvd_bits_mask[0][3] = exb_bit_rsvd |
-                       rsvd_bits(maxphyaddr, 51) | rsvd_bits(7, 8);
+                       rsvd_bits(maxphyaddr, 51) | rsvd_bits(7, 7);
                context->rsvd_bits_mask[0][2] = exb_bit_rsvd |
-                       rsvd_bits(maxphyaddr, 51) | rsvd_bits(7, 8);
+                       gbpages_bit_rsvd | rsvd_bits(maxphyaddr, 51);
                context->rsvd_bits_mask[0][1] = exb_bit_rsvd |
                        rsvd_bits(maxphyaddr, 51);
                context->rsvd_bits_mask[0][0] = exb_bit_rsvd |
                        rsvd_bits(maxphyaddr, 51);
                context->rsvd_bits_mask[1][3] = context->rsvd_bits_mask[0][3];
                context->rsvd_bits_mask[1][2] = exb_bit_rsvd |
-                       rsvd_bits(maxphyaddr, 51) |
+                       gbpages_bit_rsvd | rsvd_bits(maxphyaddr, 51) |
                        rsvd_bits(13, 29);
                context->rsvd_bits_mask[1][1] = exb_bit_rsvd |
                        rsvd_bits(maxphyaddr, 51) |
@@ -4304,15 +4313,32 @@ void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot)
                        if (*rmapp)
                                __rmap_write_protect(kvm, rmapp, false);
 
-                       if (need_resched() || spin_needbreak(&kvm->mmu_lock)) {
-                               kvm_flush_remote_tlbs(kvm);
+                       if (need_resched() || spin_needbreak(&kvm->mmu_lock))
                                cond_resched_lock(&kvm->mmu_lock);
-                       }
                }
        }
 
-       kvm_flush_remote_tlbs(kvm);
        spin_unlock(&kvm->mmu_lock);
+
+       /*
+        * kvm_mmu_slot_remove_write_access() and kvm_vm_ioctl_get_dirty_log()
+        * which do tlb flush out of mmu-lock should be serialized by
+        * kvm->slots_lock otherwise tlb flush would be missed.
+        */
+       lockdep_assert_held(&kvm->slots_lock);
+
+       /*
+        * We can flush all the TLBs out of the mmu lock without TLB
+        * corruption since we just change the spte from writable to
+        * readonly so that we only need to care the case of changing
+        * spte from present to present (changing the spte from present
+        * to nonpresent will flush all the TLBs immediately), in other
+        * words, the only case we care is mmu_spte_update() where we
+        * haved checked SPTE_HOST_WRITEABLE | SPTE_MMU_WRITEABLE
+        * instead of PT_WRITABLE_MASK, that means it does not depend
+        * on PT_WRITABLE_MASK anymore.
+        */
+       kvm_flush_remote_tlbs(kvm);
 }
 
 #define BATCH_ZAP_PAGES        10
index 3842e70..b982112 100644 (file)
@@ -104,6 +104,39 @@ static inline int is_present_gpte(unsigned long pte)
        return pte & PT_PRESENT_MASK;
 }
 
+/*
+ * Currently, we have two sorts of write-protection, a) the first one
+ * write-protects guest page to sync the guest modification, b) another one is
+ * used to sync dirty bitmap when we do KVM_GET_DIRTY_LOG. The differences
+ * between these two sorts are:
+ * 1) the first case clears SPTE_MMU_WRITEABLE bit.
+ * 2) the first case requires flushing tlb immediately avoiding corrupting
+ *    shadow page table between all vcpus so it should be in the protection of
+ *    mmu-lock. And the another case does not need to flush tlb until returning
+ *    the dirty bitmap to userspace since it only write-protects the page
+ *    logged in the bitmap, that means the page in the dirty bitmap is not
+ *    missed, so it can flush tlb out of mmu-lock.
+ *
+ * So, there is the problem: the first case can meet the corrupted tlb caused
+ * by another case which write-protects pages but without flush tlb
+ * immediately. In order to making the first case be aware this problem we let
+ * it flush tlb if we try to write-protect a spte whose SPTE_MMU_WRITEABLE bit
+ * is set, it works since another case never touches SPTE_MMU_WRITEABLE bit.
+ *
+ * Anyway, whenever a spte is updated (only permission and status bits are
+ * changed) we need to check whether the spte with SPTE_MMU_WRITEABLE becomes
+ * readonly, if that happens, we need to flush tlb. Fortunately,
+ * mmu_spte_update() has already handled it perfectly.
+ *
+ * The rules to use SPTE_MMU_WRITEABLE and PT_WRITABLE_MASK:
+ * - if we want to see if it has writable tlb entry or if the spte can be
+ *   writable on the mmu mapping, check SPTE_MMU_WRITEABLE, this is the most
+ *   case, otherwise
+ * - if we fix page fault on the spte or do write-protection by dirty logging,
+ *   check PT_WRITABLE_MASK.
+ *
+ * TODO: introduce APIs to split these two cases.
+ */
 static inline int is_writable_pte(unsigned long pte)
 {
        return pte & PT_WRITABLE_MASK;
index 123efd3..4107765 100644 (file)
@@ -913,8 +913,7 @@ static gpa_t FNAME(gva_to_gpa_nested)(struct kvm_vcpu *vcpu, gva_t vaddr,
  *   and kvm_mmu_notifier_invalidate_range_start detect the mapping page isn't
  *   used by guest then tlbs are not flushed, so guest is allowed to access the
  *   freed pages.
- *   We set tlbs_dirty to let the notifier know this change and delay the flush
- *   until such a case actually happens.
+ *   And we increase kvm->tlbs_dirty to delay tlbs flush in this case.
  */
 static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
 {
@@ -943,7 +942,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
                        return -EINVAL;
 
                if (FNAME(prefetch_invalid_gpte)(vcpu, sp, &sp->spt[i], gpte)) {
-                       vcpu->kvm->tlbs_dirty = true;
+                       vcpu->kvm->tlbs_dirty++;
                        continue;
                }
 
@@ -958,7 +957,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
 
                if (gfn != sp->gfns[i]) {
                        drop_spte(vcpu->kvm, &sp->spt[i]);
-                       vcpu->kvm->tlbs_dirty = true;
+                       vcpu->kvm->tlbs_dirty++;
                        continue;
                }
 
index 5c4f631..cbecaa9 100644 (file)
@@ -108,7 +108,10 @@ static void kvm_perf_overflow(struct perf_event *perf_event,
 {
        struct kvm_pmc *pmc = perf_event->overflow_handler_context;
        struct kvm_pmu *pmu = &pmc->vcpu->arch.pmu;
-       __set_bit(pmc->idx, (unsigned long *)&pmu->global_status);
+       if (!test_and_set_bit(pmc->idx, (unsigned long *)&pmu->reprogram_pmi)) {
+               __set_bit(pmc->idx, (unsigned long *)&pmu->global_status);
+               kvm_make_request(KVM_REQ_PMU, pmc->vcpu);
+       }
 }
 
 static void kvm_perf_overflow_intr(struct perf_event *perf_event,
@@ -117,7 +120,7 @@ static void kvm_perf_overflow_intr(struct perf_event *perf_event,
        struct kvm_pmc *pmc = perf_event->overflow_handler_context;
        struct kvm_pmu *pmu = &pmc->vcpu->arch.pmu;
        if (!test_and_set_bit(pmc->idx, (unsigned long *)&pmu->reprogram_pmi)) {
-               kvm_perf_overflow(perf_event, data, regs);
+               __set_bit(pmc->idx, (unsigned long *)&pmu->global_status);
                kvm_make_request(KVM_REQ_PMU, pmc->vcpu);
                /*
                 * Inject PMI. If vcpu was in a guest mode during NMI PMI
index 7f4f9c2..ec8366c 100644 (file)
@@ -1338,21 +1338,6 @@ static void svm_vcpu_put(struct kvm_vcpu *vcpu)
                wrmsrl(host_save_user_msrs[i], svm->host_user_msrs[i]);
 }
 
-static void svm_update_cpl(struct kvm_vcpu *vcpu)
-{
-       struct vcpu_svm *svm = to_svm(vcpu);
-       int cpl;
-
-       if (!is_protmode(vcpu))
-               cpl = 0;
-       else if (svm->vmcb->save.rflags & X86_EFLAGS_VM)
-               cpl = 3;
-       else
-               cpl = svm->vmcb->save.cs.selector & 0x3;
-
-       svm->vmcb->save.cpl = cpl;
-}
-
 static unsigned long svm_get_rflags(struct kvm_vcpu *vcpu)
 {
        return to_svm(vcpu)->vmcb->save.rflags;
@@ -1360,11 +1345,12 @@ static unsigned long svm_get_rflags(struct kvm_vcpu *vcpu)
 
 static void svm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags)
 {
-       unsigned long old_rflags = to_svm(vcpu)->vmcb->save.rflags;
-
+       /*
+        * Any change of EFLAGS.VM is accompained by a reload of SS
+        * (caused by either a task switch or an inter-privilege IRET),
+        * so we do not need to update the CPL here.
+        */
        to_svm(vcpu)->vmcb->save.rflags = rflags;
-       if ((old_rflags ^ rflags) & X86_EFLAGS_VM)
-               svm_update_cpl(vcpu);
 }
 
 static void svm_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg)
@@ -1631,8 +1617,15 @@ static void svm_set_segment(struct kvm_vcpu *vcpu,
                s->attrib |= (var->db & 1) << SVM_SELECTOR_DB_SHIFT;
                s->attrib |= (var->g & 1) << SVM_SELECTOR_G_SHIFT;
        }
-       if (seg == VCPU_SREG_CS)
-               svm_update_cpl(vcpu);
+
+       /*
+        * This is always accurate, except if SYSRET returned to a segment
+        * with SS.DPL != 3.  Intel does not have this quirk, and always
+        * forces SS.DPL to 3 on sysret, so we ignore that case; fixing it
+        * would entail passing the CPL to userspace and back.
+        */
+       if (seg == VCPU_SREG_SS)
+               svm->vmcb->save.cpl = (s->attrib >> SVM_SELECTOR_DPL_SHIFT) & 3;
 
        mark_dirty(svm->vmcb, VMCB_SEG);
 }
@@ -2770,12 +2763,6 @@ static int xsetbv_interception(struct vcpu_svm *svm)
        return 1;
 }
 
-static int invalid_op_interception(struct vcpu_svm *svm)
-{
-       kvm_queue_exception(&svm->vcpu, UD_VECTOR);
-       return 1;
-}
-
 static int task_switch_interception(struct vcpu_svm *svm)
 {
        u16 tss_selector;
@@ -3287,6 +3274,24 @@ static int pause_interception(struct vcpu_svm *svm)
        return 1;
 }
 
+static int nop_interception(struct vcpu_svm *svm)
+{
+       skip_emulated_instruction(&(svm->vcpu));
+       return 1;
+}
+
+static int monitor_interception(struct vcpu_svm *svm)
+{
+       printk_once(KERN_WARNING "kvm: MONITOR instruction emulated as NOP!\n");
+       return nop_interception(svm);
+}
+
+static int mwait_interception(struct vcpu_svm *svm)
+{
+       printk_once(KERN_WARNING "kvm: MWAIT instruction emulated as NOP!\n");
+       return nop_interception(svm);
+}
+
 static int (*const svm_exit_handlers[])(struct vcpu_svm *svm) = {
        [SVM_EXIT_READ_CR0]                     = cr_interception,
        [SVM_EXIT_READ_CR3]                     = cr_interception,
@@ -3344,8 +3349,8 @@ static int (*const svm_exit_handlers[])(struct vcpu_svm *svm) = {
        [SVM_EXIT_CLGI]                         = clgi_interception,
        [SVM_EXIT_SKINIT]                       = skinit_interception,
        [SVM_EXIT_WBINVD]                       = emulate_on_interception,
-       [SVM_EXIT_MONITOR]                      = invalid_op_interception,
-       [SVM_EXIT_MWAIT]                        = invalid_op_interception,
+       [SVM_EXIT_MONITOR]                      = monitor_interception,
+       [SVM_EXIT_MWAIT]                        = mwait_interception,
        [SVM_EXIT_XSETBV]                       = xsetbv_interception,
        [SVM_EXIT_NPF]                          = pf_interception,
 };
index 545245d..33574c9 100644 (file)
@@ -91,16 +91,21 @@ TRACE_EVENT(kvm_hv_hypercall,
 /*
  * Tracepoint for PIO.
  */
+
+#define KVM_PIO_IN   0
+#define KVM_PIO_OUT  1
+
 TRACE_EVENT(kvm_pio,
        TP_PROTO(unsigned int rw, unsigned int port, unsigned int size,
-                unsigned int count),
-       TP_ARGS(rw, port, size, count),
+                unsigned int count, void *data),
+       TP_ARGS(rw, port, size, count, data),
 
        TP_STRUCT__entry(
                __field(        unsigned int,   rw              )
                __field(        unsigned int,   port            )
                __field(        unsigned int,   size            )
                __field(        unsigned int,   count           )
+               __field(        unsigned int,   val             )
        ),
 
        TP_fast_assign(
@@ -108,11 +113,18 @@ TRACE_EVENT(kvm_pio,
                __entry->port           = port;
                __entry->size           = size;
                __entry->count          = count;
+               if (size == 1)
+                       __entry->val    = *(unsigned char *)data;
+               else if (size == 2)
+                       __entry->val    = *(unsigned short *)data;
+               else
+                       __entry->val    = *(unsigned int *)data;
        ),
 
-       TP_printk("pio_%s at 0x%x size %d count %d",
+       TP_printk("pio_%s at 0x%x size %d count %d val 0x%x %s",
                  __entry->rw ? "write" : "read",
-                 __entry->port, __entry->size, __entry->count)
+                 __entry->port, __entry->size, __entry->count, __entry->val,
+                 __entry->count > 1 ? "(...)" : "")
 );
 
 /*
index 138ceff..801332e 100644 (file)
@@ -354,6 +354,7 @@ struct vmcs02_list {
 struct nested_vmx {
        /* Has the level1 guest done vmxon? */
        bool vmxon;
+       gpa_t vmxon_ptr;
 
        /* The guest-physical address of the current VMCS L1 keeps for L2 */
        gpa_t current_vmptr;
@@ -413,7 +414,6 @@ struct vcpu_vmx {
        struct kvm_vcpu       vcpu;
        unsigned long         host_rsp;
        u8                    fail;
-       u8                    cpl;
        bool                  nmi_known_unmasked;
        u32                   exit_intr_info;
        u32                   idt_vectoring_info;
@@ -2283,7 +2283,7 @@ static __init void nested_vmx_setup_ctls_msrs(void)
        rdmsr(MSR_IA32_VMX_EXIT_CTLS,
                nested_vmx_exit_ctls_low, nested_vmx_exit_ctls_high);
        nested_vmx_exit_ctls_low = VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR;
-       /* Note that guest use of VM_EXIT_ACK_INTR_ON_EXIT is not supported. */
+
        nested_vmx_exit_ctls_high &=
 #ifdef CONFIG_X86_64
                VM_EXIT_HOST_ADDR_SPACE_SIZE |
@@ -2291,7 +2291,8 @@ static __init void nested_vmx_setup_ctls_msrs(void)
                VM_EXIT_LOAD_IA32_PAT | VM_EXIT_SAVE_IA32_PAT;
        nested_vmx_exit_ctls_high |= VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR |
                VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER |
-               VM_EXIT_SAVE_VMX_PREEMPTION_TIMER;
+               VM_EXIT_SAVE_VMX_PREEMPTION_TIMER | VM_EXIT_ACK_INTR_ON_EXIT;
+
        if (vmx_mpx_supported())
                nested_vmx_exit_ctls_high |= VM_EXIT_CLEAR_BNDCFGS;
 
@@ -2353,12 +2354,11 @@ static __init void nested_vmx_setup_ctls_msrs(void)
                         VMX_EPT_INVEPT_BIT;
                nested_vmx_ept_caps &= vmx_capability.ept;
                /*
-                * Since invept is completely emulated we support both global
-                * and context invalidation independent of what host cpu
-                * supports
+                * For nested guests, we don't do anything specific
+                * for single context invalidation. Hence, only advertise
+                * support for global context invalidation.
                 */
-               nested_vmx_ept_caps |= VMX_EPT_EXTENT_GLOBAL_BIT |
-                       VMX_EPT_EXTENT_CONTEXT_BIT;
+               nested_vmx_ept_caps |= VMX_EPT_EXTENT_GLOBAL_BIT;
        } else
                nested_vmx_ept_caps = 0;
 
@@ -3186,10 +3186,6 @@ static void enter_pmode(struct kvm_vcpu *vcpu)
        fix_pmode_seg(vcpu, VCPU_SREG_DS, &vmx->rmode.segs[VCPU_SREG_DS]);
        fix_pmode_seg(vcpu, VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]);
        fix_pmode_seg(vcpu, VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]);
-
-       /* CPL is always 0 when CPU enters protected mode */
-       __set_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail);
-       vmx->cpl = 0;
 }
 
 static void fix_rmode_seg(int seg, struct kvm_segment *save)
@@ -3591,22 +3587,14 @@ static int vmx_get_cpl(struct kvm_vcpu *vcpu)
 {
        struct vcpu_vmx *vmx = to_vmx(vcpu);
 
-       if (!is_protmode(vcpu))
+       if (unlikely(vmx->rmode.vm86_active))
                return 0;
-
-       if (!is_long_mode(vcpu)
-           && (kvm_get_rflags(vcpu) & X86_EFLAGS_VM)) /* if virtual 8086 */
-               return 3;
-
-       if (!test_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail)) {
-               __set_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail);
-               vmx->cpl = vmx_read_guest_seg_selector(vmx, VCPU_SREG_CS) & 3;
+       else {
+               int ar = vmx_read_guest_seg_ar(vmx, VCPU_SREG_SS);
+               return AR_DPL(ar);
        }
-
-       return vmx->cpl;
 }
 
-
 static u32 vmx_segment_access_rights(struct kvm_segment *var)
 {
        u32 ar;
@@ -3634,8 +3622,6 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu,
        const struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
 
        vmx_segment_cache_clear(vmx);
-       if (seg == VCPU_SREG_CS)
-               __clear_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail);
 
        if (vmx->rmode.vm86_active && seg != VCPU_SREG_LDTR) {
                vmx->rmode.segs[seg] = *var;
@@ -4564,6 +4550,16 @@ static bool nested_exit_on_intr(struct kvm_vcpu *vcpu)
                PIN_BASED_EXT_INTR_MASK;
 }
 
+/*
+ * In nested virtualization, check if L1 has set
+ * VM_EXIT_ACK_INTR_ON_EXIT
+ */
+static bool nested_exit_intr_ack_set(struct kvm_vcpu *vcpu)
+{
+       return get_vmcs12(vcpu)->vm_exit_controls &
+               VM_EXIT_ACK_INTR_ON_EXIT;
+}
+
 static bool nested_exit_on_nmi(struct kvm_vcpu *vcpu)
 {
        return get_vmcs12(vcpu)->pin_based_vm_exec_control &
@@ -4878,6 +4874,9 @@ static int handle_exception(struct kvm_vcpu *vcpu)
                      (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))) {
                        vcpu->arch.dr6 &= ~15;
                        vcpu->arch.dr6 |= dr6;
+                       if (!(dr6 & ~DR6_RESERVED)) /* icebp */
+                               skip_emulated_instruction(vcpu);
+
                        kvm_queue_exception(vcpu, DB_VECTOR);
                        return 1;
                }
@@ -5166,7 +5165,7 @@ static int handle_dr(struct kvm_vcpu *vcpu)
                        return 1;
                kvm_register_write(vcpu, reg, val);
        } else
-               if (kvm_set_dr(vcpu, dr, vcpu->arch.regs[reg]))
+               if (kvm_set_dr(vcpu, dr, kvm_register_read(vcpu, reg)))
                        return 1;
 
        skip_emulated_instruction(vcpu);
@@ -5439,7 +5438,7 @@ static int handle_task_switch(struct kvm_vcpu *vcpu)
        }
 
        /* clear all local breakpoint enable flags */
-       vmcs_writel(GUEST_DR7, vmcs_readl(GUEST_DR7) & ~55);
+       vmcs_writel(GUEST_DR7, vmcs_readl(GUEST_DR7) & ~0x55);
 
        /*
         * TODO: What about debug traps on tss switch?
@@ -5565,6 +5564,10 @@ static int handle_ept_misconfig(struct kvm_vcpu *vcpu)
        gpa_t gpa;
 
        gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS);
+       if (!kvm_io_bus_write(vcpu->kvm, KVM_FAST_MMIO_BUS, gpa, 0, NULL)) {
+               skip_emulated_instruction(vcpu);
+               return 1;
+       }
 
        ret = handle_mmio_page_fault_common(vcpu, gpa, true);
        if (likely(ret == RET_MMIO_PF_EMULATE))
@@ -5669,12 +5672,24 @@ static int handle_pause(struct kvm_vcpu *vcpu)
        return 1;
 }
 
-static int handle_invalid_op(struct kvm_vcpu *vcpu)
+static int handle_nop(struct kvm_vcpu *vcpu)
 {
-       kvm_queue_exception(vcpu, UD_VECTOR);
+       skip_emulated_instruction(vcpu);
        return 1;
 }
 
+static int handle_mwait(struct kvm_vcpu *vcpu)
+{
+       printk_once(KERN_WARNING "kvm: MWAIT instruction emulated as NOP!\n");
+       return handle_nop(vcpu);
+}
+
+static int handle_monitor(struct kvm_vcpu *vcpu)
+{
+       printk_once(KERN_WARNING "kvm: MONITOR instruction emulated as NOP!\n");
+       return handle_nop(vcpu);
+}
+
 /*
  * To run an L2 guest, we need a vmcs02 based on the L1-specified vmcs12.
  * We could reuse a single VMCS for all the L2 guests, but we also want the
@@ -5811,6 +5826,154 @@ static enum hrtimer_restart vmx_preemption_timer_fn(struct hrtimer *timer)
        return HRTIMER_NORESTART;
 }
 
+/*
+ * Decode the memory-address operand of a vmx instruction, as recorded on an
+ * exit caused by such an instruction (run by a guest hypervisor).
+ * On success, returns 0. When the operand is invalid, returns 1 and throws
+ * #UD or #GP.
+ */
+static int get_vmx_mem_address(struct kvm_vcpu *vcpu,
+                                unsigned long exit_qualification,
+                                u32 vmx_instruction_info, gva_t *ret)
+{
+       /*
+        * According to Vol. 3B, "Information for VM Exits Due to Instruction
+        * Execution", on an exit, vmx_instruction_info holds most of the
+        * addressing components of the operand. Only the displacement part
+        * is put in exit_qualification (see 3B, "Basic VM-Exit Information").
+        * For how an actual address is calculated from all these components,
+        * refer to Vol. 1, "Operand Addressing".
+        */
+       int  scaling = vmx_instruction_info & 3;
+       int  addr_size = (vmx_instruction_info >> 7) & 7;
+       bool is_reg = vmx_instruction_info & (1u << 10);
+       int  seg_reg = (vmx_instruction_info >> 15) & 7;
+       int  index_reg = (vmx_instruction_info >> 18) & 0xf;
+       bool index_is_valid = !(vmx_instruction_info & (1u << 22));
+       int  base_reg       = (vmx_instruction_info >> 23) & 0xf;
+       bool base_is_valid  = !(vmx_instruction_info & (1u << 27));
+
+       if (is_reg) {
+               kvm_queue_exception(vcpu, UD_VECTOR);
+               return 1;
+       }
+
+       /* Addr = segment_base + offset */
+       /* offset = base + [index * scale] + displacement */
+       *ret = vmx_get_segment_base(vcpu, seg_reg);
+       if (base_is_valid)
+               *ret += kvm_register_read(vcpu, base_reg);
+       if (index_is_valid)
+               *ret += kvm_register_read(vcpu, index_reg)<<scaling;
+       *ret += exit_qualification; /* holds the displacement */
+
+       if (addr_size == 1) /* 32 bit */
+               *ret &= 0xffffffff;
+
+       /*
+        * TODO: throw #GP (and return 1) in various cases that the VM*
+        * instructions require it - e.g., offset beyond segment limit,
+        * unusable or unreadable/unwritable segment, non-canonical 64-bit
+        * address, and so on. Currently these are not checked.
+        */
+       return 0;
+}
+
+/*
+ * This function performs the various checks including
+ * - if it's 4KB aligned
+ * - No bits beyond the physical address width are set
+ * - Returns 0 on success or else 1
+ * (Intel SDM Section 30.3)
+ */
+static int nested_vmx_check_vmptr(struct kvm_vcpu *vcpu, int exit_reason,
+                                 gpa_t *vmpointer)
+{
+       gva_t gva;
+       gpa_t vmptr;
+       struct x86_exception e;
+       struct page *page;
+       struct vcpu_vmx *vmx = to_vmx(vcpu);
+       int maxphyaddr = cpuid_maxphyaddr(vcpu);
+
+       if (get_vmx_mem_address(vcpu, vmcs_readl(EXIT_QUALIFICATION),
+                       vmcs_read32(VMX_INSTRUCTION_INFO), &gva))
+               return 1;
+
+       if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &vmptr,
+                               sizeof(vmptr), &e)) {
+               kvm_inject_page_fault(vcpu, &e);
+               return 1;
+       }
+
+       switch (exit_reason) {
+       case EXIT_REASON_VMON:
+               /*
+                * SDM 3: 24.11.5
+                * The first 4 bytes of VMXON region contain the supported
+                * VMCS revision identifier
+                *
+                * Note - IA32_VMX_BASIC[48] will never be 1
+                * for the nested case;
+                * which replaces physical address width with 32
+                *
+                */
+               if (!IS_ALIGNED(vmptr, PAGE_SIZE) || (vmptr >> maxphyaddr)) {
+                       nested_vmx_failInvalid(vcpu);
+                       skip_emulated_instruction(vcpu);
+                       return 1;
+               }
+
+               page = nested_get_page(vcpu, vmptr);
+               if (page == NULL ||
+                   *(u32 *)kmap(page) != VMCS12_REVISION) {
+                       nested_vmx_failInvalid(vcpu);
+                       kunmap(page);
+                       skip_emulated_instruction(vcpu);
+                       return 1;
+               }
+               kunmap(page);
+               vmx->nested.vmxon_ptr = vmptr;
+               break;
+       case EXIT_REASON_VMCLEAR:
+               if (!IS_ALIGNED(vmptr, PAGE_SIZE) || (vmptr >> maxphyaddr)) {
+                       nested_vmx_failValid(vcpu,
+                                            VMXERR_VMCLEAR_INVALID_ADDRESS);
+                       skip_emulated_instruction(vcpu);
+                       return 1;
+               }
+
+               if (vmptr == vmx->nested.vmxon_ptr) {
+                       nested_vmx_failValid(vcpu,
+                                            VMXERR_VMCLEAR_VMXON_POINTER);
+                       skip_emulated_instruction(vcpu);
+                       return 1;
+               }
+               break;
+       case EXIT_REASON_VMPTRLD:
+               if (!IS_ALIGNED(vmptr, PAGE_SIZE) || (vmptr >> maxphyaddr)) {
+                       nested_vmx_failValid(vcpu,
+                                            VMXERR_VMPTRLD_INVALID_ADDRESS);
+                       skip_emulated_instruction(vcpu);
+                       return 1;
+               }
+
+               if (vmptr == vmx->nested.vmxon_ptr) {
+                       nested_vmx_failValid(vcpu,
+                                            VMXERR_VMCLEAR_VMXON_POINTER);
+                       skip_emulated_instruction(vcpu);
+                       return 1;
+               }
+               break;
+       default:
+               return 1; /* shouldn't happen */
+       }
+
+       if (vmpointer)
+               *vmpointer = vmptr;
+       return 0;
+}
+
 /*
  * Emulate the VMXON instruction.
  * Currently, we just remember that VMX is active, and do not save or even
@@ -5849,6 +6012,10 @@ static int handle_vmon(struct kvm_vcpu *vcpu)
                kvm_inject_gp(vcpu, 0);
                return 1;
        }
+
+       if (nested_vmx_check_vmptr(vcpu, EXIT_REASON_VMON, NULL))
+               return 1;
+
        if (vmx->nested.vmxon) {
                nested_vmx_failValid(vcpu, VMXERR_VMXON_IN_VMX_ROOT_OPERATION);
                skip_emulated_instruction(vcpu);
@@ -5971,87 +6138,19 @@ static int handle_vmoff(struct kvm_vcpu *vcpu)
        return 1;
 }
 
-/*
- * Decode the memory-address operand of a vmx instruction, as recorded on an
- * exit caused by such an instruction (run by a guest hypervisor).
- * On success, returns 0. When the operand is invalid, returns 1 and throws
- * #UD or #GP.
- */
-static int get_vmx_mem_address(struct kvm_vcpu *vcpu,
-                                unsigned long exit_qualification,
-                                u32 vmx_instruction_info, gva_t *ret)
-{
-       /*
-        * According to Vol. 3B, "Information for VM Exits Due to Instruction
-        * Execution", on an exit, vmx_instruction_info holds most of the
-        * addressing components of the operand. Only the displacement part
-        * is put in exit_qualification (see 3B, "Basic VM-Exit Information").
-        * For how an actual address is calculated from all these components,
-        * refer to Vol. 1, "Operand Addressing".
-        */
-       int  scaling = vmx_instruction_info & 3;
-       int  addr_size = (vmx_instruction_info >> 7) & 7;
-       bool is_reg = vmx_instruction_info & (1u << 10);
-       int  seg_reg = (vmx_instruction_info >> 15) & 7;
-       int  index_reg = (vmx_instruction_info >> 18) & 0xf;
-       bool index_is_valid = !(vmx_instruction_info & (1u << 22));
-       int  base_reg       = (vmx_instruction_info >> 23) & 0xf;
-       bool base_is_valid  = !(vmx_instruction_info & (1u << 27));
-
-       if (is_reg) {
-               kvm_queue_exception(vcpu, UD_VECTOR);
-               return 1;
-       }
-
-       /* Addr = segment_base + offset */
-       /* offset = base + [index * scale] + displacement */
-       *ret = vmx_get_segment_base(vcpu, seg_reg);
-       if (base_is_valid)
-               *ret += kvm_register_read(vcpu, base_reg);
-       if (index_is_valid)
-               *ret += kvm_register_read(vcpu, index_reg)<<scaling;
-       *ret += exit_qualification; /* holds the displacement */
-
-       if (addr_size == 1) /* 32 bit */
-               *ret &= 0xffffffff;
-
-       /*
-        * TODO: throw #GP (and return 1) in various cases that the VM*
-        * instructions require it - e.g., offset beyond segment limit,
-        * unusable or unreadable/unwritable segment, non-canonical 64-bit
-        * address, and so on. Currently these are not checked.
-        */
-       return 0;
-}
-
 /* Emulate the VMCLEAR instruction */
 static int handle_vmclear(struct kvm_vcpu *vcpu)
 {
        struct vcpu_vmx *vmx = to_vmx(vcpu);
-       gva_t gva;
        gpa_t vmptr;
        struct vmcs12 *vmcs12;
        struct page *page;
-       struct x86_exception e;
 
        if (!nested_vmx_check_permission(vcpu))
                return 1;
 
-       if (get_vmx_mem_address(vcpu, vmcs_readl(EXIT_QUALIFICATION),
-                       vmcs_read32(VMX_INSTRUCTION_INFO), &gva))
-               return 1;
-
-       if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &vmptr,
-                               sizeof(vmptr), &e)) {
-               kvm_inject_page_fault(vcpu, &e);
-               return 1;
-       }
-
-       if (!IS_ALIGNED(vmptr, PAGE_SIZE)) {
-               nested_vmx_failValid(vcpu, VMXERR_VMCLEAR_INVALID_ADDRESS);
-               skip_emulated_instruction(vcpu);
+       if (nested_vmx_check_vmptr(vcpu, EXIT_REASON_VMCLEAR, &vmptr))
                return 1;
-       }
 
        if (vmptr == vmx->nested.current_vmptr) {
                nested_release_vmcs12(vmx);
@@ -6372,29 +6471,14 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu)
 static int handle_vmptrld(struct kvm_vcpu *vcpu)
 {
        struct vcpu_vmx *vmx = to_vmx(vcpu);
-       gva_t gva;
        gpa_t vmptr;
-       struct x86_exception e;
        u32 exec_control;
 
        if (!nested_vmx_check_permission(vcpu))
                return 1;
 
-       if (get_vmx_mem_address(vcpu, vmcs_readl(EXIT_QUALIFICATION),
-                       vmcs_read32(VMX_INSTRUCTION_INFO), &gva))
-               return 1;
-
-       if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &vmptr,
-                               sizeof(vmptr), &e)) {
-               kvm_inject_page_fault(vcpu, &e);
-               return 1;
-       }
-
-       if (!IS_ALIGNED(vmptr, PAGE_SIZE)) {
-               nested_vmx_failValid(vcpu, VMXERR_VMPTRLD_INVALID_ADDRESS);
-               skip_emulated_instruction(vcpu);
+       if (nested_vmx_check_vmptr(vcpu, EXIT_REASON_VMPTRLD, &vmptr))
                return 1;
-       }
 
        if (vmx->nested.current_vmptr != vmptr) {
                struct vmcs12 *new_vmcs12;
@@ -6471,7 +6555,6 @@ static int handle_invept(struct kvm_vcpu *vcpu)
        struct {
                u64 eptp, gpa;
        } operand;
-       u64 eptp_mask = ((1ull << 51) - 1) & PAGE_MASK;
 
        if (!(nested_vmx_secondary_ctls_high & SECONDARY_EXEC_ENABLE_EPT) ||
            !(nested_vmx_ept_caps & VMX_EPT_INVEPT_BIT)) {
@@ -6511,16 +6594,13 @@ static int handle_invept(struct kvm_vcpu *vcpu)
        }
 
        switch (type) {
-       case VMX_EPT_EXTENT_CONTEXT:
-               if ((operand.eptp & eptp_mask) !=
-                               (nested_ept_get_cr3(vcpu) & eptp_mask))
-                       break;
        case VMX_EPT_EXTENT_GLOBAL:
                kvm_mmu_sync_roots(vcpu);
                kvm_mmu_flush_tlb(vcpu);
                nested_vmx_succeed(vcpu);
                break;
        default:
+               /* Trap single context invalidation invept calls */
                BUG_ON(1);
                break;
        }
@@ -6571,8 +6651,8 @@ static int (*const kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = {
        [EXIT_REASON_EPT_VIOLATION]           = handle_ept_violation,
        [EXIT_REASON_EPT_MISCONFIG]           = handle_ept_misconfig,
        [EXIT_REASON_PAUSE_INSTRUCTION]       = handle_pause,
-       [EXIT_REASON_MWAIT_INSTRUCTION]       = handle_invalid_op,
-       [EXIT_REASON_MONITOR_INSTRUCTION]     = handle_invalid_op,
+       [EXIT_REASON_MWAIT_INSTRUCTION]       = handle_mwait,
+       [EXIT_REASON_MONITOR_INSTRUCTION]     = handle_monitor,
        [EXIT_REASON_INVEPT]                  = handle_invept,
 };
 
@@ -7413,7 +7493,6 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
 
        vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP)
                                  | (1 << VCPU_EXREG_RFLAGS)
-                                 | (1 << VCPU_EXREG_CPL)
                                  | (1 << VCPU_EXREG_PDPTR)
                                  | (1 << VCPU_EXREG_SEGMENTS)
                                  | (1 << VCPU_EXREG_CR3));
@@ -8601,6 +8680,14 @@ static void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason,
        prepare_vmcs12(vcpu, vmcs12, exit_reason, exit_intr_info,
                       exit_qualification);
 
+       if ((exit_reason == EXIT_REASON_EXTERNAL_INTERRUPT)
+           && nested_exit_intr_ack_set(vcpu)) {
+               int irq = kvm_cpu_get_interrupt(vcpu);
+               WARN_ON(irq < 0);
+               vmcs12->vm_exit_intr_info = irq |
+                       INTR_INFO_VALID_MASK | INTR_TYPE_EXT_INTR;
+       }
+
        trace_kvm_nested_vmexit_inject(vmcs12->vm_exit_reason,
                                       vmcs12->exit_qualification,
                                       vmcs12->idt_vectoring_info_field,
index 20316c6..f32a025 100644 (file)
@@ -704,25 +704,11 @@ int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
        }
 
        if (is_long_mode(vcpu)) {
-               if (kvm_read_cr4_bits(vcpu, X86_CR4_PCIDE)) {
-                       if (cr3 & CR3_PCID_ENABLED_RESERVED_BITS)
-                               return 1;
-               } else
-                       if (cr3 & CR3_L_MODE_RESERVED_BITS)
-                               return 1;
-       } else {
-               if (is_pae(vcpu)) {
-                       if (cr3 & CR3_PAE_RESERVED_BITS)
-                               return 1;
-                       if (is_paging(vcpu) &&
-                           !load_pdptrs(vcpu, vcpu->arch.walk_mmu, cr3))
-                               return 1;
-               }
-               /*
-                * We don't check reserved bits in nonpae mode, because
-                * this isn't enforced, and VMware depends on this.
-                */
-       }
+               if (cr3 & CR3_L_MODE_RESERVED_BITS)
+                       return 1;
+       } else if (is_pae(vcpu) && is_paging(vcpu) &&
+                  !load_pdptrs(vcpu, vcpu->arch.walk_mmu, cr3))
+               return 1;
 
        vcpu->arch.cr3 = cr3;
        __set_bit(VCPU_EXREG_CR3, (ulong *)&vcpu->arch.regs_avail);
@@ -1935,6 +1921,8 @@ static int set_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 data)
 
                if (!(data & HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE)) {
                        vcpu->arch.hv_vapic = data;
+                       if (kvm_lapic_enable_pv_eoi(vcpu, 0))
+                               return 1;
                        break;
                }
                gfn = data >> HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT;
@@ -1945,6 +1933,8 @@ static int set_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 data)
                        return 1;
                vcpu->arch.hv_vapic = data;
                mark_page_dirty(vcpu->kvm, gfn);
+               if (kvm_lapic_enable_pv_eoi(vcpu, gfn_to_gpa(gfn) | KVM_MSR_ENABLED))
+                       return 1;
                break;
        }
        case HV_X64_MSR_EOI:
@@ -2647,6 +2637,7 @@ int kvm_dev_ioctl_check_extension(long ext)
        case KVM_CAP_IRQ_INJECT_STATUS:
        case KVM_CAP_IRQFD:
        case KVM_CAP_IOEVENTFD:
+       case KVM_CAP_IOEVENTFD_NO_LENGTH:
        case KVM_CAP_PIT2:
        case KVM_CAP_PIT_STATE2:
        case KVM_CAP_SET_IDENTITY_MAP_ADDR:
@@ -3649,11 +3640,19 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
                offset = i * BITS_PER_LONG;
                kvm_mmu_write_protect_pt_masked(kvm, memslot, offset, mask);
        }
-       if (is_dirty)
-               kvm_flush_remote_tlbs(kvm);
 
        spin_unlock(&kvm->mmu_lock);
 
+       /* See the comments in kvm_mmu_slot_remove_write_access(). */
+       lockdep_assert_held(&kvm->slots_lock);
+
+       /*
+        * All the TLBs can be flushed out of mmu lock, see the comments in
+        * kvm_mmu_slot_remove_write_access().
+        */
+       if (is_dirty)
+               kvm_flush_remote_tlbs(kvm);
+
        r = -EFAULT;
        if (copy_to_user(log->dirty_bitmap, dirty_bitmap_buffer, n))
                goto out;
@@ -4489,8 +4488,6 @@ static int emulator_pio_in_out(struct kvm_vcpu *vcpu, int size,
                               unsigned short port, void *val,
                               unsigned int count, bool in)
 {
-       trace_kvm_pio(!in, port, size, count);
-
        vcpu->arch.pio.port = port;
        vcpu->arch.pio.in = in;
        vcpu->arch.pio.count  = count;
@@ -4525,6 +4522,7 @@ static int emulator_pio_in_emulated(struct x86_emulate_ctxt *ctxt,
        if (ret) {
 data_avail:
                memcpy(val, vcpu->arch.pio_data, size * count);
+               trace_kvm_pio(KVM_PIO_IN, port, size, count, vcpu->arch.pio_data);
                vcpu->arch.pio.count = 0;
                return 1;
        }
@@ -4539,6 +4537,7 @@ static int emulator_pio_out_emulated(struct x86_emulate_ctxt *ctxt,
        struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt);
 
        memcpy(vcpu->arch.pio_data, val, size * count);
+       trace_kvm_pio(KVM_PIO_OUT, port, size, count, vcpu->arch.pio_data);
        return emulator_pio_in_out(vcpu, size, port, (void *)val, count, false);
 }
 
@@ -4650,11 +4649,6 @@ static int emulator_set_cr(struct x86_emulate_ctxt *ctxt, int cr, ulong val)
        return res;
 }
 
-static void emulator_set_rflags(struct x86_emulate_ctxt *ctxt, ulong val)
-{
-       kvm_set_rflags(emul_to_vcpu(ctxt), val);
-}
-
 static int emulator_get_cpl(struct x86_emulate_ctxt *ctxt)
 {
        return kvm_x86_ops->get_cpl(emul_to_vcpu(ctxt));
@@ -4839,7 +4833,6 @@ static const struct x86_emulate_ops emulate_ops = {
        .set_idt             = emulator_set_idt,
        .get_cr              = emulator_get_cr,
        .set_cr              = emulator_set_cr,
-       .set_rflags          = emulator_set_rflags,
        .cpl                 = emulator_get_cpl,
        .get_dr              = emulator_get_dr,
        .set_dr              = emulator_set_dr,
@@ -4905,7 +4898,7 @@ static void init_emulate_ctxt(struct kvm_vcpu *vcpu)
        ctxt->eip = kvm_rip_read(vcpu);
        ctxt->mode = (!is_protmode(vcpu))               ? X86EMUL_MODE_REAL :
                     (ctxt->eflags & X86_EFLAGS_VM)     ? X86EMUL_MODE_VM86 :
-                    cs_l                               ? X86EMUL_MODE_PROT64 :
+                    (cs_l && is_long_mode(vcpu))       ? X86EMUL_MODE_PROT64 :
                     cs_db                              ? X86EMUL_MODE_PROT32 :
                                                          X86EMUL_MODE_PROT16;
        ctxt->guest_mode = is_guest_mode(vcpu);
@@ -7333,8 +7326,12 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
                kvm_mmu_change_mmu_pages(kvm, nr_mmu_pages);
        /*
         * Write protect all pages for dirty logging.
-        * Existing largepage mappings are destroyed here and new ones will
-        * not be created until the end of the logging.
+        *
+        * All the sptes including the large sptes which point to this
+        * slot are set to readonly. We can not create any new large
+        * spte on this slot until the end of the logging.
+        *
+        * See the comments in fast_page_fault().
         */
        if ((change != KVM_MR_DELETE) && (mem->flags & KVM_MEM_LOG_DIRTY_PAGES))
                kvm_mmu_slot_remove_write_access(kvm, mem->slot);
index 9757bb7..06370cc 100644 (file)
@@ -74,7 +74,6 @@ extern int initrd_below_start_ok;
 #endif
 
 #ifdef CONFIG_OF
-extern u32 __dtb_start[];
 void *dtb_start = __dtb_start;
 #endif
 
@@ -199,7 +198,7 @@ static int __init xtensa_dt_io_area(unsigned long node, const char *uname,
                int depth, void *data)
 {
        const __be32 *ranges;
-       unsigned long len;
+       int len;
 
        if (depth > 1)
                return 0;
index bce34af..ea55e01 100644 (file)
@@ -39,8 +39,9 @@ acpi-y                                += processor_core.o
 acpi-y                         += ec.o
 acpi-$(CONFIG_ACPI_DOCK)       += dock.o
 acpi-y                         += pci_root.o pci_link.o pci_irq.o
-acpi-$(CONFIG_X86_INTEL_LPSS)  += acpi_lpss.o
+acpi-y                         += acpi_lpss.o
 acpi-y                         += acpi_platform.o
+acpi-y                         += acpi_pnp.o
 acpi-y                         += power.o
 acpi-y                         += event.o
 acpi-y                         += sysfs.o
@@ -63,9 +64,9 @@ obj-$(CONFIG_ACPI_FAN)                += fan.o
 obj-$(CONFIG_ACPI_VIDEO)       += video.o
 obj-$(CONFIG_ACPI_PCI_SLOT)    += pci_slot.o
 obj-$(CONFIG_ACPI_PROCESSOR)   += processor.o
-obj-$(CONFIG_ACPI_CONTAINER)   += container.o
+obj-y                          += container.o
 obj-$(CONFIG_ACPI_THERMAL)     += thermal.o
-obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o
+obj-y                          += acpi_memhotplug.o
 obj-$(CONFIG_ACPI_BATTERY)     += battery.o
 obj-$(CONFIG_ACPI_SBS)         += sbshc.o
 obj-$(CONFIG_ACPI_SBS)         += sbs.o
index 961b45d..2da8660 100644 (file)
@@ -68,7 +68,7 @@ static int acpi_install_cmos_rtc_space_handler(struct acpi_device *adev,
                return -ENODEV;
        }
 
-       return 0;
+       return 1;
 }
 
 static void acpi_remove_cmos_rtc_space_handler(struct acpi_device *adev)
index c4a5d87..1853341 100644 (file)
@@ -220,13 +220,13 @@ static int __init extlog_init(void)
                goto err;
        }
 
-       extlog_l1_hdr = acpi_os_map_memory(l1_dirbase, l1_hdr_size);
+       extlog_l1_hdr = acpi_os_map_iomem(l1_dirbase, l1_hdr_size);
        l1_head = (struct extlog_l1_head *)extlog_l1_hdr;
        l1_size = l1_head->total_len;
        l1_percpu_entry = l1_head->entries;
        elog_base = l1_head->elog_base;
        elog_size = l1_head->elog_len;
-       acpi_os_unmap_memory(extlog_l1_hdr, l1_hdr_size);
+       acpi_os_unmap_iomem(extlog_l1_hdr, l1_hdr_size);
        release_mem_region(l1_dirbase, l1_hdr_size);
 
        /* remap L1 header again based on completed information */
@@ -237,7 +237,7 @@ static int __init extlog_init(void)
                        (unsigned long long)l1_dirbase + l1_size);
                goto err;
        }
-       extlog_l1_addr = acpi_os_map_memory(l1_dirbase, l1_size);
+       extlog_l1_addr = acpi_os_map_iomem(l1_dirbase, l1_size);
        l1_entry_base = (u64 *)((u8 *)extlog_l1_addr + l1_hdr_size);
 
        /* remap elog table */
@@ -248,7 +248,7 @@ static int __init extlog_init(void)
                        (unsigned long long)elog_base + elog_size);
                goto err_release_l1_dir;
        }
-       elog_addr = acpi_os_map_memory(elog_base, elog_size);
+       elog_addr = acpi_os_map_iomem(elog_base, elog_size);
 
        rc = -ENOMEM;
        /* allocate buffer to save elog record */
@@ -270,11 +270,11 @@ static int __init extlog_init(void)
 
 err_release_elog:
        if (elog_addr)
-               acpi_os_unmap_memory(elog_addr, elog_size);
+               acpi_os_unmap_iomem(elog_addr, elog_size);
        release_mem_region(elog_base, elog_size);
 err_release_l1_dir:
        if (extlog_l1_addr)
-               acpi_os_unmap_memory(extlog_l1_addr, l1_size);
+               acpi_os_unmap_iomem(extlog_l1_addr, l1_size);
        release_mem_region(l1_dirbase, l1_size);
 err:
        pr_warn(FW_BUG "Extended error log disabled because of problems parsing f/w tables\n");
@@ -287,9 +287,9 @@ static void __exit extlog_exit(void)
        mce_unregister_decode_chain(&extlog_mce_dec);
        ((struct extlog_l1_head *)extlog_l1_addr)->flags &= ~FLAG_OS_OPTIN;
        if (extlog_l1_addr)
-               acpi_os_unmap_memory(extlog_l1_addr, l1_size);
+               acpi_os_unmap_iomem(extlog_l1_addr, l1_size);
        if (elog_addr)
-               acpi_os_unmap_memory(elog_addr, elog_size);
+               acpi_os_unmap_iomem(elog_addr, elog_size);
        release_mem_region(elog_base, elog_size);
        release_mem_region(l1_dirbase, l1_size);
        kfree(elog_buf);
index d79c6d7..63407d2 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/platform_data/clk-lpss.h>
 #include <linux/pm_runtime.h>
+#include <linux/delay.h>
 
 #include "internal.h"
 
 ACPI_MODULE_NAME("acpi_lpss");
 
+#ifdef CONFIG_X86_INTEL_LPSS
+
+#define LPSS_ADDR(desc) ((unsigned long)&desc)
+
 #define LPSS_CLK_SIZE  0x04
 #define LPSS_LTR_SIZE  0x18
 
 /* Offsets relative to LPSS_PRIVATE_OFFSET */
+#define LPSS_CLK_DIVIDER_DEF_MASK      (BIT(1) | BIT(16))
 #define LPSS_GENERAL                   0x08
 #define LPSS_GENERAL_LTR_MODE_SW       BIT(2)
 #define LPSS_GENERAL_UART_RTS_OVRD     BIT(3)
@@ -43,6 +49,8 @@ ACPI_MODULE_NAME("acpi_lpss");
 #define LPSS_TX_INT                    0x20
 #define LPSS_TX_INT_MASK               BIT(1)
 
+#define LPSS_PRV_REG_COUNT             9
+
 struct lpss_shared_clock {
        const char *name;
        unsigned long rate;
@@ -57,7 +65,9 @@ struct lpss_device_desc {
        bool ltr_required;
        unsigned int prv_offset;
        size_t prv_size_override;
+       bool clk_divider;
        bool clk_gate;
+       bool save_ctx;
        struct lpss_shared_clock *shared_clock;
        void (*setup)(struct lpss_private_data *pdata);
 };
@@ -72,6 +82,7 @@ struct lpss_private_data {
        resource_size_t mmio_size;
        struct clk *clk;
        const struct lpss_device_desc *dev_desc;
+       u32 prv_reg_ctx[LPSS_PRV_REG_COUNT];
 };
 
 static void lpss_uart_setup(struct lpss_private_data *pdata)
@@ -89,6 +100,14 @@ static void lpss_uart_setup(struct lpss_private_data *pdata)
 }
 
 static struct lpss_device_desc lpt_dev_desc = {
+       .clk_required = true,
+       .prv_offset = 0x800,
+       .ltr_required = true,
+       .clk_divider = true,
+       .clk_gate = true,
+};
+
+static struct lpss_device_desc lpt_i2c_dev_desc = {
        .clk_required = true,
        .prv_offset = 0x800,
        .ltr_required = true,
@@ -99,6 +118,7 @@ static struct lpss_device_desc lpt_uart_dev_desc = {
        .clk_required = true,
        .prv_offset = 0x800,
        .ltr_required = true,
+       .clk_divider = true,
        .clk_gate = true,
        .setup = lpss_uart_setup,
 };
@@ -116,32 +136,25 @@ static struct lpss_shared_clock pwm_clock = {
 
 static struct lpss_device_desc byt_pwm_dev_desc = {
        .clk_required = true,
+       .save_ctx = true,
        .shared_clock = &pwm_clock,
 };
 
-static struct lpss_shared_clock uart_clock = {
-       .name = "uart_clk",
-       .rate = 44236800,
-};
-
 static struct lpss_device_desc byt_uart_dev_desc = {
        .clk_required = true,
        .prv_offset = 0x800,
+       .clk_divider = true,
        .clk_gate = true,
-       .shared_clock = &uart_clock,
+       .save_ctx = true,
        .setup = lpss_uart_setup,
 };
 
-static struct lpss_shared_clock spi_clock = {
-       .name = "spi_clk",
-       .rate = 50000000,
-};
-
 static struct lpss_device_desc byt_spi_dev_desc = {
        .clk_required = true,
        .prv_offset = 0x400,
+       .clk_divider = true,
        .clk_gate = true,
-       .shared_clock = &spi_clock,
+       .save_ctx = true,
 };
 
 static struct lpss_device_desc byt_sdio_dev_desc = {
@@ -156,44 +169,53 @@ static struct lpss_shared_clock i2c_clock = {
 static struct lpss_device_desc byt_i2c_dev_desc = {
        .clk_required = true,
        .prv_offset = 0x800,
+       .save_ctx = true,
        .shared_clock = &i2c_clock,
 };
 
+#else
+
+#define LPSS_ADDR(desc) (0UL)
+
+#endif /* CONFIG_X86_INTEL_LPSS */
+
 static const struct acpi_device_id acpi_lpss_device_ids[] = {
        /* Generic LPSS devices */
-       { "INTL9C60", (unsigned long)&lpss_dma_desc },
+       { "INTL9C60", LPSS_ADDR(lpss_dma_desc) },
 
        /* Lynxpoint LPSS devices */
-       { "INT33C0", (unsigned long)&lpt_dev_desc },
-       { "INT33C1", (unsigned long)&lpt_dev_desc },
-       { "INT33C2", (unsigned long)&lpt_dev_desc },
-       { "INT33C3", (unsigned long)&lpt_dev_desc },
-       { "INT33C4", (unsigned long)&lpt_uart_dev_desc },
-       { "INT33C5", (unsigned long)&lpt_uart_dev_desc },
-       { "INT33C6", (unsigned long)&lpt_sdio_dev_desc },
+       { "INT33C0", LPSS_ADDR(lpt_dev_desc) },
+       { "INT33C1", LPSS_ADDR(lpt_dev_desc) },
+       { "INT33C2", LPSS_ADDR(lpt_i2c_dev_desc) },
+       { "INT33C3", LPSS_ADDR(lpt_i2c_dev_desc) },
+       { "INT33C4", LPSS_ADDR(lpt_uart_dev_desc) },
+       { "INT33C5", LPSS_ADDR(lpt_uart_dev_desc) },
+       { "INT33C6", LPSS_ADDR(lpt_sdio_dev_desc) },
        { "INT33C7", },
 
        /* BayTrail LPSS devices */
-       { "80860F09", (unsigned long)&byt_pwm_dev_desc },
-       { "80860F0A", (unsigned long)&byt_uart_dev_desc },
-       { "80860F0E", (unsigned long)&byt_spi_dev_desc },
-       { "80860F14", (unsigned long)&byt_sdio_dev_desc },
-       { "80860F41", (unsigned long)&byt_i2c_dev_desc },
+       { "80860F09", LPSS_ADDR(byt_pwm_dev_desc) },
+       { "80860F0A", LPSS_ADDR(byt_uart_dev_desc) },
+       { "80860F0E", LPSS_ADDR(byt_spi_dev_desc) },
+       { "80860F14", LPSS_ADDR(byt_sdio_dev_desc) },
+       { "80860F41", LPSS_ADDR(byt_i2c_dev_desc) },
        { "INT33B2", },
        { "INT33FC", },
 
-       { "INT3430", (unsigned long)&lpt_dev_desc },
-       { "INT3431", (unsigned long)&lpt_dev_desc },
-       { "INT3432", (unsigned long)&lpt_dev_desc },
-       { "INT3433", (unsigned long)&lpt_dev_desc },
-       { "INT3434", (unsigned long)&lpt_uart_dev_desc },
-       { "INT3435", (unsigned long)&lpt_uart_dev_desc },
-       { "INT3436", (unsigned long)&lpt_sdio_dev_desc },
+       { "INT3430", LPSS_ADDR(lpt_dev_desc) },
+       { "INT3431", LPSS_ADDR(lpt_dev_desc) },
+       { "INT3432", LPSS_ADDR(lpt_i2c_dev_desc) },
+       { "INT3433", LPSS_ADDR(lpt_i2c_dev_desc) },
+       { "INT3434", LPSS_ADDR(lpt_uart_dev_desc) },
+       { "INT3435", LPSS_ADDR(lpt_uart_dev_desc) },
+       { "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) },
        { "INT3437", },
 
        { }
 };
 
+#ifdef CONFIG_X86_INTEL_LPSS
+
 static int is_memory(struct acpi_resource *res, void *not_used)
 {
        struct resource r;
@@ -213,9 +235,11 @@ static int register_device_clock(struct acpi_device *adev,
 {
        const struct lpss_device_desc *dev_desc = pdata->dev_desc;
        struct lpss_shared_clock *shared_clock = dev_desc->shared_clock;
+       const char *devname = dev_name(&adev->dev);
        struct clk *clk = ERR_PTR(-ENODEV);
        struct lpss_clk_data *clk_data;
-       const char *parent;
+       const char *parent, *clk_name;
+       void __iomem *prv_base;
 
        if (!lpss_clk_dev)
                lpt_register_clock_device();
@@ -226,7 +250,7 @@ static int register_device_clock(struct acpi_device *adev,
 
        if (dev_desc->clkdev_name) {
                clk_register_clkdev(clk_data->clk, dev_desc->clkdev_name,
-                                   dev_name(&adev->dev));
+                                   devname);
                return 0;
        }
 
@@ -235,6 +259,7 @@ static int register_device_clock(struct acpi_device *adev,
                return -ENODATA;
 
        parent = clk_data->name;
+       prv_base = pdata->mmio_base + dev_desc->prv_offset;
 
        if (shared_clock) {
                clk = shared_clock->clk;
@@ -248,16 +273,41 @@ static int register_device_clock(struct acpi_device *adev,
        }
 
        if (dev_desc->clk_gate) {
-               clk = clk_register_gate(NULL, dev_name(&adev->dev), parent, 0,
-                                       pdata->mmio_base + dev_desc->prv_offset,
-                                       0, 0, NULL);
-               pdata->clk = clk;
+               clk = clk_register_gate(NULL, devname, parent, 0,
+                                       prv_base, 0, 0, NULL);
+               parent = devname;
+       }
+
+       if (dev_desc->clk_divider) {
+               /* Prevent division by zero */
+               if (!readl(prv_base))
+                       writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base);
+
+               clk_name = kasprintf(GFP_KERNEL, "%s-div", devname);
+               if (!clk_name)
+                       return -ENOMEM;
+               clk = clk_register_fractional_divider(NULL, clk_name, parent,
+                                                     0, prv_base,
+                                                     1, 15, 16, 15, 0, NULL);
+               parent = clk_name;
+
+               clk_name = kasprintf(GFP_KERNEL, "%s-update", devname);
+               if (!clk_name) {
+                       kfree(parent);
+                       return -ENOMEM;
+               }
+               clk = clk_register_gate(NULL, clk_name, parent,
+                                       CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
+                                       prv_base, 31, 0, NULL);
+               kfree(parent);
+               kfree(clk_name);
        }
 
        if (IS_ERR(clk))
                return PTR_ERR(clk);
 
-       clk_register_clkdev(clk, NULL, dev_name(&adev->dev));
+       pdata->clk = clk;
+       clk_register_clkdev(clk, NULL, devname);
        return 0;
 }
 
@@ -268,12 +318,14 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
        struct lpss_private_data *pdata;
        struct resource_list_entry *rentry;
        struct list_head resource_list;
+       struct platform_device *pdev;
        int ret;
 
        dev_desc = (struct lpss_device_desc *)id->driver_data;
-       if (!dev_desc)
-               return acpi_create_platform_device(adev, id);
-
+       if (!dev_desc) {
+               pdev = acpi_create_platform_device(adev);
+               return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1;
+       }
        pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
                return -ENOMEM;
@@ -323,10 +375,13 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
                dev_desc->setup(pdata);
 
        adev->driver_data = pdata;
-       ret = acpi_create_platform_device(adev, id);
-       if (ret > 0)
-               return ret;
+       pdev = acpi_create_platform_device(adev);
+       if (!IS_ERR_OR_NULL(pdev)) {
+               device_enable_async_suspend(&pdev->dev);
+               return 1;
+       }
 
+       ret = PTR_ERR(pdev);
        adev->driver_data = NULL;
 
  err_out:
@@ -450,6 +505,126 @@ static void acpi_lpss_set_ltr(struct device *dev, s32 val)
        }
 }
 
+#ifdef CONFIG_PM
+/**
+ * acpi_lpss_save_ctx() - Save the private registers of LPSS device
+ * @dev: LPSS device
+ *
+ * Most LPSS devices have private registers which may loose their context when
+ * the device is powered down. acpi_lpss_save_ctx() saves those registers into
+ * prv_reg_ctx array.
+ */
+static void acpi_lpss_save_ctx(struct device *dev)
+{
+       struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+       unsigned int i;
+
+       for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
+               unsigned long offset = i * sizeof(u32);
+
+               pdata->prv_reg_ctx[i] = __lpss_reg_read(pdata, offset);
+               dev_dbg(dev, "saving 0x%08x from LPSS reg at offset 0x%02lx\n",
+                       pdata->prv_reg_ctx[i], offset);
+       }
+}
+
+/**
+ * acpi_lpss_restore_ctx() - Restore the private registers of LPSS device
+ * @dev: LPSS device
+ *
+ * Restores the registers that were previously stored with acpi_lpss_save_ctx().
+ */
+static void acpi_lpss_restore_ctx(struct device *dev)
+{
+       struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+       unsigned int i;
+
+       /*
+        * The following delay is needed or the subsequent write operations may
+        * fail. The LPSS devices are actually PCI devices and the PCI spec
+        * expects 10ms delay before the device can be accessed after D3 to D0
+        * transition.
+        */
+       msleep(10);
+
+       for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
+               unsigned long offset = i * sizeof(u32);
+
+               __lpss_reg_write(pdata->prv_reg_ctx[i], pdata, offset);
+               dev_dbg(dev, "restoring 0x%08x to LPSS reg at offset 0x%02lx\n",
+                       pdata->prv_reg_ctx[i], offset);
+       }
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int acpi_lpss_suspend_late(struct device *dev)
+{
+       int ret = pm_generic_suspend_late(dev);
+
+       if (ret)
+               return ret;
+
+       acpi_lpss_save_ctx(dev);
+       return acpi_dev_suspend_late(dev);
+}
+
+static int acpi_lpss_restore_early(struct device *dev)
+{
+       int ret = acpi_dev_resume_early(dev);
+
+       if (ret)
+               return ret;
+
+       acpi_lpss_restore_ctx(dev);
+       return pm_generic_resume_early(dev);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM_RUNTIME
+static int acpi_lpss_runtime_suspend(struct device *dev)
+{
+       int ret = pm_generic_runtime_suspend(dev);
+
+       if (ret)
+               return ret;
+
+       acpi_lpss_save_ctx(dev);
+       return acpi_dev_runtime_suspend(dev);
+}
+
+static int acpi_lpss_runtime_resume(struct device *dev)
+{
+       int ret = acpi_dev_runtime_resume(dev);
+
+       if (ret)
+               return ret;
+
+       acpi_lpss_restore_ctx(dev);
+       return pm_generic_runtime_resume(dev);
+}
+#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM */
+
+static struct dev_pm_domain acpi_lpss_pm_domain = {
+       .ops = {
+#ifdef CONFIG_PM_SLEEP
+               .suspend_late = acpi_lpss_suspend_late,
+               .restore_early = acpi_lpss_restore_early,
+               .prepare = acpi_subsys_prepare,
+               .complete = acpi_subsys_complete,
+               .suspend = acpi_subsys_suspend,
+               .resume_early = acpi_subsys_resume_early,
+               .freeze = acpi_subsys_freeze,
+               .poweroff = acpi_subsys_suspend,
+               .poweroff_late = acpi_subsys_suspend_late,
+#endif
+#ifdef CONFIG_PM_RUNTIME
+               .runtime_suspend = acpi_lpss_runtime_suspend,
+               .runtime_resume = acpi_lpss_runtime_resume,
+#endif
+       },
+};
+
 static int acpi_lpss_platform_notify(struct notifier_block *nb,
                                     unsigned long action, void *data)
 {
@@ -457,7 +632,6 @@ static int acpi_lpss_platform_notify(struct notifier_block *nb,
        struct lpss_private_data *pdata;
        struct acpi_device *adev;
        const struct acpi_device_id *id;
-       int ret = 0;
 
        id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev);
        if (!id || !id->driver_data)
@@ -467,7 +641,7 @@ static int acpi_lpss_platform_notify(struct notifier_block *nb,
                return 0;
 
        pdata = acpi_driver_data(adev);
-       if (!pdata || !pdata->mmio_base || !pdata->dev_desc->ltr_required)
+       if (!pdata || !pdata->mmio_base)
                return 0;
 
        if (pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) {
@@ -475,12 +649,27 @@ static int acpi_lpss_platform_notify(struct notifier_block *nb,
                return 0;
        }
 
-       if (action == BUS_NOTIFY_ADD_DEVICE)
-               ret = sysfs_create_group(&pdev->dev.kobj, &lpss_attr_group);
-       else if (action == BUS_NOTIFY_DEL_DEVICE)
-               sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
+       switch (action) {
+       case BUS_NOTIFY_BOUND_DRIVER:
+               if (pdata->dev_desc->save_ctx)
+                       pdev->dev.pm_domain = &acpi_lpss_pm_domain;
+               break;
+       case BUS_NOTIFY_UNBOUND_DRIVER:
+               if (pdata->dev_desc->save_ctx)
+                       pdev->dev.pm_domain = NULL;
+               break;
+       case BUS_NOTIFY_ADD_DEVICE:
+               if (pdata->dev_desc->ltr_required)
+                       return sysfs_create_group(&pdev->dev.kobj,
+                                                 &lpss_attr_group);
+       case BUS_NOTIFY_DEL_DEVICE:
+               if (pdata->dev_desc->ltr_required)
+                       sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
+       default:
+               break;
+       }
 
-       return ret;
+       return 0;
 }
 
 static struct notifier_block acpi_lpss_nb = {
@@ -519,3 +708,16 @@ void __init acpi_lpss_init(void)
                acpi_scan_add_handler(&lpss_handler);
        }
 }
+
+#else
+
+static struct acpi_scan_handler lpss_handler = {
+       .ids = acpi_lpss_device_ids,
+};
+
+void __init acpi_lpss_init(void)
+{
+       acpi_scan_add_handler(&lpss_handler);
+}
+
+#endif /* CONFIG_X86_INTEL_LPSS */
index b67be85..23e2319 100644 (file)
 
 ACPI_MODULE_NAME("acpi_memhotplug");
 
+static const struct acpi_device_id memory_device_ids[] = {
+       {ACPI_MEMORY_DEVICE_HID, 0},
+       {"", 0},
+};
+
+#ifdef CONFIG_ACPI_HOTPLUG_MEMORY
+
 /* Memory Device States */
 #define MEMORY_INVALID_STATE   0
 #define MEMORY_POWER_ON_STATE  1
@@ -53,11 +60,6 @@ static int acpi_memory_device_add(struct acpi_device *device,
                                  const struct acpi_device_id *not_used);
 static void acpi_memory_device_remove(struct acpi_device *device);
 
-static const struct acpi_device_id memory_device_ids[] = {
-       {ACPI_MEMORY_DEVICE_HID, 0},
-       {"", 0},
-};
-
 static struct acpi_scan_handler memory_device_handler = {
        .ids = memory_device_ids,
        .attach = acpi_memory_device_add,
@@ -364,9 +366,11 @@ static bool __initdata acpi_no_memhotplug;
 
 void __init acpi_memory_hotplug_init(void)
 {
-       if (acpi_no_memhotplug)
+       if (acpi_no_memhotplug) {
+               memory_device_handler.attach = NULL;
+               acpi_scan_add_handler(&memory_device_handler);
                return;
-
+       }
        acpi_scan_add_handler_with_hotplug(&memory_device_handler, "memory");
 }
 
@@ -376,3 +380,16 @@ static int __init disable_acpi_memory_hotplug(char *str)
        return 1;
 }
 __setup("acpi_no_memhotplug", disable_acpi_memory_hotplug);
+
+#else
+
+static struct acpi_scan_handler memory_device_handler = {
+       .ids = memory_device_ids,
+};
+
+void __init acpi_memory_hotplug_init(void)
+{
+       acpi_scan_add_handler(&memory_device_handler);
+}
+
+#endif /* CONFIG_ACPI_HOTPLUG_MEMORY */
index 37d7302..f148a05 100644 (file)
@@ -156,12 +156,13 @@ static int power_saving_thread(void *data)
 
        while (!kthread_should_stop()) {
                int cpu;
-               u64 expire_time;
+               unsigned long expire_time;
 
                try_to_freeze();
 
                /* round robin to cpus */
-               if (last_jiffies + round_robin_time * HZ < jiffies) {
+               expire_time = last_jiffies + round_robin_time * HZ;
+               if (time_before(expire_time, jiffies)) {
                        last_jiffies = jiffies;
                        round_robin_cpu(tsk_index);
                }
@@ -200,7 +201,7 @@ static int power_saving_thread(void *data)
                                        CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
                        local_irq_enable();
 
-                       if (jiffies > expire_time) {
+                       if (time_before(expire_time, jiffies)) {
                                do_sleep = 1;
                                break;
                        }
@@ -215,8 +216,15 @@ static int power_saving_thread(void *data)
                 * borrow CPU time from this CPU and cause RT task use > 95%
                 * CPU time. To make 'avoid starvation' work, takes a nap here.
                 */
-               if (do_sleep)
+               if (unlikely(do_sleep))
                        schedule_timeout_killable(HZ * idle_pct / 100);
+
+               /* If an external event has set the need_resched flag, then
+                * we need to deal with it, or this loop will continue to
+                * spin without calling __mwait().
+                */
+               if (unlikely(need_resched()))
+                       schedule();
        }
 
        exit_round_robin(tsk_index);
index 1d49503..2bf9082 100644 (file)
 
 ACPI_MODULE_NAME("platform");
 
-/*
- * The following ACPI IDs are known to be suitable for representing as
- * platform devices.
- */
-static const struct acpi_device_id acpi_platform_device_ids[] = {
-
-       { "PNP0D40" },
-       { "VPC2004" },
-       { "BCM4752" },
-
-       /* Intel Smart Sound Technology */
-       { "INT33C8" },
-       { "80860F28" },
-
-       { }
+static const struct acpi_device_id forbidden_id_list[] = {
+       {"PNP0000", 0}, /* PIC */
+       {"PNP0100", 0}, /* Timer */
+       {"PNP0200", 0}, /* AT DMA Controller */
+       {"", 0},
 };
 
 /**
  * acpi_create_platform_device - Create platform device for ACPI device node
  * @adev: ACPI device node to create a platform device for.
- * @id: ACPI device ID used to match @adev.
  *
  * Check if the given @adev can be represented as a platform device and, if
  * that's the case, create and register a platform device, populate its common
@@ -50,8 +39,7 @@ static const struct acpi_device_id acpi_platform_device_ids[] = {
  *
  * Name of the platform device will be the same as @adev's.
  */
-int acpi_create_platform_device(struct acpi_device *adev,
-                               const struct acpi_device_id *id)
+struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
 {
        struct platform_device *pdev = NULL;
        struct acpi_device *acpi_parent;
@@ -63,19 +51,22 @@ int acpi_create_platform_device(struct acpi_device *adev,
 
        /* If the ACPI node already has a physical device attached, skip it. */
        if (adev->physical_node_count)
-               return 0;
+               return NULL;
+
+       if (!acpi_match_device_ids(adev, forbidden_id_list))
+               return ERR_PTR(-EINVAL);
 
        INIT_LIST_HEAD(&resource_list);
        count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
        if (count < 0) {
-               return 0;
+               return NULL;
        } else if (count > 0) {
                resources = kmalloc(count * sizeof(struct resource),
                                    GFP_KERNEL);
                if (!resources) {
                        dev_err(&adev->dev, "No memory for resources\n");
                        acpi_dev_free_resource_list(&resource_list);
-                       return -ENOMEM;
+                       return ERR_PTR(-ENOMEM);
                }
                count = 0;
                list_for_each_entry(rentry, &resource_list, node)
@@ -112,25 +103,13 @@ int acpi_create_platform_device(struct acpi_device *adev,
        pdevinfo.num_res = count;
        pdevinfo.acpi_node.companion = adev;
        pdev = platform_device_register_full(&pdevinfo);
-       if (IS_ERR(pdev)) {
+       if (IS_ERR(pdev))
                dev_err(&adev->dev, "platform device creation failed: %ld\n",
                        PTR_ERR(pdev));
-               pdev = NULL;
-       } else {
+       else
                dev_dbg(&adev->dev, "created platform device %s\n",
                        dev_name(&pdev->dev));
-       }
 
        kfree(resources);
-       return 1;
-}
-
-static struct acpi_scan_handler platform_handler = {
-       .ids = acpi_platform_device_ids,
-       .attach = acpi_create_platform_device,
-};
-
-void __init acpi_platform_init(void)
-{
-       acpi_scan_add_handler(&platform_handler);
+       return pdev;
 }
diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c
new file mode 100644 (file)
index 0000000..6703c1f
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * ACPI support for PNP bus type
+ *
+ * Copyright (C) 2014, Intel Corporation
+ * Authors: Zhang Rui <rui.zhang@intel.com>
+ *          Rafael J. Wysocki <rafael.j.wysocki@intel.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/acpi.h>
+#include <linux/module.h>
+
+static const struct acpi_device_id acpi_pnp_device_ids[] = {
+       /* pata_isapnp */
+       {"PNP0600"},            /* Generic ESDI/IDE/ATA compatible hard disk controller */
+       /* floppy */
+       {"PNP0700"},
+       /* ipmi_si */
+       {"IPI0001"},
+       /* tpm_inf_pnp */
+       {"IFX0101"},            /* Infineon TPMs */
+       {"IFX0102"},            /* Infineon TPMs */
+       /*tpm_tis */
+       {"PNP0C31"},            /* TPM */
+       {"ATM1200"},            /* Atmel */
+       {"IFX0102"},            /* Infineon */
+       {"BCM0101"},            /* Broadcom */
+       {"BCM0102"},            /* Broadcom */
+       {"NSC1200"},            /* National */
+       {"ICO0102"},            /* Intel */
+       /* ide   */
+       {"PNP0600"},            /* Generic ESDI/IDE/ATA compatible hard disk controller */
+       /* ns558 */
+       {"ASB16fd"},            /* AdLib NSC16 */
+       {"AZT3001"},            /* AZT1008 */
+       {"CDC0001"},            /* Opl3-SAx */
+       {"CSC0001"},            /* CS4232 */
+       {"CSC000f"},            /* CS4236 */
+       {"CSC0101"},            /* CS4327 */
+       {"CTL7001"},            /* SB16 */
+       {"CTL7002"},            /* AWE64 */
+       {"CTL7005"},            /* Vibra16 */
+       {"ENS2020"},            /* SoundscapeVIVO */
+       {"ESS0001"},            /* ES1869 */
+       {"ESS0005"},            /* ES1878 */
+       {"ESS6880"},            /* ES688 */
+       {"IBM0012"},            /* CS4232 */
+       {"OPT0001"},            /* OPTi Audio16 */
+       {"YMH0006"},            /* Opl3-SA */
+       {"YMH0022"},            /* Opl3-SAx */
+       {"PNPb02f"},            /* Generic */
+       /* i8042 kbd */
+       {"PNP0300"},
+       {"PNP0301"},
+       {"PNP0302"},
+       {"PNP0303"},
+       {"PNP0304"},
+       {"PNP0305"},
+       {"PNP0306"},
+       {"PNP0309"},
+       {"PNP030a"},
+       {"PNP030b"},
+       {"PNP0320"},
+       {"PNP0343"},
+       {"PNP0344"},
+       {"PNP0345"},
+       {"CPQA0D7"},
+       /* i8042 aux */
+       {"AUI0200"},
+       {"FJC6000"},
+       {"FJC6001"},
+       {"PNP0f03"},
+       {"PNP0f0b"},
+       {"PNP0f0e"},
+       {"PNP0f12"},
+       {"PNP0f13"},
+       {"PNP0f19"},
+       {"PNP0f1c"},
+       {"SYN0801"},
+       /* fcpnp */
+       {"AVM0900"},
+       /* radio-cadet */
+       {"MSM0c24"},            /* ADS Cadet AM/FM Radio Card */
+       /* radio-gemtek */
+       {"ADS7183"},            /* AOpen FX-3D/Pro Radio */
+       /* radio-sf16fmr2 */
+       {"MFRad13"},            /* tuner subdevice of SF16-FMD2 */
+       /* ene_ir */
+       {"ENE0100"},
+       {"ENE0200"},
+       {"ENE0201"},
+       {"ENE0202"},
+       /* fintek-cir */
+       {"FIT0002"},            /* CIR */
+       /* ite-cir */
+       {"ITE8704"},            /* Default model */
+       {"ITE8713"},            /* CIR found in EEEBox 1501U */
+       {"ITE8708"},            /* Bridged IT8512 */
+       {"ITE8709"},            /* SRAM-Bridged IT8512 */
+       /* nuvoton-cir */
+       {"WEC0530"},            /* CIR */
+       {"NTN0530"},            /* CIR for new chip's pnp id */
+       /* Winbond CIR */
+       {"WEC1022"},
+       /* wbsd */
+       {"WEC0517"},
+       {"WEC0518"},
+       /* Winbond CIR */
+       {"TCM5090"},            /* 3Com Etherlink III (TP) */
+       {"TCM5091"},            /* 3Com Etherlink III */
+       {"TCM5094"},            /* 3Com Etherlink III (combo) */
+       {"TCM5095"},            /* 3Com Etherlink III (TPO) */
+       {"TCM5098"},            /* 3Com Etherlink III (TPC) */
+       {"PNP80f7"},            /* 3Com Etherlink III compatible */
+       {"PNP80f8"},            /* 3Com Etherlink III compatible */
+       /* nsc-ircc */
+       {"NSC6001"},
+       {"HWPC224"},
+       {"IBM0071"},
+       /* smsc-ircc2 */
+       {"SMCf010"},
+       /* sb1000 */
+       {"GIC1000"},
+       /* parport_pc */
+       {"PNP0400"},            /* Standard LPT Printer Port */
+       {"PNP0401"},            /* ECP Printer Port */
+       /* apple-gmux */
+       {"APP000B"},
+       /* fujitsu-laptop.c */
+       {"FUJ02bf"},
+       {"FUJ02B1"},
+       {"FUJ02E3"},
+       /* system */
+       {"PNP0c02"},            /* General ID for reserving resources */
+       {"PNP0c01"},            /* memory controller */
+       /* rtc_cmos */
+       {"PNP0b00"},
+       {"PNP0b01"},
+       {"PNP0b02"},
+       /* c6xdigio */
+       {"PNP0400"},            /* Standard LPT Printer Port */
+       {"PNP0401"},            /* ECP Printer Port */
+       /* ni_atmio.c */
+       {"NIC1900"},
+       {"NIC2400"},
+       {"NIC2500"},
+       {"NIC2600"},
+       {"NIC2700"},
+       /* serial */
+       {"AAC000F"},            /* Archtek America Corp. Archtek SmartLink Modem 3334BT Plug & Play */
+       {"ADC0001"},            /* Anchor Datacomm BV. SXPro 144 External Data Fax Modem Plug & Play */
+       {"ADC0002"},            /* SXPro 288 External Data Fax Modem Plug & Play */
+       {"AEI0250"},            /* PROLiNK 1456VH ISA PnP K56flex Fax Modem */
+       {"AEI1240"},            /* Actiontec ISA PNP 56K X2 Fax Modem */
+       {"AKY1021"},            /* Rockwell 56K ACF II Fax+Data+Voice Modem */
+       {"AZT4001"},            /* AZT3005 PnP SOUND DEVICE */
+       {"BDP3336"},            /* Best Data Products Inc. Smart One 336F PnP Modem */
+       {"BRI0A49"},            /* Boca Complete Ofc Communicator 14.4 Data-FAX */
+       {"BRI1400"},            /* Boca Research 33,600 ACF Modem */
+       {"BRI3400"},            /* Boca 33.6 Kbps Internal FD34FSVD */
+       {"BRI0A49"},            /* Boca 33.6 Kbps Internal FD34FSVD */
+       {"BDP3336"},            /* Best Data Products Inc. Smart One 336F PnP Modem */
+       {"CPI4050"},            /* Computer Peripherals Inc. EuroViVa CommCenter-33.6 SP PnP */
+       {"CTL3001"},            /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */
+       {"CTL3011"},            /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */
+       {"DAV0336"},            /* Davicom ISA 33.6K Modem */
+       {"DMB1032"},            /* Creative Modem Blaster Flash56 DI5601-1 */
+       {"DMB2001"},            /* Creative Modem Blaster V.90 DI5660 */
+       {"ETT0002"},            /* E-Tech CyberBULLET PC56RVP */
+       {"FUJ0202"},            /* Fujitsu 33600 PnP-I2 R Plug & Play */
+       {"FUJ0205"},            /* Fujitsu FMV-FX431 Plug & Play */
+       {"FUJ0206"},            /* Fujitsu 33600 PnP-I4 R Plug & Play */
+       {"FUJ0209"},            /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */
+       {"GVC000F"},            /* Archtek SmartLink Modem 3334BT Plug & Play */
+       {"GVC0303"},            /* Archtek SmartLink Modem 3334BRV 33.6K Data Fax Voice */
+       {"HAY0001"},            /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */
+       {"HAY000C"},            /* Hayes Optima 336 V.34 + FAX + Voice PnP */
+       {"HAY000D"},            /* Hayes Optima 336B V.34 + FAX + Voice PnP */
+       {"HAY5670"},            /* Hayes Accura 56K Ext Fax Modem PnP */
+       {"HAY5674"},            /* Hayes Accura 56K Ext Fax Modem PnP */
+       {"HAY5675"},            /* Hayes Accura 56K Fax Modem PnP */
+       {"HAYF000"},            /* Hayes 288, V.34 + FAX */
+       {"HAYF001"},            /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */
+       {"IBM0033"},            /* IBM Thinkpad 701 Internal Modem Voice */
+       {"PNP4972"},            /* Intermec CV60 touchscreen port */
+       {"IXDC801"},            /* Intertex 28k8 33k6 Voice EXT PnP */
+       {"IXDC901"},            /* Intertex 33k6 56k Voice EXT PnP */
+       {"IXDD801"},            /* Intertex 28k8 33k6 Voice SP EXT PnP */
+       {"IXDD901"},            /* Intertex 33k6 56k Voice SP EXT PnP */
+       {"IXDF401"},            /* Intertex 28k8 33k6 Voice SP INT PnP */
+       {"IXDF801"},            /* Intertex 28k8 33k6 Voice SP EXT PnP */
+       {"IXDF901"},            /* Intertex 33k6 56k Voice SP EXT PnP */
+       {"KOR4522"},            /* KORTEX 28800 Externe PnP */
+       {"KORF661"},            /* KXPro 33.6 Vocal ASVD PnP */
+       {"LAS4040"},            /* LASAT Internet 33600 PnP */
+       {"LAS4540"},            /* Lasat Safire 560 PnP */
+       {"LAS5440"},            /* Lasat Safire 336  PnP */
+       {"MNP0281"},            /* Microcom TravelPorte FAST V.34 Plug & Play */
+       {"MNP0336"},            /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */
+       {"MNP0339"},            /* Microcom DeskPorte FAST EP 28.8 Plug & Play */
+       {"MNP0342"},            /* Microcom DeskPorte 28.8P Plug & Play */
+       {"MNP0500"},            /* Microcom DeskPorte FAST ES 28.8 Plug & Play */
+       {"MNP0501"},            /* Microcom DeskPorte FAST ES 28.8 Plug & Play */
+       {"MNP0502"},            /* Microcom DeskPorte 28.8S Internal Plug & Play */
+       {"MOT1105"},            /* Motorola BitSURFR Plug & Play */
+       {"MOT1111"},            /* Motorola TA210 Plug & Play */
+       {"MOT1114"},            /* Motorola HMTA 200 (ISDN) Plug & Play */
+       {"MOT1115"},            /* Motorola BitSURFR Plug & Play */
+       {"MOT1190"},            /* Motorola Lifestyle 28.8 Internal */
+       {"MOT1501"},            /* Motorola V.3400 Plug & Play */
+       {"MOT1502"},            /* Motorola Lifestyle 28.8 V.34 Plug & Play */
+       {"MOT1505"},            /* Motorola Power 28.8 V.34 Plug & Play */
+       {"MOT1509"},            /* Motorola ModemSURFR External 28.8 Plug & Play */
+       {"MOT150A"},            /* Motorola Premier 33.6 Desktop Plug & Play */
+       {"MOT150F"},            /* Motorola VoiceSURFR 56K External PnP */
+       {"MOT1510"},            /* Motorola ModemSURFR 56K External PnP */
+       {"MOT1550"},            /* Motorola ModemSURFR 56K Internal PnP */
+       {"MOT1560"},            /* Motorola ModemSURFR Internal 28.8 Plug & Play */
+       {"MOT1580"},            /* Motorola Premier 33.6 Internal Plug & Play */
+       {"MOT15B0"},            /* Motorola OnlineSURFR 28.8 Internal Plug & Play */
+       {"MOT15F0"},            /* Motorola VoiceSURFR 56K Internal PnP */
+       {"MVX00A1"},            /*  Deskline K56 Phone System PnP */
+       {"MVX00F2"},            /* PC Rider K56 Phone System PnP */
+       {"nEC8241"},            /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */
+       {"PMC2430"},            /* Pace 56 Voice Internal Plug & Play Modem */
+       {"PNP0500"},            /* Generic standard PC COM port     */
+       {"PNP0501"},            /* Generic 16550A-compatible COM port */
+       {"PNPC000"},            /* Compaq 14400 Modem */
+       {"PNPC001"},            /* Compaq 2400/9600 Modem */
+       {"PNPC031"},            /* Dial-Up Networking Serial Cable between 2 PCs */
+       {"PNPC032"},            /* Dial-Up Networking Parallel Cable between 2 PCs */
+       {"PNPC100"},            /* Standard 9600 bps Modem */
+       {"PNPC101"},            /* Standard 14400 bps Modem */
+       {"PNPC102"},            /*  Standard 28800 bps Modem */
+       {"PNPC103"},            /*  Standard Modem */
+       {"PNPC104"},            /*  Standard 9600 bps Modem */
+       {"PNPC105"},            /*  Standard 14400 bps Modem */
+       {"PNPC106"},            /*  Standard 28800 bps Modem */
+       {"PNPC107"},            /*  Standard Modem */
+       {"PNPC108"},            /* Standard 9600 bps Modem */
+       {"PNPC109"},            /* Standard 14400 bps Modem */
+       {"PNPC10A"},            /* Standard 28800 bps Modem */
+       {"PNPC10B"},            /* Standard Modem */
+       {"PNPC10C"},            /* Standard 9600 bps Modem */
+       {"PNPC10D"},            /* Standard 14400 bps Modem */
+       {"PNPC10E"},            /* Standard 28800 bps Modem */
+       {"PNPC10F"},            /* Standard Modem */
+       {"PNP2000"},            /* Standard PCMCIA Card Modem */
+       {"ROK0030"},            /* Rockwell 33.6 DPF Internal PnP, Modular Technology 33.6 Internal PnP */
+       {"ROK0100"},            /* KORTEX 14400 Externe PnP */
+       {"ROK4120"},            /* Rockwell 28.8 */
+       {"ROK4920"},            /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */
+       {"RSS00A0"},            /* Rockwell 33.6 DPF External PnP, BT Prologue 33.6 External PnP, Modular Technology 33.6 External PnP */
+       {"RSS0262"},            /* Viking 56K FAX INT */
+       {"RSS0250"},            /* K56 par,VV,Voice,Speakphone,AudioSpan,PnP */
+       {"SUP1310"},            /* SupraExpress 28.8 Data/Fax PnP modem */
+       {"SUP1381"},            /* SupraExpress 336i PnP Voice Modem */
+       {"SUP1421"},            /* SupraExpress 33.6 Data/Fax PnP modem */
+       {"SUP1590"},            /* SupraExpress 33.6 Data/Fax PnP modem */
+       {"SUP1620"},            /* SupraExpress 336i Sp ASVD */
+       {"SUP1760"},            /* SupraExpress 33.6 Data/Fax PnP modem */
+       {"SUP2171"},            /* SupraExpress 56i Sp Intl */
+       {"TEX0011"},            /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */
+       {"UAC000F"},            /* Archtek SmartLink Modem 3334BT Plug & Play */
+       {"USR0000"},            /* 3Com Corp. Gateway Telepath IIvi 33.6 */
+       {"USR0002"},            /* U.S. Robotics Sporster 33.6K Fax INT PnP */
+       {"USR0004"},            /*  Sportster Vi 14.4 PnP FAX Voicemail */
+       {"USR0006"},            /* U.S. Robotics 33.6K Voice INT PnP */
+       {"USR0007"},            /* U.S. Robotics 33.6K Voice EXT PnP */
+       {"USR0009"},            /* U.S. Robotics Courier V.Everything INT PnP */
+       {"USR2002"},            /* U.S. Robotics 33.6K Voice INT PnP */
+       {"USR2070"},            /* U.S. Robotics 56K Voice INT PnP */
+       {"USR2080"},            /* U.S. Robotics 56K Voice EXT PnP */
+       {"USR3031"},            /* U.S. Robotics 56K FAX INT */
+       {"USR3050"},            /* U.S. Robotics 56K FAX INT */
+       {"USR3070"},            /* U.S. Robotics 56K Voice INT PnP */
+       {"USR3080"},            /* U.S. Robotics 56K Voice EXT PnP */
+       {"USR3090"},            /* U.S. Robotics 56K Voice INT PnP */
+       {"USR9100"},            /* U.S. Robotics 56K Message  */
+       {"USR9160"},            /* U.S. Robotics 56K FAX EXT PnP */
+       {"USR9170"},            /* U.S. Robotics 56K FAX INT PnP */
+       {"USR9180"},            /* U.S. Robotics 56K Voice EXT PnP */
+       {"USR9190"},            /* U.S. Robotics 56K Voice INT PnP */
+       {"WACFXXX"},            /* Wacom tablets */
+       {"FPI2002"},            /* Compaq touchscreen */
+       {"FUJ02B2"},            /* Fujitsu Stylistic touchscreens */
+       {"FUJ02B3"},
+       {"FUJ02B4"},            /* Fujitsu Stylistic LT touchscreens */
+       {"FUJ02B6"},            /* Passive Fujitsu Stylistic touchscreens */
+       {"FUJ02B7"},
+       {"FUJ02B8"},
+       {"FUJ02B9"},
+       {"FUJ02BC"},
+       {"FUJ02E5"},            /* Fujitsu Wacom Tablet PC device */
+       {"FUJ02E6"},            /* Fujitsu P-series tablet PC device */
+       {"FUJ02E7"},            /* Fujitsu Wacom 2FGT Tablet PC device */
+       {"FUJ02E9"},            /* Fujitsu Wacom 1FGT Tablet PC device */
+       {"LTS0001"},            /* LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in disguise) */
+       {"WCI0003"},            /* Rockwell's (PORALiNK) 33600 INT PNP */
+       {"WEC1022"},            /* Winbond CIR port, should not be probed. We should keep track of it to prevent the legacy serial driver from probing it */
+       /* scl200wdt */
+       {"NSC0800"},            /* National Semiconductor PC87307/PC97307 watchdog component */
+       /* mpu401 */
+       {"PNPb006"},
+       /* cs423x-pnpbios */
+       {"CSC0100"},
+       {"CSC0000"},
+       {"GIM0100"},            /* Guillemot Turtlebeach something appears to be cs4232 compatible */
+       /* es18xx-pnpbios */
+       {"ESS1869"},
+       {"ESS1879"},
+       /* snd-opl3sa2-pnpbios */
+       {"YMH0021"},
+       {"NMX2210"},            /* Gateway Solo 2500 */
+       {""},
+};
+
+static bool is_hex_digit(char c)
+{
+       return (c >= 0 && c <= '9') || (c >= 'A' && c <= 'F');
+}
+
+static bool matching_id(char *idstr, char *list_id)
+{
+       int i;
+
+       if (memcmp(idstr, list_id, 3))
+               return false;
+
+       for (i = 3; i < 7; i++) {
+               char c = toupper(idstr[i]);
+
+               if (!is_hex_digit(c)
+                   || (list_id[i] != 'X' && c != toupper(list_id[i])))
+                       return false;
+       }
+       return true;
+}
+
+static bool acpi_pnp_match(char *idstr, const struct acpi_device_id **matchid)
+{
+       const struct acpi_device_id *devid;
+
+       for (devid = acpi_pnp_device_ids; devid->id[0]; devid++)
+               if (matching_id(idstr, (char *)devid->id)) {
+                       if (matchid)
+                               *matchid = devid;
+
+                       return true;
+               }
+
+       return false;
+}
+
+static int acpi_pnp_attach(struct acpi_device *adev,
+                          const struct acpi_device_id *id)
+{
+       return 1;
+}
+
+static struct acpi_scan_handler acpi_pnp_handler = {
+       .ids = acpi_pnp_device_ids,
+       .match = acpi_pnp_match,
+       .attach = acpi_pnp_attach,
+};
+
+/*
+ * For CMOS RTC devices, the PNP ACPI scan handler does not work, because
+ * there is a CMOS RTC ACPI scan handler installed already, so we need to
+ * check those devices and enumerate them to the PNP bus directly.
+ */
+static int is_cmos_rtc_device(struct acpi_device *adev)
+{
+       struct acpi_device_id ids[] = {
+               { "PNP0B00" },
+               { "PNP0B01" },
+               { "PNP0B02" },
+               {""},
+       };
+       return !acpi_match_device_ids(adev, ids);
+}
+
+bool acpi_is_pnp_device(struct acpi_device *adev)
+{
+       return adev->handler == &acpi_pnp_handler || is_cmos_rtc_device(adev);
+}
+EXPORT_SYMBOL_GPL(acpi_is_pnp_device);
+
+void __init acpi_pnp_init(void)
+{
+       acpi_scan_add_handler(&acpi_pnp_handler);
+}
index 52c81c4..1c08574 100644 (file)
@@ -268,7 +268,7 @@ static int acpi_processor_get_info(struct acpi_device *device)
        pr->apic_id = apic_id;
 
        cpu_index = acpi_map_cpuid(pr->apic_id, pr->acpi_id);
-       if (!cpu0_initialized) {
+       if (!cpu0_initialized && !acpi_lapic) {
                cpu0_initialized = 1;
                /* Handle UP system running SMP kernel, with no LAPIC in MADT */
                if ((cpu_index == -1) && (num_online_cpus() == 1))
index b7ed86a..8bb43f0 100644 (file)
@@ -135,6 +135,7 @@ acpi-y +=           \
        rsxface.o
 
 acpi-y +=              \
+       tbdata.o        \
        tbfadt.o        \
        tbfind.o        \
        tbinstal.o      \
diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h
new file mode 100644 (file)
index 0000000..8698ffb
--- /dev/null
@@ -0,0 +1,170 @@
+/******************************************************************************
+ *
+ * Module Name: acapps - common include for ACPI applications/tools
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef _ACAPPS
+#define _ACAPPS
+
+/* Common info for tool signons */
+
+#define ACPICA_NAME                 "Intel ACPI Component Architecture"
+#define ACPICA_COPYRIGHT            "Copyright (c) 2000 - 2014 Intel Corporation"
+
+#if ACPI_MACHINE_WIDTH == 64
+#define ACPI_WIDTH          "-64"
+
+#elif ACPI_MACHINE_WIDTH == 32
+#define ACPI_WIDTH          "-32"
+
+#else
+#error unknown ACPI_MACHINE_WIDTH
+#define ACPI_WIDTH          "-??"
+
+#endif
+
+/* Macros for signons and file headers */
+
+#define ACPI_COMMON_SIGNON(utility_name) \
+       "\n%s\n%s version %8.8X%s [%s]\n%s\n\n", \
+       ACPICA_NAME, \
+       utility_name, ((u32) ACPI_CA_VERSION), ACPI_WIDTH, __DATE__, \
+       ACPICA_COPYRIGHT
+
+#define ACPI_COMMON_HEADER(utility_name, prefix) \
+       "%s%s\n%s%s version %8.8X%s [%s]\n%s%s\n%s\n", \
+       prefix, ACPICA_NAME, \
+       prefix, utility_name, ((u32) ACPI_CA_VERSION), ACPI_WIDTH, __DATE__, \
+       prefix, ACPICA_COPYRIGHT, \
+       prefix
+
+/* Macros for usage messages */
+
+#define ACPI_USAGE_HEADER(usage) \
+       printf ("Usage: %s\nOptions:\n", usage);
+
+#define ACPI_OPTION(name, description) \
+       printf ("  %-18s%s\n", name, description);
+
+#define FILE_SUFFIX_DISASSEMBLY     "dsl"
+#define ACPI_TABLE_FILE_SUFFIX      ".dat"
+
+/*
+ * getopt
+ */
+int acpi_getopt(int argc, char **argv, char *opts);
+
+int acpi_getopt_argument(int argc, char **argv);
+
+extern int acpi_gbl_optind;
+extern int acpi_gbl_opterr;
+extern int acpi_gbl_sub_opt_char;
+extern char *acpi_gbl_optarg;
+
+/*
+ * cmfsize - Common get file size function
+ */
+u32 cm_get_file_size(FILE * file);
+
+#ifndef ACPI_DUMP_APP
+/*
+ * adisasm
+ */
+acpi_status
+ad_aml_disassemble(u8 out_to_file,
+                  char *filename, char *prefix, char **out_filename);
+
+void ad_print_statistics(void);
+
+acpi_status ad_find_dsdt(u8 **dsdt_ptr, u32 *dsdt_length);
+
+void ad_dump_tables(void);
+
+acpi_status ad_get_local_tables(void);
+
+acpi_status
+ad_parse_table(struct acpi_table_header *table,
+              acpi_owner_id * owner_id, u8 load_table, u8 external);
+
+acpi_status ad_display_tables(char *filename, struct acpi_table_header *table);
+
+acpi_status ad_display_statistics(void);
+
+/*
+ * adwalk
+ */
+void
+acpi_dm_cross_reference_namespace(union acpi_parse_object *parse_tree_root,
+                                 struct acpi_namespace_node *namespace_root,
+                                 acpi_owner_id owner_id);
+
+void acpi_dm_dump_tree(union acpi_parse_object *origin);
+
+void acpi_dm_find_orphan_methods(union acpi_parse_object *origin);
+
+void
+acpi_dm_finish_namespace_load(union acpi_parse_object *parse_tree_root,
+                             struct acpi_namespace_node *namespace_root,
+                             acpi_owner_id owner_id);
+
+void
+acpi_dm_convert_resource_indexes(union acpi_parse_object *parse_tree_root,
+                                struct acpi_namespace_node *namespace_root);
+
+/*
+ * adfile
+ */
+acpi_status ad_initialize(void);
+
+char *fl_generate_filename(char *input_filename, char *suffix);
+
+acpi_status
+fl_split_input_pathname(char *input_path,
+                       char **out_directory_path, char **out_filename);
+
+char *ad_generate_filename(char *prefix, char *table_id);
+
+void
+ad_write_table(struct acpi_table_header *table,
+              u32 length, char *table_name, char *oem_table_id);
+#endif
+
+#endif                         /* _ACAPPS */
index 68ec61f..7a7811a 100644 (file)
@@ -104,9 +104,10 @@ acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info);
  */
 acpi_status
 acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
-                        struct acpi_generic_address *gpe_block_address,
+                        u64 address,
+                        u8 space_id,
                         u32 register_count,
-                        u8 gpe_block_base_number,
+                        u16 gpe_block_base_number,
                         u32 interrupt_number,
                         struct acpi_gpe_block_info **return_gpe_block);
 
index a08a448..115eedc 100644 (file)
 #ifndef __ACGLOBAL_H__
 #define __ACGLOBAL_H__
 
-/*
- * Ensure that the globals are actually defined and initialized only once.
- *
- * The use of these macros allows a single list of globals (here) in order
- * to simplify maintenance of the code.
- */
-#ifdef DEFINE_ACPI_GLOBALS
-#define ACPI_GLOBAL(type,name) \
-       extern type name; \
-       type name
-
-#define ACPI_INIT_GLOBAL(type,name,value) \
-       type name=value
-
-#else
-#define ACPI_GLOBAL(type,name) \
-       extern type name
-
-#define ACPI_INIT_GLOBAL(type,name,value) \
-       extern type name
-#endif
-
-#ifdef DEFINE_ACPI_GLOBALS
-
-/* Public globals, available from outside ACPICA subsystem */
-
 /*****************************************************************************
  *
- * Runtime configuration (static defaults that can be overriden at runtime)
+ * Globals related to the ACPI tables
  *
  ****************************************************************************/
 
-/*
- * Enable "slack" in the AML interpreter?  Default is FALSE, and the
- * interpreter strictly follows the ACPI specification. Setting to TRUE
- * allows the interpreter to ignore certain errors and/or bad AML constructs.
- *
- * Currently, these features are enabled by this flag:
- *
- * 1) Allow "implicit return" of last value in a control method
- * 2) Allow access beyond the end of an operation region
- * 3) Allow access to uninitialized locals/args (auto-init to integer 0)
- * 4) Allow ANY object type to be a source operand for the Store() operator
- * 5) Allow unresolved references (invalid target name) in package objects
- * 6) Enable warning messages for behavior that is not ACPI spec compliant
- */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_enable_interpreter_slack, FALSE);
+/* Master list of all ACPI tables that were found in the RSDT/XSDT */
 
-/*
- * Automatically serialize all methods that create named objects? Default
- * is TRUE, meaning that all non_serialized methods are scanned once at
- * table load time to determine those that create named objects. Methods
- * that create named objects are marked Serialized in order to prevent
- * possible run-time problems if they are entered by more than one thread.
- */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_auto_serialize_methods, TRUE);
-
-/*
- * Create the predefined _OSI method in the namespace? Default is TRUE
- * because ACPI CA is fully compatible with other ACPI implementations.
- * Changing this will revert ACPI CA (and machine ASL) to pre-OSI behavior.
- */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_create_osi_method, TRUE);
-
-/*
- * Optionally use default values for the ACPI register widths. Set this to
- * TRUE to use the defaults, if an FADT contains incorrect widths/lengths.
- */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_use_default_register_widths, TRUE);
-
-/*
- * Optionally enable output from the AML Debug Object.
- */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_enable_aml_debug_object, FALSE);
-
-/*
- * Optionally copy the entire DSDT to local memory (instead of simply
- * mapping it.) There are some BIOSs that corrupt or replace the original
- * DSDT, creating the need for this option. Default is FALSE, do not copy
- * the DSDT.
- */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_copy_dsdt_locally, FALSE);
-
-/*
- * Optionally ignore an XSDT if present and use the RSDT instead.
- * Although the ACPI specification requires that an XSDT be used instead
- * of the RSDT, the XSDT has been found to be corrupt or ill-formed on
- * some machines. Default behavior is to use the XSDT if present.
- */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE);
-
-/*
- * Optionally use 32-bit FADT addresses if and when there is a conflict
- * (address mismatch) between the 32-bit and 64-bit versions of the
- * address. Although ACPICA adheres to the ACPI specification which
- * requires the use of the corresponding 64-bit address if it is non-zero,
- * some machines have been found to have a corrupted non-zero 64-bit
- * address. Default is TRUE, favor the 32-bit addresses.
- */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_use32_bit_fadt_addresses, TRUE);
-
-/*
- * Optionally truncate I/O addresses to 16 bits. Provides compatibility
- * with other ACPI implementations. NOTE: During ACPICA initialization,
- * this value is set to TRUE if any Windows OSI strings have been
- * requested by the BIOS.
- */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_truncate_io_addresses, FALSE);
-
-/*
- * Disable runtime checking and repair of values returned by control methods.
- * Use only if the repair is causing a problem on a particular machine.
- */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_auto_repair, FALSE);
-
-/*
- * Optionally do not load any SSDTs from the RSDT/XSDT during initialization.
- * This can be useful for debugging ACPI problems on some machines.
- */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_ssdt_table_load, FALSE);
-
-/*
- * We keep track of the latest version of Windows that has been requested by
- * the BIOS.
- */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_osi_data, 0);
-
-#endif                         /* DEFINE_ACPI_GLOBALS */
-
-/*****************************************************************************
- *
- * ACPI Table globals
- *
- ****************************************************************************/
-
-/*
- * Master list of all ACPI tables that were found in the RSDT/XSDT.
- */
 ACPI_GLOBAL(struct acpi_table_list, acpi_gbl_root_table_list);
 
 /* DSDT information. Used to check for DSDT corruption */
@@ -279,7 +149,6 @@ ACPI_GLOBAL(acpi_exception_handler, acpi_gbl_exception_handler);
 ACPI_GLOBAL(acpi_init_handler, acpi_gbl_init_handler);
 ACPI_GLOBAL(acpi_table_handler, acpi_gbl_table_handler);
 ACPI_GLOBAL(void *, acpi_gbl_table_handler_context);
-ACPI_GLOBAL(struct acpi_walk_state *, acpi_gbl_breakpoint_walk);
 ACPI_GLOBAL(acpi_interface_handler, acpi_gbl_interface_handler);
 ACPI_GLOBAL(struct acpi_sci_handler_info *, acpi_gbl_sci_handler_list);
 
@@ -296,7 +165,6 @@ ACPI_GLOBAL(u8, acpi_gbl_reg_methods_executed);
 /* Misc */
 
 ACPI_GLOBAL(u32, acpi_gbl_original_mode);
-ACPI_GLOBAL(u32, acpi_gbl_rsdp_original_location);
 ACPI_GLOBAL(u32, acpi_gbl_ns_lookup_count);
 ACPI_GLOBAL(u32, acpi_gbl_ps_find_count);
 ACPI_GLOBAL(u16, acpi_gbl_pm1_enable_register_save);
@@ -483,11 +351,6 @@ ACPI_GLOBAL(u16, acpi_gbl_node_type_count_misc);
 ACPI_GLOBAL(u32, acpi_gbl_num_nodes);
 ACPI_GLOBAL(u32, acpi_gbl_num_objects);
 
-ACPI_GLOBAL(u32, acpi_gbl_size_of_parse_tree);
-ACPI_GLOBAL(u32, acpi_gbl_size_of_method_trees);
-ACPI_GLOBAL(u32, acpi_gbl_size_of_node_entries);
-ACPI_GLOBAL(u32, acpi_gbl_size_of_acpi_objects);
-
 #endif                         /* ACPI_DEBUGGER */
 
 /*****************************************************************************
@@ -509,5 +372,6 @@ ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_debug_file, NULL);
  ****************************************************************************/
 
 extern const struct ah_predefined_name asl_predefined_info[];
+extern const struct ah_device_id asl_device_ids[];
 
 #endif                         /* __ACGLOBAL_H__ */
index 52a21da..91f801a 100644 (file)
@@ -450,9 +450,9 @@ struct acpi_gpe_event_info {
 struct acpi_gpe_register_info {
        struct acpi_generic_address status_address;     /* Address of status reg */
        struct acpi_generic_address enable_address;     /* Address of enable reg */
+       u16 base_gpe_number;    /* Base GPE number for this register */
        u8 enable_for_wake;     /* GPEs to keep enabled when sleeping */
        u8 enable_for_run;      /* GPEs to keep enabled when running */
-       u8 base_gpe_number;     /* Base GPE number for this register */
 };
 
 /*
@@ -466,11 +466,12 @@ struct acpi_gpe_block_info {
        struct acpi_gpe_xrupt_info *xrupt_block;        /* Backpointer to interrupt block */
        struct acpi_gpe_register_info *register_info;   /* One per GPE register pair */
        struct acpi_gpe_event_info *event_info; /* One for each GPE */
-       struct acpi_generic_address block_address;      /* Base address of the block */
+       u64 address;            /* Base address of the block */
        u32 register_count;     /* Number of register pairs in block */
        u16 gpe_count;          /* Number of individual GPEs in block */
-       u8 block_base_number;   /* Base GPE number for this block */
-       u8 initialized;         /* TRUE if this block is initialized */
+       u16 block_base_number;  /* Base GPE number for this block */
+       u8 space_id;
+       u8 initialized;         /* TRUE if this block is initialized */
 };
 
 /* Information about GPE interrupt handlers, one per each interrupt level used for GPEs */
@@ -733,7 +734,8 @@ union acpi_parse_value {
 #define ACPI_DASM_MATCHOP               0x06   /* Parent opcode is a Match() operator */
 #define ACPI_DASM_LNOT_PREFIX           0x07   /* Start of a Lnot_equal (etc.) pair of opcodes */
 #define ACPI_DASM_LNOT_SUFFIX           0x08   /* End  of a Lnot_equal (etc.) pair of opcodes */
-#define ACPI_DASM_IGNORE                0x09   /* Not used at this time */
+#define ACPI_DASM_HID_STRING            0x09   /* String is a _HID or _CID */
+#define ACPI_DASM_IGNORE                0x0A   /* Not used at this time */
 
 /*
  * Generic operation (for example:  If, While, Store)
@@ -1147,4 +1149,9 @@ struct ah_predefined_name {
 #endif
 };
 
+struct ah_device_id {
+       char *name;
+       char *description;
+};
+
 #endif                         /* __ACLOCAL_H__ */
index a48d713..bd08817 100644 (file)
@@ -586,6 +586,10 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
        {{"_LID", METHOD_0ARGS,
          METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
 
+       {{"_LPD", METHOD_0ARGS,
+         METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(rev), n Pkg (2 Int) */
+       PACKAGE_INFO(ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_INTEGER, 2, 0, 0, 0),
+
        {{"_MAT", METHOD_0ARGS,
          METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
 
@@ -698,12 +702,6 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
          METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
        PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
 
-       {{"_PRP", METHOD_0ARGS,
-         METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each: 1 Str, 1 Int/Str/Pkg */
-       PACKAGE_INFO(ACPI_PTYPE2, ACPI_RTYPE_STRING, 1,
-                    ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING |
-                    ACPI_RTYPE_PACKAGE | ACPI_RTYPE_REFERENCE, 1, 0),
-
        {{"_PRS", METHOD_0ARGS,
          METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
 
index 5fa4b20..f148827 100644 (file)
@@ -53,6 +53,31 @@ acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp);
 
 u8 *acpi_tb_scan_memory_for_rsdp(u8 *start_address, u32 length);
 
+/*
+ * tbdata - table data structure management
+ */
+acpi_status acpi_tb_get_next_root_index(u32 *table_index);
+
+void
+acpi_tb_init_table_descriptor(struct acpi_table_desc *table_desc,
+                             acpi_physical_address address,
+                             u8 flags, struct acpi_table_header *table);
+
+acpi_status
+acpi_tb_acquire_temp_table(struct acpi_table_desc *table_desc,
+                          acpi_physical_address address, u8 flags);
+
+void acpi_tb_release_temp_table(struct acpi_table_desc *table_desc);
+
+acpi_status acpi_tb_validate_temp_table(struct acpi_table_desc *table_desc);
+
+acpi_status
+acpi_tb_verify_temp_table(struct acpi_table_desc *table_desc, char *signature);
+
+u8 acpi_tb_is_table_loaded(u32 table_index);
+
+void acpi_tb_set_table_loaded_flag(u32 table_index, u8 is_loaded);
+
 /*
  * tbfadt - FADT parse/convert/validate
  */
@@ -72,22 +97,32 @@ acpi_tb_find_table(char *signature,
  */
 acpi_status acpi_tb_resize_root_table_list(void);
 
-acpi_status acpi_tb_verify_table(struct acpi_table_desc *table_desc);
+acpi_status acpi_tb_validate_table(struct acpi_table_desc *table_desc);
+
+void acpi_tb_invalidate_table(struct acpi_table_desc *table_desc);
+
+void acpi_tb_override_table(struct acpi_table_desc *old_table_desc);
 
-struct acpi_table_header *acpi_tb_table_override(struct acpi_table_header
-                                                *table_header,
-                                                struct acpi_table_desc
-                                                *table_desc);
+acpi_status
+acpi_tb_acquire_table(struct acpi_table_desc *table_desc,
+                     struct acpi_table_header **table_ptr,
+                     u32 *table_length, u8 *table_flags);
+
+void
+acpi_tb_release_table(struct acpi_table_header *table,
+                     u32 table_length, u8 table_flags);
 
 acpi_status
-acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index);
+acpi_tb_install_standard_table(acpi_physical_address address,
+                              u8 flags,
+                              u8 reload, u8 override, u32 *table_index);
 
 acpi_status
 acpi_tb_store_table(acpi_physical_address address,
                    struct acpi_table_header *table,
                    u32 length, u8 flags, u32 *table_index);
 
-void acpi_tb_delete_table(struct acpi_table_desc *table_desc);
+void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc);
 
 void acpi_tb_terminate(void);
 
@@ -99,10 +134,6 @@ acpi_status acpi_tb_release_owner_id(u32 table_index);
 
 acpi_status acpi_tb_get_owner_id(u32 table_index, acpi_owner_id *owner_id);
 
-u8 acpi_tb_is_table_loaded(u32 table_index);
-
-void acpi_tb_set_table_loaded_flag(u32 table_index, u8 is_loaded);
-
 /*
  * tbutils - table manager utilities
  */
@@ -124,8 +155,13 @@ void acpi_tb_check_dsdt_header(void);
 struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index);
 
 void
-acpi_tb_install_table(acpi_physical_address address,
-                     char *signature, u32 table_index);
+acpi_tb_install_table_with_override(u32 table_index,
+                                   struct acpi_table_desc *new_table_desc,
+                                   u8 override);
+
+acpi_status
+acpi_tb_install_fixed_table(acpi_physical_address address,
+                           char *signature, u32 table_index);
 
 acpi_status acpi_tb_parse_root_table(acpi_physical_address rsdp_address);
 
index ceeec0b..1e256c5 100644 (file)
@@ -176,8 +176,7 @@ acpi_status acpi_ut_init_globals(void);
 
 char *acpi_ut_get_mutex_name(u32 mutex_id);
 
-const char *acpi_ut_get_notify_name(u32 notify_value);
-
+const char *acpi_ut_get_notify_name(u32 notify_value, acpi_object_type type);
 #endif
 
 char *acpi_ut_get_type_name(acpi_object_type type);
@@ -737,4 +736,11 @@ acpi_ut_method_error(const char *module_name,
                     struct acpi_namespace_node *node,
                     const char *path, acpi_status lookup_status);
 
+/*
+ * Utility functions for ACPI names and IDs
+ */
+const struct ah_predefined_name *acpi_ah_match_predefined_name(char *nameseg);
+
+const struct ah_device_id *acpi_ah_match_hardware_id(char *hid);
+
 #endif                         /* _ACUTILS_H */
index 955f83d..48f7001 100644 (file)
@@ -383,7 +383,7 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list)
                        if (!(gpe_register_info->enable_for_run |
                              gpe_register_info->enable_for_wake)) {
                                ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS,
-                                                 "Ignore disabled registers for GPE%02X-GPE%02X: "
+                                                 "Ignore disabled registers for GPE %02X-%02X: "
                                                  "RunEnable=%02X, WakeEnable=%02X\n",
                                                  gpe_register_info->
                                                  base_gpe_number,
@@ -416,7 +416,7 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list)
                        }
 
                        ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS,
-                                         "Read registers for GPE%02X-GPE%02X: Status=%02X, Enable=%02X, "
+                                         "Read registers for GPE %02X-%02X: Status=%02X, Enable=%02X, "
                                          "RunEnable=%02X, WakeEnable=%02X\n",
                                          gpe_register_info->base_gpe_number,
                                          gpe_register_info->base_gpe_number +
@@ -706,7 +706,8 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
                status = acpi_hw_clear_gpe(gpe_event_info);
                if (ACPI_FAILURE(status)) {
                        ACPI_EXCEPTION((AE_INFO, status,
-                                       "Unable to clear GPE%02X", gpe_number));
+                                       "Unable to clear GPE %02X",
+                                       gpe_number));
                        return_UINT32(ACPI_INTERRUPT_NOT_HANDLED);
                }
        }
@@ -723,7 +724,7 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
        status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
        if (ACPI_FAILURE(status)) {
                ACPI_EXCEPTION((AE_INFO, status,
-                               "Unable to disable GPE%02X", gpe_number));
+                               "Unable to disable GPE %02X", gpe_number));
                return_UINT32(ACPI_INTERRUPT_NOT_HANDLED);
        }
 
@@ -764,7 +765,7 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
                                         gpe_event_info);
                if (ACPI_FAILURE(status)) {
                        ACPI_EXCEPTION((AE_INFO, status,
-                                       "Unable to queue handler for GPE%02X - event disabled",
+                                       "Unable to queue handler for GPE %02X - event disabled",
                                        gpe_number));
                }
                break;
@@ -776,7 +777,7 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
                 * a GPE to be enabled if it has no handler or method.
                 */
                ACPI_ERROR((AE_INFO,
-                           "No handler or method for GPE%02X, disabling event",
+                           "No handler or method for GPE %02X, disabling event",
                            gpe_number));
 
                break;
index caaed3c..d86699e 100644 (file)
@@ -252,21 +252,17 @@ acpi_ev_create_gpe_info_blocks(struct acpi_gpe_block_info *gpe_block)
 
                /* Init the register_info for this GPE register (8 GPEs) */
 
-               this_register->base_gpe_number =
-                   (u8) (gpe_block->block_base_number +
-                         (i * ACPI_GPE_REGISTER_WIDTH));
+               this_register->base_gpe_number = (u16)
+                   (gpe_block->block_base_number +
+                    (i * ACPI_GPE_REGISTER_WIDTH));
 
-               this_register->status_address.address =
-                   gpe_block->block_address.address + i;
+               this_register->status_address.address = gpe_block->address + i;
 
                this_register->enable_address.address =
-                   gpe_block->block_address.address + i +
-                   gpe_block->register_count;
+                   gpe_block->address + i + gpe_block->register_count;
 
-               this_register->status_address.space_id =
-                   gpe_block->block_address.space_id;
-               this_register->enable_address.space_id =
-                   gpe_block->block_address.space_id;
+               this_register->status_address.space_id = gpe_block->space_id;
+               this_register->enable_address.space_id = gpe_block->space_id;
                this_register->status_address.bit_width =
                    ACPI_GPE_REGISTER_WIDTH;
                this_register->enable_address.bit_width =
@@ -334,9 +330,10 @@ error_exit:
 
 acpi_status
 acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
-                        struct acpi_generic_address *gpe_block_address,
+                        u64 address,
+                        u8 space_id,
                         u32 register_count,
-                        u8 gpe_block_base_number,
+                        u16 gpe_block_base_number,
                         u32 interrupt_number,
                         struct acpi_gpe_block_info **return_gpe_block)
 {
@@ -359,15 +356,14 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
 
        /* Initialize the new GPE block */
 
+       gpe_block->address = address;
+       gpe_block->space_id = space_id;
        gpe_block->node = gpe_device;
        gpe_block->gpe_count = (u16)(register_count * ACPI_GPE_REGISTER_WIDTH);
        gpe_block->initialized = FALSE;
        gpe_block->register_count = register_count;
        gpe_block->block_base_number = gpe_block_base_number;
 
-       ACPI_MEMCPY(&gpe_block->block_address, gpe_block_address,
-                   sizeof(struct acpi_generic_address));
-
        /*
         * Create the register_info and event_info sub-structures
         * Note: disables and clears all GPEs in the block
@@ -408,12 +404,14 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
        }
 
        ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
-                             "    Initialized GPE %02X to %02X [%4.4s] %u regs on interrupt 0x%X\n",
+                             "    Initialized GPE %02X to %02X [%4.4s] %u regs on interrupt 0x%X%s\n",
                              (u32)gpe_block->block_base_number,
                              (u32)(gpe_block->block_base_number +
                                    (gpe_block->gpe_count - 1)),
                              gpe_device->name.ascii, gpe_block->register_count,
-                             interrupt_number));
+                             interrupt_number,
+                             interrupt_number ==
+                             acpi_gbl_FADT.sci_interrupt ? " (SCI)" : ""));
 
        /* Update global count of currently available GPEs */
 
index ae779c1..49fc7ef 100644 (file)
@@ -131,8 +131,10 @@ acpi_status acpi_ev_gpe_initialize(void)
                /* Install GPE Block 0 */
 
                status = acpi_ev_create_gpe_block(acpi_gbl_fadt_gpe_device,
-                                                 &acpi_gbl_FADT.xgpe0_block,
-                                                 register_count0, 0,
+                                                 acpi_gbl_FADT.xgpe0_block.
+                                                 address,
+                                                 acpi_gbl_FADT.xgpe0_block.
+                                                 space_id, register_count0, 0,
                                                  acpi_gbl_FADT.sci_interrupt,
                                                  &acpi_gbl_gpe_fadt_blocks[0]);
 
@@ -169,8 +171,10 @@ acpi_status acpi_ev_gpe_initialize(void)
 
                        status =
                            acpi_ev_create_gpe_block(acpi_gbl_fadt_gpe_device,
-                                                    &acpi_gbl_FADT.xgpe1_block,
-                                                    register_count1,
+                                                    acpi_gbl_FADT.xgpe1_block.
+                                                    address,
+                                                    acpi_gbl_FADT.xgpe1_block.
+                                                    space_id, register_count1,
                                                     acpi_gbl_FADT.gpe1_base,
                                                     acpi_gbl_FADT.
                                                     sci_interrupt,
index 5d594eb..24ea342 100644 (file)
@@ -167,7 +167,8 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node,
                          "Dispatching Notify on [%4.4s] (%s) Value 0x%2.2X (%s) Node %p\n",
                          acpi_ut_get_node_name(node),
                          acpi_ut_get_type_name(node->type), notify_value,
-                         acpi_ut_get_notify_name(notify_value), node));
+                         acpi_ut_get_notify_name(notify_value, ACPI_TYPE_ANY),
+                         node));
 
        status = acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_ev_notify_dispatch,
                                 info);
index 4d8a709..29630e3 100644 (file)
@@ -117,7 +117,7 @@ static u32 ACPI_SYSTEM_XFACE acpi_ev_sci_xrupt_handler(void *context)
        ACPI_FUNCTION_TRACE(ev_sci_xrupt_handler);
 
        /*
-        * We are guaranteed by the ACPI CA initialization/shutdown code that
+        * We are guaranteed by the ACPICA initialization/shutdown code that
         * if this interrupt handler is installed, ACPI is enabled.
         */
 
index a734b27..11e5803 100644 (file)
@@ -239,7 +239,7 @@ acpi_remove_notify_handler(acpi_handle device,
        union acpi_operand_object *obj_desc;
        union acpi_operand_object *handler_obj;
        union acpi_operand_object *previous_handler_obj;
-       acpi_status status;
+       acpi_status status = AE_OK;
        u32 i;
 
        ACPI_FUNCTION_TRACE(acpi_remove_notify_handler);
@@ -251,20 +251,17 @@ acpi_remove_notify_handler(acpi_handle device,
                return_ACPI_STATUS(AE_BAD_PARAMETER);
        }
 
-       /* Make sure all deferred notify tasks are completed */
-
-       acpi_os_wait_events_complete();
-
-       status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
-       if (ACPI_FAILURE(status)) {
-               return_ACPI_STATUS(status);
-       }
-
        /* Root Object. Global handlers are removed here */
 
        if (device == ACPI_ROOT_OBJECT) {
                for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) {
                        if (handler_type & (i + 1)) {
+                               status =
+                                   acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+                               if (ACPI_FAILURE(status)) {
+                                       return_ACPI_STATUS(status);
+                               }
+
                                if (!acpi_gbl_global_notify[i].handler ||
                                    (acpi_gbl_global_notify[i].handler !=
                                     handler)) {
@@ -277,31 +274,40 @@ acpi_remove_notify_handler(acpi_handle device,
 
                                acpi_gbl_global_notify[i].handler = NULL;
                                acpi_gbl_global_notify[i].context = NULL;
+
+                               (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+                               /* Make sure all deferred notify tasks are completed */
+
+                               acpi_os_wait_events_complete();
                        }
                }
 
-               goto unlock_and_exit;
+               return_ACPI_STATUS(AE_OK);
        }
 
        /* All other objects: Are Notifies allowed on this object? */
 
        if (!acpi_ev_is_notify_object(node)) {
-               status = AE_TYPE;
-               goto unlock_and_exit;
+               return_ACPI_STATUS(AE_TYPE);
        }
 
        /* Must have an existing internal object */
 
        obj_desc = acpi_ns_get_attached_object(node);
        if (!obj_desc) {
-               status = AE_NOT_EXIST;
-               goto unlock_and_exit;
+               return_ACPI_STATUS(AE_NOT_EXIST);
        }
 
        /* Internal object exists. Find the handler and remove it */
 
        for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) {
                if (handler_type & (i + 1)) {
+                       status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+                       if (ACPI_FAILURE(status)) {
+                               return_ACPI_STATUS(status);
+                       }
+
                        handler_obj = obj_desc->common_notify.notify_list[i];
                        previous_handler_obj = NULL;
 
@@ -329,10 +335,17 @@ acpi_remove_notify_handler(acpi_handle device,
                                    handler_obj->notify.next[i];
                        }
 
+                       (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+                       /* Make sure all deferred notify tasks are completed */
+
+                       acpi_os_wait_events_complete();
                        acpi_ut_remove_reference(handler_obj);
                }
        }
 
+       return_ACPI_STATUS(status);
+
 unlock_and_exit:
        (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
        return_ACPI_STATUS(status);
@@ -457,6 +470,8 @@ exit:
        return_ACPI_STATUS(status);
 }
 
+ACPI_EXPORT_SYMBOL(acpi_install_sci_handler)
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_remove_sci_handler
@@ -468,7 +483,6 @@ exit:
  * DESCRIPTION: Remove a handler for a System Control Interrupt.
  *
  ******************************************************************************/
-
 acpi_status acpi_remove_sci_handler(acpi_sci_handler address)
 {
        struct acpi_sci_handler_info *prev_sci_handler;
@@ -522,6 +536,8 @@ unlock_and_exit:
        return_ACPI_STATUS(status);
 }
 
+ACPI_EXPORT_SYMBOL(acpi_remove_sci_handler)
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_install_global_event_handler
@@ -537,7 +553,6 @@ unlock_and_exit:
  *              Can be used to update event counters, etc.
  *
  ******************************************************************************/
-
 acpi_status
 acpi_install_global_event_handler(acpi_gbl_event_handler handler, void *context)
 {
@@ -840,10 +855,6 @@ acpi_remove_gpe_handler(acpi_handle gpe_device,
                return_ACPI_STATUS(AE_BAD_PARAMETER);
        }
 
-       /* Make sure all deferred GPE tasks are completed */
-
-       acpi_os_wait_events_complete();
-
        status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
        if (ACPI_FAILURE(status)) {
                return_ACPI_STATUS(status);
@@ -895,9 +906,17 @@ acpi_remove_gpe_handler(acpi_handle gpe_device,
                (void)acpi_ev_add_gpe_reference(gpe_event_info);
        }
 
+       acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+       (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+
+       /* Make sure all deferred GPE tasks are completed */
+
+       acpi_os_wait_events_complete();
+
        /* Now we can free the handler object */
 
        ACPI_FREE(handler);
+       return_ACPI_STATUS(status);
 
 unlock_and_exit:
        acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
index 20a1392..cb534fa 100644 (file)
@@ -599,9 +599,10 @@ acpi_install_gpe_block(acpi_handle gpe_device,
         * For user-installed GPE Block Devices, the gpe_block_base_number
         * is always zero
         */
-       status =
-           acpi_ev_create_gpe_block(node, gpe_block_address, register_count, 0,
-                                    interrupt_number, &gpe_block);
+       status = acpi_ev_create_gpe_block(node, gpe_block_address->address,
+                                         gpe_block_address->space_id,
+                                         register_count, 0, interrupt_number,
+                                         &gpe_block);
        if (ACPI_FAILURE(status)) {
                goto unlock_and_exit;
        }
index 8ba1464..7d29494 100644 (file)
@@ -343,16 +343,14 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
                struct acpi_walk_state *walk_state)
 {
        union acpi_operand_object *ddb_handle;
+       struct acpi_table_header *table_header;
        struct acpi_table_header *table;
-       struct acpi_table_desc table_desc;
        u32 table_index;
        acpi_status status;
        u32 length;
 
        ACPI_FUNCTION_TRACE(ex_load_op);
 
-       ACPI_MEMSET(&table_desc, 0, sizeof(struct acpi_table_desc));
-
        /* Source Object can be either an op_region or a Buffer/Field */
 
        switch (obj_desc->common.type) {
@@ -380,17 +378,17 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
 
                /* Get the table header first so we can get the table length */
 
-               table = ACPI_ALLOCATE(sizeof(struct acpi_table_header));
-               if (!table) {
+               table_header = ACPI_ALLOCATE(sizeof(struct acpi_table_header));
+               if (!table_header) {
                        return_ACPI_STATUS(AE_NO_MEMORY);
                }
 
                status =
                    acpi_ex_region_read(obj_desc,
                                        sizeof(struct acpi_table_header),
-                                       ACPI_CAST_PTR(u8, table));
-               length = table->length;
-               ACPI_FREE(table);
+                                       ACPI_CAST_PTR(u8, table_header));
+               length = table_header->length;
+               ACPI_FREE(table_header);
 
                if (ACPI_FAILURE(status)) {
                        return_ACPI_STATUS(status);
@@ -420,22 +418,19 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
 
                /* Allocate a buffer for the table */
 
-               table_desc.pointer = ACPI_ALLOCATE(length);
-               if (!table_desc.pointer) {
+               table = ACPI_ALLOCATE(length);
+               if (!table) {
                        return_ACPI_STATUS(AE_NO_MEMORY);
                }
 
                /* Read the entire table */
 
                status = acpi_ex_region_read(obj_desc, length,
-                                            ACPI_CAST_PTR(u8,
-                                                          table_desc.pointer));
+                                            ACPI_CAST_PTR(u8, table));
                if (ACPI_FAILURE(status)) {
-                       ACPI_FREE(table_desc.pointer);
+                       ACPI_FREE(table);
                        return_ACPI_STATUS(status);
                }
-
-               table_desc.address = obj_desc->region.address;
                break;
 
        case ACPI_TYPE_BUFFER:  /* Buffer or resolved region_field */
@@ -452,10 +447,10 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
 
                /* Get the actual table length from the table header */
 
-               table =
+               table_header =
                    ACPI_CAST_PTR(struct acpi_table_header,
                                  obj_desc->buffer.pointer);
-               length = table->length;
+               length = table_header->length;
 
                /* Table cannot extend beyond the buffer */
 
@@ -470,13 +465,12 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
                 * Copy the table from the buffer because the buffer could be modified
                 * or even deleted in the future
                 */
-               table_desc.pointer = ACPI_ALLOCATE(length);
-               if (!table_desc.pointer) {
+               table = ACPI_ALLOCATE(length);
+               if (!table) {
                        return_ACPI_STATUS(AE_NO_MEMORY);
                }
 
-               ACPI_MEMCPY(table_desc.pointer, table, length);
-               table_desc.address = ACPI_TO_INTEGER(table_desc.pointer);
+               ACPI_MEMCPY(table, table_header, length);
                break;
 
        default:
@@ -484,27 +478,32 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
                return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
        }
 
-       /* Validate table checksum (will not get validated in tb_add_table) */
-
-       status = acpi_tb_verify_checksum(table_desc.pointer, length);
-       if (ACPI_FAILURE(status)) {
-               ACPI_FREE(table_desc.pointer);
-               return_ACPI_STATUS(status);
-       }
-
-       /* Complete the table descriptor */
+       /* Install the new table into the local data structures */
 
-       table_desc.length = length;
-       table_desc.flags = ACPI_TABLE_ORIGIN_ALLOCATED;
+       ACPI_INFO((AE_INFO, "Dynamic OEM Table Load:"));
+       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
 
-       /* Install the new table into the local data structures */
+       status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table),
+                                               ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL,
+                                               TRUE, TRUE, &table_index);
 
-       status = acpi_tb_add_table(&table_desc, &table_index);
+       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
        if (ACPI_FAILURE(status)) {
 
                /* Delete allocated table buffer */
 
-               acpi_tb_delete_table(&table_desc);
+               ACPI_FREE(table);
+               return_ACPI_STATUS(status);
+       }
+
+       /*
+        * Note: Now table is "INSTALLED", it must be validated before
+        * loading.
+        */
+       status =
+           acpi_tb_validate_table(&acpi_gbl_root_table_list.
+                                  tables[table_index]);
+       if (ACPI_FAILURE(status)) {
                return_ACPI_STATUS(status);
        }
 
@@ -536,9 +535,6 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
                return_ACPI_STATUS(status);
        }
 
-       ACPI_INFO((AE_INFO, "Dynamic OEM Table Load:"));
-       acpi_tb_print_table_header(0, table_desc.pointer);
-
        /* Remove the reference by added by acpi_ex_store above */
 
        acpi_ut_remove_reference(ddb_handle);
@@ -546,8 +542,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
        /* Invoke table handler if present */
 
        if (acpi_gbl_table_handler) {
-               (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD,
-                                            table_desc.pointer,
+               (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table,
                                             acpi_gbl_table_handler_context);
        }
 
@@ -575,6 +570,13 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
 
        ACPI_FUNCTION_TRACE(ex_unload_table);
 
+       /*
+        * Temporarily emit a warning so that the ASL for the machine can be
+        * hopefully obtained. This is to say that the Unload() operator is
+        * extremely rare if not completely unused.
+        */
+       ACPI_WARNING((AE_INFO, "Received request to unload an ACPI table"));
+
        /*
         * Validate the handle
         * Although the handle is partially validated in acpi_ex_reconfiguration()
index 973fdae..925202a 100644 (file)
@@ -134,9 +134,11 @@ static struct acpi_exdump_info acpi_ex_dump_method[9] = {
        {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(method.aml_start), "Aml Start"}
 };
 
-static struct acpi_exdump_info acpi_ex_dump_mutex[5] = {
+static struct acpi_exdump_info acpi_ex_dump_mutex[6] = {
        {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_mutex), NULL},
        {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(mutex.sync_level), "Sync Level"},
+       {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(mutex.original_sync_level),
+        "Original Sync Level"},
        {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(mutex.owner_thread), "Owner Thread"},
        {ACPI_EXD_UINT16, ACPI_EXD_OFFSET(mutex.acquisition_depth),
         "Acquire Depth"},
index e701d8c..6aade8e 100644 (file)
@@ -140,11 +140,12 @@ acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,
                /* Walk the list, updating the PCI device/function/bus numbers */
 
                status = acpi_hw_process_pci_list(pci_id, list_head);
-       }
 
-       /* Always delete the list */
+               /* Delete the list */
+
+               acpi_hw_delete_pci_list(list_head);
+       }
 
-       acpi_hw_delete_pci_list(list_head);
        return_ACPI_STATUS(status);
 }
 
@@ -187,6 +188,10 @@ acpi_hw_build_pci_list(acpi_handle root_pci_device,
        while (1) {
                status = acpi_get_parent(current_device, &parent_device);
                if (ACPI_FAILURE(status)) {
+
+                       /* Must delete the list before exit */
+
+                       acpi_hw_delete_pci_list(*return_list_head);
                        return (status);
                }
 
@@ -199,6 +204,10 @@ acpi_hw_build_pci_list(acpi_handle root_pci_device,
 
                list_element = ACPI_ALLOCATE(sizeof(struct acpi_pci_device));
                if (!list_element) {
+
+                       /* Must delete the list before exit */
+
+                       acpi_hw_delete_pci_list(*return_list_head);
                        return (AE_NO_MEMORY);
                }
 
index 75d3690..049d9c2 100644 (file)
@@ -72,6 +72,8 @@ acpi_buffer_to_resource(u8 *aml_buffer,
        void *resource;
        void *current_resource_ptr;
 
+       ACPI_FUNCTION_TRACE(acpi_buffer_to_resource);
+
        /*
         * Note: we allow AE_AML_NO_RESOURCE_END_TAG, since an end tag
         * is not required here.
@@ -85,7 +87,7 @@ acpi_buffer_to_resource(u8 *aml_buffer,
                status = AE_OK;
        }
        if (ACPI_FAILURE(status)) {
-               return (status);
+               return_ACPI_STATUS(status);
        }
 
        /* Allocate a buffer for the converted resource */
@@ -93,7 +95,7 @@ acpi_buffer_to_resource(u8 *aml_buffer,
        resource = ACPI_ALLOCATE_ZEROED(list_size_needed);
        current_resource_ptr = resource;
        if (!resource) {
-               return (AE_NO_MEMORY);
+               return_ACPI_STATUS(AE_NO_MEMORY);
        }
 
        /* Perform the AML-to-Resource conversion */
@@ -110,9 +112,11 @@ acpi_buffer_to_resource(u8 *aml_buffer,
                *resource_ptr = resource;
        }
 
-       return (status);
+       return_ACPI_STATUS(status);
 }
 
+ACPI_EXPORT_SYMBOL(acpi_buffer_to_resource)
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_rs_create_resource_list
@@ -130,10 +134,9 @@ acpi_buffer_to_resource(u8 *aml_buffer,
  *              of device resources.
  *
  ******************************************************************************/
-
 acpi_status
 acpi_rs_create_resource_list(union acpi_operand_object *aml_buffer,
-                            struct acpi_buffer * output_buffer)
+                            struct acpi_buffer *output_buffer)
 {
 
        acpi_status status;
diff --git a/drivers/acpi/acpica/tbdata.c b/drivers/acpi/acpica/tbdata.c
new file mode 100644 (file)
index 0000000..f499c10
--- /dev/null
@@ -0,0 +1,760 @@
+/******************************************************************************
+ *
+ * Module Name: tbdata - Table manager data structure functions
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "actables.h"
+
+#define _COMPONENT          ACPI_TABLES
+ACPI_MODULE_NAME("tbdata")
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_init_table_descriptor
+ *
+ * PARAMETERS:  table_desc              - Table descriptor
+ *              address                 - Physical address of the table
+ *              flags                   - Allocation flags of the table
+ *              table                   - Pointer to the table
+ *
+ * RETURN:      None
+ *
+ * DESCRIPTION: Initialize a new table descriptor
+ *
+ ******************************************************************************/
+void
+acpi_tb_init_table_descriptor(struct acpi_table_desc *table_desc,
+                             acpi_physical_address address,
+                             u8 flags, struct acpi_table_header *table)
+{
+
+       /*
+        * Initialize the table descriptor. Set the pointer to NULL, since the
+        * table is not fully mapped at this time.
+        */
+       ACPI_MEMSET(table_desc, 0, sizeof(struct acpi_table_desc));
+       table_desc->address = address;
+       table_desc->length = table->length;
+       table_desc->flags = flags;
+       ACPI_MOVE_32_TO_32(table_desc->signature.ascii, table->signature);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_acquire_table
+ *
+ * PARAMETERS:  table_desc          - Table descriptor
+ *              table_ptr           - Where table is returned
+ *              table_length        - Where table length is returned
+ *              table_flags         - Where table allocation flags are returned
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Acquire an ACPI table. It can be used for tables not
+ *              maintained in the acpi_gbl_root_table_list.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_tb_acquire_table(struct acpi_table_desc *table_desc,
+                     struct acpi_table_header **table_ptr,
+                     u32 *table_length, u8 *table_flags)
+{
+       struct acpi_table_header *table = NULL;
+
+       switch (table_desc->flags & ACPI_TABLE_ORIGIN_MASK) {
+       case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL:
+
+               table =
+                   acpi_os_map_memory(table_desc->address, table_desc->length);
+               break;
+
+       case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
+       case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
+
+               table =
+                   ACPI_CAST_PTR(struct acpi_table_header,
+                                 table_desc->address);
+               break;
+
+       default:
+
+               break;
+       }
+
+       /* Table is not valid yet */
+
+       if (!table) {
+               return (AE_NO_MEMORY);
+       }
+
+       /* Fill the return values */
+
+       *table_ptr = table;
+       *table_length = table_desc->length;
+       *table_flags = table_desc->flags;
+       return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_release_table
+ *
+ * PARAMETERS:  table               - Pointer for the table
+ *              table_length        - Length for the table
+ *              table_flags         - Allocation flags for the table
+ *
+ * RETURN:      None
+ *
+ * DESCRIPTION: Release a table. The inverse of acpi_tb_acquire_table().
+ *
+ ******************************************************************************/
+
+void
+acpi_tb_release_table(struct acpi_table_header *table,
+                     u32 table_length, u8 table_flags)
+{
+
+       switch (table_flags & ACPI_TABLE_ORIGIN_MASK) {
+       case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL:
+
+               acpi_os_unmap_memory(table, table_length);
+               break;
+
+       case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
+       case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
+       default:
+
+               break;
+       }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_acquire_temp_table
+ *
+ * PARAMETERS:  table_desc          - Table descriptor to be acquired
+ *              address             - Address of the table
+ *              flags               - Allocation flags of the table
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: This function validates the table header to obtain the length
+ *              of a table and fills the table descriptor to make its state as
+ *              "INSTALLED". Such a table descriptor is only used for verified
+ *              installation.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_tb_acquire_temp_table(struct acpi_table_desc *table_desc,
+                          acpi_physical_address address, u8 flags)
+{
+       struct acpi_table_header *table_header;
+
+       switch (flags & ACPI_TABLE_ORIGIN_MASK) {
+       case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL:
+
+               /* Get the length of the full table from the header */
+
+               table_header =
+                   acpi_os_map_memory(address,
+                                      sizeof(struct acpi_table_header));
+               if (!table_header) {
+                       return (AE_NO_MEMORY);
+               }
+
+               acpi_tb_init_table_descriptor(table_desc, address, flags,
+                                             table_header);
+               acpi_os_unmap_memory(table_header,
+                                    sizeof(struct acpi_table_header));
+               return (AE_OK);
+
+       case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL:
+       case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL:
+
+               table_header = ACPI_CAST_PTR(struct acpi_table_header, address);
+               if (!table_header) {
+                       return (AE_NO_MEMORY);
+               }
+
+               acpi_tb_init_table_descriptor(table_desc, address, flags,
+                                             table_header);
+               return (AE_OK);
+
+       default:
+
+               break;
+       }
+
+       /* Table is not valid yet */
+
+       return (AE_NO_MEMORY);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_release_temp_table
+ *
+ * PARAMETERS:  table_desc          - Table descriptor to be released
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: The inverse of acpi_tb_acquire_temp_table().
+ *
+ *****************************************************************************/
+
+void acpi_tb_release_temp_table(struct acpi_table_desc *table_desc)
+{
+
+       /*
+        * Note that the .Address is maintained by the callers of
+        * acpi_tb_acquire_temp_table(), thus do not invoke acpi_tb_uninstall_table()
+        * where .Address will be freed.
+        */
+       acpi_tb_invalidate_table(table_desc);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_validate_table
+ *
+ * PARAMETERS:  table_desc          - Table descriptor
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: This function is called to validate the table, the returned
+ *              table descriptor is in "VALIDATED" state.
+ *
+ *****************************************************************************/
+
+acpi_status acpi_tb_validate_table(struct acpi_table_desc *table_desc)
+{
+       acpi_status status = AE_OK;
+
+       ACPI_FUNCTION_TRACE(tb_validate_table);
+
+       /* Validate the table if necessary */
+
+       if (!table_desc->pointer) {
+               status = acpi_tb_acquire_table(table_desc, &table_desc->pointer,
+                                              &table_desc->length,
+                                              &table_desc->flags);
+               if (!table_desc->pointer) {
+                       status = AE_NO_MEMORY;
+               }
+       }
+
+       return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_invalidate_table
+ *
+ * PARAMETERS:  table_desc          - Table descriptor
+ *
+ * RETURN:      None
+ *
+ * DESCRIPTION: Invalidate one internal ACPI table, this is the inverse of
+ *              acpi_tb_validate_table().
+ *
+ ******************************************************************************/
+
+void acpi_tb_invalidate_table(struct acpi_table_desc *table_desc)
+{
+
+       ACPI_FUNCTION_TRACE(tb_invalidate_table);
+
+       /* Table must be validated */
+
+       if (!table_desc->pointer) {
+               return_VOID;
+       }
+
+       acpi_tb_release_table(table_desc->pointer, table_desc->length,
+                             table_desc->flags);
+       table_desc->pointer = NULL;
+
+       return_VOID;
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_validate_temp_table
+ *
+ * PARAMETERS:  table_desc          - Table descriptor
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: This function is called to validate the table, the returned
+ *              table descriptor is in "VALIDATED" state.
+ *
+ *****************************************************************************/
+
+acpi_status acpi_tb_validate_temp_table(struct acpi_table_desc *table_desc)
+{
+
+       if (!table_desc->pointer && !acpi_gbl_verify_table_checksum) {
+               /*
+                * Only validates the header of the table.
+                * Note that Length contains the size of the mapping after invoking
+                * this work around, this value is required by
+                * acpi_tb_release_temp_table().
+                * We can do this because in acpi_init_table_descriptor(), the Length
+                * field of the installed descriptor is filled with the actual
+                * table length obtaining from the table header.
+                */
+               table_desc->length = sizeof(struct acpi_table_header);
+       }
+
+       return (acpi_tb_validate_table(table_desc));
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_verify_temp_table
+ *
+ * PARAMETERS:  table_desc          - Table descriptor
+ *              signature           - Table signature to verify
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: This function is called to validate and verify the table, the
+ *              returned table descriptor is in "VALIDATED" state.
+ *
+ *****************************************************************************/
+
+acpi_status
+acpi_tb_verify_temp_table(struct acpi_table_desc * table_desc, char *signature)
+{
+       acpi_status status = AE_OK;
+
+       ACPI_FUNCTION_TRACE(tb_verify_temp_table);
+
+       /* Validate the table */
+
+       status = acpi_tb_validate_temp_table(table_desc);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(AE_NO_MEMORY);
+       }
+
+       /* If a particular signature is expected (DSDT/FACS), it must match */
+
+       if (signature && !ACPI_COMPARE_NAME(&table_desc->signature, signature)) {
+               ACPI_BIOS_ERROR((AE_INFO,
+                                "Invalid signature 0x%X for ACPI table, expected [%s]",
+                                table_desc->signature.integer, signature));
+               status = AE_BAD_SIGNATURE;
+               goto invalidate_and_exit;
+       }
+
+       /* Verify the checksum */
+
+       if (acpi_gbl_verify_table_checksum) {
+               status =
+                   acpi_tb_verify_checksum(table_desc->pointer,
+                                           table_desc->length);
+               if (ACPI_FAILURE(status)) {
+                       ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY,
+                                       "%4.4s " ACPI_PRINTF_UINT
+                                       " Attempted table install failed",
+                                       acpi_ut_valid_acpi_name(table_desc->
+                                                               signature.
+                                                               ascii) ?
+                                       table_desc->signature.ascii : "????",
+                                       ACPI_FORMAT_TO_UINT(table_desc->
+                                                           address)));
+                       goto invalidate_and_exit;
+               }
+       }
+
+       return_ACPI_STATUS(AE_OK);
+
+invalidate_and_exit:
+       acpi_tb_invalidate_table(table_desc);
+       return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_resize_root_table_list
+ *
+ * PARAMETERS:  None
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Expand the size of global table array
+ *
+ ******************************************************************************/
+
+acpi_status acpi_tb_resize_root_table_list(void)
+{
+       struct acpi_table_desc *tables;
+       u32 table_count;
+
+       ACPI_FUNCTION_TRACE(tb_resize_root_table_list);
+
+       /* allow_resize flag is a parameter to acpi_initialize_tables */
+
+       if (!(acpi_gbl_root_table_list.flags & ACPI_ROOT_ALLOW_RESIZE)) {
+               ACPI_ERROR((AE_INFO,
+                           "Resize of Root Table Array is not allowed"));
+               return_ACPI_STATUS(AE_SUPPORT);
+       }
+
+       /* Increase the Table Array size */
+
+       if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) {
+               table_count = acpi_gbl_root_table_list.max_table_count;
+       } else {
+               table_count = acpi_gbl_root_table_list.current_table_count;
+       }
+
+       tables = ACPI_ALLOCATE_ZEROED(((acpi_size) table_count +
+                                      ACPI_ROOT_TABLE_SIZE_INCREMENT) *
+                                     sizeof(struct acpi_table_desc));
+       if (!tables) {
+               ACPI_ERROR((AE_INFO,
+                           "Could not allocate new root table array"));
+               return_ACPI_STATUS(AE_NO_MEMORY);
+       }
+
+       /* Copy and free the previous table array */
+
+       if (acpi_gbl_root_table_list.tables) {
+               ACPI_MEMCPY(tables, acpi_gbl_root_table_list.tables,
+                           (acpi_size) table_count *
+                           sizeof(struct acpi_table_desc));
+
+               if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) {
+                       ACPI_FREE(acpi_gbl_root_table_list.tables);
+               }
+       }
+
+       acpi_gbl_root_table_list.tables = tables;
+       acpi_gbl_root_table_list.max_table_count =
+           table_count + ACPI_ROOT_TABLE_SIZE_INCREMENT;
+       acpi_gbl_root_table_list.flags |= ACPI_ROOT_ORIGIN_ALLOCATED;
+
+       return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_get_next_root_index
+ *
+ * PARAMETERS:  table_index         - Where table index is returned
+ *
+ * RETURN:      Status and table index.
+ *
+ * DESCRIPTION: Allocate a new ACPI table entry to the global table list
+ *
+ ******************************************************************************/
+
+acpi_status acpi_tb_get_next_root_index(u32 *table_index)
+{
+       acpi_status status;
+
+       /* Ensure that there is room for the table in the Root Table List */
+
+       if (acpi_gbl_root_table_list.current_table_count >=
+           acpi_gbl_root_table_list.max_table_count) {
+               status = acpi_tb_resize_root_table_list();
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+       }
+
+       *table_index = acpi_gbl_root_table_list.current_table_count;
+       acpi_gbl_root_table_list.current_table_count++;
+       return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_terminate
+ *
+ * PARAMETERS:  None
+ *
+ * RETURN:      None
+ *
+ * DESCRIPTION: Delete all internal ACPI tables
+ *
+ ******************************************************************************/
+
+void acpi_tb_terminate(void)
+{
+       u32 i;
+
+       ACPI_FUNCTION_TRACE(tb_terminate);
+
+       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
+       /* Delete the individual tables */
+
+       for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) {
+               acpi_tb_uninstall_table(&acpi_gbl_root_table_list.tables[i]);
+       }
+
+       /*
+        * Delete the root table array if allocated locally. Array cannot be
+        * mapped, so we don't need to check for that flag.
+        */
+       if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) {
+               ACPI_FREE(acpi_gbl_root_table_list.tables);
+       }
+
+       acpi_gbl_root_table_list.tables = NULL;
+       acpi_gbl_root_table_list.flags = 0;
+       acpi_gbl_root_table_list.current_table_count = 0;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "ACPI Tables freed\n"));
+
+       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+       return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_delete_namespace_by_owner
+ *
+ * PARAMETERS:  table_index         - Table index
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Delete all namespace objects created when this table was loaded.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index)
+{
+       acpi_owner_id owner_id;
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE(tb_delete_namespace_by_owner);
+
+       status = acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       if (table_index >= acpi_gbl_root_table_list.current_table_count) {
+
+               /* The table index does not exist */
+
+               (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+               return_ACPI_STATUS(AE_NOT_EXIST);
+       }
+
+       /* Get the owner ID for this table, used to delete namespace nodes */
+
+       owner_id = acpi_gbl_root_table_list.tables[table_index].owner_id;
+       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+
+       /*
+        * Need to acquire the namespace writer lock to prevent interference
+        * with any concurrent namespace walks. The interpreter must be
+        * released during the deletion since the acquisition of the deletion
+        * lock may block, and also since the execution of a namespace walk
+        * must be allowed to use the interpreter.
+        */
+       (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
+       status = acpi_ut_acquire_write_lock(&acpi_gbl_namespace_rw_lock);
+
+       acpi_ns_delete_namespace_by_owner(owner_id);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       acpi_ut_release_write_lock(&acpi_gbl_namespace_rw_lock);
+
+       status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER);
+       return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_allocate_owner_id
+ *
+ * PARAMETERS:  table_index         - Table index
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Allocates owner_id in table_desc
+ *
+ ******************************************************************************/
+
+acpi_status acpi_tb_allocate_owner_id(u32 table_index)
+{
+       acpi_status status = AE_BAD_PARAMETER;
+
+       ACPI_FUNCTION_TRACE(tb_allocate_owner_id);
+
+       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+       if (table_index < acpi_gbl_root_table_list.current_table_count) {
+               status =
+                   acpi_ut_allocate_owner_id(&
+                                             (acpi_gbl_root_table_list.
+                                              tables[table_index].owner_id));
+       }
+
+       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+       return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_release_owner_id
+ *
+ * PARAMETERS:  table_index         - Table index
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Releases owner_id in table_desc
+ *
+ ******************************************************************************/
+
+acpi_status acpi_tb_release_owner_id(u32 table_index)
+{
+       acpi_status status = AE_BAD_PARAMETER;
+
+       ACPI_FUNCTION_TRACE(tb_release_owner_id);
+
+       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+       if (table_index < acpi_gbl_root_table_list.current_table_count) {
+               acpi_ut_release_owner_id(&
+                                        (acpi_gbl_root_table_list.
+                                         tables[table_index].owner_id));
+               status = AE_OK;
+       }
+
+       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+       return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_get_owner_id
+ *
+ * PARAMETERS:  table_index         - Table index
+ *              owner_id            - Where the table owner_id is returned
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: returns owner_id for the ACPI table
+ *
+ ******************************************************************************/
+
+acpi_status acpi_tb_get_owner_id(u32 table_index, acpi_owner_id * owner_id)
+{
+       acpi_status status = AE_BAD_PARAMETER;
+
+       ACPI_FUNCTION_TRACE(tb_get_owner_id);
+
+       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+       if (table_index < acpi_gbl_root_table_list.current_table_count) {
+               *owner_id =
+                   acpi_gbl_root_table_list.tables[table_index].owner_id;
+               status = AE_OK;
+       }
+
+       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+       return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_is_table_loaded
+ *
+ * PARAMETERS:  table_index         - Index into the root table
+ *
+ * RETURN:      Table Loaded Flag
+ *
+ ******************************************************************************/
+
+u8 acpi_tb_is_table_loaded(u32 table_index)
+{
+       u8 is_loaded = FALSE;
+
+       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+       if (table_index < acpi_gbl_root_table_list.current_table_count) {
+               is_loaded = (u8)
+                   (acpi_gbl_root_table_list.tables[table_index].flags &
+                    ACPI_TABLE_IS_LOADED);
+       }
+
+       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+       return (is_loaded);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_set_table_loaded_flag
+ *
+ * PARAMETERS:  table_index         - Table index
+ *              is_loaded           - TRUE if table is loaded, FALSE otherwise
+ *
+ * RETURN:      None
+ *
+ * DESCRIPTION: Sets the table loaded flag to either TRUE or FALSE.
+ *
+ ******************************************************************************/
+
+void acpi_tb_set_table_loaded_flag(u32 table_index, u8 is_loaded)
+{
+
+       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+       if (table_index < acpi_gbl_root_table_list.current_table_count) {
+               if (is_loaded) {
+                       acpi_gbl_root_table_list.tables[table_index].flags |=
+                           ACPI_TABLE_IS_LOADED;
+               } else {
+                       acpi_gbl_root_table_list.tables[table_index].flags &=
+                           ~ACPI_TABLE_IS_LOADED;
+               }
+       }
+
+       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+}
index ec14588..41519a9 100644 (file)
@@ -52,7 +52,8 @@ ACPI_MODULE_NAME("tbfadt")
 static void
 acpi_tb_init_generic_address(struct acpi_generic_address *generic_address,
                             u8 space_id,
-                            u8 byte_width, u64 address, char *register_name);
+                            u8 byte_width,
+                            u64 address, char *register_name, u8 flags);
 
 static void acpi_tb_convert_fadt(void);
 
@@ -69,13 +70,14 @@ typedef struct acpi_fadt_info {
        u16 address32;
        u16 length;
        u8 default_length;
-       u8 type;
+       u8 flags;
 
 } acpi_fadt_info;
 
 #define ACPI_FADT_OPTIONAL          0
 #define ACPI_FADT_REQUIRED          1
 #define ACPI_FADT_SEPARATE_LENGTH   2
+#define ACPI_FADT_GPE_REGISTER      4
 
 static struct acpi_fadt_info fadt_info_table[] = {
        {"Pm1aEventBlock",
@@ -125,14 +127,14 @@ static struct acpi_fadt_info fadt_info_table[] = {
         ACPI_FADT_OFFSET(gpe0_block),
         ACPI_FADT_OFFSET(gpe0_block_length),
         0,
-        ACPI_FADT_SEPARATE_LENGTH},
+        ACPI_FADT_SEPARATE_LENGTH | ACPI_FADT_GPE_REGISTER},
 
        {"Gpe1Block",
         ACPI_FADT_OFFSET(xgpe1_block),
         ACPI_FADT_OFFSET(gpe1_block),
         ACPI_FADT_OFFSET(gpe1_block_length),
         0,
-        ACPI_FADT_SEPARATE_LENGTH}
+        ACPI_FADT_SEPARATE_LENGTH | ACPI_FADT_GPE_REGISTER}
 };
 
 #define ACPI_FADT_INFO_ENTRIES \
@@ -189,19 +191,29 @@ static struct acpi_fadt_pm_info fadt_pm_info_table[] = {
 static void
 acpi_tb_init_generic_address(struct acpi_generic_address *generic_address,
                             u8 space_id,
-                            u8 byte_width, u64 address, char *register_name)
+                            u8 byte_width,
+                            u64 address, char *register_name, u8 flags)
 {
        u8 bit_width;
 
-       /* Bit width field in the GAS is only one byte long, 255 max */
-
+       /*
+        * Bit width field in the GAS is only one byte long, 255 max.
+        * Check for bit_width overflow in GAS.
+        */
        bit_width = (u8)(byte_width * 8);
-
-       if (byte_width > 31) {  /* (31*8)=248 */
-               ACPI_ERROR((AE_INFO,
-                           "%s - 32-bit FADT register is too long (%u bytes, %u bits) "
-                           "to convert to GAS struct - 255 bits max, truncating",
-                           register_name, byte_width, (byte_width * 8)));
+       if (byte_width > 31) {  /* (31*8)=248, (32*8)=256 */
+               /*
+                * No error for GPE blocks, because we do not use the bit_width
+                * for GPEs, the legacy length (byte_width) is used instead to
+                * allow for a large number of GPEs.
+                */
+               if (!(flags & ACPI_FADT_GPE_REGISTER)) {
+                       ACPI_ERROR((AE_INFO,
+                                   "%s - 32-bit FADT register is too long (%u bytes, %u bits) "
+                                   "to convert to GAS struct - 255 bits max, truncating",
+                                   register_name, byte_width,
+                                   (byte_width * 8)));
+               }
 
                bit_width = 255;
        }
@@ -332,15 +344,15 @@ void acpi_tb_parse_fadt(u32 table_index)
 
        /* Obtain the DSDT and FACS tables via their addresses within the FADT */
 
-       acpi_tb_install_table((acpi_physical_address) acpi_gbl_FADT.Xdsdt,
-                             ACPI_SIG_DSDT, ACPI_TABLE_INDEX_DSDT);
+       acpi_tb_install_fixed_table((acpi_physical_address) acpi_gbl_FADT.Xdsdt,
+                                   ACPI_SIG_DSDT, ACPI_TABLE_INDEX_DSDT);
 
        /* If Hardware Reduced flag is set, there is no FACS */
 
        if (!acpi_gbl_reduced_hardware) {
-               acpi_tb_install_table((acpi_physical_address) acpi_gbl_FADT.
-                                     Xfacs, ACPI_SIG_FACS,
-                                     ACPI_TABLE_INDEX_FACS);
+               acpi_tb_install_fixed_table((acpi_physical_address)
+                                           acpi_gbl_FADT.Xfacs, ACPI_SIG_FACS,
+                                           ACPI_TABLE_INDEX_FACS);
        }
 }
 
@@ -450,6 +462,7 @@ static void acpi_tb_convert_fadt(void)
        struct acpi_generic_address *address64;
        u32 address32;
        u8 length;
+       u8 flags;
        u32 i;
 
        /*
@@ -515,6 +528,7 @@ static void acpi_tb_convert_fadt(void)
                                       fadt_info_table[i].length);
 
                name = fadt_info_table[i].name;
+               flags = fadt_info_table[i].flags;
 
                /*
                 * Expand the ACPI 1.0 32-bit addresses to the ACPI 2.0 64-bit "X"
@@ -554,7 +568,7 @@ static void acpi_tb_convert_fadt(void)
                                                                           [i].
                                                                           length),
                                                             (u64)address32,
-                                                            name);
+                                                            name, flags);
                        } else if (address64->address != (u64)address32) {
 
                                /* Address mismatch */
@@ -582,7 +596,8 @@ static void acpi_tb_convert_fadt(void)
                                                                      length),
                                                                     (u64)
                                                                     address32,
-                                                                    name);
+                                                                    name,
+                                                                    flags);
                                }
                        }
                }
@@ -603,7 +618,7 @@ static void acpi_tb_convert_fadt(void)
                                           address64->bit_width));
                }
 
-               if (fadt_info_table[i].type & ACPI_FADT_REQUIRED) {
+               if (fadt_info_table[i].flags & ACPI_FADT_REQUIRED) {
                        /*
                         * Field is required (Pm1a_event, Pm1a_control).
                         * Both the address and length must be non-zero.
@@ -617,7 +632,7 @@ static void acpi_tb_convert_fadt(void)
                                                                    address),
                                                 length));
                        }
-               } else if (fadt_info_table[i].type & ACPI_FADT_SEPARATE_LENGTH) {
+               } else if (fadt_info_table[i].flags & ACPI_FADT_SEPARATE_LENGTH) {
                        /*
                         * Field is optional (Pm2_control, GPE0, GPE1) AND has its own
                         * length field. If present, both the address and length must
@@ -726,7 +741,7 @@ static void acpi_tb_setup_fadt_registers(void)
                                                     (fadt_pm_info_table[i].
                                                      register_num *
                                                      pm1_register_byte_width),
-                                                    "PmRegisters");
+                                                    "PmRegisters", 0);
                }
        }
 }
index c120039..cb94770 100644 (file)
@@ -99,8 +99,8 @@ acpi_tb_find_table(char *signature,
                        /* Table is not currently mapped, map it */
 
                        status =
-                           acpi_tb_verify_table(&acpi_gbl_root_table_list.
-                                                tables[i]);
+                           acpi_tb_validate_table(&acpi_gbl_root_table_list.
+                                                  tables[i]);
                        if (ACPI_FAILURE(status)) {
                                return_ACPI_STATUS(status);
                        }
index e304094..755b90c 100644 (file)
 
 #include <acpi/acpi.h>
 #include "accommon.h"
-#include "acnamesp.h"
 #include "actables.h"
 
 #define _COMPONENT          ACPI_TABLES
 ACPI_MODULE_NAME("tbinstal")
 
-/******************************************************************************
+/* Local prototypes */
+static u8
+acpi_tb_compare_tables(struct acpi_table_desc *table_desc, u32 table_index);
+
+/*******************************************************************************
  *
- * FUNCTION:    acpi_tb_verify_table
+ * FUNCTION:    acpi_tb_compare_tables
  *
- * PARAMETERS:  table_desc          - table
+ * PARAMETERS:  table_desc          - Table 1 descriptor to be compared
+ *              table_index         - Index of table 2 to be compared
  *
- * RETURN:      Status
+ * RETURN:      TRUE if both tables are identical.
  *
- * DESCRIPTION: this function is called to verify and map table
+ * DESCRIPTION: This function compares a table with another table that has
+ *              already been installed in the root table list.
  *
- *****************************************************************************/
-acpi_status acpi_tb_verify_table(struct acpi_table_desc *table_desc)
+ ******************************************************************************/
+
+static u8
+acpi_tb_compare_tables(struct acpi_table_desc *table_desc, u32 table_index)
 {
        acpi_status status = AE_OK;
+       u8 is_identical;
+       struct acpi_table_header *table;
+       u32 table_length;
+       u8 table_flags;
 
-       ACPI_FUNCTION_TRACE(tb_verify_table);
-
-       /* Map the table if necessary */
-
-       if (!table_desc->pointer) {
-               if ((table_desc->flags & ACPI_TABLE_ORIGIN_MASK) ==
-                   ACPI_TABLE_ORIGIN_MAPPED) {
-                       table_desc->pointer =
-                           acpi_os_map_memory(table_desc->address,
-                                              table_desc->length);
-               }
-               if (!table_desc->pointer) {
-                       return_ACPI_STATUS(AE_NO_MEMORY);
-               }
+       status =
+           acpi_tb_acquire_table(&acpi_gbl_root_table_list.tables[table_index],
+                                 &table, &table_length, &table_flags);
+       if (ACPI_FAILURE(status)) {
+               return (FALSE);
        }
 
-       /* Always calculate checksum, ignore bad checksum if requested */
+       /*
+        * Check for a table match on the entire table length,
+        * not just the header.
+        */
+       is_identical = (u8)((table_desc->length != table_length ||
+                            ACPI_MEMCMP(table_desc->pointer, table,
+                                        table_length)) ? FALSE : TRUE);
 
-       status =
-           acpi_tb_verify_checksum(table_desc->pointer, table_desc->length);
+       /* Release the acquired table */
 
-       return_ACPI_STATUS(status);
+       acpi_tb_release_table(table, table_length, table_flags);
+       return (is_identical);
 }
 
 /*******************************************************************************
  *
- * FUNCTION:    acpi_tb_add_table
+ * FUNCTION:    acpi_tb_install_table_with_override
  *
- * PARAMETERS:  table_desc          - Table descriptor
- *              table_index         - Where the table index is returned
+ * PARAMETERS:  table_index             - Index into root table array
+ *              new_table_desc          - New table descriptor to install
+ *              override                - Whether override should be performed
  *
- * RETURN:      Status
+ * RETURN:      None
  *
- * DESCRIPTION: This function is called to add an ACPI table. It is used to
- *              dynamically load tables via the Load and load_table AML
- *              operators.
+ * DESCRIPTION: Install an ACPI table into the global data structure. The
+ *              table override mechanism is called to allow the host
+ *              OS to replace any table before it is installed in the root
+ *              table array.
  *
  ******************************************************************************/
 
-acpi_status
-acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index)
+void
+acpi_tb_install_table_with_override(u32 table_index,
+                                   struct acpi_table_desc *new_table_desc,
+                                   u8 override)
 {
-       u32 i;
-       acpi_status status = AE_OK;
 
-       ACPI_FUNCTION_TRACE(tb_add_table);
-
-       if (!table_desc->pointer) {
-               status = acpi_tb_verify_table(table_desc);
-               if (ACPI_FAILURE(status) || !table_desc->pointer) {
-                       return_ACPI_STATUS(status);
-               }
+       if (table_index >= acpi_gbl_root_table_list.current_table_count) {
+               return;
        }
 
        /*
-        * Validate the incoming table signature.
+        * ACPI Table Override:
         *
-        * 1) Originally, we checked the table signature for "SSDT" or "PSDT".
-        * 2) We added support for OEMx tables, signature "OEM".
-        * 3) Valid tables were encountered with a null signature, so we just
-        *    gave up on validating the signature, (05/2008).
-        * 4) We encountered non-AML tables such as the MADT, which caused
-        *    interpreter errors and kernel faults. So now, we once again allow
-        *    only "SSDT", "OEMx", and now, also a null signature. (05/2011).
+        * Before we install the table, let the host OS override it with a new
+        * one if desired. Any table within the RSDT/XSDT can be replaced,
+        * including the DSDT which is pointed to by the FADT.
         */
-       if ((table_desc->pointer->signature[0] != 0x00) &&
-           (!ACPI_COMPARE_NAME(table_desc->pointer->signature, ACPI_SIG_SSDT))
-           && (ACPI_STRNCMP(table_desc->pointer->signature, "OEM", 3))) {
-               ACPI_BIOS_ERROR((AE_INFO,
-                                "Table has invalid signature [%4.4s] (0x%8.8X), "
-                                "must be SSDT or OEMx",
-                                acpi_ut_valid_acpi_name(table_desc->pointer->
-                                                        signature) ?
-                                table_desc->pointer->signature : "????",
-                                *(u32 *)table_desc->pointer->signature));
-
-               return_ACPI_STATUS(AE_BAD_SIGNATURE);
+       if (override) {
+               acpi_tb_override_table(new_table_desc);
        }
 
-       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+       acpi_tb_init_table_descriptor(&acpi_gbl_root_table_list.
+                                     tables[table_index],
+                                     new_table_desc->address,
+                                     new_table_desc->flags,
+                                     new_table_desc->pointer);
 
-       /* Check if table is already registered */
+       acpi_tb_print_table_header(new_table_desc->address,
+                                  new_table_desc->pointer);
 
-       for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) {
-               if (!acpi_gbl_root_table_list.tables[i].pointer) {
-                       status =
-                           acpi_tb_verify_table(&acpi_gbl_root_table_list.
-                                                tables[i]);
-                       if (ACPI_FAILURE(status)
-                           || !acpi_gbl_root_table_list.tables[i].pointer) {
-                               continue;
-                       }
-               }
+       /* Set the global integer width (based upon revision of the DSDT) */
 
-               /*
-                * Check for a table match on the entire table length,
-                * not just the header.
-                */
-               if (table_desc->length !=
-                   acpi_gbl_root_table_list.tables[i].length) {
-                       continue;
-               }
-
-               if (ACPI_MEMCMP(table_desc->pointer,
-                               acpi_gbl_root_table_list.tables[i].pointer,
-                               acpi_gbl_root_table_list.tables[i].length)) {
-                       continue;
-               }
-
-               /*
-                * Note: the current mechanism does not unregister a table if it is
-                * dynamically unloaded. The related namespace entries are deleted,
-                * but the table remains in the root table list.
-                *
-                * The assumption here is that the number of different tables that
-                * will be loaded is actually small, and there is minimal overhead
-                * in just keeping the table in case it is needed again.
-                *
-                * If this assumption changes in the future (perhaps on large
-                * machines with many table load/unload operations), tables will
-                * need to be unregistered when they are unloaded, and slots in the
-                * root table list should be reused when empty.
-                */
-
-               /*
-                * Table is already registered.
-                * We can delete the table that was passed as a parameter.
-                */
-               acpi_tb_delete_table(table_desc);
-               *table_index = i;
-
-               if (acpi_gbl_root_table_list.tables[i].
-                   flags & ACPI_TABLE_IS_LOADED) {
-
-                       /* Table is still loaded, this is an error */
-
-                       status = AE_ALREADY_EXISTS;
-                       goto release;
-               } else {
-                       /* Table was unloaded, allow it to be reloaded */
-
-                       table_desc->pointer =
-                           acpi_gbl_root_table_list.tables[i].pointer;
-                       table_desc->address =
-                           acpi_gbl_root_table_list.tables[i].address;
-                       status = AE_OK;
-                       goto print_header;
-               }
+       if (table_index == ACPI_TABLE_INDEX_DSDT) {
+               acpi_ut_set_integer_width(new_table_desc->pointer->revision);
        }
-
-       /*
-        * ACPI Table Override:
-        * Allow the host to override dynamically loaded tables.
-        * NOTE: the table is fully mapped at this point, and the mapping will
-        * be deleted by tb_table_override if the table is actually overridden.
-        */
-       (void)acpi_tb_table_override(table_desc->pointer, table_desc);
-
-       /* Add the table to the global root table list */
-
-       status = acpi_tb_store_table(table_desc->address, table_desc->pointer,
-                                    table_desc->length, table_desc->flags,
-                                    table_index);
-       if (ACPI_FAILURE(status)) {
-               goto release;
-       }
-
-print_header:
-       acpi_tb_print_table_header(table_desc->address, table_desc->pointer);
-
-release:
-       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
-       return_ACPI_STATUS(status);
 }
 
 /*******************************************************************************
  *
- * FUNCTION:    acpi_tb_table_override
+ * FUNCTION:    acpi_tb_install_fixed_table
  *
- * PARAMETERS:  table_header        - Header for the original table
- *              table_desc          - Table descriptor initialized for the
- *                                    original table. May or may not be mapped.
+ * PARAMETERS:  address                 - Physical address of DSDT or FACS
+ *              signature               - Table signature, NULL if no need to
+ *                                        match
+ *              table_index             - Index into root table array
  *
- * RETURN:      Pointer to the entire new table. NULL if table not overridden.
- *              If overridden, installs the new table within the input table
- *              descriptor.
+ * RETURN:      Status
  *
- * DESCRIPTION: Attempt table override by calling the OSL override functions.
- *              Note: If the table is overridden, then the entire new table
- *              is mapped and returned by this function.
+ * DESCRIPTION: Install a fixed ACPI table (DSDT/FACS) into the global data
+ *              structure.
  *
  ******************************************************************************/
 
-struct acpi_table_header *acpi_tb_table_override(struct acpi_table_header
-                                                *table_header,
-                                                struct acpi_table_desc
-                                                *table_desc)
+acpi_status
+acpi_tb_install_fixed_table(acpi_physical_address address,
+                           char *signature, u32 table_index)
 {
+       struct acpi_table_desc new_table_desc;
        acpi_status status;
-       struct acpi_table_header *new_table = NULL;
-       acpi_physical_address new_address = 0;
-       u32 new_table_length = 0;
-       u8 new_flags;
-       char *override_type;
 
-       /* (1) Attempt logical override (returns a logical address) */
+       ACPI_FUNCTION_TRACE(tb_install_fixed_table);
 
-       status = acpi_os_table_override(table_header, &new_table);
-       if (ACPI_SUCCESS(status) && new_table) {
-               new_address = ACPI_PTR_TO_PHYSADDR(new_table);
-               new_table_length = new_table->length;
-               new_flags = ACPI_TABLE_ORIGIN_OVERRIDE;
-               override_type = "Logical";
-               goto finish_override;
+       if (!address) {
+               ACPI_ERROR((AE_INFO,
+                           "Null physical address for ACPI table [%s]",
+                           signature));
+               return (AE_NO_MEMORY);
        }
 
-       /* (2) Attempt physical override (returns a physical address) */
+       /* Fill a table descriptor for validation */
 
-       status = acpi_os_physical_table_override(table_header,
-                                                &new_address,
-                                                &new_table_length);
-       if (ACPI_SUCCESS(status) && new_address && new_table_length) {
-
-               /* Map the entire new table */
-
-               new_table = acpi_os_map_memory(new_address, new_table_length);
-               if (!new_table) {
-                       ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY,
-                                       "%4.4s " ACPI_PRINTF_UINT
-                                       " Attempted physical table override failed",
-                                       table_header->signature,
-                                       ACPI_FORMAT_TO_UINT(table_desc->
-                                                           address)));
-                       return (NULL);
-               }
-
-               override_type = "Physical";
-               new_flags = ACPI_TABLE_ORIGIN_MAPPED;
-               goto finish_override;
+       status = acpi_tb_acquire_temp_table(&new_table_desc, address,
+                                           ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL);
+       if (ACPI_FAILURE(status)) {
+               ACPI_ERROR((AE_INFO, "Could not acquire table length at %p",
+                           ACPI_CAST_PTR(void, address)));
+               return_ACPI_STATUS(status);
        }
 
-       return (NULL);          /* There was no override */
-
-finish_override:
-
-       ACPI_INFO((AE_INFO, "%4.4s " ACPI_PRINTF_UINT
-                  " %s table override, new table: " ACPI_PRINTF_UINT,
-                  table_header->signature,
-                  ACPI_FORMAT_TO_UINT(table_desc->address),
-                  override_type, ACPI_FORMAT_TO_UINT(new_table)));
+       /* Validate and verify a table before installation */
 
-       /* We can now unmap/delete the original table (if fully mapped) */
+       status = acpi_tb_verify_temp_table(&new_table_desc, signature);
+       if (ACPI_FAILURE(status)) {
+               goto release_and_exit;
+       }
 
-       acpi_tb_delete_table(table_desc);
+       acpi_tb_install_table_with_override(table_index, &new_table_desc, TRUE);
 
-       /* Setup descriptor for the new table */
+release_and_exit:
 
-       table_desc->address = new_address;
-       table_desc->pointer = new_table;
-       table_desc->length = new_table_length;
-       table_desc->flags = new_flags;
+       /* Release the temporary table descriptor */
 
-       return (new_table);
+       acpi_tb_release_temp_table(&new_table_desc);
+       return_ACPI_STATUS(status);
 }
 
 /*******************************************************************************
  *
- * FUNCTION:    acpi_tb_resize_root_table_list
+ * FUNCTION:    acpi_tb_install_standard_table
  *
- * PARAMETERS:  None
+ * PARAMETERS:  address             - Address of the table (might be a virtual
+ *                                    address depending on the table_flags)
+ *              flags               - Flags for the table
+ *              reload              - Whether reload should be performed
+ *              override            - Whether override should be performed
+ *              table_index         - Where the table index is returned
  *
  * RETURN:      Status
  *
- * DESCRIPTION: Expand the size of global table array
+ * DESCRIPTION: This function is called to install an ACPI table that is
+ *              neither DSDT nor FACS (a "standard" table.)
+ *              When this function is called by "Load" or "LoadTable" opcodes,
+ *              or by acpi_load_table() API, the "Reload" parameter is set.
+ *              After sucessfully returning from this function, table is
+ *              "INSTALLED" but not "VALIDATED".
  *
  ******************************************************************************/
 
-acpi_status acpi_tb_resize_root_table_list(void)
+acpi_status
+acpi_tb_install_standard_table(acpi_physical_address address,
+                              u8 flags,
+                              u8 reload, u8 override, u32 *table_index)
 {
-       struct acpi_table_desc *tables;
-       u32 table_count;
-
-       ACPI_FUNCTION_TRACE(tb_resize_root_table_list);
-
-       /* allow_resize flag is a parameter to acpi_initialize_tables */
+       u32 i;
+       acpi_status status = AE_OK;
+       struct acpi_table_desc new_table_desc;
 
-       if (!(acpi_gbl_root_table_list.flags & ACPI_ROOT_ALLOW_RESIZE)) {
-               ACPI_ERROR((AE_INFO,
-                           "Resize of Root Table Array is not allowed"));
-               return_ACPI_STATUS(AE_SUPPORT);
-       }
+       ACPI_FUNCTION_TRACE(tb_install_standard_table);
 
-       /* Increase the Table Array size */
+       /* Acquire a temporary table descriptor for validation */
 
-       if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) {
-               table_count = acpi_gbl_root_table_list.max_table_count;
-       } else {
-               table_count = acpi_gbl_root_table_list.current_table_count;
+       status = acpi_tb_acquire_temp_table(&new_table_desc, address, flags);
+       if (ACPI_FAILURE(status)) {
+               ACPI_ERROR((AE_INFO, "Could not acquire table length at %p",
+                           ACPI_CAST_PTR(void, address)));
+               return_ACPI_STATUS(status);
        }
 
-       tables = ACPI_ALLOCATE_ZEROED(((acpi_size) table_count +
-                                      ACPI_ROOT_TABLE_SIZE_INCREMENT) *
-                                     sizeof(struct acpi_table_desc));
-       if (!tables) {
-               ACPI_ERROR((AE_INFO,
-                           "Could not allocate new root table array"));
-               return_ACPI_STATUS(AE_NO_MEMORY);
+       /*
+        * Optionally do not load any SSDTs from the RSDT/XSDT. This can
+        * be useful for debugging ACPI problems on some machines.
+        */
+       if (!reload &&
+           acpi_gbl_disable_ssdt_table_install &&
+           ACPI_COMPARE_NAME(&new_table_desc.signature, ACPI_SIG_SSDT)) {
+               ACPI_INFO((AE_INFO, "Ignoring installation of %4.4s at %p",
+                          new_table_desc.signature.ascii, ACPI_CAST_PTR(void,
+                                                                        address)));
+               goto release_and_exit;
        }
 
-       /* Copy and free the previous table array */
-
-       if (acpi_gbl_root_table_list.tables) {
-               ACPI_MEMCPY(tables, acpi_gbl_root_table_list.tables,
-                           (acpi_size) table_count *
-                           sizeof(struct acpi_table_desc));
+       /* Validate and verify a table before installation */
 
-               if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) {
-                       ACPI_FREE(acpi_gbl_root_table_list.tables);
-               }
+       status = acpi_tb_verify_temp_table(&new_table_desc, NULL);
+       if (ACPI_FAILURE(status)) {
+               goto release_and_exit;
        }
 
-       acpi_gbl_root_table_list.tables = tables;
-       acpi_gbl_root_table_list.max_table_count =
-           table_count + ACPI_ROOT_TABLE_SIZE_INCREMENT;
-       acpi_gbl_root_table_list.flags |= ACPI_ROOT_ORIGIN_ALLOCATED;
-
-       return_ACPI_STATUS(AE_OK);
-}
-
-/*******************************************************************************
- *
- * FUNCTION:    acpi_tb_store_table
- *
- * PARAMETERS:  address             - Table address
- *              table               - Table header
- *              length              - Table length
- *              flags               - flags
- *
- * RETURN:      Status and table index.
- *
- * DESCRIPTION: Add an ACPI table to the global table list
- *
- ******************************************************************************/
+       if (reload) {
+               /*
+                * Validate the incoming table signature.
+                *
+                * 1) Originally, we checked the table signature for "SSDT" or "PSDT".
+                * 2) We added support for OEMx tables, signature "OEM".
+                * 3) Valid tables were encountered with a null signature, so we just
+                *    gave up on validating the signature, (05/2008).
+                * 4) We encountered non-AML tables such as the MADT, which caused
+                *    interpreter errors and kernel faults. So now, we once again allow
+                *    only "SSDT", "OEMx", and now, also a null signature. (05/2011).
+                */
+               if ((new_table_desc.signature.ascii[0] != 0x00) &&
+                   (!ACPI_COMPARE_NAME
+                    (&new_table_desc.signature, ACPI_SIG_SSDT))
+                   && (ACPI_STRNCMP(new_table_desc.signature.ascii, "OEM", 3)))
+               {
+                       ACPI_BIOS_ERROR((AE_INFO,
+                                        "Table has invalid signature [%4.4s] (0x%8.8X), "
+                                        "must be SSDT or OEMx",
+                                        acpi_ut_valid_acpi_name(new_table_desc.
+                                                                signature.
+                                                                ascii) ?
+                                        new_table_desc.signature.
+                                        ascii : "????",
+                                        new_table_desc.signature.integer));
+
+                       status = AE_BAD_SIGNATURE;
+                       goto release_and_exit;
+               }
 
-acpi_status
-acpi_tb_store_table(acpi_physical_address address,
-                   struct acpi_table_header *table,
-                   u32 length, u8 flags, u32 *table_index)
-{
-       acpi_status status;
-       struct acpi_table_desc *new_table;
+               /* Check if table is already registered */
 
-       /* Ensure that there is room for the table in the Root Table List */
+               for (i = 0; i < acpi_gbl_root_table_list.current_table_count;
+                    ++i) {
+                       /*
+                        * Check for a table match on the entire table length,
+                        * not just the header.
+                        */
+                       if (!acpi_tb_compare_tables(&new_table_desc, i)) {
+                               continue;
+                       }
 
-       if (acpi_gbl_root_table_list.current_table_count >=
-           acpi_gbl_root_table_list.max_table_count) {
-               status = acpi_tb_resize_root_table_list();
-               if (ACPI_FAILURE(status)) {
-                       return (status);
+                       /*
+                        * Note: the current mechanism does not unregister a table if it is
+                        * dynamically unloaded. The related namespace entries are deleted,
+                        * but the table remains in the root table list.
+                        *
+                        * The assumption here is that the number of different tables that
+                        * will be loaded is actually small, and there is minimal overhead
+                        * in just keeping the table in case it is needed again.
+                        *
+                        * If this assumption changes in the future (perhaps on large
+                        * machines with many table load/unload operations), tables will
+                        * need to be unregistered when they are unloaded, and slots in the
+                        * root table list should be reused when empty.
+                        */
+                       if (acpi_gbl_root_table_list.tables[i].
+                           flags & ACPI_TABLE_IS_LOADED) {
+
+                               /* Table is still loaded, this is an error */
+
+                               status = AE_ALREADY_EXISTS;
+                               goto release_and_exit;
+                       } else {
+                               /*
+                                * Table was unloaded, allow it to be reloaded.
+                                * As we are going to return AE_OK to the caller, we should
+                                * take the responsibility of freeing the input descriptor.
+                                * Refill the input descriptor to ensure
+                                * acpi_tb_install_table_with_override() can be called again to
+                                * indicate the re-installation.
+                                */
+                               acpi_tb_uninstall_table(&new_table_desc);
+                               *table_index = i;
+                               (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+                               return_ACPI_STATUS(AE_OK);
+                       }
                }
        }
 
-       new_table =
-           &acpi_gbl_root_table_list.tables[acpi_gbl_root_table_list.
-                                            current_table_count];
-
-       /* Initialize added table */
-
-       new_table->address = address;
-       new_table->pointer = table;
-       new_table->length = length;
-       new_table->owner_id = 0;
-       new_table->flags = flags;
-
-       ACPI_MOVE_32_TO_32(&new_table->signature, table->signature);
-
-       *table_index = acpi_gbl_root_table_list.current_table_count;
-       acpi_gbl_root_table_list.current_table_count++;
-       return (AE_OK);
-}
-
-/*******************************************************************************
- *
- * FUNCTION:    acpi_tb_delete_table
- *
- * PARAMETERS:  table_index         - Table index
- *
- * RETURN:      None
- *
- * DESCRIPTION: Delete one internal ACPI table
- *
- ******************************************************************************/
+       /* Add the table to the global root table list */
 
-void acpi_tb_delete_table(struct acpi_table_desc *table_desc)
-{
-       /* Table must be mapped or allocated */
-       if (!table_desc->pointer) {
-               return;
+       status = acpi_tb_get_next_root_index(&i);
+       if (ACPI_FAILURE(status)) {
+               goto release_and_exit;
        }
-       switch (table_desc->flags & ACPI_TABLE_ORIGIN_MASK) {
-       case ACPI_TABLE_ORIGIN_MAPPED:
-
-               acpi_os_unmap_memory(table_desc->pointer, table_desc->length);
-               break;
-
-       case ACPI_TABLE_ORIGIN_ALLOCATED:
 
-               ACPI_FREE(table_desc->pointer);
-               break;
+       *table_index = i;
+       acpi_tb_install_table_with_override(i, &new_table_desc, override);
 
-               /* Not mapped or allocated, there is nothing we can do */
+release_and_exit:
 
-       default:
+       /* Release the temporary table descriptor */
 
-               return;
-       }
-
-       table_desc->pointer = NULL;
+       acpi_tb_release_temp_table(&new_table_desc);
+       return_ACPI_STATUS(status);
 }
 
 /*******************************************************************************
  *
- * FUNCTION:    acpi_tb_terminate
+ * FUNCTION:    acpi_tb_override_table
  *
- * PARAMETERS:  None
+ * PARAMETERS:  old_table_desc      - Validated table descriptor to be
+ *                                    overridden
  *
  * RETURN:      None
  *
- * DESCRIPTION: Delete all internal ACPI tables
+ * DESCRIPTION: Attempt table override by calling the OSL override functions.
+ *              Note: If the table is overridden, then the entire new table
+ *              is acquired and returned by this function.
+ *              Before/after invocation, the table descriptor is in a state
+ *              that is "VALIDATED".
  *
  ******************************************************************************/
 
-void acpi_tb_terminate(void)
+void acpi_tb_override_table(struct acpi_table_desc *old_table_desc)
 {
-       u32 i;
-
-       ACPI_FUNCTION_TRACE(tb_terminate);
-
-       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
-
-       /* Delete the individual tables */
+       acpi_status status;
+       char *override_type;
+       struct acpi_table_desc new_table_desc;
+       struct acpi_table_header *table;
+       acpi_physical_address address;
+       u32 length;
 
-       for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) {
-               acpi_tb_delete_table(&acpi_gbl_root_table_list.tables[i]);
-       }
+       /* (1) Attempt logical override (returns a logical address) */
 
-       /*
-        * Delete the root table array if allocated locally. Array cannot be
-        * mapped, so we don't need to check for that flag.
-        */
-       if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) {
-               ACPI_FREE(acpi_gbl_root_table_list.tables);
+       status = acpi_os_table_override(old_table_desc->pointer, &table);
+       if (ACPI_SUCCESS(status) && table) {
+               acpi_tb_acquire_temp_table(&new_table_desc,
+                                          ACPI_PTR_TO_PHYSADDR(table),
+                                          ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL);
+               override_type = "Logical";
+               goto finish_override;
        }
 
-       acpi_gbl_root_table_list.tables = NULL;
-       acpi_gbl_root_table_list.flags = 0;
-       acpi_gbl_root_table_list.current_table_count = 0;
+       /* (2) Attempt physical override (returns a physical address) */
 
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "ACPI Tables freed\n"));
-       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+       status = acpi_os_physical_table_override(old_table_desc->pointer,
+                                                &address, &length);
+       if (ACPI_SUCCESS(status) && address && length) {
+               acpi_tb_acquire_temp_table(&new_table_desc, address,
+                                          ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL);
+               override_type = "Physical";
+               goto finish_override;
+       }
 
-       return_VOID;
-}
+       return;                 /* There was no override */
 
-/*******************************************************************************
- *
- * FUNCTION:    acpi_tb_delete_namespace_by_owner
- *
- * PARAMETERS:  table_index         - Table index
- *
- * RETURN:      Status
- *
- * DESCRIPTION: Delete all namespace objects created when this table was loaded.
- *
- ******************************************************************************/
-
-acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index)
-{
-       acpi_owner_id owner_id;
-       acpi_status status;
+finish_override:
 
-       ACPI_FUNCTION_TRACE(tb_delete_namespace_by_owner);
+       /* Validate and verify a table before overriding */
 
-       status = acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+       status = acpi_tb_verify_temp_table(&new_table_desc, NULL);
        if (ACPI_FAILURE(status)) {
-               return_ACPI_STATUS(status);
+               return;
        }
 
-       if (table_index >= acpi_gbl_root_table_list.current_table_count) {
-
-               /* The table index does not exist */
-
-               (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
-               return_ACPI_STATUS(AE_NOT_EXIST);
-       }
+       ACPI_INFO((AE_INFO, "%4.4s " ACPI_PRINTF_UINT
+                  " %s table override, new table: " ACPI_PRINTF_UINT,
+                  old_table_desc->signature.ascii,
+                  ACPI_FORMAT_TO_UINT(old_table_desc->address),
+                  override_type, ACPI_FORMAT_TO_UINT(new_table_desc.address)));
 
-       /* Get the owner ID for this table, used to delete namespace nodes */
+       /* We can now uninstall the original table */
 
-       owner_id = acpi_gbl_root_table_list.tables[table_index].owner_id;
-       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+       acpi_tb_uninstall_table(old_table_desc);
 
        /*
-        * Need to acquire the namespace writer lock to prevent interference
-        * with any concurrent namespace walks. The interpreter must be
-        * released during the deletion since the acquisition of the deletion
-        * lock may block, and also since the execution of a namespace walk
-        * must be allowed to use the interpreter.
+        * Replace the original table descriptor and keep its state as
+        * "VALIDATED".
         */
-       (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
-       status = acpi_ut_acquire_write_lock(&acpi_gbl_namespace_rw_lock);
+       acpi_tb_init_table_descriptor(old_table_desc, new_table_desc.address,
+                                     new_table_desc.flags,
+                                     new_table_desc.pointer);
+       acpi_tb_validate_temp_table(old_table_desc);
 
-       acpi_ns_delete_namespace_by_owner(owner_id);
-       if (ACPI_FAILURE(status)) {
-               return_ACPI_STATUS(status);
-       }
+       /* Release the temporary table descriptor */
 
-       acpi_ut_release_write_lock(&acpi_gbl_namespace_rw_lock);
-
-       status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER);
-       return_ACPI_STATUS(status);
+       acpi_tb_release_temp_table(&new_table_desc);
 }
 
 /*******************************************************************************
  *
- * FUNCTION:    acpi_tb_allocate_owner_id
+ * FUNCTION:    acpi_tb_store_table
  *
- * PARAMETERS:  table_index         - Table index
+ * PARAMETERS:  address             - Table address
+ *              table               - Table header
+ *              length              - Table length
+ *              flags               - Install flags
+ *              table_index         - Where the table index is returned
  *
- * RETURN:      Status
+ * RETURN:      Status and table index.
  *
- * DESCRIPTION: Allocates owner_id in table_desc
+ * DESCRIPTION: Add an ACPI table to the global table list
  *
  ******************************************************************************/
 
-acpi_status acpi_tb_allocate_owner_id(u32 table_index)
+acpi_status
+acpi_tb_store_table(acpi_physical_address address,
+                   struct acpi_table_header * table,
+                   u32 length, u8 flags, u32 *table_index)
 {
-       acpi_status status = AE_BAD_PARAMETER;
-
-       ACPI_FUNCTION_TRACE(tb_allocate_owner_id);
+       acpi_status status;
+       struct acpi_table_desc *table_desc;
 
-       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
-       if (table_index < acpi_gbl_root_table_list.current_table_count) {
-               status = acpi_ut_allocate_owner_id
-                   (&(acpi_gbl_root_table_list.tables[table_index].owner_id));
+       status = acpi_tb_get_next_root_index(table_index);
+       if (ACPI_FAILURE(status)) {
+               return (status);
        }
 
-       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
-       return_ACPI_STATUS(status);
-}
-
-/*******************************************************************************
- *
- * FUNCTION:    acpi_tb_release_owner_id
- *
- * PARAMETERS:  table_index         - Table index
- *
- * RETURN:      Status
- *
- * DESCRIPTION: Releases owner_id in table_desc
- *
- ******************************************************************************/
-
-acpi_status acpi_tb_release_owner_id(u32 table_index)
-{
-       acpi_status status = AE_BAD_PARAMETER;
-
-       ACPI_FUNCTION_TRACE(tb_release_owner_id);
-
-       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
-       if (table_index < acpi_gbl_root_table_list.current_table_count) {
-               acpi_ut_release_owner_id(&
-                                        (acpi_gbl_root_table_list.
-                                         tables[table_index].owner_id));
-               status = AE_OK;
-       }
+       /* Initialize added table */
 
-       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
-       return_ACPI_STATUS(status);
+       table_desc = &acpi_gbl_root_table_list.tables[*table_index];
+       acpi_tb_init_table_descriptor(table_desc, address, flags, table);
+       table_desc->pointer = table;
+       return (AE_OK);
 }
 
 /*******************************************************************************
  *
- * FUNCTION:    acpi_tb_get_owner_id
+ * FUNCTION:    acpi_tb_uninstall_table
  *
- * PARAMETERS:  table_index         - Table index
- *              owner_id            - Where the table owner_id is returned
+ * PARAMETERS:  table_desc          - Table descriptor
  *
- * RETURN:      Status
+ * RETURN:      None
  *
- * DESCRIPTION: returns owner_id for the ACPI table
+ * DESCRIPTION: Delete one internal ACPI table
  *
  ******************************************************************************/
 
-acpi_status acpi_tb_get_owner_id(u32 table_index, acpi_owner_id *owner_id)
+void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc)
 {
-       acpi_status status = AE_BAD_PARAMETER;
-
-       ACPI_FUNCTION_TRACE(tb_get_owner_id);
 
-       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
-       if (table_index < acpi_gbl_root_table_list.current_table_count) {
-               *owner_id =
-                   acpi_gbl_root_table_list.tables[table_index].owner_id;
-               status = AE_OK;
-       }
+       ACPI_FUNCTION_TRACE(tb_uninstall_table);
 
-       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
-       return_ACPI_STATUS(status);
-}
-
-/*******************************************************************************
- *
- * FUNCTION:    acpi_tb_is_table_loaded
- *
- * PARAMETERS:  table_index         - Table index
- *
- * RETURN:      Table Loaded Flag
- *
- ******************************************************************************/
+       /* Table must be installed */
 
-u8 acpi_tb_is_table_loaded(u32 table_index)
-{
-       u8 is_loaded = FALSE;
-
-       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
-       if (table_index < acpi_gbl_root_table_list.current_table_count) {
-               is_loaded = (u8)
-                   (acpi_gbl_root_table_list.tables[table_index].flags &
-                    ACPI_TABLE_IS_LOADED);
+       if (!table_desc->address) {
+               return_VOID;
        }
 
-       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
-       return (is_loaded);
-}
-
-/*******************************************************************************
- *
- * FUNCTION:    acpi_tb_set_table_loaded_flag
- *
- * PARAMETERS:  table_index         - Table index
- *              is_loaded           - TRUE if table is loaded, FALSE otherwise
- *
- * RETURN:      None
- *
- * DESCRIPTION: Sets the table loaded flag to either TRUE or FALSE.
- *
- ******************************************************************************/
-
-void acpi_tb_set_table_loaded_flag(u32 table_index, u8 is_loaded)
-{
+       acpi_tb_invalidate_table(table_desc);
 
-       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
-       if (table_index < acpi_gbl_root_table_list.current_table_count) {
-               if (is_loaded) {
-                       acpi_gbl_root_table_list.tables[table_index].flags |=
-                           ACPI_TABLE_IS_LOADED;
-               } else {
-                       acpi_gbl_root_table_list.tables[table_index].flags &=
-                           ~ACPI_TABLE_IS_LOADED;
-               }
+       if ((table_desc->flags & ACPI_TABLE_ORIGIN_MASK) ==
+           ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL) {
+               ACPI_FREE(ACPI_CAST_PTR(void, table_desc->address));
        }
 
-       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+       table_desc->address = ACPI_PTR_TO_PHYSADDR(NULL);
+       return_VOID;
 }
index 9fb85f3..6b1ca99 100644 (file)
@@ -49,8 +49,6 @@
 ACPI_MODULE_NAME("tbutils")
 
 /* Local prototypes */
-static acpi_status acpi_tb_validate_xsdt(acpi_physical_address address);
-
 static acpi_physical_address
 acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size);
 
@@ -178,9 +176,13 @@ struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index)
        }
 
        ACPI_MEMCPY(new_table, table_desc->pointer, table_desc->length);
-       acpi_tb_delete_table(table_desc);
-       table_desc->pointer = new_table;
-       table_desc->flags = ACPI_TABLE_ORIGIN_ALLOCATED;
+       acpi_tb_uninstall_table(table_desc);
+
+       acpi_tb_init_table_descriptor(&acpi_gbl_root_table_list.
+                                     tables[ACPI_TABLE_INDEX_DSDT],
+                                     ACPI_PTR_TO_PHYSADDR(new_table),
+                                     ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL,
+                                     new_table);
 
        ACPI_INFO((AE_INFO,
                   "Forced DSDT copy: length 0x%05X copied locally, original unmapped",
@@ -189,116 +191,6 @@ struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index)
        return (new_table);
 }
 
-/*******************************************************************************
- *
- * FUNCTION:    acpi_tb_install_table
- *
- * PARAMETERS:  address                 - Physical address of DSDT or FACS
- *              signature               - Table signature, NULL if no need to
- *                                        match
- *              table_index             - Index into root table array
- *
- * RETURN:      None
- *
- * DESCRIPTION: Install an ACPI table into the global data structure. The
- *              table override mechanism is called to allow the host
- *              OS to replace any table before it is installed in the root
- *              table array.
- *
- ******************************************************************************/
-
-void
-acpi_tb_install_table(acpi_physical_address address,
-                     char *signature, u32 table_index)
-{
-       struct acpi_table_header *table;
-       struct acpi_table_header *final_table;
-       struct acpi_table_desc *table_desc;
-
-       if (!address) {
-               ACPI_ERROR((AE_INFO,
-                           "Null physical address for ACPI table [%s]",
-                           signature));
-               return;
-       }
-
-       /* Map just the table header */
-
-       table = acpi_os_map_memory(address, sizeof(struct acpi_table_header));
-       if (!table) {
-               ACPI_ERROR((AE_INFO,
-                           "Could not map memory for table [%s] at %p",
-                           signature, ACPI_CAST_PTR(void, address)));
-               return;
-       }
-
-       /* If a particular signature is expected (DSDT/FACS), it must match */
-
-       if (signature && !ACPI_COMPARE_NAME(table->signature, signature)) {
-               ACPI_BIOS_ERROR((AE_INFO,
-                                "Invalid signature 0x%X for ACPI table, expected [%s]",
-                                *ACPI_CAST_PTR(u32, table->signature),
-                                signature));
-               goto unmap_and_exit;
-       }
-
-       /*
-        * Initialize the table entry. Set the pointer to NULL, since the
-        * table is not fully mapped at this time.
-        */
-       table_desc = &acpi_gbl_root_table_list.tables[table_index];
-
-       table_desc->address = address;
-       table_desc->pointer = NULL;
-       table_desc->length = table->length;
-       table_desc->flags = ACPI_TABLE_ORIGIN_MAPPED;
-       ACPI_MOVE_32_TO_32(table_desc->signature.ascii, table->signature);
-
-       /*
-        * ACPI Table Override:
-        *
-        * Before we install the table, let the host OS override it with a new
-        * one if desired. Any table within the RSDT/XSDT can be replaced,
-        * including the DSDT which is pointed to by the FADT.
-        *
-        * NOTE: If the table is overridden, then final_table will contain a
-        * mapped pointer to the full new table. If the table is not overridden,
-        * or if there has been a physical override, then the table will be
-        * fully mapped later (in verify table). In any case, we must
-        * unmap the header that was mapped above.
-        */
-       final_table = acpi_tb_table_override(table, table_desc);
-       if (!final_table) {
-               final_table = table;    /* There was no override */
-       }
-
-       acpi_tb_print_table_header(table_desc->address, final_table);
-
-       /* Set the global integer width (based upon revision of the DSDT) */
-
-       if (table_index == ACPI_TABLE_INDEX_DSDT) {
-               acpi_ut_set_integer_width(final_table->revision);
-       }
-
-       /*
-        * If we have a physical override during this early loading of the ACPI
-        * tables, unmap the table for now. It will be mapped again later when
-        * it is actually used. This supports very early loading of ACPI tables,
-        * before virtual memory is fully initialized and running within the
-        * host OS. Note: A logical override has the ACPI_TABLE_ORIGIN_OVERRIDE
-        * flag set and will not be deleted below.
-        */
-       if (final_table != table) {
-               acpi_tb_delete_table(table_desc);
-       }
-
-unmap_and_exit:
-
-       /* Always unmap the table header that we mapped above */
-
-       acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));
-}
-
 /*******************************************************************************
  *
  * FUNCTION:    acpi_tb_get_root_table_entry
@@ -355,87 +247,6 @@ acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size)
        }
 }
 
-/*******************************************************************************
- *
- * FUNCTION:    acpi_tb_validate_xsdt
- *
- * PARAMETERS:  address             - Physical address of the XSDT (from RSDP)
- *
- * RETURN:      Status. AE_OK if the table appears to be valid.
- *
- * DESCRIPTION: Validate an XSDT to ensure that it is of minimum size and does
- *              not contain any NULL entries. A problem that is seen in the
- *              field is that the XSDT exists, but is actually useless because
- *              of one or more (or all) NULL entries.
- *
- ******************************************************************************/
-
-static acpi_status acpi_tb_validate_xsdt(acpi_physical_address xsdt_address)
-{
-       struct acpi_table_header *table;
-       u8 *next_entry;
-       acpi_physical_address address;
-       u32 length;
-       u32 entry_count;
-       acpi_status status;
-       u32 i;
-
-       /* Get the XSDT length */
-
-       table =
-           acpi_os_map_memory(xsdt_address, sizeof(struct acpi_table_header));
-       if (!table) {
-               return (AE_NO_MEMORY);
-       }
-
-       length = table->length;
-       acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));
-
-       /*
-        * Minimum XSDT length is the size of the standard ACPI header
-        * plus one physical address entry
-        */
-       if (length < (sizeof(struct acpi_table_header) + ACPI_XSDT_ENTRY_SIZE)) {
-               return (AE_INVALID_TABLE_LENGTH);
-       }
-
-       /* Map the entire XSDT */
-
-       table = acpi_os_map_memory(xsdt_address, length);
-       if (!table) {
-               return (AE_NO_MEMORY);
-       }
-
-       /* Get the number of entries and pointer to first entry */
-
-       status = AE_OK;
-       next_entry = ACPI_ADD_PTR(u8, table, sizeof(struct acpi_table_header));
-       entry_count = (u32)((table->length - sizeof(struct acpi_table_header)) /
-                           ACPI_XSDT_ENTRY_SIZE);
-
-       /* Validate each entry (physical address) within the XSDT */
-
-       for (i = 0; i < entry_count; i++) {
-               address =
-                   acpi_tb_get_root_table_entry(next_entry,
-                                                ACPI_XSDT_ENTRY_SIZE);
-               if (!address) {
-
-                       /* Detected a NULL entry, XSDT is invalid */
-
-                       status = AE_NULL_ENTRY;
-                       break;
-               }
-
-               next_entry += ACPI_XSDT_ENTRY_SIZE;
-       }
-
-       /* Unmap table */
-
-       acpi_os_unmap_memory(table, length);
-       return (status);
-}
-
 /*******************************************************************************
  *
  * FUNCTION:    acpi_tb_parse_root_table
@@ -461,10 +272,10 @@ acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
        u32 table_count;
        struct acpi_table_header *table;
        acpi_physical_address address;
-       acpi_physical_address rsdt_address;
        u32 length;
        u8 *table_entry;
        acpi_status status;
+       u32 table_index;
 
        ACPI_FUNCTION_TRACE(tb_parse_root_table);
 
@@ -489,14 +300,11 @@ acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
                 * as per the ACPI specification.
                 */
                address = (acpi_physical_address) rsdp->xsdt_physical_address;
-               rsdt_address =
-                   (acpi_physical_address) rsdp->rsdt_physical_address;
                table_entry_size = ACPI_XSDT_ENTRY_SIZE;
        } else {
                /* Root table is an RSDT (32-bit physical addresses) */
 
                address = (acpi_physical_address) rsdp->rsdt_physical_address;
-               rsdt_address = address;
                table_entry_size = ACPI_RSDT_ENTRY_SIZE;
        }
 
@@ -506,24 +314,6 @@ acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
         */
        acpi_os_unmap_memory(rsdp, sizeof(struct acpi_table_rsdp));
 
-       /*
-        * If it is present and used, validate the XSDT for access/size
-        * and ensure that all table entries are at least non-NULL
-        */
-       if (table_entry_size == ACPI_XSDT_ENTRY_SIZE) {
-               status = acpi_tb_validate_xsdt(address);
-               if (ACPI_FAILURE(status)) {
-                       ACPI_BIOS_WARNING((AE_INFO,
-                                          "XSDT is invalid (%s), using RSDT",
-                                          acpi_format_exception(status)));
-
-                       /* Fall back to the RSDT */
-
-                       address = rsdt_address;
-                       table_entry_size = ACPI_RSDT_ENTRY_SIZE;
-               }
-       }
-
        /* Map the RSDT/XSDT table header to get the full table length */
 
        table = acpi_os_map_memory(address, sizeof(struct acpi_table_header));
@@ -576,55 +366,36 @@ acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
        /* Initialize the root table array from the RSDT/XSDT */
 
        for (i = 0; i < table_count; i++) {
-               if (acpi_gbl_root_table_list.current_table_count >=
-                   acpi_gbl_root_table_list.max_table_count) {
-
-                       /* There is no more room in the root table array, attempt resize */
-
-                       status = acpi_tb_resize_root_table_list();
-                       if (ACPI_FAILURE(status)) {
-                               ACPI_WARNING((AE_INFO,
-                                             "Truncating %u table entries!",
-                                             (unsigned) (table_count -
-                                              (acpi_gbl_root_table_list.
-                                                         current_table_count -
-                                                         2))));
-                               break;
-                       }
-               }
 
                /* Get the table physical address (32-bit for RSDT, 64-bit for XSDT) */
 
-               acpi_gbl_root_table_list.tables[acpi_gbl_root_table_list.
-                                               current_table_count].address =
+               address =
                    acpi_tb_get_root_table_entry(table_entry, table_entry_size);
 
-               table_entry += table_entry_size;
-               acpi_gbl_root_table_list.current_table_count++;
-       }
-
-       /*
-        * It is not possible to map more than one entry in some environments,
-        * so unmap the root table here before mapping other tables
-        */
-       acpi_os_unmap_memory(table, length);
+               /* Skip NULL entries in RSDT/XSDT */
 
-       /*
-        * Complete the initialization of the root table array by examining
-        * the header of each table
-        */
-       for (i = 2; i < acpi_gbl_root_table_list.current_table_count; i++) {
-               acpi_tb_install_table(acpi_gbl_root_table_list.tables[i].
-                                     address, NULL, i);
+               if (!address) {
+                       goto next_table;
+               }
 
-               /* Special case for FADT - validate it then get the DSDT and FACS */
+               status = acpi_tb_install_standard_table(address,
+                                                       ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
+                                                       FALSE, TRUE,
+                                                       &table_index);
 
-               if (ACPI_COMPARE_NAME
-                   (&acpi_gbl_root_table_list.tables[i].signature,
-                    ACPI_SIG_FADT)) {
-                       acpi_tb_parse_fadt(i);
+               if (ACPI_SUCCESS(status) &&
+                   ACPI_COMPARE_NAME(&acpi_gbl_root_table_list.
+                                     tables[table_index].signature,
+                                     ACPI_SIG_FADT)) {
+                       acpi_tb_parse_fadt(table_index);
                }
+
+next_table:
+
+               table_entry += table_entry_size;
        }
 
+       acpi_os_unmap_memory(table, length);
+
        return_ACPI_STATUS(AE_OK);
 }
index a159315..6482b0d 100644 (file)
@@ -206,8 +206,8 @@ acpi_status
 acpi_get_table_header(char *signature,
                      u32 instance, struct acpi_table_header *out_table_header)
 {
-       u32 i;
-       u32 j;
+       u32 i;
+       u32 j;
        struct acpi_table_header *header;
 
        /* Parameter validation */
@@ -233,7 +233,7 @@ acpi_get_table_header(char *signature,
                if (!acpi_gbl_root_table_list.tables[i].pointer) {
                        if ((acpi_gbl_root_table_list.tables[i].flags &
                             ACPI_TABLE_ORIGIN_MASK) ==
-                           ACPI_TABLE_ORIGIN_MAPPED) {
+                           ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL) {
                                header =
                                    acpi_os_map_memory(acpi_gbl_root_table_list.
                                                       tables[i].address,
@@ -321,8 +321,8 @@ acpi_get_table_with_size(char *signature,
               u32 instance, struct acpi_table_header **out_table,
               acpi_size *tbl_size)
 {
-       u32 i;
-       u32 j;
+       u32 i;
+       u32 j;
        acpi_status status;
 
        /* Parameter validation */
@@ -346,7 +346,7 @@ acpi_get_table_with_size(char *signature,
                }
 
                status =
-                   acpi_tb_verify_table(&acpi_gbl_root_table_list.tables[i]);
+                   acpi_tb_validate_table(&acpi_gbl_root_table_list.tables[i]);
                if (ACPI_SUCCESS(status)) {
                        *out_table = acpi_gbl_root_table_list.tables[i].pointer;
                        *tbl_size = acpi_gbl_root_table_list.tables[i].length;
@@ -390,7 +390,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_table)
  *
  ******************************************************************************/
 acpi_status
-acpi_get_table_by_index(u32 table_index, struct acpi_table_header **table)
+acpi_get_table_by_index(u32 table_index, struct acpi_table_header ** table)
 {
        acpi_status status;
 
@@ -416,8 +416,8 @@ acpi_get_table_by_index(u32 table_index, struct acpi_table_header **table)
                /* Table is not mapped, map it */
 
                status =
-                   acpi_tb_verify_table(&acpi_gbl_root_table_list.
-                                        tables[table_index]);
+                   acpi_tb_validate_table(&acpi_gbl_root_table_list.
+                                          tables[table_index]);
                if (ACPI_FAILURE(status)) {
                        (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
                        return_ACPI_STATUS(status);
index 0909420..ab5308b 100644 (file)
@@ -117,7 +117,7 @@ static acpi_status acpi_tb_load_namespace(void)
                                tables[ACPI_TABLE_INDEX_DSDT].signature),
                               ACPI_SIG_DSDT)
            ||
-           ACPI_FAILURE(acpi_tb_verify_table
+           ACPI_FAILURE(acpi_tb_validate_table
                         (&acpi_gbl_root_table_list.
                          tables[ACPI_TABLE_INDEX_DSDT]))) {
                status = AE_NO_ACPI_TABLES;
@@ -128,7 +128,7 @@ static acpi_status acpi_tb_load_namespace(void)
         * Save the DSDT pointer for simple access. This is the mapped memory
         * address. We must take care here because the address of the .Tables
         * array can change dynamically as tables are loaded at run-time. Note:
-        * .Pointer field is not validated until after call to acpi_tb_verify_table.
+        * .Pointer field is not validated until after call to acpi_tb_validate_table.
         */
        acpi_gbl_DSDT =
            acpi_gbl_root_table_list.tables[ACPI_TABLE_INDEX_DSDT].pointer;
@@ -174,24 +174,11 @@ static acpi_status acpi_tb_load_namespace(void)
                                        (acpi_gbl_root_table_list.tables[i].
                                         signature), ACPI_SIG_PSDT))
                    ||
-                   ACPI_FAILURE(acpi_tb_verify_table
+                   ACPI_FAILURE(acpi_tb_validate_table
                                 (&acpi_gbl_root_table_list.tables[i]))) {
                        continue;
                }
 
-               /*
-                * Optionally do not load any SSDTs from the RSDT/XSDT. This can
-                * be useful for debugging ACPI problems on some machines.
-                */
-               if (acpi_gbl_disable_ssdt_table_load) {
-                       ACPI_INFO((AE_INFO, "Ignoring %4.4s at %p",
-                                  acpi_gbl_root_table_list.tables[i].signature.
-                                  ascii, ACPI_CAST_PTR(void,
-                                                       acpi_gbl_root_table_list.
-                                                       tables[i].address)));
-                       continue;
-               }
-
                /* Ignore errors while loading tables, get as many as possible */
 
                (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
@@ -206,6 +193,45 @@ unlock_and_exit:
        return_ACPI_STATUS(status);
 }
 
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_install_table
+ *
+ * PARAMETERS:  address             - Address of the ACPI table to be installed.
+ *              physical            - Whether the address is a physical table
+ *                                    address or not
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Dynamically install an ACPI table.
+ *              Note: This function should only be invoked after
+ *                    acpi_initialize_tables() and before acpi_load_tables().
+ *
+ ******************************************************************************/
+
+acpi_status __init
+acpi_install_table(acpi_physical_address address, u8 physical)
+{
+       acpi_status status;
+       u8 flags;
+       u32 table_index;
+
+       ACPI_FUNCTION_TRACE(acpi_install_table);
+
+       if (physical) {
+               flags = ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL;
+       } else {
+               flags = ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL;
+       }
+
+       status = acpi_tb_install_standard_table(address, flags,
+                                               FALSE, FALSE, &table_index);
+
+       return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL_INIT(acpi_install_table)
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_load_table
@@ -222,11 +248,9 @@ unlock_and_exit:
  *              to ensure that the table is not deleted or unmapped.
  *
  ******************************************************************************/
-
 acpi_status acpi_load_table(struct acpi_table_header *table)
 {
        acpi_status status;
-       struct acpi_table_desc table_desc;
        u32 table_index;
 
        ACPI_FUNCTION_TRACE(acpi_load_table);
@@ -237,14 +261,6 @@ acpi_status acpi_load_table(struct acpi_table_header *table)
                return_ACPI_STATUS(AE_BAD_PARAMETER);
        }
 
-       /* Init local table descriptor */
-
-       ACPI_MEMSET(&table_desc, 0, sizeof(struct acpi_table_desc));
-       table_desc.address = ACPI_PTR_TO_PHYSADDR(table);
-       table_desc.pointer = table;
-       table_desc.length = table->length;
-       table_desc.flags = ACPI_TABLE_ORIGIN_UNKNOWN;
-
        /* Must acquire the interpreter lock during this operation */
 
        status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER);
@@ -255,7 +271,24 @@ acpi_status acpi_load_table(struct acpi_table_header *table)
        /* Install the table and load it into the namespace */
 
        ACPI_INFO((AE_INFO, "Host-directed Dynamic ACPI Table Load:"));
-       status = acpi_tb_add_table(&table_desc, &table_index);
+       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
+       status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table),
+                                               ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL,
+                                               TRUE, FALSE, &table_index);
+
+       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+       if (ACPI_FAILURE(status)) {
+               goto unlock_and_exit;
+       }
+
+       /*
+        * Note: Now table is "INSTALLED", it must be validated before
+        * using.
+        */
+       status =
+           acpi_tb_validate_table(&acpi_gbl_root_table_list.
+                                  tables[table_index]);
        if (ACPI_FAILURE(status)) {
                goto unlock_and_exit;
        }
index fbfa9ec..90ec37c 100644 (file)
@@ -462,7 +462,7 @@ char *acpi_ut_get_mutex_name(u32 mutex_id)
 
 /* Names for Notify() values, used for debug output */
 
-static const char *acpi_gbl_notify_value_names[ACPI_NOTIFY_MAX + 1] = {
+static const char *acpi_gbl_generic_notify[ACPI_NOTIFY_MAX + 1] = {
        /* 00 */ "Bus Check",
        /* 01 */ "Device Check",
        /* 02 */ "Device Wake",
@@ -473,23 +473,75 @@ static const char *acpi_gbl_notify_value_names[ACPI_NOTIFY_MAX + 1] = {
        /* 07 */ "Power Fault",
        /* 08 */ "Capabilities Check",
        /* 09 */ "Device PLD Check",
-       /* 10 */ "Reserved",
-       /* 11 */ "System Locality Update",
-       /* 12 */ "Shutdown Request"
+       /* 0A */ "Reserved",
+       /* 0B */ "System Locality Update",
+       /* 0C */ "Shutdown Request"
 };
 
-const char *acpi_ut_get_notify_name(u32 notify_value)
+static const char *acpi_gbl_device_notify[4] = {
+       /* 80 */ "Status Change",
+       /* 81 */ "Information Change",
+       /* 82 */ "Device-Specific Change",
+       /* 83 */ "Device-Specific Change"
+};
+
+static const char *acpi_gbl_processor_notify[4] = {
+       /* 80 */ "Performance Capability Change",
+       /* 81 */ "C-State Change",
+       /* 82 */ "Throttling Capability Change",
+       /* 83 */ "Device-Specific Change"
+};
+
+static const char *acpi_gbl_thermal_notify[4] = {
+       /* 80 */ "Thermal Status Change",
+       /* 81 */ "Thermal Trip Point Change",
+       /* 82 */ "Thermal Device List Change",
+       /* 83 */ "Thermal Relationship Change"
+};
+
+const char *acpi_ut_get_notify_name(u32 notify_value, acpi_object_type type)
 {
 
+       /* 00 - 0C are common to all object types */
+
        if (notify_value <= ACPI_NOTIFY_MAX) {
-               return (acpi_gbl_notify_value_names[notify_value]);
-       } else if (notify_value <= ACPI_MAX_SYS_NOTIFY) {
+               return (acpi_gbl_generic_notify[notify_value]);
+       }
+
+       /* 0D - 7F are reserved */
+
+       if (notify_value <= ACPI_MAX_SYS_NOTIFY) {
                return ("Reserved");
-       } else if (notify_value <= ACPI_MAX_DEVICE_SPECIFIC_NOTIFY) {
-               return ("Device Specific");
-       } else {
-               return ("Hardware Specific");
        }
+
+       /* 80 - 83 are per-object-type */
+
+       if (notify_value <= 0x83) {
+               switch (type) {
+               case ACPI_TYPE_ANY:
+               case ACPI_TYPE_DEVICE:
+                       return (acpi_gbl_device_notify[notify_value - 0x80]);
+
+               case ACPI_TYPE_PROCESSOR:
+                       return (acpi_gbl_processor_notify[notify_value - 0x80]);
+
+               case ACPI_TYPE_THERMAL:
+                       return (acpi_gbl_thermal_notify[notify_value - 0x80]);
+
+               default:
+                       return ("Target object type does not support notifies");
+               }
+       }
+
+       /* 84 - BF are device-specific */
+
+       if (notify_value <= ACPI_MAX_DEVICE_SPECIFIC_NOTIFY) {
+               return ("Device-Specific");
+       }
+
+       /* C0 and above are hardware-specific */
+
+       return ("Hardware-Specific");
 }
 #endif
 
index f3abeae..d69be3c 100644 (file)
@@ -55,28 +55,7 @@ ACPI_MODULE_NAME("utglobal")
  * Static global variable initialization.
  *
  ******************************************************************************/
-/* Debug output control masks */
-u32 acpi_dbg_level = ACPI_DEBUG_DEFAULT;
-
-u32 acpi_dbg_layer = 0;
-
-/* acpi_gbl_FADT is a local copy of the FADT, converted to a common format. */
-
-struct acpi_table_fadt acpi_gbl_FADT;
-u32 acpi_gbl_trace_flags;
-acpi_name acpi_gbl_trace_method_name;
-u8 acpi_gbl_system_awake_and_running;
-u32 acpi_current_gpe_count;
-
-/*
- * ACPI 5.0 introduces the concept of a "reduced hardware platform", meaning
- * that the ACPI hardware is no longer required. A flag in the FADT indicates
- * a reduced HW machine, and that flag is duplicated here for convenience.
- */
-u8 acpi_gbl_reduced_hardware;
-
 /* Various state name strings */
-
 const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT] = {
        "\\_S0_",
        "\\_S1_",
@@ -337,7 +316,6 @@ acpi_status acpi_ut_init_globals(void)
        acpi_gbl_acpi_hardware_present = TRUE;
        acpi_gbl_last_owner_id_index = 0;
        acpi_gbl_next_owner_id_offset = 0;
-       acpi_gbl_trace_method_name = 0;
        acpi_gbl_trace_dbg_level = 0;
        acpi_gbl_trace_dbg_layer = 0;
        acpi_gbl_debugger_configuration = DEBUGGER_THREADING;
@@ -377,9 +355,7 @@ acpi_status acpi_ut_init_globals(void)
        acpi_gbl_disable_mem_tracking = FALSE;
 #endif
 
-#ifdef ACPI_DEBUGGER
-       acpi_gbl_db_terminate_threads = FALSE;
-#endif
+       ACPI_DEBUGGER_EXEC(acpi_gbl_db_terminate_threads = FALSE);
 
        return_ACPI_STATUS(AE_OK);
 }
index 7721933..6dc54b3 100644 (file)
@@ -353,7 +353,7 @@ void acpi_ut_print_string(char *string, u16 max_length)
        }
 
        acpi_os_printf("\"");
-       for (i = 0; string[i] && (i < max_length); i++) {
+       for (i = 0; (i < max_length) && string[i]; i++) {
 
                /* Escape sequences */
 
index edd8611..88ef77f 100644 (file)
@@ -53,6 +53,7 @@ ACPI_MODULE_NAME("utxferror")
  * This module is used for the in-kernel ACPICA as well as the ACPICA
  * tools/applications.
  */
+#ifndef ACPI_NO_ERROR_MESSAGES /* Entire module */
 /*******************************************************************************
  *
  * FUNCTION:    acpi_error
@@ -249,3 +250,4 @@ acpi_bios_warning(const char *module_name,
 }
 
 ACPI_EXPORT_SYMBOL(acpi_bios_warning)
+#endif                         /* ACPI_NO_ERROR_MESSAGES */
index 1be6f55..a095d4f 100644 (file)
@@ -202,7 +202,7 @@ static void check_vendor_extension(u64 paddr,
 
        if (!offset)
                return;
-       v = acpi_os_map_memory(paddr + offset, sizeof(*v));
+       v = acpi_os_map_iomem(paddr + offset, sizeof(*v));
        if (!v)
                return;
        sbdf = v->pcie_sbdf;
@@ -210,7 +210,7 @@ static void check_vendor_extension(u64 paddr,
                sbdf >> 24, (sbdf >> 16) & 0xff,
                (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7,
                 v->vendor_id, v->device_id, v->rev_id);
-       acpi_os_unmap_memory(v, sizeof(*v));
+       acpi_os_unmap_iomem(v, sizeof(*v));
 }
 
 static void *einj_get_parameter_address(void)
@@ -236,7 +236,7 @@ static void *einj_get_parameter_address(void)
        if (pa_v5) {
                struct set_error_type_with_address *v5param;
 
-               v5param = acpi_os_map_memory(pa_v5, sizeof(*v5param));
+               v5param = acpi_os_map_iomem(pa_v5, sizeof(*v5param));
                if (v5param) {
                        acpi5 = 1;
                        check_vendor_extension(pa_v5, v5param);
@@ -246,11 +246,11 @@ static void *einj_get_parameter_address(void)
        if (param_extension && pa_v4) {
                struct einj_parameter *v4param;
 
-               v4param = acpi_os_map_memory(pa_v4, sizeof(*v4param));
+               v4param = acpi_os_map_iomem(pa_v4, sizeof(*v4param));
                if (!v4param)
                        return NULL;
                if (v4param->reserved1 || v4param->reserved2) {
-                       acpi_os_unmap_memory(v4param, sizeof(*v4param));
+                       acpi_os_unmap_iomem(v4param, sizeof(*v4param));
                        return NULL;
                }
                return v4param;
@@ -794,7 +794,7 @@ err_unmap:
                        sizeof(struct set_error_type_with_address) :
                        sizeof(struct einj_parameter);
 
-               acpi_os_unmap_memory(einj_param, size);
+               acpi_os_unmap_iomem(einj_param, size);
        }
        apei_exec_post_unmap_gars(&ctx);
 err_release:
@@ -816,7 +816,7 @@ static void __exit einj_exit(void)
                        sizeof(struct set_error_type_with_address) :
                        sizeof(struct einj_parameter);
 
-               acpi_os_unmap_memory(einj_param, size);
+               acpi_os_unmap_iomem(einj_param, size);
        }
        einj_exec_ctx_init(&ctx);
        apei_exec_post_unmap_gars(&ctx);
index 6e7b2a1..e48fc98 100644 (file)
 /* Battery power unit: 0 means mW, 1 means mA */
 #define ACPI_BATTERY_POWER_UNIT_MA     1
 
+#define ACPI_BATTERY_STATE_DISCHARGING 0x1
+#define ACPI_BATTERY_STATE_CHARGING    0x2
+#define ACPI_BATTERY_STATE_CRITICAL    0x4
+
 #define _COMPONENT             ACPI_BATTERY_COMPONENT
 
 ACPI_MODULE_NAME("battery");
@@ -169,7 +173,7 @@ static int acpi_battery_get_state(struct acpi_battery *battery);
 
 static int acpi_battery_is_charged(struct acpi_battery *battery)
 {
-       /* either charging or discharging */
+       /* charging, discharging or critical low */
        if (battery->state != 0)
                return 0;
 
@@ -204,9 +208,9 @@ static int acpi_battery_get_property(struct power_supply *psy,
                return -ENODEV;
        switch (psp) {
        case POWER_SUPPLY_PROP_STATUS:
-               if (battery->state & 0x01)
+               if (battery->state & ACPI_BATTERY_STATE_DISCHARGING)
                        val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               else if (battery->state & 0x02)
+               else if (battery->state & ACPI_BATTERY_STATE_CHARGING)
                        val->intval = POWER_SUPPLY_STATUS_CHARGING;
                else if (acpi_battery_is_charged(battery))
                        val->intval = POWER_SUPPLY_STATUS_FULL;
@@ -269,6 +273,17 @@ static int acpi_battery_get_property(struct power_supply *psy,
                else
                        val->intval = 0;
                break;
+       case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+               if (battery->state & ACPI_BATTERY_STATE_CRITICAL)
+                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+               else if (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
+                       (battery->capacity_now <= battery->alarm))
+                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+               else if (acpi_battery_is_charged(battery))
+                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+               else
+                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+               break;
        case POWER_SUPPLY_PROP_MODEL_NAME:
                val->strval = battery->model_number;
                break;
@@ -296,6 +311,7 @@ static enum power_supply_property charge_battery_props[] = {
        POWER_SUPPLY_PROP_CHARGE_FULL,
        POWER_SUPPLY_PROP_CHARGE_NOW,
        POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
        POWER_SUPPLY_PROP_MODEL_NAME,
        POWER_SUPPLY_PROP_MANUFACTURER,
        POWER_SUPPLY_PROP_SERIAL_NUMBER,
@@ -313,6 +329,7 @@ static enum power_supply_property energy_battery_props[] = {
        POWER_SUPPLY_PROP_ENERGY_FULL,
        POWER_SUPPLY_PROP_ENERGY_NOW,
        POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
        POWER_SUPPLY_PROP_MODEL_NAME,
        POWER_SUPPLY_PROP_MANUFACTURER,
        POWER_SUPPLY_PROP_SERIAL_NUMBER,
@@ -605,7 +622,8 @@ static int sysfs_add_battery(struct acpi_battery *battery)
        battery->bat.type = POWER_SUPPLY_TYPE_BATTERY;
        battery->bat.get_property = acpi_battery_get_property;
 
-       result = power_supply_register(&battery->device->dev, &battery->bat);
+       result = power_supply_register_no_ws(&battery->device->dev, &battery->bat);
+
        if (result)
                return result;
        return device_create_file(battery->bat.dev, &alarm_attr);
@@ -696,7 +714,7 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
        }
 }
 
-static int acpi_battery_update(struct acpi_battery *battery)
+static int acpi_battery_update(struct acpi_battery *battery, bool resume)
 {
        int result, old_present = acpi_battery_present(battery);
        result = acpi_battery_get_status(battery);
@@ -707,6 +725,10 @@ static int acpi_battery_update(struct acpi_battery *battery)
                battery->update_time = 0;
                return 0;
        }
+
+       if (resume)
+               return 0;
+
        if (!battery->update_time ||
            old_present != acpi_battery_present(battery)) {
                result = acpi_battery_get_info(battery);
@@ -720,7 +742,19 @@ static int acpi_battery_update(struct acpi_battery *battery)
                        return result;
        }
        result = acpi_battery_get_state(battery);
+       if (result)
+               return result;
        acpi_battery_quirks(battery);
+
+       /*
+        * Wakeup the system if battery is critical low
+        * or lower than the alarm level
+        */
+       if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) ||
+           (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
+            (battery->capacity_now <= battery->alarm)))
+               pm_wakeup_event(&battery->device->dev, 0);
+
        return result;
 }
 
@@ -915,7 +949,7 @@ static print_func acpi_print_funcs[ACPI_BATTERY_NUMFILES] = {
 static int acpi_battery_read(int fid, struct seq_file *seq)
 {
        struct acpi_battery *battery = seq->private;
-       int result = acpi_battery_update(battery);
+       int result = acpi_battery_update(battery, false);
        return acpi_print_funcs[fid](seq, result);
 }
 
@@ -1030,7 +1064,7 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event)
        old = battery->bat.dev;
        if (event == ACPI_BATTERY_NOTIFY_INFO)
                acpi_battery_refresh(battery);
-       acpi_battery_update(battery);
+       acpi_battery_update(battery, false);
        acpi_bus_generate_netlink_event(device->pnp.device_class,
                                        dev_name(&device->dev), event,
                                        acpi_battery_present(battery));
@@ -1045,13 +1079,27 @@ static int battery_notify(struct notifier_block *nb,
 {
        struct acpi_battery *battery = container_of(nb, struct acpi_battery,
                                                    pm_nb);
+       int result;
+
        switch (mode) {
        case PM_POST_HIBERNATION:
        case PM_POST_SUSPEND:
-               if (battery->bat.dev) {
-                       sysfs_remove_battery(battery);
-                       sysfs_add_battery(battery);
-               }
+               if (!acpi_battery_present(battery))
+                       return 0;
+
+               if (!battery->bat.dev) {
+                       result = acpi_battery_get_info(battery);
+                       if (result)
+                               return result;
+
+                       result = sysfs_add_battery(battery);
+                       if (result)
+                               return result;
+               } else
+                       acpi_battery_refresh(battery);
+
+               acpi_battery_init_alarm(battery);
+               acpi_battery_get_state(battery);
                break;
        }
 
@@ -1087,7 +1135,7 @@ static int acpi_battery_add(struct acpi_device *device)
        mutex_init(&battery->sysfs_lock);
        if (acpi_has_method(battery->device->handle, "_BIX"))
                set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
-       result = acpi_battery_update(battery);
+       result = acpi_battery_update(battery, false);
        if (result)
                goto fail;
 #ifdef CONFIG_ACPI_PROCFS_POWER
@@ -1107,6 +1155,8 @@ static int acpi_battery_add(struct acpi_device *device)
        battery->pm_nb.notifier_call = battery_notify;
        register_pm_notifier(&battery->pm_nb);
 
+       device_init_wakeup(&device->dev, 1);
+
        return result;
 
 fail:
@@ -1123,6 +1173,7 @@ static int acpi_battery_remove(struct acpi_device *device)
 
        if (!device || !acpi_driver_data(device))
                return -EINVAL;
+       device_init_wakeup(&device->dev, 0);
        battery = acpi_driver_data(device);
        unregister_pm_notifier(&battery->pm_nb);
 #ifdef CONFIG_ACPI_PROCFS_POWER
@@ -1149,7 +1200,7 @@ static int acpi_battery_resume(struct device *dev)
                return -EINVAL;
 
        battery->update_time = 0;
-       acpi_battery_update(battery);
+       acpi_battery_update(battery, true);
        return 0;
 }
 #else
index cf925c4..c5bc8cf 100644 (file)
@@ -52,6 +52,12 @@ struct proc_dir_entry *acpi_root_dir;
 EXPORT_SYMBOL(acpi_root_dir);
 
 #ifdef CONFIG_X86
+#ifdef CONFIG_ACPI_CUSTOM_DSDT
+static inline int set_copy_dsdt(const struct dmi_system_id *id)
+{
+       return 0;
+}
+#else
 static int set_copy_dsdt(const struct dmi_system_id *id)
 {
        printk(KERN_NOTICE "%s detected - "
@@ -59,6 +65,7 @@ static int set_copy_dsdt(const struct dmi_system_id *id)
        acpi_gbl_copy_dsdt_locally = 1;
        return 0;
 }
+#endif
 
 static struct dmi_system_id dsdt_dmi_table[] __initdata = {
        /*
@@ -132,6 +139,21 @@ void acpi_bus_private_data_handler(acpi_handle handle,
 }
 EXPORT_SYMBOL(acpi_bus_private_data_handler);
 
+int acpi_bus_attach_private_data(acpi_handle handle, void *data)
+{
+       acpi_status status;
+
+       status = acpi_attach_data(handle,
+                       acpi_bus_private_data_handler, data);
+       if (ACPI_FAILURE(status)) {
+               acpi_handle_debug(handle, "Error attaching device data\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_bus_attach_private_data);
+
 int acpi_bus_get_private_data(acpi_handle handle, void **data)
 {
        acpi_status status;
@@ -140,15 +162,20 @@ int acpi_bus_get_private_data(acpi_handle handle, void **data)
                return -EINVAL;
 
        status = acpi_get_data(handle, acpi_bus_private_data_handler, data);
-       if (ACPI_FAILURE(status) || !*data) {
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
-                               handle));
+       if (ACPI_FAILURE(status)) {
+               acpi_handle_debug(handle, "No context for object\n");
                return -ENODEV;
        }
 
        return 0;
 }
-EXPORT_SYMBOL(acpi_bus_get_private_data);
+EXPORT_SYMBOL_GPL(acpi_bus_get_private_data);
+
+void acpi_bus_detach_private_data(acpi_handle handle)
+{
+       acpi_detach_data(handle, acpi_bus_private_data_handler);
+}
+EXPORT_SYMBOL_GPL(acpi_bus_detach_private_data);
 
 void acpi_bus_no_hotplug(acpi_handle handle)
 {
@@ -340,16 +367,18 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
 {
        struct acpi_device *adev;
        struct acpi_driver *driver;
-       acpi_status status;
        u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
+       bool hotplug_event = false;
 
        switch (type) {
        case ACPI_NOTIFY_BUS_CHECK:
                acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
+               hotplug_event = true;
                break;
 
        case ACPI_NOTIFY_DEVICE_CHECK:
                acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
+               hotplug_event = true;
                break;
 
        case ACPI_NOTIFY_DEVICE_WAKE:
@@ -358,6 +387,7 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
 
        case ACPI_NOTIFY_EJECT_REQUEST:
                acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
+               hotplug_event = true;
                break;
 
        case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
@@ -393,16 +423,9 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
            (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
                driver->ops.notify(adev, type);
 
-       switch (type) {
-       case ACPI_NOTIFY_BUS_CHECK:
-       case ACPI_NOTIFY_DEVICE_CHECK:
-       case ACPI_NOTIFY_EJECT_REQUEST:
-               status = acpi_hotplug_schedule(adev, type);
-               if (ACPI_SUCCESS(status))
-                       return;
-       default:
-               break;
-       }
+       if (hotplug_event && ACPI_SUCCESS(acpi_hotplug_schedule(adev, type)))
+               return;
+
        acpi_bus_put_acpi_device(adev);
        return;
 
@@ -466,6 +489,9 @@ void __init acpi_early_init(void)
 
        printk(KERN_INFO PREFIX "Core revision %08x\n", ACPI_CA_VERSION);
 
+       /* It's safe to verify table checksums during late stage */
+       acpi_gbl_verify_table_checksum = TRUE;
+
        /* enable workarounds, unless strict ACPI spec. compliance */
        if (!acpi_strict)
                acpi_gbl_enable_interpreter_slack = TRUE;
index 63119d0..76f7cff 100644 (file)
@@ -41,6 +41,8 @@ static const struct acpi_device_id container_device_ids[] = {
        {"", 0},
 };
 
+#ifdef CONFIG_ACPI_CONTAINER
+
 static int acpi_container_offline(struct container_dev *cdev)
 {
        struct acpi_device *adev = ACPI_COMPANION(&cdev->dev);
@@ -107,7 +109,20 @@ static struct acpi_scan_handler container_handler = {
        },
 };
 
+void __init acpi_container_init(void)
+{
+       acpi_scan_add_handler(&container_handler);
+}
+
+#else
+
+static struct acpi_scan_handler container_handler = {
+       .ids = container_device_ids,
+};
+
 void __init acpi_container_init(void)
 {
        acpi_scan_add_handler_with_hotplug(&container_handler, "container");
 }
+
+#endif /* CONFIG_ACPI_CONTAINER */
index d047739..49a5127 100644 (file)
@@ -900,17 +900,46 @@ EXPORT_SYMBOL_GPL(acpi_dev_resume_early);
  */
 int acpi_subsys_prepare(struct device *dev)
 {
-       /*
-        * Devices having power.ignore_children set may still be necessary for
-        * suspending their children in the next phase of device suspend.
-        */
-       if (dev->power.ignore_children)
-               pm_runtime_resume(dev);
+       struct acpi_device *adev = ACPI_COMPANION(dev);
+       u32 sys_target;
+       int ret, state;
+
+       ret = pm_generic_prepare(dev);
+       if (ret < 0)
+               return ret;
+
+       if (!adev || !pm_runtime_suspended(dev)
+           || device_may_wakeup(dev) != !!adev->wakeup.prepare_count)
+               return 0;
+
+       sys_target = acpi_target_system_state();
+       if (sys_target == ACPI_STATE_S0)
+               return 1;
 
-       return pm_generic_prepare(dev);
+       if (adev->power.flags.dsw_present)
+               return 0;
+
+       ret = acpi_dev_pm_get_state(dev, adev, sys_target, NULL, &state);
+       return !ret && state == adev->power.state;
 }
 EXPORT_SYMBOL_GPL(acpi_subsys_prepare);
 
+/**
+ * acpi_subsys_complete - Finalize device's resume during system resume.
+ * @dev: Device to handle.
+ */
+void acpi_subsys_complete(struct device *dev)
+{
+       /*
+        * If the device had been runtime-suspended before the system went into
+        * the sleep state it is going out of and it has never been resumed till
+        * now, resume it in case the firmware powered it up.
+        */
+       if (dev->power.direct_complete)
+               pm_request_resume(dev);
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_complete);
+
 /**
  * acpi_subsys_suspend - Run the device driver's suspend callback.
  * @dev: Device to handle.
@@ -923,6 +952,7 @@ int acpi_subsys_suspend(struct device *dev)
        pm_runtime_resume(dev);
        return pm_generic_suspend(dev);
 }
+EXPORT_SYMBOL_GPL(acpi_subsys_suspend);
 
 /**
  * acpi_subsys_suspend_late - Suspend device using ACPI.
@@ -968,6 +998,7 @@ int acpi_subsys_freeze(struct device *dev)
        pm_runtime_resume(dev);
        return pm_generic_freeze(dev);
 }
+EXPORT_SYMBOL_GPL(acpi_subsys_freeze);
 
 #endif /* CONFIG_PM_SLEEP */
 
@@ -979,6 +1010,7 @@ static struct dev_pm_domain acpi_general_pm_domain = {
 #endif
 #ifdef CONFIG_PM_SLEEP
                .prepare = acpi_subsys_prepare,
+               .complete = acpi_subsys_complete,
                .suspend = acpi_subsys_suspend,
                .suspend_late = acpi_subsys_suspend_late,
                .resume_early = acpi_subsys_resume_early,
index 9573913..7de5b60 100644 (file)
@@ -30,12 +30,10 @@ void acpi_pci_root_init(void);
 void acpi_pci_link_init(void);
 void acpi_processor_init(void);
 void acpi_platform_init(void);
+void acpi_pnp_init(void);
 int acpi_sysfs_init(void);
-#ifdef CONFIG_ACPI_CONTAINER
 void acpi_container_init(void);
-#else
-static inline void acpi_container_init(void) {}
-#endif
+void acpi_memory_hotplug_init(void);
 #ifdef CONFIG_ACPI_DOCK
 void register_dock_dependent_device(struct acpi_device *adev,
                                    acpi_handle dshandle);
@@ -47,11 +45,6 @@ static inline void register_dock_dependent_device(struct acpi_device *adev,
 static inline int dock_notify(struct acpi_device *adev, u32 event) { return -ENODEV; }
 static inline void acpi_dock_add(struct acpi_device *adev) {}
 #endif
-#ifdef CONFIG_ACPI_HOTPLUG_MEMORY
-void acpi_memory_hotplug_init(void);
-#else
-static inline void acpi_memory_hotplug_init(void) {}
-#endif
 #ifdef CONFIG_X86
 void acpi_cmos_rtc_init(void);
 #else
@@ -72,11 +65,7 @@ int acpi_debugfs_init(void);
 #else
 static inline void acpi_debugfs_init(void) { return; }
 #endif
-#ifdef CONFIG_X86_INTEL_LPSS
 void acpi_lpss_init(void);
-#else
-static inline void acpi_lpss_init(void) {}
-#endif
 
 acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src);
 bool acpi_queue_hotplug_work(struct work_struct *work);
@@ -180,8 +169,7 @@ static inline void suspend_nvs_restore(void) {}
   -------------------------------------------------------------------------- */
 struct platform_device;
 
-int acpi_create_platform_device(struct acpi_device *adev,
-                               const struct acpi_device_id *id);
+struct platform_device *acpi_create_platform_device(struct acpi_device *adev);
 
 /*--------------------------------------------------------------------------
                                        Video
index de4fe03..85287b8 100644 (file)
@@ -139,8 +139,8 @@ void suspend_nvs_free(void)
                                        iounmap(entry->kaddr);
                                        entry->unmap = false;
                                } else {
-                                       acpi_os_unmap_memory(entry->kaddr,
-                                                            entry->size);
+                                       acpi_os_unmap_iomem(entry->kaddr,
+                                                           entry->size);
                                }
                                entry->kaddr = NULL;
                        }
index 6776c59..147bc1b 100644 (file)
@@ -355,7 +355,7 @@ static void acpi_unmap(acpi_physical_address pg_off, void __iomem *vaddr)
 }
 
 void __iomem *__init_refok
-acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
+acpi_os_map_iomem(acpi_physical_address phys, acpi_size size)
 {
        struct acpi_ioremap *map;
        void __iomem *virt;
@@ -401,10 +401,17 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
 
        list_add_tail_rcu(&map->list, &acpi_ioremaps);
 
- out:
+out:
        mutex_unlock(&acpi_ioremap_lock);
        return map->virt + (phys - map->phys);
 }
+EXPORT_SYMBOL_GPL(acpi_os_map_iomem);
+
+void *__init_refok
+acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
+{
+       return (void *)acpi_os_map_iomem(phys, size);
+}
 EXPORT_SYMBOL_GPL(acpi_os_map_memory);
 
 static void acpi_os_drop_map_ref(struct acpi_ioremap *map)
@@ -422,7 +429,7 @@ static void acpi_os_map_cleanup(struct acpi_ioremap *map)
        }
 }
 
-void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size)
+void __ref acpi_os_unmap_iomem(void __iomem *virt, acpi_size size)
 {
        struct acpi_ioremap *map;
 
@@ -443,6 +450,12 @@ void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size)
 
        acpi_os_map_cleanup(map);
 }
+EXPORT_SYMBOL_GPL(acpi_os_unmap_iomem);
+
+void __ref acpi_os_unmap_memory(void *virt, acpi_size size)
+{
+       return acpi_os_unmap_iomem((void __iomem *)virt, size);
+}
 EXPORT_SYMBOL_GPL(acpi_os_unmap_memory);
 
 void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size)
@@ -464,7 +477,7 @@ int acpi_os_map_generic_address(struct acpi_generic_address *gas)
        if (!addr || !gas->bit_width)
                return -EINVAL;
 
-       virt = acpi_os_map_memory(addr, gas->bit_width / 8);
+       virt = acpi_os_map_iomem(addr, gas->bit_width / 8);
        if (!virt)
                return -EIO;
 
@@ -1770,16 +1783,15 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object)
 }
 #endif
 
-static int __init acpi_no_auto_ssdt_setup(char *s)
+static int __init acpi_no_static_ssdt_setup(char *s)
 {
-        printk(KERN_NOTICE PREFIX "SSDT auto-load disabled\n");
+       acpi_gbl_disable_ssdt_table_install = TRUE;
+       pr_info("ACPI: static SSDT installation disabled\n");
 
-        acpi_gbl_disable_ssdt_table_load = TRUE;
-
-        return 1;
+       return 0;
 }
 
-__setup("acpi_no_auto_ssdt", acpi_no_auto_ssdt_setup);
+early_param("acpi_no_static_ssdt", acpi_no_static_ssdt_setup);
 
 static int __init acpi_disable_return_repair(char *s)
 {
index 7f70f31..4fcbd67 100644 (file)
@@ -121,6 +121,13 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb,
        struct acpi_processor *pr = per_cpu(processors, cpu);
        struct acpi_device *device;
 
+       /*
+        * CPU_STARTING and CPU_DYING must not sleep. Return here since
+        * acpi_bus_get_device() may sleep.
+        */
+       if (action == CPU_STARTING || action == CPU_DYING)
+               return NOTIFY_DONE;
+
        if (!pr || acpi_bus_get_device(pr->handle, &device))
                return NOTIFY_DONE;
 
index 7efe546..f775fa0 100644 (file)
@@ -84,7 +84,7 @@ EXPORT_SYMBOL_GPL(acpi_initialize_hp_context);
 
 int acpi_scan_add_handler(struct acpi_scan_handler *handler)
 {
-       if (!handler || !handler->attach)
+       if (!handler)
                return -EINVAL;
 
        list_add_tail(&handler->list_node, &acpi_scan_handlers_list);
@@ -1551,9 +1551,13 @@ static void acpi_bus_get_power_flags(struct acpi_device *device)
         */
        if (acpi_has_method(device->handle, "_PSC"))
                device->power.flags.explicit_get = 1;
+
        if (acpi_has_method(device->handle, "_IRC"))
                device->power.flags.inrush_current = 1;
 
+       if (acpi_has_method(device->handle, "_DSW"))
+               device->power.flags.dsw_present = 1;
+
        /*
         * Enumerate supported power management states
         */
@@ -1793,8 +1797,10 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,
                        return;
                }
 
-               if (info->valid & ACPI_VALID_HID)
+               if (info->valid & ACPI_VALID_HID) {
                        acpi_add_id(pnp, info->hardware_id.string);
+                       pnp->type.platform_id = 1;
+               }
                if (info->valid & ACPI_VALID_CID) {
                        cid_list = &info->compatible_id_list;
                        for (i = 0; i < cid_list->count; i++)
@@ -1973,6 +1979,9 @@ static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler,
 {
        const struct acpi_device_id *devid;
 
+       if (handler->match)
+               return handler->match(idstr, matchid);
+
        for (devid = handler->ids; devid->id[0]; devid++)
                if (!strcmp((char *)devid->id, idstr)) {
                        if (matchid)
@@ -2061,6 +2070,44 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
        return AE_OK;
 }
 
+static int acpi_check_spi_i2c_slave(struct acpi_resource *ares, void *data)
+{
+       bool *is_spi_i2c_slave_p = data;
+
+       if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
+               return 1;
+
+       /*
+        * devices that are connected to UART still need to be enumerated to
+        * platform bus
+        */
+       if (ares->data.common_serial_bus.type != ACPI_RESOURCE_SERIAL_TYPE_UART)
+               *is_spi_i2c_slave_p = true;
+
+        /* no need to do more checking */
+       return -1;
+}
+
+static void acpi_default_enumeration(struct acpi_device *device)
+{
+       struct list_head resource_list;
+       bool is_spi_i2c_slave = false;
+
+       if (!device->pnp.type.platform_id || device->handler)
+               return;
+
+       /*
+        * Do not enemerate SPI/I2C slaves as they will be enuerated by their
+        * respective parents.
+        */
+       INIT_LIST_HEAD(&resource_list);
+       acpi_dev_get_resources(device, &resource_list, acpi_check_spi_i2c_slave,
+                              &is_spi_i2c_slave);
+       acpi_dev_free_resource_list(&resource_list);
+       if (!is_spi_i2c_slave)
+               acpi_create_platform_device(device);
+}
+
 static int acpi_scan_attach_handler(struct acpi_device *device)
 {
        struct acpi_hardware_id *hwid;
@@ -2072,6 +2119,10 @@ static int acpi_scan_attach_handler(struct acpi_device *device)
 
                handler = acpi_scan_match_handler(hwid->id, &devid);
                if (handler) {
+                       if (!handler->attach) {
+                               device->pnp.type.platform_id = 0;
+                               continue;
+                       }
                        device->handler = handler;
                        ret = handler->attach(device, devid);
                        if (ret > 0)
@@ -2082,6 +2133,9 @@ static int acpi_scan_attach_handler(struct acpi_device *device)
                                break;
                }
        }
+       if (!ret)
+               acpi_default_enumeration(device);
+
        return ret;
 }
 
@@ -2241,11 +2295,11 @@ int __init acpi_scan_init(void)
        acpi_pci_root_init();
        acpi_pci_link_init();
        acpi_processor_init();
-       acpi_platform_init();
        acpi_lpss_init();
        acpi_cmos_rtc_init();
        acpi_container_init();
        acpi_memory_hotplug_init();
+       acpi_pnp_init();
 
        mutex_lock(&acpi_scan_lock);
        /*
@@ -2259,12 +2313,16 @@ int __init acpi_scan_init(void)
        if (result)
                goto out;
 
-       result = acpi_bus_scan_fixed();
-       if (result) {
-               acpi_detach_data(acpi_root->handle, acpi_scan_drop_device);
-               acpi_device_del(acpi_root);
-               put_device(&acpi_root->dev);
-               goto out;
+       /* Fixed feature devices do not exist on HW-reduced platform */
+       if (!acpi_gbl_reduced_hardware) {
+               result = acpi_bus_scan_fixed();
+               if (result) {
+                       acpi_detach_data(acpi_root->handle,
+                                        acpi_scan_drop_device);
+                       acpi_device_del(acpi_root);
+                       put_device(&acpi_root->dev);
+                       goto out;
+               }
        }
 
        acpi_update_all_gpes();
index c40fb2e..c11e379 100644 (file)
@@ -89,6 +89,7 @@ u32 acpi_target_system_state(void)
 {
        return acpi_target_sleep_state;
 }
+EXPORT_SYMBOL_GPL(acpi_target_system_state);
 
 static bool pwr_btn_event_pending;
 
@@ -611,6 +612,22 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = {
        .recover = acpi_pm_finish,
 };
 
+static int acpi_freeze_begin(void)
+{
+       acpi_scan_lock_acquire();
+       return 0;
+}
+
+static void acpi_freeze_end(void)
+{
+       acpi_scan_lock_release();
+}
+
+static const struct platform_freeze_ops acpi_freeze_ops = {
+       .begin = acpi_freeze_begin,
+       .end = acpi_freeze_end,
+};
+
 static void acpi_sleep_suspend_setup(void)
 {
        int i;
@@ -621,7 +638,9 @@ static void acpi_sleep_suspend_setup(void)
 
        suspend_set_ops(old_suspend_ordering ?
                &acpi_suspend_ops_old : &acpi_suspend_ops);
+       freeze_set_ops(&acpi_freeze_ops);
 }
+
 #else /* !CONFIG_SUSPEND */
 static inline void acpi_sleep_suspend_setup(void) {}
 #endif /* !CONFIG_SUSPEND */
index 2178229..05550ba 100644 (file)
@@ -44,6 +44,12 @@ static struct acpi_table_desc initial_tables[ACPI_MAX_TABLES] __initdata;
 
 static int acpi_apic_instance __initdata;
 
+/*
+ * Disable table checksum verification for the early stage due to the size
+ * limitation of the current x86 early mapping implementation.
+ */
+static bool acpi_verify_table_checksum __initdata = false;
+
 void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
 {
        if (!header)
@@ -333,6 +339,14 @@ int __init acpi_table_init(void)
 {
        acpi_status status;
 
+       if (acpi_verify_table_checksum) {
+               pr_info("Early table checksum verification enabled\n");
+               acpi_gbl_verify_table_checksum = TRUE;
+       } else {
+               pr_info("Early table checksum verification disabled\n");
+               acpi_gbl_verify_table_checksum = FALSE;
+       }
+
        status = acpi_initialize_tables(initial_tables, ACPI_MAX_TABLES, 0);
        if (ACPI_FAILURE(status))
                return -EINVAL;
@@ -354,3 +368,12 @@ static int __init acpi_parse_apic_instance(char *str)
 }
 
 early_param("acpi_apic_instance", acpi_parse_apic_instance);
+
+static int __init acpi_force_table_verification_setup(char *s)
+{
+       acpi_verify_table_checksum = true;
+
+       return 0;
+}
+
+early_param("acpi_force_table_verification", acpi_force_table_verification_setup);
index 25bbc55..112817e 100644 (file)
@@ -925,13 +925,10 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
        if (result)
                return result;
 
-       status = acpi_attach_data(tz->device->handle,
-                                 acpi_bus_private_data_handler,
-                                 tz->thermal_zone);
-       if (ACPI_FAILURE(status)) {
-               pr_err(PREFIX "Error attaching device data\n");
+       status =  acpi_bus_attach_private_data(tz->device->handle,
+                                              tz->thermal_zone);
+       if (ACPI_FAILURE(status))
                return -ENODEV;
-       }
 
        tz->tz_enabled = 1;
 
@@ -946,7 +943,7 @@ static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
        sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
        thermal_zone_device_unregister(tz->thermal_zone);
        tz->thermal_zone = NULL;
-       acpi_detach_data(tz->device->handle, acpi_bus_private_data_handler);
+       acpi_bus_detach_private_data(tz->device->handle);
 }
 
 
index bba5261..07c8c5a 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/types.h>
 #include <linux/hardirq.h>
 #include <linux/acpi.h>
+#include <linux/dynamic_debug.h>
 
 #include "internal.h"
 
@@ -456,6 +457,24 @@ acpi_evaluate_ost(acpi_handle handle, u32 source_event, u32 status_code,
 }
 EXPORT_SYMBOL(acpi_evaluate_ost);
 
+/**
+ * acpi_handle_path: Return the object path of handle
+ *
+ * Caller must free the returned buffer
+ */
+static char *acpi_handle_path(acpi_handle handle)
+{
+       struct acpi_buffer buffer = {
+               .length = ACPI_ALLOCATE_BUFFER,
+               .pointer = NULL
+       };
+
+       if (in_interrupt() ||
+           acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer) != AE_OK)
+               return NULL;
+       return buffer.pointer;
+}
+
 /**
  * acpi_handle_printk: Print message with ACPI prefix and object path
  *
@@ -469,29 +488,50 @@ acpi_handle_printk(const char *level, acpi_handle handle, const char *fmt, ...)
 {
        struct va_format vaf;
        va_list args;
-       struct acpi_buffer buffer = {
-               .length = ACPI_ALLOCATE_BUFFER,
-               .pointer = NULL
-       };
        const char *path;
 
        va_start(args, fmt);
        vaf.fmt = fmt;
        vaf.va = &args;
 
-       if (in_interrupt() ||
-           acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer) != AE_OK)
-               path = "<n/a>";
-       else
-               path = buffer.pointer;
-
-       printk("%sACPI: %s: %pV", level, path, &vaf);
+       path = acpi_handle_path(handle);
+       printk("%sACPI: %s: %pV", level, path ? path : "<n/a>" , &vaf);
 
        va_end(args);
-       kfree(buffer.pointer);
+       kfree(path);
 }
 EXPORT_SYMBOL(acpi_handle_printk);
 
+#if defined(CONFIG_DYNAMIC_DEBUG)
+/**
+ * __acpi_handle_debug: pr_debug with ACPI prefix and object path
+ *
+ * This function is called through acpi_handle_debug macro and debug
+ * prints a message with ACPI prefix and object path. This function
+ * acquires the global namespace mutex to obtain an object path.  In
+ * interrupt context, it shows the object path as <n/a>.
+ */
+void
+__acpi_handle_debug(struct _ddebug *descriptor, acpi_handle handle,
+                   const char *fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+       const char *path;
+
+       va_start(args, fmt);
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       path = acpi_handle_path(handle);
+       __dynamic_pr_debug(descriptor, "ACPI: %s: %pV", path ? path : "<n/a>", &vaf);
+
+       va_end(args);
+       kfree(path);
+}
+EXPORT_SYMBOL(__acpi_handle_debug);
+#endif
+
 /**
  * acpi_has_method: Check whether @handle has a method named @name
  * @handle: ACPI device handle
index f8bc5a7..101fb09 100644 (file)
@@ -68,7 +68,7 @@ MODULE_AUTHOR("Bruno Ducrot");
 MODULE_DESCRIPTION("ACPI Video Driver");
 MODULE_LICENSE("GPL");
 
-static bool brightness_switch_enabled = 1;
+static bool brightness_switch_enabled;
 module_param(brightness_switch_enabled, bool, 0644);
 
 /*
@@ -150,6 +150,8 @@ struct acpi_video_enumerated_device {
 
 struct acpi_video_bus {
        struct acpi_device *device;
+       bool backlight_registered;
+       bool backlight_notifier_registered;
        u8 dos_setting;
        struct acpi_video_enumerated_device *attached_array;
        u8 attached_count;
@@ -161,6 +163,7 @@ struct acpi_video_bus {
        struct input_dev *input;
        char phys[32];  /* for input device */
        struct notifier_block pm_nb;
+       struct notifier_block backlight_nb;
 };
 
 struct acpi_video_device_flags {
@@ -471,6 +474,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
                DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X230"),
                },
        },
+       {
+        .callback = video_set_use_native_backlight,
+        .ident = "ThinkPad W530",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad W530"),
+               },
+       },
        {
         .callback = video_set_use_native_backlight,
        .ident = "ThinkPad X1 Carbon",
@@ -487,6 +498,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
                DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo IdeaPad Yoga 13"),
                },
        },
+       {
+        .callback = video_set_use_native_backlight,
+        .ident = "Lenovo Yoga 2 11",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+               DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2 11"),
+               },
+       },
        {
        .callback = video_set_use_native_backlight,
        .ident = "Thinkpad Helix",
@@ -519,6 +538,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
                DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5742G"),
                },
        },
+       {
+        .callback = video_set_use_native_backlight,
+        .ident = "Acer Aspire V5-171",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "V5-171"),
+               },
+       },
        {
         .callback = video_set_use_native_backlight,
         .ident = "Acer Aspire V5-431",
@@ -527,6 +554,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
                DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-431"),
                },
        },
+       {
+        .callback = video_set_use_native_backlight,
+        .ident = "Acer Aspire V5-471G",
+        .matches = {
+               DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-471G"),
+               },
+       },
        {
        .callback = video_set_use_native_backlight,
        .ident = "HP ProBook 4340s",
@@ -579,6 +614,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
        },
        {
        .callback = video_set_use_native_backlight,
+       .ident = "HP EliteBook 8470p",
+       .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 8470p"),
+               },
+       },
+       {
+       .callback = video_set_use_native_backlight,
        .ident = "HP EliteBook 8780w",
        .matches = {
                DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
@@ -1658,88 +1701,92 @@ acpi_video_bus_match(acpi_handle handle, u32 level, void *context,
 
 static void acpi_video_dev_register_backlight(struct acpi_video_device *device)
 {
-       if (acpi_video_verify_backlight_support()) {
-               struct backlight_properties props;
-               struct pci_dev *pdev;
-               acpi_handle acpi_parent;
-               struct device *parent = NULL;
-               int result;
-               static int count;
-               char *name;
-
-               result = acpi_video_init_brightness(device);
-               if (result)
-                       return;
-               name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
-               if (!name)
-                       return;
-               count++;
+       struct backlight_properties props;
+       struct pci_dev *pdev;
+       acpi_handle acpi_parent;
+       struct device *parent = NULL;
+       int result;
+       static int count;
+       char *name;
 
-               acpi_get_parent(device->dev->handle, &acpi_parent);
+       result = acpi_video_init_brightness(device);
+       if (result)
+               return;
+       name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
+       if (!name)
+               return;
+       count++;
 
-               pdev = acpi_get_pci_dev(acpi_parent);
-               if (pdev) {
-                       parent = &pdev->dev;
-                       pci_dev_put(pdev);
-               }
+       acpi_get_parent(device->dev->handle, &acpi_parent);
 
-               memset(&props, 0, sizeof(struct backlight_properties));
-               props.type = BACKLIGHT_FIRMWARE;
-               props.max_brightness = device->brightness->count - 3;
-               device->backlight = backlight_device_register(name,
-                                                             parent,
-                                                             device,
-                                                             &acpi_backlight_ops,
-                                                             &props);
-               kfree(name);
-               if (IS_ERR(device->backlight))
-                       return;
+       pdev = acpi_get_pci_dev(acpi_parent);
+       if (pdev) {
+               parent = &pdev->dev;
+               pci_dev_put(pdev);
+       }
 
-               /*
-                * Save current brightness level in case we have to restore it
-                * before acpi_video_device_lcd_set_level() is called next time.
-                */
-               device->backlight->props.brightness =
-                               acpi_video_get_brightness(device->backlight);
+       memset(&props, 0, sizeof(struct backlight_properties));
+       props.type = BACKLIGHT_FIRMWARE;
+       props.max_brightness = device->brightness->count - 3;
+       device->backlight = backlight_device_register(name,
+                                                     parent,
+                                                     device,
+                                                     &acpi_backlight_ops,
+                                                     &props);
+       kfree(name);
+       if (IS_ERR(device->backlight))
+               return;
 
-               device->cooling_dev = thermal_cooling_device_register("LCD",
-                                       device->dev, &video_cooling_ops);
-               if (IS_ERR(device->cooling_dev)) {
-                       /*
-                        * Set cooling_dev to NULL so we don't crash trying to
-                        * free it.
-                        * Also, why the hell we are returning early and
-                        * not attempt to register video output if cooling
-                        * device registration failed?
-                        * -- dtor
-                        */
-                       device->cooling_dev = NULL;
-                       return;
-               }
+       /*
+        * Save current brightness level in case we have to restore it
+        * before acpi_video_device_lcd_set_level() is called next time.
+        */
+       device->backlight->props.brightness =
+                       acpi_video_get_brightness(device->backlight);
 
-               dev_info(&device->dev->dev, "registered as cooling_device%d\n",
-                        device->cooling_dev->id);
-               result = sysfs_create_link(&device->dev->dev.kobj,
-                               &device->cooling_dev->device.kobj,
-                               "thermal_cooling");
-               if (result)
-                       printk(KERN_ERR PREFIX "Create sysfs link\n");
-               result = sysfs_create_link(&device->cooling_dev->device.kobj,
-                               &device->dev->dev.kobj, "device");
-               if (result)
-                       printk(KERN_ERR PREFIX "Create sysfs link\n");
+       device->cooling_dev = thermal_cooling_device_register("LCD",
+                               device->dev, &video_cooling_ops);
+       if (IS_ERR(device->cooling_dev)) {
+               /*
+                * Set cooling_dev to NULL so we don't crash trying to free it.
+                * Also, why the hell we are returning early and not attempt to
+                * register video output if cooling device registration failed?
+                * -- dtor
+                */
+               device->cooling_dev = NULL;
+               return;
        }
+
+       dev_info(&device->dev->dev, "registered as cooling_device%d\n",
+                device->cooling_dev->id);
+       result = sysfs_create_link(&device->dev->dev.kobj,
+                       &device->cooling_dev->device.kobj,
+                       "thermal_cooling");
+       if (result)
+               printk(KERN_ERR PREFIX "Create sysfs link\n");
+       result = sysfs_create_link(&device->cooling_dev->device.kobj,
+                       &device->dev->dev.kobj, "device");
+       if (result)
+               printk(KERN_ERR PREFIX "Create sysfs link\n");
 }
 
 static int acpi_video_bus_register_backlight(struct acpi_video_bus *video)
 {
        struct acpi_video_device *dev;
 
+       if (video->backlight_registered)
+               return 0;
+
+       if (!acpi_video_verify_backlight_support())
+               return 0;
+
        mutex_lock(&video->device_list_lock);
        list_for_each_entry(dev, &video->video_device_list, entry)
                acpi_video_dev_register_backlight(dev);
        mutex_unlock(&video->device_list_lock);
 
+       video->backlight_registered = true;
+
        video->pm_nb.notifier_call = acpi_video_resume;
        video->pm_nb.priority = 0;
        return register_pm_notifier(&video->pm_nb);
@@ -1767,13 +1814,20 @@ static void acpi_video_dev_unregister_backlight(struct acpi_video_device *device
 static int acpi_video_bus_unregister_backlight(struct acpi_video_bus *video)
 {
        struct acpi_video_device *dev;
-       int error = unregister_pm_notifier(&video->pm_nb);
+       int error;
+
+       if (!video->backlight_registered)
+               return 0;
+
+       error = unregister_pm_notifier(&video->pm_nb);
 
        mutex_lock(&video->device_list_lock);
        list_for_each_entry(dev, &video->video_device_list, entry)
                acpi_video_dev_unregister_backlight(dev);
        mutex_unlock(&video->device_list_lock);
 
+       video->backlight_registered = false;
+
        return error;
 }
 
@@ -1867,6 +1921,56 @@ static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video)
        video->input = NULL;
 }
 
+static int acpi_video_backlight_notify(struct notifier_block *nb,
+                                       unsigned long val, void *bd)
+{
+       struct backlight_device *backlight = bd;
+       struct acpi_video_bus *video;
+
+       /* acpi_video_verify_backlight_support only cares about raw devices */
+       if (backlight->props.type != BACKLIGHT_RAW)
+               return NOTIFY_DONE;
+
+       video = container_of(nb, struct acpi_video_bus, backlight_nb);
+
+       switch (val) {
+       case BACKLIGHT_REGISTERED:
+               if (!acpi_video_verify_backlight_support())
+                       acpi_video_bus_unregister_backlight(video);
+               break;
+       case BACKLIGHT_UNREGISTERED:
+               acpi_video_bus_register_backlight(video);
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static int acpi_video_bus_add_backlight_notify_handler(
+                                               struct acpi_video_bus *video)
+{
+       int error;
+
+       video->backlight_nb.notifier_call = acpi_video_backlight_notify;
+       video->backlight_nb.priority = 0;
+       error = backlight_register_notifier(&video->backlight_nb);
+       if (error == 0)
+               video->backlight_notifier_registered = true;
+
+       return error;
+}
+
+static int acpi_video_bus_remove_backlight_notify_handler(
+                                               struct acpi_video_bus *video)
+{
+       if (!video->backlight_notifier_registered)
+               return 0;
+
+       video->backlight_notifier_registered = false;
+
+       return backlight_unregister_notifier(&video->backlight_nb);
+}
+
 static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
 {
        struct acpi_video_device *dev, *next;
@@ -1948,6 +2052,7 @@ static int acpi_video_bus_add(struct acpi_device *device)
 
        acpi_video_bus_register_backlight(video);
        acpi_video_bus_add_notify_handler(video);
+       acpi_video_bus_add_backlight_notify_handler(video);
 
        return 0;
 
@@ -1971,6 +2076,7 @@ static int acpi_video_bus_remove(struct acpi_device *device)
 
        video = acpi_driver_data(device);
 
+       acpi_video_bus_remove_backlight_notify_handler(video);
        acpi_video_bus_remove_notify_handler(video);
        acpi_video_bus_unregister_backlight(video);
        acpi_video_bus_put_devices(video);
@@ -2061,6 +2167,20 @@ void acpi_video_unregister(void)
 }
 EXPORT_SYMBOL(acpi_video_unregister);
 
+void acpi_video_unregister_backlight(void)
+{
+       struct acpi_video_bus *video;
+
+       if (!register_count)
+               return;
+
+       mutex_lock(&video_list_lock);
+       list_for_each_entry(video, &video_bus_head, entry)
+               acpi_video_bus_unregister_backlight(video);
+       mutex_unlock(&video_list_lock);
+}
+EXPORT_SYMBOL(acpi_video_unregister_backlight);
+
 /*
  * This is kind of nasty. Hardware using Intel chipsets may require
  * the video opregion code to be run first in order to initialise
index ea77701..840c7fa 100644 (file)
@@ -491,7 +491,7 @@ EXPORT_SYMBOL_GPL(dma_buf_kunmap);
  *                     dma-buf buffer.
  *
  * This function adjusts the passed in vma so that it points at the file of the
- * dma_buf operation. It alsog adjusts the starting pgoff and does bounds
+ * dma_buf operation. It also adjusts the starting pgoff and does bounds
  * checking on the size of the vma. Then it calls the exporters mmap function to
  * set up the mapping.
  *
index 5b47210..9e9227e 100644 (file)
@@ -131,9 +131,12 @@ EXPORT_SYMBOL_GPL(platform_get_resource_byname);
  */
 int platform_get_irq_byname(struct platform_device *dev, const char *name)
 {
-       struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ,
-                                                         name);
+       struct resource *r;
+
+       if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node)
+               return of_irq_get_byname(dev->dev.of_node, name);
 
+       r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name);
        return r ? r->start : -ENXIO;
 }
 EXPORT_SYMBOL_GPL(platform_get_irq_byname);
index 86d5e4f..343ffad 100644 (file)
@@ -479,7 +479,7 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
        TRACE_DEVICE(dev);
        TRACE_RESUME(0);
 
-       if (dev->power.syscore)
+       if (dev->power.syscore || dev->power.direct_complete)
                goto Out;
 
        if (!dev->power.is_noirq_suspended)
@@ -605,7 +605,7 @@ static int device_resume_early(struct device *dev, pm_message_t state, bool asyn
        TRACE_DEVICE(dev);
        TRACE_RESUME(0);
 
-       if (dev->power.syscore)
+       if (dev->power.syscore || dev->power.direct_complete)
                goto Out;
 
        if (!dev->power.is_late_suspended)
@@ -735,6 +735,12 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
        if (dev->power.syscore)
                goto Complete;
 
+       if (dev->power.direct_complete) {
+               /* Match the pm_runtime_disable() in __device_suspend(). */
+               pm_runtime_enable(dev);
+               goto Complete;
+       }
+
        dpm_wait(dev->parent, async);
        dpm_watchdog_set(&wd, dev);
        device_lock(dev);
@@ -1007,7 +1013,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
                goto Complete;
        }
 
-       if (dev->power.syscore)
+       if (dev->power.syscore || dev->power.direct_complete)
                goto Complete;
 
        dpm_wait_for_children(dev, async);
@@ -1146,7 +1152,7 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
                goto Complete;
        }
 
-       if (dev->power.syscore)
+       if (dev->power.syscore || dev->power.direct_complete)
                goto Complete;
 
        dpm_wait_for_children(dev, async);
@@ -1332,6 +1338,17 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
        if (dev->power.syscore)
                goto Complete;
 
+       if (dev->power.direct_complete) {
+               if (pm_runtime_status_suspended(dev)) {
+                       pm_runtime_disable(dev);
+                       if (pm_runtime_suspended_if_enabled(dev))
+                               goto Complete;
+
+                       pm_runtime_enable(dev);
+               }
+               dev->power.direct_complete = false;
+       }
+
        dpm_watchdog_set(&wd, dev);
        device_lock(dev);
 
@@ -1382,10 +1399,19 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
 
  End:
        if (!error) {
+               struct device *parent = dev->parent;
+
                dev->power.is_suspended = true;
-               if (dev->power.wakeup_path
-                   && dev->parent && !dev->parent->power.ignore_children)
-                       dev->parent->power.wakeup_path = true;
+               if (parent) {
+                       spin_lock_irq(&parent->power.lock);
+
+                       dev->parent->power.direct_complete = false;
+                       if (dev->power.wakeup_path
+                           && !dev->parent->power.ignore_children)
+                               dev->parent->power.wakeup_path = true;
+
+                       spin_unlock_irq(&parent->power.lock);
+               }
        }
 
        device_unlock(dev);
@@ -1487,7 +1513,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
 {
        int (*callback)(struct device *) = NULL;
        char *info = NULL;
-       int error = 0;
+       int ret = 0;
 
        if (dev->power.syscore)
                return 0;
@@ -1523,17 +1549,27 @@ static int device_prepare(struct device *dev, pm_message_t state)
                callback = dev->driver->pm->prepare;
        }
 
-       if (callback) {
-               error = callback(dev);
-               suspend_report_result(callback, error);
-       }
+       if (callback)
+               ret = callback(dev);
 
        device_unlock(dev);
 
-       if (error)
+       if (ret < 0) {
+               suspend_report_result(callback, ret);
                pm_runtime_put(dev);
-
-       return error;
+               return ret;
+       }
+       /*
+        * A positive return value from ->prepare() means "this device appears
+        * to be runtime-suspended and its state is fine, so if it really is
+        * runtime-suspended, you can leave it in that state provided that you
+        * will do the same thing with all of its descendants".  This only
+        * applies to suspend transitions, however.
+        */
+       spin_lock_irq(&dev->power.lock);
+       dev->power.direct_complete = ret > 0 && state.event == PM_EVENT_SUSPEND;
+       spin_unlock_irq(&dev->power.lock);
+       return 0;
 }
 
 /**
index 2553867..89ced95 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/errno.h>
 #include <linux/err.h>
 #include <linux/slab.h>
-#include <linux/cpufreq.h>
 #include <linux/device.h>
 #include <linux/list.h>
 #include <linux/rculist.h>
@@ -394,6 +393,13 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor);
  * to keep the integrity of the internal data structures. Callers should ensure
  * that this function is *NOT* called under RCU protection or in contexts where
  * mutex cannot be locked.
+ *
+ * Return:
+ * 0:          On success OR
+ *             Duplicate OPPs (both freq and volt are same) and opp->available
+ * -EEXIST:    Freq are same and volt are different OR
+ *             Duplicate OPPs (both freq and volt are same) and !opp->available
+ * -ENOMEM:    Memory allocation failure
  */
 int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
 {
@@ -443,15 +449,31 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
        new_opp->u_volt = u_volt;
        new_opp->available = true;
 
-       /* Insert new OPP in order of increasing frequency */
+       /*
+        * Insert new OPP in order of increasing frequency
+        * and discard if already present
+        */
        head = &dev_opp->opp_list;
        list_for_each_entry_rcu(opp, &dev_opp->opp_list, node) {
-               if (new_opp->rate < opp->rate)
+               if (new_opp->rate <= opp->rate)
                        break;
                else
                        head = &opp->node;
        }
 
+       /* Duplicate OPPs ? */
+       if (new_opp->rate == opp->rate) {
+               int ret = opp->available && new_opp->u_volt == opp->u_volt ?
+                       0 : -EEXIST;
+
+               dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n",
+                        __func__, opp->rate, opp->u_volt, opp->available,
+                        new_opp->rate, new_opp->u_volt, new_opp->available);
+               mutex_unlock(&dev_opp_list_lock);
+               kfree(new_opp);
+               return ret;
+       }
+
        list_add_rcu(&new_opp->node, head);
        mutex_unlock(&dev_opp_list_lock);
 
@@ -596,96 +618,6 @@ int dev_pm_opp_disable(struct device *dev, unsigned long freq)
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_disable);
 
-#ifdef CONFIG_CPU_FREQ
-/**
- * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device
- * @dev:       device for which we do this operation
- * @table:     Cpufreq table returned back to caller
- *
- * Generate a cpufreq table for a provided device- this assumes that the
- * opp list is already initialized and ready for usage.
- *
- * This function allocates required memory for the cpufreq table. It is
- * expected that the caller does the required maintenance such as freeing
- * the table as required.
- *
- * Returns -EINVAL for bad pointers, -ENODEV if the device is not found, -ENOMEM
- * if no memory available for the operation (table is not populated), returns 0
- * if successful and table is populated.
- *
- * WARNING: It is  important for the callers to ensure refreshing their copy of
- * the table if any of the mentioned functions have been invoked in the interim.
- *
- * Locking: The internal device_opp and opp structures are RCU protected.
- * To simplify the logic, we pretend we are updater and hold relevant mutex here
- * Callers should ensure that this function is *NOT* called under RCU protection
- * or in contexts where mutex locking cannot be used.
- */
-int dev_pm_opp_init_cpufreq_table(struct device *dev,
-                           struct cpufreq_frequency_table **table)
-{
-       struct device_opp *dev_opp;
-       struct dev_pm_opp *opp;
-       struct cpufreq_frequency_table *freq_table;
-       int i = 0;
-
-       /* Pretend as if I am an updater */
-       mutex_lock(&dev_opp_list_lock);
-
-       dev_opp = find_device_opp(dev);
-       if (IS_ERR(dev_opp)) {
-               int r = PTR_ERR(dev_opp);
-               mutex_unlock(&dev_opp_list_lock);
-               dev_err(dev, "%s: Device OPP not found (%d)\n", __func__, r);
-               return r;
-       }
-
-       freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) *
-                            (dev_pm_opp_get_opp_count(dev) + 1), GFP_KERNEL);
-       if (!freq_table) {
-               mutex_unlock(&dev_opp_list_lock);
-               dev_warn(dev, "%s: Unable to allocate frequency table\n",
-                       __func__);
-               return -ENOMEM;
-       }
-
-       list_for_each_entry(opp, &dev_opp->opp_list, node) {
-               if (opp->available) {
-                       freq_table[i].driver_data = i;
-                       freq_table[i].frequency = opp->rate / 1000;
-                       i++;
-               }
-       }
-       mutex_unlock(&dev_opp_list_lock);
-
-       freq_table[i].driver_data = i;
-       freq_table[i].frequency = CPUFREQ_TABLE_END;
-
-       *table = &freq_table[0];
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table);
-
-/**
- * dev_pm_opp_free_cpufreq_table() - free the cpufreq table
- * @dev:       device for which we do this operation
- * @table:     table to free
- *
- * Free up the table allocated by dev_pm_opp_init_cpufreq_table
- */
-void dev_pm_opp_free_cpufreq_table(struct device *dev,
-                               struct cpufreq_frequency_table **table)
-{
-       if (!table)
-               return;
-
-       kfree(*table);
-       *table = NULL;
-}
-EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table);
-#endif         /* CONFIG_CPU_FREQ */
-
 /**
  * dev_pm_opp_get_notifier() - find notifier_head of the device with opp
  * @dev:       device pointer used to lookup device OPPs.
@@ -734,11 +666,9 @@ int of_init_opp_table(struct device *dev)
                unsigned long freq = be32_to_cpup(val++) * 1000;
                unsigned long volt = be32_to_cpup(val++);
 
-               if (dev_pm_opp_add(dev, freq, volt)) {
+               if (dev_pm_opp_add(dev, freq, volt))
                        dev_warn(dev, "%s: Failed to add OPP %ld\n",
                                 __func__, freq);
-                       continue;
-               }
                nr -= 2;
        }
 
index 2d56f41..eb1bd2e 100644 (file)
@@ -318,10 +318,16 @@ int device_init_wakeup(struct device *dev, bool enable)
 {
        int ret = 0;
 
+       if (!dev)
+               return -EINVAL;
+
        if (enable) {
                device_set_wakeup_capable(dev, true);
                ret = device_wakeup_enable(dev);
        } else {
+               if (dev->power.can_wakeup)
+                       device_wakeup_disable(dev);
+
                device_set_wakeup_capable(dev, false);
        }
 
index b9a57fa..565a947 100644 (file)
@@ -95,7 +95,7 @@ int read_log(struct tpm_bios_log *log)
 
        log->bios_event_log_end = log->bios_event_log + len;
 
-       virt = acpi_os_map_memory(start, len);
+       virt = acpi_os_map_iomem(start, len);
        if (!virt) {
                kfree(log->bios_event_log);
                printk("%s: ERROR - Unable to map memory\n", __func__);
@@ -104,6 +104,6 @@ int read_log(struct tpm_bios_log *log)
 
        memcpy_fromio(log->bios_event_log, virt, len);
 
-       acpi_os_unmap_memory(virt, len);
+       acpi_os_unmap_iomem(virt, len);
        return 0;
 }
index 17d7f13..50b2a7e 100644 (file)
@@ -8,6 +8,7 @@ obj-$(CONFIG_COMMON_CLK)        += clk-fixed-rate.o
 obj-$(CONFIG_COMMON_CLK)       += clk-gate.o
 obj-$(CONFIG_COMMON_CLK)       += clk-mux.o
 obj-$(CONFIG_COMMON_CLK)       += clk-composite.o
+obj-$(CONFIG_COMMON_CLK)       += clk-fractional-divider.o
 
 # hardware specific clock types
 # please keep this section sorted lexicographically by file/directory path name
diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
new file mode 100644 (file)
index 0000000..ede685c
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * 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.
+ *
+ * Adjustable fractional divider clock implementation.
+ * Output rate = (m / n) * parent_rate.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/gcd.h>
+
+#define to_clk_fd(_hw) container_of(_hw, struct clk_fractional_divider, hw)
+
+static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
+                                       unsigned long parent_rate)
+{
+       struct clk_fractional_divider *fd = to_clk_fd(hw);
+       unsigned long flags = 0;
+       u32 val, m, n;
+       u64 ret;
+
+       if (fd->lock)
+               spin_lock_irqsave(fd->lock, flags);
+
+       val = clk_readl(fd->reg);
+
+       if (fd->lock)
+               spin_unlock_irqrestore(fd->lock, flags);
+
+       m = (val & fd->mmask) >> fd->mshift;
+       n = (val & fd->nmask) >> fd->nshift;
+
+       ret = parent_rate * m;
+       do_div(ret, n);
+
+       return ret;
+}
+
+static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate,
+                             unsigned long *prate)
+{
+       struct clk_fractional_divider *fd = to_clk_fd(hw);
+       unsigned maxn = (fd->nmask >> fd->nshift) + 1;
+       unsigned div;
+
+       if (!rate || rate >= *prate)
+               return *prate;
+
+       div = gcd(*prate, rate);
+
+       while ((*prate / div) > maxn) {
+               div <<= 1;
+               rate <<= 1;
+       }
+
+       return rate;
+}
+
+static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
+                          unsigned long parent_rate)
+{
+       struct clk_fractional_divider *fd = to_clk_fd(hw);
+       unsigned long flags = 0;
+       unsigned long div;
+       unsigned n, m;
+       u32 val;
+
+       div = gcd(parent_rate, rate);
+       m = rate / div;
+       n = parent_rate / div;
+
+       if (fd->lock)
+               spin_lock_irqsave(fd->lock, flags);
+
+       val = clk_readl(fd->reg);
+       val &= ~(fd->mmask | fd->nmask);
+       val |= (m << fd->mshift) | (n << fd->nshift);
+       clk_writel(val, fd->reg);
+
+       if (fd->lock)
+               spin_unlock_irqrestore(fd->lock, flags);
+
+       return 0;
+}
+
+const struct clk_ops clk_fractional_divider_ops = {
+       .recalc_rate = clk_fd_recalc_rate,
+       .round_rate = clk_fd_round_rate,
+       .set_rate = clk_fd_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_fractional_divider_ops);
+
+struct clk *clk_register_fractional_divider(struct device *dev,
+               const char *name, const char *parent_name, unsigned long flags,
+               void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
+               u8 clk_divider_flags, spinlock_t *lock)
+{
+       struct clk_fractional_divider *fd;
+       struct clk_init_data init;
+       struct clk *clk;
+
+       fd = kzalloc(sizeof(*fd), GFP_KERNEL);
+       if (!fd) {
+               dev_err(dev, "could not allocate fractional divider clk\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       init.name = name;
+       init.ops = &clk_fractional_divider_ops;
+       init.flags = flags | CLK_IS_BASIC;
+       init.parent_names = parent_name ? &parent_name : NULL;
+       init.num_parents = parent_name ? 1 : 0;
+
+       fd->reg = reg;
+       fd->mshift = mshift;
+       fd->mmask = (BIT(mwidth) - 1) << mshift;
+       fd->nshift = nshift;
+       fd->nmask = (BIT(nwidth) - 1) << nshift;
+       fd->flags = clk_divider_flags;
+       fd->lock = lock;
+       fd->hw.init = &init;
+
+       clk = clk_register(dev, &fd->hw);
+       if (IS_ERR(clk))
+               kfree(fd);
+
+       return clk;
+}
+EXPORT_SYMBOL_GPL(clk_register_fractional_divider);
index 967c141..4cf838d 100644 (file)
@@ -24,8 +24,7 @@ static DEFINE_SPINLOCK(clk_lock);
  * Gate clocks
  */
 
-static void __init rk2928_gate_clk_init(struct device_node *node,
-                                        void *data)
+static void __init rk2928_gate_clk_init(struct device_node *node)
 {
        struct clk_onecell_data *clk_data;
        const char *clk_parent;
index bd7dc73..9eddf22 100644 (file)
@@ -1278,8 +1278,7 @@ static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_mat
        const struct of_device_id *match;
        void (*setup_function)(struct device_node *, const void *) = function;
 
-       for_each_matching_node(np, clk_match) {
-               match = of_match_node(clk_match, np);
+       for_each_matching_node_and_match(np, clk_match, &match) {
                data = match->data;
                setup_function(np, data);
        }
@@ -1310,7 +1309,7 @@ static void __init sunxi_clock_protect(void)
        }
 }
 
-static void __init sunxi_init_clocks(void)
+static void __init sunxi_init_clocks(struct device_node *np)
 {
        /* Register factor clocks */
        of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup);
index 3e2999d..5873481 100644 (file)
@@ -221,7 +221,7 @@ static void __init of_ti_gate_clk_setup(struct device_node *node)
 {
        _of_ti_gate_clk_setup(node, &omap_gate_clk_ops, NULL);
 }
-CLK_OF_DECLARE(ti_gate_clk, "ti,gate-clock", of_ti_gate_clk_setup)
+CLK_OF_DECLARE(ti_gate_clk, "ti,gate-clock", of_ti_gate_clk_setup);
 
 static void __init of_ti_wait_gate_clk_setup(struct device_node *node)
 {
index ae2e427..0093a8e 100644 (file)
@@ -27,7 +27,7 @@ void __init clocksource_of_init(void)
 {
        struct device_node *np;
        const struct of_device_id *match;
-       clocksource_of_init_fn init_func;
+       of_init_fn_1 init_func;
        unsigned clocksources = 0;
 
        for_each_matching_node_and_match(np, __clksrc_of_table, &match) {
index e4c50ad..2798e74 100644 (file)
@@ -20,7 +20,7 @@
 
 static void __iomem *versatile_sys_24mhz;
 
-static u32 notrace versatile_sys_24mhz_read(void)
+static u64 notrace versatile_sys_24mhz_read(void)
 {
        return readl(versatile_sys_24mhz);
 }
@@ -34,7 +34,7 @@ static void __init versatile_sched_clock_init(struct device_node *node)
 
        versatile_sys_24mhz = base + SYS_24MHZ;
 
-       setup_sched_clock(versatile_sys_24mhz_read, 32, 24000000);
+       sched_clock_register(versatile_sys_24mhz_read, 32, 24000000);
 }
 CLOCKSOURCE_OF_DECLARE(versatile, "arm,vexpress-sysreg",
-               versatile_sched_clock_init);
+                      versatile_sched_clock_init);
index d2c7b4b..36d20d0 100644 (file)
@@ -5,7 +5,8 @@
 # big LITTLE core layer and glue drivers
 config ARM_BIG_LITTLE_CPUFREQ
        tristate "Generic ARM big LITTLE CPUfreq driver"
-       depends on ARM && BIG_LITTLE && ARM_CPU_TOPOLOGY && HAVE_CLK
+       depends on (BIG_LITTLE && ARM_CPU_TOPOLOGY) || (ARM64 && SMP)
+       depends on HAVE_CLK
        select PM_OPP
        help
          This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.
@@ -85,7 +86,7 @@ config ARM_EXYNOS_CPU_FREQ_BOOST_SW
          It allows usage of special frequencies for Samsung Exynos
          processors if thermal conditions are appropriate.
 
-         It reguires, for safe operation, thermal framework with properly
+         It requires, for safe operation, thermal framework with properly
          defined trip points.
 
          If in doubt, say N.
@@ -186,7 +187,7 @@ config ARM_S3C2416_CPUFREQ
          S3C2450 SoC. The S3C2416 supports changing the rate of the
          armdiv clock source and also entering a so called dynamic
          voltage scaling mode in which it is possible to reduce the
-         core voltage of the cpu.
+         core voltage of the CPU.
 
          If in doubt, say N.
 
index d369349..89ae88f 100644 (file)
@@ -10,7 +10,7 @@ config X86_INTEL_PSTATE
          The driver implements an internal governor and will become
           the scaling driver and governor for Sandy bridge processors.
 
-         When this driver is enabled it will become the perferred
+         When this driver is enabled it will become the preferred
           scaling driver for Sandy bridge processors.
 
          If in doubt, say N.
@@ -52,7 +52,7 @@ config X86_ACPI_CPUFREQ_CPB
        help
          The powernow-k8 driver used to provide a sysfs knob called "cpb"
          to disable the Core Performance Boosting feature of AMD CPUs. This
-         file has now been superseeded by the more generic "boost" entry.
+         file has now been superseded by the more generic "boost" entry.
 
          By enabling this option the acpi_cpufreq driver provides the old
          entry in addition to the new boost ones, for compatibility reasons.
index 0dbb963..738c8b7 100644 (file)
@@ -1,5 +1,7 @@
 # CPUfreq core
 obj-$(CONFIG_CPU_FREQ)                 += cpufreq.o freq_table.o
+obj-$(CONFIG_PM_OPP)                   += cpufreq_opp.o
+
 # CPUfreq stats
 obj-$(CONFIG_CPU_FREQ_STAT)             += cpufreq_stats.o
 
index 000e4e0..b0c18ed 100644 (file)
@@ -213,7 +213,7 @@ static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data)
 
 static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data)
 {
-       int i;
+       struct cpufreq_frequency_table *pos;
        struct acpi_processor_performance *perf;
 
        if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
@@ -223,10 +223,9 @@ static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data)
 
        perf = data->acpi_data;
 
-       for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               if (msr == perf->states[data->freq_table[i].driver_data].status)
-                       return data->freq_table[i].frequency;
-       }
+       cpufreq_for_each_entry(pos, data->freq_table)
+               if (msr == perf->states[pos->driver_data].status)
+                       return pos->frequency;
        return data->freq_table[0].frequency;
 }
 
index bad2ed3..1f4d4e3 100644 (file)
@@ -226,22 +226,22 @@ static inline u32 get_table_count(struct cpufreq_frequency_table *table)
 /* get the minimum frequency in the cpufreq_frequency_table */
 static inline u32 get_table_min(struct cpufreq_frequency_table *table)
 {
-       int i;
+       struct cpufreq_frequency_table *pos;
        uint32_t min_freq = ~0;
-       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++)
-               if (table[i].frequency < min_freq)
-                       min_freq = table[i].frequency;
+       cpufreq_for_each_entry(pos, table)
+               if (pos->frequency < min_freq)
+                       min_freq = pos->frequency;
        return min_freq;
 }
 
 /* get the maximum frequency in the cpufreq_frequency_table */
 static inline u32 get_table_max(struct cpufreq_frequency_table *table)
 {
-       int i;
+       struct cpufreq_frequency_table *pos;
        uint32_t max_freq = 0;
-       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++)
-               if (table[i].frequency > max_freq)
-                       max_freq = table[i].frequency;
+       cpufreq_for_each_entry(pos, table)
+               if (pos->frequency > max_freq)
+                       max_freq = pos->frequency;
        return max_freq;
 }
 
index bc447b9..a225809 100644 (file)
@@ -379,7 +379,7 @@ static struct cpufreq_driver nforce2_driver = {
 };
 
 #ifdef MODULE
-static DEFINE_PCI_DEVICE_TABLE(nforce2_ids) = {
+static const struct pci_device_id nforce2_ids[] = {
        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2 },
        {}
 };
index abda660..ae11dd5 100644 (file)
@@ -354,6 +354,18 @@ static void cpufreq_notify_post_transition(struct cpufreq_policy *policy,
 void cpufreq_freq_transition_begin(struct cpufreq_policy *policy,
                struct cpufreq_freqs *freqs)
 {
+
+       /*
+        * Catch double invocations of _begin() which lead to self-deadlock.
+        * ASYNC_NOTIFICATION drivers are left out because the cpufreq core
+        * doesn't invoke _begin() on their behalf, and hence the chances of
+        * double invocations are very low. Moreover, there are scenarios
+        * where these checks can emit false-positive warnings in these
+        * drivers; so we avoid that by skipping them altogether.
+        */
+       WARN_ON(!(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION)
+                               && current == policy->transition_task);
+
 wait:
        wait_event(policy->transition_wait, !policy->transition_ongoing);
 
@@ -365,6 +377,7 @@ wait:
        }
 
        policy->transition_ongoing = true;
+       policy->transition_task = current;
 
        spin_unlock(&policy->transition_lock);
 
@@ -381,6 +394,7 @@ void cpufreq_freq_transition_end(struct cpufreq_policy *policy,
        cpufreq_notify_post_transition(policy, freqs, transition_failed);
 
        policy->transition_ongoing = false;
+       policy->transition_task = NULL;
 
        wake_up(&policy->transition_wait);
 }
@@ -1802,12 +1816,43 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier);
  *                              GOVERNORS                            *
  *********************************************************************/
 
+static int __target_index(struct cpufreq_policy *policy,
+                         struct cpufreq_frequency_table *freq_table, int index)
+{
+       struct cpufreq_freqs freqs;
+       int retval = -EINVAL;
+       bool notify;
+
+       notify = !(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION);
+
+       if (notify) {
+               freqs.old = policy->cur;
+               freqs.new = freq_table[index].frequency;
+               freqs.flags = 0;
+
+               pr_debug("%s: cpu: %d, oldfreq: %u, new freq: %u\n",
+                        __func__, policy->cpu, freqs.old, freqs.new);
+
+               cpufreq_freq_transition_begin(policy, &freqs);
+       }
+
+       retval = cpufreq_driver->target_index(policy, index);
+       if (retval)
+               pr_err("%s: Failed to change cpu frequency: %d\n", __func__,
+                      retval);
+
+       if (notify)
+               cpufreq_freq_transition_end(policy, &freqs, retval);
+
+       return retval;
+}
+
 int __cpufreq_driver_target(struct cpufreq_policy *policy,
                            unsigned int target_freq,
                            unsigned int relation)
 {
-       int retval = -EINVAL;
        unsigned int old_target_freq = target_freq;
+       int retval = -EINVAL;
 
        if (cpufreq_disabled())
                return -ENODEV;
@@ -1834,8 +1879,6 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
                retval = cpufreq_driver->target(policy, target_freq, relation);
        else if (cpufreq_driver->target_index) {
                struct cpufreq_frequency_table *freq_table;
-               struct cpufreq_freqs freqs;
-               bool notify;
                int index;
 
                freq_table = cpufreq_frequency_get_table(policy->cpu);
@@ -1856,26 +1899,7 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
                        goto out;
                }
 
-               notify = !(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION);
-
-               if (notify) {
-                       freqs.old = policy->cur;
-                       freqs.new = freq_table[index].frequency;
-                       freqs.flags = 0;
-
-                       pr_debug("%s: cpu: %d, oldfreq: %u, new freq: %u\n",
-                                __func__, policy->cpu, freqs.old, freqs.new);
-
-                       cpufreq_freq_transition_begin(policy, &freqs);
-               }
-
-               retval = cpufreq_driver->target_index(policy, index);
-               if (retval)
-                       pr_err("%s: Failed to change cpu frequency: %d\n",
-                              __func__, retval);
-
-               if (notify)
-                       cpufreq_freq_transition_end(policy, &freqs, retval);
+               retval = __target_index(policy, freq_table, index);
        }
 
 out:
diff --git a/drivers/cpufreq/cpufreq_opp.c b/drivers/cpufreq/cpufreq_opp.c
new file mode 100644 (file)
index 0000000..c0c6f4a
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Generic OPP helper interface for CPUFreq drivers
+ *
+ * Copyright (C) 2009-2014 Texas Instruments Incorporated.
+ *     Nishanth Menon
+ *     Romit Dasgupta
+ *     Kevin Hilman
+ *
+ * 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/cpufreq.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/pm_opp.h>
+#include <linux/rcupdate.h>
+#include <linux/slab.h>
+
+/**
+ * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device
+ * @dev:       device for which we do this operation
+ * @table:     Cpufreq table returned back to caller
+ *
+ * Generate a cpufreq table for a provided device- this assumes that the
+ * opp list is already initialized and ready for usage.
+ *
+ * This function allocates required memory for the cpufreq table. It is
+ * expected that the caller does the required maintenance such as freeing
+ * the table as required.
+ *
+ * Returns -EINVAL for bad pointers, -ENODEV if the device is not found, -ENOMEM
+ * if no memory available for the operation (table is not populated), returns 0
+ * if successful and table is populated.
+ *
+ * WARNING: It is  important for the callers to ensure refreshing their copy of
+ * the table if any of the mentioned functions have been invoked in the interim.
+ *
+ * Locking: The internal device_opp and opp structures are RCU protected.
+ * Since we just use the regular accessor functions to access the internal data
+ * structures, we use RCU read lock inside this function. As a result, users of
+ * this function DONOT need to use explicit locks for invoking.
+ */
+int dev_pm_opp_init_cpufreq_table(struct device *dev,
+                                 struct cpufreq_frequency_table **table)
+{
+       struct dev_pm_opp *opp;
+       struct cpufreq_frequency_table *freq_table = NULL;
+       int i, max_opps, ret = 0;
+       unsigned long rate;
+
+       rcu_read_lock();
+
+       max_opps = dev_pm_opp_get_opp_count(dev);
+       if (max_opps <= 0) {
+               ret = max_opps ? max_opps : -ENODATA;
+               goto out;
+       }
+
+       freq_table = kzalloc(sizeof(*freq_table) * (max_opps + 1), GFP_KERNEL);
+       if (!freq_table) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       for (i = 0, rate = 0; i < max_opps; i++, rate++) {
+               /* find next rate */
+               opp = dev_pm_opp_find_freq_ceil(dev, &rate);
+               if (IS_ERR(opp)) {
+                       ret = PTR_ERR(opp);
+                       goto out;
+               }
+               freq_table[i].driver_data = i;
+               freq_table[i].frequency = rate / 1000;
+       }
+
+       freq_table[i].driver_data = i;
+       freq_table[i].frequency = CPUFREQ_TABLE_END;
+
+       *table = &freq_table[0];
+
+out:
+       rcu_read_unlock();
+       if (ret)
+               kfree(freq_table);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table);
+
+/**
+ * dev_pm_opp_free_cpufreq_table() - free the cpufreq table
+ * @dev:       device for which we do this operation
+ * @table:     table to free
+ *
+ * Free up the table allocated by dev_pm_opp_init_cpufreq_table
+ */
+void dev_pm_opp_free_cpufreq_table(struct device *dev,
+                                  struct cpufreq_frequency_table **table)
+{
+       if (!table)
+               return;
+
+       kfree(*table);
+       *table = NULL;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table);
index ecaaebf..0cd9b4d 100644 (file)
@@ -182,11 +182,11 @@ static void cpufreq_stats_free_table(unsigned int cpu)
 
 static int __cpufreq_stats_create_table(struct cpufreq_policy *policy)
 {
-       unsigned int i, j, count = 0, ret = 0;
+       unsigned int i, count = 0, ret = 0;
        struct cpufreq_stats *stat;
        unsigned int alloc_size;
        unsigned int cpu = policy->cpu;
-       struct cpufreq_frequency_table *table;
+       struct cpufreq_frequency_table *pos, *table;
 
        table = cpufreq_frequency_get_table(cpu);
        if (unlikely(!table))
@@ -205,12 +205,8 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy)
        stat->cpu = cpu;
        per_cpu(cpufreq_stats_table, cpu) = stat;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               unsigned int freq = table[i].frequency;
-               if (freq == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       cpufreq_for_each_valid_entry(pos, table)
                count++;
-       }
 
        alloc_size = count * sizeof(int) + count * sizeof(u64);
 
@@ -228,15 +224,11 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy)
 #ifdef CONFIG_CPU_FREQ_STAT_DETAILS
        stat->trans_table = stat->freq_table + count;
 #endif
-       j = 0;
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               unsigned int freq = table[i].frequency;
-               if (freq == CPUFREQ_ENTRY_INVALID)
-                       continue;
-               if (freq_table_get_index(stat, freq) == -1)
-                       stat->freq_table[j++] = freq;
-       }
-       stat->state_num = j;
+       i = 0;
+       cpufreq_for_each_valid_entry(pos, table)
+               if (freq_table_get_index(stat, pos->frequency) == -1)
+                       stat->freq_table[i++] = pos->frequency;
+       stat->state_num = i;
        spin_lock(&cpufreq_stats_lock);
        stat->last_time = get_jiffies_64();
        stat->last_index = freq_table_get_index(stat, policy->cur);
index 412a78b..4bebc1b 100644 (file)
@@ -45,7 +45,7 @@ static struct cpufreq_driver dbx500_cpufreq_driver = {
 
 static int dbx500_cpufreq_probe(struct platform_device *pdev)
 {
-       int i = 0;
+       struct cpufreq_frequency_table *pos;
 
        freq_table = dev_get_platdata(&pdev->dev);
        if (!freq_table) {
@@ -60,10 +60,8 @@ static int dbx500_cpufreq_probe(struct platform_device *pdev)
        }
 
        pr_info("dbx500-cpufreq: Available frequencies:\n");
-       while (freq_table[i].frequency != CPUFREQ_TABLE_END) {
-               pr_info("  %d Mhz\n", freq_table[i].frequency/1000);
-               i++;
-       }
+       cpufreq_for_each_entry(pos, freq_table)
+               pr_info("  %d Mhz\n", pos->frequency / 1000);
 
        return cpufreq_register_driver(&dbx500_cpufreq_driver);
 }
index 7f5d2a6..1c06e78 100644 (file)
@@ -147,7 +147,7 @@ static int elanfreq_target(struct cpufreq_policy *policy,
 static int elanfreq_cpu_init(struct cpufreq_policy *policy)
 {
        struct cpuinfo_x86 *c = &cpu_data(0);
-       unsigned int i;
+       struct cpufreq_frequency_table *pos;
 
        /* capability check */
        if ((c->x86_vendor != X86_VENDOR_AMD) ||
@@ -159,10 +159,9 @@ static int elanfreq_cpu_init(struct cpufreq_policy *policy)
                max_freq = elanfreq_get_cpu_frequency(0);
 
        /* table init */
-       for (i = 0; (elanfreq_table[i].frequency != CPUFREQ_TABLE_END); i++) {
-               if (elanfreq_table[i].frequency > max_freq)
-                       elanfreq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
-       }
+       cpufreq_for_each_entry(pos, elanfreq_table)
+               if (pos->frequency > max_freq)
+                       pos->frequency = CPUFREQ_ENTRY_INVALID;
 
        /* cpuinfo and default policy values */
        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
index 348c8ba..1e0ec57 100644 (file)
@@ -28,17 +28,16 @@ static unsigned int locking_frequency;
 static int exynos_cpufreq_get_index(unsigned int freq)
 {
        struct cpufreq_frequency_table *freq_table = exynos_info->freq_table;
-       int index;
+       struct cpufreq_frequency_table *pos;
 
-       for (index = 0;
-               freq_table[index].frequency != CPUFREQ_TABLE_END; index++)
-               if (freq_table[index].frequency == freq)
+       cpufreq_for_each_entry(pos, freq_table)
+               if (pos->frequency == freq)
                        break;
 
-       if (freq_table[index].frequency == CPUFREQ_TABLE_END)
+       if (pos->frequency == CPUFREQ_TABLE_END)
                return -EINVAL;
 
-       return index;
+       return pos - freq_table;
 }
 
 static int exynos_cpufreq_scale(unsigned int target_freq)
@@ -48,6 +47,7 @@ static int exynos_cpufreq_scale(unsigned int target_freq)
        struct cpufreq_policy *policy = cpufreq_cpu_get(0);
        unsigned int arm_volt, safe_arm_volt = 0;
        unsigned int mpll_freq_khz = exynos_info->mpll_freq_khz;
+       struct device *dev = exynos_info->dev;
        unsigned int old_freq;
        int index, old_index;
        int ret = 0;
@@ -89,8 +89,8 @@ static int exynos_cpufreq_scale(unsigned int target_freq)
                /* Firstly, voltage up to increase frequency */
                ret = regulator_set_voltage(arm_regulator, arm_volt, arm_volt);
                if (ret) {
-                       pr_err("%s: failed to set cpu voltage to %d\n",
-                               __func__, arm_volt);
+                       dev_err(dev, "failed to set cpu voltage to %d\n",
+                               arm_volt);
                        return ret;
                }
        }
@@ -99,8 +99,8 @@ static int exynos_cpufreq_scale(unsigned int target_freq)
                ret = regulator_set_voltage(arm_regulator, safe_arm_volt,
                                      safe_arm_volt);
                if (ret) {
-                       pr_err("%s: failed to set cpu voltage to %d\n",
-                               __func__, safe_arm_volt);
+                       dev_err(dev, "failed to set cpu voltage to %d\n",
+                               safe_arm_volt);
                        return ret;
                }
        }
@@ -114,8 +114,8 @@ static int exynos_cpufreq_scale(unsigned int target_freq)
                ret = regulator_set_voltage(arm_regulator, arm_volt,
                                arm_volt);
                if (ret) {
-                       pr_err("%s: failed to set cpu voltage to %d\n",
-                               __func__, arm_volt);
+                       dev_err(dev, "failed to set cpu voltage to %d\n",
+                               arm_volt);
                        goto out;
                }
        }
@@ -162,6 +162,8 @@ static int exynos_cpufreq_probe(struct platform_device *pdev)
        if (!exynos_info)
                return -ENOMEM;
 
+       exynos_info->dev = &pdev->dev;
+
        if (of_machine_is_compatible("samsung,exynos4210")) {
                exynos_info->type = EXYNOS_SOC_4210;
                ret = exynos4210_cpufreq_init(exynos_info);
@@ -183,13 +185,13 @@ static int exynos_cpufreq_probe(struct platform_device *pdev)
                goto err_vdd_arm;
 
        if (exynos_info->set_freq == NULL) {
-               pr_err("%s: No set_freq function (ERR)\n", __func__);
+               dev_err(&pdev->dev, "No set_freq function (ERR)\n");
                goto err_vdd_arm;
        }
 
        arm_regulator = regulator_get(NULL, "vdd_arm");
        if (IS_ERR(arm_regulator)) {
-               pr_err("%s: failed to get resource vdd_arm\n", __func__);
+               dev_err(&pdev->dev, "failed to get resource vdd_arm\n");
                goto err_vdd_arm;
        }
 
@@ -199,7 +201,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev)
        if (!cpufreq_register_driver(&exynos_driver))
                return 0;
 
-       pr_err("%s: failed to register cpufreq driver\n", __func__);
+       dev_err(&pdev->dev, "failed to register cpufreq driver\n");
        regulator_put(arm_regulator);
 err_vdd_arm:
        kfree(exynos_info);
index 51af42e..9f2062a 100644 (file)
@@ -42,6 +42,7 @@ struct apll_freq {
 
 struct exynos_dvfs_info {
        enum exynos_soc_type type;
+       struct device   *dev;
        unsigned long   mpll_freq_khz;
        unsigned int    pll_safe_idx;
        struct clk      *cpu_clk;
index a6b8214..f33f25b 100644 (file)
@@ -114,25 +114,23 @@ static struct cpufreq_freqs freqs;
 
 static int init_div_table(void)
 {
-       struct cpufreq_frequency_table *freq_tbl = dvfs_info->freq_table;
+       struct cpufreq_frequency_table *pos, *freq_tbl = dvfs_info->freq_table;
        unsigned int tmp, clk_div, ema_div, freq, volt_id;
-       int i = 0;
        struct dev_pm_opp *opp;
 
        rcu_read_lock();
-       for (i = 0; freq_tbl[i].frequency != CPUFREQ_TABLE_END; i++) {
-
+       cpufreq_for_each_entry(pos, freq_tbl) {
                opp = dev_pm_opp_find_freq_exact(dvfs_info->dev,
-                                       freq_tbl[i].frequency * 1000, true);
+                                       pos->frequency * 1000, true);
                if (IS_ERR(opp)) {
                        rcu_read_unlock();
                        dev_err(dvfs_info->dev,
                                "failed to find valid OPP for %u KHZ\n",
-                               freq_tbl[i].frequency);
+                               pos->frequency);
                        return PTR_ERR(opp);
                }
 
-               freq = freq_tbl[i].frequency / 1000; /* In MHZ */
+               freq = pos->frequency / 1000; /* In MHZ */
                clk_div = ((freq / CPU_DIV_FREQ_MAX) & P0_7_CPUCLKDEV_MASK)
                                        << P0_7_CPUCLKDEV_SHIFT;
                clk_div |= ((freq / CPU_ATB_FREQ_MAX) & P0_7_ATBCLKDEV_MASK)
@@ -157,7 +155,8 @@ static int init_div_table(void)
                tmp = (clk_div | ema_div | (volt_id << P0_7_VDD_SHIFT)
                        | ((freq / FREQ_UNIT) << P0_7_FREQ_SHIFT));
 
-               __raw_writel(tmp, dvfs_info->base + XMU_PMU_P0_7 + 4 * i);
+               __raw_writel(tmp, dvfs_info->base + XMU_PMU_P0_7 + 4 *
+                                               (pos - freq_tbl));
        }
 
        rcu_read_unlock();
@@ -166,8 +165,9 @@ static int init_div_table(void)
 
 static void exynos_enable_dvfs(unsigned int cur_frequency)
 {
-       unsigned int tmp, i, cpu;
+       unsigned int tmp, cpu;
        struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table;
+       struct cpufreq_frequency_table *pos;
        /* Disable DVFS */
        __raw_writel(0, dvfs_info->base + XMU_DVFS_CTRL);
 
@@ -182,15 +182,15 @@ static void exynos_enable_dvfs(unsigned int cur_frequency)
         __raw_writel(tmp, dvfs_info->base + XMU_PMUIRQEN);
 
        /* Set initial performance index */
-       for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
-               if (freq_table[i].frequency == cur_frequency)
+       cpufreq_for_each_entry(pos, freq_table)
+               if (pos->frequency == cur_frequency)
                        break;
 
-       if (freq_table[i].frequency == CPUFREQ_TABLE_END) {
+       if (pos->frequency == CPUFREQ_TABLE_END) {
                dev_crit(dvfs_info->dev, "Boot up frequency not supported\n");
                /* Assign the highest frequency */
-               i = 0;
-               cur_frequency = freq_table[i].frequency;
+               pos = freq_table;
+               cur_frequency = pos->frequency;
        }
 
        dev_info(dvfs_info->dev, "Setting dvfs initial frequency = %uKHZ",
@@ -199,7 +199,7 @@ static void exynos_enable_dvfs(unsigned int cur_frequency)
        for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++) {
                tmp = __raw_readl(dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4);
                tmp &= ~(P_VALUE_MASK << C0_3_PSTATE_NEW_SHIFT);
-               tmp |= (i << C0_3_PSTATE_NEW_SHIFT);
+               tmp |= ((pos - freq_table) << C0_3_PSTATE_NEW_SHIFT);
                __raw_writel(tmp, dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4);
        }
 
index 08e7bbc..1632981 100644 (file)
 int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
                                    struct cpufreq_frequency_table *table)
 {
+       struct cpufreq_frequency_table *pos;
        unsigned int min_freq = ~0;
        unsigned int max_freq = 0;
-       unsigned int i;
+       unsigned int freq;
 
-       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
-               unsigned int freq = table[i].frequency;
-               if (freq == CPUFREQ_ENTRY_INVALID) {
-                       pr_debug("table entry %u is invalid, skipping\n", i);
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
-                       continue;
-               }
                if (!cpufreq_boost_enabled()
-                   && (table[i].flags & CPUFREQ_BOOST_FREQ))
+                   && (pos->flags & CPUFREQ_BOOST_FREQ))
                        continue;
 
-               pr_debug("table entry %u: %u kHz\n", i, freq);
+               pr_debug("table entry %u: %u kHz\n", (int)(pos - table), freq);
                if (freq < min_freq)
                        min_freq = freq;
                if (freq > max_freq)
@@ -57,7 +54,8 @@ EXPORT_SYMBOL_GPL(cpufreq_frequency_table_cpuinfo);
 int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
                                   struct cpufreq_frequency_table *table)
 {
-       unsigned int next_larger = ~0, freq, i = 0;
+       struct cpufreq_frequency_table *pos;
+       unsigned int freq, next_larger = ~0;
        bool found = false;
 
        pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n",
@@ -65,9 +63,9 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
 
        cpufreq_verify_within_cpu_limits(policy);
 
-       for (; freq = table[i].frequency, freq != CPUFREQ_TABLE_END; i++) {
-               if (freq == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
+
                if ((freq >= policy->min) && (freq <= policy->max)) {
                        found = true;
                        break;
@@ -118,7 +116,8 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
                .driver_data = ~0,
                .frequency = 0,
        };
-       unsigned int i;
+       struct cpufreq_frequency_table *pos;
+       unsigned int freq, i = 0;
 
        pr_debug("request for target %u kHz (relation: %u) for cpu %u\n",
                                        target_freq, relation, policy->cpu);
@@ -132,15 +131,19 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
                break;
        }
 
-       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
-               unsigned int freq = table[i].frequency;
-               if (freq == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
+
+               i = pos - table;
                if ((freq < policy->min) || (freq > policy->max))
                        continue;
+               if (freq == target_freq) {
+                       optimal.driver_data = i;
+                       break;
+               }
                switch (relation) {
                case CPUFREQ_RELATION_H:
-                       if (freq <= target_freq) {
+                       if (freq < target_freq) {
                                if (freq >= optimal.frequency) {
                                        optimal.frequency = freq;
                                        optimal.driver_data = i;
@@ -153,7 +156,7 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
                        }
                        break;
                case CPUFREQ_RELATION_L:
-                       if (freq >= target_freq) {
+                       if (freq > target_freq) {
                                if (freq <= optimal.frequency) {
                                        optimal.frequency = freq;
                                        optimal.driver_data = i;
@@ -184,8 +187,7 @@ EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target);
 int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
                unsigned int freq)
 {
-       struct cpufreq_frequency_table *table;
-       int i;
+       struct cpufreq_frequency_table *pos, *table;
 
        table = cpufreq_frequency_get_table(policy->cpu);
        if (unlikely(!table)) {
@@ -193,10 +195,9 @@ int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
                return -ENOENT;
        }
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               if (table[i].frequency == freq)
-                       return i;
-       }
+       cpufreq_for_each_valid_entry(pos, table)
+               if (pos->frequency == freq)
+                       return pos - table;
 
        return -EINVAL;
 }
@@ -208,16 +209,13 @@ EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);
 static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
                                    bool show_boost)
 {
-       unsigned int i = 0;
        ssize_t count = 0;
-       struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *table = policy->freq_table;
 
        if (!table)
                return -ENODEV;
 
-       for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
-               if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       cpufreq_for_each_valid_entry(pos, table) {
                /*
                 * show_boost = true and driver_data = BOOST freq
                 * display BOOST freqs
@@ -229,10 +227,10 @@ static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
                 * show_boost = false and driver_data != BOOST freq
                 * display NON BOOST freqs
                 */
-               if (show_boost ^ (table[i].flags & CPUFREQ_BOOST_FREQ))
+               if (show_boost ^ (pos->flags & CPUFREQ_BOOST_FREQ))
                        continue;
 
-               count += sprintf(&buf[count], "%d ", table[i].frequency);
+               count += sprintf(&buf[count], "%d ", pos->frequency);
        }
        count += sprintf(&buf[count], "\n");
 
index e27fca8..af366c2 100644 (file)
@@ -9,7 +9,6 @@
 #include <linux/clk.h>
 #include <linux/cpu.h>
 #include <linux/cpufreq.h>
-#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -170,25 +169,25 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
                return -ENOENT;
        }
 
-       arm_clk = devm_clk_get(cpu_dev, "arm");
-       pll1_sys_clk = devm_clk_get(cpu_dev, "pll1_sys");
-       pll1_sw_clk = devm_clk_get(cpu_dev, "pll1_sw");
-       step_clk = devm_clk_get(cpu_dev, "step");
-       pll2_pfd2_396m_clk = devm_clk_get(cpu_dev, "pll2_pfd2_396m");
+       arm_clk = clk_get(cpu_dev, "arm");
+       pll1_sys_clk = clk_get(cpu_dev, "pll1_sys");
+       pll1_sw_clk = clk_get(cpu_dev, "pll1_sw");
+       step_clk = clk_get(cpu_dev, "step");
+       pll2_pfd2_396m_clk = clk_get(cpu_dev, "pll2_pfd2_396m");
        if (IS_ERR(arm_clk) || IS_ERR(pll1_sys_clk) || IS_ERR(pll1_sw_clk) ||
            IS_ERR(step_clk) || IS_ERR(pll2_pfd2_396m_clk)) {
                dev_err(cpu_dev, "failed to get clocks\n");
                ret = -ENOENT;
-               goto put_node;
+               goto put_clk;
        }
 
-       arm_reg = devm_regulator_get(cpu_dev, "arm");
-       pu_reg = devm_regulator_get(cpu_dev, "pu");
-       soc_reg = devm_regulator_get(cpu_dev, "soc");
+       arm_reg = regulator_get(cpu_dev, "arm");
+       pu_reg = regulator_get(cpu_dev, "pu");
+       soc_reg = regulator_get(cpu_dev, "soc");
        if (IS_ERR(arm_reg) || IS_ERR(pu_reg) || IS_ERR(soc_reg)) {
                dev_err(cpu_dev, "failed to get regulators\n");
                ret = -ENOENT;
-               goto put_node;
+               goto put_reg;
        }
 
        /*
@@ -201,21 +200,21 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
                ret = of_init_opp_table(cpu_dev);
                if (ret < 0) {
                        dev_err(cpu_dev, "failed to init OPP table: %d\n", ret);
-                       goto put_node;
+                       goto put_reg;
                }
 
                num = dev_pm_opp_get_opp_count(cpu_dev);
                if (num < 0) {
                        ret = num;
                        dev_err(cpu_dev, "no OPP table is found: %d\n", ret);
-                       goto put_node;
+                       goto put_reg;
                }
        }
 
        ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
        if (ret) {
                dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
-               goto put_node;
+               goto put_reg;
        }
 
        /* Make imx6_soc_volt array's size same as arm opp number */
@@ -301,7 +300,24 @@ soc_opp_out:
 
 free_freq_table:
        dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
-put_node:
+put_reg:
+       if (!IS_ERR(arm_reg))
+               regulator_put(arm_reg);
+       if (!IS_ERR(pu_reg))
+               regulator_put(pu_reg);
+       if (!IS_ERR(soc_reg))
+               regulator_put(soc_reg);
+put_clk:
+       if (!IS_ERR(arm_clk))
+               clk_put(arm_clk);
+       if (!IS_ERR(pll1_sys_clk))
+               clk_put(pll1_sys_clk);
+       if (!IS_ERR(pll1_sw_clk))
+               clk_put(pll1_sw_clk);
+       if (!IS_ERR(step_clk))
+               clk_put(step_clk);
+       if (!IS_ERR(pll2_pfd2_396m_clk))
+               clk_put(pll2_pfd2_396m_clk);
        of_node_put(np);
        return ret;
 }
@@ -310,6 +326,14 @@ static int imx6q_cpufreq_remove(struct platform_device *pdev)
 {
        cpufreq_unregister_driver(&imx6q_cpufreq_driver);
        dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
+       regulator_put(arm_reg);
+       regulator_put(pu_reg);
+       regulator_put(soc_reg);
+       clk_put(arm_clk);
+       clk_put(pll1_sys_clk);
+       clk_put(pll1_sw_clk);
+       clk_put(step_clk);
+       clk_put(pll2_pfd2_396m_clk);
 
        return 0;
 }
index eab8ccf..aebd457 100644 (file)
 #include <asm/msr.h>
 #include <asm/cpu_device_id.h>
 
-#define SAMPLE_COUNT           3
-
 #define BYT_RATIOS             0x66a
 #define BYT_VIDS               0x66b
 #define BYT_TURBO_RATIOS       0x66c
 #define BYT_TURBO_VIDS         0x66d
 
 
-#define FRAC_BITS 6
+#define FRAC_BITS 8
 #define int_tofp(X) ((int64_t)(X) << FRAC_BITS)
 #define fp_toint(X) ((X) >> FRAC_BITS)
-#define FP_ROUNDUP(X) ((X) += 1 << FRAC_BITS)
+
 
 static inline int32_t mul_fp(int32_t x, int32_t y)
 {
@@ -59,8 +57,8 @@ struct sample {
        int32_t core_pct_busy;
        u64 aperf;
        u64 mperf;
-       unsigned long long tsc;
        int freq;
+       ktime_t time;
 };
 
 struct pstate_data {
@@ -90,17 +88,15 @@ struct _pid {
 struct cpudata {
        int cpu;
 
-       char name[64];
-
        struct timer_list timer;
 
        struct pstate_data pstate;
        struct vid_data vid;
        struct _pid pid;
 
+       ktime_t last_sample_time;
        u64     prev_aperf;
        u64     prev_mperf;
-       unsigned long long prev_tsc;
        struct sample sample;
 };
 
@@ -200,7 +196,10 @@ static signed int pid_calc(struct _pid *pid, int32_t busy)
        pid->last_err = fp_error;
 
        result = pterm + mul_fp(pid->integral, pid->i_gain) + dterm;
-
+       if (result >= 0)
+               result = result + (1 << (FRAC_BITS-1));
+       else
+               result = result - (1 << (FRAC_BITS-1));
        return (signed int)fp_toint(result);
 }
 
@@ -546,8 +545,6 @@ static inline void intel_pstate_pstate_decrease(struct cpudata *cpu, int steps)
 
 static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
 {
-       sprintf(cpu->name, "Intel 2nd generation core");
-
        cpu->pstate.min_pstate = pstate_funcs.get_min();
        cpu->pstate.max_pstate = pstate_funcs.get_max();
        cpu->pstate.turbo_pstate = pstate_funcs.get_turbo();
@@ -557,50 +554,45 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
        intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate);
 }
 
-static inline void intel_pstate_calc_busy(struct cpudata *cpu,
-                                       struct sample *sample)
+static inline void intel_pstate_calc_busy(struct cpudata *cpu)
 {
-       int32_t core_pct;
-       int32_t c0_pct;
+       struct sample *sample = &cpu->sample;
+       int64_t core_pct;
+       int32_t rem;
 
-       core_pct = div_fp(int_tofp((sample->aperf)),
-                       int_tofp((sample->mperf)));
-       core_pct = mul_fp(core_pct, int_tofp(100));
-       FP_ROUNDUP(core_pct);
+       core_pct = int_tofp(sample->aperf) * int_tofp(100);
+       core_pct = div_u64_rem(core_pct, int_tofp(sample->mperf), &rem);
 
-       c0_pct = div_fp(int_tofp(sample->mperf), int_tofp(sample->tsc));
+       if ((rem << 1) >= int_tofp(sample->mperf))
+               core_pct += 1;
 
        sample->freq = fp_toint(
                mul_fp(int_tofp(cpu->pstate.max_pstate * 1000), core_pct));
 
-       sample->core_pct_busy = mul_fp(core_pct, c0_pct);
+       sample->core_pct_busy = (int32_t)core_pct;
 }
 
 static inline void intel_pstate_sample(struct cpudata *cpu)
 {
        u64 aperf, mperf;
-       unsigned long long tsc;
 
        rdmsrl(MSR_IA32_APERF, aperf);
        rdmsrl(MSR_IA32_MPERF, mperf);
-       tsc = native_read_tsc();
 
        aperf = aperf >> FRAC_BITS;
        mperf = mperf >> FRAC_BITS;
-       tsc = tsc >> FRAC_BITS;
 
+       cpu->last_sample_time = cpu->sample.time;
+       cpu->sample.time = ktime_get();
        cpu->sample.aperf = aperf;
        cpu->sample.mperf = mperf;
-       cpu->sample.tsc = tsc;
        cpu->sample.aperf -= cpu->prev_aperf;
        cpu->sample.mperf -= cpu->prev_mperf;
-       cpu->sample.tsc -= cpu->prev_tsc;
 
-       intel_pstate_calc_busy(cpu, &cpu->sample);
+       intel_pstate_calc_busy(cpu);
 
        cpu->prev_aperf = aperf;
        cpu->prev_mperf = mperf;
-       cpu->prev_tsc = tsc;
 }
 
 static inline void intel_pstate_set_sample_time(struct cpudata *cpu)
@@ -614,13 +606,25 @@ static inline void intel_pstate_set_sample_time(struct cpudata *cpu)
 
 static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu)
 {
-       int32_t core_busy, max_pstate, current_pstate;
+       int32_t core_busy, max_pstate, current_pstate, sample_ratio;
+       u32 duration_us;
+       u32 sample_time;
 
        core_busy = cpu->sample.core_pct_busy;
        max_pstate = int_tofp(cpu->pstate.max_pstate);
        current_pstate = int_tofp(cpu->pstate.current_pstate);
        core_busy = mul_fp(core_busy, div_fp(max_pstate, current_pstate));
-       return FP_ROUNDUP(core_busy);
+
+       sample_time = (pid_params.sample_rate_ms  * USEC_PER_MSEC);
+       duration_us = (u32) ktime_us_delta(cpu->sample.time,
+                                       cpu->last_sample_time);
+       if (duration_us > sample_time * 3) {
+               sample_ratio = div_fp(int_tofp(sample_time),
+                               int_tofp(duration_us));
+               core_busy = mul_fp(core_busy, sample_ratio);
+       }
+
+       return core_busy;
 }
 
 static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu)
@@ -674,10 +678,13 @@ static const struct x86_cpu_id intel_pstate_cpu_ids[] = {
        ICPU(0x37, byt_params),
        ICPU(0x3a, core_params),
        ICPU(0x3c, core_params),
+       ICPU(0x3d, core_params),
        ICPU(0x3e, core_params),
        ICPU(0x3f, core_params),
        ICPU(0x45, core_params),
        ICPU(0x46, core_params),
+       ICPU(0x4f, core_params),
+       ICPU(0x56, core_params),
        {}
 };
 MODULE_DEVICE_TABLE(x86cpu, intel_pstate_cpu_ids);
index 5c4369b..c913906 100644 (file)
@@ -530,6 +530,7 @@ static int longhaul_get_ranges(void)
 
 static void longhaul_setup_voltagescaling(void)
 {
+       struct cpufreq_frequency_table *freq_pos;
        union msr_longhaul longhaul;
        struct mV_pos minvid, maxvid, vid;
        unsigned int j, speed, pos, kHz_step, numvscales;
@@ -608,18 +609,16 @@ static void longhaul_setup_voltagescaling(void)
        /* Calculate kHz for one voltage step */
        kHz_step = (highest_speed - min_vid_speed) / numvscales;
 
-       j = 0;
-       while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) {
-               speed = longhaul_table[j].frequency;
+       cpufreq_for_each_entry(freq_pos, longhaul_table) {
+               speed = freq_pos->frequency;
                if (speed > min_vid_speed)
                        pos = (speed - min_vid_speed) / kHz_step + minvid.pos;
                else
                        pos = minvid.pos;
-               longhaul_table[j].driver_data |= mV_vrm_table[pos] << 8;
+               freq_pos->driver_data |= mV_vrm_table[pos] << 8;
                vid = vrm_mV_table[mV_vrm_table[pos]];
                printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n",
-                               speed, j, vid.mV);
-               j++;
+                       speed, (int)(freq_pos - longhaul_table), vid.mV);
        }
 
        can_scale_voltage = 1;
index 84c84b5..35dd4d7 100644 (file)
@@ -136,9 +136,10 @@ void restore_astate(int cpu)
 
 static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
+       struct cpufreq_frequency_table *pos;
        const u32 *max_freqp;
        u32 max_freq;
-       int i, cur_astate;
+       int cur_astate;
        struct resource res;
        struct device_node *cpu, *dn;
        int err = -ENODEV;
@@ -197,10 +198,9 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
        pr_debug("initializing frequency table\n");
 
        /* initialize frequency table */
-       for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
-               pas_freqs[i].frequency =
-                       get_astate_freq(pas_freqs[i].driver_data) * 100000;
-               pr_debug("%d: %d\n", i, pas_freqs[i].frequency);
+       cpufreq_for_each_entry(pos, pas_freqs) {
+               pos->frequency = get_astate_freq(pos->driver_data) * 100000;
+               pr_debug("%d: %d\n", (int)(pos - pas_freqs), pos->frequency);
        }
 
        cur_astate = get_cur_astate(policy->cpu);
index 78904e6..c8012bc 100644 (file)
@@ -151,6 +151,7 @@ static int powernow_k6_target(struct cpufreq_policy *policy,
 
 static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
 {
+       struct cpufreq_frequency_table *pos;
        unsigned int i, f;
        unsigned khz;
 
@@ -168,12 +169,11 @@ static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
                }
        }
        if (param_max_multiplier) {
-               for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
-                       if (clock_ratio[i].driver_data == param_max_multiplier) {
+               cpufreq_for_each_entry(pos, clock_ratio)
+                       if (pos->driver_data == param_max_multiplier) {
                                max_multiplier = param_max_multiplier;
                                goto have_max_multiplier;
                        }
-               }
                printk(KERN_ERR "powernow-k6: invalid max_multiplier parameter, valid parameters 20, 30, 35, 40, 45, 50, 55, 60\n");
                return -EINVAL;
        }
@@ -201,12 +201,12 @@ have_busfreq:
        param_busfreq = busfreq * 10;
 
        /* table init */
-       for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
-               f = clock_ratio[i].driver_data;
+       cpufreq_for_each_entry(pos, clock_ratio) {
+               f = pos->driver_data;
                if (f > max_multiplier)
-                       clock_ratio[i].frequency = CPUFREQ_ENTRY_INVALID;
+                       pos->frequency = CPUFREQ_ENTRY_INVALID;
                else
-                       clock_ratio[i].frequency = busfreq * f;
+                       pos->frequency = busfreq * f;
        }
 
        /* cpuinfo and default policy values */
index 1b6ae6b..f9ce7e4 100644 (file)
@@ -27,6 +27,8 @@
  *  power and thermal data sheets, (e.g. 30417.pdf, 30430.pdf, 43375.pdf)
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/smp.h>
 #include <linux/module.h>
@@ -45,7 +47,6 @@
 #include <linux/mutex.h>
 #include <acpi/processor.h>
 
-#define PFX "powernow-k8: "
 #define VERSION "version 2.20.00"
 #include "powernow-k8.h"
 
@@ -161,7 +162,7 @@ static int write_new_fid(struct powernow_k8_data *data, u32 fid)
        u32 i = 0;
 
        if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) {
-               printk(KERN_ERR PFX "internal error - overflow on fid write\n");
+               pr_err("internal error - overflow on fid write\n");
                return 1;
        }
 
@@ -175,9 +176,7 @@ static int write_new_fid(struct powernow_k8_data *data, u32 fid)
        do {
                wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
                if (i++ > 100) {
-                       printk(KERN_ERR PFX
-                               "Hardware error - pending bit very stuck - "
-                               "no further pstate changes possible\n");
+                       pr_err("Hardware error - pending bit very stuck - no further pstate changes possible\n");
                        return 1;
                }
        } while (query_current_values_with_pending_wait(data));
@@ -185,15 +184,13 @@ static int write_new_fid(struct powernow_k8_data *data, u32 fid)
        count_off_irt(data);
 
        if (savevid != data->currvid) {
-               printk(KERN_ERR PFX
-                       "vid change on fid trans, old 0x%x, new 0x%x\n",
-                       savevid, data->currvid);
+               pr_err("vid change on fid trans, old 0x%x, new 0x%x\n",
+                      savevid, data->currvid);
                return 1;
        }
 
        if (fid != data->currfid) {
-               printk(KERN_ERR PFX
-                       "fid trans failed, fid 0x%x, curr 0x%x\n", fid,
+               pr_err("fid trans failed, fid 0x%x, curr 0x%x\n", fid,
                        data->currfid);
                return 1;
        }
@@ -209,7 +206,7 @@ static int write_new_vid(struct powernow_k8_data *data, u32 vid)
        int i = 0;
 
        if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
-               printk(KERN_ERR PFX "internal error - overflow on vid write\n");
+               pr_err("internal error - overflow on vid write\n");
                return 1;
        }
 
@@ -223,23 +220,19 @@ static int write_new_vid(struct powernow_k8_data *data, u32 vid)
        do {
                wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
                if (i++ > 100) {
-                       printk(KERN_ERR PFX "internal error - pending bit "
-                                       "very stuck - no further pstate "
-                                       "changes possible\n");
+                       pr_err("internal error - pending bit very stuck - no further pstate changes possible\n");
                        return 1;
                }
        } while (query_current_values_with_pending_wait(data));
 
        if (savefid != data->currfid) {
-               printk(KERN_ERR PFX "fid changed on vid trans, old "
-                       "0x%x new 0x%x\n",
-                      savefid, data->currfid);
+               pr_err("fid changed on vid trans, old 0x%x new 0x%x\n",
+                       savefid, data->currfid);
                return 1;
        }
 
        if (vid != data->currvid) {
-               printk(KERN_ERR PFX "vid trans failed, vid 0x%x, "
-                               "curr 0x%x\n",
+               pr_err("vid trans failed, vid 0x%x, curr 0x%x\n",
                                vid, data->currvid);
                return 1;
        }
@@ -283,8 +276,7 @@ static int transition_fid_vid(struct powernow_k8_data *data,
                return 1;
 
        if ((reqfid != data->currfid) || (reqvid != data->currvid)) {
-               printk(KERN_ERR PFX "failed (cpu%d): req 0x%x 0x%x, "
-                               "curr 0x%x 0x%x\n",
+               pr_err("failed (cpu%d): req 0x%x 0x%x, curr 0x%x 0x%x\n",
                                smp_processor_id(),
                                reqfid, reqvid, data->currfid, data->currvid);
                return 1;
@@ -304,8 +296,7 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data,
        u32 savefid = data->currfid;
        u32 maxvid, lo, rvomult = 1;
 
-       pr_debug("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, "
-               "reqvid 0x%x, rvo 0x%x\n",
+       pr_debug("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n",
                smp_processor_id(),
                data->currfid, data->currvid, reqvid, data->rvo);
 
@@ -342,8 +333,7 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data,
                return 1;
 
        if (savefid != data->currfid) {
-               printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n",
-                               data->currfid);
+               pr_err("ph1 err, currfid changed 0x%x\n", data->currfid);
                return 1;
        }
 
@@ -360,13 +350,11 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
        u32 fid_interval, savevid = data->currvid;
 
        if (data->currfid == reqfid) {
-               printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n",
-                               data->currfid);
+               pr_err("ph2 null fid transition 0x%x\n", data->currfid);
                return 0;
        }
 
-       pr_debug("ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, "
-               "reqfid 0x%x\n",
+       pr_debug("ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n",
                smp_processor_id(),
                data->currfid, data->currvid, reqfid);
 
@@ -409,15 +397,13 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
                return 1;
 
        if (data->currfid != reqfid) {
-               printk(KERN_ERR PFX
-                       "ph2: mismatch, failed fid transition, "
-                       "curr 0x%x, req 0x%x\n",
+               pr_err("ph2: mismatch, failed fid transition, curr 0x%x, req 0x%x\n",
                        data->currfid, reqfid);
                return 1;
        }
 
        if (savevid != data->currvid) {
-               printk(KERN_ERR PFX "ph2: vid changed, save 0x%x, curr 0x%x\n",
+               pr_err("ph2: vid changed, save 0x%x, curr 0x%x\n",
                        savevid, data->currvid);
                return 1;
        }
@@ -444,17 +430,14 @@ static int core_voltage_post_transition(struct powernow_k8_data *data,
                        return 1;
 
                if (savefid != data->currfid) {
-                       printk(KERN_ERR PFX
-                              "ph3: bad fid change, save 0x%x, curr 0x%x\n",
-                              savefid, data->currfid);
+                       pr_err("ph3: bad fid change, save 0x%x, curr 0x%x\n",
+                               savefid, data->currfid);
                        return 1;
                }
 
                if (data->currvid != reqvid) {
-                       printk(KERN_ERR PFX
-                              "ph3: failed vid transition\n, "
-                              "req 0x%x, curr 0x%x",
-                              reqvid, data->currvid);
+                       pr_err("ph3: failed vid transition\n, req 0x%x, curr 0x%x",
+                               reqvid, data->currvid);
                        return 1;
                }
        }
@@ -498,23 +481,20 @@ static void check_supported_cpu(void *_rc)
        if ((eax & CPUID_XFAM) == CPUID_XFAM_K8) {
                if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) ||
                    ((eax & CPUID_XMOD) > CPUID_XMOD_REV_MASK)) {
-                       printk(KERN_INFO PFX
-                               "Processor cpuid %x not supported\n", eax);
+                       pr_info("Processor cpuid %x not supported\n", eax);
                        return;
                }
 
                eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
                if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
-                       printk(KERN_INFO PFX
-                              "No frequency change capabilities detected\n");
+                       pr_info("No frequency change capabilities detected\n");
                        return;
                }
 
                cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
                if ((edx & P_STATE_TRANSITION_CAPABLE)
                        != P_STATE_TRANSITION_CAPABLE) {
-                       printk(KERN_INFO PFX
-                               "Power state transitions not supported\n");
+                       pr_info("Power state transitions not supported\n");
                        return;
                }
                *rc = 0;
@@ -529,43 +509,39 @@ static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst,
 
        for (j = 0; j < data->numps; j++) {
                if (pst[j].vid > LEAST_VID) {
-                       printk(KERN_ERR FW_BUG PFX "vid %d invalid : 0x%x\n",
-                              j, pst[j].vid);
+                       pr_err(FW_BUG "vid %d invalid : 0x%x\n", j,
+                               pst[j].vid);
                        return -EINVAL;
                }
                if (pst[j].vid < data->rvo) {
                        /* vid + rvo >= 0 */
-                       printk(KERN_ERR FW_BUG PFX "0 vid exceeded with pstate"
-                              " %d\n", j);
+                       pr_err(FW_BUG "0 vid exceeded with pstate %d\n", j);
                        return -ENODEV;
                }
                if (pst[j].vid < maxvid + data->rvo) {
                        /* vid + rvo >= maxvid */
-                       printk(KERN_ERR FW_BUG PFX "maxvid exceeded with pstate"
-                              " %d\n", j);
+                       pr_err(FW_BUG "maxvid exceeded with pstate %d\n", j);
                        return -ENODEV;
                }
                if (pst[j].fid > MAX_FID) {
-                       printk(KERN_ERR FW_BUG PFX "maxfid exceeded with pstate"
-                              " %d\n", j);
+                       pr_err(FW_BUG "maxfid exceeded with pstate %d\n", j);
                        return -ENODEV;
                }
                if (j && (pst[j].fid < HI_FID_TABLE_BOTTOM)) {
                        /* Only first fid is allowed to be in "low" range */
-                       printk(KERN_ERR FW_BUG PFX "two low fids - %d : "
-                              "0x%x\n", j, pst[j].fid);
+                       pr_err(FW_BUG "two low fids - %d : 0x%x\n", j,
+                               pst[j].fid);
                        return -EINVAL;
                }
                if (pst[j].fid < lastfid)
                        lastfid = pst[j].fid;
        }
        if (lastfid & 1) {
-               printk(KERN_ERR FW_BUG PFX "lastfid invalid\n");
+               pr_err(FW_BUG "lastfid invalid\n");
                return -EINVAL;
        }
        if (lastfid > LO_FID_TABLE_TOP)
-               printk(KERN_INFO FW_BUG PFX
-                       "first fid not from lo freq table\n");
+               pr_info(FW_BUG "first fid not from lo freq table\n");
 
        return 0;
 }
@@ -582,16 +558,14 @@ static void print_basics(struct powernow_k8_data *data)
        for (j = 0; j < data->numps; j++) {
                if (data->powernow_table[j].frequency !=
                                CPUFREQ_ENTRY_INVALID) {
-                               printk(KERN_INFO PFX
-                                       "fid 0x%x (%d MHz), vid 0x%x\n",
-                                       data->powernow_table[j].driver_data & 0xff,
-                                       data->powernow_table[j].frequency/1000,
-                                       data->powernow_table[j].driver_data >> 8);
+                       pr_info("fid 0x%x (%d MHz), vid 0x%x\n",
+                               data->powernow_table[j].driver_data & 0xff,
+                               data->powernow_table[j].frequency/1000,
+                               data->powernow_table[j].driver_data >> 8);
                }
        }
        if (data->batps)
-               printk(KERN_INFO PFX "Only %d pstates on battery\n",
-                               data->batps);
+               pr_info("Only %d pstates on battery\n", data->batps);
 }
 
 static int fill_powernow_table(struct powernow_k8_data *data,
@@ -602,21 +576,20 @@ static int fill_powernow_table(struct powernow_k8_data *data,
 
        if (data->batps) {
                /* use ACPI support to get full speed on mains power */
-               printk(KERN_WARNING PFX
-                       "Only %d pstates usable (use ACPI driver for full "
-                       "range\n", data->batps);
+               pr_warn("Only %d pstates usable (use ACPI driver for full range\n",
+                       data->batps);
                data->numps = data->batps;
        }
 
        for (j = 1; j < data->numps; j++) {
                if (pst[j-1].fid >= pst[j].fid) {
-                       printk(KERN_ERR PFX "PST out of sequence\n");
+                       pr_err("PST out of sequence\n");
                        return -EINVAL;
                }
        }
 
        if (data->numps < 2) {
-               printk(KERN_ERR PFX "no p states to transition\n");
+               pr_err("no p states to transition\n");
                return -ENODEV;
        }
 
@@ -626,7 +599,7 @@ static int fill_powernow_table(struct powernow_k8_data *data,
        powernow_table = kzalloc((sizeof(*powernow_table)
                * (data->numps + 1)), GFP_KERNEL);
        if (!powernow_table) {
-               printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
+               pr_err("powernow_table memory alloc failure\n");
                return -ENOMEM;
        }
 
@@ -681,13 +654,13 @@ static int find_psb_table(struct powernow_k8_data *data)
 
                pr_debug("table vers: 0x%x\n", psb->tableversion);
                if (psb->tableversion != PSB_VERSION_1_4) {
-                       printk(KERN_ERR FW_BUG PFX "PSB table is not v1.4\n");
+                       pr_err(FW_BUG "PSB table is not v1.4\n");
                        return -ENODEV;
                }
 
                pr_debug("flags: 0x%x\n", psb->flags1);
                if (psb->flags1) {
-                       printk(KERN_ERR FW_BUG PFX "unknown flags\n");
+                       pr_err(FW_BUG "unknown flags\n");
                        return -ENODEV;
                }
 
@@ -716,7 +689,7 @@ static int find_psb_table(struct powernow_k8_data *data)
                                cpst = 1;
                }
                if (cpst != 1) {
-                       printk(KERN_ERR FW_BUG PFX "numpst must be 1\n");
+                       pr_err(FW_BUG "numpst must be 1\n");
                        return -ENODEV;
                }
 
@@ -742,9 +715,8 @@ static int find_psb_table(struct powernow_k8_data *data)
         * BIOS and Kernel Developer's Guide, which is available on
         * www.amd.com
         */
-       printk(KERN_ERR FW_BUG PFX "No PSB or ACPI _PSS objects\n");
-       printk(KERN_ERR PFX "Make sure that your BIOS is up to date"
-               " and Cool'N'Quiet support is enabled in BIOS setup\n");
+       pr_err(FW_BUG "No PSB or ACPI _PSS objects\n");
+       pr_err("Make sure that your BIOS is up to date and Cool'N'Quiet support is enabled in BIOS setup\n");
        return -ENODEV;
 }
 
@@ -819,8 +791,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
        acpi_processor_notify_smm(THIS_MODULE);
 
        if (!zalloc_cpumask_var(&data->acpi_data.shared_cpu_map, GFP_KERNEL)) {
-               printk(KERN_ERR PFX
-                               "unable to alloc powernow_k8_data cpumask\n");
+               pr_err("unable to alloc powernow_k8_data cpumask\n");
                ret_val = -ENOMEM;
                goto err_out_mem;
        }
@@ -885,9 +856,8 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data,
                }
 
                if (freq != (data->acpi_data.states[i].core_frequency * 1000)) {
-                       printk(KERN_INFO PFX "invalid freq entries "
-                               "%u kHz vs. %u kHz\n", freq,
-                               (unsigned int)
+                       pr_info("invalid freq entries %u kHz vs. %u kHz\n",
+                               freq, (unsigned int)
                                (data->acpi_data.states[i].core_frequency
                                 * 1000));
                        invalidate_entry(powernow_table, i);
@@ -916,7 +886,7 @@ static int get_transition_latency(struct powernow_k8_data *data)
                        max_latency = cur_latency;
        }
        if (max_latency == 0) {
-               pr_err(FW_WARN PFX "Invalid zero transition latency\n");
+               pr_err(FW_WARN "Invalid zero transition latency\n");
                max_latency = 1;
        }
        /* value in usecs, needs to be in nanoseconds */
@@ -991,7 +961,7 @@ static long powernowk8_target_fn(void *arg)
        checkvid = data->currvid;
 
        if (pending_bit_stuck()) {
-               printk(KERN_ERR PFX "failing targ, change pending bit set\n");
+               pr_err("failing targ, change pending bit set\n");
                return -EIO;
        }
 
@@ -1003,12 +973,11 @@ static long powernowk8_target_fn(void *arg)
                return -EIO;
 
        pr_debug("targ: curr fid 0x%x, vid 0x%x\n",
-                data->currfid, data->currvid);
+               data->currfid, data->currvid);
 
        if ((checkvid != data->currvid) ||
            (checkfid != data->currfid)) {
-               pr_info(PFX
-                      "error - out of sync, fix 0x%x 0x%x, vid 0x%x 0x%x\n",
+               pr_info("error - out of sync, fix 0x%x 0x%x, vid 0x%x 0x%x\n",
                       checkfid, data->currfid,
                       checkvid, data->currvid);
        }
@@ -1020,7 +989,7 @@ static long powernowk8_target_fn(void *arg)
        ret = transition_frequency_fidvid(data, newstate);
 
        if (ret) {
-               printk(KERN_ERR PFX "transition frequency failed\n");
+               pr_err("transition frequency failed\n");
                mutex_unlock(&fidvid_mutex);
                return 1;
        }
@@ -1049,7 +1018,7 @@ static void powernowk8_cpu_init_on_cpu(void *_init_on_cpu)
        struct init_on_cpu *init_on_cpu = _init_on_cpu;
 
        if (pending_bit_stuck()) {
-               printk(KERN_ERR PFX "failing init, change pending bit set\n");
+               pr_err("failing init, change pending bit set\n");
                init_on_cpu->rc = -ENODEV;
                return;
        }
@@ -1064,11 +1033,10 @@ static void powernowk8_cpu_init_on_cpu(void *_init_on_cpu)
        init_on_cpu->rc = 0;
 }
 
-static const char missing_pss_msg[] =
-       KERN_ERR
-       FW_BUG PFX "No compatible ACPI _PSS objects found.\n"
-       FW_BUG PFX "First, make sure Cool'N'Quiet is enabled in the BIOS.\n"
-       FW_BUG PFX "If that doesn't help, try upgrading your BIOS.\n";
+#define MISSING_PSS_MSG \
+       FW_BUG "No compatible ACPI _PSS objects found.\n" \
+       FW_BUG "First, make sure Cool'N'Quiet is enabled in the BIOS.\n" \
+       FW_BUG "If that doesn't help, try upgrading your BIOS.\n"
 
 /* per CPU init entry point to the driver */
 static int powernowk8_cpu_init(struct cpufreq_policy *pol)
@@ -1083,7 +1051,7 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol)
 
        data = kzalloc(sizeof(*data), GFP_KERNEL);
        if (!data) {
-               printk(KERN_ERR PFX "unable to alloc powernow_k8_data");
+               pr_err("unable to alloc powernow_k8_data");
                return -ENOMEM;
        }
 
@@ -1095,13 +1063,11 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol)
                 * an UP version, and is deprecated by AMD.
                 */
                if (num_online_cpus() != 1) {
-                       printk_once(missing_pss_msg);
+                       pr_err_once(MISSING_PSS_MSG);
                        goto err_out;
                }
                if (pol->cpu != 0) {
-                       printk(KERN_ERR FW_BUG PFX "No ACPI _PSS objects for "
-                              "CPU other than CPU0. Complain to your BIOS "
-                              "vendor.\n");
+                       pr_err(FW_BUG "No ACPI _PSS objects for CPU other than CPU0. Complain to your BIOS vendor.\n");
                        goto err_out;
                }
                rc = find_psb_table(data);
@@ -1129,7 +1095,7 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol)
 
        /* min/max the cpu is capable of */
        if (cpufreq_table_validate_and_show(pol, data->powernow_table)) {
-               printk(KERN_ERR FW_BUG PFX "invalid powernow_table\n");
+               pr_err(FW_BUG "invalid powernow_table\n");
                powernow_k8_cpu_exit_acpi(data);
                kfree(data->powernow_table);
                kfree(data);
@@ -1137,7 +1103,7 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol)
        }
 
        pr_debug("cpu_init done, current fid 0x%x, vid 0x%x\n",
-                data->currfid, data->currvid);
+               data->currfid, data->currvid);
 
        /* Point all the CPUs in this policy to the same data */
        for_each_cpu(cpu, pol->cpus)
@@ -1220,12 +1186,12 @@ static void __request_acpi_cpufreq(void)
                goto request;
 
        if (strncmp(cur_drv, drv, min_t(size_t, strlen(cur_drv), strlen(drv))))
-               pr_warn(PFX "WTF driver: %s\n", cur_drv);
+               pr_warn("WTF driver: %s\n", cur_drv);
 
        return;
 
  request:
-       pr_warn(PFX "This CPU is not supported anymore, using acpi-cpufreq instead.\n");
+       pr_warn("This CPU is not supported anymore, using acpi-cpufreq instead.\n");
        request_module(drv);
 }
 
@@ -1260,7 +1226,7 @@ static int powernowk8_init(void)
        if (ret)
                return ret;
 
-       pr_info(PFX "Found %d %s (%d cpu cores) (" VERSION ")\n",
+       pr_info("Found %d %s (%d cpu cores) (" VERSION ")\n",
                num_online_nodes(), boot_cpu_data.x86_model_id, supported_cpus);
 
        return ret;
@@ -1274,8 +1240,8 @@ static void __exit powernowk8_exit(void)
        cpufreq_unregister_driver(&cpufreq_amd64_driver);
 }
 
-MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com> and "
-               "Mark Langsdorf <mark.langsdorf@amd.com>");
+MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com>");
+MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@amd.com>");
 MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
 MODULE_LICENSE("GPL");
 
index 79329d4..45ce11e 100644 (file)
@@ -19,7 +19,7 @@ struct powernow_k8_data {
        u32 vidmvs;  /* usable value calculated from mvs */
        u32 vstable; /* voltage stabilization time, units 20 us */
        u32 plllock; /* pll lock time, units 1 us */
-        u32 exttype; /* extended interface = 1 */
+       u32 exttype; /* extended interface = 1 */
 
        /* keep track of the current fid / vid or pstate */
        u32 currvid;
index af49688..bb1d08d 100644 (file)
@@ -235,7 +235,7 @@ static void powernv_read_cpu_freq(void *arg)
  * firmware for CPU 'cpu'. This value is reported through the sysfs
  * file cpuinfo_cur_freq.
  */
-unsigned int powernv_cpufreq_get(unsigned int cpu)
+static unsigned int powernv_cpufreq_get(unsigned int cpu)
 {
        struct powernv_smp_call_data freq_data;
 
index 5be8a48..5a4c5a6 100644 (file)
@@ -67,9 +67,10 @@ static int set_pmode(unsigned int cpu, unsigned int slow_mode)
 
 static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
+       struct cpufreq_frequency_table *pos;
        const u32 *max_freqp;
        u32 max_freq;
-       int i, cur_pmode;
+       int cur_pmode;
        struct device_node *cpu;
 
        cpu = of_get_cpu_node(policy->cpu, NULL);
@@ -102,9 +103,9 @@ static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy)
        pr_debug("initializing frequency table\n");
 
        /* initialize frequency table */
-       for (i=0; cbe_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
-               cbe_freqs[i].frequency = max_freq / cbe_freqs[i].driver_data;
-               pr_debug("%d: %d\n", i, cbe_freqs[i].frequency);
+       cpufreq_for_each_entry(pos, cbe_freqs) {
+               pos->frequency = max_freq / pos->driver_data;
+               pr_debug("%d: %d\n", (int)(pos - cbe_freqs), pos->frequency);
        }
 
        /* if DEBUG is enabled set_pmode() measures the latency
index 4626f90..2fd53ea 100644 (file)
@@ -266,7 +266,7 @@ out:
 static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq)
 {
        int count, v, i, found;
-       struct cpufreq_frequency_table *freq;
+       struct cpufreq_frequency_table *pos;
        struct s3c2416_dvfs *dvfs;
 
        count = regulator_count_voltages(s3c_freq->vddarm);
@@ -275,12 +275,11 @@ static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq)
                return;
        }
 
-       freq = s3c_freq->freq_table;
-       while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) {
-               if (freq->frequency == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       if (!count)
+               goto out;
 
-               dvfs = &s3c2416_dvfs_table[freq->driver_data];
+       cpufreq_for_each_valid_entry(pos, s3c_freq->freq_table) {
+               dvfs = &s3c2416_dvfs_table[pos->driver_data];
                found = 0;
 
                /* Check only the min-voltage, more is always ok on S3C2416 */
@@ -292,13 +291,12 @@ static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq)
 
                if (!found) {
                        pr_debug("cpufreq: %dkHz unsupported by regulator\n",
-                                freq->frequency);
-                       freq->frequency = CPUFREQ_ENTRY_INVALID;
+                                pos->frequency);
+                       pos->frequency = CPUFREQ_ENTRY_INVALID;
                }
-
-               freq++;
        }
 
+out:
        /* Guessed */
        s3c_freq->regulator_latency = 1 * 1000 * 1000;
 }
@@ -338,7 +336,7 @@ static struct notifier_block s3c2416_cpufreq_reboot_notifier = {
 static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy)
 {
        struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
-       struct cpufreq_frequency_table *freq;
+       struct cpufreq_frequency_table *pos;
        struct clk *msysclk;
        unsigned long rate;
        int ret;
@@ -427,31 +425,27 @@ static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy)
        s3c_freq->regulator_latency = 0;
 #endif
 
-       freq = s3c_freq->freq_table;
-       while (freq->frequency != CPUFREQ_TABLE_END) {
+       cpufreq_for_each_entry(pos, s3c_freq->freq_table) {
                /* special handling for dvs mode */
-               if (freq->driver_data == 0) {
+               if (pos->driver_data == 0) {
                        if (!s3c_freq->hclk) {
                                pr_debug("cpufreq: %dkHz unsupported as it would need unavailable dvs mode\n",
-                                        freq->frequency);
-                               freq->frequency = CPUFREQ_ENTRY_INVALID;
+                                        pos->frequency);
+                               pos->frequency = CPUFREQ_ENTRY_INVALID;
                        } else {
-                               freq++;
                                continue;
                        }
                }
 
                /* Check for frequencies we can generate */
                rate = clk_round_rate(s3c_freq->armdiv,
-                                     freq->frequency * 1000);
+                                     pos->frequency * 1000);
                rate /= 1000;
-               if (rate != freq->frequency) {
+               if (rate != pos->frequency) {
                        pr_debug("cpufreq: %dkHz unsupported by clock (clk_round_rate return %lu)\n",
-                                freq->frequency, rate);
-                       freq->frequency = CPUFREQ_ENTRY_INVALID;
+                               pos->frequency, rate);
+                       pos->frequency = CPUFREQ_ENTRY_INVALID;
                }
-
-               freq++;
        }
 
        /* Datasheet says PLL stabalisation time must be at least 300us,
index ff7d3ec..176e84c 100644 (file)
@@ -118,11 +118,10 @@ static void __init s3c64xx_cpufreq_config_regulator(void)
                pr_err("Unable to check supported voltages\n");
        }
 
-       freq = s3c64xx_freq_table;
-       while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) {
-               if (freq->frequency == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       if (!count)
+               goto out;
 
+       cpufreq_for_each_valid_entry(freq, s3c64xx_freq_table) {
                dvfs = &s3c64xx_dvfs_table[freq->driver_data];
                found = 0;
 
@@ -137,10 +136,9 @@ static void __init s3c64xx_cpufreq_config_regulator(void)
                                 freq->frequency);
                        freq->frequency = CPUFREQ_ENTRY_INVALID;
                }
-
-               freq++;
        }
 
+out:
        /* Guess based on having to do an I2C/SPI write; in future we
         * will be able to query the regulator performance here. */
        regulator_latency = 1 * 1000 * 1000;
@@ -179,8 +177,7 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
        }
 #endif
 
-       freq = s3c64xx_freq_table;
-       while (freq->frequency != CPUFREQ_TABLE_END) {
+       cpufreq_for_each_entry(freq, s3c64xx_freq_table) {
                unsigned long r;
 
                /* Check for frequencies we can generate */
@@ -196,8 +193,6 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
                 * frequency is the maximum we can support. */
                if (!vddarm && freq->frequency > clk_get_rate(policy->clk) / 1000)
                        freq->frequency = CPUFREQ_ENTRY_INVALID;
-
-               freq++;
        }
 
        /* Datasheet says PLL stabalisation time (if we were to use
index ab2c1a4..19a10b8 100644 (file)
@@ -175,10 +175,8 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
        mutex_lock(&set_freq_lock);
 
        if (no_cpufreq_access) {
-#ifdef CONFIG_PM_VERBOSE
-               pr_err("%s:%d denied access to %s as it is disabled"
-                               "temporarily\n", __FILE__, __LINE__, __func__);
-#endif
+               pr_err("Denied access to %s as it is disabled temporarily\n",
+                      __func__);
                ret = -EINVAL;
                goto exit;
        }
index 6723f03..7d4a315 100644 (file)
@@ -28,7 +28,7 @@
 #include <asm/cpu_device_id.h>
 
 #define PFX            "speedstep-centrino: "
-#define MAINTAINER     "cpufreq@vger.kernel.org"
+#define MAINTAINER     "linux-pm@vger.kernel.org"
 
 #define INTEL_MSR_RANGE        (0xffff)
 
index 63f0059..6e774c6 100644 (file)
@@ -82,9 +82,9 @@ out:
        return ret;
 }
 
-static int tegra_update_cpu_speed(struct cpufreq_policy *policy,
-               unsigned long rate)
+static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
 {
+       unsigned long rate = freq_table[index].frequency;
        int ret = 0;
 
        /*
@@ -106,11 +106,6 @@ static int tegra_update_cpu_speed(struct cpufreq_policy *policy,
        return ret;
 }
 
-static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
-{
-       return tegra_update_cpu_speed(policy, freq_table[index].frequency);
-}
-
 static int tegra_cpu_init(struct cpufreq_policy *policy)
 {
        int ret;
index ae1d78e..b6d69e8 100644 (file)
@@ -18,6 +18,12 @@ config ARM_BIG_LITTLE_CPUIDLE
          define different C-states for little and big cores through the
          multiple CPU idle drivers infrastructure.
 
+config ARM_CLPS711X_CPUIDLE
+       bool "CPU Idle Driver for CLPS711X processors"
+       depends on ARCH_CLPS711X || COMPILE_TEST
+       help
+         Select this to enable cpuidle on Cirrus Logic CLPS711X SOCs.
+
 config ARM_HIGHBANK_CPUIDLE
        bool "CPU Idle Driver for Calxeda processors"
        depends on ARM_PSCI
index cd3ab59..9b5b2b5 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
 # ARM SoC drivers
 obj-$(CONFIG_ARM_ARMADA_370_XP_CPUIDLE) += cpuidle-armada-370-xp.o
 obj-$(CONFIG_ARM_BIG_LITTLE_CPUIDLE)   += cpuidle-big_little.o
+obj-$(CONFIG_ARM_CLPS711X_CPUIDLE)     += cpuidle-clps711x.o
 obj-$(CONFIG_ARM_HIGHBANK_CPUIDLE)     += cpuidle-calxeda.o
 obj-$(CONFIG_ARM_KIRKWOOD_CPUIDLE)     += cpuidle-kirkwood.o
 obj-$(CONFIG_ARM_ZYNQ_CPUIDLE)         += cpuidle-zynq.o
diff --git a/drivers/cpuidle/cpuidle-clps711x.c b/drivers/cpuidle/cpuidle-clps711x.c
new file mode 100644 (file)
index 0000000..5243811
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ *  CLPS711X CPU idle driver
+ *
+ *  Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * 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/cpuidle.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define CLPS711X_CPUIDLE_NAME  "clps711x-cpuidle"
+
+static void __iomem *clps711x_halt;
+
+static int clps711x_cpuidle_halt(struct cpuidle_device *dev,
+                                struct cpuidle_driver *drv, int index)
+{
+       writel(0xaa, clps711x_halt);
+
+       return index;
+}
+
+static struct cpuidle_driver clps711x_idle_driver = {
+       .name           = CLPS711X_CPUIDLE_NAME,
+       .owner          = THIS_MODULE,
+       .states[0]      = {
+               .name           = "HALT",
+               .desc           = "CLPS711X HALT",
+               .enter          = clps711x_cpuidle_halt,
+               .exit_latency   = 1,
+       },
+       .state_count    = 1,
+};
+
+static int __init clps711x_cpuidle_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       clps711x_halt = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(clps711x_halt))
+               return PTR_ERR(clps711x_halt);
+
+       return cpuidle_register(&clps711x_idle_driver, NULL);
+}
+
+static struct platform_driver clps711x_cpuidle_driver = {
+       .driver = {
+               .name   = CLPS711X_CPUIDLE_NAME,
+               .owner  = THIS_MODULE,
+       },
+};
+module_platform_driver_probe(clps711x_cpuidle_driver, clps711x_cpuidle_probe);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("CLPS711X CPU idle driver");
+MODULE_LICENSE("GPL");
index 7d2f435..49e74c1 100644 (file)
@@ -70,19 +70,20 @@ config ARM_EXYNOS4_BUS_DEVFREQ
        depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM
        select ARCH_HAS_OPP
        select DEVFREQ_GOV_SIMPLE_ONDEMAND
+       select PM_OPP
        help
          This adds the DEVFREQ driver for Exynos4210 memory bus (vdd_int)
          and Exynos4212/4412 memory interface and bus (vdd_mif + vdd_int).
          It reads PPMU counters of memory controllers and adjusts
          the operating frequencies and voltages with OPP support.
-         To operate with optimal voltages, ASV support is required
-         (CONFIG_EXYNOS_ASV).
+         This does not yet operate with optimal voltages.
 
 config ARM_EXYNOS5_BUS_DEVFREQ
        bool "ARM Exynos5250 Bus DEVFREQ Driver"
        depends on SOC_EXYNOS5250
        select ARCH_HAS_OPP
        select DEVFREQ_GOV_SIMPLE_ONDEMAND
+       select PM_OPP
        help
          This adds the DEVFREQ driver for Exynos5250 bus interface (vdd_int).
          It reads PPMU counters of memory controllers and adjusts the
index 2042ec3..9f90369 100644 (file)
@@ -394,7 +394,7 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
  * @devfreq:   the devfreq struct
  * @skip:      skip calling device_unregister().
  */
-static void _remove_devfreq(struct devfreq *devfreq, bool skip)
+static void _remove_devfreq(struct devfreq *devfreq)
 {
        mutex_lock(&devfreq_list_lock);
        if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) {
@@ -412,11 +412,6 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip)
        if (devfreq->profile->exit)
                devfreq->profile->exit(devfreq->dev.parent);
 
-       if (!skip && get_device(&devfreq->dev)) {
-               device_unregister(&devfreq->dev);
-               put_device(&devfreq->dev);
-       }
-
        mutex_destroy(&devfreq->lock);
        kfree(devfreq);
 }
@@ -426,14 +421,12 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip)
  * @dev:       the devfreq device
  *
  * This calls _remove_devfreq() if _remove_devfreq() is not called.
- * Note that devfreq_dev_release() could be called by _remove_devfreq() as
- * well as by others unregistering the device.
  */
 static void devfreq_dev_release(struct device *dev)
 {
        struct devfreq *devfreq = to_devfreq(dev);
 
-       _remove_devfreq(devfreq, true);
+       _remove_devfreq(devfreq);
 }
 
 /**
@@ -544,12 +537,76 @@ int devfreq_remove_device(struct devfreq *devfreq)
        if (!devfreq)
                return -EINVAL;
 
-       _remove_devfreq(devfreq, false);
+       device_unregister(&devfreq->dev);
+       put_device(&devfreq->dev);
 
        return 0;
 }
 EXPORT_SYMBOL(devfreq_remove_device);
 
+static int devm_devfreq_dev_match(struct device *dev, void *res, void *data)
+{
+       struct devfreq **r = res;
+
+       if (WARN_ON(!r || !*r))
+               return 0;
+
+       return *r == data;
+}
+
+static void devm_devfreq_dev_release(struct device *dev, void *res)
+{
+       devfreq_remove_device(*(struct devfreq **)res);
+}
+
+/**
+ * devm_devfreq_add_device() - Resource-managed devfreq_add_device()
+ * @dev:       the device to add devfreq feature.
+ * @profile:   device-specific profile to run devfreq.
+ * @governor_name:     name of the policy to choose frequency.
+ * @data:      private data for the governor. The devfreq framework does not
+ *             touch this value.
+ *
+ * This function manages automatically the memory of devfreq device using device
+ * resource management and simplify the free operation for memory of devfreq
+ * device.
+ */
+struct devfreq *devm_devfreq_add_device(struct device *dev,
+                                       struct devfreq_dev_profile *profile,
+                                       const char *governor_name,
+                                       void *data)
+{
+       struct devfreq **ptr, *devfreq;
+
+       ptr = devres_alloc(devm_devfreq_dev_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       devfreq = devfreq_add_device(dev, profile, governor_name, data);
+       if (IS_ERR(devfreq)) {
+               devres_free(ptr);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       *ptr = devfreq;
+       devres_add(dev, ptr);
+
+       return devfreq;
+}
+EXPORT_SYMBOL(devm_devfreq_add_device);
+
+/**
+ * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device()
+ * @dev:       the device to add devfreq feature.
+ * @devfreq:   the devfreq instance to be removed
+ */
+void devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq)
+{
+       WARN_ON(devres_release(dev, devm_devfreq_dev_release,
+                              devm_devfreq_dev_match, devfreq));
+}
+EXPORT_SYMBOL(devm_devfreq_remove_device);
+
 /**
  * devfreq_suspend_device() - Suspend devfreq of a device.
  * @devfreq: the devfreq instance to be suspended
@@ -1112,6 +1169,54 @@ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)
        return ret;
 }
 
+static void devm_devfreq_opp_release(struct device *dev, void *res)
+{
+       devfreq_unregister_opp_notifier(dev, *(struct devfreq **)res);
+}
+
+/**
+ * devm_ devfreq_register_opp_notifier()
+ *             - Resource-managed devfreq_register_opp_notifier()
+ * @dev:       The devfreq user device. (parent of devfreq)
+ * @devfreq:   The devfreq object.
+ */
+int devm_devfreq_register_opp_notifier(struct device *dev,
+                                      struct devfreq *devfreq)
+{
+       struct devfreq **ptr;
+       int ret;
+
+       ptr = devres_alloc(devm_devfreq_opp_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return -ENOMEM;
+
+       ret = devfreq_register_opp_notifier(dev, devfreq);
+       if (ret) {
+               devres_free(ptr);
+               return ret;
+       }
+
+       *ptr = devfreq;
+       devres_add(dev, ptr);
+
+       return 0;
+}
+EXPORT_SYMBOL(devm_devfreq_register_opp_notifier);
+
+/**
+ * devm_devfreq_unregister_opp_notifier()
+ *             - Resource-managed devfreq_unregister_opp_notifier()
+ * @dev:       The devfreq user device. (parent of devfreq)
+ * @devfreq:   The devfreq object.
+ */
+void devm_devfreq_unregister_opp_notifier(struct device *dev,
+                                        struct devfreq *devfreq)
+{
+       WARN_ON(devres_release(dev, devm_devfreq_opp_release,
+                              devm_devfreq_dev_match, devfreq));
+}
+EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier);
+
 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
 MODULE_DESCRIPTION("devfreq class support");
 MODULE_LICENSE("GPL");
index bfaaf5b..49bc917 100644 (file)
@@ -1,3 +1,3 @@
 # Exynos DEVFREQ Drivers
-obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ)  += exynos4_bus.o
+obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ)  += exynos_ppmu.o exynos4_bus.o
 obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ)  += exynos_ppmu.o exynos5_bus.o
index e07b0c6..d9b08d3 100644 (file)
 #include <linux/regulator/consumer.h>
 #include <linux/module.h>
 
-/* Exynos4 ASV has been in the mailing list, but not upstreamed, yet. */
-#ifdef CONFIG_EXYNOS_ASV
-extern unsigned int exynos_result_of_asv;
-#endif
-
 #include <mach/map.h>
 
+#include "exynos_ppmu.h"
 #include "exynos4_bus.h"
 
 #define MAX_SAFEVOLT   1200000 /* 1.2V */
@@ -44,22 +40,6 @@ enum exynos4_busf_type {
 /* Assume that the bus is saturated if the utilization is 40% */
 #define BUS_SATURATION_RATIO   40
 
-enum ppmu_counter {
-       PPMU_PMNCNT0 = 0,
-       PPMU_PMCCNT1,
-       PPMU_PMNCNT2,
-       PPMU_PMNCNT3,
-       PPMU_PMNCNT_MAX,
-};
-struct exynos4_ppmu {
-       void __iomem *hw_base;
-       unsigned int ccnt;
-       unsigned int event;
-       unsigned int count[PPMU_PMNCNT_MAX];
-       bool ccnt_overflow;
-       bool count_overflow[PPMU_PMNCNT_MAX];
-};
-
 enum busclk_level_idx {
        LV_0 = 0,
        LV_1,
@@ -68,6 +48,13 @@ enum busclk_level_idx {
        LV_4,
        _LV_END
 };
+
+enum exynos_ppmu_idx {
+       PPMU_DMC0,
+       PPMU_DMC1,
+       PPMU_END,
+};
+
 #define EX4210_LV_MAX  LV_2
 #define EX4x12_LV_MAX  LV_4
 #define EX4210_LV_NUM  (LV_2 + 1)
@@ -91,7 +78,7 @@ struct busfreq_data {
        struct regulator *vdd_int;
        struct regulator *vdd_mif; /* Exynos4412/4212 only */
        struct busfreq_opp_info curr_oppinfo;
-       struct exynos4_ppmu dmc[2];
+       struct busfreq_ppmu_data ppmu_data;
 
        struct notifier_block pm_notifier;
        struct mutex lock;
@@ -101,12 +88,6 @@ struct busfreq_data {
        unsigned int top_divtable[_LV_END];
 };
 
-struct bus_opp_table {
-       unsigned int idx;
-       unsigned long clk;
-       unsigned long volt;
-};
-
 /* 4210 controls clock of mif and voltage of int */
 static struct bus_opp_table exynos4210_busclk_table[] = {
        {LV_0, 400000, 1150000},
@@ -524,57 +505,6 @@ static int exynos4x12_set_busclk(struct busfreq_data *data,
        return 0;
 }
 
-
-static void busfreq_mon_reset(struct busfreq_data *data)
-{
-       unsigned int i;
-
-       for (i = 0; i < 2; i++) {
-               void __iomem *ppmu_base = data->dmc[i].hw_base;
-
-               /* Reset PPMU */
-               __raw_writel(0x8000000f, ppmu_base + 0xf010);
-               __raw_writel(0x8000000f, ppmu_base + 0xf050);
-               __raw_writel(0x6, ppmu_base + 0xf000);
-               __raw_writel(0x0, ppmu_base + 0xf100);
-
-               /* Set PPMU Event */
-               data->dmc[i].event = 0x6;
-               __raw_writel(((data->dmc[i].event << 12) | 0x1),
-                            ppmu_base + 0xfc);
-
-               /* Start PPMU */
-               __raw_writel(0x1, ppmu_base + 0xf000);
-       }
-}
-
-static void exynos4_read_ppmu(struct busfreq_data *data)
-{
-       int i, j;
-
-       for (i = 0; i < 2; i++) {
-               void __iomem *ppmu_base = data->dmc[i].hw_base;
-               u32 overflow;
-
-               /* Stop PPMU */
-               __raw_writel(0x0, ppmu_base + 0xf000);
-
-               /* Update local data from PPMU */
-               overflow = __raw_readl(ppmu_base + 0xf050);
-
-               data->dmc[i].ccnt = __raw_readl(ppmu_base + 0xf100);
-               data->dmc[i].ccnt_overflow = overflow & (1 << 31);
-
-               for (j = 0; j < PPMU_PMNCNT_MAX; j++) {
-                       data->dmc[i].count[j] = __raw_readl(
-                                       ppmu_base + (0xf110 + (0x10 * j)));
-                       data->dmc[i].count_overflow[j] = overflow & (1 << j);
-               }
-       }
-
-       busfreq_mon_reset(data);
-}
-
 static int exynos4x12_get_intspec(unsigned long mifclk)
 {
        int i = 0;
@@ -698,84 +628,35 @@ out:
        return err;
 }
 
-static int exynos4_get_busier_dmc(struct busfreq_data *data)
-{
-       u64 p0 = data->dmc[0].count[0];
-       u64 p1 = data->dmc[1].count[0];
-
-       p0 *= data->dmc[1].ccnt;
-       p1 *= data->dmc[0].ccnt;
-
-       if (data->dmc[1].ccnt == 0)
-               return 0;
-
-       if (p0 > p1)
-               return 0;
-       return 1;
-}
-
 static int exynos4_bus_get_dev_status(struct device *dev,
                                      struct devfreq_dev_status *stat)
 {
        struct busfreq_data *data = dev_get_drvdata(dev);
-       int busier_dmc;
-       int cycles_x2 = 2; /* 2 x cycles */
-       void __iomem *addr;
-       u32 timing;
-       u32 memctrl;
-
-       exynos4_read_ppmu(data);
-       busier_dmc = exynos4_get_busier_dmc(data);
-       stat->current_frequency = data->curr_oppinfo.rate;
+       struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data;
+       int busier;
 
-       if (busier_dmc)
-               addr = S5P_VA_DMC1;
-       else
-               addr = S5P_VA_DMC0;
-
-       memctrl = __raw_readl(addr + 0x04); /* one of DDR2/3/LPDDR2 */
-       timing = __raw_readl(addr + 0x38); /* CL or WL/RL values */
-
-       switch ((memctrl >> 8) & 0xf) {
-       case 0x4: /* DDR2 */
-               cycles_x2 = ((timing >> 16) & 0xf) * 2;
-               break;
-       case 0x5: /* LPDDR2 */
-       case 0x6: /* DDR3 */
-               cycles_x2 = ((timing >> 8) & 0xf) + ((timing >> 0) & 0xf);
-               break;
-       default:
-               pr_err("%s: Unknown Memory Type(%d).\n", __func__,
-                      (memctrl >> 8) & 0xf);
-               return -EINVAL;
-       }
+       exynos_read_ppmu(ppmu_data);
+       busier = exynos_get_busier_ppmu(ppmu_data);
+       stat->current_frequency = data->curr_oppinfo.rate;
 
        /* Number of cycles spent on memory access */
-       stat->busy_time = data->dmc[busier_dmc].count[0] / 2 * (cycles_x2 + 2);
+       stat->busy_time = ppmu_data->ppmu[busier].count[PPMU_PMNCNT3];
        stat->busy_time *= 100 / BUS_SATURATION_RATIO;
-       stat->total_time = data->dmc[busier_dmc].ccnt;
+       stat->total_time = ppmu_data->ppmu[busier].ccnt;
 
        /* If the counters have overflown, retry */
-       if (data->dmc[busier_dmc].ccnt_overflow ||
-           data->dmc[busier_dmc].count_overflow[0])
+       if (ppmu_data->ppmu[busier].ccnt_overflow ||
+           ppmu_data->ppmu[busier].count_overflow[0])
                return -EAGAIN;
 
        return 0;
 }
 
-static void exynos4_bus_exit(struct device *dev)
-{
-       struct busfreq_data *data = dev_get_drvdata(dev);
-
-       devfreq_unregister_opp_notifier(dev, data->devfreq);
-}
-
 static struct devfreq_dev_profile exynos4_devfreq_profile = {
        .initial_freq   = 400000,
        .polling_ms     = 50,
        .target         = exynos4_bus_target,
        .get_dev_status = exynos4_bus_get_dev_status,
-       .exit           = exynos4_bus_exit,
 };
 
 static int exynos4210_init_tables(struct busfreq_data *data)
@@ -837,11 +718,11 @@ static int exynos4210_init_tables(struct busfreq_data *data)
                data->top_divtable[i] = tmp;
        }
 
-#ifdef CONFIG_EXYNOS_ASV
-       tmp = exynos4_result_of_asv;
-#else
+       /*
+        * TODO: init tmp based on busfreq_data
+        * (device-tree or platform-data)
+        */
        tmp = 0; /* Max voltages for the reliability of the unknown */
-#endif
 
        pr_debug("ASV Group of Exynos4 is %d\n", tmp);
        /* Use merged grouping for voltage */
@@ -922,11 +803,7 @@ static int exynos4x12_init_tables(struct busfreq_data *data)
                data->dmc_divtable[i] = tmp;
        }
 
-#ifdef CONFIG_EXYNOS_ASV
-       tmp = exynos4_result_of_asv;
-#else
        tmp = 0; /* Max voltages for the reliability of the unknown */
-#endif
 
        if (tmp > 8)
                tmp = 0;
@@ -1020,6 +897,7 @@ unlock:
 static int exynos4_busfreq_probe(struct platform_device *pdev)
 {
        struct busfreq_data *data;
+       struct busfreq_ppmu_data *ppmu_data;
        struct dev_pm_opp *opp;
        struct device *dev = &pdev->dev;
        int err = 0;
@@ -1030,9 +908,19 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       ppmu_data = &data->ppmu_data;
+       ppmu_data->ppmu_end = PPMU_END;
+       ppmu_data->ppmu = devm_kzalloc(dev,
+                                      sizeof(struct exynos_ppmu) * PPMU_END,
+                                      GFP_KERNEL);
+       if (!ppmu_data->ppmu) {
+               dev_err(dev, "Failed to allocate memory for exynos_ppmu\n");
+               return -ENOMEM;
+       }
+
        data->type = pdev->id_entry->driver_data;
-       data->dmc[0].hw_base = S5P_VA_DMC0;
-       data->dmc[1].hw_base = S5P_VA_DMC1;
+       ppmu_data->ppmu[PPMU_DMC0].hw_base = S5P_VA_DMC0;
+       ppmu_data->ppmu[PPMU_DMC1].hw_base = S5P_VA_DMC1;
        data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event;
        data->dev = dev;
        mutex_init(&data->lock);
@@ -1048,8 +936,11 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)
                dev_err(dev, "Cannot determine the device id %d\n", data->type);
                err = -EINVAL;
        }
-       if (err)
+       if (err) {
+               dev_err(dev, "Cannot initialize busfreq table %d\n",
+                            data->type);
                return err;
+       }
 
        data->vdd_int = devm_regulator_get(dev, "vdd_int");
        if (IS_ERR(data->vdd_int)) {
@@ -1079,19 +970,28 @@ static int exynos4_busfreq_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, data);
 
-       busfreq_mon_reset(data);
-
-       data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile,
+       data->devfreq = devm_devfreq_add_device(dev, &exynos4_devfreq_profile,
                                           "simple_ondemand", NULL);
        if (IS_ERR(data->devfreq))
                return PTR_ERR(data->devfreq);
 
-       devfreq_register_opp_notifier(dev, data->devfreq);
+       /*
+        * Start PPMU (Performance Profiling Monitoring Unit) to check
+        * utilization of each IP in the Exynos4 SoC.
+        */
+       busfreq_mon_reset(ppmu_data);
 
+       /* Register opp_notifier for Exynos4 busfreq */
+       err = devm_devfreq_register_opp_notifier(dev, data->devfreq);
+       if (err < 0) {
+               dev_err(dev, "Failed to register opp notifier\n");
+               return err;
+       }
+
+       /* Register pm_notifier for Exynos4 busfreq */
        err = register_pm_notifier(&data->pm_notifier);
        if (err) {
                dev_err(dev, "Failed to setup pm notifier\n");
-               devfreq_remove_device(data->devfreq);
                return err;
        }
 
@@ -1102,23 +1002,24 @@ static int exynos4_busfreq_remove(struct platform_device *pdev)
 {
        struct busfreq_data *data = platform_get_drvdata(pdev);
 
+       /* Unregister all of notifier chain */
        unregister_pm_notifier(&data->pm_notifier);
-       devfreq_remove_device(data->devfreq);
 
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
 static int exynos4_busfreq_resume(struct device *dev)
 {
        struct busfreq_data *data = dev_get_drvdata(dev);
+       struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data;
 
-       busfreq_mon_reset(data);
+       busfreq_mon_reset(ppmu_data);
        return 0;
 }
+#endif
 
-static const struct dev_pm_ops exynos4_busfreq_pm = {
-       .resume = exynos4_busfreq_resume,
-};
+static SIMPLE_DEV_PM_OPS(exynos4_busfreq_pm_ops, NULL, exynos4_busfreq_resume);
 
 static const struct platform_device_id exynos4_busfreq_id[] = {
        { "exynos4210-busfreq", TYPE_BUSF_EXYNOS4210 },
@@ -1134,7 +1035,7 @@ static struct platform_driver exynos4_busfreq_driver = {
        .driver = {
                .name   = "exynos4-busfreq",
                .owner  = THIS_MODULE,
-               .pm     = &exynos4_busfreq_pm,
+               .pm     = &exynos4_busfreq_pm_ops,
        },
 };
 
index 6eef1f7..6cd0392 100644 (file)
@@ -50,7 +50,7 @@ struct busfreq_data_int {
        struct device *dev;
        struct devfreq *devfreq;
        struct regulator *vdd_int;
-       struct exynos_ppmu ppmu[PPMU_END];
+       struct busfreq_ppmu_data ppmu_data;
        unsigned long curr_freq;
        bool disabled;
 
@@ -75,49 +75,6 @@ static struct int_bus_opp_table exynos5_int_opp_table[] = {
        {0, 0, 0},
 };
 
-static void busfreq_mon_reset(struct busfreq_data_int *data)
-{
-       unsigned int i;
-
-       for (i = PPMU_RIGHT; i < PPMU_END; i++) {
-               void __iomem *ppmu_base = data->ppmu[i].hw_base;
-
-               /* Reset the performance and cycle counters */
-               exynos_ppmu_reset(ppmu_base);
-
-               /* Setup count registers to monitor read/write transactions */
-               data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT;
-               exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3,
-                                       data->ppmu[i].event[PPMU_PMNCNT3]);
-
-               exynos_ppmu_start(ppmu_base);
-       }
-}
-
-static void exynos5_read_ppmu(struct busfreq_data_int *data)
-{
-       int i, j;
-
-       for (i = PPMU_RIGHT; i < PPMU_END; i++) {
-               void __iomem *ppmu_base = data->ppmu[i].hw_base;
-
-               exynos_ppmu_stop(ppmu_base);
-
-               /* Update local data from PPMU */
-               data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT);
-
-               for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) {
-                       if (data->ppmu[i].event[j] == 0)
-                               data->ppmu[i].count[j] = 0;
-                       else
-                               data->ppmu[i].count[j] =
-                                       exynos_ppmu_read(ppmu_base, j);
-               }
-       }
-
-       busfreq_mon_reset(data);
-}
-
 static int exynos5_int_setvolt(struct busfreq_data_int *data,
                                unsigned long volt)
 {
@@ -185,59 +142,33 @@ out:
        return err;
 }
 
-static int exynos5_get_busier_dmc(struct busfreq_data_int *data)
-{
-       int i, j;
-       int busy = 0;
-       unsigned int temp = 0;
-
-       for (i = PPMU_RIGHT; i < PPMU_END; i++) {
-               for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) {
-                       if (data->ppmu[i].count[j] > temp) {
-                               temp = data->ppmu[i].count[j];
-                               busy = i;
-                       }
-               }
-       }
-
-       return busy;
-}
-
 static int exynos5_int_get_dev_status(struct device *dev,
                                      struct devfreq_dev_status *stat)
 {
        struct platform_device *pdev = container_of(dev, struct platform_device,
                                                    dev);
        struct busfreq_data_int *data = platform_get_drvdata(pdev);
+       struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data;
        int busier_dmc;
 
-       exynos5_read_ppmu(data);
-       busier_dmc = exynos5_get_busier_dmc(data);
+       exynos_read_ppmu(ppmu_data);
+       busier_dmc = exynos_get_busier_ppmu(ppmu_data);
 
        stat->current_frequency = data->curr_freq;
 
        /* Number of cycles spent on memory access */
-       stat->busy_time = data->ppmu[busier_dmc].count[PPMU_PMNCNT3];
+       stat->busy_time = ppmu_data->ppmu[busier_dmc].count[PPMU_PMNCNT3];
        stat->busy_time *= 100 / INT_BUS_SATURATION_RATIO;
-       stat->total_time = data->ppmu[busier_dmc].ccnt;
+       stat->total_time = ppmu_data->ppmu[busier_dmc].ccnt;
 
        return 0;
 }
-static void exynos5_int_exit(struct device *dev)
-{
-       struct platform_device *pdev = container_of(dev, struct platform_device,
-                                                   dev);
-       struct busfreq_data_int *data = platform_get_drvdata(pdev);
-
-       devfreq_unregister_opp_notifier(dev, data->devfreq);
-}
 
 static struct devfreq_dev_profile exynos5_devfreq_int_profile = {
        .initial_freq           = 160000,
        .polling_ms             = 100,
        .target                 = exynos5_busfreq_int_target,
        .get_dev_status         = exynos5_int_get_dev_status,
-       .exit                   = exynos5_int_exit,
 };
 
 static int exynos5250_init_int_tables(struct busfreq_data_int *data)
@@ -315,6 +246,7 @@ unlock:
 static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 {
        struct busfreq_data_int *data;
+       struct busfreq_ppmu_data *ppmu_data;
        struct dev_pm_opp *opp;
        struct device *dev = &pdev->dev;
        struct device_node *np;
@@ -330,16 +262,26 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       ppmu_data = &data->ppmu_data;
+       ppmu_data->ppmu_end = PPMU_END;
+       ppmu_data->ppmu = devm_kzalloc(dev,
+                                      sizeof(struct exynos_ppmu) * PPMU_END,
+                                      GFP_KERNEL);
+       if (!ppmu_data->ppmu) {
+               dev_err(dev, "Failed to allocate memory for exynos_ppmu\n");
+               return -ENOMEM;
+       }
+
        np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu");
        if (np == NULL) {
                pr_err("Unable to find PPMU node\n");
                return -ENOENT;
        }
 
-       for (i = PPMU_RIGHT; i < PPMU_END; i++) {
+       for (i = 0; i < ppmu_data->ppmu_end; i++) {
                /* map PPMU memory region */
-               data->ppmu[i].hw_base = of_iomap(np, i);
-               if (data->ppmu[i].hw_base == NULL) {
+               ppmu_data->ppmu[i].hw_base = of_iomap(np, i);
+               if (ppmu_data->ppmu[i].hw_base == NULL) {
                        dev_err(&pdev->dev, "failed to map memory region\n");
                        return -ENOMEM;
                }
@@ -390,32 +332,29 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, data);
 
-       busfreq_mon_reset(data);
+       busfreq_mon_reset(ppmu_data);
 
-       data->devfreq = devfreq_add_device(dev, &exynos5_devfreq_int_profile,
+       data->devfreq = devm_devfreq_add_device(dev, &exynos5_devfreq_int_profile,
                                           "simple_ondemand", NULL);
+       if (IS_ERR(data->devfreq))
+               return PTR_ERR(data->devfreq);
 
-       if (IS_ERR(data->devfreq)) {
-               err = PTR_ERR(data->devfreq);
-               goto err_devfreq_add;
+       err = devm_devfreq_register_opp_notifier(dev, data->devfreq);
+       if (err < 0) {
+               dev_err(dev, "Failed to register opp notifier\n");
+               return err;
        }
 
-       devfreq_register_opp_notifier(dev, data->devfreq);
-
        err = register_pm_notifier(&data->pm_notifier);
        if (err) {
                dev_err(dev, "Failed to setup pm notifier\n");
-               goto err_devfreq_add;
+               return err;
        }
 
        /* TODO: Add a new QOS class for int/mif bus */
        pm_qos_add_request(&data->int_req, PM_QOS_NETWORK_THROUGHPUT, -1);
 
        return 0;
-
-err_devfreq_add:
-       devfreq_remove_device(data->devfreq);
-       return err;
 }
 
 static int exynos5_busfreq_int_remove(struct platform_device *pdev)
@@ -424,24 +363,27 @@ static int exynos5_busfreq_int_remove(struct platform_device *pdev)
 
        pm_qos_remove_request(&data->int_req);
        unregister_pm_notifier(&data->pm_notifier);
-       devfreq_remove_device(data->devfreq);
 
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
 static int exynos5_busfreq_int_resume(struct device *dev)
 {
        struct platform_device *pdev = container_of(dev, struct platform_device,
                                                    dev);
        struct busfreq_data_int *data = platform_get_drvdata(pdev);
+       struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data;
 
-       busfreq_mon_reset(data);
+       busfreq_mon_reset(ppmu_data);
        return 0;
 }
-
 static const struct dev_pm_ops exynos5_busfreq_int_pm = {
        .resume = exynos5_busfreq_int_resume,
 };
+#endif
+static SIMPLE_DEV_PM_OPS(exynos5_busfreq_int_pm_ops, NULL,
+                        exynos5_busfreq_int_resume);
 
 /* platform device pointer for exynos5 devfreq device. */
 static struct platform_device *exynos5_devfreq_pdev;
@@ -452,7 +394,7 @@ static struct platform_driver exynos5_busfreq_int_driver = {
        .driver         = {
                .name           = "exynos5-bus-int",
                .owner          = THIS_MODULE,
-               .pm             = &exynos5_busfreq_int_pm,
+               .pm             = &exynos5_busfreq_int_pm_ops,
        },
 };
 
index 85fc5ac..75fcc51 100644 (file)
@@ -54,3 +54,63 @@ unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch)
 
        return total;
 }
+
+void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data)
+{
+       unsigned int i;
+
+       for (i = 0; i < ppmu_data->ppmu_end; i++) {
+               void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base;
+
+               /* Reset the performance and cycle counters */
+               exynos_ppmu_reset(ppmu_base);
+
+               /* Setup count registers to monitor read/write transactions */
+               ppmu_data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT;
+               exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3,
+                                       ppmu_data->ppmu[i].event[PPMU_PMNCNT3]);
+
+               exynos_ppmu_start(ppmu_base);
+       }
+}
+
+void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data)
+{
+       int i, j;
+
+       for (i = 0; i < ppmu_data->ppmu_end; i++) {
+               void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base;
+
+               exynos_ppmu_stop(ppmu_base);
+
+               /* Update local data from PPMU */
+               ppmu_data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT);
+
+               for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) {
+                       if (ppmu_data->ppmu[i].event[j] == 0)
+                               ppmu_data->ppmu[i].count[j] = 0;
+                       else
+                               ppmu_data->ppmu[i].count[j] =
+                                       exynos_ppmu_read(ppmu_base, j);
+               }
+       }
+
+       busfreq_mon_reset(ppmu_data);
+}
+
+int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data)
+{
+       unsigned int count = 0;
+       int i, j, busy = 0;
+
+       for (i = 0; i < ppmu_data->ppmu_end; i++) {
+               for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) {
+                       if (ppmu_data->ppmu[i].count[j] > count) {
+                               count = ppmu_data->ppmu[i].count[j];
+                               busy = i;
+                       }
+               }
+       }
+
+       return busy;
+}
index 7dfb221..71f17ba 100644 (file)
@@ -69,10 +69,18 @@ struct exynos_ppmu {
        bool count_overflow[PPMU_PMNCNT_MAX];
 };
 
+struct busfreq_ppmu_data {
+       struct exynos_ppmu *ppmu;
+       int ppmu_end;
+};
+
 void exynos_ppmu_reset(void __iomem *ppmu_base);
 void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch,
                        unsigned int evt);
 void exynos_ppmu_start(void __iomem *ppmu_base);
 void exynos_ppmu_stop(void __iomem *ppmu_base);
 unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch);
+void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data);
+void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data);
+int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data);
 #endif /* __DEVFREQ_EXYNOS_PPMU_H */
index 7a701a5..4199849 100644 (file)
@@ -1,5 +1,5 @@
 menu "IEEE 1394 (FireWire) support"
-       depends on PCI || BROKEN
+       depends on PCI || COMPILE_TEST
        # firewire-core does not depend on PCI but is
        # not useful without PCI controller driver
 
index f477308..e1480ff 100644 (file)
@@ -118,7 +118,6 @@ int fw_card_add(struct fw_card *card,
                u32 max_receive, u32 link_speed, u64 guid);
 void fw_core_remove_card(struct fw_card *card);
 int fw_compute_block_crc(__be32 *block);
-void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset);
 void fw_schedule_bm_work(struct fw_card *card, unsigned long delay);
 
 /* -cdev */
index 4af0a7b..c398645 100644 (file)
@@ -1462,8 +1462,8 @@ static int fwnet_probe(struct fw_unit *unit,
 
        net = alloc_netdev(sizeof(*dev), "firewire%d", fwnet_init_dev);
        if (net == NULL) {
-               ret = -ENOMEM;
-               goto out;
+               mutex_unlock(&fwnet_device_mutex);
+               return -ENOMEM;
        }
 
        allocated_netdev = true;
index ce7a581..5798541 100644 (file)
@@ -282,6 +282,7 @@ static char ohci_driver_name[] = KBUILD_MODNAME;
 #define PCI_DEVICE_ID_TI_TSB82AA2      0x8025
 #define PCI_DEVICE_ID_VIA_VT630X       0x3044
 #define PCI_REV_ID_VIA_VT6306          0x46
+#define PCI_DEVICE_ID_VIA_VT6315       0x3403
 
 #define QUIRK_CYCLE_TIMER              0x1
 #define QUIRK_RESET_PACKET             0x2
@@ -334,6 +335,12 @@ static const struct {
        {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT630X, PCI_REV_ID_VIA_VT6306,
                QUIRK_CYCLE_TIMER | QUIRK_IR_WAKE},
 
+       {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT6315, 0,
+               QUIRK_CYCLE_TIMER | QUIRK_NO_MSI},
+
+       {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT6315, PCI_ANY_ID,
+               0},
+
        {PCI_VENDOR_ID_VIA, PCI_ANY_ID, PCI_ANY_ID,
                QUIRK_CYCLE_TIMER | QUIRK_NO_MSI},
 };
index 09312b8..3d78144 100644 (file)
@@ -284,7 +284,7 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id)
        /*
         * This case is search ipp driver by prop_id handle.
         * sometimes, ipp subsystem find driver by prop_id.
-        * e.g PAUSE state, queue buf, command contro.
+        * e.g PAUSE state, queue buf, command control.
         */
        list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
                DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", count++, (int)ippdrv);
index 630f6e8..2c1e4aa 100644 (file)
@@ -31,7 +31,6 @@
  */
 
 #include <linux/backlight.h>
-#include <linux/acpi.h>
 
 #include "nouveau_drm.h"
 #include "nouveau_reg.h"
@@ -222,14 +221,6 @@ nouveau_backlight_init(struct drm_device *dev)
        struct nouveau_device *device = nv_device(drm->device);
        struct drm_connector *connector;
 
-#ifdef CONFIG_ACPI
-       if (acpi_video_backlight_support()) {
-               NV_INFO(drm, "ACPI backlight interface available, "
-                            "not registering our own\n");
-               return 0;
-       }
-#endif
-
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
                    connector->connector_type != DRM_MODE_CONNECTOR_eDP)
index 7af9d0b..800c8b6 100644 (file)
@@ -608,7 +608,10 @@ config HID_SAITEK
        Support for Saitek devices that are not fully compliant with the
        HID standard.
 
-       Currently only supports the PS1000 controller.
+       Supported devices:
+       - PS1000 Dual Analog Pad
+       - R.A.T.7 Gaming Mouse
+       - M.M.O.7 Gaming Mouse
 
 config HID_SAMSUNG
        tristate "Samsung InfraRed remote control or keyboards"
@@ -657,6 +660,14 @@ config HID_SUNPLUS
        ---help---
        Support for Sunplus wireless desktop.
 
+config HID_RMI
+       tristate "Synaptics RMI4 device support"
+       depends on HID
+       ---help---
+       Support for Synaptics RMI4 touchpads.
+       Say Y here if you have a Synaptics RMI4 touchpads over i2c-hid or usbhid
+       and want support for its special functionalities.
+
 config HID_GREENASIA
        tristate "GreenAsia (Product ID 0x12) game controller support"
        depends on HID
index fc712dd..a6fa6ba 100644 (file)
@@ -97,6 +97,7 @@ obj-$(CONFIG_HID_ROCCAT)      += hid-roccat.o hid-roccat-common.o \
        hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
        hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \
        hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-ryos.o hid-roccat-savu.o
+obj-$(CONFIG_HID_RMI)          += hid-rmi.o
 obj-$(CONFIG_HID_SAITEK)       += hid-saitek.o
 obj-$(CONFIG_HID_SAMSUNG)      += hid-samsung.o
 obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
index da52279..8ed66fd 100644 (file)
@@ -779,6 +779,14 @@ static int hid_scan_report(struct hid_device *hid)
            (hid->group == HID_GROUP_MULTITOUCH))
                hid->group = HID_GROUP_MULTITOUCH_WIN_8;
 
+       /*
+       * Vendor specific handlings
+       */
+       if ((hid->vendor == USB_VENDOR_ID_SYNAPTICS) &&
+           (hid->group == HID_GROUP_GENERIC))
+               /* hid-rmi should take care of them, not hid-generic */
+               hid->group = HID_GROUP_RMI;
+
        vfree(parser);
        return 0;
 }
@@ -842,7 +850,17 @@ struct hid_report *hid_validate_values(struct hid_device *hid,
         * ->numbered being checked, which may not always be the case when
         * drivers go to access report values.
         */
-       report = hid->report_enum[type].report_id_hash[id];
+       if (id == 0) {
+               /*
+                * Validating on id 0 means we should examine the first
+                * report in the list.
+                */
+               report = list_entry(
+                               hid->report_enum[type].report_list.next,
+                               struct hid_report, list);
+       } else {
+               report = hid->report_enum[type].report_id_hash[id];
+       }
        if (!report) {
                hid_err(hid, "missing %s %u\n", hid_report_names[type], id);
                return NULL;
@@ -1868,7 +1886,11 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
 #endif
+#if IS_ENABLED(CONFIG_HID_SAITEK)
        { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) },
+#endif
        { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
index 53b771d..84c3cb1 100644 (file)
@@ -165,6 +165,8 @@ static const struct hid_usage_entry hid_usage_table[] = {
     {0, 0x53, "DeviceIndex"},
     {0, 0x54, "ContactCount"},
     {0, 0x55, "ContactMaximumNumber"},
+    {0, 0x5A, "SecondaryBarrelSwitch"},
+    {0, 0x5B, "TransducerSerialNumber"},
   { 15, 0, "PhysicalInterfaceDevice" },
     {0, 0x00, "Undefined"},
     {0, 0x01, "Physical_Interface_Device"},
@@ -272,6 +274,85 @@ static const struct hid_usage_entry hid_usage_table[] = {
     {0, 0xAA, "Shared_Parameter_Blocks"},
     {0, 0xAB, "Create_New_Effect_Report"},
     {0, 0xAC, "RAM_Pool_Available"},
+  {  0x20, 0, "Sensor" },
+    { 0x20, 0x01, "Sensor" },
+    { 0x20, 0x10, "Biometric" },
+      { 0x20, 0x11, "BiometricHumanPresence" },
+      { 0x20, 0x12, "BiometricHumanProximity" },
+      { 0x20, 0x13, "BiometricHumanTouch" },
+    { 0x20, 0x20, "Electrical" },
+      { 0x20, 0x21, "ElectricalCapacitance" },
+      { 0x20, 0x22, "ElectricalCurrent" },
+      { 0x20, 0x23, "ElectricalPower" },
+      { 0x20, 0x24, "ElectricalInductance" },
+      { 0x20, 0x25, "ElectricalResistance" },
+      { 0x20, 0x26, "ElectricalVoltage" },
+      { 0x20, 0x27, "ElectricalPoteniometer" },
+      { 0x20, 0x28, "ElectricalFrequency" },
+      { 0x20, 0x29, "ElectricalPeriod" },
+    { 0x20, 0x30, "Environmental" },
+      { 0x20, 0x31, "EnvironmentalAtmosphericPressure" },
+      { 0x20, 0x32, "EnvironmentalHumidity" },
+      { 0x20, 0x33, "EnvironmentalTemperature" },
+      { 0x20, 0x34, "EnvironmentalWindDirection" },
+      { 0x20, 0x35, "EnvironmentalWindSpeed" },
+    { 0x20, 0x40, "Light" },
+      { 0x20, 0x41, "LightAmbientLight" },
+      { 0x20, 0x42, "LightConsumerInfrared" },
+    { 0x20, 0x50, "Location" },
+      { 0x20, 0x51, "LocationBroadcast" },
+      { 0x20, 0x52, "LocationDeadReckoning" },
+      { 0x20, 0x53, "LocationGPS" },
+      { 0x20, 0x54, "LocationLookup" },
+      { 0x20, 0x55, "LocationOther" },
+      { 0x20, 0x56, "LocationStatic" },
+      { 0x20, 0x57, "LocationTriangulation" },
+    { 0x20, 0x60, "Mechanical" },
+      { 0x20, 0x61, "MechanicalBooleanSwitch" },
+      { 0x20, 0x62, "MechanicalBooleanSwitchArray" },
+      { 0x20, 0x63, "MechanicalMultivalueSwitch" },
+      { 0x20, 0x64, "MechanicalForce" },
+      { 0x20, 0x65, "MechanicalPressure" },
+      { 0x20, 0x66, "MechanicalStrain" },
+      { 0x20, 0x67, "MechanicalWeight" },
+      { 0x20, 0x68, "MechanicalHapticVibrator" },
+      { 0x20, 0x69, "MechanicalHallEffectSwitch" },
+    { 0x20, 0x70, "Motion" },
+      { 0x20, 0x71, "MotionAccelerometer1D" },
+      { 0x20, 0x72, "MotionAccelerometer2D" },
+      { 0x20, 0x73, "MotionAccelerometer3D" },
+      { 0x20, 0x74, "MotionGyrometer1D" },
+      { 0x20, 0x75, "MotionGyrometer2D" },
+      { 0x20, 0x76, "MotionGyrometer3D" },
+      { 0x20, 0x77, "MotionMotionDetector" },
+      { 0x20, 0x78, "MotionSpeedometer" },
+      { 0x20, 0x79, "MotionAccelerometer" },
+      { 0x20, 0x7A, "MotionGyrometer" },
+    { 0x20, 0x80, "Orientation" },
+      { 0x20, 0x81, "OrientationCompass1D" },
+      { 0x20, 0x82, "OrientationCompass2D" },
+      { 0x20, 0x83, "OrientationCompass3D" },
+      { 0x20, 0x84, "OrientationInclinometer1D" },
+      { 0x20, 0x85, "OrientationInclinometer2D" },
+      { 0x20, 0x86, "OrientationInclinometer3D" },
+      { 0x20, 0x87, "OrientationDistance1D" },
+      { 0x20, 0x88, "OrientationDistance2D" },
+      { 0x20, 0x89, "OrientationDistance3D" },
+      { 0x20, 0x8A, "OrientationDeviceOrientation" },
+      { 0x20, 0x8B, "OrientationCompass" },
+      { 0x20, 0x8C, "OrientationInclinometer" },
+      { 0x20, 0x8D, "OrientationDistance" },
+    { 0x20, 0x90, "Scanner" },
+      { 0x20, 0x91, "ScannerBarcode" },
+      { 0x20, 0x91, "ScannerRFID" },
+      { 0x20, 0x91, "ScannerNFC" },
+    { 0x20, 0xA0, "Time" },
+      { 0x20, 0xA1, "TimeAlarmTimer" },
+      { 0x20, 0xA2, "TimeRealTimeClock" },
+    { 0x20, 0xE0, "Other" },
+      { 0x20, 0xE1, "OtherCustom" },
+      { 0x20, 0xE2, "OtherGeneric" },
+      { 0x20, 0xE3, "OtherGenericEnumerator" },
   { 0x84, 0, "Power Device" },
     { 0x84, 0x02, "PresentStatus" },
     { 0x84, 0x03, "ChangeStatus" },
@@ -855,6 +936,16 @@ static const char *keys[KEY_MAX + 1] = {
        [KEY_KBDILLUMDOWN] = "KbdIlluminationDown",
        [KEY_KBDILLUMUP] = "KbdIlluminationUp",
        [KEY_SWITCHVIDEOMODE] = "SwitchVideoMode",
+       [KEY_BUTTONCONFIG] = "ButtonConfig",
+       [KEY_TASKMANAGER] = "TaskManager",
+       [KEY_JOURNAL] = "Journal",
+       [KEY_CONTROLPANEL] = "ControlPanel",
+       [KEY_APPSELECT] = "AppSelect",
+       [KEY_SCREENSAVER] = "ScreenSaver",
+       [KEY_VOICECOMMAND] = "VoiceCommand",
+       [KEY_BRIGHTNESS_MIN] = "BrightnessMin",
+       [KEY_BRIGHTNESS_MAX] = "BrightnessMax",
+       [KEY_BRIGHTNESS_AUTO] = "BrightnessAuto",
 };
 
 static const char *relatives[REL_MAX + 1] = {
index 34bb220..6d00bb9 100644 (file)
 
 #define USB_VENDOR_ID_STM_0             0x0483
 #define USB_DEVICE_ID_STM_HID_SENSOR    0x91d1
+#define USB_DEVICE_ID_STM_HID_SENSOR_1  0x9100
 
 #define USB_VENDOR_ID_ION              0x15e4
 #define USB_DEVICE_ID_ICADE            0x0132
 #define USB_DEVICE_ID_MS_PRESENTER_8K_USB      0x0713
 #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K      0x0730
 #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500    0x076c
+#define USB_DEVICE_ID_MS_SURFACE_PRO_2   0x0799
+#define USB_DEVICE_ID_MS_TOUCH_COVER_2   0x07a7
+#define USB_DEVICE_ID_MS_TYPE_COVER_2    0x07a9
 
 #define USB_VENDOR_ID_MOJO             0x8282
 #define USB_DEVICE_ID_RETRO_ADAPTER    0x3201
 #define USB_VENDOR_ID_SAITEK           0x06a3
 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
 #define USB_DEVICE_ID_SAITEK_PS1000    0x0621
+#define USB_DEVICE_ID_SAITEK_RAT7      0x0cd7
+#define USB_DEVICE_ID_SAITEK_MMO7      0x0cd0
 
 #define USB_VENDOR_ID_SAMSUNG          0x0419
 #define USB_DEVICE_ID_SAMSUNG_IR_REMOTE        0x0001
 #define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE       0x0600
 
+#define USB_VENDOR_ID_SEMICO                   0x1a2c
+#define USB_DEVICE_ID_SEMICO_USB_KEYKOARD      0x0023
+
 #define USB_VENDOR_ID_SENNHEISER       0x1395
 #define USB_DEVICE_ID_SENNHEISER_BTD500USB     0x002c
 
index e7e8b19..2619f7f 100644 (file)
@@ -684,9 +684,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                        break;
 
                case 0x46: /* TabletPick */
+               case 0x5a: /* SecondaryBarrelSwitch */
                        map_key_clear(BTN_STYLUS2);
                        break;
 
+               case 0x5b: /* TransducerSerialNumber */
+                       set_bit(MSC_SERIAL, input->mscbit);
+                       break;
+
                default:  goto unknown;
                }
                break;
@@ -721,6 +726,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x06c: map_key_clear(KEY_YELLOW);          break;
                case 0x06d: map_key_clear(KEY_ZOOM);            break;
 
+               case 0x06f: map_key_clear(KEY_BRIGHTNESSUP);            break;
+               case 0x070: map_key_clear(KEY_BRIGHTNESSDOWN);          break;
+               case 0x072: map_key_clear(KEY_BRIGHTNESS_TOGGLE);       break;
+               case 0x073: map_key_clear(KEY_BRIGHTNESS_MIN);          break;
+               case 0x074: map_key_clear(KEY_BRIGHTNESS_MAX);          break;
+               case 0x075: map_key_clear(KEY_BRIGHTNESS_AUTO);         break;
+
                case 0x082: map_key_clear(KEY_VIDEO_NEXT);      break;
                case 0x083: map_key_clear(KEY_LAST);            break;
                case 0x084: map_key_clear(KEY_ENTER);           break;
@@ -761,6 +773,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x0bf: map_key_clear(KEY_SLOW);            break;
 
                case 0x0cd: map_key_clear(KEY_PLAYPAUSE);       break;
+               case 0x0cf: map_key_clear(KEY_VOICECOMMAND);    break;
                case 0x0e0: map_abs_clear(ABS_VOLUME);          break;
                case 0x0e2: map_key_clear(KEY_MUTE);            break;
                case 0x0e5: map_key_clear(KEY_BASSBOOST);       break;
@@ -768,6 +781,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x0ea: map_key_clear(KEY_VOLUMEDOWN);      break;
                case 0x0f5: map_key_clear(KEY_SLOW);            break;
 
+               case 0x181: map_key_clear(KEY_BUTTONCONFIG);    break;
                case 0x182: map_key_clear(KEY_BOOKMARKS);       break;
                case 0x183: map_key_clear(KEY_CONFIG);          break;
                case 0x184: map_key_clear(KEY_WORDPROCESSOR);   break;
@@ -781,6 +795,8 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x18c: map_key_clear(KEY_VOICEMAIL);       break;
                case 0x18d: map_key_clear(KEY_ADDRESSBOOK);     break;
                case 0x18e: map_key_clear(KEY_CALENDAR);        break;
+               case 0x18f: map_key_clear(KEY_TASKMANAGER);     break;
+               case 0x190: map_key_clear(KEY_JOURNAL);         break;
                case 0x191: map_key_clear(KEY_FINANCE);         break;
                case 0x192: map_key_clear(KEY_CALC);            break;
                case 0x193: map_key_clear(KEY_PLAYER);          break;
@@ -789,12 +805,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x199: map_key_clear(KEY_CHAT);            break;
                case 0x19c: map_key_clear(KEY_LOGOFF);          break;
                case 0x19e: map_key_clear(KEY_COFFEE);          break;
+               case 0x19f: map_key_clear(KEY_CONTROLPANEL);            break;
+               case 0x1a2: map_key_clear(KEY_APPSELECT);               break;
                case 0x1a3: map_key_clear(KEY_NEXT);            break;
                case 0x1a4: map_key_clear(KEY_PREVIOUS);        break;
                case 0x1a6: map_key_clear(KEY_HELP);            break;
                case 0x1a7: map_key_clear(KEY_DOCUMENTS);       break;
                case 0x1ab: map_key_clear(KEY_SPELLCHECK);      break;
                case 0x1ae: map_key_clear(KEY_KEYBOARD);        break;
+               case 0x1b1: map_key_clear(KEY_SCREENSAVER);             break;
                case 0x1b4: map_key_clear(KEY_FILE);            break;
                case 0x1b6: map_key_clear(KEY_IMAGES);          break;
                case 0x1b7: map_key_clear(KEY_AUDIO);           break;
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
new file mode 100644 (file)
index 0000000..2451c7e
--- /dev/null
@@ -0,0 +1,920 @@
+/*
+ *  Copyright (c) 2013 Andrew Duggan <aduggan@synaptics.com>
+ *  Copyright (c) 2013 Synaptics Incorporated
+ *  Copyright (c) 2014 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ *  Copyright (c) 2014 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include "hid-ids.h"
+
+#define RMI_MOUSE_REPORT_ID            0x01 /* Mouse emulation Report */
+#define RMI_WRITE_REPORT_ID            0x09 /* Output Report */
+#define RMI_READ_ADDR_REPORT_ID                0x0a /* Output Report */
+#define RMI_READ_DATA_REPORT_ID                0x0b /* Input Report */
+#define RMI_ATTN_REPORT_ID             0x0c /* Input Report */
+#define RMI_SET_RMI_MODE_REPORT_ID     0x0f /* Feature Report */
+
+/* flags */
+#define RMI_READ_REQUEST_PENDING       BIT(0)
+#define RMI_READ_DATA_PENDING          BIT(1)
+#define RMI_STARTED                    BIT(2)
+
+enum rmi_mode_type {
+       RMI_MODE_OFF                    = 0,
+       RMI_MODE_ATTN_REPORTS           = 1,
+       RMI_MODE_NO_PACKED_ATTN_REPORTS = 2,
+};
+
+struct rmi_function {
+       unsigned page;                  /* page of the function */
+       u16 query_base_addr;            /* base address for queries */
+       u16 command_base_addr;          /* base address for commands */
+       u16 control_base_addr;          /* base address for controls */
+       u16 data_base_addr;             /* base address for datas */
+       unsigned int interrupt_base;    /* cross-function interrupt number
+                                        * (uniq in the device)*/
+       unsigned int interrupt_count;   /* number of interrupts */
+       unsigned int report_size;       /* size of a report */
+       unsigned long irq_mask;         /* mask of the interrupts
+                                        * (to be applied against ATTN IRQ) */
+};
+
+/**
+ * struct rmi_data - stores information for hid communication
+ *
+ * @page_mutex: Locks current page to avoid changing pages in unexpected ways.
+ * @page: Keeps track of the current virtual page
+ *
+ * @wait: Used for waiting for read data
+ *
+ * @writeReport: output buffer when writing RMI registers
+ * @readReport: input buffer when reading RMI registers
+ *
+ * @input_report_size: size of an input report (advertised by HID)
+ * @output_report_size: size of an output report (advertised by HID)
+ *
+ * @flags: flags for the current device (started, reading, etc...)
+ *
+ * @f11: placeholder of internal RMI function F11 description
+ * @f30: placeholder of internal RMI function F30 description
+ *
+ * @max_fingers: maximum finger count reported by the device
+ * @max_x: maximum x value reported by the device
+ * @max_y: maximum y value reported by the device
+ *
+ * @gpio_led_count: count of GPIOs + LEDs reported by F30
+ * @button_count: actual physical buttons count
+ * @button_mask: button mask used to decode GPIO ATTN reports
+ * @button_state_mask: pull state of the buttons
+ *
+ * @input: pointer to the kernel input device
+ *
+ * @reset_work: worker which will be called in case of a mouse report
+ * @hdev: pointer to the struct hid_device
+ */
+struct rmi_data {
+       struct mutex page_mutex;
+       int page;
+
+       wait_queue_head_t wait;
+
+       u8 *writeReport;
+       u8 *readReport;
+
+       int input_report_size;
+       int output_report_size;
+
+       unsigned long flags;
+
+       struct rmi_function f11;
+       struct rmi_function f30;
+
+       unsigned int max_fingers;
+       unsigned int max_x;
+       unsigned int max_y;
+       unsigned int x_size_mm;
+       unsigned int y_size_mm;
+
+       unsigned int gpio_led_count;
+       unsigned int button_count;
+       unsigned long button_mask;
+       unsigned long button_state_mask;
+
+       struct input_dev *input;
+
+       struct work_struct reset_work;
+       struct hid_device *hdev;
+};
+
+#define RMI_PAGE(addr) (((addr) >> 8) & 0xff)
+
+static int rmi_write_report(struct hid_device *hdev, u8 *report, int len);
+
+/**
+ * rmi_set_page - Set RMI page
+ * @hdev: The pointer to the hid_device struct
+ * @page: The new page address.
+ *
+ * RMI devices have 16-bit addressing, but some of the physical
+ * implementations (like SMBus) only have 8-bit addressing. So RMI implements
+ * a page address at 0xff of every page so we can reliable page addresses
+ * every 256 registers.
+ *
+ * The page_mutex lock must be held when this function is entered.
+ *
+ * Returns zero on success, non-zero on failure.
+ */
+static int rmi_set_page(struct hid_device *hdev, u8 page)
+{
+       struct rmi_data *data = hid_get_drvdata(hdev);
+       int retval;
+
+       data->writeReport[0] = RMI_WRITE_REPORT_ID;
+       data->writeReport[1] = 1;
+       data->writeReport[2] = 0xFF;
+       data->writeReport[4] = page;
+
+       retval = rmi_write_report(hdev, data->writeReport,
+                       data->output_report_size);
+       if (retval != data->output_report_size) {
+               dev_err(&hdev->dev,
+                       "%s: set page failed: %d.", __func__, retval);
+               return retval;
+       }
+
+       data->page = page;
+       return 0;
+}
+
+static int rmi_set_mode(struct hid_device *hdev, u8 mode)
+{
+       int ret;
+       u8 txbuf[2] = {RMI_SET_RMI_MODE_REPORT_ID, mode};
+
+       ret = hid_hw_raw_request(hdev, RMI_SET_RMI_MODE_REPORT_ID, txbuf,
+                       sizeof(txbuf), HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+       if (ret < 0) {
+               dev_err(&hdev->dev, "unable to set rmi mode to %d (%d)\n", mode,
+                       ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int rmi_write_report(struct hid_device *hdev, u8 *report, int len)
+{
+       int ret;
+
+       ret = hid_hw_output_report(hdev, (void *)report, len);
+       if (ret < 0) {
+               dev_err(&hdev->dev, "failed to write hid report (%d)\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int rmi_read_block(struct hid_device *hdev, u16 addr, void *buf,
+               const int len)
+{
+       struct rmi_data *data = hid_get_drvdata(hdev);
+       int ret;
+       int bytes_read;
+       int bytes_needed;
+       int retries;
+       int read_input_count;
+
+       mutex_lock(&data->page_mutex);
+
+       if (RMI_PAGE(addr) != data->page) {
+               ret = rmi_set_page(hdev, RMI_PAGE(addr));
+               if (ret < 0)
+                       goto exit;
+       }
+
+       for (retries = 5; retries > 0; retries--) {
+               data->writeReport[0] = RMI_READ_ADDR_REPORT_ID;
+               data->writeReport[1] = 0; /* old 1 byte read count */
+               data->writeReport[2] = addr & 0xFF;
+               data->writeReport[3] = (addr >> 8) & 0xFF;
+               data->writeReport[4] = len  & 0xFF;
+               data->writeReport[5] = (len >> 8) & 0xFF;
+
+               set_bit(RMI_READ_REQUEST_PENDING, &data->flags);
+
+               ret = rmi_write_report(hdev, data->writeReport,
+                                               data->output_report_size);
+               if (ret != data->output_report_size) {
+                       clear_bit(RMI_READ_REQUEST_PENDING, &data->flags);
+                       dev_err(&hdev->dev,
+                               "failed to write request output report (%d)\n",
+                               ret);
+                       goto exit;
+               }
+
+               bytes_read = 0;
+               bytes_needed = len;
+               while (bytes_read < len) {
+                       if (!wait_event_timeout(data->wait,
+                               test_bit(RMI_READ_DATA_PENDING, &data->flags),
+                                       msecs_to_jiffies(1000))) {
+                               hid_warn(hdev, "%s: timeout elapsed\n",
+                                        __func__);
+                               ret = -EAGAIN;
+                               break;
+                       }
+
+                       read_input_count = data->readReport[1];
+                       memcpy(buf + bytes_read, &data->readReport[2],
+                               read_input_count < bytes_needed ?
+                                       read_input_count : bytes_needed);
+
+                       bytes_read += read_input_count;
+                       bytes_needed -= read_input_count;
+                       clear_bit(RMI_READ_DATA_PENDING, &data->flags);
+               }
+
+               if (ret >= 0) {
+                       ret = 0;
+                       break;
+               }
+       }
+
+exit:
+       clear_bit(RMI_READ_REQUEST_PENDING, &data->flags);
+       mutex_unlock(&data->page_mutex);
+       return ret;
+}
+
+static inline int rmi_read(struct hid_device *hdev, u16 addr, void *buf)
+{
+       return rmi_read_block(hdev, addr, buf, 1);
+}
+
+static void rmi_f11_process_touch(struct rmi_data *hdata, int slot,
+               u8 finger_state, u8 *touch_data)
+{
+       int x, y, wx, wy;
+       int wide, major, minor;
+       int z;
+
+       input_mt_slot(hdata->input, slot);
+       input_mt_report_slot_state(hdata->input, MT_TOOL_FINGER,
+                       finger_state == 0x01);
+       if (finger_state == 0x01) {
+               x = (touch_data[0] << 4) | (touch_data[2] & 0x0F);
+               y = (touch_data[1] << 4) | (touch_data[2] >> 4);
+               wx = touch_data[3] & 0x0F;
+               wy = touch_data[3] >> 4;
+               wide = (wx > wy);
+               major = max(wx, wy);
+               minor = min(wx, wy);
+               z = touch_data[4];
+
+               /* y is inverted */
+               y = hdata->max_y - y;
+
+               input_event(hdata->input, EV_ABS, ABS_MT_POSITION_X, x);
+               input_event(hdata->input, EV_ABS, ABS_MT_POSITION_Y, y);
+               input_event(hdata->input, EV_ABS, ABS_MT_ORIENTATION, wide);
+               input_event(hdata->input, EV_ABS, ABS_MT_PRESSURE, z);
+               input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
+               input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
+       }
+}
+
+static void rmi_reset_work(struct work_struct *work)
+{
+       struct rmi_data *hdata = container_of(work, struct rmi_data,
+                                               reset_work);
+
+       /* switch the device to RMI if we receive a generic mouse report */
+       rmi_set_mode(hdata->hdev, RMI_MODE_ATTN_REPORTS);
+}
+
+static inline int rmi_schedule_reset(struct hid_device *hdev)
+{
+       struct rmi_data *hdata = hid_get_drvdata(hdev);
+       return schedule_work(&hdata->reset_work);
+}
+
+static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data,
+               int size)
+{
+       struct rmi_data *hdata = hid_get_drvdata(hdev);
+       int offset;
+       int i;
+
+       if (size < hdata->f11.report_size)
+               return 0;
+
+       if (!(irq & hdata->f11.irq_mask))
+               return 0;
+
+       offset = (hdata->max_fingers >> 2) + 1;
+       for (i = 0; i < hdata->max_fingers; i++) {
+               int fs_byte_position = i >> 2;
+               int fs_bit_position = (i & 0x3) << 1;
+               int finger_state = (data[fs_byte_position] >> fs_bit_position) &
+                                       0x03;
+
+               rmi_f11_process_touch(hdata, i, finger_state,
+                               &data[offset + 5 * i]);
+       }
+       input_mt_sync_frame(hdata->input);
+       input_sync(hdata->input);
+       return hdata->f11.report_size;
+}
+
+static int rmi_f30_input_event(struct hid_device *hdev, u8 irq, u8 *data,
+               int size)
+{
+       struct rmi_data *hdata = hid_get_drvdata(hdev);
+       int i;
+       int button = 0;
+       bool value;
+
+       if (!(irq & hdata->f30.irq_mask))
+               return 0;
+
+       for (i = 0; i < hdata->gpio_led_count; i++) {
+               if (test_bit(i, &hdata->button_mask)) {
+                       value = (data[i / 8] >> (i & 0x07)) & BIT(0);
+                       if (test_bit(i, &hdata->button_state_mask))
+                               value = !value;
+                       input_event(hdata->input, EV_KEY, BTN_LEFT + button++,
+                                       value);
+               }
+       }
+       return hdata->f30.report_size;
+}
+
+static int rmi_input_event(struct hid_device *hdev, u8 *data, int size)
+{
+       struct rmi_data *hdata = hid_get_drvdata(hdev);
+       unsigned long irq_mask = 0;
+       unsigned index = 2;
+
+       if (!(test_bit(RMI_STARTED, &hdata->flags)))
+               return 0;
+
+       irq_mask |= hdata->f11.irq_mask;
+       irq_mask |= hdata->f30.irq_mask;
+
+       if (data[1] & ~irq_mask)
+               hid_warn(hdev, "unknown intr source:%02lx %s:%d\n",
+                       data[1] & ~irq_mask, __FILE__, __LINE__);
+
+       if (hdata->f11.interrupt_base < hdata->f30.interrupt_base) {
+               index += rmi_f11_input_event(hdev, data[1], &data[index],
+                               size - index);
+               index += rmi_f30_input_event(hdev, data[1], &data[index],
+                               size - index);
+       } else {
+               index += rmi_f30_input_event(hdev, data[1], &data[index],
+                               size - index);
+               index += rmi_f11_input_event(hdev, data[1], &data[index],
+                               size - index);
+       }
+
+       return 1;
+}
+
+static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size)
+{
+       struct rmi_data *hdata = hid_get_drvdata(hdev);
+
+       if (!test_bit(RMI_READ_REQUEST_PENDING, &hdata->flags)) {
+               hid_err(hdev, "no read request pending\n");
+               return 0;
+       }
+
+       memcpy(hdata->readReport, data, size < hdata->input_report_size ?
+                       size : hdata->input_report_size);
+       set_bit(RMI_READ_DATA_PENDING, &hdata->flags);
+       wake_up(&hdata->wait);
+
+       return 1;
+}
+
+static int rmi_raw_event(struct hid_device *hdev,
+               struct hid_report *report, u8 *data, int size)
+{
+       switch (data[0]) {
+       case RMI_READ_DATA_REPORT_ID:
+               return rmi_read_data_event(hdev, data, size);
+       case RMI_ATTN_REPORT_ID:
+               return rmi_input_event(hdev, data, size);
+       case RMI_MOUSE_REPORT_ID:
+               rmi_schedule_reset(hdev);
+               break;
+       }
+
+       return 0;
+}
+
+static int rmi_post_reset(struct hid_device *hdev)
+{
+       return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
+}
+
+static int rmi_post_resume(struct hid_device *hdev)
+{
+       return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
+}
+
+#define RMI4_MAX_PAGE 0xff
+#define RMI4_PAGE_SIZE 0x0100
+
+#define PDT_START_SCAN_LOCATION 0x00e9
+#define PDT_END_SCAN_LOCATION  0x0005
+#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
+
+struct pdt_entry {
+       u8 query_base_addr:8;
+       u8 command_base_addr:8;
+       u8 control_base_addr:8;
+       u8 data_base_addr:8;
+       u8 interrupt_source_count:3;
+       u8 bits3and4:2;
+       u8 function_version:2;
+       u8 bit7:1;
+       u8 function_number:8;
+} __attribute__((__packed__));
+
+static inline unsigned long rmi_gen_mask(unsigned irq_base, unsigned irq_count)
+{
+       return GENMASK(irq_count + irq_base - 1, irq_base);
+}
+
+static void rmi_register_function(struct rmi_data *data,
+       struct pdt_entry *pdt_entry, int page, unsigned interrupt_count)
+{
+       struct rmi_function *f = NULL;
+       u16 page_base = page << 8;
+
+       switch (pdt_entry->function_number) {
+       case 0x11:
+               f = &data->f11;
+               break;
+       case 0x30:
+               f = &data->f30;
+               break;
+       }
+
+       if (f) {
+               f->page = page;
+               f->query_base_addr = page_base | pdt_entry->query_base_addr;
+               f->command_base_addr = page_base | pdt_entry->command_base_addr;
+               f->control_base_addr = page_base | pdt_entry->control_base_addr;
+               f->data_base_addr = page_base | pdt_entry->data_base_addr;
+               f->interrupt_base = interrupt_count;
+               f->interrupt_count = pdt_entry->interrupt_source_count;
+               f->irq_mask = rmi_gen_mask(f->interrupt_base,
+                                               f->interrupt_count);
+       }
+}
+
+static int rmi_scan_pdt(struct hid_device *hdev)
+{
+       struct rmi_data *data = hid_get_drvdata(hdev);
+       struct pdt_entry entry;
+       int page;
+       bool page_has_function;
+       int i;
+       int retval;
+       int interrupt = 0;
+       u16 page_start, pdt_start , pdt_end;
+
+       hid_info(hdev, "Scanning PDT...\n");
+
+       for (page = 0; (page <= RMI4_MAX_PAGE); page++) {
+               page_start = RMI4_PAGE_SIZE * page;
+               pdt_start = page_start + PDT_START_SCAN_LOCATION;
+               pdt_end = page_start + PDT_END_SCAN_LOCATION;
+
+               page_has_function = false;
+               for (i = pdt_start; i >= pdt_end; i -= sizeof(entry)) {
+                       retval = rmi_read_block(hdev, i, &entry, sizeof(entry));
+                       if (retval) {
+                               hid_err(hdev,
+                                       "Read of PDT entry at %#06x failed.\n",
+                                       i);
+                               goto error_exit;
+                       }
+
+                       if (RMI4_END_OF_PDT(entry.function_number))
+                               break;
+
+                       page_has_function = true;
+
+                       hid_info(hdev, "Found F%02X on page %#04x\n",
+                                       entry.function_number, page);
+
+                       rmi_register_function(data, &entry, page, interrupt);
+                       interrupt += entry.interrupt_source_count;
+               }
+
+               if (!page_has_function)
+                       break;
+       }
+
+       hid_info(hdev, "%s: Done with PDT scan.\n", __func__);
+       retval = 0;
+
+error_exit:
+       return retval;
+}
+
+static int rmi_populate_f11(struct hid_device *hdev)
+{
+       struct rmi_data *data = hid_get_drvdata(hdev);
+       u8 buf[20];
+       int ret;
+       bool has_query9;
+       bool has_query10;
+       bool has_query11;
+       bool has_query12;
+       bool has_physical_props;
+       unsigned x_size, y_size;
+       u16 query12_offset;
+
+       if (!data->f11.query_base_addr) {
+               hid_err(hdev, "No 2D sensor found, giving up.\n");
+               return -ENODEV;
+       }
+
+       /* query 0 contains some useful information */
+       ret = rmi_read(hdev, data->f11.query_base_addr, buf);
+       if (ret) {
+               hid_err(hdev, "can not get query 0: %d.\n", ret);
+               return ret;
+       }
+       has_query9 = !!(buf[0] & BIT(3));
+       has_query11 = !!(buf[0] & BIT(4));
+       has_query12 = !!(buf[0] & BIT(5));
+
+       /* query 1 to get the max number of fingers */
+       ret = rmi_read(hdev, data->f11.query_base_addr + 1, buf);
+       if (ret) {
+               hid_err(hdev, "can not get NumberOfFingers: %d.\n", ret);
+               return ret;
+       }
+       data->max_fingers = (buf[0] & 0x07) + 1;
+       if (data->max_fingers > 5)
+               data->max_fingers = 10;
+
+       data->f11.report_size = data->max_fingers * 5 +
+                               DIV_ROUND_UP(data->max_fingers, 4);
+
+       if (!(buf[0] & BIT(4))) {
+               hid_err(hdev, "No absolute events, giving up.\n");
+               return -ENODEV;
+       }
+
+       /* query 8 to find out if query 10 exists */
+       ret = rmi_read(hdev, data->f11.query_base_addr + 8, buf);
+       if (ret) {
+               hid_err(hdev, "can not read gesture information: %d.\n", ret);
+               return ret;
+       }
+       has_query10 = !!(buf[0] & BIT(2));
+
+       /*
+        * At least 8 queries are guaranteed to be present in F11
+        * +1 for query12.
+        */
+       query12_offset = 9;
+
+       if (has_query9)
+               ++query12_offset;
+
+       if (has_query10)
+               ++query12_offset;
+
+       if (has_query11)
+               ++query12_offset;
+
+       /* query 12 to know if the physical properties are reported */
+       if (has_query12) {
+               ret = rmi_read(hdev, data->f11.query_base_addr
+                               + query12_offset, buf);
+               if (ret) {
+                       hid_err(hdev, "can not get query 12: %d.\n", ret);
+                       return ret;
+               }
+               has_physical_props = !!(buf[0] & BIT(5));
+
+               if (has_physical_props) {
+                       ret = rmi_read_block(hdev,
+                                       data->f11.query_base_addr
+                                               + query12_offset + 1, buf, 4);
+                       if (ret) {
+                               hid_err(hdev, "can not read query 15-18: %d.\n",
+                                       ret);
+                               return ret;
+                       }
+
+                       x_size = buf[0] | (buf[1] << 8);
+                       y_size = buf[2] | (buf[3] << 8);
+
+                       data->x_size_mm = DIV_ROUND_CLOSEST(x_size, 10);
+                       data->y_size_mm = DIV_ROUND_CLOSEST(y_size, 10);
+
+                       hid_info(hdev, "%s: size in mm: %d x %d\n",
+                                __func__, data->x_size_mm, data->y_size_mm);
+               }
+       }
+
+       /*
+        * retrieve the ctrl registers
+        * the ctrl register has a size of 20 but a fw bug split it into 16 + 4,
+        * and there is no way to know if the first 20 bytes are here or not.
+        * We use only the first 10 bytes, so get only them.
+        */
+       ret = rmi_read_block(hdev, data->f11.control_base_addr, buf, 10);
+       if (ret) {
+               hid_err(hdev, "can not read ctrl block of size 10: %d.\n", ret);
+               return ret;
+       }
+
+       data->max_x = buf[6] | (buf[7] << 8);
+       data->max_y = buf[8] | (buf[9] << 8);
+
+       return 0;
+}
+
+static int rmi_populate_f30(struct hid_device *hdev)
+{
+       struct rmi_data *data = hid_get_drvdata(hdev);
+       u8 buf[20];
+       int ret;
+       bool has_gpio, has_led;
+       unsigned bytes_per_ctrl;
+       u8 ctrl2_addr;
+       int ctrl2_3_length;
+       int i;
+
+       /* function F30 is for physical buttons */
+       if (!data->f30.query_base_addr) {
+               hid_err(hdev, "No GPIO/LEDs found, giving up.\n");
+               return -ENODEV;
+       }
+
+       ret = rmi_read_block(hdev, data->f30.query_base_addr, buf, 2);
+       if (ret) {
+               hid_err(hdev, "can not get F30 query registers: %d.\n", ret);
+               return ret;
+       }
+
+       has_gpio = !!(buf[0] & BIT(3));
+       has_led = !!(buf[0] & BIT(2));
+       data->gpio_led_count = buf[1] & 0x1f;
+
+       /* retrieve ctrl 2 & 3 registers */
+       bytes_per_ctrl = (data->gpio_led_count + 7) / 8;
+       /* Ctrl0 is present only if both has_gpio and has_led are set*/
+       ctrl2_addr = (has_gpio && has_led) ? bytes_per_ctrl : 0;
+       /* Ctrl1 is always be present */
+       ctrl2_addr += bytes_per_ctrl;
+       ctrl2_3_length = 2 * bytes_per_ctrl;
+
+       data->f30.report_size = bytes_per_ctrl;
+
+       ret = rmi_read_block(hdev, data->f30.control_base_addr + ctrl2_addr,
+                               buf, ctrl2_3_length);
+       if (ret) {
+               hid_err(hdev, "can not read ctrl 2&3 block of size %d: %d.\n",
+                       ctrl2_3_length, ret);
+               return ret;
+       }
+
+       for (i = 0; i < data->gpio_led_count; i++) {
+               int byte_position = i >> 3;
+               int bit_position = i & 0x07;
+               u8 dir_byte = buf[byte_position];
+               u8 data_byte = buf[byte_position + bytes_per_ctrl];
+               bool dir = (dir_byte >> bit_position) & BIT(0);
+               bool dat = (data_byte >> bit_position) & BIT(0);
+
+               if (dir == 0) {
+                       /* input mode */
+                       if (dat) {
+                               /* actual buttons have pull up resistor */
+                               data->button_count++;
+                               set_bit(i, &data->button_mask);
+                               set_bit(i, &data->button_state_mask);
+                       }
+               }
+
+       }
+
+       return 0;
+}
+
+static int rmi_populate(struct hid_device *hdev)
+{
+       int ret;
+
+       ret = rmi_scan_pdt(hdev);
+       if (ret) {
+               hid_err(hdev, "PDT scan failed with code %d.\n", ret);
+               return ret;
+       }
+
+       ret = rmi_populate_f11(hdev);
+       if (ret) {
+               hid_err(hdev, "Error while initializing F11 (%d).\n", ret);
+               return ret;
+       }
+
+       ret = rmi_populate_f30(hdev);
+       if (ret)
+               hid_warn(hdev, "Error while initializing F30 (%d).\n", ret);
+
+       return 0;
+}
+
+static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi)
+{
+       struct rmi_data *data = hid_get_drvdata(hdev);
+       struct input_dev *input = hi->input;
+       int ret;
+       int res_x, res_y, i;
+
+       data->input = input;
+
+       hid_dbg(hdev, "Opening low level driver\n");
+       ret = hid_hw_open(hdev);
+       if (ret)
+               return;
+
+       /* Allow incoming hid reports */
+       hid_device_io_start(hdev);
+
+       ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
+       if (ret < 0) {
+               dev_err(&hdev->dev, "failed to set rmi mode\n");
+               goto exit;
+       }
+
+       ret = rmi_set_page(hdev, 0);
+       if (ret < 0) {
+               dev_err(&hdev->dev, "failed to set page select to 0.\n");
+               goto exit;
+       }
+
+       ret = rmi_populate(hdev);
+       if (ret)
+               goto exit;
+
+       __set_bit(EV_ABS, input->evbit);
+       input_set_abs_params(input, ABS_MT_POSITION_X, 1, data->max_x, 0, 0);
+       input_set_abs_params(input, ABS_MT_POSITION_Y, 1, data->max_y, 0, 0);
+
+       if (data->x_size_mm && data->y_size_mm) {
+               res_x = (data->max_x - 1) / data->x_size_mm;
+               res_y = (data->max_y - 1) / data->y_size_mm;
+
+               input_abs_set_res(input, ABS_MT_POSITION_X, res_x);
+               input_abs_set_res(input, ABS_MT_POSITION_Y, res_y);
+       }
+
+       input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+       input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0);
+       input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0);
+       input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0);
+
+       input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER);
+
+       if (data->button_count) {
+               __set_bit(EV_KEY, input->evbit);
+               for (i = 0; i < data->button_count; i++)
+                       __set_bit(BTN_LEFT + i, input->keybit);
+
+               if (data->button_count == 1)
+                       __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+       }
+
+       set_bit(RMI_STARTED, &data->flags);
+
+exit:
+       hid_device_io_stop(hdev);
+       hid_hw_close(hdev);
+}
+
+static int rmi_input_mapping(struct hid_device *hdev,
+               struct hid_input *hi, struct hid_field *field,
+               struct hid_usage *usage, unsigned long **bit, int *max)
+{
+       /* we want to make HID ignore the advertised HID collection */
+       return -1;
+}
+
+static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       struct rmi_data *data = NULL;
+       int ret;
+       size_t alloc_size;
+
+       data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       INIT_WORK(&data->reset_work, rmi_reset_work);
+       data->hdev = hdev;
+
+       hid_set_drvdata(hdev, data);
+
+       hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "parse failed\n");
+               return ret;
+       }
+
+       data->input_report_size = (hdev->report_enum[HID_INPUT_REPORT]
+               .report_id_hash[RMI_ATTN_REPORT_ID]->size >> 3)
+               + 1 /* report id */;
+       data->output_report_size = (hdev->report_enum[HID_OUTPUT_REPORT]
+               .report_id_hash[RMI_WRITE_REPORT_ID]->size >> 3)
+               + 1 /* report id */;
+
+       alloc_size = data->output_report_size + data->input_report_size;
+
+       data->writeReport = devm_kzalloc(&hdev->dev, alloc_size, GFP_KERNEL);
+       if (!data->writeReport) {
+               ret = -ENOMEM;
+               return ret;
+       }
+
+       data->readReport = data->writeReport + data->output_report_size;
+
+       init_waitqueue_head(&data->wait);
+
+       mutex_init(&data->page_mutex);
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               hid_err(hdev, "hw start failed\n");
+               return ret;
+       }
+
+       if (!test_bit(RMI_STARTED, &data->flags)) {
+               hid_hw_stop(hdev);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void rmi_remove(struct hid_device *hdev)
+{
+       struct rmi_data *hdata = hid_get_drvdata(hdev);
+
+       clear_bit(RMI_STARTED, &hdata->flags);
+
+       hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id rmi_id[] = {
+       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_RMI, HID_ANY_ID, HID_ANY_ID) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, rmi_id);
+
+static struct hid_driver rmi_driver = {
+       .name = "hid-rmi",
+       .id_table               = rmi_id,
+       .probe                  = rmi_probe,
+       .remove                 = rmi_remove,
+       .raw_event              = rmi_raw_event,
+       .input_mapping          = rmi_input_mapping,
+       .input_configured       = rmi_input_configured,
+#ifdef CONFIG_PM
+       .resume                 = rmi_post_resume,
+       .reset_resume           = rmi_post_reset,
+#endif
+};
+
+module_hid_driver(rmi_driver);
+
+MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>");
+MODULE_DESCRIPTION("RMI HID driver");
+MODULE_LICENSE("GPL");
index 37961c7..69cca14 100644 (file)
@@ -1,10 +1,17 @@
 /*
- *  HID driver for Saitek devices, currently only the PS1000 (USB gamepad).
+ *  HID driver for Saitek devices.
+ *
+ *  PS1000 (USB gamepad):
  *  Fixes the HID report descriptor by removing a non-existent axis and
  *  clearing the constant bit on the input reports for buttons and d-pad.
  *  (This module is based on "hid-ortek".)
- *
  *  Copyright (c) 2012 Andreas Hübner
+ *
+ *  R.A.T.7, M.M.O.7 (USB gaming mice):
+ *  Fixes the mode button which cycles through three constantly pressed
+ *  buttons. All three press events are mapped to one button and the
+ *  missing release event is generated immediately.
+ *
  */
 
 /*
 
 #include "hid-ids.h"
 
+#define SAITEK_FIX_PS1000      0x0001
+#define SAITEK_RELEASE_MODE_RAT7       0x0002
+#define SAITEK_RELEASE_MODE_MMO7       0x0004
+
+struct saitek_sc {
+       unsigned long quirks;
+       int mode;
+};
+
+static int saitek_probe(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       unsigned long quirks = id->driver_data;
+       struct saitek_sc *ssc;
+       int ret;
+
+       ssc = devm_kzalloc(&hdev->dev, sizeof(*ssc), GFP_KERNEL);
+       if (ssc == NULL) {
+               hid_err(hdev, "can't alloc saitek descriptor\n");
+               return -ENOMEM;
+       }
+
+       ssc->quirks = quirks;
+       ssc->mode = -1;
+
+       hid_set_drvdata(hdev, ssc);
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "parse failed\n");
+               return ret;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               hid_err(hdev, "hw start failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
 static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                unsigned int *rsize)
 {
-       if (*rsize == 137 && rdesc[20] == 0x09 && rdesc[21] == 0x33
-                       && rdesc[94] == 0x81 && rdesc[95] == 0x03
-                       && rdesc[110] == 0x81 && rdesc[111] == 0x03) {
+       struct saitek_sc *ssc = hid_get_drvdata(hdev);
+
+       if ((ssc->quirks & SAITEK_FIX_PS1000) && *rsize == 137 &&
+                       rdesc[20] == 0x09 && rdesc[21] == 0x33 &&
+                       rdesc[94] == 0x81 && rdesc[95] == 0x03 &&
+                       rdesc[110] == 0x81 && rdesc[111] == 0x03) {
 
                hid_info(hdev, "Fixing up Saitek PS1000 report descriptor\n");
 
@@ -42,8 +94,93 @@ static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
        return rdesc;
 }
 
+static int saitek_raw_event(struct hid_device *hdev,
+               struct hid_report *report, u8 *raw_data, int size)
+{
+       struct saitek_sc *ssc = hid_get_drvdata(hdev);
+
+       if (ssc->quirks & SAITEK_RELEASE_MODE_RAT7 && size == 7) {
+               /* R.A.T.7 uses bits 13, 14, 15 for the mode */
+               int mode = -1;
+               if (raw_data[1] & 0x01)
+                       mode = 0;
+               else if (raw_data[1] & 0x02)
+                       mode = 1;
+               else if (raw_data[1] & 0x04)
+                       mode = 2;
+
+               /* clear mode bits */
+               raw_data[1] &= ~0x07;
+
+               if (mode != ssc->mode) {
+                       hid_dbg(hdev, "entered mode %d\n", mode);
+                       if (ssc->mode != -1) {
+                               /* use bit 13 as the mode button */
+                               raw_data[1] |= 0x04;
+                       }
+                       ssc->mode = mode;
+               }
+       } else if (ssc->quirks & SAITEK_RELEASE_MODE_MMO7 && size == 8) {
+
+               /* M.M.O.7 uses bits 8, 22, 23 for the mode */
+               int mode = -1;
+               if (raw_data[1] & 0x80)
+                       mode = 0;
+               else if (raw_data[2] & 0x01)
+                       mode = 1;
+               else if (raw_data[2] & 0x02)
+                       mode = 2;
+
+               /* clear mode bits */
+               raw_data[1] &= ~0x80;
+               raw_data[2] &= ~0x03;
+
+               if (mode != ssc->mode) {
+                       hid_dbg(hdev, "entered mode %d\n", mode);
+                       if (ssc->mode != -1) {
+                               /* use bit 8 as the mode button, bits 22
+                                * and 23 do not represent buttons
+                                * according to the HID report descriptor
+                                */
+                               raw_data[1] |= 0x80;
+                       }
+                       ssc->mode = mode;
+               }
+       }
+
+       return 0;
+}
+
+static int saitek_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       struct saitek_sc *ssc = hid_get_drvdata(hdev);
+       struct input_dev *input = field->hidinput->input;
+
+       if (usage->type == EV_KEY && value &&
+                       (((ssc->quirks & SAITEK_RELEASE_MODE_RAT7) &&
+                         usage->code - BTN_MOUSE == 10) ||
+                       ((ssc->quirks & SAITEK_RELEASE_MODE_MMO7) &&
+                        usage->code - BTN_MOUSE == 15))) {
+
+               input_report_key(input, usage->code, 1);
+
+               /* report missing release event */
+               input_report_key(input, usage->code, 0);
+
+               return 1;
+       }
+
+       return 0;
+}
+
 static const struct hid_device_id saitek_devices[] = {
-       { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000)},
+       { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000),
+               .driver_data = SAITEK_FIX_PS1000 },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7),
+               .driver_data = SAITEK_RELEASE_MODE_RAT7 },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7),
+               .driver_data = SAITEK_RELEASE_MODE_MMO7 },
        { }
 };
 
@@ -52,7 +189,10 @@ MODULE_DEVICE_TABLE(hid, saitek_devices);
 static struct hid_driver saitek_driver = {
        .name = "saitek",
        .id_table = saitek_devices,
-       .report_fixup = saitek_report_fixup
+       .probe = saitek_probe,
+       .report_fixup = saitek_report_fixup,
+       .raw_event = saitek_raw_event,
+       .event = saitek_event,
 };
 module_hid_driver(saitek_driver);
 
index be14b56..a8d5c8f 100644 (file)
@@ -705,8 +705,17 @@ static const struct hid_device_id sensor_hub_devices[] = {
        { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_1,
                        USB_DEVICE_ID_INTEL_HID_SENSOR_1),
                        .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
+                       USB_DEVICE_ID_MS_SURFACE_PRO_2),
+                       .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
+                       USB_DEVICE_ID_MS_TOUCH_COVER_2),
+                       .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
+                       USB_DEVICE_ID_MS_TYPE_COVER_2),
+                       .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
        { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0,
-                       USB_DEVICE_ID_STM_HID_SENSOR),
+                       USB_DEVICE_ID_STM_HID_SENSOR_1),
                        .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
        { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_TEXAS_INSTRUMENTS,
                        USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA),
index 908de27..2259eaa 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/power_supply.h>
 #include <linux/spinlock.h>
 #include <linux/list.h>
+#include <linux/idr.h>
 #include <linux/input/mt.h>
 
 #include "hid-ids.h"
@@ -717,8 +718,39 @@ static enum power_supply_property sony_battery_props[] = {
        POWER_SUPPLY_PROP_STATUS,
 };
 
+struct sixaxis_led {
+       __u8 time_enabled; /* the total time the led is active (0xff means forever) */
+       __u8 duty_length;  /* how long a cycle is in deciseconds (0 means "really fast") */
+       __u8 enabled;
+       __u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */
+       __u8 duty_on;  /* % of duty_length the led is on (0xff mean 100%) */
+} __packed;
+
+struct sixaxis_rumble {
+       __u8 padding;
+       __u8 right_duration; /* Right motor duration (0xff means forever) */
+       __u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
+       __u8 left_duration;    /* Left motor duration (0xff means forever) */
+       __u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
+} __packed;
+
+struct sixaxis_output_report {
+       __u8 report_id;
+       struct sixaxis_rumble rumble;
+       __u8 padding[4];
+       __u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
+       struct sixaxis_led led[4];    /* LEDx at (4 - x) */
+       struct sixaxis_led _reserved; /* LED5, not actually soldered */
+} __packed;
+
+union sixaxis_output_report_01 {
+       struct sixaxis_output_report data;
+       __u8 buf[36];
+};
+
 static spinlock_t sony_dev_list_lock;
 static LIST_HEAD(sony_device_list);
+static DEFINE_IDA(sony_device_id_allocator);
 
 struct sony_sc {
        spinlock_t lock;
@@ -728,6 +760,7 @@ struct sony_sc {
        unsigned long quirks;
        struct work_struct state_worker;
        struct power_supply battery;
+       int device_id;
 
 #ifdef CONFIG_SONY_FF
        __u8 left;
@@ -740,6 +773,8 @@ struct sony_sc {
        __u8 battery_charging;
        __u8 battery_capacity;
        __u8 led_state[MAX_LEDS];
+       __u8 led_delay_on[MAX_LEDS];
+       __u8 led_delay_off[MAX_LEDS];
        __u8 led_count;
 };
 
@@ -1048,6 +1083,52 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
                                HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
 }
 
+static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+{
+       static const __u8 sixaxis_leds[10][4] = {
+                               { 0x01, 0x00, 0x00, 0x00 },
+                               { 0x00, 0x01, 0x00, 0x00 },
+                               { 0x00, 0x00, 0x01, 0x00 },
+                               { 0x00, 0x00, 0x00, 0x01 },
+                               { 0x01, 0x00, 0x00, 0x01 },
+                               { 0x00, 0x01, 0x00, 0x01 },
+                               { 0x00, 0x00, 0x01, 0x01 },
+                               { 0x01, 0x00, 0x01, 0x01 },
+                               { 0x00, 0x01, 0x01, 0x01 },
+                               { 0x01, 0x01, 0x01, 0x01 }
+       };
+
+       BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
+
+       if (id < 0)
+               return;
+
+       id %= 10;
+       memcpy(values, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
+}
+
+static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+{
+       /* The first 4 color/index entries match what the PS4 assigns */
+       static const __u8 color_code[7][3] = {
+                       /* Blue   */    { 0x00, 0x00, 0x01 },
+                       /* Red    */    { 0x01, 0x00, 0x00 },
+                       /* Green  */    { 0x00, 0x01, 0x00 },
+                       /* Pink   */    { 0x02, 0x00, 0x01 },
+                       /* Orange */    { 0x02, 0x01, 0x00 },
+                       /* Teal   */    { 0x00, 0x01, 0x01 },
+                       /* White  */    { 0x01, 0x01, 0x01 }
+       };
+
+       BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
+
+       if (id < 0)
+               return;
+
+       id %= 7;
+       memcpy(values, color_code[id], sizeof(color_code[id]));
+}
+
 static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
 {
        struct list_head *report_list =
@@ -1066,19 +1147,18 @@ static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
        hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
 }
 
-static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count)
+static void sony_set_leds(struct sony_sc *sc, const __u8 *leds, int count)
 {
-       struct sony_sc *drv_data = hid_get_drvdata(hdev);
        int n;
 
        BUG_ON(count > MAX_LEDS);
 
-       if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) {
-               buzz_set_leds(hdev, leds);
+       if (sc->quirks & BUZZ_CONTROLLER && count == 4) {
+               buzz_set_leds(sc->hdev, leds);
        } else {
                for (n = 0; n < count; n++)
-                       drv_data->led_state[n] = leds[n];
-               schedule_work(&drv_data->state_worker);
+                       sc->led_state[n] = leds[n];
+               schedule_work(&sc->state_worker);
        }
 }
 
@@ -1090,6 +1170,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
        struct sony_sc *drv_data;
 
        int n;
+       int force_update;
 
        drv_data = hid_get_drvdata(hdev);
        if (!drv_data) {
@@ -1097,12 +1178,29 @@ static void sony_led_set_brightness(struct led_classdev *led,
                return;
        }
 
+       /*
+        * The Sixaxis on USB will override any LED settings sent to it
+        * and keep flashing all of the LEDs until the PS button is pressed.
+        * Updates, even if redundant, must be always be sent to the
+        * controller to avoid having to toggle the state of an LED just to
+        * stop the flashing later on.
+        */
+       force_update = !!(drv_data->quirks & SIXAXIS_CONTROLLER_USB);
+
        for (n = 0; n < drv_data->led_count; n++) {
-               if (led == drv_data->leds[n]) {
-                       if (value != drv_data->led_state[n]) {
-                               drv_data->led_state[n] = value;
-                               sony_set_leds(hdev, drv_data->led_state, drv_data->led_count);
-                       }
+               if (led == drv_data->leds[n] && (force_update ||
+                       (value != drv_data->led_state[n] ||
+                       drv_data->led_delay_on[n] ||
+                       drv_data->led_delay_off[n]))) {
+
+                       drv_data->led_state[n] = value;
+
+                       /* Setting the brightness stops the blinking */
+                       drv_data->led_delay_on[n] = 0;
+                       drv_data->led_delay_off[n] = 0;
+
+                       sony_set_leds(drv_data, drv_data->led_state,
+                                       drv_data->led_count);
                        break;
                }
        }
@@ -1130,63 +1228,112 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
        return LED_OFF;
 }
 
-static void sony_leds_remove(struct hid_device *hdev)
+static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
+                               unsigned long *delay_off)
 {
-       struct sony_sc *drv_data;
-       struct led_classdev *led;
+       struct device *dev = led->dev->parent;
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct sony_sc *drv_data = hid_get_drvdata(hdev);
        int n;
+       __u8 new_on, new_off;
 
-       drv_data = hid_get_drvdata(hdev);
-       BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
+       if (!drv_data) {
+               hid_err(hdev, "No device data\n");
+               return -EINVAL;
+       }
+
+       /* Max delay is 255 deciseconds or 2550 milliseconds */
+       if (*delay_on > 2550)
+               *delay_on = 2550;
+       if (*delay_off > 2550)
+               *delay_off = 2550;
+
+       /* Blink at 1 Hz if both values are zero */
+       if (!*delay_on && !*delay_off)
+               *delay_on = *delay_off = 500;
+
+       new_on = *delay_on / 10;
+       new_off = *delay_off / 10;
 
        for (n = 0; n < drv_data->led_count; n++) {
-               led = drv_data->leds[n];
-               drv_data->leds[n] = NULL;
+               if (led == drv_data->leds[n])
+                       break;
+       }
+
+       /* This LED is not registered on this device */
+       if (n >= drv_data->led_count)
+               return -EINVAL;
+
+       /* Don't schedule work if the values didn't change */
+       if (new_on != drv_data->led_delay_on[n] ||
+               new_off != drv_data->led_delay_off[n]) {
+               drv_data->led_delay_on[n] = new_on;
+               drv_data->led_delay_off[n] = new_off;
+               schedule_work(&drv_data->state_worker);
+       }
+
+       return 0;
+}
+
+static void sony_leds_remove(struct sony_sc *sc)
+{
+       struct led_classdev *led;
+       int n;
+
+       BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
+
+       for (n = 0; n < sc->led_count; n++) {
+               led = sc->leds[n];
+               sc->leds[n] = NULL;
                if (!led)
                        continue;
                led_classdev_unregister(led);
                kfree(led);
        }
 
-       drv_data->led_count = 0;
+       sc->led_count = 0;
 }
 
-static int sony_leds_init(struct hid_device *hdev)
+static int sony_leds_init(struct sony_sc *sc)
 {
-       struct sony_sc *drv_data;
+       struct hid_device *hdev = sc->hdev;
        int n, ret = 0;
-       int max_brightness;
-       int use_colors;
+       int use_ds4_names;
        struct led_classdev *led;
        size_t name_sz;
        char *name;
        size_t name_len;
        const char *name_fmt;
-       static const char * const color_str[] = { "red", "green", "blue" };
-       static const __u8 initial_values[MAX_LEDS] = { 0x00, 0x00, 0x00, 0x00 };
+       static const char * const ds4_name_str[] = { "red", "green", "blue",
+                                                 "global" };
+       __u8 initial_values[MAX_LEDS] = { 0 };
+       __u8 max_brightness[MAX_LEDS] = { 1 };
+       __u8 use_hw_blink[MAX_LEDS] = { 0 };
 
-       drv_data = hid_get_drvdata(hdev);
-       BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
+       BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
 
-       if (drv_data->quirks & BUZZ_CONTROLLER) {
-               drv_data->led_count = 4;
-               max_brightness = 1;
-               use_colors = 0;
+       if (sc->quirks & BUZZ_CONTROLLER) {
+               sc->led_count = 4;
+               use_ds4_names = 0;
                name_len = strlen("::buzz#");
                name_fmt = "%s::buzz%d";
                /* Validate expected report characteristics. */
                if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
                        return -ENODEV;
-       } else if (drv_data->quirks & DUALSHOCK4_CONTROLLER) {
-               drv_data->led_count = 3;
-               max_brightness = 255;
-               use_colors = 1;
+       } else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
+               dualshock4_set_leds_from_id(sc->device_id, initial_values);
+               initial_values[3] = 1;
+               sc->led_count = 4;
+               memset(max_brightness, 255, 3);
+               use_hw_blink[3] = 1;
+               use_ds4_names = 1;
                name_len = 0;
                name_fmt = "%s:%s";
        } else {
-               drv_data->led_count = 4;
-               max_brightness = 1;
-               use_colors = 0;
+               sixaxis_set_leds_from_id(sc->device_id, initial_values);
+               sc->led_count = 4;
+               memset(use_hw_blink, 1, 4);
+               use_ds4_names = 0;
                name_len = strlen("::sony#");
                name_fmt = "%s::sony%d";
        }
@@ -1196,14 +1343,14 @@ static int sony_leds_init(struct hid_device *hdev)
         * only relevant if the driver is loaded after somebody actively set the
         * LEDs to on
         */
-       sony_set_leds(hdev, initial_values, drv_data->led_count);
+       sony_set_leds(sc, initial_values, sc->led_count);
 
        name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
 
-       for (n = 0; n < drv_data->led_count; n++) {
+       for (n = 0; n < sc->led_count; n++) {
 
-               if (use_colors)
-                       name_sz = strlen(dev_name(&hdev->dev)) + strlen(color_str[n]) + 2;
+               if (use_ds4_names)
+                       name_sz = strlen(dev_name(&hdev->dev)) + strlen(ds4_name_str[n]) + 2;
 
                led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL);
                if (!led) {
@@ -1213,30 +1360,35 @@ static int sony_leds_init(struct hid_device *hdev)
                }
 
                name = (void *)(&led[1]);
-               if (use_colors)
-                       snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), color_str[n]);
+               if (use_ds4_names)
+                       snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev),
+                       ds4_name_str[n]);
                else
                        snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
                led->name = name;
-               led->brightness = 0;
-               led->max_brightness = max_brightness;
+               led->brightness = initial_values[n];
+               led->max_brightness = max_brightness[n];
                led->brightness_get = sony_led_get_brightness;
                led->brightness_set = sony_led_set_brightness;
 
+               if (use_hw_blink[n])
+                       led->blink_set = sony_led_blink_set;
+
+               sc->leds[n] = led;
+
                ret = led_classdev_register(&hdev->dev, led);
                if (ret) {
                        hid_err(hdev, "Failed to register LED %d\n", n);
+                       sc->leds[n] = NULL;
                        kfree(led);
                        goto error_leds;
                }
-
-               drv_data->leds[n] = led;
        }
 
        return ret;
 
 error_leds:
-       sony_leds_remove(hdev);
+       sony_leds_remove(sc);
 
        return ret;
 }
@@ -1244,29 +1396,52 @@ error_leds:
 static void sixaxis_state_worker(struct work_struct *work)
 {
        struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
-       unsigned char buf[] = {
-               0x01,
-               0x00, 0xff, 0x00, 0xff, 0x00,
-               0x00, 0x00, 0x00, 0x00, 0x00,
-               0xff, 0x27, 0x10, 0x00, 0x32,
-               0xff, 0x27, 0x10, 0x00, 0x32,
-               0xff, 0x27, 0x10, 0x00, 0x32,
-               0xff, 0x27, 0x10, 0x00, 0x32,
-               0x00, 0x00, 0x00, 0x00, 0x00
+       int n;
+       union sixaxis_output_report_01 report = {
+               .buf = {
+                       0x01,
+                       0x00, 0xff, 0x00, 0xff, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00,
+                       0xff, 0x27, 0x10, 0x00, 0x32,
+                       0xff, 0x27, 0x10, 0x00, 0x32,
+                       0xff, 0x27, 0x10, 0x00, 0x32,
+                       0xff, 0x27, 0x10, 0x00, 0x32,
+                       0x00, 0x00, 0x00, 0x00, 0x00
+               }
        };
 
 #ifdef CONFIG_SONY_FF
-       buf[3] = sc->right ? 1 : 0;
-       buf[5] = sc->left;
+       report.data.rumble.right_motor_on = sc->right ? 1 : 0;
+       report.data.rumble.left_motor_force = sc->left;
 #endif
 
-       buf[10] |= sc->led_state[0] << 1;
-       buf[10] |= sc->led_state[1] << 2;
-       buf[10] |= sc->led_state[2] << 3;
-       buf[10] |= sc->led_state[3] << 4;
+       report.data.leds_bitmap |= sc->led_state[0] << 1;
+       report.data.leds_bitmap |= sc->led_state[1] << 2;
+       report.data.leds_bitmap |= sc->led_state[2] << 3;
+       report.data.leds_bitmap |= sc->led_state[3] << 4;
+
+       /* Set flag for all leds off, required for 3rd party INTEC controller */
+       if ((report.data.leds_bitmap & 0x1E) == 0)
+               report.data.leds_bitmap |= 0x20;
 
-       hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT,
-                       HID_REQ_SET_REPORT);
+       /*
+        * The LEDs in the report are indexed in reverse order to their
+        * corresponding light on the controller.
+        * Index 0 = LED 4, index 1 = LED 3, etc...
+        *
+        * In the case of both delay values being zero (blinking disabled) the
+        * default report values should be used or the controller LED will be
+        * always off.
+        */
+       for (n = 0; n < 4; n++) {
+               if (sc->led_delay_on[n] || sc->led_delay_off[n]) {
+                       report.data.led[3 - n].duty_off = sc->led_delay_off[n];
+                       report.data.led[3 - n].duty_on = sc->led_delay_on[n];
+               }
+       }
+
+       hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf,
+                       sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
 }
 
 static void dualshock4_state_worker(struct work_struct *work)
@@ -1279,7 +1454,7 @@ static void dualshock4_state_worker(struct work_struct *work)
 
        if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
                buf[0] = 0x05;
-               buf[1] = 0x03;
+               buf[1] = 0xFF;
                offset = 4;
        } else {
                buf[0] = 0x11;
@@ -1295,9 +1470,18 @@ static void dualshock4_state_worker(struct work_struct *work)
        offset += 2;
 #endif
 
-       buf[offset++] = sc->led_state[0];
-       buf[offset++] = sc->led_state[1];
-       buf[offset++] = sc->led_state[2];
+       /* LED 3 is the global control */
+       if (sc->led_state[3]) {
+               buf[offset++] = sc->led_state[0];
+               buf[offset++] = sc->led_state[1];
+               buf[offset++] = sc->led_state[2];
+       } else {
+               offset += 3;
+       }
+
+       /* If both delay values are zero the DualShock 4 disables blinking. */
+       buf[offset++] = sc->led_delay_on[3];
+       buf[offset++] = sc->led_delay_off[3];
 
        if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
                hid_hw_output_report(hdev, buf, 32);
@@ -1323,9 +1507,9 @@ static int sony_play_effect(struct input_dev *dev, void *data,
        return 0;
 }
 
-static int sony_init_ff(struct hid_device *hdev)
+static int sony_init_ff(struct sony_sc *sc)
 {
-       struct hid_input *hidinput = list_entry(hdev->inputs.next,
+       struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
                                                struct hid_input, list);
        struct input_dev *input_dev = hidinput->input;
 
@@ -1334,7 +1518,7 @@ static int sony_init_ff(struct hid_device *hdev)
 }
 
 #else
-static int sony_init_ff(struct hid_device *hdev)
+static int sony_init_ff(struct sony_sc *sc)
 {
        return 0;
 }
@@ -1384,8 +1568,6 @@ static int sony_battery_get_property(struct power_supply *psy,
 
 static int sony_battery_probe(struct sony_sc *sc)
 {
-       static atomic_t power_id_seq = ATOMIC_INIT(0);
-       unsigned long power_id;
        struct hid_device *hdev = sc->hdev;
        int ret;
 
@@ -1395,15 +1577,13 @@ static int sony_battery_probe(struct sony_sc *sc)
         */
        sc->battery_capacity = 100;
 
-       power_id = (unsigned long)atomic_inc_return(&power_id_seq);
-
        sc->battery.properties = sony_battery_props;
        sc->battery.num_properties = ARRAY_SIZE(sony_battery_props);
        sc->battery.get_property = sony_battery_get_property;
        sc->battery.type = POWER_SUPPLY_TYPE_BATTERY;
        sc->battery.use_for_apm = 0;
-       sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%lu",
-                                    power_id);
+       sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%pMR",
+                                    sc->mac_address);
        if (!sc->battery.name)
                return -ENOMEM;
 
@@ -1578,6 +1758,52 @@ static int sony_check_add(struct sony_sc *sc)
        return sony_check_add_dev_list(sc);
 }
 
+static int sony_set_device_id(struct sony_sc *sc)
+{
+       int ret;
+
+       /*
+        * Only DualShock 4 or Sixaxis controllers get an id.
+        * All others are set to -1.
+        */
+       if ((sc->quirks & SIXAXIS_CONTROLLER) ||
+           (sc->quirks & DUALSHOCK4_CONTROLLER)) {
+               ret = ida_simple_get(&sony_device_id_allocator, 0, 0,
+                                       GFP_KERNEL);
+               if (ret < 0) {
+                       sc->device_id = -1;
+                       return ret;
+               }
+               sc->device_id = ret;
+       } else {
+               sc->device_id = -1;
+       }
+
+       return 0;
+}
+
+static void sony_release_device_id(struct sony_sc *sc)
+{
+       if (sc->device_id >= 0) {
+               ida_simple_remove(&sony_device_id_allocator, sc->device_id);
+               sc->device_id = -1;
+       }
+}
+
+static inline void sony_init_work(struct sony_sc *sc,
+                                       void (*worker)(struct work_struct *))
+{
+       if (!sc->worker_initialized)
+               INIT_WORK(&sc->state_worker, worker);
+
+       sc->worker_initialized = 1;
+}
+
+static inline void sony_cancel_work_sync(struct sony_sc *sc)
+{
+       if (sc->worker_initialized)
+               cancel_work_sync(&sc->state_worker);
+}
 
 static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
@@ -1615,6 +1841,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                return ret;
        }
 
+       ret = sony_set_device_id(sc);
+       if (ret < 0) {
+               hid_err(hdev, "failed to allocate the device id\n");
+               goto err_stop;
+       }
+
        if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
                /*
                 * The Sony Sixaxis does not handle HID Output Reports on the
@@ -1629,8 +1861,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
                hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
                ret = sixaxis_set_operational_usb(hdev);
-               sc->worker_initialized = 1;
-               INIT_WORK(&sc->state_worker, sixaxis_state_worker);
+               sony_init_work(sc, sixaxis_state_worker);
        } else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
                /*
                 * The Sixaxis wants output reports sent on the ctrl endpoint
@@ -1638,8 +1869,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                 */
                hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
                ret = sixaxis_set_operational_bt(hdev);
-               sc->worker_initialized = 1;
-               INIT_WORK(&sc->state_worker, sixaxis_state_worker);
+               sony_init_work(sc, sixaxis_state_worker);
        } else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
                if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
                        /*
@@ -1661,8 +1891,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                if (ret < 0)
                        goto err_stop;
 
-               sc->worker_initialized = 1;
-               INIT_WORK(&sc->state_worker, dualshock4_state_worker);
+               sony_init_work(sc, dualshock4_state_worker);
        } else {
                ret = 0;
        }
@@ -1675,7 +1904,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                goto err_stop;
 
        if (sc->quirks & SONY_LED_SUPPORT) {
-               ret = sony_leds_init(hdev);
+               ret = sony_leds_init(sc);
                if (ret < 0)
                        goto err_stop;
        }
@@ -1694,7 +1923,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
        }
 
        if (sc->quirks & SONY_FF_SUPPORT) {
-               ret = sony_init_ff(hdev);
+               ret = sony_init_ff(sc);
                if (ret < 0)
                        goto err_close;
        }
@@ -1704,12 +1933,12 @@ err_close:
        hid_hw_close(hdev);
 err_stop:
        if (sc->quirks & SONY_LED_SUPPORT)
-               sony_leds_remove(hdev);
+               sony_leds_remove(sc);
        if (sc->quirks & SONY_BATTERY_SUPPORT)
                sony_battery_remove(sc);
-       if (sc->worker_initialized)
-               cancel_work_sync(&sc->state_worker);
+       sony_cancel_work_sync(sc);
        sony_remove_dev_list(sc);
+       sony_release_device_id(sc);
        hid_hw_stop(hdev);
        return ret;
 }
@@ -1719,18 +1948,19 @@ static void sony_remove(struct hid_device *hdev)
        struct sony_sc *sc = hid_get_drvdata(hdev);
 
        if (sc->quirks & SONY_LED_SUPPORT)
-               sony_leds_remove(hdev);
+               sony_leds_remove(sc);
 
        if (sc->quirks & SONY_BATTERY_SUPPORT) {
                hid_hw_close(hdev);
                sony_battery_remove(sc);
        }
 
-       if (sc->worker_initialized)
-               cancel_work_sync(&sc->state_worker);
+       sony_cancel_work_sync(sc);
 
        sony_remove_dev_list(sc);
 
+       sony_release_device_id(sc);
+
        hid_hw_stop(hdev);
 }
 
@@ -1775,6 +2005,22 @@ static struct hid_driver sony_driver = {
        .report_fixup  = sony_report_fixup,
        .raw_event     = sony_raw_event
 };
-module_hid_driver(sony_driver);
+
+static int __init sony_init(void)
+{
+       dbg_hid("Sony:%s\n", __func__);
+
+       return hid_register_driver(&sony_driver);
+}
+
+static void __exit sony_exit(void)
+{
+       dbg_hid("Sony:%s\n", __func__);
+
+       ida_destroy(&sony_device_id_allocator);
+       hid_unregister_driver(&sony_driver);
+}
+module_init(sony_init);
+module_exit(sony_exit);
 
 MODULE_LICENSE("GPL");
index a97c788..134be89 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * ThingM blink(1) USB RGB LED driver
  *
- * Copyright 2013 Savoir-faire Linux Inc.
+ * Copyright 2013-2014 Savoir-faire Linux Inc.
  *     Vivien Didelot <vivien.didelot@savoirfairelinux.com>
  *
  * This program is free software; you can redistribute it and/or
  */
 
 #include <linux/hid.h>
+#include <linux/hidraw.h>
 #include <linux/leds.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
 
 #include "hid-ids.h"
 
-#define BLINK1_CMD_SIZE                9
+#define REPORT_ID      1
+#define REPORT_SIZE    9
 
-#define blink1_rgb_to_r(rgb)   ((rgb & 0xFF0000) >> 16)
-#define blink1_rgb_to_g(rgb)   ((rgb & 0x00FF00) >> 8)
-#define blink1_rgb_to_b(rgb)   ((rgb & 0x0000FF) >> 0)
+/* Firmware major number of supported devices */
+#define THINGM_MAJOR_MK1       '1'
+#define THINGM_MAJOR_MK2       '2'
 
-/**
- * struct blink1_data - blink(1) device specific data
- * @hdev:              HID device.
- * @led_cdev:          LED class instance.
- * @rgb:               8-bit per channel RGB notation.
- * @fade:              fade time in hundredths of a second.
- * @brightness:                brightness coefficient.
- * @play:              play/pause in-memory patterns.
- */
-struct blink1_data {
+struct thingm_fwinfo {
+       char major;
+       unsigned numrgb;
+       unsigned first;
+};
+
+static const struct thingm_fwinfo thingm_fwinfo[] = {
+       {
+               .major = THINGM_MAJOR_MK1,
+               .numrgb = 1,
+               .first = 0,
+       }, {
+               .major = THINGM_MAJOR_MK2,
+               .numrgb = 2,
+               .first = 1,
+       }
+};
+
+/* A red, green or blue channel, part of an RGB chip */
+struct thingm_led {
+       struct thingm_rgb *rgb;
+       struct led_classdev ldev;
+       char name[32];
+};
+
+/* Basically a WS2812 5050 RGB LED chip */
+struct thingm_rgb {
+       struct thingm_device *tdev;
+       struct thingm_led red;
+       struct thingm_led green;
+       struct thingm_led blue;
+       struct work_struct work;
+       u8 num;
+};
+
+struct thingm_device {
        struct hid_device *hdev;
-       struct led_classdev led_cdev;
-       u32 rgb;
-       u16 fade;
-       u8 brightness;
-       bool play;
+       struct {
+               char major;
+               char minor;
+       } version;
+       const struct thingm_fwinfo *fwinfo;
+       struct mutex lock;
+       struct thingm_rgb *rgb;
 };
 
-static int blink1_send_command(struct blink1_data *data,
-               u8 buf[BLINK1_CMD_SIZE])
+static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
 {
        int ret;
 
-       hid_dbg(data->hdev, "command: %d%c%.2x%.2x%.2x%.2x%.2x%.2x%.2x\n",
+       hid_dbg(tdev->hdev, "-> %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n",
                        buf[0], buf[1], buf[2], buf[3], buf[4],
                        buf[5], buf[6], buf[7], buf[8]);
 
-       ret = hid_hw_raw_request(data->hdev, buf[0], buf, BLINK1_CMD_SIZE,
-                                HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+       ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
+                       HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 
        return ret < 0 ? ret : 0;
 }
 
-static int blink1_update_color(struct blink1_data *data)
+static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
 {
-       u8 buf[BLINK1_CMD_SIZE] = { 1, 'n', 0, 0, 0, 0, 0, 0, 0 };
-
-       if (data->brightness) {
-               unsigned int coef = DIV_ROUND_CLOSEST(255, data->brightness);
+       int ret;
 
-               buf[2] = DIV_ROUND_CLOSEST(blink1_rgb_to_r(data->rgb), coef);
-               buf[3] = DIV_ROUND_CLOSEST(blink1_rgb_to_g(data->rgb), coef);
-               buf[4] = DIV_ROUND_CLOSEST(blink1_rgb_to_b(data->rgb), coef);
-       }
+       ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
+                       HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+       if (ret < 0)
+               return ret;
 
-       if (data->fade) {
-               buf[1] = 'c';
-               buf[5] = (data->fade & 0xFF00) >> 8;
-               buf[6] = (data->fade & 0x00FF);
-       }
+       hid_dbg(tdev->hdev, "<- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n",
+                       buf[0], buf[1], buf[2], buf[3], buf[4],
+                       buf[5], buf[6], buf[7], buf[8]);
 
-       return blink1_send_command(data, buf);
+       return 0;
 }
 
-static void blink1_led_set(struct led_classdev *led_cdev,
-               enum led_brightness brightness)
+static int thingm_version(struct thingm_device *tdev)
 {
-       struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent);
+       u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 };
+       int err;
 
-       data->brightness = brightness;
-       if (blink1_update_color(data))
-               hid_err(data->hdev, "failed to update color\n");
-}
+       err = thingm_send(tdev, buf);
+       if (err)
+               return err;
 
-static enum led_brightness blink1_led_get(struct led_classdev *led_cdev)
-{
-       struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent);
+       err = thingm_recv(tdev, buf);
+       if (err)
+               return err;
 
-       return data->brightness;
+       tdev->version.major = buf[3];
+       tdev->version.minor = buf[4];
+
+       return 0;
 }
 
-static ssize_t blink1_show_rgb(struct device *dev,
-               struct device_attribute *attr, char *buf)
+static int thingm_write_color(struct thingm_rgb *rgb)
 {
-       struct blink1_data *data = dev_get_drvdata(dev->parent);
+       u8 buf[REPORT_SIZE] = { REPORT_ID, 'c', 0, 0, 0, 0, 0, rgb->num, 0 };
 
-       return sprintf(buf, "%.6X\n", data->rgb);
+       buf[2] = rgb->red.ldev.brightness;
+       buf[3] = rgb->green.ldev.brightness;
+       buf[4] = rgb->blue.ldev.brightness;
+
+       return thingm_send(rgb->tdev, buf);
 }
 
-static ssize_t blink1_store_rgb(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
+static void thingm_work(struct work_struct *work)
 {
-       struct blink1_data *data = dev_get_drvdata(dev->parent);
-       long unsigned int rgb;
-       int ret;
+       struct thingm_rgb *rgb = container_of(work, struct thingm_rgb, work);
 
-       ret = kstrtoul(buf, 16, &rgb);
-       if (ret)
-               return ret;
-
-       /* RGB triplet notation is 24-bit hexadecimal */
-       if (rgb > 0xFFFFFF)
-               return -EINVAL;
+       mutex_lock(&rgb->tdev->lock);
 
-       data->rgb = rgb;
-       ret = blink1_update_color(data);
+       if (thingm_write_color(rgb))
+               hid_err(rgb->tdev->hdev, "failed to write color\n");
 
-       return ret ? ret : count;
+       mutex_unlock(&rgb->tdev->lock);
 }
 
-static DEVICE_ATTR(rgb, S_IRUGO | S_IWUSR, blink1_show_rgb, blink1_store_rgb);
-
-static ssize_t blink1_show_fade(struct device *dev,
-               struct device_attribute *attr, char *buf)
+static void thingm_led_set(struct led_classdev *ldev,
+               enum led_brightness brightness)
 {
-       struct blink1_data *data = dev_get_drvdata(dev->parent);
+       struct thingm_led *led = container_of(ldev, struct thingm_led, ldev);
 
-       return sprintf(buf, "%d\n", data->fade * 10);
+       /* the ledclass has already stored the brightness value */
+       schedule_work(&led->rgb->work);
 }
 
-static ssize_t blink1_store_fade(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
+static int thingm_init_rgb(struct thingm_rgb *rgb)
 {
-       struct blink1_data *data = dev_get_drvdata(dev->parent);
-       long unsigned int fade;
-       int ret;
+       const int minor = ((struct hidraw *) rgb->tdev->hdev->hidraw)->minor;
+       int err;
+
+       /* Register the red diode */
+       snprintf(rgb->red.name, sizeof(rgb->red.name),
+                       "thingm%d:red:led%d", minor, rgb->num);
+       rgb->red.ldev.name = rgb->red.name;
+       rgb->red.ldev.max_brightness = 255;
+       rgb->red.ldev.brightness_set = thingm_led_set;
+       rgb->red.rgb = rgb;
+
+       err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->red.ldev);
+       if (err)
+               return err;
+
+       /* Register the green diode */
+       snprintf(rgb->green.name, sizeof(rgb->green.name),
+                       "thingm%d:green:led%d", minor, rgb->num);
+       rgb->green.ldev.name = rgb->green.name;
+       rgb->green.ldev.max_brightness = 255;
+       rgb->green.ldev.brightness_set = thingm_led_set;
+       rgb->green.rgb = rgb;
+
+       err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->green.ldev);
+       if (err)
+               goto unregister_red;
+
+       /* Register the blue diode */
+       snprintf(rgb->blue.name, sizeof(rgb->blue.name),
+                       "thingm%d:blue:led%d", minor, rgb->num);
+       rgb->blue.ldev.name = rgb->blue.name;
+       rgb->blue.ldev.max_brightness = 255;
+       rgb->blue.ldev.brightness_set = thingm_led_set;
+       rgb->blue.rgb = rgb;
+
+       err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->blue.ldev);
+       if (err)
+               goto unregister_green;
+
+       INIT_WORK(&rgb->work, thingm_work);
 
-       ret = kstrtoul(buf, 10, &fade);
-       if (ret)
-               return ret;
+       return 0;
 
-       /* blink(1) accepts 16-bit fade time, number of 10ms ticks */
-       fade = DIV_ROUND_CLOSEST(fade, 10);
-       if (fade > 65535)
-               return -EINVAL;
+unregister_green:
+       led_classdev_unregister(&rgb->green.ldev);
 
-       data->fade = fade;
+unregister_red:
+       led_classdev_unregister(&rgb->red.ldev);
 
-       return count;
+       return err;
 }
 
-static DEVICE_ATTR(fade, S_IRUGO | S_IWUSR,
-               blink1_show_fade, blink1_store_fade);
-
-static ssize_t blink1_show_play(struct device *dev,
-               struct device_attribute *attr, char *buf)
+static void thingm_remove_rgb(struct thingm_rgb *rgb)
 {
-       struct blink1_data *data = dev_get_drvdata(dev->parent);
-
-       return sprintf(buf, "%d\n", data->play);
+       flush_work(&rgb->work);
+       led_classdev_unregister(&rgb->red.ldev);
+       led_classdev_unregister(&rgb->green.ldev);
+       led_classdev_unregister(&rgb->blue.ldev);
 }
 
-static ssize_t blink1_store_play(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
+static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
-       struct blink1_data *data = dev_get_drvdata(dev->parent);
-       u8 cmd[BLINK1_CMD_SIZE] = { 1, 'p', 0, 0, 0, 0, 0, 0, 0 };
-       long unsigned int play;
-       int ret;
+       struct thingm_device *tdev;
+       int i, err;
 
-       ret = kstrtoul(buf, 10, &play);
-       if (ret)
-               return ret;
+       tdev = devm_kzalloc(&hdev->dev, sizeof(struct thingm_device),
+                       GFP_KERNEL);
+       if (!tdev)
+               return -ENOMEM;
 
-       data->play = !!play;
-       cmd[2] = data->play;
-       ret = blink1_send_command(data, cmd);
+       tdev->hdev = hdev;
+       hid_set_drvdata(hdev, tdev);
 
-       return ret ? ret : count;
-}
-
-static DEVICE_ATTR(play, S_IRUGO | S_IWUSR,
-               blink1_show_play, blink1_store_play);
+       err = hid_parse(hdev);
+       if (err)
+               goto error;
 
-static const struct attribute_group blink1_sysfs_group = {
-       .attrs = (struct attribute *[]) {
-               &dev_attr_rgb.attr,
-               &dev_attr_fade.attr,
-               &dev_attr_play.attr,
-               NULL
-       },
-};
+       err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+       if (err)
+               goto error;
 
-static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
-{
-       struct blink1_data *data;
-       struct led_classdev *led;
-       char led_name[13];
-       int ret;
+       mutex_init(&tdev->lock);
 
-       data = devm_kzalloc(&hdev->dev, sizeof(struct blink1_data), GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
+       err = thingm_version(tdev);
+       if (err)
+               goto stop;
 
-       hid_set_drvdata(hdev, data);
-       data->hdev = hdev;
-       data->rgb = 0xFFFFFF; /* set a default white color */
+       hid_dbg(hdev, "firmware version: %c.%c\n",
+                       tdev->version.major, tdev->version.minor);
 
-       ret = hid_parse(hdev);
-       if (ret)
-               goto error;
+       for (i = 0; i < ARRAY_SIZE(thingm_fwinfo) && !tdev->fwinfo; ++i)
+               if (thingm_fwinfo[i].major == tdev->version.major)
+                       tdev->fwinfo = &thingm_fwinfo[i];
 
-       ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
-       if (ret)
-               goto error;
+       if (!tdev->fwinfo) {
+               hid_err(hdev, "unsupported firmware %c\n", tdev->version.major);
+               goto stop;
+       }
 
-       /* blink(1) serial numbers range is 0x1A001000 to 0x1A002FFF */
-       led = &data->led_cdev;
-       snprintf(led_name, sizeof(led_name), "blink1::%s", hdev->uniq + 4);
-       led->name = led_name;
-       led->brightness_set = blink1_led_set;
-       led->brightness_get = blink1_led_get;
-       ret = led_classdev_register(&hdev->dev, led);
-       if (ret)
+       tdev->rgb = devm_kzalloc(&hdev->dev,
+                       sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb,
+                       GFP_KERNEL);
+       if (!tdev->rgb) {
+               err = -ENOMEM;
                goto stop;
+       }
 
-       ret = sysfs_create_group(&led->dev->kobj, &blink1_sysfs_group);
-       if (ret)
-               goto remove_led;
+       for (i = 0; i < tdev->fwinfo->numrgb; ++i) {
+               struct thingm_rgb *rgb = tdev->rgb + i;
+
+               rgb->tdev = tdev;
+               rgb->num = tdev->fwinfo->first + i;
+               err = thingm_init_rgb(rgb);
+               if (err) {
+                       while (--i >= 0)
+                               thingm_remove_rgb(tdev->rgb + i);
+                       goto stop;
+               }
+       }
 
        return 0;
-
-remove_led:
-       led_classdev_unregister(led);
 stop:
        hid_hw_stop(hdev);
 error:
-       return ret;
+       return err;
 }
 
 static void thingm_remove(struct hid_device *hdev)
 {
-       struct blink1_data *data = hid_get_drvdata(hdev);
-       struct led_classdev *led = &data->led_cdev;
+       struct thingm_device *tdev = hid_get_drvdata(hdev);
+       int i;
+
+       for (i = 0; i < tdev->fwinfo->numrgb; ++i)
+               thingm_remove_rgb(tdev->rgb + i);
 
-       sysfs_remove_group(&led->dev->kobj, &blink1_sysfs_group);
-       led_classdev_unregister(led);
        hid_hw_stop(hdev);
 }
 
index b50860d..21aafc8 100644 (file)
@@ -807,34 +807,18 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
        unsigned int dsize;
        int ret;
 
-       /* Fetch the length of HID description, retrieve the 4 first bytes:
-        * bytes 0-1 -> length
-        * bytes 2-3 -> bcdVersion (has to be 1.00) */
-       ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, 4);
-
-       i2c_hid_dbg(ihid, "%s, ihid->hdesc_buffer: %4ph\n", __func__,
-                       ihid->hdesc_buffer);
-
+       /* i2c hid fetch using a fixed descriptor size (30 bytes) */
+       i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
+       ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer,
+                               sizeof(struct i2c_hid_desc));
        if (ret) {
-               dev_err(&client->dev,
-                       "unable to fetch the size of HID descriptor (ret=%d)\n",
-                       ret);
-               return -ENODEV;
-       }
-
-       dsize = le16_to_cpu(hdesc->wHIDDescLength);
-       /*
-        * the size of the HID descriptor should at least contain
-        * its size and the bcdVersion (4 bytes), and should not be greater
-        * than sizeof(struct i2c_hid_desc) as we directly fill this struct
-        * through i2c_hid_command.
-        */
-       if (dsize < 4 || dsize > sizeof(struct i2c_hid_desc)) {
-               dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
-                       dsize);
+               dev_err(&client->dev, "hid_descr_cmd failed\n");
                return -ENODEV;
        }
 
+       /* Validate the length of HID descriptor, the 4 first bytes:
+        * bytes 0-1 -> length
+        * bytes 2-3 -> bcdVersion (has to be 1.00) */
        /* check bcdVersion == 1.0 */
        if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
                dev_err(&client->dev,
@@ -843,17 +827,14 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
                return -ENODEV;
        }
 
-       i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
-
-       ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer,
-                               dsize);
-       if (ret) {
-               dev_err(&client->dev, "hid_descr_cmd Fail\n");
+       /* Descriptor length should be 30 bytes as per the specification */
+       dsize = le16_to_cpu(hdesc->wHIDDescLength);
+       if (dsize != sizeof(struct i2c_hid_desc)) {
+               dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
+                       dsize);
                return -ENODEV;
        }
-
        i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer);
-
        return 0;
 }
 
index 0d078c3..0cb92e3 100644 (file)
@@ -441,12 +441,11 @@ static int uhid_dev_create2(struct uhid_device *uhid,
        if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)
                return -EINVAL;
 
-       uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL);
+       uhid->rd_data = kmemdup(ev->u.create2.rd_data, uhid->rd_size,
+                               GFP_KERNEL);
        if (!uhid->rd_data)
                return -ENOMEM;
 
-       memcpy(uhid->rd_data, ev->u.create2.rd_data, uhid->rd_size);
-
        hid = hid_allocate_device();
        if (IS_ERR(hid)) {
                ret = PTR_ERR(hid);
index 8e4ddb3..59badc1 100644 (file)
@@ -115,6 +115,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS1, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS },
index 63b3d4e..4044ff2 100644 (file)
@@ -96,7 +96,7 @@ static struct irq_domain_ops icoll_irq_domain_ops = {
        .xlate = irq_domain_xlate_onecell,
 };
 
-static void __init icoll_of_init(struct device_node *np,
+static int __init icoll_of_init(struct device_node *np,
                          struct device_node *interrupt_parent)
 {
        icoll_base = of_iomap(np, 0);
@@ -110,6 +110,6 @@ static void __init icoll_of_init(struct device_node *np,
 
        icoll_domain = irq_domain_add_linear(np, ICOLL_NUM_IRQS,
                                             &icoll_irq_domain_ops, NULL);
-       WARN_ON(!icoll_domain);
+       return icoll_domain ? 0 : -ENODEV;
 }
 IRQCHIP_DECLARE(mxs, "fsl,icoll", icoll_of_init);
index bbcc944..78a6acc 100644 (file)
@@ -1323,8 +1323,7 @@ static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = {
 };
 
 int __init s3c2410_init_intc_of(struct device_node *np,
-                       struct device_node *interrupt_parent,
-                       struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl)
+                       struct device_node *interrupt_parent)
 {
        return s3c_init_intc_of(np, interrupt_parent,
                                s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl));
@@ -1346,8 +1345,7 @@ static struct s3c24xx_irq_of_ctrl s3c2416_ctrl[] = {
 };
 
 int __init s3c2416_init_intc_of(struct device_node *np,
-                       struct device_node *interrupt_parent,
-                       struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl)
+                       struct device_node *interrupt_parent)
 {
        return s3c_init_intc_of(np, interrupt_parent,
                                s3c2416_ctrl, ARRAY_SIZE(s3c2416_ctrl));
index cad3e24..0fe2f71 100644 (file)
  * special section.
  */
 static const struct of_device_id
-irqchip_of_match_end __used __section(__irqchip_of_end);
+irqchip_of_match_end __used __section(__irqchip_of_table_end);
 
-extern struct of_device_id __irqchip_begin[];
+extern struct of_device_id __irqchip_of_table[];
 
 void __init irqchip_init(void)
 {
-       of_irq_init(__irqchip_begin);
+       of_irq_init(__irqchip_of_table);
 }
index e445ba2..0f6486d 100644 (file)
@@ -11,6 +11,8 @@
 #ifndef _IRQCHIP_H
 #define _IRQCHIP_H
 
+#include <linux/of.h>
+
 /*
  * This macro must be used by the different irqchip drivers to declare
  * the association between their DT compatible string and their
@@ -21,9 +23,6 @@
  * @compstr: compatible string of the irqchip driver
  * @fn: initialization function
  */
-#define IRQCHIP_DECLARE(name,compstr,fn)                               \
-       static const struct of_device_id irqchip_of_match_##name        \
-       __used __section(__irqchip_of_table)                            \
-       = { .compatible = compstr, .data = fn }
+#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)
 
 #endif
index 39e7177..089841c 100644 (file)
@@ -479,6 +479,8 @@ config LEDS_OT200
          This option enables support for the LEDs on the Bachmann OT200.
          Say Y to enable LEDs on the Bachmann OT200.
 
+comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)"
+
 config LEDS_BLINKM
        tristate "LED support for the BlinkM I2C RGB LED"
        depends on LEDS_CLASS
index 1bdc0e7..11d2bea 100644 (file)
 #define USB_PID_ELGATO_EYETV_DTT_2                     0x003f
 #define USB_PID_ELGATO_EYETV_DTT_Dlx                   0x0020
 #define USB_PID_ELGATO_EYETV_SAT                       0x002a
+#define USB_PID_ELGATO_EYETV_SAT_V2                    0x0025
 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD                0x5000
 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM                0x5001
 #define USB_PID_FRIIO_WHITE                            0x0001
 #define USB_PID_TVWAY_PLUS                             0x0002
 #define USB_PID_SVEON_STV20                            0xe39d
+#define USB_PID_SVEON_STV20_RTL2832U                   0xd39d
 #define USB_PID_SVEON_STV22                            0xe401
 #define USB_PID_SVEON_STV22_IT9137                     0xe411
 #define USB_PID_AZUREWAVE_AZ6027                       0x3275
 #define USB_PID_CTVDIGDUAL_V2                          0xe410
 #define USB_PID_PCTV_2002E                              0x025c
 #define USB_PID_PCTV_2002E_SE                           0x025d
+#define USB_PID_SVEON_STV27                             0xd3af
 #endif
index 025fc54..1469d44 100644 (file)
@@ -446,6 +446,13 @@ config DVB_RTL2832
        help
          Say Y when you want to support this frontend.
 
+config DVB_SI2168
+       tristate "Silicon Labs Si2168"
+       depends on DVB_CORE && I2C && I2C_MUX
+       default m if !MEDIA_SUBDRV_AUTOSELECT
+       help
+         Say Y when you want to support this frontend.
+
 comment "DVB-C (cable) frontends"
        depends on DVB_CORE
 
index 282aba2..dda0bee 100644 (file)
@@ -78,6 +78,7 @@ obj-$(CONFIG_DVB_AF9013) += af9013.o
 obj-$(CONFIG_DVB_CX24116) += cx24116.o
 obj-$(CONFIG_DVB_CX24117) += cx24117.o
 obj-$(CONFIG_DVB_SI21XX) += si21xx.o
+obj-$(CONFIG_DVB_SI2168) += si2168.o
 obj-$(CONFIG_DVB_STV0288) += stv0288.o
 obj-$(CONFIG_DVB_STB6000) += stb6000.o
 obj-$(CONFIG_DVB_S921) += s921.o
diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c
new file mode 100644 (file)
index 0000000..8637d2e
--- /dev/null
@@ -0,0 +1,760 @@
+/*
+ * Silicon Labs Si2168 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ */
+
+#include "si2168_priv.h"
+
+static const struct dvb_frontend_ops si2168_ops;
+
+/* execute firmware command */
+static int si2168_cmd_execute(struct si2168 *s, struct si2168_cmd *cmd)
+{
+       int ret;
+       unsigned long timeout;
+
+       mutex_lock(&s->i2c_mutex);
+
+       if (cmd->wlen) {
+               /* write cmd and args for firmware */
+               ret = i2c_master_send(s->client, cmd->args, cmd->wlen);
+               if (ret < 0) {
+                       goto err_mutex_unlock;
+               } else if (ret != cmd->wlen) {
+                       ret = -EREMOTEIO;
+                       goto err_mutex_unlock;
+               }
+       }
+
+       if (cmd->rlen) {
+               /* wait cmd execution terminate */
+               #define TIMEOUT 50
+               timeout = jiffies + msecs_to_jiffies(TIMEOUT);
+               while (!time_after(jiffies, timeout)) {
+                       ret = i2c_master_recv(s->client, cmd->args, cmd->rlen);
+                       if (ret < 0) {
+                               goto err_mutex_unlock;
+                       } else if (ret != cmd->rlen) {
+                               ret = -EREMOTEIO;
+                               goto err_mutex_unlock;
+                       }
+
+                       /* firmware ready? */
+                       if ((cmd->args[0] >> 7) & 0x01)
+                               break;
+               }
+
+               dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n",
+                               __func__,
+                               jiffies_to_msecs(jiffies) -
+                               (jiffies_to_msecs(timeout) - TIMEOUT));
+
+               if (!(cmd->args[0] >> 7) & 0x01) {
+                       ret = -ETIMEDOUT;
+                       goto err_mutex_unlock;
+               }
+       }
+
+       ret = 0;
+
+err_mutex_unlock:
+       mutex_unlock(&s->i2c_mutex);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       return ret;
+}
+
+static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+       struct si2168 *s = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       int ret;
+       struct si2168_cmd cmd;
+
+       *status = 0;
+
+       if (!s->active) {
+               ret = -EAGAIN;
+               goto err;
+       }
+
+       switch (c->delivery_system) {
+       case SYS_DVBT:
+               cmd.args[0] = 0xa0;
+               cmd.args[1] = 0x01;
+               cmd.wlen = 2;
+               cmd.rlen = 13;
+               break;
+       case SYS_DVBC_ANNEX_A:
+               cmd.args[0] = 0x90;
+               cmd.args[1] = 0x01;
+               cmd.wlen = 2;
+               cmd.rlen = 9;
+               break;
+       case SYS_DVBT2:
+               cmd.args[0] = 0x50;
+               cmd.args[1] = 0x01;
+               cmd.wlen = 2;
+               cmd.rlen = 14;
+               break;
+       default:
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       /*
+        * Possible values seen, in order from strong signal to weak:
+        * 16 0001 0110 full lock
+        * 1e 0001 1110 partial lock
+        * 1a 0001 1010 partial lock
+        * 18 0001 1000 no lock
+        *
+        * [b3:b1] lock bits
+        * [b4] statistics ready? Set in a few secs after lock is gained.
+        */
+
+       switch ((cmd.args[2] >> 1) & 0x03) {
+       case 0x01:
+               *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+               break;
+       case 0x03:
+               *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
+                               FE_HAS_SYNC | FE_HAS_LOCK;
+               break;
+       }
+
+       s->fe_status = *status;
+
+       dev_dbg(&s->client->dev, "%s: status=%02x args=%*ph\n",
+                       __func__, *status, cmd.rlen, cmd.args);
+
+       return 0;
+err:
+       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       return ret;
+}
+
+static int si2168_set_frontend(struct dvb_frontend *fe)
+{
+       struct si2168 *s = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       int ret;
+       struct si2168_cmd cmd;
+       u8 bandwidth, delivery_system;
+
+       dev_dbg(&s->client->dev,
+                       "%s: delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u\n",
+                       __func__, c->delivery_system, c->modulation,
+                       c->frequency, c->bandwidth_hz, c->symbol_rate,
+                       c->inversion);
+
+       if (!s->active) {
+               ret = -EAGAIN;
+               goto err;
+       }
+
+       switch (c->delivery_system) {
+       case SYS_DVBT:
+               delivery_system = 0x20;
+               break;
+       case SYS_DVBC_ANNEX_A:
+               delivery_system = 0x30;
+               break;
+       case SYS_DVBT2:
+               delivery_system = 0x70;
+               break;
+       default:
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (c->bandwidth_hz <= 5000000)
+               bandwidth = 0x05;
+       else if (c->bandwidth_hz <= 6000000)
+               bandwidth = 0x06;
+       else if (c->bandwidth_hz <= 7000000)
+               bandwidth = 0x07;
+       else if (c->bandwidth_hz <= 8000000)
+               bandwidth = 0x08;
+       else if (c->bandwidth_hz <= 9000000)
+               bandwidth = 0x09;
+       else if (c->bandwidth_hz <= 10000000)
+               bandwidth = 0x0a;
+       else
+               bandwidth = 0x0f;
+
+       /* program tuner */
+       if (fe->ops.tuner_ops.set_params) {
+               ret = fe->ops.tuner_ops.set_params(fe);
+               if (ret)
+                       goto err;
+       }
+
+       memcpy(cmd.args, "\x88\x02\x02\x02\x02", 5);
+       cmd.wlen = 5;
+       cmd.rlen = 5;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       /* that has no big effect */
+       if (c->delivery_system == SYS_DVBT)
+               memcpy(cmd.args, "\x89\x21\x06\x11\xff\x98", 6);
+       else if (c->delivery_system == SYS_DVBC_ANNEX_A)
+               memcpy(cmd.args, "\x89\x21\x06\x11\x89\xf0", 6);
+       else if (c->delivery_system == SYS_DVBT2)
+               memcpy(cmd.args, "\x89\x21\x06\x11\x89\x20", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 3;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x51\x03", 2);
+       cmd.wlen = 2;
+       cmd.rlen = 12;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x12\x08\x04", 3);
+       cmd.wlen = 3;
+       cmd.rlen = 3;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x01\x04\x00\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x03\x10\x17\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x02\x10\x15\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x0c\x10\x12\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x06\x10\x24\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x0b\x10\x88\x13", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x07\x10\x00\x24", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x0a\x10\x00\x00", 6);
+       cmd.args[4] = delivery_system | bandwidth;
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x04\x10\x15\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x05\x10\xa1\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x0d\x10\xd0\x02", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x01\x10\x00\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x18", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x08\x10\xd7\x15", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x04\x03\x00\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x03\x03\x00\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x08\x03\x00\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x07\x03\x01\x02", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x06\x03\x00\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x05\x03\x00\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x01\x03\x0c\x40", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x01\x10\x16\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       memcpy(cmd.args, "\x14\x00\x01\x12\x00\x00", 6);
+       cmd.wlen = 6;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       cmd.args[0] = 0x85;
+       cmd.wlen = 1;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       s->delivery_system = c->delivery_system;
+
+       return 0;
+err:
+       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       return ret;
+}
+
+static int si2168_init(struct dvb_frontend *fe)
+{
+       struct si2168 *s = fe->demodulator_priv;
+       int ret, len, remaining;
+       const struct firmware *fw = NULL;
+       u8 *fw_file = SI2168_FIRMWARE;
+       const unsigned int i2c_wr_max = 8;
+       struct si2168_cmd cmd;
+
+       dev_dbg(&s->client->dev, "%s:\n", __func__);
+
+       cmd.args[0] = 0x13;
+       cmd.wlen = 1;
+       cmd.rlen = 0;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       cmd.args[0] = 0xc0;
+       cmd.args[1] = 0x12;
+       cmd.args[2] = 0x00;
+       cmd.args[3] = 0x0c;
+       cmd.args[4] = 0x00;
+       cmd.args[5] = 0x0d;
+       cmd.args[6] = 0x16;
+       cmd.args[7] = 0x00;
+       cmd.args[8] = 0x00;
+       cmd.args[9] = 0x00;
+       cmd.args[10] = 0x00;
+       cmd.args[11] = 0x00;
+       cmd.args[12] = 0x00;
+       cmd.wlen = 13;
+       cmd.rlen = 0;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       cmd.args[0] = 0xc0;
+       cmd.args[1] = 0x06;
+       cmd.args[2] = 0x01;
+       cmd.args[3] = 0x0f;
+       cmd.args[4] = 0x00;
+       cmd.args[5] = 0x20;
+       cmd.args[6] = 0x20;
+       cmd.args[7] = 0x01;
+       cmd.wlen = 8;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       cmd.args[0] = 0x02;
+       cmd.wlen = 1;
+       cmd.rlen = 13;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       cmd.args[0] = 0x05;
+       cmd.args[1] = 0x00;
+       cmd.args[2] = 0xaa;
+       cmd.args[3] = 0x4d;
+       cmd.args[4] = 0x56;
+       cmd.args[5] = 0x40;
+       cmd.args[6] = 0x00;
+       cmd.args[7] = 0x00;
+       cmd.wlen = 8;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       /* cold state - try to download firmware */
+       dev_info(&s->client->dev, "%s: found a '%s' in cold state\n",
+                       KBUILD_MODNAME, si2168_ops.info.name);
+
+       /* request the firmware, this will block and timeout */
+       ret = request_firmware(&fw, fw_file, &s->client->dev);
+       if (ret) {
+               dev_err(&s->client->dev, "%s: firmare file '%s' not found\n",
+                               KBUILD_MODNAME, fw_file);
+               goto err;
+       }
+
+       dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n",
+                       KBUILD_MODNAME, fw_file);
+
+       for (remaining = fw->size; remaining > 0; remaining -= i2c_wr_max) {
+               len = remaining;
+               if (len > i2c_wr_max)
+                       len = i2c_wr_max;
+
+               memcpy(cmd.args, &fw->data[fw->size - remaining], len);
+               cmd.wlen = len;
+               cmd.rlen = 1;
+               ret = si2168_cmd_execute(s, &cmd);
+               if (ret) {
+                       dev_err(&s->client->dev,
+                                       "%s: firmware download failed=%d\n",
+                                       KBUILD_MODNAME, ret);
+                       goto err;
+               }
+       }
+
+       release_firmware(fw);
+       fw = NULL;
+
+       cmd.args[0] = 0x01;
+       cmd.args[1] = 0x01;
+       cmd.wlen = 2;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       dev_info(&s->client->dev, "%s: found a '%s' in warm state\n",
+                       KBUILD_MODNAME, si2168_ops.info.name);
+
+       s->active = true;
+
+       return 0;
+err:
+       if (fw)
+               release_firmware(fw);
+
+       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       return ret;
+}
+
+static int si2168_sleep(struct dvb_frontend *fe)
+{
+       struct si2168 *s = fe->demodulator_priv;
+
+       dev_dbg(&s->client->dev, "%s:\n", __func__);
+
+       s->active = false;
+
+       return 0;
+}
+
+static int si2168_get_tune_settings(struct dvb_frontend *fe,
+       struct dvb_frontend_tune_settings *s)
+{
+       s->min_delay_ms = 900;
+
+       return 0;
+}
+
+/*
+ * I2C gate logic
+ * We must use unlocked i2c_transfer() here because I2C lock is already taken
+ * by tuner driver.
+ */
+static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
+{
+       struct si2168 *s = mux_priv;
+       int ret;
+       struct i2c_msg gate_open_msg = {
+               .addr = s->client->addr,
+               .flags = 0,
+               .len = 3,
+               .buf = "\xc0\x0d\x01",
+       };
+
+       mutex_lock(&s->i2c_mutex);
+
+       /* open tuner I2C gate */
+       ret = __i2c_transfer(s->client->adapter, &gate_open_msg, 1);
+       if (ret != 1) {
+               dev_warn(&s->client->dev, "%s: i2c write failed=%d\n",
+                               KBUILD_MODNAME, ret);
+               if (ret >= 0)
+                       ret = -EREMOTEIO;
+       } else {
+               ret = 0;
+       }
+
+       return ret;
+}
+
+static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan)
+{
+       struct si2168 *s = mux_priv;
+       int ret;
+       struct i2c_msg gate_close_msg = {
+               .addr = s->client->addr,
+               .flags = 0,
+               .len = 3,
+               .buf = "\xc0\x0d\x00",
+       };
+
+       /* close tuner I2C gate */
+       ret = __i2c_transfer(s->client->adapter, &gate_close_msg, 1);
+       if (ret != 1) {
+               dev_warn(&s->client->dev, "%s: i2c write failed=%d\n",
+                               KBUILD_MODNAME, ret);
+               if (ret >= 0)
+                       ret = -EREMOTEIO;
+       } else {
+               ret = 0;
+       }
+
+       mutex_unlock(&s->i2c_mutex);
+
+       return ret;
+}
+
+static const struct dvb_frontend_ops si2168_ops = {
+       .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A},
+       .info = {
+               .name = "Silicon Labs Si2168",
+               .caps = FE_CAN_FEC_1_2 |
+                       FE_CAN_FEC_2_3 |
+                       FE_CAN_FEC_3_4 |
+                       FE_CAN_FEC_5_6 |
+                       FE_CAN_FEC_7_8 |
+                       FE_CAN_FEC_AUTO |
+                       FE_CAN_QPSK |
+                       FE_CAN_QAM_16 |
+                       FE_CAN_QAM_32 |
+                       FE_CAN_QAM_64 |
+                       FE_CAN_QAM_128 |
+                       FE_CAN_QAM_256 |
+                       FE_CAN_QAM_AUTO |
+                       FE_CAN_TRANSMISSION_MODE_AUTO |
+                       FE_CAN_GUARD_INTERVAL_AUTO |
+                       FE_CAN_HIERARCHY_AUTO |
+                       FE_CAN_MUTE_TS |
+                       FE_CAN_2G_MODULATION
+       },
+
+       .get_tune_settings = si2168_get_tune_settings,
+
+       .init = si2168_init,
+       .sleep = si2168_sleep,
+
+       .set_frontend = si2168_set_frontend,
+
+       .read_status = si2168_read_status,
+};
+
+static int si2168_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct si2168_config *config = client->dev.platform_data;
+       struct si2168 *s;
+       int ret;
+       struct si2168_cmd cmd;
+
+       dev_dbg(&client->dev, "%s:\n", __func__);
+
+       s = kzalloc(sizeof(struct si2168), GFP_KERNEL);
+       if (!s) {
+               ret = -ENOMEM;
+               dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+               goto err;
+       }
+
+       s->client = client;
+       mutex_init(&s->i2c_mutex);
+
+       /* check if the demod is there */
+       cmd.wlen = 0;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       /* create mux i2c adapter for tuner */
+       s->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, s,
+                       0, 0, 0, si2168_select, si2168_deselect);
+       if (s->adapter == NULL)
+               goto err;
+
+       /* create dvb_frontend */
+       memcpy(&s->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops));
+       s->fe.demodulator_priv = s;
+
+       *config->i2c_adapter = s->adapter;
+       *config->fe = &s->fe;
+
+       i2c_set_clientdata(client, s);
+
+       dev_info(&s->client->dev,
+                       "%s: Silicon Labs Si2168 successfully attached\n",
+                       KBUILD_MODNAME);
+       return 0;
+err:
+       kfree(s);
+       dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
+       return ret;
+}
+
+static int si2168_remove(struct i2c_client *client)
+{
+       struct si2168 *s = i2c_get_clientdata(client);
+
+       dev_dbg(&client->dev, "%s:\n", __func__);
+
+       i2c_del_mux_adapter(s->adapter);
+
+       s->fe.ops.release = NULL;
+       s->fe.demodulator_priv = NULL;
+
+       kfree(s);
+
+       return 0;
+}
+
+static const struct i2c_device_id si2168_id[] = {
+       {"si2168", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, si2168_id);
+
+static struct i2c_driver si2168_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "si2168",
+       },
+       .probe          = si2168_probe,
+       .remove         = si2168_remove,
+       .id_table       = si2168_id,
+};
+
+module_i2c_driver(si2168_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Silicon Labs Si2168 DVB-T/T2/C demodulator driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(SI2168_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/si2168.h b/drivers/media/dvb-frontends/si2168.h
new file mode 100644 (file)
index 0000000..3c5b5ab
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Silicon Labs Si2168 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.
+ */
+
+#ifndef SI2168_H
+#define SI2168_H
+
+#include <linux/dvb/frontend.h>
+/*
+ * I2C address
+ * 0x64
+ */
+struct si2168_config {
+       /*
+        * frontend
+        * returned by driver
+        */
+       struct dvb_frontend **fe;
+
+       /*
+        * tuner I2C adapter
+        * returned by driver
+        */
+       struct i2c_adapter **i2c_adapter;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h
new file mode 100644 (file)
index 0000000..2a343e8
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Silicon Labs Si2168 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.
+ */
+
+#ifndef SI2168_PRIV_H
+#define SI2168_PRIV_H
+
+#include "si2168.h"
+#include "dvb_frontend.h"
+#include <linux/firmware.h>
+#include <linux/i2c-mux.h>
+
+#define SI2168_FIRMWARE "dvb-demod-si2168-01.fw"
+
+/* state struct */
+struct si2168 {
+       struct i2c_client *client;
+       struct i2c_adapter *adapter;
+       struct mutex i2c_mutex;
+       struct dvb_frontend fe;
+       fe_delivery_system_t delivery_system;
+       fe_status_t fe_status;
+       bool active;
+};
+
+/* firmare command struct */
+#define SI2157_ARGLEN      30
+struct si2168_cmd {
+       u8 args[SI2157_ARGLEN];
+       unsigned wlen;
+       unsigned rlen;
+};
+
+#endif
index 1b7ecfd..fada175 100644 (file)
@@ -571,35 +571,6 @@ static const struct v4l2_subdev_core_ops ad9389b_core_ops = {
        .interrupt_service_routine = ad9389b_isr,
 };
 
-/* ------------------------------ PAD OPS ------------------------------ */
-
-static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
-{
-       struct ad9389b_state *state = get_ad9389b_state(sd);
-
-       if (edid->pad != 0)
-               return -EINVAL;
-       if (edid->blocks == 0 || edid->blocks > 256)
-               return -EINVAL;
-       if (!edid->edid)
-               return -EINVAL;
-       if (!state->edid.segments) {
-               v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n");
-               return -ENODATA;
-       }
-       if (edid->start_block >= state->edid.segments * 2)
-               return -E2BIG;
-       if (edid->blocks + edid->start_block >= state->edid.segments * 2)
-               edid->blocks = state->edid.segments * 2 - edid->start_block;
-       memcpy(edid->edid, &state->edid.data[edid->start_block * 128],
-              128 * edid->blocks);
-       return 0;
-}
-
-static const struct v4l2_subdev_pad_ops ad9389b_pad_ops = {
-       .get_edid = ad9389b_get_edid,
-};
-
 /* ------------------------------ VIDEO OPS ------------------------------ */
 
 /* Enable/disable ad9389b output */
@@ -678,6 +649,9 @@ static int ad9389b_g_dv_timings(struct v4l2_subdev *sd,
 static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd,
                                   struct v4l2_enum_dv_timings *timings)
 {
+       if (timings->pad != 0)
+               return -EINVAL;
+
        return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap,
                        NULL, NULL);
 }
@@ -685,6 +659,9 @@ static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd,
 static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd,
                                  struct v4l2_dv_timings_cap *cap)
 {
+       if (cap->pad != 0)
+               return -EINVAL;
+
        *cap = ad9389b_timings_cap;
        return 0;
 }
@@ -693,10 +670,39 @@ static const struct v4l2_subdev_video_ops ad9389b_video_ops = {
        .s_stream = ad9389b_s_stream,
        .s_dv_timings = ad9389b_s_dv_timings,
        .g_dv_timings = ad9389b_g_dv_timings,
+};
+
+/* ------------------------------ PAD OPS ------------------------------ */
+
+static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+       struct ad9389b_state *state = get_ad9389b_state(sd);
+
+       if (edid->pad != 0)
+               return -EINVAL;
+       if (edid->blocks == 0 || edid->blocks > 256)
+               return -EINVAL;
+       if (!state->edid.segments) {
+               v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n");
+               return -ENODATA;
+       }
+       if (edid->start_block >= state->edid.segments * 2)
+               return -E2BIG;
+       if (edid->blocks + edid->start_block >= state->edid.segments * 2)
+               edid->blocks = state->edid.segments * 2 - edid->start_block;
+       memcpy(edid->edid, &state->edid.data[edid->start_block * 128],
+              128 * edid->blocks);
+       return 0;
+}
+
+static const struct v4l2_subdev_pad_ops ad9389b_pad_ops = {
+       .get_edid = ad9389b_get_edid,
        .enum_dv_timings = ad9389b_enum_dv_timings,
        .dv_timings_cap = ad9389b_dv_timings_cap,
 };
 
+/* ------------------------------ AUDIO OPS ------------------------------ */
+
 static int ad9389b_s_audio_stream(struct v4l2_subdev *sd, int enable)
 {
        v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis"));
index 5e638b1..ac1cdbe 100644 (file)
@@ -461,6 +461,7 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
 }
 
 static const struct v4l2_subdev_video_ops adv7180_video_ops = {
+       .s_std = adv7180_s_std,
        .querystd = adv7180_querystd,
        .g_input_status = adv7180_g_input_status,
        .s_routing = adv7180_s_routing,
@@ -472,7 +473,6 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {
 };
 
 static const struct v4l2_subdev_core_ops adv7180_core_ops = {
-       .s_std = adv7180_s_std,
        .s_power = adv7180_s_power,
 };
 
index d45e0e3..df461b0 100644 (file)
@@ -501,8 +501,6 @@ static const struct v4l2_ctrl_ops adv7183_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops adv7183_core_ops = {
        .log_status = adv7183_log_status,
-       .g_std = adv7183_g_std,
-       .s_std = adv7183_s_std,
        .reset = adv7183_reset,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        .g_register = adv7183_g_register,
@@ -511,6 +509,8 @@ static const struct v4l2_subdev_core_ops adv7183_core_ops = {
 };
 
 static const struct v4l2_subdev_video_ops adv7183_video_ops = {
+       .g_std = adv7183_g_std,
+       .s_std = adv7183_s_std,
        .s_routing = adv7183_s_routing,
        .querystd = adv7183_querystd,
        .g_input_status = adv7183_g_input_status,
index 942ca4b..f98acf4 100644 (file)
@@ -597,34 +597,6 @@ static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
        return 0;
 }
 
-static int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
-{
-       struct adv7511_state *state = get_adv7511_state(sd);
-
-       if (edid->pad != 0)
-               return -EINVAL;
-       if ((edid->blocks == 0) || (edid->blocks > 256))
-               return -EINVAL;
-       if (!edid->edid)
-               return -EINVAL;
-       if (!state->edid.segments) {
-               v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n");
-               return -ENODATA;
-       }
-       if (edid->start_block >= state->edid.segments * 2)
-               return -E2BIG;
-       if ((edid->blocks + edid->start_block) >= state->edid.segments * 2)
-               edid->blocks = state->edid.segments * 2 - edid->start_block;
-
-       memcpy(edid->edid, &state->edid.data[edid->start_block * 128],
-                       128 * edid->blocks);
-       return 0;
-}
-
-static const struct v4l2_subdev_pad_ops adv7511_pad_ops = {
-       .get_edid = adv7511_get_edid,
-};
-
 static const struct v4l2_subdev_core_ops adv7511_core_ops = {
        .log_status = adv7511_log_status,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -700,12 +672,18 @@ static int adv7511_g_dv_timings(struct v4l2_subdev *sd,
 static int adv7511_enum_dv_timings(struct v4l2_subdev *sd,
                                   struct v4l2_enum_dv_timings *timings)
 {
+       if (timings->pad != 0)
+               return -EINVAL;
+
        return v4l2_enum_dv_timings_cap(timings, &adv7511_timings_cap, NULL, NULL);
 }
 
 static int adv7511_dv_timings_cap(struct v4l2_subdev *sd,
                                  struct v4l2_dv_timings_cap *cap)
 {
+       if (cap->pad != 0)
+               return -EINVAL;
+
        *cap = adv7511_timings_cap;
        return 0;
 }
@@ -714,8 +692,6 @@ static const struct v4l2_subdev_video_ops adv7511_video_ops = {
        .s_stream = adv7511_s_stream,
        .s_dv_timings = adv7511_s_dv_timings,
        .g_dv_timings = adv7511_g_dv_timings,
-       .enum_dv_timings = adv7511_enum_dv_timings,
-       .dv_timings_cap = adv7511_dv_timings_cap,
 };
 
 /* ------------------------------ AUDIO OPS ------------------------------ */
@@ -797,6 +773,36 @@ static const struct v4l2_subdev_audio_ops adv7511_audio_ops = {
        .s_routing = adv7511_s_routing,
 };
 
+/* ---------------------------- PAD OPS ------------------------------------- */
+
+static int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+       struct adv7511_state *state = get_adv7511_state(sd);
+
+       if (edid->pad != 0)
+               return -EINVAL;
+       if ((edid->blocks == 0) || (edid->blocks > 256))
+               return -EINVAL;
+       if (!state->edid.segments) {
+               v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n");
+               return -ENODATA;
+       }
+       if (edid->start_block >= state->edid.segments * 2)
+               return -E2BIG;
+       if ((edid->blocks + edid->start_block) >= state->edid.segments * 2)
+               edid->blocks = state->edid.segments * 2 - edid->start_block;
+
+       memcpy(edid->edid, &state->edid.data[edid->start_block * 128],
+                       128 * edid->blocks);
+       return 0;
+}
+
+static const struct v4l2_subdev_pad_ops adv7511_pad_ops = {
+       .get_edid = adv7511_get_edid,
+       .enum_dv_timings = adv7511_enum_dv_timings,
+       .dv_timings_cap = adv7511_dv_timings_cap,
+};
+
 /* --------------------- SUBDEV OPS --------------------------------------- */
 
 static const struct v4l2_subdev_ops adv7511_ops = {
index 98cc540..338baa4 100644 (file)
@@ -1673,8 +1673,6 @@ static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
                return -EINVAL;
        if (edid->start_block == 1)
                edid->blocks = 1;
-       if (!edid->edid)
-               return -EINVAL;
 
        if (edid->blocks > state->edid.blocks)
                edid->blocks = state->edid.blocks;
@@ -1761,8 +1759,6 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
                edid->blocks = 2;
                return -E2BIG;
        }
-       if (!edid->edid)
-               return -EINVAL;
 
        v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n",
                        __func__, edid->pad, state->edid.present);
index 636ac08..0d55491 100644 (file)
@@ -1399,6 +1399,9 @@ static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi)
 static int adv7842_enum_dv_timings(struct v4l2_subdev *sd,
                                   struct v4l2_enum_dv_timings *timings)
 {
+       if (timings->pad != 0)
+               return -EINVAL;
+
        return v4l2_enum_dv_timings_cap(timings,
                adv7842_get_dv_timings_cap(sd), adv7842_check_dv_timings, NULL);
 }
@@ -1406,6 +1409,9 @@ static int adv7842_enum_dv_timings(struct v4l2_subdev *sd,
 static int adv7842_dv_timings_cap(struct v4l2_subdev *sd,
                                  struct v4l2_dv_timings_cap *cap)
 {
+       if (cap->pad != 0)
+               return -EINVAL;
+
        *cap = *adv7842_get_dv_timings_cap(sd);
        return 0;
 }
@@ -2000,6 +2006,7 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
        if (irq_status[5] & 0x08) {
                v4l2_dbg(1, debug, sd, "%s: irq %s mode\n", __func__,
                         (io_read(sd, 0x65) & 0x08) ? "HDMI" : "DVI");
+               set_rgb_quantization_range(sd);
                if (handled)
                        *handled = true;
        }
@@ -2029,8 +2036,6 @@ static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
                return -EINVAL;
        if (edid->start_block == 1)
                edid->blocks = 1;
-       if (!edid->edid)
-               return -EINVAL;
 
        switch (edid->pad) {
        case ADV7842_EDID_PORT_A:
@@ -2065,8 +2070,6 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e)
                return -EINVAL;
        if (e->blocks > 2)
                return -E2BIG;
-       if (!e->edid)
-               return -EINVAL;
 
        /* todo, per edid */
        state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15],
@@ -2610,6 +2613,12 @@ static int adv7842_core_init(struct v4l2_subdev *sd)
 
        disable_input(sd);
 
+       /*
+        * Disable I2C access to internal EDID ram from HDMI DDC ports
+        * Disable auto edid enable when leaving powerdown mode
+        */
+       rep_write_and_or(sd, 0x77, 0xd3, 0x20);
+
        /* power */
        io_write(sd, 0x0c, 0x42);   /* Power up part and power down VDP */
        io_write(sd, 0x15, 0x80);   /* Power up pads */
@@ -2690,9 +2699,6 @@ static int adv7842_core_init(struct v4l2_subdev *sd)
 
        enable_input(sd);
 
-       /* disable I2C access to internal EDID ram from HDMI DDC ports */
-       rep_write_and_or(sd, 0x77, 0xf3, 0x00);
-
        if (pdata->hpa_auto) {
                /* HPA auto, HPA 0.5s after Edid set and Cable detect */
                hdmi_write(sd, 0x69, 0x5c);
@@ -2869,8 +2875,6 @@ static const struct v4l2_ctrl_ops adv7842_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops adv7842_core_ops = {
        .log_status = adv7842_log_status,
-       .g_std = adv7842_g_std,
-       .s_std = adv7842_s_std,
        .ioctl = adv7842_ioctl,
        .interrupt_service_routine = adv7842_isr,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -2880,14 +2884,14 @@ static const struct v4l2_subdev_core_ops adv7842_core_ops = {
 };
 
 static const struct v4l2_subdev_video_ops adv7842_video_ops = {
+       .g_std = adv7842_g_std,
+       .s_std = adv7842_s_std,
        .s_routing = adv7842_s_routing,
        .querystd = adv7842_querystd,
        .g_input_status = adv7842_g_input_status,
        .s_dv_timings = adv7842_s_dv_timings,
        .g_dv_timings = adv7842_g_dv_timings,
        .query_dv_timings = adv7842_query_dv_timings,
-       .enum_dv_timings = adv7842_enum_dv_timings,
-       .dv_timings_cap = adv7842_dv_timings_cap,
        .enum_mbus_fmt = adv7842_enum_mbus_fmt,
        .g_mbus_fmt = adv7842_g_mbus_fmt,
        .try_mbus_fmt = adv7842_g_mbus_fmt,
@@ -2897,6 +2901,8 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = {
 static const struct v4l2_subdev_pad_ops adv7842_pad_ops = {
        .get_edid = adv7842_get_edid,
        .set_edid = adv7842_set_edid,
+       .enum_dv_timings = adv7842_enum_dv_timings,
+       .dv_timings_cap = adv7842_dv_timings_cap,
 };
 
 static const struct v4l2_subdev_ops adv7842_ops = {
index 369cf6f..76b334a 100644 (file)
@@ -387,10 +387,10 @@ static const struct v4l2_subdev_core_ops bt819_core_ops = {
        .s_ctrl = v4l2_subdev_s_ctrl,
        .queryctrl = v4l2_subdev_queryctrl,
        .querymenu = v4l2_subdev_querymenu,
-       .s_std = bt819_s_std,
 };
 
 static const struct v4l2_subdev_video_ops bt819_video_ops = {
+       .s_std = bt819_s_std,
        .s_routing = bt819_s_routing,
        .s_stream = bt819_s_stream,
        .querystd = bt819_querystd,
index 2e3771d..e453a3f 100644 (file)
@@ -5041,8 +5041,6 @@ static const struct v4l2_subdev_core_ops cx25840_core_ops = {
        .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
        .queryctrl = v4l2_subdev_queryctrl,
        .querymenu = v4l2_subdev_querymenu,
-       .s_std = cx25840_s_std,
-       .g_std = cx25840_g_std,
        .reset = cx25840_reset,
        .load_fw = cx25840_load_fw,
        .s_io_pin_config = common_s_io_pin_config,
@@ -5067,6 +5065,8 @@ static const struct v4l2_subdev_audio_ops cx25840_audio_ops = {
 };
 
 static const struct v4l2_subdev_video_ops cx25840_video_ops = {
+       .s_std = cx25840_s_std,
+       .g_std = cx25840_g_std,
        .s_routing = cx25840_s_video_routing,
        .s_mbus_fmt = cx25840_s_mbus_fmt,
        .s_stream = cx25840_s_stream,
index c3e94ae..25b81bc 100644 (file)
@@ -648,11 +648,8 @@ static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status)
 
 /* ----------------------------------------------------------------------- */
 
-static const struct v4l2_subdev_core_ops ks0127_core_ops = {
-       .s_std = ks0127_s_std,
-};
-
 static const struct v4l2_subdev_video_ops ks0127_video_ops = {
+       .s_std = ks0127_s_std,
        .s_routing = ks0127_s_routing,
        .s_stream = ks0127_s_stream,
        .querystd = ks0127_querystd,
@@ -660,7 +657,6 @@ static const struct v4l2_subdev_video_ops ks0127_video_ops = {
 };
 
 static const struct v4l2_subdev_ops ks0127_ops = {
-       .core = &ks0127_core_ops,
        .video = &ks0127_video_ops,
 };
 
index ab34cce..1a03d02 100644 (file)
@@ -26,7 +26,7 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-subdev.h>
 #include <media/m5mols.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
 
 #include "m5mols.h"
 #include "m5mols_reg.h"
index a9110d8..2cace73 100644 (file)
@@ -276,6 +276,7 @@ static const struct v4l2_ctrl_ops ml86v7667_ctrl_ops = {
 };
 
 static struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = {
+       .s_std = ml86v7667_s_std,
        .querystd = ml86v7667_querystd,
        .g_input_status = ml86v7667_g_input_status,
        .enum_mbus_fmt = ml86v7667_enum_mbus_fmt,
@@ -286,7 +287,6 @@ static struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = {
 };
 
 static struct v4l2_subdev_core_ops ml86v7667_subdev_core_ops = {
-       .s_std = ml86v7667_s_std,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        .g_register = ml86v7667_g_register,
        .s_register = ml86v7667_s_register,
index 8190fec..4d9c6bc 100644 (file)
@@ -649,10 +649,10 @@ static const struct v4l2_subdev_core_ops msp_core_ops = {
        .s_ctrl = v4l2_subdev_s_ctrl,
        .queryctrl = v4l2_subdev_queryctrl,
        .querymenu = v4l2_subdev_querymenu,
-       .s_std = msp_s_std,
 };
 
 static const struct v4l2_subdev_video_ops msp_video_ops = {
+       .s_std = msp_s_std,
        .querystd = msp_querystd,
 };
 
index 33daace..e18797f 100644 (file)
@@ -647,6 +647,28 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev,
 #define V4L2_CID_BLC_ANALOG_OFFSET     (V4L2_CID_USER_BASE | 0x1004)
 #define V4L2_CID_BLC_DIGITAL_OFFSET    (V4L2_CID_USER_BASE | 0x1005)
 
+static int mt9p031_restore_blc(struct mt9p031 *mt9p031)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+       int ret;
+
+       if (mt9p031->blc_auto->cur.val != 0) {
+               ret = mt9p031_set_mode2(mt9p031, 0,
+                                       MT9P031_READ_MODE_2_ROW_BLC);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (mt9p031->blc_offset->cur.val != 0) {
+               ret = mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
+                                   mt9p031->blc_offset->cur.val);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
 {
        struct mt9p031 *mt9p031 =
@@ -655,6 +677,9 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
        u16 data;
        int ret;
 
+       if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+               return 0;
+
        switch (ctrl->id) {
        case V4L2_CID_EXPOSURE:
                ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER,
@@ -709,18 +734,20 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
                                        MT9P031_READ_MODE_2_ROW_MIR, 0);
 
        case V4L2_CID_TEST_PATTERN:
+               /* The digital side of the Black Level Calibration function must
+                * be disabled when generating a test pattern to avoid artifacts
+                * in the image. Activate (deactivate) the BLC-related controls
+                * when the test pattern is enabled (disabled).
+                */
+               v4l2_ctrl_activate(mt9p031->blc_auto, ctrl->val == 0);
+               v4l2_ctrl_activate(mt9p031->blc_offset, ctrl->val == 0);
+
                if (!ctrl->val) {
-                       /* Restore the black level compensation settings. */
-                       if (mt9p031->blc_auto->cur.val != 0) {
-                               ret = mt9p031_s_ctrl(mt9p031->blc_auto);
-                               if (ret < 0)
-                                       return ret;
-                       }
-                       if (mt9p031->blc_offset->cur.val != 0) {
-                               ret = mt9p031_s_ctrl(mt9p031->blc_offset);
-                               if (ret < 0)
-                                       return ret;
-                       }
+                       /* Restore the BLC settings. */
+                       ret = mt9p031_restore_blc(mt9p031);
+                       if (ret < 0)
+                               return ret;
+
                        return mt9p031_write(client, MT9P031_TEST_PATTERN,
                                             MT9P031_TEST_PATTERN_DISABLE);
                }
@@ -735,9 +762,7 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
                if (ret < 0)
                        return ret;
 
-               /* Disable digital black level compensation when using a test
-                * pattern.
-                */
+               /* Disable digital BLC when generating a test pattern. */
                ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,
                                        0);
                if (ret < 0)
index 8272c0b..04e9e55 100644 (file)
@@ -643,10 +643,10 @@ static const struct v4l2_ctrl_ops saa6752hs_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops saa6752hs_core_ops = {
        .init = saa6752hs_init,
-       .s_std = saa6752hs_s_std,
 };
 
 static const struct v4l2_subdev_video_ops saa6752hs_video_ops = {
+       .s_std = saa6752hs_s_std,
        .s_mbus_fmt = saa6752hs_s_mbus_fmt,
        .try_mbus_fmt = saa6752hs_try_mbus_fmt,
        .g_mbus_fmt = saa6752hs_g_mbus_fmt,
index ac43e92..99689ee 100644 (file)
@@ -365,10 +365,10 @@ static const struct v4l2_subdev_core_ops saa7110_core_ops = {
        .s_ctrl = v4l2_subdev_s_ctrl,
        .queryctrl = v4l2_subdev_queryctrl,
        .querymenu = v4l2_subdev_querymenu,
-       .s_std = saa7110_s_std,
 };
 
 static const struct v4l2_subdev_video_ops saa7110_video_ops = {
+       .s_std = saa7110_s_std,
        .s_routing = saa7110_s_routing,
        .s_stream = saa7110_s_stream,
        .querystd = saa7110_querystd,
index afdbcb0..35a4464 100644 (file)
@@ -1582,7 +1582,6 @@ static const struct v4l2_subdev_core_ops saa711x_core_ops = {
        .s_ctrl = v4l2_subdev_s_ctrl,
        .queryctrl = v4l2_subdev_queryctrl,
        .querymenu = v4l2_subdev_querymenu,
-       .s_std = saa711x_s_std,
        .reset = saa711x_reset,
        .s_gpio = saa711x_s_gpio,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -1601,6 +1600,7 @@ static const struct v4l2_subdev_audio_ops saa711x_audio_ops = {
 };
 
 static const struct v4l2_subdev_video_ops saa711x_video_ops = {
+       .s_std = saa711x_s_std,
        .s_routing = saa711x_s_routing,
        .s_crystal_freq = saa711x_s_crystal_freq,
        .s_mbus_fmt = saa711x_s_mbus_fmt,
index 401ca11..6922a9f 100644 (file)
@@ -1198,7 +1198,6 @@ static const struct v4l2_subdev_core_ops saa717x_core_ops = {
        .g_register = saa717x_g_register,
        .s_register = saa717x_s_register,
 #endif
-       .s_std = saa717x_s_std,
        .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
        .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
        .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
@@ -1216,6 +1215,7 @@ static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = {
 };
 
 static const struct v4l2_subdev_video_ops saa717x_video_ops = {
+       .s_std = saa717x_s_std,
        .s_routing = saa717x_s_video_routing,
        .s_mbus_fmt = saa717x_s_mbus_fmt,
        .s_stream = saa717x_s_stream,
index 606a4ba..8e96992 100644 (file)
@@ -573,10 +573,10 @@ static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status)
 static const struct v4l2_subdev_core_ops saa7191_core_ops = {
        .g_ctrl = saa7191_g_ctrl,
        .s_ctrl = saa7191_s_ctrl,
-       .s_std = saa7191_s_std,
 };
 
 static const struct v4l2_subdev_video_ops saa7191_video_ops = {
+       .s_std = saa7191_s_std,
        .s_routing = saa7191_s_routing,
        .querystd = saa7191_querystd,
        .g_input_status = saa7191_g_input_status,
index a4a6498..5ce2b61 100644 (file)
@@ -46,7 +46,7 @@ struct smiapp_pll {
                        uint8_t bus_width;
                } parallel;
        };
-       uint8_t flags;
+       unsigned long flags;
        uint8_t binning_horizontal;
        uint8_t binning_vertical;
        uint8_t scale_m;
index 8741cae..06fb032 100644 (file)
@@ -606,7 +606,7 @@ static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit,
                if (rval)
                        return rval;
                sensor->limits[limit[i]] = val;
-               dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n",
+               dev_dbg(&client->dev, "0x%8.8x \"%s\" = %u, 0x%x\n",
                        smiapp_reg_limits[limit[i]].addr,
                        smiapp_reg_limits[limit[i]].what, val, val);
        }
@@ -741,8 +741,8 @@ static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor)
                if (rval)
                        return rval;
 
-               dev_dbg(&client->dev, "bpp %d, compressed %d\n",
-                       fmt >> 8, (u8)fmt);
+               dev_dbg(&client->dev, "%u: bpp %u, compressed %u\n",
+                       i, fmt >> 8, (u8)fmt);
 
                for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) {
                        const struct smiapp_csi_data_format *f =
@@ -1128,7 +1128,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor)
        }
        usleep_range(1000, 1000);
 
-       if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+       if (gpio_is_valid(sensor->platform_data->xshutdown))
                gpio_set_value(sensor->platform_data->xshutdown, 1);
 
        sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk);
@@ -1238,7 +1238,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor)
        return 0;
 
 out_cci_addr_fail:
-       if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+       if (gpio_is_valid(sensor->platform_data->xshutdown))
                gpio_set_value(sensor->platform_data->xshutdown, 0);
        if (sensor->platform_data->set_xclk)
                sensor->platform_data->set_xclk(&sensor->src->sd, 0);
@@ -1264,7 +1264,7 @@ static void smiapp_power_off(struct smiapp_sensor *sensor)
                             SMIAPP_REG_U8_SOFTWARE_RESET,
                             SMIAPP_SOFTWARE_RESET);
 
-       if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+       if (gpio_is_valid(sensor->platform_data->xshutdown))
                gpio_set_value(sensor->platform_data->xshutdown, 0);
        if (sensor->platform_data->set_xclk)
                sensor->platform_data->set_xclk(&sensor->src->sd, 0);
@@ -1766,7 +1766,7 @@ static void smiapp_set_compose_binner(struct v4l2_subdev *subdev,
        struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
        unsigned int i;
        unsigned int binh = 1, binv = 1;
-       unsigned int best = scaling_goodness(
+       int best = scaling_goodness(
                subdev,
                crops[SMIAPP_PAD_SINK]->width, sel->r.width,
                crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags);
@@ -2355,17 +2355,17 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
        unsigned int i;
        int rval;
 
-       sensor->vana = devm_regulator_get(&client->dev, "VANA");
+       sensor->vana = devm_regulator_get(&client->dev, "vana");
        if (IS_ERR(sensor->vana)) {
                dev_err(&client->dev, "could not get regulator for vana\n");
-               return -ENODEV;
+               return PTR_ERR(sensor->vana);
        }
 
        if (!sensor->platform_data->set_xclk) {
                sensor->ext_clk = devm_clk_get(&client->dev, "ext_clk");
                if (IS_ERR(sensor->ext_clk)) {
                        dev_err(&client->dev, "could not get clock\n");
-                       return -ENODEV;
+                       return PTR_ERR(sensor->ext_clk);
                }
 
                rval = clk_set_rate(sensor->ext_clk,
@@ -2374,18 +2374,19 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
                        dev_err(&client->dev,
                                "unable to set clock freq to %u\n",
                                sensor->platform_data->ext_clk);
-                       return -ENODEV;
+                       return rval;
                }
        }
 
-       if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) {
-               if (devm_gpio_request_one(&client->dev,
-                                         sensor->platform_data->xshutdown, 0,
-                                         "SMIA++ xshutdown") != 0) {
+       if (gpio_is_valid(sensor->platform_data->xshutdown)) {
+               rval = devm_gpio_request_one(
+                       &client->dev, sensor->platform_data->xshutdown, 0,
+                       "SMIA++ xshutdown");
+               if (rval < 0) {
                        dev_err(&client->dev,
                                "unable to acquire reset gpio %d\n",
                                sensor->platform_data->xshutdown);
-                       return -ENODEV;
+                       return rval;
                }
        }
 
@@ -2423,6 +2424,12 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
                sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP |
                                          SMIAPP_IMAGE_ORIENTATION_VFLIP;
 
+       rval = smiapp_call_quirk(sensor, limits);
+       if (rval) {
+               dev_err(&client->dev, "limits quirks failed\n");
+               goto out_power_off;
+       }
+
        rval = smiapp_get_mbus_formats(sensor);
        if (rval) {
                rval = -ENODEV;
@@ -2483,12 +2490,6 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
                }
        }
 
-       rval = smiapp_call_quirk(sensor, limits);
-       if (rval) {
-               dev_err(&client->dev, "limits quirks failed\n");
-               goto out_nvm_release;
-       }
-
        /* We consider this as profile 0 sensor if any of these are zero. */
        if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] ||
            !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] ||
@@ -2543,8 +2544,9 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
                }
 
                snprintf(this->sd.name,
-                        sizeof(this->sd.name), "%s %s",
-                        sensor->minfo.name, _this->name);
+                        sizeof(this->sd.name), "%s %d-%4.4x %s",
+                        sensor->minfo.name, i2c_adapter_id(client->adapter),
+                        client->addr, _this->name);
 
                this->sink_fmt.width =
                        sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
@@ -2616,12 +2618,11 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
        pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2;
        pll->csi2.lanes = sensor->platform_data->lanes;
        pll->ext_clk_freq_hz = sensor->platform_data->ext_clk;
+       pll->flags = smiapp_call_quirk(sensor, pll_flags);
+
        /* Profile 0 sensors have no separate OP clock branch. */
        if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
                pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
-       if (smiapp_needs_quirk(sensor,
-                              SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE))
-               pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
        pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
 
        rval = smiapp_update_mode(sensor);
@@ -2830,7 +2831,7 @@ static int smiapp_remove(struct i2c_client *client)
        unsigned int i;
 
        if (sensor->power_count) {
-               if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+               if (gpio_is_valid(sensor->platform_data->xshutdown))
                        gpio_set_value(sensor->platform_data->xshutdown, 0);
                if (sensor->platform_data->set_xclk)
                        sensor->platform_data->set_xclk(&sensor->src->sd, 0);
index bb8c506..e0bee87 100644 (file)
@@ -28,7 +28,7 @@
 
 static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val)
 {
-       return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val);
+       return smiapp_write(sensor, SMIAPP_REG_MK_U8(reg), val);
 }
 
 static int smiapp_write_8s(struct smiapp_sensor *sensor,
@@ -61,52 +61,6 @@ void smiapp_replace_limit(struct smiapp_sensor *sensor,
        sensor->limits[limit] = val;
 }
 
-bool smiapp_quirk_reg(struct smiapp_sensor *sensor,
-                     u32 reg, u32 *val)
-{
-       struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
-       const struct smia_reg *sreg;
-
-       if (!sensor->minfo.quirk)
-               return false;
-
-       sreg = sensor->minfo.quirk->regs;
-
-       if (!sreg)
-               return false;
-
-       while (sreg->type) {
-               u16 type = reg >> 16;
-               u16 reg16 = reg;
-
-               if (sreg->type != type || sreg->reg != reg16) {
-                       sreg++;
-                       continue;
-               }
-
-               switch ((u8)type) {
-               case SMIA_REG_8BIT:
-                       dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n",
-                               reg, sreg->val);
-                       break;
-               case SMIA_REG_16BIT:
-                       dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n",
-                               reg, sreg->val);
-                       break;
-               case SMIA_REG_32BIT:
-                       dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n",
-                               reg, sreg->val);
-                       break;
-               }
-
-               *val = sreg->val;
-
-               return true;
-       }
-
-       return false;
-}
-
 static int jt8ew9_limits(struct smiapp_sensor *sensor)
 {
        if (sensor->minfo.revision_number_major < 0x03)
@@ -266,12 +220,17 @@ static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor)
        return smiapp_write_8(sensor, 0x3328, 0x80);
 }
 
+static unsigned long jt8ev1_pll_flags(struct smiapp_sensor *sensor)
+{
+       return SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
+}
+
 const struct smiapp_quirk smiapp_jt8ev1_quirk = {
        .limits = jt8ev1_limits,
        .post_poweron = jt8ev1_post_poweron,
        .pre_streamon = jt8ev1_pre_streamon,
        .post_streamoff = jt8ev1_post_streamoff,
-       .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE,
+       .pll_flags = jt8ev1_pll_flags,
 };
 
 static int tcm8500md_limits(struct smiapp_sensor *sensor)
index 504a6d8..46e9ea8 100644 (file)
@@ -35,19 +35,30 @@ struct smiapp_sensor;
  * @post_poweron: Called always after the sensor has been fully powered on.
  * @pre_streamon: Called just before streaming is enabled.
  * @post_streamon: Called right after stopping streaming.
+ * @reg_access: Register access quirk. The quirk may divert the access
+ *             to another register, or no register at all.
+ *
+ *             @write: Is this read (false) or write (true) access?
+ *             @reg: Pointer to the register to access
+ *             @value: Register value, set by the caller on write, or
+ *                     by the quirk on read
+ *
+ *             @return: 0 on success, -ENOIOCTLCMD if no register
+ *                      access may be done by the caller (default read
+ *                      value is zero), else negative error code on error
  */
 struct smiapp_quirk {
        int (*limits)(struct smiapp_sensor *sensor);
        int (*post_poweron)(struct smiapp_sensor *sensor);
        int (*pre_streamon)(struct smiapp_sensor *sensor);
        int (*post_streamoff)(struct smiapp_sensor *sensor);
-       const struct smia_reg *regs;
+       unsigned long (*pll_flags)(struct smiapp_sensor *sensor);
+       int (*reg_access)(struct smiapp_sensor *sensor, bool write, u32 *reg,
+                         u32 *val);
        unsigned long flags;
 };
 
-/* op pix clock is for all lanes in total normally */
-#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE                        (1 << 0)
-#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY                       (1 << 1)
+#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY                       (1 << 0)
 
 struct smiapp_reg_8 {
        u16 reg;
@@ -56,12 +67,9 @@ struct smiapp_reg_8 {
 
 void smiapp_replace_limit(struct smiapp_sensor *sensor,
                          u32 limit, u32 val);
-bool smiapp_quirk_reg(struct smiapp_sensor *sensor,
-                     u32 reg, u32 *val);
 
-#define SMIAPP_MK_QUIRK_REG(_reg, _val) \
+#define SMIAPP_MK_QUIRK_REG_8(_reg, _val) \
        {                               \
-               .type = (_reg >> 16),   \
                .reg = (u16)_reg,       \
                .val = _val,            \
        }
index 3aa0ca9..c488ef0 100644 (file)
  * 02110-1301 USA
  *
  */
-#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r))
-#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r))
-#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r))
+#define SMIAPP_REG_MK_U8(r) ((SMIAPP_REG_8BIT << 16) | (r))
+#define SMIAPP_REG_MK_U16(r) ((SMIAPP_REG_16BIT << 16) | (r))
+#define SMIAPP_REG_MK_U32(r) ((SMIAPP_REG_32BIT << 16) | (r))
 
-#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r))
+#define SMIAPP_REG_MK_F32(r) (SMIAPP_REG_FLAG_FLOAT | (SMIAPP_REG_32BIT << 16) | (r))
 
 #define SMIAPP_REG_U16_MODEL_ID                                        SMIAPP_REG_MK_U16(0x0000)
 #define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR                    SMIAPP_REG_MK_U8(0x0002)
index 4fac32c..a209800 100644 (file)
@@ -114,14 +114,14 @@ static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg,
        *val = 0;
        /* high byte comes first */
        switch (len) {
-       case SMIA_REG_32BIT:
+       case SMIAPP_REG_32BIT:
                *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) +
                        data[3];
                break;
-       case SMIA_REG_16BIT:
+       case SMIAPP_REG_16BIT:
                *val = (data[0] << 8) + data[1];
                break;
-       case SMIA_REG_8BIT:
+       case SMIAPP_REG_8BIT:
                *val = data[0];
                break;
        default:
@@ -165,31 +165,28 @@ static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val,
                         bool only8)
 {
        struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
-       unsigned int len = (u8)(reg >> 16);
+       u8 len = SMIAPP_REG_WIDTH(reg);
        int rval;
 
-       if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT
-           && len != SMIA_REG_32BIT)
+       if (len != SMIAPP_REG_8BIT && len != SMIAPP_REG_16BIT
+           && len != SMIAPP_REG_32BIT)
                return -EINVAL;
 
-       if (smiapp_quirk_reg(sensor, reg, val))
-               goto found_quirk;
-
-       if (len == SMIA_REG_8BIT && !only8)
-               rval = ____smiapp_read(sensor, (u16)reg, len, val);
+       if (len == SMIAPP_REG_8BIT || !only8)
+               rval = ____smiapp_read(sensor, SMIAPP_REG_ADDR(reg), len, val);
        else
-               rval = ____smiapp_read_8only(sensor, (u16)reg, len, val);
+               rval = ____smiapp_read_8only(sensor, SMIAPP_REG_ADDR(reg), len,
+                                            val);
        if (rval < 0)
                return rval;
 
-found_quirk:
-       if (reg & SMIA_REG_FLAG_FLOAT)
+       if (reg & SMIAPP_REG_FLAG_FLOAT)
                *val = float_to_u32_mul_1000000(client, *val);
 
        return 0;
 }
 
-int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val)
+int smiapp_read_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val)
 {
        return __smiapp_read(
                sensor, reg, val,
@@ -197,28 +194,47 @@ int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val)
                                   SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY));
 }
 
+int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val)
+{
+       int rval;
+
+       *val = 0;
+       rval = smiapp_call_quirk(sensor, reg_access, false, &reg, val);
+       if (rval == -ENOIOCTLCMD)
+               return 0;
+       if (rval < 0)
+               return rval;
+
+       return smiapp_read_no_quirk(sensor, reg, val);
+}
+
 int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val)
 {
+       int rval;
+
+       *val = 0;
+       rval = smiapp_call_quirk(sensor, reg_access, false, &reg, val);
+       if (rval == -ENOIOCTLCMD)
+               return 0;
+       if (rval < 0)
+               return rval;
+
        return __smiapp_read(sensor, reg, val, true);
 }
 
-/*
- * Write to a 8/16-bit register.
- * Returns zero if successful, or non-zero otherwise.
- */
-int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val)
+int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val)
 {
        struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
        struct i2c_msg msg;
        unsigned char data[6];
        unsigned int retries;
-       unsigned int flags = reg >> 24;
-       unsigned int len = (u8)(reg >> 16);
-       u16 offset = reg;
+       u8 flags = SMIAPP_REG_FLAGS(reg);
+       u8 len = SMIAPP_REG_WIDTH(reg);
+       u16 offset = SMIAPP_REG_ADDR(reg);
        int r;
 
-       if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT &&
-            len != SMIA_REG_32BIT) || flags)
+       if ((len != SMIAPP_REG_8BIT && len != SMIAPP_REG_16BIT &&
+            len != SMIAPP_REG_32BIT) || flags)
                return -EINVAL;
 
        msg.addr = client->addr;
@@ -231,14 +247,14 @@ int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val)
        data[1] = (u8) (reg & 0xff);
 
        switch (len) {
-       case SMIA_REG_8BIT:
+       case SMIAPP_REG_8BIT:
                data[2] = val;
                break;
-       case SMIA_REG_16BIT:
+       case SMIAPP_REG_16BIT:
                data[2] = val >> 8;
                data[3] = val;
                break;
-       case SMIA_REG_32BIT:
+       case SMIAPP_REG_32BIT:
                data[2] = val >> 24;
                data[3] = val >> 16;
                data[4] = val >> 8;
@@ -271,3 +287,20 @@ int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val)
 
        return r;
 }
+
+/*
+ * Write to a 8/16-bit register.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val)
+{
+       int rval;
+
+       rval = smiapp_call_quirk(sensor, reg_access, true, &reg, &val);
+       if (rval == -ENOIOCTLCMD)
+               return 0;
+       if (rval < 0)
+               return rval;
+
+       return smiapp_write_no_quirk(sensor, reg, val);
+}
index eefc6c8..3552112 100644 (file)
 #include <linux/i2c.h>
 #include <linux/types.h>
 
+#define SMIAPP_REG_ADDR(reg)           ((u16)reg)
+#define SMIAPP_REG_WIDTH(reg)          ((u8)(reg >> 16))
+#define SMIAPP_REG_FLAGS(reg)          ((u8)(reg >> 24))
+
 /* Use upper 8 bits of the type field for flags */
-#define SMIA_REG_FLAG_FLOAT            (1 << 24)
+#define SMIAPP_REG_FLAG_FLOAT          (1 << 24)
 
-#define SMIA_REG_8BIT                  1
-#define SMIA_REG_16BIT                 2
-#define SMIA_REG_32BIT                 4
-struct smia_reg {
-       u16 type;
-       u16 reg;                        /* 16-bit offset */
-       u32 val;                        /* 8/16/32-bit value */
-};
+#define SMIAPP_REG_8BIT                        1
+#define SMIAPP_REG_16BIT               2
+#define SMIAPP_REG_32BIT               4
 
 struct smiapp_sensor;
 
+int smiapp_read_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val);
 int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val);
 int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val);
+int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val);
 int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val);
 
 #endif
index ab54628..416402e 100644 (file)
@@ -814,8 +814,6 @@ done:
 }
 
 static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
-       .s_std          = tw9910_s_std,
-       .g_std          = tw9910_g_std,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        .g_register     = tw9910_g_register,
        .s_register     = tw9910_s_register,
@@ -872,7 +870,15 @@ static int tw9910_s_mbus_config(struct v4l2_subdev *sd,
        return i2c_smbus_write_byte_data(client, OUTCTR1, val);
 }
 
+static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
+{
+       *norm = V4L2_STD_NTSC | V4L2_STD_PAL;
+       return 0;
+}
+
 static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
+       .s_std          = tw9910_s_std,
+       .g_std          = tw9910_g_std,
        .s_stream       = tw9910_s_stream,
        .g_mbus_fmt     = tw9910_g_fmt,
        .s_mbus_fmt     = tw9910_s_fmt,
@@ -882,6 +888,7 @@ static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
        .enum_mbus_fmt  = tw9910_enum_fmt,
        .g_mbus_config  = tw9910_g_mbus_config,
        .s_mbus_config  = tw9910_s_mbus_config,
+       .g_tvnorms      = tw9910_g_tvnorms,
 };
 
 static struct v4l2_subdev_ops tw9910_subdev_ops = {
index 32d8232..1da8004 100644 (file)
@@ -327,18 +327,18 @@ static int sony_btf_mpx_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner
 
 /* --------------------------------------------------------------------------*/
 
-static const struct v4l2_subdev_core_ops sony_btf_mpx_core_ops = {
-       .s_std = sony_btf_mpx_s_std,
-};
-
 static const struct v4l2_subdev_tuner_ops sony_btf_mpx_tuner_ops = {
        .s_tuner = sony_btf_mpx_s_tuner,
        .g_tuner = sony_btf_mpx_g_tuner,
 };
 
+static const struct v4l2_subdev_video_ops sony_btf_mpx_video_ops = {
+       .s_std = sony_btf_mpx_s_std,
+};
+
 static const struct v4l2_subdev_ops sony_btf_mpx_ops = {
-       .core = &sony_btf_mpx_core_ops,
        .tuner = &sony_btf_mpx_tuner_ops,
+       .video = &sony_btf_mpx_video_ops,
 };
 
 /* --------------------------------------------------------------------------*/
index f72561e..656d889 100644 (file)
@@ -410,6 +410,9 @@ static int ths8200_g_dv_timings(struct v4l2_subdev *sd,
 static int ths8200_enum_dv_timings(struct v4l2_subdev *sd,
                                   struct v4l2_enum_dv_timings *timings)
 {
+       if (timings->pad != 0)
+               return -EINVAL;
+
        return v4l2_enum_dv_timings_cap(timings, &ths8200_timings_cap,
                        NULL, NULL);
 }
@@ -417,6 +420,9 @@ static int ths8200_enum_dv_timings(struct v4l2_subdev *sd,
 static int ths8200_dv_timings_cap(struct v4l2_subdev *sd,
                                  struct v4l2_dv_timings_cap *cap)
 {
+       if (cap->pad != 0)
+               return -EINVAL;
+
        *cap = ths8200_timings_cap;
        return 0;
 }
@@ -426,6 +432,9 @@ static const struct v4l2_subdev_video_ops ths8200_video_ops = {
        .s_stream = ths8200_s_stream,
        .s_dv_timings = ths8200_s_dv_timings,
        .g_dv_timings = ths8200_g_dv_timings,
+};
+
+static const struct v4l2_subdev_pad_ops ths8200_pad_ops = {
        .enum_dv_timings = ths8200_enum_dv_timings,
        .dv_timings_cap = ths8200_dv_timings_cap,
 };
@@ -434,6 +443,7 @@ static const struct v4l2_subdev_video_ops ths8200_video_ops = {
 static const struct v4l2_subdev_ops ths8200_ops = {
        .core  = &ths8200_core_ops,
        .video = &ths8200_video_ops,
+       .pad = &ths8200_pad_ops,
 };
 
 static int ths8200_probe(struct i2c_client *client,
index d76c53a..070c152 100644 (file)
@@ -1862,7 +1862,6 @@ static const struct v4l2_subdev_core_ops tvaudio_core_ops = {
        .s_ctrl = v4l2_subdev_s_ctrl,
        .queryctrl = v4l2_subdev_queryctrl,
        .querymenu = v4l2_subdev_querymenu,
-       .s_std = tvaudio_s_std,
 };
 
 static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = {
@@ -1876,10 +1875,15 @@ static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = {
        .s_routing = tvaudio_s_routing,
 };
 
+static const struct v4l2_subdev_video_ops tvaudio_video_ops = {
+       .s_std = tvaudio_s_std,
+};
+
 static const struct v4l2_subdev_ops tvaudio_ops = {
        .core = &tvaudio_core_ops,
        .tuner = &tvaudio_tuner_ops,
        .audio = &tvaudio_audio_ops,
+       .video = &tvaudio_video_ops,
 };
 
 /* ----------------------------------------------------------------------- */
index ca00117..b9dabc9 100644 (file)
@@ -1010,10 +1010,10 @@ static const struct v4l2_subdev_core_ops tvp514x_core_ops = {
        .s_ctrl = v4l2_subdev_s_ctrl,
        .queryctrl = v4l2_subdev_queryctrl,
        .querymenu = v4l2_subdev_querymenu,
-       .s_std = tvp514x_s_std,
 };
 
 static const struct v4l2_subdev_video_ops tvp514x_video_ops = {
+       .s_std = tvp514x_s_std,
        .s_routing = tvp514x_s_routing,
        .querystd = tvp514x_querystd,
        .enum_mbus_fmt = tvp514x_enum_mbus_fmt,
index 4fd3688..a912125 100644 (file)
@@ -913,7 +913,7 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
 
 static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 {
-       struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
+       struct tvp5150 *decoder = to_tvp5150(sd);
 
        a->c    = decoder->rect;
        a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -923,7 +923,7 @@ static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
 
 static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
 {
-       struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
+       struct tvp5150 *decoder = to_tvp5150(sd);
        v4l2_std_id std;
 
        if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -1063,7 +1063,6 @@ static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops tvp5150_core_ops = {
        .log_status = tvp5150_log_status,
-       .s_std = tvp5150_s_std,
        .reset = tvp5150_reset,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        .g_register = tvp5150_g_register,
@@ -1076,6 +1075,7 @@ static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
 };
 
 static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
+       .s_std = tvp5150_s_std,
        .s_routing = tvp5150_s_routing,
        .enum_mbus_fmt = tvp5150_enum_mbus_fmt,
        .s_mbus_fmt = tvp5150_mbus_fmt,
index c4e1e2c..11f2387 100644 (file)
@@ -833,6 +833,9 @@ static int tvp7002_log_status(struct v4l2_subdev *sd)
 static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd,
                struct v4l2_enum_dv_timings *timings)
 {
+       if (timings->pad != 0)
+               return -EINVAL;
+
        /* Check requested format index is within range */
        if (timings->index >= NUM_TIMINGS)
                return -EINVAL;
@@ -924,7 +927,6 @@ static const struct v4l2_subdev_core_ops tvp7002_core_ops = {
 static const struct v4l2_subdev_video_ops tvp7002_video_ops = {
        .g_dv_timings = tvp7002_g_dv_timings,
        .s_dv_timings = tvp7002_s_dv_timings,
-       .enum_dv_timings = tvp7002_enum_dv_timings,
        .query_dv_timings = tvp7002_query_dv_timings,
        .s_stream = tvp7002_s_stream,
        .g_mbus_fmt = tvp7002_mbus_fmt,
@@ -938,6 +940,7 @@ static const struct v4l2_subdev_pad_ops tvp7002_pad_ops = {
        .enum_mbus_code = tvp7002_enum_mbus_code,
        .get_fmt = tvp7002_get_pad_format,
        .set_fmt = tvp7002_set_pad_format,
+       .enum_dv_timings = tvp7002_enum_dv_timings,
 };
 
 /* V4L2 top level operation handlers */
index f58607d..7347480 100644 (file)
@@ -342,12 +342,12 @@ static const struct v4l2_ctrl_ops tw2804_ctrl_ops = {
 };
 
 static const struct v4l2_subdev_video_ops tw2804_video_ops = {
+       .s_std = tw2804_s_std,
        .s_routing = tw2804_s_video_routing,
 };
 
 static const struct v4l2_subdev_core_ops tw2804_core_ops = {
        .log_status = tw2804_log_status,
-       .s_std = tw2804_s_std,
 };
 
 static const struct v4l2_subdev_ops tw2804_ops = {
index 285b759..12c7d21 100644 (file)
@@ -187,10 +187,10 @@ static const struct v4l2_ctrl_ops tw9903_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops tw9903_core_ops = {
        .log_status = tw9903_log_status,
-       .s_std = tw9903_s_std,
 };
 
 static const struct v4l2_subdev_video_ops tw9903_video_ops = {
+       .s_std = tw9903_s_std,
        .s_routing = tw9903_s_video_routing,
 };
 
index f6bef25..2672d89 100644 (file)
@@ -157,10 +157,10 @@ static const struct v4l2_ctrl_ops tw9906_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops tw9906_core_ops = {
        .log_status = tw9906_log_status,
-       .s_std = tw9906_s_std,
 };
 
 static const struct v4l2_subdev_video_ops tw9906_video_ops = {
+       .s_std = tw9906_s_std,
        .s_routing = tw9906_s_video_routing,
 };
 
index 6a3a3ff..819ab6d 100644 (file)
@@ -124,7 +124,6 @@ static int vp27smpx_log_status(struct v4l2_subdev *sd)
 
 static const struct v4l2_subdev_core_ops vp27smpx_core_ops = {
        .log_status = vp27smpx_log_status,
-       .s_std = vp27smpx_s_std,
 };
 
 static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = {
@@ -133,9 +132,14 @@ static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = {
        .g_tuner = vp27smpx_g_tuner,
 };
 
+static const struct v4l2_subdev_video_ops vp27smpx_video_ops = {
+       .s_std = vp27smpx_s_std,
+};
+
 static const struct v4l2_subdev_ops vp27smpx_ops = {
        .core = &vp27smpx_core_ops,
        .tuner = &vp27smpx_tuner_ops,
+       .video = &vp27smpx_video_ops,
 };
 
 /* ----------------------------------------------------------------------- */
index ece90df..016e766 100644 (file)
@@ -457,10 +457,10 @@ static const struct v4l2_subdev_core_ops vpx3220_core_ops = {
        .s_ctrl = v4l2_subdev_s_ctrl,
        .queryctrl = v4l2_subdev_queryctrl,
        .querymenu = v4l2_subdev_querymenu,
-       .s_std = vpx3220_s_std,
 };
 
 static const struct v4l2_subdev_video_ops vpx3220_video_ops = {
+       .s_std = vpx3220_s_std,
        .s_routing = vpx3220_s_routing,
        .s_stream = vpx3220_s_stream,
        .querystd = vpx3220_querystd,
index 703560f..88b97c9 100644 (file)
@@ -373,7 +373,8 @@ static void media_device_release(struct media_devnode *mdev)
  * - dev must point to the parent device
  * - model must be filled with the device model name
  */
-int __must_check media_device_register(struct media_device *mdev)
+int __must_check __media_device_register(struct media_device *mdev,
+                                        struct module *owner)
 {
        int ret;
 
@@ -389,7 +390,7 @@ int __must_check media_device_register(struct media_device *mdev)
        mdev->devnode.fops = &media_device_fops;
        mdev->devnode.parent = mdev->dev;
        mdev->devnode.release = media_device_release;
-       ret = media_devnode_register(&mdev->devnode);
+       ret = media_devnode_register(&mdev->devnode, owner);
        if (ret < 0)
                return ret;
 
@@ -401,7 +402,7 @@ int __must_check media_device_register(struct media_device *mdev)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(media_device_register);
+EXPORT_SYMBOL_GPL(__media_device_register);
 
 /**
  * media_device_unregister - unregister a media device
index fb0f046..7acd19c 100644 (file)
@@ -232,7 +232,8 @@ static const struct file_operations media_devnode_fops = {
  * the media_devnode structure is *not* called, so the caller is responsible for
  * freeing any data.
  */
-int __must_check media_devnode_register(struct media_devnode *mdev)
+int __must_check media_devnode_register(struct media_devnode *mdev,
+                                       struct module *owner)
 {
        int minor;
        int ret;
@@ -253,7 +254,7 @@ int __must_check media_devnode_register(struct media_devnode *mdev)
 
        /* Part 2: Initialize and register the character device */
        cdev_init(&mdev->cdev, &media_devnode_fops);
-       mdev->cdev.owner = mdev->fops->owner;
+       mdev->cdev.owner = owner;
 
        ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1);
        if (ret < 0) {
index 8a0e84c..416507a 100644 (file)
@@ -937,7 +937,7 @@ static struct qcam *qcam_init(struct parport *port)
                return NULL;
 
        v4l2_dev = &qcam->v4l2_dev;
-       snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "bw-qcam%d", num_cams);
+       snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "bw-qcam%u", num_cams);
 
        if (v4l2_device_register(port->dev, v4l2_dev) < 0) {
                v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
index d06963b..d8ec583 100644 (file)
@@ -52,6 +52,7 @@ static void osprey_eeprom(struct bttv *btv, const u8 ee[256]);
 static void modtec_eeprom(struct bttv *btv);
 static void init_PXC200(struct bttv *btv);
 static void init_RTV24(struct bttv *btv);
+static void init_PCI8604PW(struct bttv *btv);
 
 static void rv605_muxsel(struct bttv *btv, unsigned int input);
 static void eagle_muxsel(struct bttv *btv, unsigned int input);
@@ -2871,6 +2872,22 @@ struct tvcard bttv_tvcards[] = {
                .has_remote     = 1,
                .has_radio      = 1,
        },
+       /* ---- card 0xa6---------------------------------- */
+       [BTTV_BOARD_PCI_8604PW] = {
+               /* PCI-8604PW with special unlock sequence */
+               .name           = "PCI-8604PW",
+               .video_inputs   = 2,
+               /* .audio_inputs= 0, */
+               .svhs           = NO_SVHS,
+               /* The second input is available on CN4, if populated.
+                * The other 5x2 header (CN2?) connects to the same inputs
+                * as the on-board BNCs */
+               .muxsel         = MUXSEL(2, 3),
+               .tuner_type     = TUNER_ABSENT,
+               .no_msp34xx     = 1,
+               .no_tda7432     = 1,
+               .pll            = PLL_35,
+       },
 };
 
 static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
@@ -3305,6 +3322,9 @@ void bttv_init_card1(struct bttv *btv)
        case BTTV_BOARD_ADLINK_RTV24:
                init_RTV24( btv );
                break;
+       case BTTV_BOARD_PCI_8604PW:
+               init_PCI8604PW(btv);
+               break;
 
        }
        if (!bttv_tvcards[btv->c.type].has_dvb)
@@ -4184,6 +4204,96 @@ init_RTV24 (struct bttv *btv)
 
 
 
+/* ----------------------------------------------------------------------- */
+/*
+ *  The PCI-8604PW contains a CPLD, probably an ispMACH 4A, that filters
+ *  the PCI REQ signals comming from the four BT878 chips. After power
+ *  up, the CPLD does not forward requests to the bus, which prevents
+ *  the BT878 from fetching RISC instructions from memory. While the
+ *  CPLD is connected to most of the GPIOs of PCI device 0xD, only
+ *  five appear to play a role in unlocking the REQ signal. The following
+ *  sequence has been determined by trial and error without access to the
+ *  original driver.
+ *
+ *  Eight GPIOs of device 0xC are provided on connector CN4 (4 in, 4 out).
+ *  Devices 0xE and 0xF do not appear to have anything connected to their
+ *  GPIOs.
+ *
+ *  The correct GPIO_OUT_EN value might have some more bits set. It should
+ *  be possible to derive it from a boundary scan of the CPLD. Its JTAG
+ *  pins are routed to test points.
+ *
+ */
+/* ----------------------------------------------------------------------- */
+static void
+init_PCI8604PW(struct bttv *btv)
+{
+       int state;
+
+       if ((PCI_SLOT(btv->c.pci->devfn) & ~3) != 0xC) {
+               pr_warn("This is not a PCI-8604PW\n");
+               return;
+       }
+
+       if (PCI_SLOT(btv->c.pci->devfn) != 0xD)
+               return;
+
+       btwrite(0x080002, BT848_GPIO_OUT_EN);
+
+       state = (btread(BT848_GPIO_DATA) >> 21) & 7;
+
+       for (;;) {
+               switch (state) {
+               case 1:
+               case 5:
+               case 6:
+               case 4:
+                       pr_debug("PCI-8604PW in state %i, toggling pin\n",
+                                state);
+                       btwrite(0x080000, BT848_GPIO_DATA);
+                       msleep(1);
+                       btwrite(0x000000, BT848_GPIO_DATA);
+                       msleep(1);
+                       break;
+               case 7:
+                       pr_info("PCI-8604PW unlocked\n");
+                       return;
+               case 0:
+                       /* FIXME: If we are in state 7 and toggle GPIO[19] one
+                          more time, the CPLD goes into state 0, where PCI bus
+                          mastering is inhibited again. We have not managed to
+                          get out of that state. */
+
+                       pr_err("PCI-8604PW locked until reset\n");
+                       return;
+               default:
+                       pr_err("PCI-8604PW in unknown state %i\n", state);
+                       return;
+               }
+
+               state = (state << 4) | ((btread(BT848_GPIO_DATA) >> 21) & 7);
+
+               switch (state) {
+               case 0x15:
+               case 0x56:
+               case 0x64:
+               case 0x47:
+               /* The transition from state 7 to state 0 is, as explained
+                  above, valid but undesired and with this code impossible
+                  as we exit as soon as we are in state 7.
+               case 0x70: */
+                       break;
+               default:
+                       pr_err("PCI-8604PW invalid transition %i -> %i\n",
+                              state >> 4, state & 7);
+                       return;
+               }
+               state &= 7;
+       }
+}
+
+
+
 /* ----------------------------------------------------------------------- */
 /* Miro Pro radio stuff -- the tea5757 is connected to some GPIO ports     */
 /*
index afcd53b..da780f4 100644 (file)
@@ -1182,7 +1182,7 @@ set_tvnorm(struct bttv *btv, unsigned int norm)
                break;
        }
        id = tvnorm->v4l2_id;
-       bttv_call_all(btv, core, s_std, id);
+       bttv_call_all(btv, video, s_std, id);
 
        return 0;
 }
index bb5da34..f081262 100644 (file)
 #define BTTV_BOARD_BT848_CAP_14            0xa3
 #define BTTV_BOARD_CYBERVISION_CV06        0xa4
 #define BTTV_BOARD_KWORLD_VSTREAM_XPERT    0xa5
+#define BTTV_BOARD_PCI_8604PW              0xa6
 
 /* more card-specific defines */
 #define PT2254_L_CHANNEL 0x10
index 430b3eb..f2261df 100644 (file)
@@ -1544,7 +1544,7 @@ static int dst_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t minicmd)
 }
 
 
-static int dst_init(struct dvb_frontend *fe)
+static int bt8xx_dst_init(struct dvb_frontend *fe)
 {
        struct dst_state *state = fe->demodulator_priv;
 
@@ -1707,7 +1707,7 @@ static int dst_get_frontend(struct dvb_frontend *fe)
        return 0;
 }
 
-static void dst_release(struct dvb_frontend *fe)
+static void bt8xx_dst_release(struct dvb_frontend *fe)
 {
        struct dst_state *state = fe->demodulator_priv;
        if (state->dst_ca) {
@@ -1776,8 +1776,8 @@ static struct dvb_frontend_ops dst_dvbt_ops = {
                        FE_CAN_GUARD_INTERVAL_AUTO
        },
 
-       .release = dst_release,
-       .init = dst_init,
+       .release = bt8xx_dst_release,
+       .init = bt8xx_dst_init,
        .tune = dst_tune_frontend,
        .set_frontend = dst_set_frontend,
        .get_frontend = dst_get_frontend,
@@ -1801,8 +1801,8 @@ static struct dvb_frontend_ops dst_dvbs_ops = {
                .caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK
        },
 
-       .release = dst_release,
-       .init = dst_init,
+       .release = bt8xx_dst_release,
+       .init = bt8xx_dst_init,
        .tune = dst_tune_frontend,
        .set_frontend = dst_set_frontend,
        .get_frontend = dst_get_frontend,
@@ -1834,8 +1834,8 @@ static struct dvb_frontend_ops dst_dvbc_ops = {
                        FE_CAN_QAM_256
        },
 
-       .release = dst_release,
-       .init = dst_init,
+       .release = bt8xx_dst_release,
+       .init = bt8xx_dst_init,
        .tune = dst_tune_frontend,
        .set_frontend = dst_set_frontend,
        .get_frontend = dst_get_frontend,
@@ -1857,8 +1857,8 @@ static struct dvb_frontend_ops dst_atsc_ops = {
                .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
        },
 
-       .release = dst_release,
-       .init = dst_init,
+       .release = bt8xx_dst_release,
+       .init = bt8xx_dst_init,
        .tune = dst_tune_frontend,
        .set_frontend = dst_set_frontend,
        .get_frontend = dst_get_frontend,
index c4890a4..2d3afe0 100644 (file)
@@ -1263,7 +1263,6 @@ static const struct v4l2_subdev_core_ops cx18_av_general_ops = {
        .log_status = cx18_av_log_status,
        .load_fw = cx18_av_load_fw,
        .reset = cx18_av_reset,
-       .s_std = cx18_av_s_std,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        .g_register = cx18_av_g_register,
        .s_register = cx18_av_s_register,
@@ -1283,6 +1282,7 @@ static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = {
 };
 
 static const struct v4l2_subdev_video_ops cx18_av_video_ops = {
+       .s_std = cx18_av_s_std,
        .s_routing = cx18_av_s_video_routing,
        .s_stream = cx18_av_s_stream,
        .s_mbus_fmt = cx18_av_s_mbus_fmt,
index 4bfd865..76a3b4a 100644 (file)
@@ -760,7 +760,7 @@ int cx18_v4l2_close(struct file *filp)
                /* Mark that the radio is no longer in use */
                clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
                /* Switch tuner to TV */
-               cx18_call_all(cx, core, s_std, cx->std);
+               cx18_call_all(cx, video, s_std, cx->std);
                /* Select correct audio input (i.e. TV tuner or Line in) */
                cx18_audio_set_io(cx);
                if (atomic_read(&cx->ana_capturing) > 0) {
index 5374aeb..38dc6b8 100644 (file)
@@ -180,7 +180,6 @@ static int gpiomux_s_audio_routing(struct v4l2_subdev *sd,
 
 static const struct v4l2_subdev_core_ops gpiomux_core_ops = {
        .log_status = gpiomux_log_status,
-       .s_std = gpiomux_s_std,
 };
 
 static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = {
@@ -191,10 +190,15 @@ static const struct v4l2_subdev_audio_ops gpiomux_audio_ops = {
        .s_routing = gpiomux_s_audio_routing,
 };
 
+static const struct v4l2_subdev_video_ops gpiomux_video_ops = {
+       .s_std = gpiomux_s_std,
+};
+
 static const struct v4l2_subdev_ops gpiomux_ops = {
        .core = &gpiomux_core_ops,
        .tuner = &gpiomux_tuner_ops,
        .audio = &gpiomux_audio_ops,
+       .video = &gpiomux_video_ops,
 };
 
 /*
index 1110bcb..fefb2cd 100644 (file)
@@ -602,7 +602,7 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id std)
                        (unsigned long long) cx->std);
 
        /* Tuner */
-       cx18_call_all(cx, core, s_std, cx->std);
+       cx18_call_all(cx, video, s_std, cx->std);
        return 0;
 }
 
index 7891f34..e0a5952 100644 (file)
@@ -326,7 +326,7 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
 
        dev->tvnorm = norm;
 
-       call_all(dev, core, s_std, norm);
+       call_all(dev, video, s_std, norm);
 
        return 0;
 }
@@ -1589,7 +1589,7 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev,
                fe = &dev->ts1.analog_fe;
 
        if (fe && fe->ops.tuner_ops.set_analog_params) {
-               call_all(dev, core, s_std, dev->tvnorm);
+               call_all(dev, video, s_std, dev->tvnorm);
                fe->ops.tuner_ops.set_analog_params(fe, &params);
        }
        else
index ad59dc9..e061c88 100644 (file)
@@ -1012,7 +1012,7 @@ int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
        set_tvaudio(core);
 
        // tell i2c chips
-       call_all(core, core, s_std, norm);
+       call_all(core, video, s_std, norm);
 
        /* The chroma_agc control should be inaccessible if the video format is SECAM */
        v4l2_ctrl_grab(core->chroma_agc, cxiformat == VideoFormatSECAM);
index e1863db..7a9b98b 100644 (file)
@@ -159,6 +159,12 @@ static int snd_ivtv_pcm_capture_open(struct snd_pcm_substream *substream)
 
        /* Instruct the CX2341[56] to start sending packets */
        snd_ivtv_lock(itvsc);
+
+       if (ivtv_init_on_first_open(itv)) {
+               snd_ivtv_unlock(itvsc);
+               return -ENXIO;
+       }
+
        s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
 
        v4l2_fh_init(&item.fh, s->vdev);
index 9caffd8..e5ff627 100644 (file)
@@ -894,7 +894,7 @@ int ivtv_v4l2_close(struct file *filp)
                /* Mark that the radio is no longer in use */
                clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
                /* Switch tuner to TV */
-               ivtv_call_all(itv, core, s_std, itv->std);
+               ivtv_call_all(itv, video, s_std, itv->std);
                /* Select correct audio input (i.e. TV tuner or Line in) */
                ivtv_audio_set_io(itv);
                if (itv->hw_flags & IVTV_HW_SAA711X) {
index 807b275..b3667a0 100644 (file)
@@ -1090,7 +1090,7 @@ void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id std)
                itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284;
 
        /* Tuner */
-       ivtv_call_all(itv, core, s_std, itv->std);
+       ivtv_call_all(itv, video, s_std, itv->std);
 }
 
 void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id std)
index 7883393..18ae755 100644 (file)
@@ -1,7 +1,7 @@
 config VIDEO_SAA7134
        tristate "Philips SAA7134 support"
        depends on VIDEO_DEV && PCI && I2C
-       select VIDEOBUF_DMA_SG
+       select VIDEOBUF2_DMA_SG
        select VIDEO_TUNER
        select VIDEO_TVEEPROM
        select CRC32
@@ -37,7 +37,7 @@ config VIDEO_SAA7134_RC
 config VIDEO_SAA7134_DVB
        tristate "DVB/ATSC Support for saa7134 based TV cards"
        depends on VIDEO_SAA7134 && DVB_CORE
-       select VIDEOBUF_DVB
+       select VIDEOBUF2_DVB
        select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
        select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
index e04a4d5..4056989 100644 (file)
@@ -27,6 +27,7 @@
 #include <sound/pcm_params.h>
 #include <sound/initval.h>
 #include <linux/interrupt.h>
+#include <linux/vmalloc.h>
 
 #include "saa7134.h"
 #include "saa7134-reg.h"
@@ -274,6 +275,82 @@ static int snd_card_saa7134_capture_trigger(struct snd_pcm_substream * substream
        return err;
 }
 
+static int saa7134_alsa_dma_init(struct saa7134_dev *dev, int nr_pages)
+{
+       struct saa7134_dmasound *dma = &dev->dmasound;
+       struct page *pg;
+       int i;
+
+       dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT);
+       if (NULL == dma->vaddr) {
+               dprintk("vmalloc_32(%d pages) failed\n", nr_pages);
+               return -ENOMEM;
+       }
+
+       dprintk("vmalloc is at addr 0x%08lx, size=%d\n",
+                               (unsigned long)dma->vaddr,
+                               nr_pages << PAGE_SHIFT);
+
+       memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT);
+       dma->nr_pages = nr_pages;
+
+       dma->sglist = vzalloc(dma->nr_pages * sizeof(*dma->sglist));
+       if (NULL == dma->sglist)
+               goto vzalloc_err;
+
+       sg_init_table(dma->sglist, dma->nr_pages);
+       for (i = 0; i < dma->nr_pages; i++) {
+               pg = vmalloc_to_page(dma->vaddr + i * PAGE_SIZE);
+               if (NULL == pg)
+                       goto vmalloc_to_page_err;
+               sg_set_page(&dma->sglist[i], pg, PAGE_SIZE, 0);
+       }
+       return 0;
+
+vmalloc_to_page_err:
+       vfree(dma->sglist);
+       dma->sglist = NULL;
+vzalloc_err:
+       vfree(dma->vaddr);
+       dma->vaddr = NULL;
+       return -ENOMEM;
+}
+
+static int saa7134_alsa_dma_map(struct saa7134_dev *dev)
+{
+       struct saa7134_dmasound *dma = &dev->dmasound;
+
+       dma->sglen = dma_map_sg(&dev->pci->dev, dma->sglist,
+                       dma->nr_pages, PCI_DMA_FROMDEVICE);
+
+       if (0 == dma->sglen) {
+               pr_warn("%s: saa7134_alsa_map_sg failed\n", __func__);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+static int saa7134_alsa_dma_unmap(struct saa7134_dev *dev)
+{
+       struct saa7134_dmasound *dma = &dev->dmasound;
+
+       if (!dma->sglen)
+               return 0;
+
+       dma_unmap_sg(&dev->pci->dev, dma->sglist, dma->sglen, PCI_DMA_FROMDEVICE);
+       dma->sglen = 0;
+       return 0;
+}
+
+static int saa7134_alsa_dma_free(struct saa7134_dmasound *dma)
+{
+       vfree(dma->sglist);
+       dma->sglist = NULL;
+       vfree(dma->vaddr);
+       dma->vaddr = NULL;
+       return 0;
+}
+
 /*
  * DMA buffer initialization
  *
@@ -291,9 +368,8 @@ static int dsp_buffer_init(struct saa7134_dev *dev)
 
        BUG_ON(!dev->dmasound.bufsize);
 
-       videobuf_dma_init(&dev->dmasound.dma);
-       err = videobuf_dma_init_kernel(&dev->dmasound.dma, PCI_DMA_FROMDEVICE,
-                                      (dev->dmasound.bufsize + PAGE_SIZE) >> PAGE_SHIFT);
+       err = saa7134_alsa_dma_init(dev,
+                              (dev->dmasound.bufsize + PAGE_SIZE) >> PAGE_SHIFT);
        if (0 != err)
                return err;
        return 0;
@@ -310,7 +386,7 @@ static int dsp_buffer_free(struct saa7134_dev *dev)
 {
        BUG_ON(!dev->dmasound.blksize);
 
-       videobuf_dma_free(&dev->dmasound.dma);
+       saa7134_alsa_dma_free(&dev->dmasound);
 
        dev->dmasound.blocks  = 0;
        dev->dmasound.blksize = 0;
@@ -632,7 +708,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream,
        /* release the old buffer */
        if (substream->runtime->dma_area) {
                saa7134_pgtable_free(dev->pci, &dev->dmasound.pt);
-               videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma);
+               saa7134_alsa_dma_unmap(dev);
                dsp_buffer_free(dev);
                substream->runtime->dma_area = NULL;
        }
@@ -648,21 +724,22 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream,
                return err;
        }
 
-       if (0 != (err = videobuf_dma_map(&dev->pci->dev, &dev->dmasound.dma))) {
+       err = saa7134_alsa_dma_map(dev);
+       if (err) {
                dsp_buffer_free(dev);
                return err;
        }
-       if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) {
-               videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma);
+       err = saa7134_pgtable_alloc(dev->pci, &dev->dmasound.pt);
+       if (err) {
+               saa7134_alsa_dma_unmap(dev);
                dsp_buffer_free(dev);
                return err;
        }
-       if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->dmasound.pt,
-                                               dev->dmasound.dma.sglist,
-                                               dev->dmasound.dma.sglen,
-                                               0))) {
+       err = saa7134_pgtable_build(dev->pci, &dev->dmasound.pt,
+                               dev->dmasound.sglist, dev->dmasound.sglen, 0);
+       if (err) {
                saa7134_pgtable_free(dev->pci, &dev->dmasound.pt);
-               videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma);
+               saa7134_alsa_dma_unmap(dev);
                dsp_buffer_free(dev);
                return err;
        }
@@ -671,7 +748,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream,
           byte, but it doesn't work. So I allocate the DMA using the
           V4L functions, and force ALSA to use that as the DMA area */
 
-       substream->runtime->dma_area = dev->dmasound.dma.vaddr;
+       substream->runtime->dma_area = dev->dmasound.vaddr;
        substream->runtime->dma_bytes = dev->dmasound.bufsize;
        substream->runtime->dma_addr = 0;
 
@@ -698,7 +775,7 @@ static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream)
 
        if (substream->runtime->dma_area) {
                saa7134_pgtable_free(dev->pci, &dev->dmasound.pt);
-               videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma);
+               saa7134_alsa_dma_unmap(dev);
                dsp_buffer_free(dev);
                substream->runtime->dma_area = NULL;
        }
index 1362b4a..be19a05 100644 (file)
@@ -69,6 +69,10 @@ module_param_named(no_overlay, saa7134_no_overlay, int, 0444);
 MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)"
                " [some VIA/SIS chipsets are known to have problem with overlay]");
 
+bool saa7134_userptr;
+module_param(saa7134_userptr, bool, 0644);
+MODULE_PARM_DESC(saa7134_userptr, "enable page-aligned userptr support");
+
 static unsigned int video_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
 static unsigned int vbi_nr[]   = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
 static unsigned int radio_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
@@ -203,16 +207,16 @@ int saa7134_buffer_count(unsigned int size, unsigned int count)
 
 int saa7134_buffer_startpage(struct saa7134_buf *buf)
 {
-       return saa7134_buffer_pages(buf->vb.bsize) * buf->vb.i;
+       return saa7134_buffer_pages(vb2_plane_size(&buf->vb2, 0)) * buf->vb2.v4l2_buf.index;
 }
 
 unsigned long saa7134_buffer_base(struct saa7134_buf *buf)
 {
        unsigned long base;
-       struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+       struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0);
 
        base  = saa7134_buffer_startpage(buf) * 4096;
-       base += dma->sglist[0].offset;
+       base += dma->sgl[0].offset;
        return base;
 }
 
@@ -237,14 +241,16 @@ int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt,
                          unsigned int startpage)
 {
        __le32        *ptr;
-       unsigned int  i,p;
+       unsigned int  i, p;
 
        BUG_ON(NULL == pt || NULL == pt->cpu);
 
        ptr = pt->cpu + startpage;
-       for (i = 0; i < length; i++, list++)
+       for (i = 0; i < length; i++, list = sg_next(list)) {
                for (p = 0; p * 4096 < list->length; p++, ptr++)
-                       *ptr = cpu_to_le32(sg_dma_address(list) - list->offset);
+                       *ptr = cpu_to_le32(sg_dma_address(list) +
+                                               list->offset + p * 4096);
+       }
        return 0;
 }
 
@@ -258,44 +264,31 @@ void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt)
 
 /* ------------------------------------------------------------------ */
 
-void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf)
-{
-       struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
-       BUG_ON(in_interrupt());
-
-       videobuf_waiton(q, &buf->vb, 0, 0);
-       videobuf_dma_unmap(q->dev, dma);
-       videobuf_dma_free(dma);
-       buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-/* ------------------------------------------------------------------ */
-
 int saa7134_buffer_queue(struct saa7134_dev *dev,
                         struct saa7134_dmaqueue *q,
                         struct saa7134_buf *buf)
 {
        struct saa7134_buf *next = NULL;
+       unsigned long flags;
 
-       assert_spin_locked(&dev->slock);
-       dprintk("buffer_queue %p\n",buf);
+       spin_lock_irqsave(&dev->slock, flags);
+       dprintk("buffer_queue %p\n", buf);
        if (NULL == q->curr) {
                if (!q->need_two) {
                        q->curr = buf;
-                       buf->activate(dev,buf,NULL);
+                       buf->activate(dev, buf, NULL);
                } else if (list_empty(&q->queue)) {
-                       list_add_tail(&buf->vb.queue,&q->queue);
-                       buf->vb.state = VIDEOBUF_QUEUED;
+                       list_add_tail(&buf->entry, &q->queue);
                } else {
-                       next = list_entry(q->queue.next,struct saa7134_buf,
-                                         vb.queue);
+                       next = list_entry(q->queue.next, struct saa7134_buf,
+                                         entry);
                        q->curr = buf;
-                       buf->activate(dev,buf,next);
+                       buf->activate(dev, buf, next);
                }
        } else {
-               list_add_tail(&buf->vb.queue,&q->queue);
-               buf->vb.state = VIDEOBUF_QUEUED;
+               list_add_tail(&buf->entry, &q->queue);
        }
+       spin_unlock_irqrestore(&dev->slock, flags);
        return 0;
 }
 
@@ -303,13 +296,12 @@ void saa7134_buffer_finish(struct saa7134_dev *dev,
                           struct saa7134_dmaqueue *q,
                           unsigned int state)
 {
-       assert_spin_locked(&dev->slock);
-       dprintk("buffer_finish %p\n",q->curr);
+       dprintk("buffer_finish %p\n", q->curr);
 
        /* finish current buffer */
-       q->curr->vb.state = state;
-       v4l2_get_timestamp(&q->curr->vb.ts);
-       wake_up(&q->curr->vb.done);
+       v4l2_get_timestamp(&q->curr->vb2.v4l2_buf.timestamp);
+       q->curr->vb2.v4l2_buf.sequence = q->seq_nr++;
+       vb2_buffer_done(&q->curr->vb2, state);
        q->curr = NULL;
 }
 
@@ -323,36 +315,31 @@ void saa7134_buffer_next(struct saa7134_dev *dev,
 
        if (!list_empty(&q->queue)) {
                /* activate next one from queue */
-               buf = list_entry(q->queue.next,struct saa7134_buf,vb.queue);
+               buf = list_entry(q->queue.next, struct saa7134_buf, entry);
                dprintk("buffer_next %p [prev=%p/next=%p]\n",
-                       buf,q->queue.prev,q->queue.next);
-               list_del(&buf->vb.queue);
+                       buf, q->queue.prev, q->queue.next);
+               list_del(&buf->entry);
                if (!list_empty(&q->queue))
-                       next = list_entry(q->queue.next,struct saa7134_buf,
-                                         vb.queue);
+                       next = list_entry(q->queue.next, struct saa7134_buf, entry);
                q->curr = buf;
-               buf->activate(dev,buf,next);
+               buf->activate(dev, buf, next);
                dprintk("buffer_next #2 prev=%p/next=%p\n",
-                       q->queue.prev,q->queue.next);
+                       q->queue.prev, q->queue.next);
        } else {
                /* nothing to do -- just stop DMA */
-               dprintk("buffer_next %p\n",NULL);
+               dprintk("buffer_next %p\n", NULL);
                saa7134_set_dmabits(dev);
                del_timer(&q->timeout);
-
-               if (card_has_mpeg(dev))
-                       if (dev->ts_started)
-                               saa7134_ts_stop(dev);
        }
 }
 
 void saa7134_buffer_timeout(unsigned long data)
 {
-       struct saa7134_dmaqueue *q = (struct saa7134_dmaqueue*)data;
+       struct saa7134_dmaqueue *q = (struct saa7134_dmaqueue *)data;
        struct saa7134_dev *dev = q->dev;
        unsigned long flags;
 
-       spin_lock_irqsave(&dev->slock,flags);
+       spin_lock_irqsave(&dev->slock, flags);
 
        /* try to reset the hardware (SWRST) */
        saa_writeb(SAA7134_REGION_ENABLE, 0x00);
@@ -362,13 +349,33 @@ void saa7134_buffer_timeout(unsigned long data)
        /* flag current buffer as failed,
           try to start over with the next one. */
        if (q->curr) {
-               dprintk("timeout on %p\n",q->curr);
-               saa7134_buffer_finish(dev,q,VIDEOBUF_ERROR);
+               dprintk("timeout on %p\n", q->curr);
+               saa7134_buffer_finish(dev, q, VB2_BUF_STATE_ERROR);
        }
-       saa7134_buffer_next(dev,q);
-       spin_unlock_irqrestore(&dev->slock,flags);
+       saa7134_buffer_next(dev, q);
+       spin_unlock_irqrestore(&dev->slock, flags);
 }
 
+void saa7134_stop_streaming(struct saa7134_dev *dev, struct saa7134_dmaqueue *q)
+{
+       unsigned long flags;
+       struct list_head *pos, *n;
+       struct saa7134_buf *tmp;
+
+       spin_lock_irqsave(&dev->slock, flags);
+       if (!list_empty(&q->queue)) {
+               list_for_each_safe(pos, n, &q->queue) {
+                        tmp = list_entry(pos, struct saa7134_buf, entry);
+                        vb2_buffer_done(&tmp->vb2, VB2_BUF_STATE_ERROR);
+                        list_del(pos);
+                        tmp = NULL;
+               }
+       }
+       spin_unlock_irqrestore(&dev->slock, flags);
+       saa7134_buffer_timeout((unsigned long)q); /* also calls del_timer(&q->timeout) */
+}
+EXPORT_SYMBOL_GPL(saa7134_stop_streaming);
+
 /* ------------------------------------------------------------------ */
 
 int saa7134_set_dmabits(struct saa7134_dev *dev)
@@ -388,12 +395,11 @@ int saa7134_set_dmabits(struct saa7134_dev *dev)
                ctrl |= SAA7134_MAIN_CTRL_TE0;
                irq  |= SAA7134_IRQ1_INTE_RA0_1 |
                        SAA7134_IRQ1_INTE_RA0_0;
-               cap = dev->video_q.curr->vb.field;
+               cap = dev->field;
        }
 
        /* video capture -- dma 1+2 (planar modes) */
-       if (dev->video_q.curr &&
-           dev->video_q.curr->fmt->planar) {
+       if (dev->video_q.curr && dev->fmt->planar) {
                ctrl |= SAA7134_MAIN_CTRL_TE4 |
                        SAA7134_MAIN_CTRL_TE5;
        }
@@ -1047,6 +1053,8 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
 
        dev->video_dev = vdev_init(dev,&saa7134_video_template,"video");
        dev->video_dev->ctrl_handler = &dev->ctrl_handler;
+       dev->video_dev->lock = &dev->lock;
+       dev->video_dev->queue = &dev->video_vbq;
        err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
                                    video_nr[dev->nr]);
        if (err < 0) {
@@ -1059,6 +1067,8 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
 
        dev->vbi_dev = vdev_init(dev, &saa7134_video_template, "vbi");
        dev->vbi_dev->ctrl_handler = &dev->ctrl_handler;
+       dev->vbi_dev->lock = &dev->lock;
+       dev->vbi_dev->queue = &dev->vbi_vbq;
 
        err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
                                    vbi_nr[dev->nr]);
@@ -1070,6 +1080,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
        if (card_has_radio(dev)) {
                dev->radio_dev = vdev_init(dev,&saa7134_radio_template,"radio");
                dev->radio_dev->ctrl_handler = &dev->radio_ctrl_handler;
+               dev->radio_dev->lock = &dev->lock;
                err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
                                            radio_nr[dev->nr]);
                if (err < 0)
@@ -1189,7 +1200,7 @@ static int saa7134_buffer_requeue(struct saa7134_dev *dev,
 
        if (!list_empty(&q->queue))
                next = list_entry(q->queue.next, struct saa7134_buf,
-                                         vb.queue);
+                                         entry);
        buf->activate(dev, buf, next);
 
        return 0;
@@ -1360,10 +1371,3 @@ EXPORT_SYMBOL(saa7134_pgtable_free);
 EXPORT_SYMBOL(saa7134_pgtable_build);
 EXPORT_SYMBOL(saa7134_pgtable_alloc);
 EXPORT_SYMBOL(saa7134_set_dmabits);
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
index 4a08ae3..73ffbab 100644 (file)
@@ -602,10 +602,10 @@ static int configure_tda827x_fe(struct saa7134_dev *dev,
                                struct tda1004x_config *cdec_conf,
                                struct tda827x_config *tuner_conf)
 {
-       struct videobuf_dvb_frontend *fe0;
+       struct vb2_dvb_frontend *fe0;
 
        /* Get the first frontend */
-       fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+       fe0 = vb2_dvb_get_frontend(&dev->frontends, 1);
 
        if (!fe0)
                return -EINVAL;
@@ -1215,29 +1215,38 @@ static int dvb_init(struct saa7134_dev *dev)
 {
        int ret;
        int attach_xc3028 = 0;
-       struct videobuf_dvb_frontend *fe0;
+       struct vb2_dvb_frontend *fe0;
+       struct vb2_queue *q;
 
        /* FIXME: add support for multi-frontend */
        mutex_init(&dev->frontends.lock);
        INIT_LIST_HEAD(&dev->frontends.felist);
 
        printk(KERN_INFO "%s() allocating 1 frontend\n", __func__);
-       fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, 1);
+       fe0 = vb2_dvb_alloc_frontend(&dev->frontends, 1);
        if (!fe0) {
                printk(KERN_ERR "%s() failed to alloc\n", __func__);
                return -ENOMEM;
        }
 
-       /* init struct videobuf_dvb */
+       /* init struct vb2_dvb */
        dev->ts.nr_bufs    = 32;
        dev->ts.nr_packets = 32*4;
        fe0->dvb.name = dev->name;
-       videobuf_queue_sg_init(&fe0->dvb.dvbq, &saa7134_ts_qops,
-                           &dev->pci->dev, &dev->slock,
-                           V4L2_BUF_TYPE_VIDEO_CAPTURE,
-                           V4L2_FIELD_ALTERNATE,
-                           sizeof(struct saa7134_buf),
-                           dev, NULL);
+       q = &fe0->dvb.dvbq;
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_READ;
+       q->drv_priv = &dev->ts_q;
+       q->ops = &saa7134_ts_qops;
+       q->mem_ops = &vb2_dma_sg_memops;
+       q->buf_struct_size = sizeof(struct saa7134_buf);
+       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       q->lock = &dev->lock;
+       ret = vb2_queue_init(q);
+       if (ret) {
+               vb2_dvb_dealloc_frontends(&dev->frontends);
+               return ret;
+       }
 
        switch (dev->board) {
        case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
@@ -1876,7 +1885,7 @@ static int dvb_init(struct saa7134_dev *dev)
        fe0->dvb.frontend->callback = saa7134_tuner_callback;
 
        /* register everything else */
-       ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
+       ret = vb2_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
                                        &dev->pci->dev, adapter_nr, 0);
 
        /* this sequence is necessary to make the tda1004x load its firmware
@@ -1893,16 +1902,17 @@ static int dvb_init(struct saa7134_dev *dev)
        return ret;
 
 detach_frontend:
-       videobuf_dvb_dealloc_frontends(&dev->frontends);
+       vb2_dvb_dealloc_frontends(&dev->frontends);
+       vb2_queue_release(&fe0->dvb.dvbq);
        return -EINVAL;
 }
 
 static int dvb_fini(struct saa7134_dev *dev)
 {
-       struct videobuf_dvb_frontend *fe0;
+       struct vb2_dvb_frontend *fe0;
 
        /* Get the first frontend */
-       fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+       fe0 = vb2_dvb_get_frontend(&dev->frontends, 1);
        if (!fe0)
                return -EINVAL;
 
@@ -1933,7 +1943,8 @@ static int dvb_fini(struct saa7134_dev *dev)
                        }
                }
        }
-       videobuf_dvb_unregister_bus(&dev->frontends);
+       vb2_dvb_unregister_bus(&dev->frontends);
+       vb2_queue_release(&fe0->dvb.dvbq);
        return 0;
 }
 
@@ -1955,10 +1966,3 @@ static void __exit dvb_unregister(void)
 
 module_init(dvb_register);
 module_exit(dvb_unregister);
-
-/* ------------------------------------------------------------------ */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
index 0a9047e..e65c760 100644 (file)
@@ -48,21 +48,16 @@ MODULE_PARM_DESC(debug,"enable debug messages");
 
 /* ------------------------------------------------------------------ */
 
-static void ts_reset_encoder(struct saa7134_dev* dev)
-{
-       if (!dev->empress_started)
-               return;
-
-       saa_writeb(SAA7134_SPECIAL_MODE, 0x00);
-       msleep(10);
-       saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
-       msleep(100);
-       dev->empress_started = 0;
-}
-
-static int ts_init_encoder(struct saa7134_dev* dev)
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
 {
+       struct saa7134_dmaqueue *dmaq = vq->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
        u32 leading_null_bytes = 0;
+       int err;
+
+       err = saa7134_ts_start_streaming(vq, count);
+       if (err)
+               return err;
 
        /* If more cards start to need this, then this
           should probably be added to the card definitions. */
@@ -73,97 +68,43 @@ static int ts_init_encoder(struct saa7134_dev* dev)
                leading_null_bytes = 1;
                break;
        }
-       ts_reset_encoder(dev);
        saa_call_all(dev, core, init, leading_null_bytes);
-       dev->empress_started = 1;
-       return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int ts_open(struct file *file)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct saa7134_dev *dev = video_drvdata(file);
-       struct saa7134_fh *fh;
-
-       /* allocate + initialize per filehandle data */
-       fh = kzalloc(sizeof(*fh), GFP_KERNEL);
-       if (NULL == fh)
-               return -ENOMEM;
-
-       v4l2_fh_init(&fh->fh, vdev);
-       file->private_data = fh;
-       fh->is_empress = true;
-       v4l2_fh_add(&fh->fh);
-
        /* Unmute audio */
        saa_writeb(SAA7134_AUDIO_MUTE_CTRL,
-               saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6));
-
-       return 0;
-}
-
-static int ts_release(struct file *file)
-{
-       struct saa7134_dev *dev = video_drvdata(file);
-       struct saa7134_fh *fh = file->private_data;
-
-       if (res_check(fh, RESOURCE_EMPRESS)) {
-               videobuf_stop(&dev->empress_tsq);
-               videobuf_mmap_free(&dev->empress_tsq);
-
-               /* stop the encoder */
-               ts_reset_encoder(dev);
-
-               /* Mute audio */
-               saa_writeb(SAA7134_AUDIO_MUTE_CTRL,
-                               saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6));
-       }
-
-       v4l2_fh_del(&fh->fh);
-       v4l2_fh_exit(&fh->fh);
+                       saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6));
+       dev->empress_started = 1;
        return 0;
 }
 
-static ssize_t
-ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+static void stop_streaming(struct vb2_queue *vq)
 {
-       struct saa7134_dev *dev = video_drvdata(file);
+       struct saa7134_dmaqueue *dmaq = vq->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
 
-       if (res_locked(dev, RESOURCE_EMPRESS))
-               return -EBUSY;
-       if (!dev->empress_started)
-               ts_init_encoder(dev);
-
-       return videobuf_read_stream(&dev->empress_tsq,
-                                   data, count, ppos, 0,
-                                   file->f_flags & O_NONBLOCK);
-}
-
-static unsigned int
-ts_poll(struct file *file, struct poll_table_struct *wait)
-{
-       unsigned long req_events = poll_requested_events(wait);
-       struct saa7134_dev *dev = video_drvdata(file);
-       struct saa7134_fh *fh = file->private_data;
-       unsigned int rc = 0;
-
-       if (v4l2_event_pending(&fh->fh))
-               rc = POLLPRI;
-       else if (req_events & POLLPRI)
-               poll_wait(file, &fh->fh.wait, wait);
-       return rc | videobuf_poll_stream(file, &dev->empress_tsq, wait);
+       saa7134_ts_stop_streaming(vq);
+       saa_writeb(SAA7134_SPECIAL_MODE, 0x00);
+       msleep(20);
+       saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
+       msleep(100);
+       /* Mute audio */
+       saa_writeb(SAA7134_AUDIO_MUTE_CTRL,
+                       saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6));
+       dev->empress_started = 0;
 }
 
+static struct vb2_ops saa7134_empress_qops = {
+       .queue_setup    = saa7134_ts_queue_setup,
+       .buf_init       = saa7134_ts_buffer_init,
+       .buf_prepare    = saa7134_ts_buffer_prepare,
+       .buf_finish     = saa7134_ts_buffer_finish,
+       .buf_queue      = saa7134_vb2_buffer_queue,
+       .wait_prepare   = vb2_ops_wait_prepare,
+       .wait_finish    = vb2_ops_wait_finish,
+       .start_streaming = start_streaming,
+       .stop_streaming = stop_streaming,
+};
 
-static int
-ts_mmap(struct file *file, struct vm_area_struct * vma)
-{
-       struct saa7134_dev *dev = video_drvdata(file);
-
-       return videobuf_mmap_mapper(&dev->empress_tsq, vma);
-}
+/* ------------------------------------------------------------------ */
 
 static int empress_enum_fmt_vid_cap(struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *f)
@@ -233,11 +174,11 @@ static int empress_try_fmt_vid_cap(struct file *file, void *priv,
 static const struct v4l2_file_operations ts_fops =
 {
        .owner    = THIS_MODULE,
-       .open     = ts_open,
-       .release  = ts_release,
-       .read     = ts_read,
-       .poll     = ts_poll,
-       .mmap     = ts_mmap,
+       .open     = v4l2_fh_open,
+       .release  = vb2_fop_release,
+       .read     = vb2_fop_read,
+       .poll     = vb2_fop_poll,
+       .mmap     = vb2_fop_mmap,
        .ioctl    = video_ioctl2,
 };
 
@@ -247,12 +188,12 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = {
        .vidioc_try_fmt_vid_cap         = empress_try_fmt_vid_cap,
        .vidioc_s_fmt_vid_cap           = empress_s_fmt_vid_cap,
        .vidioc_g_fmt_vid_cap           = empress_g_fmt_vid_cap,
-       .vidioc_reqbufs                 = saa7134_reqbufs,
-       .vidioc_querybuf                = saa7134_querybuf,
-       .vidioc_qbuf                    = saa7134_qbuf,
-       .vidioc_dqbuf                   = saa7134_dqbuf,
-       .vidioc_streamon                = saa7134_streamon,
-       .vidioc_streamoff               = saa7134_streamoff,
+       .vidioc_reqbufs                 = vb2_ioctl_reqbufs,
+       .vidioc_querybuf                = vb2_ioctl_querybuf,
+       .vidioc_qbuf                    = vb2_ioctl_qbuf,
+       .vidioc_dqbuf                   = vb2_ioctl_dqbuf,
+       .vidioc_streamon                = vb2_ioctl_streamon,
+       .vidioc_streamoff               = vb2_ioctl_streamoff,
        .vidioc_g_frequency             = saa7134_g_frequency,
        .vidioc_s_frequency             = saa7134_s_frequency,
        .vidioc_g_tuner                 = saa7134_g_tuner,
@@ -262,6 +203,7 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = {
        .vidioc_s_input                 = saa7134_s_input,
        .vidioc_s_std                   = saa7134_s_std,
        .vidioc_g_std                   = saa7134_g_std,
+       .vidioc_querystd                = saa7134_querystd,
        .vidioc_log_status              = v4l2_ctrl_log_status,
        .vidioc_subscribe_event         = v4l2_ctrl_subscribe_event,
        .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
@@ -314,6 +256,7 @@ static bool empress_ctrl_filter(const struct v4l2_ctrl *ctrl)
 static int empress_init(struct saa7134_dev *dev)
 {
        struct v4l2_ctrl_handler *hdl = &dev->empress_ctrl_handler;
+       struct vb2_queue *q;
        int err;
 
        dprintk("%s: %s\n",dev->name,__func__);
@@ -323,6 +266,7 @@ static int empress_init(struct saa7134_dev *dev)
        *(dev->empress_dev) = saa7134_empress_template;
        dev->empress_dev->v4l2_dev  = &dev->v4l2_dev;
        dev->empress_dev->release = video_device_release;
+       dev->empress_dev->lock = &dev->lock;
        snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name),
                 "%s empress (%s)", dev->name,
                 saa7134_boards[dev->board].name);
@@ -339,6 +283,26 @@ static int empress_init(struct saa7134_dev *dev)
 
        INIT_WORK(&dev->empress_workqueue, empress_signal_update);
 
+       q = &dev->empress_vbq;
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       /*
+        * Do not add VB2_USERPTR: the saa7134 DMA engine cannot handle
+        * transfers that do not start at the beginning of a page. A USERPTR
+        * can start anywhere in a page, so USERPTR support is a no-go.
+        */
+       q->io_modes = VB2_MMAP | VB2_READ;
+       q->drv_priv = &dev->ts_q;
+       q->ops = &saa7134_empress_qops;
+       q->gfp_flags = GFP_DMA32;
+       q->mem_ops = &vb2_dma_sg_memops;
+       q->buf_struct_size = sizeof(struct saa7134_buf);
+       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       q->lock = &dev->lock;
+       err = vb2_queue_init(q);
+       if (err)
+               return err;
+       dev->empress_dev->queue = q;
+
        video_set_drvdata(dev->empress_dev, dev);
        err = video_register_device(dev->empress_dev,VFL_TYPE_GRABBER,
                                    empress_nr[dev->nr]);
@@ -352,13 +316,6 @@ static int empress_init(struct saa7134_dev *dev)
        printk(KERN_INFO "%s: registered device %s [mpeg]\n",
               dev->name, video_device_node_name(dev->empress_dev));
 
-       videobuf_queue_sg_init(&dev->empress_tsq, &saa7134_ts_qops,
-                           &dev->pci->dev, &dev->slock,
-                           V4L2_BUF_TYPE_VIDEO_CAPTURE,
-                           V4L2_FIELD_ALTERNATE,
-                           sizeof(struct saa7134_buf),
-                           dev, NULL);
-
        empress_signal_update(&dev->empress_workqueue);
        return 0;
 }
@@ -371,6 +328,7 @@ static int empress_fini(struct saa7134_dev *dev)
                return 0;
        flush_work(&dev->empress_workqueue);
        video_unregister_device(dev->empress_dev);
+       vb2_queue_release(&dev->empress_vbq);
        v4l2_ctrl_handler_free(&dev->empress_ctrl_handler);
        dev->empress_dev = NULL;
        return 0;
@@ -395,10 +353,3 @@ static void __exit empress_unregister(void)
 
 module_init(empress_register);
 module_exit(empress_unregister);
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
index c68169d..f4da674 100644 (file)
@@ -427,10 +427,3 @@ int saa7134_i2c_unregister(struct saa7134_dev *dev)
        i2c_del_adapter(&dev->i2c_adap);
        return 0;
 }
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
index e7e0af1..b6ea6f4 100644 (file)
 #define SAA7134_HSYNC_START                     0x106
 #define SAA7134_HSYNC_STOP                      0x107
 #define SAA7134_SYNC_CTRL                       0x108
+#define   SAA7134_SYNC_CTRL_AUFD                (1 << 7)
 #define SAA7134_LUMA_CTRL                       0x109
+#define   SAA7134_LUMA_CTRL_LDEL                (1 << 5)
 #define SAA7134_DEC_LUMA_BRIGHT                 0x10a
 #define SAA7134_DEC_LUMA_CONTRAST               0x10b
 #define SAA7134_DEC_CHROMA_SATURATION           0x10c
 #define SAA7134_DEC_CHROMA_HUE                  0x10d
 #define SAA7134_CHROMA_CTRL1                    0x10e
+#define   SAA7134_CHROMA_CTRL1_AUTO0            (1 << 1)
+#define   SAA7134_CHROMA_CTRL1_FCTC             (1 << 2)
 #define SAA7134_CHROMA_GAIN                     0x10f
 #define SAA7134_CHROMA_CTRL2                    0x110
 #define SAA7134_MODE_DELAY_CTRL                 0x111
 
 #define SAA7134_ANALOG_ADC                      0x114
+#define   SAA7134_ANALOG_ADC_AUTO1              (1 << 2)
 #define SAA7134_VGATE_START                     0x115
 #define SAA7134_VGATE_STOP                      0x116
 #define SAA7134_MISC_VGATE_MSB                  0x117
 #define SAA7135_DSP_RWCLEAR_RERR                   1
 
 #define SAA7133_I2S_AUDIO_CONTROL               0x591
-/* ------------------------------------------------------------------ */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
-
index 2e3f4b4..bd25323 100644 (file)
@@ -39,26 +39,29 @@ MODULE_PARM_DESC(ts_debug,"enable debug messages [ts]");
        printk(KERN_DEBUG "%s/ts: " fmt, dev->name , ## arg)
 
 /* ------------------------------------------------------------------ */
-
 static int buffer_activate(struct saa7134_dev *dev,
                           struct saa7134_buf *buf,
                           struct saa7134_buf *next)
 {
 
        dprintk("buffer_activate [%p]",buf);
-       buf->vb.state = VIDEOBUF_ACTIVE;
        buf->top_seen = 0;
 
+       if (!dev->ts_started)
+               dev->ts_field = V4L2_FIELD_TOP;
+
        if (NULL == next)
                next = buf;
-       if (V4L2_FIELD_TOP == buf->vb.field) {
+       if (V4L2_FIELD_TOP == dev->ts_field) {
                dprintk("- [top]     buf=%p next=%p\n",buf,next);
                saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(buf));
                saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(next));
+               dev->ts_field = V4L2_FIELD_BOTTOM;
        } else {
                dprintk("- [bottom]  buf=%p next=%p\n",buf,next);
                saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(next));
                saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(buf));
+               dev->ts_field = V4L2_FIELD_TOP;
        }
 
        /* start DMA */
@@ -72,96 +75,123 @@ static int buffer_activate(struct saa7134_dev *dev,
        return 0;
 }
 
-static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
-               enum v4l2_field field)
+int saa7134_ts_buffer_init(struct vb2_buffer *vb2)
 {
-       struct saa7134_dev *dev = q->priv_data;
-       struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+       struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
+       struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
+
+       dmaq->curr = NULL;
+       buf->activate = buffer_activate;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(saa7134_ts_buffer_init);
+
+int saa7134_ts_buffer_prepare(struct vb2_buffer *vb2)
+{
+       struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
+       struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
+       struct sg_table *dma = vb2_dma_sg_plane_desc(vb2, 0);
        unsigned int lines, llength, size;
-       int err;
+       int ret;
 
-       dprintk("buffer_prepare [%p,%s]\n",buf,v4l2_field_names[field]);
+       dprintk("buffer_prepare [%p]\n", buf);
 
        llength = TS_PACKET_SIZE;
        lines = dev->ts.nr_packets;
 
        size = lines * llength;
-       if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
+       if (vb2_plane_size(vb2, 0) < size)
                return -EINVAL;
 
-       if (buf->vb.size != size) {
-               saa7134_dma_free(q,buf);
-       }
-
-       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
-
-               struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
-
-               dprintk("buffer_prepare: needs_init\n");
-
-               buf->vb.width  = llength;
-               buf->vb.height = lines;
-               buf->vb.size   = size;
-               buf->pt        = &dev->ts.pt_ts;
-
-               err = videobuf_iolock(q,&buf->vb,NULL);
-               if (err)
-                       goto oops;
-               err = saa7134_pgtable_build(dev->pci,buf->pt,
-                                           dma->sglist,
-                                           dma->sglen,
-                                           saa7134_buffer_startpage(buf));
-               if (err)
-                       goto oops;
-       }
-
-       buf->vb.state = VIDEOBUF_PREPARED;
-       buf->activate = buffer_activate;
-       buf->vb.field = field;
-       return 0;
+       vb2_set_plane_payload(vb2, 0, size);
+       vb2->v4l2_buf.field = dev->field;
 
- oops:
-       saa7134_dma_free(q,buf);
-       return err;
+       ret = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
+       if (!ret)
+               return -EIO;
+       return saa7134_pgtable_build(dev->pci, &dmaq->pt, dma->sgl, dma->nents,
+                                   saa7134_buffer_startpage(buf));
 }
+EXPORT_SYMBOL_GPL(saa7134_ts_buffer_prepare);
 
-static int
-buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+void saa7134_ts_buffer_finish(struct vb2_buffer *vb2)
 {
-       struct saa7134_dev *dev = q->priv_data;
+       struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
+       struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
+       struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0);
 
-       *size = TS_PACKET_SIZE * dev->ts.nr_packets;
-       if (0 == *count)
-               *count = dev->ts.nr_bufs;
-       *count = saa7134_buffer_count(*size,*count);
+       dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
+}
+EXPORT_SYMBOL_GPL(saa7134_ts_buffer_finish);
 
+int saa7134_ts_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+                          unsigned int *nbuffers, unsigned int *nplanes,
+                          unsigned int sizes[], void *alloc_ctxs[])
+{
+       struct saa7134_dmaqueue *dmaq = q->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
+       int size = TS_PACKET_SIZE * dev->ts.nr_packets;
+
+       if (0 == *nbuffers)
+               *nbuffers = dev->ts.nr_bufs;
+       *nbuffers = saa7134_buffer_count(size, *nbuffers);
+       if (*nbuffers < 3)
+               *nbuffers = 3;
+       *nplanes = 1;
+       sizes[0] = size;
        return 0;
 }
+EXPORT_SYMBOL_GPL(saa7134_ts_queue_setup);
 
-static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+int saa7134_ts_start_streaming(struct vb2_queue *vq, unsigned int count)
 {
-       struct saa7134_dev *dev = q->priv_data;
-       struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
-
-       saa7134_buffer_queue(dev,&dev->ts_q,buf);
+       struct saa7134_dmaqueue *dmaq = vq->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
+
+       /*
+        * Planar video capture and TS share the same DMA channel,
+        * so only one can be active at a time.
+        */
+       if (vb2_is_busy(&dev->video_vbq) && dev->fmt->planar) {
+               struct saa7134_buf *buf, *tmp;
+
+               list_for_each_entry_safe(buf, tmp, &dmaq->queue, entry) {
+                       list_del(&buf->entry);
+                       vb2_buffer_done(&buf->vb2, VB2_BUF_STATE_QUEUED);
+               }
+               if (dmaq->curr) {
+                       vb2_buffer_done(&dmaq->curr->vb2, VB2_BUF_STATE_QUEUED);
+                       dmaq->curr = NULL;
+               }
+               return -EBUSY;
+       }
+       dmaq->seq_nr = 0;
+       return 0;
 }
+EXPORT_SYMBOL_GPL(saa7134_ts_start_streaming);
 
-static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+void saa7134_ts_stop_streaming(struct vb2_queue *vq)
 {
-       struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
-       struct saa7134_dev *dev = q->priv_data;
-
-       if (dev->ts_started)
-               saa7134_ts_stop(dev);
+       struct saa7134_dmaqueue *dmaq = vq->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
 
-       saa7134_dma_free(q,buf);
+       saa7134_ts_stop(dev);
+       saa7134_stop_streaming(dev, dmaq);
 }
-
-struct videobuf_queue_ops saa7134_ts_qops = {
-       .buf_setup    = buffer_setup,
-       .buf_prepare  = buffer_prepare,
-       .buf_queue    = buffer_queue,
-       .buf_release  = buffer_release,
+EXPORT_SYMBOL_GPL(saa7134_ts_stop_streaming);
+
+struct vb2_ops saa7134_ts_qops = {
+       .queue_setup    = saa7134_ts_queue_setup,
+       .buf_init       = saa7134_ts_buffer_init,
+       .buf_prepare    = saa7134_ts_buffer_prepare,
+       .buf_finish     = saa7134_ts_buffer_finish,
+       .buf_queue      = saa7134_vb2_buffer_queue,
+       .wait_prepare   = vb2_ops_wait_prepare,
+       .wait_finish    = vb2_ops_wait_finish,
+       .stop_streaming = saa7134_ts_stop_streaming,
 };
 EXPORT_SYMBOL_GPL(saa7134_ts_qops);
 
@@ -213,7 +243,7 @@ int saa7134_ts_init1(struct saa7134_dev *dev)
        dev->ts_q.dev              = dev;
        dev->ts_q.need_two         = 1;
        dev->ts_started            = 0;
-       saa7134_pgtable_alloc(dev->pci,&dev->ts.pt_ts);
+       saa7134_pgtable_alloc(dev->pci, &dev->ts_q.pt);
 
        /* init TS hw */
        saa7134_ts_init_hw(dev);
@@ -226,7 +256,8 @@ int saa7134_ts_stop(struct saa7134_dev *dev)
 {
        dprintk("TS stop\n");
 
-       BUG_ON(!dev->ts_started);
+       if (!dev->ts_started)
+               return 0;
 
        /* Stop TS stream */
        switch (saa7134_boards[dev->board].ts_type) {
@@ -247,7 +278,8 @@ int saa7134_ts_start(struct saa7134_dev *dev)
 {
        dprintk("TS start\n");
 
-       BUG_ON(dev->ts_started);
+       if (WARN_ON(dev->ts_started))
+               return 0;
 
        /* dma: setup channel 5 (= TS) */
        saa_writeb(SAA7134_TS_DMA0, (dev->ts.nr_packets - 1) & 0xff);
@@ -259,7 +291,7 @@ int saa7134_ts_start(struct saa7134_dev *dev)
        saa_writel(SAA7134_RS_PITCH(5), TS_PACKET_SIZE);
        saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_16 |
                                          SAA7134_RS_CONTROL_ME |
-                                         (dev->ts.pt_ts.dma >> 12));
+                                         (dev->ts_q.pt.dma >> 12));
 
        /* reset hardware TS buffers */
        saa_writeb(SAA7134_TS_SERIAL1, 0x00);
@@ -293,7 +325,7 @@ int saa7134_ts_start(struct saa7134_dev *dev)
 
 int saa7134_ts_fini(struct saa7134_dev *dev)
 {
-       saa7134_pgtable_free(dev->pci,&dev->ts.pt_ts);
+       saa7134_pgtable_free(dev->pci, &dev->ts_q.pt);
        return 0;
 }
 
@@ -303,25 +335,18 @@ void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status)
 
        spin_lock(&dev->slock);
        if (dev->ts_q.curr) {
-               field = dev->ts_q.curr->vb.field;
-               if (field == V4L2_FIELD_TOP) {
+               field = dev->ts_field;
+               if (field != V4L2_FIELD_TOP) {
                        if ((status & 0x100000) != 0x000000)
                                goto done;
                } else {
                        if ((status & 0x100000) != 0x100000)
                                goto done;
                }
-               saa7134_buffer_finish(dev,&dev->ts_q,VIDEOBUF_DONE);
+               saa7134_buffer_finish(dev, &dev->ts_q, VB2_BUF_STATE_DONE);
        }
        saa7134_buffer_next(dev,&dev->ts_q);
 
  done:
        spin_unlock(&dev->slock);
 }
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
index 0f34e09..3afbcb7 100644 (file)
@@ -1079,10 +1079,3 @@ int saa7134_tvaudio_do_scan(struct saa7134_dev *dev)
 
 EXPORT_SYMBOL(saa_dsp_writel);
 EXPORT_SYMBOL(saa7134_tvaudio_setmute);
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
index d4da18d..c06dbe1 100644 (file)
@@ -67,10 +67,10 @@ static void task_init(struct saa7134_dev *dev, struct saa7134_buf *buf,
        saa_writeb(SAA7134_VBI_PHASE_OFFSET_LUMA(task),   0x00);
        saa_writeb(SAA7134_VBI_PHASE_OFFSET_CHROMA(task), 0x00);
 
-       saa_writeb(SAA7134_VBI_H_LEN1(task), buf->vb.width   & 0xff);
-       saa_writeb(SAA7134_VBI_H_LEN2(task), buf->vb.width   >> 8);
-       saa_writeb(SAA7134_VBI_V_LEN1(task), buf->vb.height  & 0xff);
-       saa_writeb(SAA7134_VBI_V_LEN2(task), buf->vb.height  >> 8);
+       saa_writeb(SAA7134_VBI_H_LEN1(task), dev->vbi_hlen & 0xff);
+       saa_writeb(SAA7134_VBI_H_LEN2(task), dev->vbi_hlen >> 8);
+       saa_writeb(SAA7134_VBI_V_LEN1(task), dev->vbi_vlen & 0xff);
+       saa_writeb(SAA7134_VBI_V_LEN2(task), dev->vbi_vlen >> 8);
 
        saa_andorb(SAA7134_DATA_PATH(task), 0xc0, 0x00);
 }
@@ -81,14 +81,14 @@ static int buffer_activate(struct saa7134_dev *dev,
                           struct saa7134_buf *buf,
                           struct saa7134_buf *next)
 {
-       unsigned long control,base;
+       struct saa7134_dmaqueue *dmaq = buf->vb2.vb2_queue->drv_priv;
+       unsigned long control, base;
 
-       dprintk("buffer_activate [%p]\n",buf);
-       buf->vb.state = VIDEOBUF_ACTIVE;
+       dprintk("buffer_activate [%p]\n", buf);
        buf->top_seen = 0;
 
-       task_init(dev,buf,TASK_A);
-       task_init(dev,buf,TASK_B);
+       task_init(dev, buf, TASK_A);
+       task_init(dev, buf, TASK_B);
        saa_writeb(SAA7134_OFMT_DATA_A, 0x06);
        saa_writeb(SAA7134_OFMT_DATA_B, 0x06);
 
@@ -96,107 +96,99 @@ static int buffer_activate(struct saa7134_dev *dev,
        base    = saa7134_buffer_base(buf);
        control = SAA7134_RS_CONTROL_BURST_16 |
                SAA7134_RS_CONTROL_ME |
-               (buf->pt->dma >> 12);
-       saa_writel(SAA7134_RS_BA1(2),base);
-       saa_writel(SAA7134_RS_BA2(2),base + buf->vb.size/2);
-       saa_writel(SAA7134_RS_PITCH(2),buf->vb.width);
-       saa_writel(SAA7134_RS_CONTROL(2),control);
-       saa_writel(SAA7134_RS_BA1(3),base);
-       saa_writel(SAA7134_RS_BA2(3),base + buf->vb.size/2);
-       saa_writel(SAA7134_RS_PITCH(3),buf->vb.width);
-       saa_writel(SAA7134_RS_CONTROL(3),control);
+               (dmaq->pt.dma >> 12);
+       saa_writel(SAA7134_RS_BA1(2), base);
+       saa_writel(SAA7134_RS_BA2(2), base + dev->vbi_hlen * dev->vbi_vlen);
+       saa_writel(SAA7134_RS_PITCH(2), dev->vbi_hlen);
+       saa_writel(SAA7134_RS_CONTROL(2), control);
+       saa_writel(SAA7134_RS_BA1(3), base);
+       saa_writel(SAA7134_RS_BA2(3), base + dev->vbi_hlen * dev->vbi_vlen);
+       saa_writel(SAA7134_RS_PITCH(3), dev->vbi_hlen);
+       saa_writel(SAA7134_RS_CONTROL(3), control);
 
        /* start DMA */
        saa7134_set_dmabits(dev);
-       mod_timer(&dev->vbi_q.timeout, jiffies+BUFFER_TIMEOUT);
+       mod_timer(&dmaq->timeout, jiffies + BUFFER_TIMEOUT);
 
        return 0;
 }
 
-static int buffer_prepare(struct videobuf_queue *q,
-                         struct videobuf_buffer *vb,
-                         enum v4l2_field field)
+static int buffer_prepare(struct vb2_buffer *vb2)
 {
-       struct saa7134_dev *dev = q->priv_data;
-       struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
-       struct saa7134_tvnorm *norm = dev->tvnorm;
-       unsigned int lines, llength, size;
-       int err;
-
-       lines   = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1;
-       if (lines > VBI_LINE_COUNT)
-               lines = VBI_LINE_COUNT;
-       llength = VBI_LINE_LENGTH;
-       size = lines * llength * 2;
-       if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
+       struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
+       struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
+       struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0);
+       unsigned int size;
+       int ret;
+
+       if (dma->sgl->offset) {
+               pr_err("The buffer is not page-aligned\n");
                return -EINVAL;
-
-       if (buf->vb.size != size)
-               saa7134_dma_free(q,buf);
-
-       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
-               struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
-
-               buf->vb.width  = llength;
-               buf->vb.height = lines;
-               buf->vb.size   = size;
-               buf->pt        = &dev->pt_vbi;
-
-               err = videobuf_iolock(q,&buf->vb,NULL);
-               if (err)
-                       goto oops;
-               err = saa7134_pgtable_build(dev->pci,buf->pt,
-                                           dma->sglist,
-                                           dma->sglen,
-                                           saa7134_buffer_startpage(buf));
-               if (err)
-                       goto oops;
        }
-       buf->vb.state = VIDEOBUF_PREPARED;
-       buf->activate = buffer_activate;
-       buf->vb.field = field;
-       return 0;
+       size = dev->vbi_hlen * dev->vbi_vlen * 2;
+       if (vb2_plane_size(vb2, 0) < size)
+               return -EINVAL;
+
+       vb2_set_plane_payload(vb2, 0, size);
 
- oops:
-       saa7134_dma_free(q,buf);
-       return err;
+       ret = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
+       if (!ret)
+               return -EIO;
+       return saa7134_pgtable_build(dev->pci, &dmaq->pt, dma->sgl, dma->nents,
+                                   saa7134_buffer_startpage(buf));
 }
 
-static int
-buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+                          unsigned int *nbuffers, unsigned int *nplanes,
+                          unsigned int sizes[], void *alloc_ctxs[])
 {
-       struct saa7134_dev *dev = q->priv_data;
-       int llength,lines;
-
-       lines   = dev->tvnorm->vbi_v_stop_0 - dev->tvnorm->vbi_v_start_0 +1;
-       llength = VBI_LINE_LENGTH;
-       *size = lines * llength * 2;
-       if (0 == *count)
-               *count = vbibufs;
-       *count = saa7134_buffer_count(*size,*count);
+       struct saa7134_dmaqueue *dmaq = q->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
+       unsigned int size;
+
+       dev->vbi_vlen = dev->tvnorm->vbi_v_stop_0 - dev->tvnorm->vbi_v_start_0 + 1;
+       if (dev->vbi_vlen > VBI_LINE_COUNT)
+               dev->vbi_vlen = VBI_LINE_COUNT;
+       dev->vbi_hlen = VBI_LINE_LENGTH;
+       size = dev->vbi_hlen * dev->vbi_vlen * 2;
+
+       *nbuffers = saa7134_buffer_count(size, *nbuffers);
+       *nplanes = 1;
+       sizes[0] = size;
        return 0;
 }
 
-static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static int buffer_init(struct vb2_buffer *vb2)
 {
-       struct saa7134_dev *dev = q->priv_data;
-       struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+       struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
+       struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
 
-       saa7134_buffer_queue(dev,&dev->vbi_q,buf);
+       dmaq->curr = NULL;
+       buf->activate = buffer_activate;
+       return 0;
 }
 
-static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static void buffer_finish(struct vb2_buffer *vb2)
 {
-       struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+       struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
+       struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
+       struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0);
 
-       saa7134_dma_free(q,buf);
+       dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
 }
 
-struct videobuf_queue_ops saa7134_vbi_qops = {
-       .buf_setup    = buffer_setup,
-       .buf_prepare  = buffer_prepare,
-       .buf_queue    = buffer_queue,
-       .buf_release  = buffer_release,
+struct vb2_ops saa7134_vbi_qops = {
+       .queue_setup    = queue_setup,
+       .buf_init       = buffer_init,
+       .buf_prepare    = buffer_prepare,
+       .buf_finish     = buffer_finish,
+       .buf_queue      = saa7134_vb2_buffer_queue,
+       .wait_prepare   = vb2_ops_wait_prepare,
+       .wait_finish    = vb2_ops_wait_finish,
+       .start_streaming = saa7134_vb2_start_streaming,
+       .stop_streaming = saa7134_vb2_stop_streaming,
 };
 
 /* ------------------------------------------------------------------ */
@@ -226,7 +218,6 @@ void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status)
 {
        spin_lock(&dev->slock);
        if (dev->vbi_q.curr) {
-               dev->vbi_fieldcount++;
                /* make sure we have seen both fields */
                if ((status & 0x10) == 0x00) {
                        dev->vbi_q.curr->top_seen = 1;
@@ -235,18 +226,10 @@ void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status)
                if (!dev->vbi_q.curr->top_seen)
                        goto done;
 
-               dev->vbi_q.curr->vb.field_count = dev->vbi_fieldcount;
-               saa7134_buffer_finish(dev,&dev->vbi_q,VIDEOBUF_DONE);
+               saa7134_buffer_finish(dev, &dev->vbi_q, VB2_BUF_STATE_DONE);
        }
-       saa7134_buffer_next(dev,&dev->vbi_q);
+       saa7134_buffer_next(dev, &dev->vbi_q);
 
  done:
        spin_unlock(&dev->slock);
 }
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
index eb472b5..d375999 100644 (file)
@@ -381,42 +381,6 @@ static struct saa7134_format* format_by_fourcc(unsigned int fourcc)
        return NULL;
 }
 
-/* ----------------------------------------------------------------------- */
-/* resource management                                                     */
-
-static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bit)
-{
-       if (fh->resources & bit)
-               /* have it already allocated */
-               return 1;
-
-       /* is it free? */
-       mutex_lock(&dev->lock);
-       if (dev->resources & bit) {
-               /* no, someone else uses it */
-               mutex_unlock(&dev->lock);
-               return 0;
-       }
-       /* it's free, grab it */
-       fh->resources  |= bit;
-       dev->resources |= bit;
-       dprintk("res: get %d\n",bit);
-       mutex_unlock(&dev->lock);
-       return 1;
-}
-
-static
-void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits)
-{
-       BUG_ON((fh->resources & bits) != bits);
-
-       mutex_lock(&dev->lock);
-       fh->resources  &= ~bits;
-       dev->resources &= ~bits;
-       dprintk("res: put %d\n",bits);
-       mutex_unlock(&dev->lock);
-}
-
 /* ------------------------------------------------------------------ */
 
 static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
@@ -452,19 +416,26 @@ static void video_mux(struct saa7134_dev *dev, int input)
 
 static void saa7134_set_decoder(struct saa7134_dev *dev)
 {
-       int luma_control, sync_control, mux;
+       int luma_control, sync_control, chroma_ctrl1, mux;
 
        struct saa7134_tvnorm *norm = dev->tvnorm;
        mux = card_in(dev, dev->ctl_input).vmux;
 
        luma_control = norm->luma_control;
        sync_control = norm->sync_control;
+       chroma_ctrl1 = norm->chroma_ctrl1;
 
        if (mux > 5)
                luma_control |= 0x80; /* svideo */
        if (noninterlaced || dev->nosignal)
                sync_control |= 0x20;
 
+       /* switch on auto standard detection */
+       sync_control |= SAA7134_SYNC_CTRL_AUFD;
+       chroma_ctrl1 |= SAA7134_CHROMA_CTRL1_AUTO0;
+       chroma_ctrl1 &= ~SAA7134_CHROMA_CTRL1_FCTC;
+       luma_control &= ~SAA7134_LUMA_CTRL_LDEL;
+
        /* setup video decoder */
        saa_writeb(SAA7134_INCR_DELAY,            0x08);
        saa_writeb(SAA7134_ANALOG_IN_CTRL1,       0xc0 | mux);
@@ -487,7 +458,7 @@ static void saa7134_set_decoder(struct saa7134_dev *dev)
                dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
 
        saa_writeb(SAA7134_DEC_CHROMA_HUE,        dev->ctl_hue);
-       saa_writeb(SAA7134_CHROMA_CTRL1,          norm->chroma_ctrl1);
+       saa_writeb(SAA7134_CHROMA_CTRL1,          chroma_ctrl1);
        saa_writeb(SAA7134_CHROMA_GAIN,           norm->chroma_gain);
 
        saa_writeb(SAA7134_CHROMA_CTRL2,          norm->chroma_ctrl2);
@@ -506,10 +477,10 @@ void saa7134_set_tvnorm_hw(struct saa7134_dev *dev)
        saa7134_set_decoder(dev);
 
        if (card_in(dev, dev->ctl_input).tv)
-               saa_call_all(dev, core, s_std, dev->tvnorm->id);
+               saa_call_all(dev, video, s_std, dev->tvnorm->id);
        /* Set the correct norm for the saa6752hs. This function
           does nothing if there is no saa6752hs. */
-       saa_call_empress(dev, core, s_std, dev->tvnorm->id);
+       saa_call_empress(dev, video, s_std, dev->tvnorm->id);
 }
 
 static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale)
@@ -817,35 +788,35 @@ static int buffer_activate(struct saa7134_dev *dev,
                           struct saa7134_buf *buf,
                           struct saa7134_buf *next)
 {
+       struct saa7134_dmaqueue *dmaq = buf->vb2.vb2_queue->drv_priv;
        unsigned long base,control,bpl;
        unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */
 
        dprintk("buffer_activate buf=%p\n",buf);
-       buf->vb.state = VIDEOBUF_ACTIVE;
        buf->top_seen = 0;
 
-       set_size(dev,TASK_A,buf->vb.width,buf->vb.height,
-                V4L2_FIELD_HAS_BOTH(buf->vb.field));
-       if (buf->fmt->yuv)
+       set_size(dev, TASK_A, dev->width, dev->height,
+                V4L2_FIELD_HAS_BOTH(dev->field));
+       if (dev->fmt->yuv)
                saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x03);
        else
                saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x01);
-       saa_writeb(SAA7134_OFMT_VIDEO_A, buf->fmt->pm);
+       saa_writeb(SAA7134_OFMT_VIDEO_A, dev->fmt->pm);
 
        /* DMA: setup channel 0 (= Video Task A0) */
        base  = saa7134_buffer_base(buf);
-       if (buf->fmt->planar)
-               bpl = buf->vb.width;
+       if (dev->fmt->planar)
+               bpl = dev->width;
        else
-               bpl = (buf->vb.width * buf->fmt->depth) / 8;
+               bpl = (dev->width * dev->fmt->depth) / 8;
        control = SAA7134_RS_CONTROL_BURST_16 |
                SAA7134_RS_CONTROL_ME |
-               (buf->pt->dma >> 12);
-       if (buf->fmt->bswap)
+               (dmaq->pt.dma >> 12);
+       if (dev->fmt->bswap)
                control |= SAA7134_RS_CONTROL_BSWAP;
-       if (buf->fmt->wswap)
+       if (dev->fmt->wswap)
                control |= SAA7134_RS_CONTROL_WSWAP;
-       if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) {
+       if (V4L2_FIELD_HAS_BOTH(dev->field)) {
                /* interlaced */
                saa_writel(SAA7134_RS_BA1(0),base);
                saa_writel(SAA7134_RS_BA2(0),base+bpl);
@@ -858,17 +829,17 @@ static int buffer_activate(struct saa7134_dev *dev,
        }
        saa_writel(SAA7134_RS_CONTROL(0),control);
 
-       if (buf->fmt->planar) {
+       if (dev->fmt->planar) {
                /* DMA: setup channel 4+5 (= planar task A) */
-               bpl_uv   = bpl >> buf->fmt->hshift;
-               lines_uv = buf->vb.height >> buf->fmt->vshift;
-               base2    = base + bpl * buf->vb.height;
+               bpl_uv   = bpl >> dev->fmt->hshift;
+               lines_uv = dev->height >> dev->fmt->vshift;
+               base2    = base + bpl * dev->height;
                base3    = base2 + bpl_uv * lines_uv;
-               if (buf->fmt->uvswap)
+               if (dev->fmt->uvswap)
                        tmp = base2, base2 = base3, base3 = tmp;
                dprintk("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n",
                        bpl_uv,lines_uv,base2,base3);
-               if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) {
+               if (V4L2_FIELD_HAS_BOTH(dev->field)) {
                        /* interlaced */
                        saa_writel(SAA7134_RS_BA1(4),base2);
                        saa_writel(SAA7134_RS_BA2(4),base2+bpl_uv);
@@ -891,22 +862,65 @@ static int buffer_activate(struct saa7134_dev *dev,
 
        /* start DMA */
        saa7134_set_dmabits(dev);
-       mod_timer(&dev->video_q.timeout, jiffies+BUFFER_TIMEOUT);
+       mod_timer(&dmaq->timeout, jiffies + BUFFER_TIMEOUT);
+       return 0;
+}
+
+static int buffer_init(struct vb2_buffer *vb2)
+{
+       struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
+       struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
+
+       dmaq->curr = NULL;
+       buf->activate = buffer_activate;
        return 0;
 }
 
-static int buffer_prepare(struct videobuf_queue *q,
-                         struct videobuf_buffer *vb,
-                         enum v4l2_field field)
+static int buffer_prepare(struct vb2_buffer *vb2)
 {
-       struct saa7134_dev *dev = q->priv_data;
-       struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+       struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
+       struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
+       struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0);
        unsigned int size;
-       int err;
+       int ret;
 
-       /* sanity checks */
-       if (NULL == dev->fmt)
+       if (dma->sgl->offset) {
+               pr_err("The buffer is not page-aligned\n");
                return -EINVAL;
+       }
+       size = (dev->width * dev->height * dev->fmt->depth) >> 3;
+       if (vb2_plane_size(vb2, 0) < size)
+               return -EINVAL;
+
+       vb2_set_plane_payload(vb2, 0, size);
+       vb2->v4l2_buf.field = dev->field;
+
+       ret = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
+       if (!ret)
+               return -EIO;
+       return saa7134_pgtable_build(dev->pci, &dmaq->pt, dma->sgl, dma->nents,
+                                   saa7134_buffer_startpage(buf));
+}
+
+static void buffer_finish(struct vb2_buffer *vb2)
+{
+       struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
+       struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
+       struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0);
+
+       dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
+}
+
+static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+                          unsigned int *nbuffers, unsigned int *nplanes,
+                          unsigned int sizes[], void *alloc_ctxs[])
+{
+       struct saa7134_dmaqueue *dmaq = q->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
+       int size = dev->fmt->depth * dev->width * dev->height >> 3;
+
        if (dev->width    < 48 ||
            dev->height   < 32 ||
            dev->width/4  > dev->crop_current.width  ||
@@ -914,83 +928,88 @@ static int buffer_prepare(struct videobuf_queue *q,
            dev->width    > dev->crop_bounds.width  ||
            dev->height   > dev->crop_bounds.height)
                return -EINVAL;
-       size = (dev->width * dev->height * dev->fmt->depth) >> 3;
-       if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
-               return -EINVAL;
-
-       dprintk("buffer_prepare [%d,size=%dx%d,bytes=%d,fields=%s,%s]\n",
-               vb->i, dev->width, dev->height, size, v4l2_field_names[field],
-               dev->fmt->name);
-       if (buf->vb.width  != dev->width  ||
-           buf->vb.height != dev->height ||
-           buf->vb.size   != size       ||
-           buf->vb.field  != field      ||
-           buf->fmt       != dev->fmt) {
-               saa7134_dma_free(q,buf);
-       }
 
-       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
-               struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
-
-               buf->vb.width  = dev->width;
-               buf->vb.height = dev->height;
-               buf->vb.size   = size;
-               buf->vb.field  = field;
-               buf->fmt       = dev->fmt;
-               buf->pt        = &dev->pt_cap;
-               dev->video_q.curr = NULL;
-
-               err = videobuf_iolock(q,&buf->vb,&dev->ovbuf);
-               if (err)
-                       goto oops;
-               err = saa7134_pgtable_build(dev->pci,buf->pt,
-                                           dma->sglist,
-                                           dma->sglen,
-                                           saa7134_buffer_startpage(buf));
-               if (err)
-                       goto oops;
-       }
-       buf->vb.state = VIDEOBUF_PREPARED;
-       buf->activate = buffer_activate;
+       *nbuffers = saa7134_buffer_count(size, *nbuffers);
+       *nplanes = 1;
+       sizes[0] = size;
        return 0;
-
- oops:
-       saa7134_dma_free(q,buf);
-       return err;
 }
 
-static int
-buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+/*
+ * move buffer to hardware queue
+ */
+void saa7134_vb2_buffer_queue(struct vb2_buffer *vb)
 {
-       struct saa7134_dev *dev = q->priv_data;
+       struct saa7134_dmaqueue *dmaq = vb->vb2_queue->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
+       struct saa7134_buf *buf = container_of(vb, struct saa7134_buf, vb2);
 
-       *size = dev->fmt->depth * dev->width * dev->height >> 3;
-       if (0 == *count)
-               *count = gbuffers;
-       *count = saa7134_buffer_count(*size,*count);
-       return 0;
+       saa7134_buffer_queue(dev, dmaq, buf);
 }
+EXPORT_SYMBOL_GPL(saa7134_vb2_buffer_queue);
 
-static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+int saa7134_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
 {
-       struct saa7134_dev *dev = q->priv_data;
-       struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+       struct saa7134_dmaqueue *dmaq = vq->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
+
+       /*
+        * Planar video capture and TS share the same DMA channel,
+        * so only one can be active at a time.
+        */
+       if (card_is_empress(dev) && vb2_is_busy(&dev->empress_vbq) &&
+           dmaq == &dev->video_q && dev->fmt->planar) {
+               struct saa7134_buf *buf, *tmp;
 
-       saa7134_buffer_queue(dev, &dev->video_q, buf);
+               list_for_each_entry_safe(buf, tmp, &dmaq->queue, entry) {
+                       list_del(&buf->entry);
+                       vb2_buffer_done(&buf->vb2, VB2_BUF_STATE_QUEUED);
+               }
+               if (dmaq->curr) {
+                       vb2_buffer_done(&dmaq->curr->vb2, VB2_BUF_STATE_QUEUED);
+                       dmaq->curr = NULL;
+               }
+               return -EBUSY;
+       }
+
+       /* The SAA7134 has a 1K FIFO; the datasheet suggests that when
+        * configured conservatively, there's 22 usec of buffering for video.
+        * We therefore request a DMA latency of 20 usec, giving us 2 usec of
+        * margin in case the FIFO is configured differently to the datasheet.
+        * Unfortunately, I lack register-level documentation to check the
+        * Linux FIFO setup and confirm the perfect value.
+        */
+       if ((dmaq == &dev->video_q && !vb2_is_streaming(&dev->vbi_vbq)) ||
+           (dmaq == &dev->vbi_q && !vb2_is_streaming(&dev->video_vbq)))
+               pm_qos_add_request(&dev->qos_request,
+                       PM_QOS_CPU_DMA_LATENCY, 20);
+       dmaq->seq_nr = 0;
+
+       return 0;
 }
 
-static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+void saa7134_vb2_stop_streaming(struct vb2_queue *vq)
 {
-       struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+       struct saa7134_dmaqueue *dmaq = vq->drv_priv;
+       struct saa7134_dev *dev = dmaq->dev;
+
+       saa7134_stop_streaming(dev, dmaq);
 
-       saa7134_dma_free(q,buf);
+       if ((dmaq == &dev->video_q && !vb2_is_streaming(&dev->vbi_vbq)) ||
+           (dmaq == &dev->vbi_q && !vb2_is_streaming(&dev->video_vbq)))
+               pm_qos_remove_request(&dev->qos_request);
 }
 
-static struct videobuf_queue_ops video_qops = {
-       .buf_setup    = buffer_setup,
-       .buf_prepare  = buffer_prepare,
-       .buf_queue    = buffer_queue,
-       .buf_release  = buffer_release,
+static struct vb2_ops vb2_qops = {
+       .queue_setup    = queue_setup,
+       .buf_init       = buffer_init,
+       .buf_prepare    = buffer_prepare,
+       .buf_finish     = buffer_finish,
+       .buf_queue      = saa7134_vb2_buffer_queue,
+       .wait_prepare   = vb2_ops_wait_prepare,
+       .wait_finish    = vb2_ops_wait_finish,
+       .start_streaming = saa7134_vb2_start_streaming,
+       .stop_streaming = saa7134_vb2_stop_streaming,
 };
 
 /* ------------------------------------------------------------------ */
@@ -1068,7 +1087,7 @@ static int saa7134_s_ctrl(struct v4l2_ctrl *ctrl)
        default:
                return -EINVAL;
        }
-       if (restart_overlay && res_locked(dev, RESOURCE_OVERLAY)) {
+       if (restart_overlay && dev->overlay_owner) {
                spin_lock_irqsave(&dev->slock, flags);
                stop_preview(dev);
                start_preview(dev);
@@ -1079,182 +1098,57 @@ static int saa7134_s_ctrl(struct v4l2_ctrl *ctrl)
 
 /* ------------------------------------------------------------------ */
 
-static struct videobuf_queue *saa7134_queue(struct file *file)
+static inline struct vb2_queue *saa7134_queue(struct file *file)
 {
-       struct video_device *vdev = video_devdata(file);
-       struct saa7134_dev *dev = video_drvdata(file);
-       struct saa7134_fh *fh = file->private_data;
-       struct videobuf_queue *q = NULL;
-
-       switch (vdev->vfl_type) {
-       case VFL_TYPE_GRABBER:
-               q = fh->is_empress ? &dev->empress_tsq : &dev->cap;
-               break;
-       case VFL_TYPE_VBI:
-               q = &dev->vbi;
-               break;
-       default:
-               BUG();
-       }
-       return q;
-}
-
-static int saa7134_resource(struct file *file)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct saa7134_fh *fh = file->private_data;
-
-       if (vdev->vfl_type == VFL_TYPE_GRABBER)
-               return fh->is_empress ? RESOURCE_EMPRESS : RESOURCE_VIDEO;
-
-       if (vdev->vfl_type == VFL_TYPE_VBI)
-               return RESOURCE_VBI;
-
-       BUG();
-       return 0;
+       return video_devdata(file)->queue;
 }
 
 static int video_open(struct file *file)
 {
        struct video_device *vdev = video_devdata(file);
        struct saa7134_dev *dev = video_drvdata(file);
-       struct saa7134_fh *fh;
-
-       /* allocate + initialize per filehandle data */
-       fh = kzalloc(sizeof(*fh),GFP_KERNEL);
-       if (NULL == fh)
-               return -ENOMEM;
+       int ret = v4l2_fh_open(file);
 
-       v4l2_fh_init(&fh->fh, vdev);
-       file->private_data = fh;
+       if (ret < 0)
+               return ret;
 
+       mutex_lock(&dev->lock);
        if (vdev->vfl_type == VFL_TYPE_RADIO) {
                /* switch to radio mode */
-               saa7134_tvaudio_setinput(dev,&card(dev).radio);
+               saa7134_tvaudio_setinput(dev, &card(dev).radio);
                saa_call_all(dev, tuner, s_radio);
        } else {
                /* switch to video/vbi mode */
-               video_mux(dev,dev->ctl_input);
+               video_mux(dev, dev->ctl_input);
        }
-       v4l2_fh_add(&fh->fh);
+       mutex_unlock(&dev->lock);
 
        return 0;
 }
 
-static ssize_t
-video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct saa7134_dev *dev = video_drvdata(file);
-       struct saa7134_fh *fh = file->private_data;
-
-       switch (vdev->vfl_type) {
-       case VFL_TYPE_GRABBER:
-               if (res_locked(dev, RESOURCE_VIDEO))
-                       return -EBUSY;
-               return videobuf_read_one(saa7134_queue(file),
-                                        data, count, ppos,
-                                        file->f_flags & O_NONBLOCK);
-       case VFL_TYPE_VBI:
-               if (!res_get(dev, fh, RESOURCE_VBI))
-                       return -EBUSY;
-               return videobuf_read_stream(saa7134_queue(file),
-                                           data, count, ppos, 1,
-                                           file->f_flags & O_NONBLOCK);
-               break;
-       default:
-               BUG();
-               return 0;
-       }
-}
-
-static unsigned int
-video_poll(struct file *file, struct poll_table_struct *wait)
-{
-       unsigned long req_events = poll_requested_events(wait);
-       struct video_device *vdev = video_devdata(file);
-       struct saa7134_dev *dev = video_drvdata(file);
-       struct saa7134_fh *fh = file->private_data;
-       struct videobuf_buffer *buf = NULL;
-       unsigned int rc = 0;
-
-       if (v4l2_event_pending(&fh->fh))
-               rc = POLLPRI;
-       else if (req_events & POLLPRI)
-               poll_wait(file, &fh->fh.wait, wait);
-
-       if (vdev->vfl_type == VFL_TYPE_VBI)
-               return rc | videobuf_poll_stream(file, &dev->vbi, wait);
-
-       if (res_check(fh, RESOURCE_VIDEO)) {
-               mutex_lock(&dev->cap.vb_lock);
-               if (!list_empty(&dev->cap.stream))
-                       buf = list_entry(dev->cap.stream.next, struct videobuf_buffer, stream);
-       } else {
-               mutex_lock(&dev->cap.vb_lock);
-               if (UNSET == dev->cap.read_off) {
-                       /* need to capture a new frame */
-                       if (res_locked(dev, RESOURCE_VIDEO))
-                               goto err;
-                       if (0 != dev->cap.ops->buf_prepare(&dev->cap,
-                                       dev->cap.read_buf, dev->cap.field))
-                               goto err;
-                       dev->cap.ops->buf_queue(&dev->cap, dev->cap.read_buf);
-                       dev->cap.read_off = 0;
-               }
-               buf = dev->cap.read_buf;
-       }
-
-       if (!buf)
-               goto err;
-
-       poll_wait(file, &buf->done, wait);
-       if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR)
-               rc |= POLLIN | POLLRDNORM;
-       mutex_unlock(&dev->cap.vb_lock);
-       return rc;
-
-err:
-       mutex_unlock(&dev->cap.vb_lock);
-       return rc | POLLERR;
-}
-
 static int video_release(struct file *file)
 {
        struct video_device *vdev = video_devdata(file);
        struct saa7134_dev *dev = video_drvdata(file);
-       struct saa7134_fh *fh = file->private_data;
+       struct v4l2_fh *fh = file->private_data;
        struct saa6588_command cmd;
        unsigned long flags;
 
+       mutex_lock(&dev->lock);
        saa7134_tvaudio_close(dev);
 
        /* turn off overlay */
-       if (res_check(fh, RESOURCE_OVERLAY)) {
+       if (fh == dev->overlay_owner) {
                spin_lock_irqsave(&dev->slock,flags);
                stop_preview(dev);
                spin_unlock_irqrestore(&dev->slock,flags);
-               res_free(dev, fh, RESOURCE_OVERLAY);
-       }
-
-       /* stop video capture */
-       if (res_check(fh, RESOURCE_VIDEO)) {
-               pm_qos_remove_request(&dev->qos_request);
-               videobuf_streamoff(&dev->cap);
-               res_free(dev, fh, RESOURCE_VIDEO);
-               videobuf_mmap_free(&dev->cap);
-       }
-       if (dev->cap.read_buf) {
-               buffer_release(&dev->cap, dev->cap.read_buf);
-               kfree(dev->cap.read_buf);
+               dev->overlay_owner = NULL;
        }
 
-       /* stop vbi capture */
-       if (res_check(fh, RESOURCE_VBI)) {
-               videobuf_stop(&dev->vbi);
-               res_free(dev, fh, RESOURCE_VBI);
-               videobuf_mmap_free(&dev->vbi);
-       }
+       if (vdev->vfl_type == VFL_TYPE_RADIO)
+               v4l2_fh_release(file);
+       else
+               _vb2_fop_release(file, NULL);
 
        /* ts-capture will not work in planar mode, so turn it off Hac: 04.05*/
        saa_andorb(SAA7134_OFMT_VIDEO_A, 0x1f, 0);
@@ -1265,19 +1159,11 @@ static int video_release(struct file *file)
        saa_call_all(dev, core, s_power, 0);
        if (vdev->vfl_type == VFL_TYPE_RADIO)
                saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
+       mutex_unlock(&dev->lock);
 
-       v4l2_fh_del(&fh->fh);
-       v4l2_fh_exit(&fh->fh);
-       file->private_data = NULL;
-       kfree(fh);
        return 0;
 }
 
-static int video_mmap(struct file *file, struct vm_area_struct * vma)
-{
-       return videobuf_mmap_mapper(saa7134_queue(file), vma);
-}
-
 static ssize_t radio_read(struct file *file, char __user *data,
                         size_t count, loff_t *ppos)
 {
@@ -1290,7 +1176,9 @@ static ssize_t radio_read(struct file *file, char __user *data,
        cmd.instance = file;
        cmd.result = -ENODEV;
 
+       mutex_lock(&dev->lock);
        saa_call_all(dev, core, ioctl, SAA6588_CMD_READ, &cmd);
+       mutex_unlock(&dev->lock);
 
        return cmd.result;
 }
@@ -1304,7 +1192,9 @@ static unsigned int radio_poll(struct file *file, poll_table *wait)
        cmd.instance = file;
        cmd.event_list = wait;
        cmd.result = 0;
+       mutex_lock(&dev->lock);
        saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd);
+       mutex_unlock(&dev->lock);
 
        return rc | cmd.result;
 }
@@ -1338,7 +1228,7 @@ static int saa7134_g_fmt_vid_cap(struct file *file, void *priv,
 
        f->fmt.pix.width        = dev->width;
        f->fmt.pix.height       = dev->height;
-       f->fmt.pix.field        = dev->cap.field;
+       f->fmt.pix.field        = dev->field;
        f->fmt.pix.pixelformat  = dev->fmt->fourcc;
        f->fmt.pix.bytesperline =
                (f->fmt.pix.width * dev->fmt->depth) >> 3;
@@ -1362,7 +1252,6 @@ static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv,
                printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
                return -EINVAL;
        }
-       mutex_lock(&dev->lock);
        f->fmt.win = dev->win;
        f->fmt.win.clips = clips;
        if (clips == NULL)
@@ -1376,7 +1265,6 @@ static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv,
                                        sizeof(struct v4l2_rect)))
                        err = -EFAULT;
        }
-       mutex_unlock(&dev->lock);
 
        return err;
 }
@@ -1457,10 +1345,10 @@ static int saa7134_s_fmt_vid_cap(struct file *file, void *priv,
        if (0 != err)
                return err;
 
-       dev->fmt       = format_by_fourcc(f->fmt.pix.pixelformat);
-       dev->width     = f->fmt.pix.width;
-       dev->height    = f->fmt.pix.height;
-       dev->cap.field = f->fmt.pix.field;
+       dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+       dev->width = f->fmt.pix.width;
+       dev->height = f->fmt.pix.height;
+       dev->field = f->fmt.pix.field;
        return 0;
 }
 
@@ -1481,25 +1369,20 @@ static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv,
        if (0 != err)
                return err;
 
-       mutex_lock(&dev->lock);
-
        dev->win    = f->fmt.win;
        dev->nclips = f->fmt.win.clipcount;
 
        if (copy_from_user(dev->clips, f->fmt.win.clips,
-                          sizeof(struct v4l2_clip) * dev->nclips)) {
-               mutex_unlock(&dev->lock);
+                          sizeof(struct v4l2_clip) * dev->nclips))
                return -EFAULT;
-       }
 
-       if (res_check(priv, RESOURCE_OVERLAY)) {
+       if (priv == dev->overlay_owner) {
                spin_lock_irqsave(&dev->slock, flags);
                stop_preview(dev);
                start_preview(dev);
                spin_unlock_irqrestore(&dev->slock, flags);
        }
 
-       mutex_unlock(&dev->lock);
        return 0;
 }
 
@@ -1551,9 +1434,7 @@ int saa7134_s_input(struct file *file, void *priv, unsigned int i)
                return -EINVAL;
        if (NULL == card_in(dev, i).name)
                return -EINVAL;
-       mutex_lock(&dev->lock);
        video_mux(dev, i);
-       mutex_unlock(&dev->lock);
        return 0;
 }
 EXPORT_SYMBOL_GPL(saa7134_s_input);
@@ -1563,7 +1444,6 @@ int saa7134_querycap(struct file *file, void *priv,
 {
        struct saa7134_dev *dev = video_drvdata(file);
        struct video_device *vdev = video_devdata(file);
-       struct saa7134_fh *fh = priv;
        u32 radio_caps, video_caps, vbi_caps;
 
        unsigned int tuner_type = dev->tuner_type;
@@ -1582,7 +1462,7 @@ int saa7134_querycap(struct file *file, void *priv,
                radio_caps |= V4L2_CAP_RDS_CAPTURE;
 
        video_caps = V4L2_CAP_VIDEO_CAPTURE;
-       if (saa7134_no_overlay <= 0 && !fh->is_empress)
+       if (saa7134_no_overlay <= 0 && !is_empress(file))
                video_caps |= V4L2_CAP_VIDEO_OVERLAY;
 
        vbi_caps = V4L2_CAP_VBI_CAPTURE;
@@ -1613,12 +1493,12 @@ EXPORT_SYMBOL_GPL(saa7134_querycap);
 int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id)
 {
        struct saa7134_dev *dev = video_drvdata(file);
-       struct saa7134_fh *fh = priv;
+       struct v4l2_fh *fh = priv;
        unsigned long flags;
        unsigned int i;
        v4l2_std_id fixup;
 
-       if (fh->is_empress && res_locked(dev, RESOURCE_OVERLAY)) {
+       if (is_empress(file) && dev->overlay_owner) {
                /* Don't change the std from the mpeg device
                   if overlay is active. */
                return -EBUSY;
@@ -1657,8 +1537,7 @@ int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id)
 
        id = tvnorms[i].id;
 
-       mutex_lock(&dev->lock);
-       if (!fh->is_empress && res_check(fh, RESOURCE_OVERLAY)) {
+       if (!is_empress(file) && fh == dev->overlay_owner) {
                spin_lock_irqsave(&dev->slock, flags);
                stop_preview(dev);
                spin_unlock_irqrestore(&dev->slock, flags);
@@ -1672,7 +1551,6 @@ int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id)
                set_tvnorm(dev, &tvnorms[i]);
 
        saa7134_tvaudio_do_scan(dev);
-       mutex_unlock(&dev->lock);
        return 0;
 }
 EXPORT_SYMBOL_GPL(saa7134_s_std);
@@ -1686,6 +1564,35 @@ int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id)
 }
 EXPORT_SYMBOL_GPL(saa7134_g_std);
 
+static v4l2_std_id saa7134_read_std(struct saa7134_dev *dev)
+{
+       static v4l2_std_id stds[] = {
+               V4L2_STD_UNKNOWN,
+               V4L2_STD_NTSC,
+               V4L2_STD_PAL,
+               V4L2_STD_SECAM };
+
+       v4l2_std_id result = 0;
+
+       u8 st1 = saa_readb(SAA7134_STATUS_VIDEO1);
+       u8 st2 = saa_readb(SAA7134_STATUS_VIDEO2);
+
+       if (!(st2 & 0x1)) /* RDCAP == 0 */
+               result = V4L2_STD_UNKNOWN;
+       else
+               result = stds[st1 & 0x03];
+
+       return result;
+}
+
+int saa7134_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+       struct saa7134_dev *dev = video_drvdata(file);
+       *std &= saa7134_read_std(dev);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(saa7134_querystd);
+
 static int saa7134_cropcap(struct file *file, void *priv,
                                        struct v4l2_cropcap *cap)
 {
@@ -1730,9 +1637,9 @@ static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *cr
            crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
                return -EINVAL;
 
-       if (res_locked(dev, RESOURCE_OVERLAY))
+       if (dev->overlay_owner)
                return -EBUSY;
-       if (res_locked(dev, RESOURCE_VIDEO))
+       if (vb2_is_streaming(&dev->video_vbq))
                return -EBUSY;
 
        *c = crop->c;
@@ -1826,12 +1733,10 @@ int saa7134_s_frequency(struct file *file, void *priv,
 
        if (0 != f->tuner)
                return -EINVAL;
-       mutex_lock(&dev->lock);
 
        saa_call_all(dev, tuner, s_frequency, f);
 
        saa7134_tvaudio_do_scan(dev);
-       mutex_unlock(&dev->lock);
        return 0;
 }
 EXPORT_SYMBOL_GPL(saa7134_s_frequency);
@@ -1915,92 +1820,24 @@ static int saa7134_overlay(struct file *file, void *priv, unsigned int on)
                        return -EINVAL;
                }
 
-               if (!res_get(dev, priv, RESOURCE_OVERLAY))
+               if (dev->overlay_owner && priv != dev->overlay_owner)
                        return -EBUSY;
+               dev->overlay_owner = priv;
                spin_lock_irqsave(&dev->slock, flags);
                start_preview(dev);
                spin_unlock_irqrestore(&dev->slock, flags);
        }
        if (!on) {
-               if (!res_check(priv, RESOURCE_OVERLAY))
+               if (priv != dev->overlay_owner)
                        return -EINVAL;
                spin_lock_irqsave(&dev->slock, flags);
                stop_preview(dev);
                spin_unlock_irqrestore(&dev->slock, flags);
-               res_free(dev, priv, RESOURCE_OVERLAY);
+               dev->overlay_owner = NULL;
        }
        return 0;
 }
 
-int saa7134_reqbufs(struct file *file, void *priv,
-                                       struct v4l2_requestbuffers *p)
-{
-       return videobuf_reqbufs(saa7134_queue(file), p);
-}
-EXPORT_SYMBOL_GPL(saa7134_reqbufs);
-
-int saa7134_querybuf(struct file *file, void *priv,
-                                       struct v4l2_buffer *b)
-{
-       return videobuf_querybuf(saa7134_queue(file), b);
-}
-EXPORT_SYMBOL_GPL(saa7134_querybuf);
-
-int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
-       return videobuf_qbuf(saa7134_queue(file), b);
-}
-EXPORT_SYMBOL_GPL(saa7134_qbuf);
-
-int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
-       return videobuf_dqbuf(saa7134_queue(file), b,
-                               file->f_flags & O_NONBLOCK);
-}
-EXPORT_SYMBOL_GPL(saa7134_dqbuf);
-
-int saa7134_streamon(struct file *file, void *priv,
-                                       enum v4l2_buf_type type)
-{
-       struct saa7134_dev *dev = video_drvdata(file);
-       int res = saa7134_resource(file);
-
-       if (!res_get(dev, priv, res))
-               return -EBUSY;
-
-       /* The SAA7134 has a 1K FIFO; the datasheet suggests that when
-        * configured conservatively, there's 22 usec of buffering for video.
-        * We therefore request a DMA latency of 20 usec, giving us 2 usec of
-        * margin in case the FIFO is configured differently to the datasheet.
-        * Unfortunately, I lack register-level documentation to check the
-        * Linux FIFO setup and confirm the perfect value.
-        */
-       if (res != RESOURCE_EMPRESS)
-               pm_qos_add_request(&dev->qos_request,
-                          PM_QOS_CPU_DMA_LATENCY, 20);
-
-       return videobuf_streamon(saa7134_queue(file));
-}
-EXPORT_SYMBOL_GPL(saa7134_streamon);
-
-int saa7134_streamoff(struct file *file, void *priv,
-                                       enum v4l2_buf_type type)
-{
-       struct saa7134_dev *dev = video_drvdata(file);
-       int err;
-       int res = saa7134_resource(file);
-
-       if (res != RESOURCE_EMPRESS)
-               pm_qos_remove_request(&dev->qos_request);
-
-       err = videobuf_streamoff(saa7134_queue(file));
-       if (err < 0)
-               return err;
-       res_free(dev, priv, res);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(saa7134_streamoff);
-
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 static int vidioc_g_register (struct file *file, void *priv,
                              struct v4l2_dbg_register *reg)
@@ -2058,10 +1895,10 @@ static const struct v4l2_file_operations video_fops =
        .owner    = THIS_MODULE,
        .open     = video_open,
        .release  = video_release,
-       .read     = video_read,
-       .poll     = video_poll,
-       .mmap     = video_mmap,
-       .ioctl    = video_ioctl2,
+       .read     = vb2_fop_read,
+       .poll     = vb2_fop_poll,
+       .mmap     = vb2_fop_mmap,
+       .unlocked_ioctl   = video_ioctl2,
 };
 
 static const struct v4l2_ioctl_ops video_ioctl_ops = {
@@ -2078,17 +1915,18 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
        .vidioc_try_fmt_vbi_cap         = saa7134_try_get_set_fmt_vbi_cap,
        .vidioc_s_fmt_vbi_cap           = saa7134_try_get_set_fmt_vbi_cap,
        .vidioc_cropcap                 = saa7134_cropcap,
-       .vidioc_reqbufs                 = saa7134_reqbufs,
-       .vidioc_querybuf                = saa7134_querybuf,
-       .vidioc_qbuf                    = saa7134_qbuf,
-       .vidioc_dqbuf                   = saa7134_dqbuf,
+       .vidioc_reqbufs                 = vb2_ioctl_reqbufs,
+       .vidioc_querybuf                = vb2_ioctl_querybuf,
+       .vidioc_qbuf                    = vb2_ioctl_qbuf,
+       .vidioc_dqbuf                   = vb2_ioctl_dqbuf,
        .vidioc_s_std                   = saa7134_s_std,
        .vidioc_g_std                   = saa7134_g_std,
+       .vidioc_querystd                = saa7134_querystd,
        .vidioc_enum_input              = saa7134_enum_input,
        .vidioc_g_input                 = saa7134_g_input,
        .vidioc_s_input                 = saa7134_s_input,
-       .vidioc_streamon                = saa7134_streamon,
-       .vidioc_streamoff               = saa7134_streamoff,
+       .vidioc_streamon                = vb2_ioctl_streamon,
+       .vidioc_streamoff               = vb2_ioctl_streamoff,
        .vidioc_g_tuner                 = saa7134_g_tuner,
        .vidioc_s_tuner                 = saa7134_s_tuner,
        .vidioc_g_crop                  = saa7134_g_crop,
@@ -2112,7 +1950,7 @@ static const struct v4l2_file_operations radio_fops = {
        .open     = video_open,
        .read     = radio_read,
        .release  = video_release,
-       .ioctl    = video_ioctl2,
+       .unlocked_ioctl = video_ioctl2,
        .poll     = radio_poll,
 };
 
@@ -2190,6 +2028,8 @@ static const struct v4l2_ctrl_config saa7134_ctrl_automute = {
 int saa7134_video_init1(struct saa7134_dev *dev)
 {
        struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
+       struct vb2_queue *q;
+       int ret;
 
        /* sanitycheck insmod options */
        if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
@@ -2241,6 +2081,7 @@ int saa7134_video_init1(struct saa7134_dev *dev)
        dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
        dev->width    = 720;
        dev->height   = 576;
+       dev->field = V4L2_FIELD_INTERLACED;
        dev->win.w.width = dev->width;
        dev->win.w.height = dev->height;
        dev->win.field = V4L2_FIELD_INTERLACED;
@@ -2252,20 +2093,47 @@ int saa7134_video_init1(struct saa7134_dev *dev)
        if (saa7134_boards[dev->board].video_out)
                saa7134_videoport_init(dev);
 
-       videobuf_queue_sg_init(&dev->cap, &video_qops,
-                           &dev->pci->dev, &dev->slock,
-                           V4L2_BUF_TYPE_VIDEO_CAPTURE,
-                           V4L2_FIELD_INTERLACED,
-                           sizeof(struct saa7134_buf),
-                           dev, NULL);
-       videobuf_queue_sg_init(&dev->vbi, &saa7134_vbi_qops,
-                           &dev->pci->dev, &dev->slock,
-                           V4L2_BUF_TYPE_VBI_CAPTURE,
-                           V4L2_FIELD_SEQ_TB,
-                           sizeof(struct saa7134_buf),
-                           dev, NULL);
-       saa7134_pgtable_alloc(dev->pci, &dev->pt_cap);
-       saa7134_pgtable_alloc(dev->pci, &dev->pt_vbi);
+       q = &dev->video_vbq;
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       /*
+        * Do not add VB2_USERPTR unless explicitly requested: the saa7134 DMA
+        * engine cannot handle transfers that do not start at the beginning
+        * of a page. A user-provided pointer can start anywhere in a page, so
+        * USERPTR support is a no-go unless the application knows about these
+        * limitations and has special support for this.
+        */
+       q->io_modes = VB2_MMAP | VB2_READ;
+       if (saa7134_userptr)
+               q->io_modes |= VB2_USERPTR;
+       q->drv_priv = &dev->video_q;
+       q->ops = &vb2_qops;
+       q->gfp_flags = GFP_DMA32;
+       q->mem_ops = &vb2_dma_sg_memops;
+       q->buf_struct_size = sizeof(struct saa7134_buf);
+       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       q->lock = &dev->lock;
+       ret = vb2_queue_init(q);
+       if (ret)
+               return ret;
+       saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt);
+
+       q = &dev->vbi_vbq;
+       q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+       /* Don't add VB2_USERPTR, see comment above */
+       q->io_modes = VB2_MMAP | VB2_READ;
+       if (saa7134_userptr)
+               q->io_modes |= VB2_USERPTR;
+       q->drv_priv = &dev->vbi_q;
+       q->ops = &saa7134_vbi_qops;
+       q->gfp_flags = GFP_DMA32;
+       q->mem_ops = &vb2_dma_sg_memops;
+       q->buf_struct_size = sizeof(struct saa7134_buf);
+       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       q->lock = &dev->lock;
+       ret = vb2_queue_init(q);
+       if (ret)
+               return ret;
+       saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt);
 
        return 0;
 }
@@ -2273,8 +2141,10 @@ int saa7134_video_init1(struct saa7134_dev *dev)
 void saa7134_video_fini(struct saa7134_dev *dev)
 {
        /* free stuff */
-       saa7134_pgtable_free(dev->pci, &dev->pt_cap);
-       saa7134_pgtable_free(dev->pci, &dev->pt_vbi);
+       vb2_queue_release(&dev->video_vbq);
+       saa7134_pgtable_free(dev->pci, &dev->video_q.pt);
+       vb2_queue_release(&dev->vbi_vbq);
+       saa7134_pgtable_free(dev->pci, &dev->vbi_q.pt);
        v4l2_ctrl_handler_free(&dev->ctrl_handler);
        if (card_has_radio(dev))
                v4l2_ctrl_handler_free(&dev->radio_ctrl_handler);
@@ -2367,8 +2237,7 @@ void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status)
 
        spin_lock(&dev->slock);
        if (dev->video_q.curr) {
-               dev->video_fieldcount++;
-               field = dev->video_q.curr->vb.field;
+               field = dev->field;
                if (V4L2_FIELD_HAS_BOTH(field)) {
                        /* make sure we have seen both fields */
                        if ((status & 0x10) == 0x00) {
@@ -2384,18 +2253,10 @@ void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status)
                        if ((status & 0x10) != 0x00)
                                goto done;
                }
-               dev->video_q.curr->vb.field_count = dev->video_fieldcount;
-               saa7134_buffer_finish(dev,&dev->video_q,VIDEOBUF_DONE);
+               saa7134_buffer_finish(dev, &dev->video_q, VB2_BUF_STATE_DONE);
        }
-       saa7134_buffer_next(dev,&dev->video_q);
+       saa7134_buffer_next(dev, &dev->video_q);
 
  done:
        spin_unlock(&dev->slock);
 }
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
index 2474e84..e47edd4 100644 (file)
 #include <media/tuner.h>
 #include <media/rc-core.h>
 #include <media/ir-kbd-i2c.h>
-#include <media/videobuf-dma-sg.h>
+#include <media/videobuf2-dma-sg.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB)
-#include <media/videobuf-dvb.h>
+#include <media/videobuf2-dvb.h>
 #endif
 #include "tda8290.h"
 
@@ -453,17 +453,15 @@ struct saa7134_thread {
 /* buffer for one video/vbi/ts frame */
 struct saa7134_buf {
        /* common v4l buffer stuff -- must be first */
-       struct videobuf_buffer vb;
+       struct vb2_buffer vb2;
 
        /* saa7134 specific */
-       struct saa7134_format   *fmt;
        unsigned int            top_seen;
        int (*activate)(struct saa7134_dev *dev,
                        struct saa7134_buf *buf,
                        struct saa7134_buf *next);
 
-       /* page tables */
-       struct saa7134_pgtable  *pt;
+       struct list_head        entry;
 };
 
 struct saa7134_dmaqueue {
@@ -472,13 +470,8 @@ struct saa7134_dmaqueue {
        struct list_head           queue;
        struct timer_list          timeout;
        unsigned int               need_two;
-};
-
-/* video filehandle status */
-struct saa7134_fh {
-       struct v4l2_fh             fh;
-       bool                       is_empress;
-       unsigned int               resources;
+       unsigned int               seq_nr;
+       struct saa7134_pgtable     pt;
 };
 
 /* dmasound dsp status */
@@ -504,7 +497,10 @@ struct saa7134_dmasound {
        unsigned int               blksize;
        unsigned int               bufsize;
        struct saa7134_pgtable     pt;
-       struct videobuf_dmabuf     dma;
+       void                       *vaddr;
+       struct scatterlist         *sglist;
+       int                        sglen;
+       int                        nr_pages;
        unsigned int               dma_blk;
        unsigned int               read_offset;
        unsigned int               read_count;
@@ -515,7 +511,6 @@ struct saa7134_dmasound {
 /* ts/mpeg status */
 struct saa7134_ts {
        /* TS capture */
-       struct saa7134_pgtable     pt_ts;
        int                        nr_packets;
        int                        nr_bufs;
 };
@@ -584,21 +579,35 @@ struct saa7134_dev {
        struct v4l2_window         win;
        struct v4l2_clip           clips[8];
        unsigned int               nclips;
+       struct v4l2_fh             *overlay_owner;
 
 
        /* video+ts+vbi capture */
        struct saa7134_dmaqueue    video_q;
-       struct videobuf_queue      cap;
-       struct saa7134_pgtable     pt_cap;
+       struct vb2_queue           video_vbq;
        struct saa7134_dmaqueue    vbi_q;
-       struct videobuf_queue      vbi;
-       struct saa7134_pgtable     pt_vbi;
-       unsigned int               video_fieldcount;
-       unsigned int               vbi_fieldcount;
+       struct vb2_queue           vbi_vbq;
+       enum v4l2_field            field;
        struct saa7134_format      *fmt;
        unsigned int               width, height;
+       unsigned int               vbi_hlen, vbi_vlen;
        struct pm_qos_request      qos_request;
 
+       /* SAA7134_MPEG_* */
+       struct saa7134_ts          ts;
+       struct saa7134_dmaqueue    ts_q;
+       enum v4l2_field            ts_field;
+       int                        ts_started;
+       struct saa7134_mpeg_ops    *mops;
+
+       /* SAA7134_MPEG_EMPRESS only */
+       struct video_device        *empress_dev;
+       struct v4l2_subdev         *empress_sd;
+       struct vb2_queue           empress_vbq;
+       struct work_struct         empress_workqueue;
+       int                        empress_started;
+       struct v4l2_ctrl_handler   empress_ctrl_handler;
+
        /* various v4l controls */
        struct saa7134_tvnorm      *tvnorm;              /* video */
        struct saa7134_tvaudio     *tvaudio;
@@ -635,23 +644,9 @@ struct saa7134_dev {
        /* I2C keyboard data */
        struct IR_i2c_init_data    init_data;
 
-       /* SAA7134_MPEG_* */
-       struct saa7134_ts          ts;
-       struct saa7134_dmaqueue    ts_q;
-       int                        ts_started;
-       struct saa7134_mpeg_ops    *mops;
-
-       /* SAA7134_MPEG_EMPRESS only */
-       struct video_device        *empress_dev;
-       struct v4l2_subdev         *empress_sd;
-       struct videobuf_queue      empress_tsq;
-       struct work_struct         empress_workqueue;
-       int                        empress_started;
-       struct v4l2_ctrl_handler   empress_ctrl_handler;
-
 #if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB)
        /* SAA7134_MPEG_DVB only */
-       struct videobuf_dvb_frontends frontends;
+       struct vb2_dvb_frontends frontends;
        int (*original_demod_sleep)(struct dvb_frontend *fe);
        int (*original_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
        int (*original_set_high_voltage)(struct dvb_frontend *fe, long arg);
@@ -705,14 +700,12 @@ struct saa7134_dev {
        _rc;                                                            \
 })
 
-static inline int res_check(struct saa7134_fh *fh, unsigned int bit)
+static inline bool is_empress(struct file *file)
 {
-       return fh->resources & bit;
-}
+       struct video_device *vdev = video_devdata(file);
+       struct saa7134_dev *dev = video_get_drvdata(vdev);
 
-static inline int res_locked(struct saa7134_dev *dev, unsigned int bit)
-{
-       return dev->resources & bit;
+       return vdev->queue == &dev->empress_vbq;
 }
 
 /* ----------------------------------------------------------- */
@@ -721,6 +714,7 @@ static inline int res_locked(struct saa7134_dev *dev, unsigned int bit)
 extern struct list_head  saa7134_devlist;
 extern struct mutex saa7134_devlist_lock;
 extern int saa7134_no_overlay;
+extern bool saa7134_userptr;
 
 void saa7134_track_gpio(struct saa7134_dev *dev, char *msg);
 void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value);
@@ -743,7 +737,7 @@ void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q,
                           unsigned int state);
 void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q);
 void saa7134_buffer_timeout(unsigned long data);
-void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf);
+void saa7134_stop_streaming(struct saa7134_dev *dev, struct saa7134_dmaqueue *q);
 
 int saa7134_set_dmabits(struct saa7134_dev *dev);
 
@@ -777,8 +771,13 @@ extern unsigned int video_debug;
 extern struct video_device saa7134_video_template;
 extern struct video_device saa7134_radio_template;
 
+void saa7134_vb2_buffer_queue(struct vb2_buffer *vb);
+int saa7134_vb2_start_streaming(struct vb2_queue *vq, unsigned int count);
+void saa7134_vb2_stop_streaming(struct vb2_queue *vq);
+
 int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id);
 int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id);
+int saa7134_querystd(struct file *file, void *priv, v4l2_std_id *std);
 int saa7134_enum_input(struct file *file, void *priv, struct v4l2_input *i);
 int saa7134_g_input(struct file *file, void *priv, unsigned int *i);
 int saa7134_s_input(struct file *file, void *priv, unsigned int i);
@@ -792,16 +791,6 @@ int saa7134_g_frequency(struct file *file, void *priv,
                                        struct v4l2_frequency *f);
 int saa7134_s_frequency(struct file *file, void *priv,
                                        const struct v4l2_frequency *f);
-int saa7134_reqbufs(struct file *file, void *priv,
-                                       struct v4l2_requestbuffers *p);
-int saa7134_querybuf(struct file *file, void *priv,
-                                       struct v4l2_buffer *b);
-int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b);
-int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b);
-int saa7134_streamon(struct file *file, void *priv,
-                                       enum v4l2_buf_type type);
-int saa7134_streamoff(struct file *file, void *priv,
-                                       enum v4l2_buf_type type);
 
 int saa7134_videoport_init(struct saa7134_dev *dev);
 void saa7134_set_tvnorm_hw(struct saa7134_dev *dev);
@@ -818,7 +807,16 @@ void saa7134_video_fini(struct saa7134_dev *dev);
 
 #define TS_PACKET_SIZE 188 /* TS packets 188 bytes */
 
-extern struct videobuf_queue_ops saa7134_ts_qops;
+int saa7134_ts_buffer_init(struct vb2_buffer *vb2);
+int saa7134_ts_buffer_prepare(struct vb2_buffer *vb2);
+void saa7134_ts_buffer_finish(struct vb2_buffer *vb2);
+int saa7134_ts_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+                          unsigned int *nbuffers, unsigned int *nplanes,
+                          unsigned int sizes[], void *alloc_ctxs[]);
+int saa7134_ts_start_streaming(struct vb2_queue *vq, unsigned int count);
+void saa7134_ts_stop_streaming(struct vb2_queue *vq);
+
+extern struct vb2_ops saa7134_ts_qops;
 
 int saa7134_ts_init1(struct saa7134_dev *dev);
 int saa7134_ts_fini(struct saa7134_dev *dev);
@@ -835,7 +833,7 @@ int saa7134_ts_stop(struct saa7134_dev *dev);
 /* ----------------------------------------------------------- */
 /* saa7134-vbi.c                                               */
 
-extern struct videobuf_queue_ops saa7134_vbi_qops;
+extern struct vb2_ops saa7134_vbi_qops;
 extern struct video_device saa7134_vbi_template;
 
 int saa7134_vbi_init1(struct saa7134_dev *dev);
index 33abe33..c4c8fce 100644 (file)
@@ -357,7 +357,7 @@ static int mxb_init_done(struct saa7146_dev* dev)
        tea6420_route(mxb, 6);
 
        /* select video mode in saa7111a */
-       saa7111a_call(mxb, core, s_std, std);
+       saa7111a_call(mxb, video, s_std, std);
 
        /* select tuner-output on saa7111a */
        i = 0;
@@ -379,8 +379,8 @@ static int mxb_init_done(struct saa7146_dev* dev)
        /* These two gpio calls set the GPIO pins that control the tda9820 */
        saa7146_write(dev, GPIO_CTRL, 0x00404050);
        saa7111a_call(mxb, core, s_gpio, 1);
-       saa7111a_call(mxb, core, s_std, std);
-       tuner_call(mxb, core, s_std, std);
+       saa7111a_call(mxb, video, s_std, std);
+       tuner_call(mxb, video, s_std, std);
 
        /* switch to tuner-channel on tea6415c */
        tea6415c_call(mxb, video, s_routing, 3, 17, 0);
@@ -771,9 +771,9 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa
                /* These two gpio calls set the GPIO pins that control the tda9820 */
                saa7146_write(dev, GPIO_CTRL, 0x00404050);
                saa7111a_call(mxb, core, s_gpio, 0);
-               saa7111a_call(mxb, core, s_std, std);
+               saa7111a_call(mxb, video, s_std, std);
                if (mxb->cur_input == 0)
-                       tuner_call(mxb, core, s_std, std);
+                       tuner_call(mxb, video, s_std, std);
        } else {
                v4l2_std_id std = V4L2_STD_PAL_BG;
 
@@ -783,9 +783,9 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa
                /* These two gpio calls set the GPIO pins that control the tda9820 */
                saa7146_write(dev, GPIO_CTRL, 0x00404050);
                saa7111a_call(mxb, core, s_gpio, 1);
-               saa7111a_call(mxb, core, s_std, std);
+               saa7111a_call(mxb, video, s_std, std);
                if (mxb->cur_input == 0)
-                       tuner_call(mxb, core, s_std, std);
+                       tuner_call(mxb, video, s_std, std);
        }
        return 0;
 }
index bb11443..d2abd3b 100644 (file)
@@ -357,7 +357,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
 }
 
 /* abort streaming and wait for last buffer */
-static int stop_streaming(struct vb2_queue *vq)
+static void stop_streaming(struct vb2_queue *vq)
 {
        struct sta2x11_vip *vip = vb2_get_drv_priv(vq);
        struct vip_buffer *vip_buf, *node;
@@ -374,7 +374,6 @@ static int stop_streaming(struct vb2_queue *vq)
                list_del(&vip_buf->list);
        }
        spin_unlock(&vip->lock);
-       return 0;
 }
 
 static struct vb2_ops vip_video_qops = {
@@ -445,7 +444,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std)
        int status;
 
        if (V4L2_STD_ALL == std) {
-               v4l2_subdev_call(vip->decoder, core, s_std, std);
+               v4l2_subdev_call(vip->decoder, video, s_std, std);
                ssleep(2);
                v4l2_subdev_call(vip->decoder, video, querystd, &newstd);
                v4l2_subdev_call(vip->decoder, video, g_input_status, &status);
@@ -468,7 +467,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std)
                        vip->format = formats_50[0];
        }
 
-       return v4l2_subdev_call(vip->decoder, core, s_std, std);
+       return v4l2_subdev_call(vip->decoder, video, s_std, std);
 }
 
 /**
index 301029c..9544cfc 100644 (file)
@@ -958,8 +958,10 @@ static unsigned int dvb_video_poll(struct file *file, poll_table *wait)
                if (av7110->playing) {
                        if (FREE_COND)
                                mask |= (POLLOUT | POLLWRNORM);
-                       } else /* if not playing: may play if asked for */
-                               mask |= (POLLOUT | POLLWRNORM);
+               } else {
+                       /* if not playing: may play if asked for */
+                       mask |= (POLLOUT | POLLWRNORM);
+               }
        }
 
        return mask;
index 519164c..bf34b93 100644 (file)
@@ -1572,7 +1572,7 @@ zoran_init_hardware (struct zoran *zr)
        }
 
        decoder_call(zr, core, init, 0);
-       decoder_call(zr, core, s_std, zr->norm);
+       decoder_call(zr, video, s_std, zr->norm);
        decoder_call(zr, video, s_routing,
                zr->card.input[zr->input].muxsel, 0, 0);
 
index e7e9840..099d5fb 100644 (file)
@@ -1469,7 +1469,7 @@ zoran_set_norm (struct zoran *zr,
        if (on)
                zr36057_overlay(zr, 0);
 
-       decoder_call(zr, core, s_std, norm);
+       decoder_call(zr, video, s_std, norm);
        encoder_call(zr, video, s_std_output, norm);
 
        if (on)
index 200bec9..16e4b1c 100644 (file)
@@ -427,15 +427,12 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
        return 0;
 }
 
-static int bcap_stop_streaming(struct vb2_queue *vq)
+static void bcap_stop_streaming(struct vb2_queue *vq)
 {
        struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
        struct ppi_if *ppi = bcap_dev->ppi;
        int ret;
 
-       if (!vb2_is_streaming(vq))
-               return 0;
-
        bcap_dev->stop = true;
        wait_for_completion(&bcap_dev->comp);
        ppi->ops->stop(ppi);
@@ -452,7 +449,6 @@ static int bcap_stop_streaming(struct vb2_queue *vq)
                list_del(&bcap_dev->cur_frm->list);
                vb2_buffer_done(&bcap_dev->cur_frm->vb, VB2_BUF_STATE_ERROR);
        }
-       return 0;
 }
 
 static struct vb2_ops bcap_video_qops = {
@@ -635,7 +631,7 @@ static int bcap_s_std(struct file *file, void *priv, v4l2_std_id std)
        if (vb2_is_busy(&bcap_dev->buffer_queue))
                return -EBUSY;
 
-       ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, std);
+       ret = v4l2_subdev_call(bcap_dev->sd, video, s_std, std);
        if (ret < 0)
                return ret;
 
@@ -648,7 +644,9 @@ static int bcap_enum_dv_timings(struct file *file, void *priv,
 {
        struct bcap_device *bcap_dev = video_drvdata(file);
 
-       return v4l2_subdev_call(bcap_dev->sd, video,
+       timings->pad = 0;
+
+       return v4l2_subdev_call(bcap_dev->sd, pad,
                        enum_dv_timings, timings);
 }
 
@@ -1069,7 +1067,7 @@ static int bcap_probe(struct platform_device *pdev)
        /* now we can probe the default state */
        if (config->inputs[0].capabilities & V4L2_IN_CAP_STD) {
                v4l2_std_id std;
-               ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std);
+               ret = v4l2_subdev_call(bcap_dev->sd, video, g_std, &std);
                if (ret) {
                        v4l2_err(&bcap_dev->v4l2_dev,
                                        "Unable to get std\n");
index 3e5199e..b178379 100644 (file)
@@ -2269,7 +2269,7 @@ out:
        return ret;
 }
 
-static int coda_stop_streaming(struct vb2_queue *q)
+static void coda_stop_streaming(struct vb2_queue *q)
 {
        struct coda_ctx *ctx = vb2_get_drv_priv(q);
        struct coda_dev *dev = ctx->dev;
@@ -2295,8 +2295,6 @@ static int coda_stop_streaming(struct vb2_queue *q)
                        ctx->bitstream.vaddr, ctx->bitstream.size);
                ctx->runcounter = 0;
        }
-
-       return 0;
 }
 
 static struct vb2_ops coda_qops = {
@@ -3235,7 +3233,7 @@ static int coda_probe(struct platform_device *pdev)
        }
 
        if (devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler,
-               IRQF_ONESHOT, CODA_NAME, dev) < 0) {
+               IRQF_ONESHOT, dev_name(&pdev->dev), dev) < 0) {
                dev_err(&pdev->dev, "failed to request irq\n");
                return -ENOENT;
        }
index 6567082..bf5eff9 100644 (file)
@@ -355,8 +355,17 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
 
        /* Set parameters in OSD and VENC */
        ret = vpbe_set_osd_display_params(fh->disp_dev, layer);
-       if (ret < 0)
+       if (ret < 0) {
+               struct vpbe_disp_buffer *buf, *tmp;
+
+               vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_QUEUED);
+               list_for_each_entry_safe(buf, tmp, &layer->dma_queue, list) {
+                       list_del(&buf->list);
+                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+               }
+
                return ret;
+       }
 
        /*
         * if request format is yuv420 semiplanar, need to
@@ -368,7 +377,7 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
        return ret;
 }
 
-static int vpbe_stop_streaming(struct vb2_queue *vq)
+static void vpbe_stop_streaming(struct vb2_queue *vq)
 {
        struct vpbe_fh *fh = vb2_get_drv_priv(vq);
        struct vpbe_layer *layer = fh->layer;
@@ -376,7 +385,7 @@ static int vpbe_stop_streaming(struct vb2_queue *vq)
        unsigned long flags;
 
        if (!vb2_is_streaming(vq))
-               return 0;
+               return;
 
        /* release all active buffers */
        spin_lock_irqsave(&disp->dma_queue_lock, flags);
@@ -398,7 +407,6 @@ static int vpbe_stop_streaming(struct vb2_queue *vq)
                vb2_buffer_done(&layer->next_frm->vb, VB2_BUF_STATE_ERROR);
        }
        spin_unlock_irqrestore(&disp->dma_queue_lock, flags);
-       return 0;
 }
 
 static struct vb2_ops video_qops = {
@@ -680,29 +688,6 @@ static int vpbe_try_format(struct vpbe_display *disp_dev,
        return 0;
 }
 
-static int vpbe_display_g_priority(struct file *file, void *priv,
-                               enum v4l2_priority *p)
-{
-       struct vpbe_fh *fh = file->private_data;
-       struct vpbe_layer *layer = fh->layer;
-
-       *p = v4l2_prio_max(&layer->prio);
-
-       return 0;
-}
-
-static int vpbe_display_s_priority(struct file *file, void *priv,
-                               enum v4l2_priority p)
-{
-       struct vpbe_fh *fh = file->private_data;
-       struct vpbe_layer *layer = fh->layer;
-       int ret;
-
-       ret = v4l2_prio_change(&layer->prio, &fh->prio, p);
-
-       return ret;
-}
-
 static int vpbe_display_querycap(struct file *file, void  *priv,
                               struct v4l2_capability *cap)
 {
@@ -1492,6 +1477,7 @@ static int vpbe_display_open(struct file *file)
 {
        struct vpbe_fh *fh = NULL;
        struct vpbe_layer *layer = video_drvdata(file);
+       struct video_device *vdev = video_devdata(file);
        struct vpbe_display *disp_dev = layer->disp_dev;
        struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
        struct osd_state *osd_device = disp_dev->osd_device;
@@ -1504,6 +1490,7 @@ static int vpbe_display_open(struct file *file)
                        "unable to allocate memory for file handle object\n");
                return -ENOMEM;
        }
+       v4l2_fh_init(&fh->fh, vdev);
        v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
                        "vpbe display open plane = %d\n",
                        layer->device_id);
@@ -1532,9 +1519,7 @@ static int vpbe_display_open(struct file *file)
        layer->usrs++;
        /* Set io_allowed member to false */
        fh->io_allowed = 0;
-       /* Initialize priority of this instance to default priority */
-       fh->prio = V4L2_PRIORITY_UNSET;
-       v4l2_prio_open(&layer->prio, &fh->prio);
+       v4l2_fh_add(&fh->fh);
        v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
                        "vpbe display device opened successfully\n");
        return 0;
@@ -1589,8 +1574,9 @@ static int vpbe_display_release(struct file *file)
                osd_device->ops.release_layer(osd_device,
                                layer->layer_info.id);
        }
-       /* Close the priority */
-       v4l2_prio_close(&layer->prio, fh->prio);
+
+       v4l2_fh_del(&fh->fh);
+       v4l2_fh_exit(&fh->fh);
        file->private_data = NULL;
        mutex_unlock(&layer->opslock);
 
@@ -1618,8 +1604,6 @@ static const struct v4l2_ioctl_ops vpbe_ioctl_ops = {
        .vidioc_cropcap          = vpbe_display_cropcap,
        .vidioc_g_crop           = vpbe_display_g_crop,
        .vidioc_s_crop           = vpbe_display_s_crop,
-       .vidioc_g_priority       = vpbe_display_g_priority,
-       .vidioc_s_priority       = vpbe_display_s_priority,
        .vidioc_s_std            = vpbe_display_s_std,
        .vidioc_g_std            = vpbe_display_g_std,
        .vidioc_enum_output      = vpbe_display_enum_output,
@@ -1699,8 +1683,6 @@ static int init_vpbe_layer(int i, struct vpbe_display *disp_dev,
        vpbe_display_layer->layer_info.id =
                ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1);
 
-       /* Initialize prio member of layer object */
-       v4l2_prio_init(&vpbe_display_layer->prio);
 
        return 0;
 }
@@ -1727,6 +1709,7 @@ static int register_device(struct vpbe_layer *vpbe_display_layer,
        vpbe_display_layer->disp_dev = disp_dev;
        /* set the driver data in platform device */
        platform_set_drvdata(pdev, disp_dev);
+       set_bit(V4L2_FL_USE_FH_PRIO, &vpbe_display_layer->video_dev.flags);
        video_set_drvdata(&vpbe_display_layer->video_dev,
                          vpbe_display_layer);
 
index 0379cb9..a51bda2 100644 (file)
@@ -498,6 +498,7 @@ unlock:
 static int vpfe_open(struct file *file)
 {
        struct vpfe_device *vpfe_dev = video_drvdata(file);
+       struct video_device *vdev = video_devdata(file);
        struct vpfe_fh *fh;
 
        v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n");
@@ -517,6 +518,7 @@ static int vpfe_open(struct file *file)
        /* store pointer to fh in private_data member of file */
        file->private_data = fh;
        fh->vpfe_dev = vpfe_dev;
+       v4l2_fh_init(&fh->fh, vdev);
        mutex_lock(&vpfe_dev->lock);
        /* If decoder is not initialized. initialize it */
        if (!vpfe_dev->initialized) {
@@ -529,9 +531,7 @@ static int vpfe_open(struct file *file)
        vpfe_dev->usrs++;
        /* Set io_allowed member to false */
        fh->io_allowed = 0;
-       /* Initialize priority of this instance to default priority */
-       fh->prio = V4L2_PRIORITY_UNSET;
-       v4l2_prio_open(&vpfe_dev->prio, &fh->prio);
+       v4l2_fh_add(&fh->fh);
        mutex_unlock(&vpfe_dev->lock);
        return 0;
 }
@@ -740,8 +740,8 @@ static int vpfe_release(struct file *file)
 
        /* Decrement device usrs counter */
        vpfe_dev->usrs--;
-       /* Close the priority */
-       v4l2_prio_close(&vpfe_dev->prio, fh->prio);
+       v4l2_fh_del(&fh->fh);
+       v4l2_fh_exit(&fh->fh);
        /* If this is the last file handle */
        if (!vpfe_dev->usrs) {
                vpfe_dev->initialized = 0;
@@ -1217,7 +1217,7 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id)
        }
 
        ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
-                                        core, s_std, std_id);
+                                        video, s_std, std_id);
        if (ret < 0) {
                v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n");
                goto unlock_out;
@@ -1910,14 +1910,13 @@ static int vpfe_probe(struct platform_device *pdev)
        /* Initialize field of the device objects */
        vpfe_dev->numbuffers = config_params.numbuffers;
 
-       /* Initialize prio member of device object */
-       v4l2_prio_init(&vpfe_dev->prio);
        /* register video device */
        v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
                "trying to register vpfe device.\n");
        v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
                "video_dev=%x\n", (int)&vpfe_dev->video_dev);
        vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       set_bit(V4L2_FL_USE_FH_PRIO, &vpfe_dev->video_dev->flags);
        ret = video_register_device(vpfe_dev->video_dev,
                                    VFL_TYPE_GRABBER, -1);
 
index 8dea0b8..a7ed164 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009 Texas Instruments Inc
+ * Copyright (C) 2014 Lad, Prabhakar <prabhakar.csengg@gmail.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
@@ -65,14 +66,26 @@ static struct vpif_config_params config_params = {
        .channel_bufsize[1] = 720 * 576 * 2,
 };
 
+#define VPIF_DRIVER_NAME       "vpif_capture"
+
 /* global variables */
 static struct vpif_device vpif_obj = { {NULL} };
 static struct device *vpif_dev;
 static void vpif_calculate_offsets(struct channel_obj *ch);
 static void vpif_config_addr(struct channel_obj *ch, int muxmode);
 
+static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] = { {1, 1} };
+
+/* Is set to 1 in case of SDTV formats, 2 in case of HDTV formats. */
+static int ycmux_mode;
+
+static inline struct vpif_cap_buffer *to_vpif_buffer(struct vb2_buffer *vb)
+{
+       return container_of(vb, struct vpif_cap_buffer, vb);
+}
+
 /**
- * buffer_prepare :  callback function for buffer prepare
+ * vpif_buffer_prepare :  callback function for buffer prepare
  * @vb: ptr to vb2_buffer
  *
  * This is the callback function for buffer prepare when vb2_qbuf()
@@ -81,10 +94,8 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode);
  */
 static int vpif_buffer_prepare(struct vb2_buffer *vb)
 {
-       /* Get the file handle object and channel object */
-       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
        struct vb2_queue *q = vb->vb2_queue;
-       struct channel_obj *ch = fh->channel;
+       struct channel_obj *ch = vb2_get_drv_priv(q);
        struct common_obj *common;
        unsigned long addr;
 
@@ -92,26 +103,22 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb)
 
        common = &ch->common[VPIF_VIDEO_INDEX];
 
-       if (vb->state != VB2_BUF_STATE_ACTIVE &&
-               vb->state != VB2_BUF_STATE_PREPARED) {
-               vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
-               if (vb2_plane_vaddr(vb, 0) &&
-               vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
-                       goto exit;
-               addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+       vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
+       if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
+               return -EINVAL;
 
-               if (q->streaming) {
-                       if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
-                               !IS_ALIGNED((addr + common->ybtm_off), 8) ||
-                               !IS_ALIGNED((addr + common->ctop_off), 8) ||
-                               !IS_ALIGNED((addr + common->cbtm_off), 8))
-                               goto exit;
-               }
+       vb->v4l2_buf.field = common->fmt.fmt.pix.field;
+
+       addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+       if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
+               !IS_ALIGNED((addr + common->ybtm_off), 8) ||
+               !IS_ALIGNED((addr + common->ctop_off), 8) ||
+               !IS_ALIGNED((addr + common->cbtm_off), 8)) {
+               vpif_dbg(1, debug, "offset is not aligned\n");
+               return -EINVAL;
        }
+
        return 0;
-exit:
-       vpif_dbg(1, debug, "buffer_prepare:offset is not aligned to 8 bytes\n");
-       return -EINVAL;
 }
 
 /**
@@ -131,49 +138,26 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq,
                                unsigned int *nbuffers, unsigned int *nplanes,
                                unsigned int sizes[], void *alloc_ctxs[])
 {
-       /* Get the file handle object and channel object */
-       struct vpif_fh *fh = vb2_get_drv_priv(vq);
-       struct channel_obj *ch = fh->channel;
+       struct channel_obj *ch = vb2_get_drv_priv(vq);
        struct common_obj *common;
-       unsigned long size;
 
        common = &ch->common[VPIF_VIDEO_INDEX];
 
        vpif_dbg(2, debug, "vpif_buffer_setup\n");
 
-       /* If memory type is not mmap, return */
-       if (V4L2_MEMORY_MMAP == common->memory) {
-               /* Calculate the size of the buffer */
-               size = config_params.channel_bufsize[ch->channel_id];
-               /*
-                * Checking if the buffer size exceeds the available buffer
-                * ycmux_mode = 0 means 1 channel mode HD and
-                * ycmux_mode = 1 means 2 channels mode SD
-                */
-               if (ch->vpifparams.std_info.ycmux_mode == 0) {
-                       if (config_params.video_limit[ch->channel_id])
-                               while (size * *nbuffers >
-                                       (config_params.video_limit[0]
-                                               + config_params.video_limit[1]))
-                                       (*nbuffers)--;
-               } else {
-                       if (config_params.video_limit[ch->channel_id])
-                               while (size * *nbuffers >
-                               config_params.video_limit[ch->channel_id])
-                                       (*nbuffers)--;
-               }
-
-       } else {
-               size = common->fmt.fmt.pix.sizeimage;
-       }
+       if (fmt && fmt->fmt.pix.sizeimage < common->fmt.fmt.pix.sizeimage)
+               return -EINVAL;
 
-       if (*nbuffers < config_params.min_numbuffers)
-               *nbuffers = config_params.min_numbuffers;
+       if (vq->num_buffers + *nbuffers < 3)
+               *nbuffers = 3 - vq->num_buffers;
 
        *nplanes = 1;
-       sizes[0] = size;
+       sizes[0] = fmt ? fmt->fmt.pix.sizeimage : common->fmt.fmt.pix.sizeimage;
        alloc_ctxs[0] = common->alloc_ctx;
 
+       /* Calculate the offset for Y and C data in the buffer */
+       vpif_calculate_offsets(ch);
+
        return 0;
 }
 
@@ -183,11 +167,8 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq,
  */
 static void vpif_buffer_queue(struct vb2_buffer *vb)
 {
-       /* Get the file handle object and channel object */
-       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
-       struct channel_obj *ch = fh->channel;
-       struct vpif_cap_buffer *buf = container_of(vb,
-                               struct vpif_cap_buffer, vb);
+       struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue);
+       struct vpif_cap_buffer *buf = to_vpif_buffer(vb);
        struct common_obj *common;
        unsigned long flags;
 
@@ -202,102 +183,25 @@ static void vpif_buffer_queue(struct vb2_buffer *vb)
 }
 
 /**
- * vpif_buf_cleanup : Callback function to free buffer
+ * vpif_start_streaming : Starts the DMA engine for streaming
  * @vb: ptr to vb2_buffer
- *
- * This function is called from the videobuf2 layer to free memory
- * allocated to  the buffers
+ * @count: number of buffers
  */
-static void vpif_buf_cleanup(struct vb2_buffer *vb)
-{
-       /* Get the file handle object and channel object */
-       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
-       struct vpif_cap_buffer *buf = container_of(vb,
-                                       struct vpif_cap_buffer, vb);
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common;
-       unsigned long flags;
-
-       common = &ch->common[VPIF_VIDEO_INDEX];
-
-       spin_lock_irqsave(&common->irqlock, flags);
-       if (vb->state == VB2_BUF_STATE_ACTIVE)
-               list_del_init(&buf->list);
-       spin_unlock_irqrestore(&common->irqlock, flags);
-
-}
-
-static void vpif_wait_prepare(struct vb2_queue *vq)
-{
-       struct vpif_fh *fh = vb2_get_drv_priv(vq);
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common;
-
-       common = &ch->common[VPIF_VIDEO_INDEX];
-       mutex_unlock(&common->lock);
-}
-
-static void vpif_wait_finish(struct vb2_queue *vq)
-{
-       struct vpif_fh *fh = vb2_get_drv_priv(vq);
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common;
-
-       common = &ch->common[VPIF_VIDEO_INDEX];
-       mutex_lock(&common->lock);
-}
-
-static int vpif_buffer_init(struct vb2_buffer *vb)
-{
-       struct vpif_cap_buffer *buf = container_of(vb,
-                                       struct vpif_cap_buffer, vb);
-
-       INIT_LIST_HEAD(&buf->list);
-
-       return 0;
-}
-
-static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
-       { {1, 1} };
-
 static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
 {
        struct vpif_capture_config *vpif_config_data =
                                        vpif_dev->platform_data;
-       struct vpif_fh *fh = vb2_get_drv_priv(vq);
-       struct channel_obj *ch = fh->channel;
+       struct channel_obj *ch = vb2_get_drv_priv(vq);
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
        struct vpif_params *vpif = &ch->vpifparams;
-       unsigned long addr = 0;
-       unsigned long flags;
+       struct vpif_cap_buffer *buf, *tmp;
+       unsigned long addr, flags;
        int ret;
 
        spin_lock_irqsave(&common->irqlock, flags);
 
-       /* Get the next frame from the buffer queue */
-       common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
-                                   struct vpif_cap_buffer, list);
-       /* Remove buffer from the buffer queue */
-       list_del(&common->cur_frm->list);
-       spin_unlock_irqrestore(&common->irqlock, flags);
-       /* Mark state of the current frame to active */
-       common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
-       /* Initialize field_id and started member */
+       /* Initialize field_id */
        ch->field_id = 0;
-       common->started = 1;
-       addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
-
-       /* Calculate the offset for Y and C data in the buffer */
-       vpif_calculate_offsets(ch);
-
-       if ((vpif->std_info.frm_fmt &&
-           ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) &&
-            (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ||
-           (!vpif->std_info.frm_fmt &&
-            (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
-               vpif_dbg(1, debug, "conflict in field format and std format\n");
-               return -EINVAL;
-       }
 
        /* configure 1 or 2 channel mode */
        if (vpif_config_data->setup_input_channel_mode) {
@@ -305,21 +209,37 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
                        setup_input_channel_mode(vpif->std_info.ycmux_mode);
                if (ret < 0) {
                        vpif_dbg(1, debug, "can't set vpif channel mode\n");
-                       return ret;
+                       goto err;
                }
        }
 
+       ret = v4l2_subdev_call(ch->sd, video, s_stream, 1);
+       if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) {
+               vpif_dbg(1, debug, "stream on failed in subdev\n");
+               goto err;
+       }
+
        /* Call vpif_set_params function to set the parameters and addresses */
        ret = vpif_set_video_params(vpif, ch->channel_id);
-
        if (ret < 0) {
                vpif_dbg(1, debug, "can't set video params\n");
-               return ret;
+               goto err;
        }
 
-       common->started = ret;
+       ycmux_mode = ret;
        vpif_config_addr(ch, ret);
 
+       /* Get the next frame from the buffer queue */
+       common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
+                                   struct vpif_cap_buffer, list);
+       /* Remove buffer from the buffer queue */
+       list_del(&common->cur_frm->list);
+       spin_unlock_irqrestore(&common->irqlock, flags);
+       /* Mark state of the current frame to active */
+       common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+
+       addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
+
        common->set_addr(addr + common->ytop_off,
                         addr + common->ybtm_off,
                         addr + common->ctop_off,
@@ -330,31 +250,42 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
         * VPIF register
         */
        channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
-       if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) {
+       if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
                channel0_intr_assert();
                channel0_intr_enable(1);
                enable_channel0(1);
        }
-       if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
-           (common->started == 2)) {
+       if (VPIF_CHANNEL1_VIDEO == ch->channel_id ||
+               ycmux_mode == 2) {
                channel1_intr_assert();
                channel1_intr_enable(1);
                enable_channel1(1);
        }
 
        return 0;
+
+err:
+       list_for_each_entry_safe(buf, tmp, &common->dma_queue, list) {
+               list_del(&buf->list);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+       }
+
+       return ret;
 }
 
-/* abort streaming and wait for last buffer */
-static int vpif_stop_streaming(struct vb2_queue *vq)
+/**
+ * vpif_stop_streaming : Stop the DMA engine
+ * @vq: ptr to vb2_queue
+ *
+ * This callback stops the DMA engine and any remaining buffers
+ * in the DMA queue are released.
+ */
+static void vpif_stop_streaming(struct vb2_queue *vq)
 {
-       struct vpif_fh *fh = vb2_get_drv_priv(vq);
-       struct channel_obj *ch = fh->channel;
+       struct channel_obj *ch = vb2_get_drv_priv(vq);
        struct common_obj *common;
        unsigned long flags;
-
-       if (!vb2_is_streaming(vq))
-               return 0;
+       int ret;
 
        common = &ch->common[VPIF_VIDEO_INDEX];
 
@@ -363,12 +294,17 @@ static int vpif_stop_streaming(struct vb2_queue *vq)
                enable_channel0(0);
                channel0_intr_enable(0);
        }
-       if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
-               (2 == common->started)) {
+       if (VPIF_CHANNEL1_VIDEO == ch->channel_id ||
+               ycmux_mode == 2) {
                enable_channel1(0);
                channel1_intr_enable(0);
        }
-       common->started = 0;
+
+       ycmux_mode = 0;
+
+       ret = v4l2_subdev_call(ch->sd, video, s_stream, 0);
+       if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
+               vpif_dbg(1, debug, "stream off failed in subdev\n");
 
        /* release all active buffers */
        spin_lock_irqsave(&common->irqlock, flags);
@@ -390,19 +326,13 @@ static int vpif_stop_streaming(struct vb2_queue *vq)
                vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
        }
        spin_unlock_irqrestore(&common->irqlock, flags);
-
-       return 0;
 }
 
 static struct vb2_ops video_qops = {
        .queue_setup            = vpif_buffer_queue_setup,
-       .wait_prepare           = vpif_wait_prepare,
-       .wait_finish            = vpif_wait_finish,
-       .buf_init               = vpif_buffer_init,
        .buf_prepare            = vpif_buffer_prepare,
        .start_streaming        = vpif_start_streaming,
        .stop_streaming         = vpif_stop_streaming,
-       .buf_cleanup            = vpif_buf_cleanup,
        .buf_queue              = vpif_buffer_queue,
 };
 
@@ -479,9 +409,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
        for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) {
                common = &ch->common[i];
                /* skip If streaming is not started in this channel */
-               if (0 == common->started)
-                       continue;
-
                /* Check the field format */
                if (1 == ch->vpifparams.std_info.frm_fmt) {
                        /* Progressive mode */
@@ -683,11 +610,6 @@ static void vpif_config_format(struct channel_obj *ch)
        vpif_dbg(2, debug, "vpif_config_format\n");
 
        common->fmt.fmt.pix.field = V4L2_FIELD_ANY;
-       if (config_params.numbuffers[ch->channel_id] == 0)
-               common->memory = V4L2_MEMORY_USERPTR;
-       else
-               common->memory = V4L2_MEMORY_MMAP;
-
        common->fmt.fmt.pix.sizeimage
            = config_params.channel_bufsize[ch->channel_id];
 
@@ -836,415 +758,6 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode)
                common->set_addr = ch0_set_videobuf_addr;
 }
 
-/**
- * vpif_mmap : It is used to map kernel space buffers into user spaces
- * @filep: file pointer
- * @vma: ptr to vm_area_struct
- */
-static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
-{
-       /* Get the channel object and file handle object */
-       struct vpif_fh *fh = filep->private_data;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
-       int ret;
-
-       vpif_dbg(2, debug, "vpif_mmap\n");
-
-       if (mutex_lock_interruptible(&common->lock))
-               return -ERESTARTSYS;
-       ret = vb2_mmap(&common->buffer_queue, vma);
-       mutex_unlock(&common->lock);
-       return ret;
-}
-
-/**
- * vpif_poll: It is used for select/poll system call
- * @filep: file pointer
- * @wait: poll table to wait
- */
-static unsigned int vpif_poll(struct file *filep, poll_table * wait)
-{
-       struct vpif_fh *fh = filep->private_data;
-       struct channel_obj *channel = fh->channel;
-       struct common_obj *common = &(channel->common[VPIF_VIDEO_INDEX]);
-       unsigned int res = 0;
-
-       vpif_dbg(2, debug, "vpif_poll\n");
-
-       if (common->started) {
-               mutex_lock(&common->lock);
-               res = vb2_poll(&common->buffer_queue, filep, wait);
-               mutex_unlock(&common->lock);
-       }
-       return res;
-}
-
-/**
- * vpif_open : vpif open handler
- * @filep: file ptr
- *
- * It creates object of file handle structure and stores it in private_data
- * member of filepointer
- */
-static int vpif_open(struct file *filep)
-{
-       struct video_device *vdev = video_devdata(filep);
-       struct common_obj *common;
-       struct video_obj *vid_ch;
-       struct channel_obj *ch;
-       struct vpif_fh *fh;
-
-       vpif_dbg(2, debug, "vpif_open\n");
-
-       ch = video_get_drvdata(vdev);
-
-       vid_ch = &ch->video;
-       common = &ch->common[VPIF_VIDEO_INDEX];
-
-       /* Allocate memory for the file handle object */
-       fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL);
-       if (NULL == fh) {
-               vpif_err("unable to allocate memory for file handle object\n");
-               return -ENOMEM;
-       }
-
-       if (mutex_lock_interruptible(&common->lock)) {
-               kfree(fh);
-               return -ERESTARTSYS;
-       }
-       /* store pointer to fh in private_data member of filep */
-       filep->private_data = fh;
-       fh->channel = ch;
-       fh->initialized = 0;
-       /* If decoder is not initialized. initialize it */
-       if (!ch->initialized) {
-               fh->initialized = 1;
-               ch->initialized = 1;
-               memset(&(ch->vpifparams), 0, sizeof(struct vpif_params));
-       }
-       /* Increment channel usrs counter */
-       ch->usrs++;
-       /* Set io_allowed member to false */
-       fh->io_allowed[VPIF_VIDEO_INDEX] = 0;
-       /* Initialize priority of this instance to default priority */
-       fh->prio = V4L2_PRIORITY_UNSET;
-       v4l2_prio_open(&ch->prio, &fh->prio);
-       mutex_unlock(&common->lock);
-       return 0;
-}
-
-/**
- * vpif_release : function to clean up file close
- * @filep: file pointer
- *
- * This function deletes buffer queue, frees the buffers and the vpif file
- * handle
- */
-static int vpif_release(struct file *filep)
-{
-       struct vpif_fh *fh = filep->private_data;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common;
-
-       vpif_dbg(2, debug, "vpif_release\n");
-
-       common = &ch->common[VPIF_VIDEO_INDEX];
-
-       mutex_lock(&common->lock);
-       /* if this instance is doing IO */
-       if (fh->io_allowed[VPIF_VIDEO_INDEX]) {
-               /* Reset io_usrs member of channel object */
-               common->io_usrs = 0;
-               /* Free buffers allocated */
-               vb2_queue_release(&common->buffer_queue);
-               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
-       }
-
-       /* Decrement channel usrs counter */
-       ch->usrs--;
-
-       /* Close the priority */
-       v4l2_prio_close(&ch->prio, fh->prio);
-
-       if (fh->initialized)
-               ch->initialized = 0;
-
-       mutex_unlock(&common->lock);
-       filep->private_data = NULL;
-       kfree(fh);
-       return 0;
-}
-
-/**
- * vpif_reqbufs() - request buffer handler
- * @file: file ptr
- * @priv: file handle
- * @reqbuf: request buffer structure ptr
- */
-static int vpif_reqbufs(struct file *file, void *priv,
-                       struct v4l2_requestbuffers *reqbuf)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common;
-       u8 index = 0;
-       struct vb2_queue *q;
-       int ret;
-
-       vpif_dbg(2, debug, "vpif_reqbufs\n");
-
-       /**
-        * This file handle has not initialized the channel,
-        * It is not allowed to do settings
-        */
-       if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)
-           || (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
-               if (!fh->initialized) {
-                       vpif_dbg(1, debug, "Channel Busy\n");
-                       return -EBUSY;
-               }
-       }
-
-       if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type || !vpif_dev)
-               return -EINVAL;
-
-       index = VPIF_VIDEO_INDEX;
-
-       common = &ch->common[index];
-
-       if (0 != common->io_usrs)
-               return -EBUSY;
-
-       /* Initialize videobuf2 queue as per the buffer type */
-       common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
-       if (IS_ERR(common->alloc_ctx)) {
-               vpif_err("Failed to get the context\n");
-               return PTR_ERR(common->alloc_ctx);
-       }
-       q = &common->buffer_queue;
-       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-       q->io_modes = VB2_MMAP | VB2_USERPTR;
-       q->drv_priv = fh;
-       q->ops = &video_qops;
-       q->mem_ops = &vb2_dma_contig_memops;
-       q->buf_struct_size = sizeof(struct vpif_cap_buffer);
-       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-       q->min_buffers_needed = 1;
-
-       ret = vb2_queue_init(q);
-       if (ret) {
-               vpif_err("vpif_capture: vb2_queue_init() failed\n");
-               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
-               return ret;
-       }
-       /* Set io allowed member of file handle to TRUE */
-       fh->io_allowed[index] = 1;
-       /* Increment io usrs member of channel object to 1 */
-       common->io_usrs = 1;
-       /* Store type of memory requested in channel object */
-       common->memory = reqbuf->memory;
-       INIT_LIST_HEAD(&common->dma_queue);
-
-       /* Allocate buffers */
-       return vb2_reqbufs(&common->buffer_queue, reqbuf);
-}
-
-/**
- * vpif_querybuf() - query buffer handler
- * @file: file ptr
- * @priv: file handle
- * @buf: v4l2 buffer structure ptr
- */
-static int vpif_querybuf(struct file *file, void *priv,
-                               struct v4l2_buffer *buf)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
-       vpif_dbg(2, debug, "vpif_querybuf\n");
-
-       if (common->fmt.type != buf->type)
-               return -EINVAL;
-
-       if (common->memory != V4L2_MEMORY_MMAP) {
-               vpif_dbg(1, debug, "Invalid memory\n");
-               return -EINVAL;
-       }
-
-       return vb2_querybuf(&common->buffer_queue, buf);
-}
-
-/**
- * vpif_qbuf() - query buffer handler
- * @file: file ptr
- * @priv: file handle
- * @buf: v4l2 buffer structure ptr
- */
-static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
-
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       struct v4l2_buffer tbuf = *buf;
-
-       vpif_dbg(2, debug, "vpif_qbuf\n");
-
-       if (common->fmt.type != tbuf.type) {
-               vpif_err("invalid buffer type\n");
-               return -EINVAL;
-       }
-
-       if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
-               vpif_err("fh io not allowed\n");
-               return -EACCES;
-       }
-
-       return vb2_qbuf(&common->buffer_queue, buf);
-}
-
-/**
- * vpif_dqbuf() - query buffer handler
- * @file: file ptr
- * @priv: file handle
- * @buf: v4l2 buffer structure ptr
- */
-static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
-       vpif_dbg(2, debug, "vpif_dqbuf\n");
-
-       return vb2_dqbuf(&common->buffer_queue, buf,
-                        (file->f_flags & O_NONBLOCK));
-}
-
-/**
- * vpif_streamon() - streamon handler
- * @file: file ptr
- * @priv: file handle
- * @buftype: v4l2 buffer type
- */
-static int vpif_streamon(struct file *file, void *priv,
-                               enum v4l2_buf_type buftype)
-{
-
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
-       struct vpif_params *vpif;
-       int ret = 0;
-
-       vpif_dbg(2, debug, "vpif_streamon\n");
-
-       vpif = &ch->vpifparams;
-
-       if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-               vpif_dbg(1, debug, "buffer type not supported\n");
-               return -EINVAL;
-       }
-
-       /* If file handle is not allowed IO, return error */
-       if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
-               vpif_dbg(1, debug, "io not allowed\n");
-               return -EACCES;
-       }
-
-       /* If Streaming is already started, return error */
-       if (common->started) {
-               vpif_dbg(1, debug, "channel->started\n");
-               return -EBUSY;
-       }
-
-       if ((ch->channel_id == VPIF_CHANNEL0_VIDEO &&
-           oth_ch->common[VPIF_VIDEO_INDEX].started &&
-           vpif->std_info.ycmux_mode == 0) ||
-          ((ch->channel_id == VPIF_CHANNEL1_VIDEO) &&
-           (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) {
-               vpif_dbg(1, debug, "other channel is being used\n");
-               return -EBUSY;
-       }
-
-       ret = vpif_check_format(ch, &common->fmt.fmt.pix, 0);
-       if (ret)
-               return ret;
-
-       /* Enable streamon on the sub device */
-       ret = v4l2_subdev_call(ch->sd, video, s_stream, 1);
-
-       if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) {
-               vpif_dbg(1, debug, "stream on failed in subdev\n");
-               return ret;
-       }
-
-       /* Call vb2_streamon to start streaming in videobuf2 */
-       ret = vb2_streamon(&common->buffer_queue, buftype);
-       if (ret) {
-               vpif_dbg(1, debug, "vb2_streamon\n");
-               return ret;
-       }
-
-       return ret;
-}
-
-/**
- * vpif_streamoff() - streamoff handler
- * @file: file ptr
- * @priv: file handle
- * @buftype: v4l2 buffer type
- */
-static int vpif_streamoff(struct file *file, void *priv,
-                               enum v4l2_buf_type buftype)
-{
-
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       int ret;
-
-       vpif_dbg(2, debug, "vpif_streamoff\n");
-
-       if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-               vpif_dbg(1, debug, "buffer type not supported\n");
-               return -EINVAL;
-       }
-
-       /* If io is allowed for this file handle, return error */
-       if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
-               vpif_dbg(1, debug, "io not allowed\n");
-               return -EACCES;
-       }
-
-       /* If streaming is not started, return error */
-       if (!common->started) {
-               vpif_dbg(1, debug, "channel->started\n");
-               return -EINVAL;
-       }
-
-       /* disable channel */
-       if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
-               enable_channel0(0);
-               channel0_intr_enable(0);
-       } else {
-               enable_channel1(0);
-               channel1_intr_enable(0);
-       }
-
-       common->started = 0;
-
-       ret = v4l2_subdev_call(ch->sd, video, s_stream, 0);
-
-       if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
-               vpif_dbg(1, debug, "stream off failed in subdev\n");
-
-       return vb2_streamoff(&common->buffer_queue, buftype);
-}
-
 /**
  * vpif_input_to_subdev() - Maps input to sub device
  * @vpif_cfg - global config ptr
@@ -1348,8 +861,8 @@ static int vpif_set_input(
  */
 static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        int ret = 0;
 
        vpif_dbg(2, debug, "vpif_querystd\n");
@@ -1375,11 +888,22 @@ static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
  */
 static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct vpif_capture_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
+       struct vpif_capture_chan_config *chan_cfg;
+       struct v4l2_input input;
 
        vpif_dbg(2, debug, "vpif_g_std\n");
 
+       if (config->chan_config[ch->channel_id].inputs == NULL)
+               return -ENODATA;
+
+       chan_cfg = &config->chan_config[ch->channel_id];
+       input = chan_cfg->inputs[ch->input_idx].input;
+       if (input.capabilities != V4L2_IN_CAP_STD)
+               return -ENODATA;
+
        *std = ch->video.stdid;
        return 0;
 }
@@ -1392,31 +916,26 @@ static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)
  */
 static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct vpif_capture_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       int ret = 0;
+       struct vpif_capture_chan_config *chan_cfg;
+       struct v4l2_input input;
+       int ret;
 
        vpif_dbg(2, debug, "vpif_s_std\n");
 
-       if (common->started) {
-               vpif_err("streaming in progress\n");
-               return -EBUSY;
-       }
-
-       if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
-           (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
-               if (!fh->initialized) {
-                       vpif_dbg(1, debug, "Channel Busy\n");
-                       return -EBUSY;
-               }
-       }
+       if (config->chan_config[ch->channel_id].inputs == NULL)
+               return -ENODATA;
 
-       ret = v4l2_prio_check(&ch->prio, fh->prio);
-       if (0 != ret)
-               return ret;
+       chan_cfg = &config->chan_config[ch->channel_id];
+       input = chan_cfg->inputs[ch->input_idx].input;
+       if (input.capabilities != V4L2_IN_CAP_STD)
+               return -ENODATA;
 
-       fh->initialized = 1;
+       if (vb2_is_busy(&common->buffer_queue))
+               return -EBUSY;
 
        /* Call encoder subdevice function to set the standard */
        ch->video.stdid = std_id;
@@ -1432,7 +951,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)
        vpif_config_format(ch);
 
        /* set standard in the sub device */
-       ret = v4l2_subdev_call(ch->sd, core, s_std, std_id);
+       ret = v4l2_subdev_call(ch->sd, video, s_std, std_id);
        if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) {
                vpif_dbg(1, debug, "Failed to set standard for sub devices\n");
                return ret;
@@ -1451,9 +970,9 @@ static int vpif_enum_input(struct file *file, void *priv,
 {
 
        struct vpif_capture_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct vpif_capture_chan_config *chan_cfg;
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
 
        chan_cfg = &config->chan_config[ch->channel_id];
 
@@ -1475,8 +994,8 @@ static int vpif_enum_input(struct file *file, void *priv,
  */
 static int vpif_g_input(struct file *file, void *priv, unsigned int *index)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
 
        *index = ch->input_idx;
        return 0;
@@ -1491,35 +1010,19 @@ static int vpif_g_input(struct file *file, void *priv, unsigned int *index)
 static int vpif_s_input(struct file *file, void *priv, unsigned int index)
 {
        struct vpif_capture_config *config = vpif_dev->platform_data;
-       struct vpif_capture_chan_config *chan_cfg;
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       int ret;
+       struct vpif_capture_chan_config *chan_cfg;
 
        chan_cfg = &config->chan_config[ch->channel_id];
 
        if (index >= chan_cfg->input_count)
                return -EINVAL;
 
-       if (common->started) {
-               vpif_err("Streaming in progress\n");
+       if (vb2_is_busy(&common->buffer_queue))
                return -EBUSY;
-       }
-
-       if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
-           (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
-               if (!fh->initialized) {
-                       vpif_dbg(1, debug, "Channel Busy\n");
-                       return -EBUSY;
-               }
-       }
-
-       ret = v4l2_prio_check(&ch->prio, fh->prio);
-       if (0 != ret)
-               return ret;
 
-       fh->initialized = 1;
        return vpif_set_input(config, ch, index);
 }
 
@@ -1532,8 +1035,8 @@ static int vpif_s_input(struct file *file, void *priv, unsigned int index)
 static int vpif_enum_fmt_vid_cap(struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *fmt)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
 
        if (fmt->index != 0) {
                vpif_dbg(1, debug, "Invalid format index\n");
@@ -1562,8 +1065,8 @@ static int vpif_enum_fmt_vid_cap(struct file *file, void  *priv,
 static int vpif_try_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *fmt)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
 
        return vpif_check_format(ch, pixfmt, 1);
@@ -1579,8 +1082,8 @@ static int vpif_try_fmt_vid_cap(struct file *file, void *priv,
 static int vpif_g_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *fmt)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
 
        /* Check the validity of the buffer type */
@@ -1601,33 +1104,16 @@ static int vpif_g_fmt_vid_cap(struct file *file, void *priv,
 static int vpif_s_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *fmt)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
        struct v4l2_pix_format *pixfmt;
        int ret = 0;
 
        vpif_dbg(2, debug, "%s\n", __func__);
 
-       /* If streaming is started, return error */
-       if (common->started) {
-               vpif_dbg(1, debug, "Streaming is started\n");
+       if (vb2_is_busy(&common->buffer_queue))
                return -EBUSY;
-       }
-
-       if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
-           (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
-               if (!fh->initialized) {
-                       vpif_dbg(1, debug, "Channel Busy\n");
-                       return -EBUSY;
-               }
-       }
-
-       ret = v4l2_prio_check(&ch->prio, fh->prio);
-       if (0 != ret)
-               return ret;
-
-       fh->initialized = 1;
 
        pixfmt = &fmt->fmt.pix;
        /* Check for valid field format */
@@ -1653,7 +1139,7 @@ static int vpif_querycap(struct file *file, void  *priv,
 
        cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
        cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-       snprintf(cap->driver, sizeof(cap->driver), "%s", dev_name(vpif_dev));
+       strlcpy(cap->driver, VPIF_DRIVER_NAME, sizeof(cap->driver));
        snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
                 dev_name(vpif_dev));
        strlcpy(cap->card, config->card_name, sizeof(cap->card));
@@ -1661,61 +1147,6 @@ static int vpif_querycap(struct file *file, void  *priv,
        return 0;
 }
 
-/**
- * vpif_g_priority() - get priority handler
- * @file: file ptr
- * @priv: file handle
- * @prio: ptr to v4l2_priority structure
- */
-static int vpif_g_priority(struct file *file, void *priv,
-                          enum v4l2_priority *prio)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-
-       *prio = v4l2_prio_max(&ch->prio);
-
-       return 0;
-}
-
-/**
- * vpif_s_priority() - set priority handler
- * @file: file ptr
- * @priv: file handle
- * @prio: ptr to v4l2_priority structure
- */
-static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-
-       return v4l2_prio_change(&ch->prio, &fh->prio, p);
-}
-
-/**
- * vpif_cropcap() - cropcap handler
- * @file: file ptr
- * @priv: file handle
- * @crop: ptr to v4l2_cropcap structure
- */
-static int vpif_cropcap(struct file *file, void *priv,
-                       struct v4l2_cropcap *crop)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
-       if (V4L2_BUF_TYPE_VIDEO_CAPTURE != crop->type)
-               return -EINVAL;
-
-       crop->bounds.left = 0;
-       crop->bounds.top = 0;
-       crop->bounds.height = common->height;
-       crop->bounds.width = common->width;
-       crop->defrect = crop->bounds;
-       return 0;
-}
-
 /**
  * vpif_enum_dv_timings() - ENUM_DV_TIMINGS handler
  * @file: file ptr
@@ -1726,13 +1157,27 @@ static int
 vpif_enum_dv_timings(struct file *file, void *priv,
                     struct v4l2_enum_dv_timings *timings)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct vpif_capture_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
+       struct vpif_capture_chan_config *chan_cfg;
+       struct v4l2_input input;
        int ret;
 
-       ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings);
+       if (config->chan_config[ch->channel_id].inputs == NULL)
+               return -ENODATA;
+
+       chan_cfg = &config->chan_config[ch->channel_id];
+       input = chan_cfg->inputs[ch->input_idx].input;
+       if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS)
+               return -ENODATA;
+
+       timings->pad = 0;
+
+       ret = v4l2_subdev_call(ch->sd, pad, enum_dv_timings, timings);
        if (ret == -ENOIOCTLCMD || ret == -ENODEV)
                return -EINVAL;
+
        return ret;
 }
 
@@ -1746,13 +1191,25 @@ static int
 vpif_query_dv_timings(struct file *file, void *priv,
                      struct v4l2_dv_timings *timings)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct vpif_capture_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
+       struct vpif_capture_chan_config *chan_cfg;
+       struct v4l2_input input;
        int ret;
 
+       if (config->chan_config[ch->channel_id].inputs == NULL)
+               return -ENODATA;
+
+       chan_cfg = &config->chan_config[ch->channel_id];
+       input = chan_cfg->inputs[ch->input_idx].input;
+       if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS)
+               return -ENODATA;
+
        ret = v4l2_subdev_call(ch->sd, video, query_dv_timings, timings);
        if (ret == -ENOIOCTLCMD || ret == -ENODEV)
                return -ENODATA;
+
        return ret;
 }
 
@@ -1765,19 +1222,34 @@ vpif_query_dv_timings(struct file *file, void *priv,
 static int vpif_s_dv_timings(struct file *file, void *priv,
                struct v4l2_dv_timings *timings)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct vpif_capture_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct vpif_params *vpifparams = &ch->vpifparams;
        struct vpif_channel_config_params *std_info = &vpifparams->std_info;
+       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
        struct video_obj *vid_ch = &ch->video;
        struct v4l2_bt_timings *bt = &vid_ch->dv_timings.bt;
+       struct vpif_capture_chan_config *chan_cfg;
+       struct v4l2_input input;
        int ret;
 
+       if (config->chan_config[ch->channel_id].inputs == NULL)
+               return -ENODATA;
+
+       chan_cfg = &config->chan_config[ch->channel_id];
+       input = chan_cfg->inputs[ch->input_idx].input;
+       if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS)
+               return -ENODATA;
+
        if (timings->type != V4L2_DV_BT_656_1120) {
                vpif_dbg(2, debug, "Timing type not defined\n");
                return -EINVAL;
        }
 
+       if (vb2_is_busy(&common->buffer_queue))
+               return -EBUSY;
+
        /* Configure subdevice timings, if any */
        ret = v4l2_subdev_call(ch->sd, video, s_dv_timings, timings);
        if (ret == -ENOIOCTLCMD || ret == -ENODEV)
@@ -1853,9 +1325,20 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
 static int vpif_g_dv_timings(struct file *file, void *priv,
                struct v4l2_dv_timings *timings)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct vpif_capture_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct video_obj *vid_ch = &ch->video;
+       struct vpif_capture_chan_config *chan_cfg;
+       struct v4l2_input input;
+
+       if (config->chan_config[ch->channel_id].inputs == NULL)
+               return -ENODATA;
+
+       chan_cfg = &config->chan_config[ch->channel_id];
+       input = chan_cfg->inputs[ch->input_idx].input;
+       if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS)
+               return -ENODATA;
 
        *timings = vid_ch->dv_timings;
 
@@ -1879,49 +1362,45 @@ static int vpif_log_status(struct file *filep, void *priv)
 
 /* vpif capture ioctl operations */
 static const struct v4l2_ioctl_ops vpif_ioctl_ops = {
-       .vidioc_querycap                = vpif_querycap,
-       .vidioc_g_priority              = vpif_g_priority,
-       .vidioc_s_priority              = vpif_s_priority,
+       .vidioc_querycap                = vpif_querycap,
        .vidioc_enum_fmt_vid_cap        = vpif_enum_fmt_vid_cap,
-       .vidioc_g_fmt_vid_cap           = vpif_g_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap           = vpif_g_fmt_vid_cap,
        .vidioc_s_fmt_vid_cap           = vpif_s_fmt_vid_cap,
        .vidioc_try_fmt_vid_cap         = vpif_try_fmt_vid_cap,
+
        .vidioc_enum_input              = vpif_enum_input,
        .vidioc_s_input                 = vpif_s_input,
        .vidioc_g_input                 = vpif_g_input,
-       .vidioc_reqbufs                 = vpif_reqbufs,
-       .vidioc_querybuf                = vpif_querybuf,
+
+       .vidioc_reqbufs                 = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs             = vb2_ioctl_create_bufs,
+       .vidioc_querybuf                = vb2_ioctl_querybuf,
+       .vidioc_qbuf                    = vb2_ioctl_qbuf,
+       .vidioc_dqbuf                   = vb2_ioctl_dqbuf,
+       .vidioc_expbuf                  = vb2_ioctl_expbuf,
+       .vidioc_streamon                = vb2_ioctl_streamon,
+       .vidioc_streamoff               = vb2_ioctl_streamoff,
+
        .vidioc_querystd                = vpif_querystd,
-       .vidioc_s_std                   = vpif_s_std,
+       .vidioc_s_std                   = vpif_s_std,
        .vidioc_g_std                   = vpif_g_std,
-       .vidioc_qbuf                    = vpif_qbuf,
-       .vidioc_dqbuf                   = vpif_dqbuf,
-       .vidioc_streamon                = vpif_streamon,
-       .vidioc_streamoff               = vpif_streamoff,
-       .vidioc_cropcap                 = vpif_cropcap,
-       .vidioc_enum_dv_timings         = vpif_enum_dv_timings,
-       .vidioc_query_dv_timings        = vpif_query_dv_timings,
-       .vidioc_s_dv_timings            = vpif_s_dv_timings,
-       .vidioc_g_dv_timings            = vpif_g_dv_timings,
+
+       .vidioc_enum_dv_timings         = vpif_enum_dv_timings,
+       .vidioc_query_dv_timings        = vpif_query_dv_timings,
+       .vidioc_s_dv_timings            = vpif_s_dv_timings,
+       .vidioc_g_dv_timings            = vpif_g_dv_timings,
+
        .vidioc_log_status              = vpif_log_status,
 };
 
 /* vpif file operations */
 static struct v4l2_file_operations vpif_fops = {
        .owner = THIS_MODULE,
-       .open = vpif_open,
-       .release = vpif_release,
+       .open = v4l2_fh_open,
+       .release = vb2_fop_release,
        .unlocked_ioctl = video_ioctl2,
-       .mmap = vpif_mmap,
-       .poll = vpif_poll
-};
-
-/* vpif video template */
-static struct video_device vpif_video_template = {
-       .name           = "vpif",
-       .fops           = &vpif_fops,
-       .minor          = -1,
-       .ioctl_ops      = &vpif_ioctl_ops,
+       .mmap = vb2_fop_mmap,
+       .poll = vb2_fop_poll
 };
 
 /**
@@ -1999,7 +1478,9 @@ static int vpif_async_bound(struct v4l2_async_notifier *notifier,
 static int vpif_probe_complete(void)
 {
        struct common_obj *common;
+       struct video_device *vdev;
        struct channel_obj *ch;
+       struct vb2_queue *q;
        int i, j, err, k;
 
        for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
@@ -2008,17 +1489,52 @@ static int vpif_probe_complete(void)
                common = &(ch->common[VPIF_VIDEO_INDEX]);
                spin_lock_init(&common->irqlock);
                mutex_init(&common->lock);
-               ch->video_dev->lock = &common->lock;
-               /* Initialize prio member of channel object */
-               v4l2_prio_init(&ch->prio);
-               video_set_drvdata(ch->video_dev, ch);
 
                /* select input 0 */
                err = vpif_set_input(vpif_obj.config, ch, 0);
                if (err)
                        goto probe_out;
 
-               err = video_register_device(ch->video_dev,
+               /* Initialize vb2 queue */
+               q = &common->buffer_queue;
+               q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+               q->drv_priv = ch;
+               q->ops = &video_qops;
+               q->mem_ops = &vb2_dma_contig_memops;
+               q->buf_struct_size = sizeof(struct vpif_cap_buffer);
+               q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+               q->min_buffers_needed = 1;
+               q->lock = &common->lock;
+
+               err = vb2_queue_init(q);
+               if (err) {
+                       vpif_err("vpif_capture: vb2_queue_init() failed\n");
+                       goto probe_out;
+               }
+
+               common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
+               if (IS_ERR(common->alloc_ctx)) {
+                       vpif_err("Failed to get the context\n");
+                       err = PTR_ERR(common->alloc_ctx);
+                       goto probe_out;
+               }
+
+               INIT_LIST_HEAD(&common->dma_queue);
+
+               /* Initialize the video_device structure */
+               vdev = ch->video_dev;
+               strlcpy(vdev->name, VPIF_DRIVER_NAME, sizeof(vdev->name));
+               vdev->release = video_device_release;
+               vdev->fops = &vpif_fops;
+               vdev->ioctl_ops = &vpif_ioctl_ops;
+               vdev->v4l2_dev = &vpif_obj.v4l2_dev;
+               vdev->vfl_dir = VFL_DIR_RX;
+               vdev->queue = q;
+               vdev->lock = &common->lock;
+               set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags);
+               video_set_drvdata(ch->video_dev, ch);
+               err = video_register_device(vdev,
                                            VFL_TYPE_GRABBER, (j ? 1 : 0));
                if (err)
                        goto probe_out;
@@ -2031,6 +1547,8 @@ probe_out:
        for (k = 0; k < j; k++) {
                /* Get the pointer to the channel object */
                ch = vpif_obj.dev[k];
+               common = &ch->common[k];
+               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
                /* Unregister video device */
                video_unregister_device(ch->video_dev);
        }
@@ -2067,7 +1585,6 @@ static __init int vpif_probe(struct platform_device *pdev)
        struct video_device *vfd;
        struct resource *res;
        int subdev_count;
-       size_t size;
 
        vpif_dev = &pdev->dev;
 
@@ -2085,7 +1602,7 @@ static __init int vpif_probe(struct platform_device *pdev)
 
        while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) {
                err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr,
-                                       IRQF_SHARED, "VPIF_Capture",
+                                       IRQF_SHARED, VPIF_DRIVER_NAME,
                                        (void *)(&vpif_obj.dev[res_idx]->
                                        channel_id));
                if (err) {
@@ -2109,34 +1626,10 @@ static __init int vpif_probe(struct platform_device *pdev)
                        goto vpif_unregister;
                }
 
-               /* Initialize field of video device */
-               *vfd = vpif_video_template;
-               vfd->v4l2_dev = &vpif_obj.v4l2_dev;
-               vfd->release = video_device_release;
-               snprintf(vfd->name, sizeof(vfd->name),
-                        "VPIF_Capture_DRIVER_V%s",
-                        VPIF_CAPTURE_VERSION);
                /* Set video_dev to the video device */
                ch->video_dev = vfd;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res) {
-               size = resource_size(res);
-               /* The resources are divided into two equal memory and when we
-                * have HD output we can add them together
-                */
-               for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
-                       ch = vpif_obj.dev[j];
-                       ch->channel_id = j;
-                       /* only enabled if second resource exists */
-                       config_params.video_limit[ch->channel_id] = 0;
-                       if (size)
-                               config_params.video_limit[ch->channel_id] =
-                                                                       size/2;
-               }
-       }
-
        vpif_obj.config = pdev->dev.platform_data;
 
        subdev_count = vpif_obj.config->subdev_count;
@@ -2209,6 +1702,7 @@ vpif_unregister:
  */
 static int vpif_remove(struct platform_device *device)
 {
+       struct common_obj *common;
        struct channel_obj *ch;
        int i;
 
@@ -2219,6 +1713,8 @@ static int vpif_remove(struct platform_device *device)
        for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
                /* Get the pointer to the channel object */
                ch = vpif_obj.dev[i];
+               common = &ch->common[i];
+               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
                /* Unregister video device */
                video_unregister_device(ch->video_dev);
                kfree(vpif_obj.dev[i]);
@@ -2226,7 +1722,7 @@ static int vpif_remove(struct platform_device *device)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 /**
  * vpif_suspend: vpif device suspend
  */
@@ -2241,18 +1737,20 @@ static int vpif_suspend(struct device *dev)
                /* Get the pointer to the channel object */
                ch = vpif_obj.dev[i];
                common = &ch->common[VPIF_VIDEO_INDEX];
+
+               if (!vb2_is_streaming(&common->buffer_queue))
+                       continue;
+
                mutex_lock(&common->lock);
-               if (ch->usrs && common->io_usrs) {
-                       /* Disable channel */
-                       if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
-                               enable_channel0(0);
-                               channel0_intr_enable(0);
-                       }
-                       if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
-                           common->started == 2) {
-                               enable_channel1(0);
-                               channel1_intr_enable(0);
-                       }
+               /* Disable channel */
+               if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
+                       enable_channel0(0);
+                       channel0_intr_enable(0);
+               }
+               if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
+                       ycmux_mode == 2) {
+                       enable_channel1(0);
+                       channel1_intr_enable(0);
                }
                mutex_unlock(&common->lock);
        }
@@ -2273,40 +1771,35 @@ static int vpif_resume(struct device *dev)
                /* Get the pointer to the channel object */
                ch = vpif_obj.dev[i];
                common = &ch->common[VPIF_VIDEO_INDEX];
+
+               if (!vb2_is_streaming(&common->buffer_queue))
+                       continue;
+
                mutex_lock(&common->lock);
-               if (ch->usrs && common->io_usrs) {
-                       /* Disable channel */
-                       if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
-                               enable_channel0(1);
-                               channel0_intr_enable(1);
-                       }
-                       if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
-                           common->started == 2) {
-                               enable_channel1(1);
-                               channel1_intr_enable(1);
-                       }
+               /* Enable channel */
+               if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
+                       enable_channel0(1);
+                       channel0_intr_enable(1);
+               }
+               if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
+                       ycmux_mode == 2) {
+                       enable_channel1(1);
+                       channel1_intr_enable(1);
                }
                mutex_unlock(&common->lock);
        }
 
        return 0;
 }
-
-static const struct dev_pm_ops vpif_dev_pm_ops = {
-       .suspend = vpif_suspend,
-       .resume = vpif_resume,
-};
-
-#define vpif_pm_ops (&vpif_dev_pm_ops)
-#else
-#define vpif_pm_ops NULL
 #endif
 
+static SIMPLE_DEV_PM_OPS(vpif_pm_ops, vpif_suspend, vpif_resume);
+
 static __refdata struct platform_driver vpif_driver = {
        .driver = {
-               .name   = "vpif_capture",
+               .name   = VPIF_DRIVER_NAME,
                .owner  = THIS_MODULE,
-               .pm     = vpif_pm_ops,
+               .pm     = &vpif_pm_ops,
        },
        .probe = vpif_probe,
        .remove = vpif_remove,
index 5a29d9a..1ee1782 100644 (file)
@@ -19,8 +19,6 @@
 #ifndef VPIF_CAPTURE_H
 #define VPIF_CAPTURE_H
 
-#ifdef __KERNEL__
-
 /* Header files */
 #include <media/videobuf2-dma-contig.h>
 #include <media/v4l2-device.h>
@@ -63,11 +61,6 @@ struct common_obj {
        struct vpif_cap_buffer *cur_frm;
        /* Pointer pointing to current v4l2_buffer */
        struct vpif_cap_buffer *next_frm;
-       /*
-        * This field keeps track of type of buffer exchange mechanism
-        * user has selected
-        */
-       enum v4l2_memory memory;
        /* Used to store pixel format */
        struct v4l2_format fmt;
        /* Buffer queue used in video-buf */
@@ -80,10 +73,6 @@ struct common_obj {
        spinlock_t irqlock;
        /* lock used to access this structure */
        struct mutex lock;
-       /* number of users performing IO */
-       u32 io_usrs;
-       /* Indicates whether streaming started */
-       u8 started;
        /* Function pointer to set the addresses */
        void (*set_addr) (unsigned long, unsigned long, unsigned long,
                          unsigned long);
@@ -104,10 +93,6 @@ struct common_obj {
 struct channel_obj {
        /* Identifies video device for this channel */
        struct video_device *video_dev;
-       /* Used to keep track of state of the priority */
-       struct v4l2_prio_state prio;
-       /* number of open instances of the channel */
-       int usrs;
        /* Indicates id of the field which is being displayed */
        u32 field_id;
        /* flag to indicate whether decoder is initialized */
@@ -126,18 +111,6 @@ struct channel_obj {
        struct video_obj video;
 };
 
-/* File handle structure */
-struct vpif_fh {
-       /* pointer to channel object for opened device */
-       struct channel_obj *channel;
-       /* Indicates whether this file handle is doing IO */
-       u8 io_allowed[VPIF_NUMBER_OF_OBJECTS];
-       /* Used to keep track priority of this instance */
-       enum v4l2_priority prio;
-       /* Used to indicate channel is initialize or not */
-       u8 initialized;
-};
-
 struct vpif_device {
        struct v4l2_device v4l2_dev;
        struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS];
@@ -157,5 +130,4 @@ struct vpif_config_params {
        u8 max_device_type;
 };
 
-#endif                         /* End of __KERNEL__ */
 #endif                         /* VPIF_CAPTURE_H */
index aed41ed..5bb085b 100644 (file)
@@ -3,6 +3,7 @@
  * Display driver for TI DaVinci VPIF
  *
  * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2014 Lad, Prabhakar <prabhakar.csengg@gmail.com>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -35,129 +36,110 @@ MODULE_VERSION(VPIF_DISPLAY_VERSION);
                v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg)
 
 static int debug = 1;
-static u32 ch2_numbuffers = 3;
-static u32 ch3_numbuffers = 3;
-static u32 ch2_bufsize = 1920 * 1080 * 2;
-static u32 ch3_bufsize = 720 * 576 * 2;
 
 module_param(debug, int, 0644);
-module_param(ch2_numbuffers, uint, S_IRUGO);
-module_param(ch3_numbuffers, uint, S_IRUGO);
-module_param(ch2_bufsize, uint, S_IRUGO);
-module_param(ch3_bufsize, uint, S_IRUGO);
 
 MODULE_PARM_DESC(debug, "Debug level 0-1");
-MODULE_PARM_DESC(ch2_numbuffers, "Channel2 buffer count (default:3)");
-MODULE_PARM_DESC(ch3_numbuffers, "Channel3 buffer count (default:3)");
-MODULE_PARM_DESC(ch2_bufsize, "Channel2 buffer size (default:1920 x 1080 x 2)");
-MODULE_PARM_DESC(ch3_bufsize, "Channel3 buffer size (default:720 x 576 x 2)");
-
-static struct vpif_config_params config_params = {
-       .min_numbuffers         = 3,
-       .numbuffers[0]          = 3,
-       .numbuffers[1]          = 3,
-       .min_bufsize[0]         = 720 * 480 * 2,
-       .min_bufsize[1]         = 720 * 480 * 2,
-       .channel_bufsize[0]     = 1920 * 1080 * 2,
-       .channel_bufsize[1]     = 720 * 576 * 2,
-};
+
+#define VPIF_DRIVER_NAME       "vpif_display"
+
+/* Is set to 1 in case of SDTV formats, 2 in case of HDTV formats. */
+static int ycmux_mode;
+
+static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} };
 
 static struct vpif_device vpif_obj = { {NULL} };
 static struct device *vpif_dev;
 static void vpif_calculate_offsets(struct channel_obj *ch);
 static void vpif_config_addr(struct channel_obj *ch, int muxmode);
 
-/*
- * buffer_prepare: This is the callback function called from vb2_qbuf()
- * function the buffer is prepared and user space virtual address is converted
- * into physical address
+static inline struct vpif_disp_buffer *to_vpif_buffer(struct vb2_buffer *vb)
+{
+       return container_of(vb, struct vpif_disp_buffer, vb);
+}
+
+/**
+ * vpif_buffer_prepare :  callback function for buffer prepare
+ * @vb: ptr to vb2_buffer
+ *
+ * This is the callback function for buffer prepare when vb2_qbuf()
+ * function is called. The buffer is prepared and user space virtual address
+ * or user address is converted into  physical address
  */
 static int vpif_buffer_prepare(struct vb2_buffer *vb)
 {
-       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
-       struct vb2_queue *q = vb->vb2_queue;
+       struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue);
        struct common_obj *common;
-       unsigned long addr;
-
-       common = &fh->channel->common[VPIF_VIDEO_INDEX];
-       if (vb->state != VB2_BUF_STATE_ACTIVE &&
-               vb->state != VB2_BUF_STATE_PREPARED) {
-               vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
-               if (vb2_plane_vaddr(vb, 0) &&
-               vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
-                       goto buf_align_exit;
-
-               addr = vb2_dma_contig_plane_dma_addr(vb, 0);
-               if (q->streaming &&
-                       (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) {
-                       if (!ISALIGNED(addr + common->ytop_off) ||
+
+       common = &ch->common[VPIF_VIDEO_INDEX];
+
+       vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
+       if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
+               return -EINVAL;
+
+       vb->v4l2_buf.field = common->fmt.fmt.pix.field;
+
+       if (vb->vb2_queue->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+               unsigned long addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+               if (!ISALIGNED(addr + common->ytop_off) ||
                        !ISALIGNED(addr + common->ybtm_off) ||
                        !ISALIGNED(addr + common->ctop_off) ||
-                       !ISALIGNED(addr + common->cbtm_off))
-                               goto buf_align_exit;
+                       !ISALIGNED(addr + common->cbtm_off)) {
+                       vpif_err("buffer offset not aligned to 8 bytes\n");
+                       return -EINVAL;
                }
        }
-       return 0;
 
-buf_align_exit:
-       vpif_err("buffer offset not aligned to 8 bytes\n");
-       return -EINVAL;
+       return 0;
 }
 
-/*
- * vpif_buffer_queue_setup: This function allocates memory for the buffers
+/**
+ * vpif_buffer_queue_setup : Callback function for buffer setup.
+ * @vq: vb2_queue ptr
+ * @fmt: v4l2 format
+ * @nbuffers: ptr to number of buffers requested by application
+ * @nplanes:: contains number of distinct video planes needed to hold a frame
+ * @sizes[]: contains the size (in bytes) of each plane.
+ * @alloc_ctxs: ptr to allocation context
+ *
+ * This callback function is called when reqbuf() is called to adjust
+ * the buffer count and buffer size
  */
 static int vpif_buffer_queue_setup(struct vb2_queue *vq,
                                const struct v4l2_format *fmt,
                                unsigned int *nbuffers, unsigned int *nplanes,
                                unsigned int sizes[], void *alloc_ctxs[])
 {
-       struct vpif_fh *fh = vb2_get_drv_priv(vq);
-       struct channel_obj *ch = fh->channel;
+       struct channel_obj *ch = vb2_get_drv_priv(vq);
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       unsigned long size;
-
-       if (V4L2_MEMORY_MMAP == common->memory) {
-               size = config_params.channel_bufsize[ch->channel_id];
-               /*
-               * Checking if the buffer size exceeds the available buffer
-               * ycmux_mode = 0 means 1 channel mode HD and
-               * ycmux_mode = 1 means 2 channels mode SD
-               */
-               if (ch->vpifparams.std_info.ycmux_mode == 0) {
-                       if (config_params.video_limit[ch->channel_id])
-                               while (size * *nbuffers >
-                                       (config_params.video_limit[0]
-                                               + config_params.video_limit[1]))
-                                       (*nbuffers)--;
-               } else {
-                       if (config_params.video_limit[ch->channel_id])
-                               while (size * *nbuffers >
-                               config_params.video_limit[ch->channel_id])
-                                       (*nbuffers)--;
-               }
-       } else {
-               size = common->fmt.fmt.pix.sizeimage;
-       }
 
-       if (*nbuffers < config_params.min_numbuffers)
-                       *nbuffers = config_params.min_numbuffers;
+       if (fmt && fmt->fmt.pix.sizeimage < common->fmt.fmt.pix.sizeimage)
+               return -EINVAL;
+
+       if (vq->num_buffers + *nbuffers < 3)
+               *nbuffers = 3 - vq->num_buffers;
 
        *nplanes = 1;
-       sizes[0] = size;
+       sizes[0] = fmt ? fmt->fmt.pix.sizeimage : common->fmt.fmt.pix.sizeimage;
        alloc_ctxs[0] = common->alloc_ctx;
+
+       /* Calculate the offset for Y and C data  in the buffer */
+       vpif_calculate_offsets(ch);
+
        return 0;
 }
 
-/*
- * vpif_buffer_queue: This function adds the buffer to DMA queue
+/**
+ * vpif_buffer_queue : Callback function to add buffer to DMA queue
+ * @vb: ptr to vb2_buffer
+ *
+ * This callback fucntion queues the buffer to DMA engine
  */
 static void vpif_buffer_queue(struct vb2_buffer *vb)
 {
-       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
-       struct vpif_disp_buffer *buf = container_of(vb,
-                               struct vpif_disp_buffer, vb);
-       struct channel_obj *ch = fh->channel;
+       struct vpif_disp_buffer *buf = to_vpif_buffer(vb);
+       struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue);
        struct common_obj *common;
        unsigned long flags;
 
@@ -169,98 +151,26 @@ static void vpif_buffer_queue(struct vb2_buffer *vb)
        spin_unlock_irqrestore(&common->irqlock, flags);
 }
 
-/*
- * vpif_buf_cleanup: This function is called from the videobuf2 layer to
- * free memory allocated to the buffers
+/**
+ * vpif_start_streaming : Starts the DMA engine for streaming
+ * @vb: ptr to vb2_buffer
+ * @count: number of buffers
  */
-static void vpif_buf_cleanup(struct vb2_buffer *vb)
-{
-       struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
-       struct vpif_disp_buffer *buf = container_of(vb,
-                                       struct vpif_disp_buffer, vb);
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common;
-       unsigned long flags;
-
-       common = &ch->common[VPIF_VIDEO_INDEX];
-
-       spin_lock_irqsave(&common->irqlock, flags);
-       if (vb->state == VB2_BUF_STATE_ACTIVE)
-               list_del_init(&buf->list);
-       spin_unlock_irqrestore(&common->irqlock, flags);
-}
-
-static void vpif_wait_prepare(struct vb2_queue *vq)
-{
-       struct vpif_fh *fh = vb2_get_drv_priv(vq);
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common;
-
-       common = &ch->common[VPIF_VIDEO_INDEX];
-       mutex_unlock(&common->lock);
-}
-
-static void vpif_wait_finish(struct vb2_queue *vq)
-{
-       struct vpif_fh *fh = vb2_get_drv_priv(vq);
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common;
-
-       common = &ch->common[VPIF_VIDEO_INDEX];
-       mutex_lock(&common->lock);
-}
-
-static int vpif_buffer_init(struct vb2_buffer *vb)
-{
-       struct vpif_disp_buffer *buf = container_of(vb,
-                                       struct vpif_disp_buffer, vb);
-
-       INIT_LIST_HEAD(&buf->list);
-
-       return 0;
-}
-
-static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} };
-
 static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
 {
        struct vpif_display_config *vpif_config_data =
                                        vpif_dev->platform_data;
-       struct vpif_fh *fh = vb2_get_drv_priv(vq);
-       struct channel_obj *ch = fh->channel;
+       struct channel_obj *ch = vb2_get_drv_priv(vq);
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
        struct vpif_params *vpif = &ch->vpifparams;
-       unsigned long addr = 0;
-       unsigned long flags;
+       struct vpif_disp_buffer *buf, *tmp;
+       unsigned long addr, flags;
        int ret;
 
        spin_lock_irqsave(&common->irqlock, flags);
 
-       /* Get the next frame from the buffer queue */
-       common->next_frm = common->cur_frm =
-                           list_entry(common->dma_queue.next,
-                                      struct vpif_disp_buffer, list);
-
-       list_del(&common->cur_frm->list);
-       spin_unlock_irqrestore(&common->irqlock, flags);
-       /* Mark state of the current frame to active */
-       common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
-
-       /* Initialize field_id and started member */
+       /* Initialize field_id */
        ch->field_id = 0;
-       common->started = 1;
-       addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
-       /* Calculate the offset for Y and C data  in the buffer */
-       vpif_calculate_offsets(ch);
-
-       if ((ch->vpifparams.std_info.frm_fmt &&
-               ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE)
-               && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY)))
-               || (!ch->vpifparams.std_info.frm_fmt
-               && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
-               vpif_err("conflict in field format and std format\n");
-               return -EINVAL;
-       }
 
        /* clock settings */
        if (vpif_config_data->set_clock) {
@@ -268,24 +178,37 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
                ycmux_mode, ch->vpifparams.std_info.hd_sd);
                if (ret < 0) {
                        vpif_err("can't set clock\n");
-                       return ret;
+                       goto err;
                }
        }
 
        /* set the parameters and addresses */
        ret = vpif_set_video_params(vpif, ch->channel_id + 2);
        if (ret < 0)
-               return ret;
+               goto err;
 
-       common->started = ret;
+       ycmux_mode = ret;
        vpif_config_addr(ch, ret);
+       /* Get the next frame from the buffer queue */
+       common->next_frm = common->cur_frm =
+                           list_entry(common->dma_queue.next,
+                                      struct vpif_disp_buffer, list);
+
+       list_del(&common->cur_frm->list);
+       spin_unlock_irqrestore(&common->irqlock, flags);
+       /* Mark state of the current frame to active */
+       common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+
+       addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
        common->set_addr((addr + common->ytop_off),
                            (addr + common->ybtm_off),
                            (addr + common->ctop_off),
                            (addr + common->cbtm_off));
 
-       /* Set interrupt for both the fields in VPIF
-           Register enable channel in VPIF register */
+       /*
+        * Set interrupt for both the fields in VPIF
+        * Register enable channel in VPIF register
+        */
        channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
        if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
                channel2_intr_assert();
@@ -295,8 +218,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
                        channel2_clipping_enable(1);
        }
 
-       if ((VPIF_CHANNEL3_VIDEO == ch->channel_id)
-               || (common->started == 2)) {
+       if (VPIF_CHANNEL3_VIDEO == ch->channel_id || ycmux_mode == 2) {
                channel3_intr_assert();
                channel3_intr_enable(1);
                enable_channel3(1);
@@ -305,19 +227,29 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
        }
 
        return 0;
+
+err:
+       list_for_each_entry_safe(buf, tmp, &common->dma_queue, list) {
+               list_del(&buf->list);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+       }
+
+       return ret;
 }
 
-/* abort streaming and wait for last buffer */
-static int vpif_stop_streaming(struct vb2_queue *vq)
+/**
+ * vpif_stop_streaming : Stop the DMA engine
+ * @vq: ptr to vb2_queue
+ *
+ * This callback stops the DMA engine and any remaining buffers
+ * in the DMA queue are released.
+ */
+static void vpif_stop_streaming(struct vb2_queue *vq)
 {
-       struct vpif_fh *fh = vb2_get_drv_priv(vq);
-       struct channel_obj *ch = fh->channel;
+       struct channel_obj *ch = vb2_get_drv_priv(vq);
        struct common_obj *common;
        unsigned long flags;
 
-       if (!vb2_is_streaming(vq))
-               return 0;
-
        common = &ch->common[VPIF_VIDEO_INDEX];
 
        /* Disable channel */
@@ -325,12 +257,10 @@ static int vpif_stop_streaming(struct vb2_queue *vq)
                enable_channel2(0);
                channel2_intr_enable(0);
        }
-       if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
-               (2 == common->started)) {
+       if (VPIF_CHANNEL3_VIDEO == ch->channel_id || ycmux_mode == 2) {
                enable_channel3(0);
                channel3_intr_enable(0);
        }
-       common->started = 0;
 
        /* release all active buffers */
        spin_lock_irqsave(&common->irqlock, flags);
@@ -352,19 +282,15 @@ static int vpif_stop_streaming(struct vb2_queue *vq)
                vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
        }
        spin_unlock_irqrestore(&common->irqlock, flags);
-
-       return 0;
 }
 
 static struct vb2_ops video_qops = {
        .queue_setup            = vpif_buffer_queue_setup,
-       .wait_prepare           = vpif_wait_prepare,
-       .wait_finish            = vpif_wait_finish,
-       .buf_init               = vpif_buffer_init,
+       .wait_prepare           = vb2_ops_wait_prepare,
+       .wait_finish            = vb2_ops_wait_finish,
        .buf_prepare            = vpif_buffer_prepare,
        .start_streaming        = vpif_start_streaming,
        .stop_streaming         = vpif_stop_streaming,
-       .buf_cleanup            = vpif_buf_cleanup,
        .buf_queue              = vpif_buffer_queue,
 };
 
@@ -446,8 +372,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
        for (i = 0; i < VPIF_NUMOBJECTS; i++) {
                common = &ch->common[i];
                /* If streaming is started in this channel */
-               if (0 == common->started)
-                       continue;
 
                if (1 == ch->vpifparams.std_info.frm_fmt) {
                        spin_lock(&common->irqlock);
@@ -543,6 +467,7 @@ static int vpif_update_resolution(struct channel_obj *ch)
                        return -EINVAL;
        }
 
+       common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
        common->fmt.fmt.pix.width = std_info->width;
        common->fmt.fmt.pix.height = std_info->height;
        vpif_dbg(1, debug, "Pixel details: Width = %d,Height = %d\n",
@@ -551,6 +476,17 @@ static int vpif_update_resolution(struct channel_obj *ch)
        /* Set height and width paramateres */
        common->height = std_info->height;
        common->width = std_info->width;
+       common->fmt.fmt.pix.sizeimage = common->height * common->width * 2;
+
+       if (vid_ch->stdid)
+               common->fmt.fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+       else
+               common->fmt.fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
+
+       if (ch->vpifparams.std_info.frm_fmt)
+               common->fmt.fmt.pix.field = V4L2_FIELD_NONE;
+       else
+               common->fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
 
        return 0;
 }
@@ -621,70 +557,6 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
        ch->vpifparams.video_params.stdid = ch->vpifparams.std_info.stdid;
 }
 
-static void vpif_config_format(struct channel_obj *ch)
-{
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
-       common->fmt.fmt.pix.field = V4L2_FIELD_ANY;
-       if (config_params.numbuffers[ch->channel_id] == 0)
-               common->memory = V4L2_MEMORY_USERPTR;
-       else
-               common->memory = V4L2_MEMORY_MMAP;
-
-       common->fmt.fmt.pix.sizeimage =
-                       config_params.channel_bufsize[ch->channel_id];
-       common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
-       common->fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-}
-
-static int vpif_check_format(struct channel_obj *ch,
-                            struct v4l2_pix_format *pixfmt)
-{
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       enum v4l2_field field = pixfmt->field;
-       u32 sizeimage, hpitch, vpitch;
-
-       if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P)
-               goto invalid_fmt_exit;
-
-       if (!(VPIF_VALID_FIELD(field)))
-               goto invalid_fmt_exit;
-
-       if (pixfmt->bytesperline <= 0)
-               goto invalid_pitch_exit;
-
-       sizeimage = pixfmt->sizeimage;
-
-       if (vpif_update_resolution(ch))
-               return -EINVAL;
-
-       hpitch = pixfmt->bytesperline;
-       vpitch = sizeimage / (hpitch * 2);
-
-       /* Check for valid value of pitch */
-       if ((hpitch < ch->vpifparams.std_info.width) ||
-           (vpitch < ch->vpifparams.std_info.height))
-               goto invalid_pitch_exit;
-
-       /* Check for 8 byte alignment */
-       if (!ISALIGNED(hpitch)) {
-               vpif_err("invalid pitch alignment\n");
-               return -EINVAL;
-       }
-       pixfmt->width = common->fmt.fmt.pix.width;
-       pixfmt->height = common->fmt.fmt.pix.height;
-
-       return 0;
-
-invalid_fmt_exit:
-       vpif_err("invalid field format\n");
-       return -EINVAL;
-
-invalid_pitch_exit:
-       vpif_err("invalid pitch\n");
-       return -EINVAL;
-}
-
 static void vpif_config_addr(struct channel_obj *ch, int muxmode)
 {
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
@@ -699,127 +571,6 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode)
        }
 }
 
-/*
- * vpif_mmap: It is used to map kernel space buffers into user spaces
- */
-static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
-{
-       struct vpif_fh *fh = filep->private_data;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
-       int ret;
-
-       vpif_dbg(2, debug, "vpif_mmap\n");
-
-       if (mutex_lock_interruptible(&common->lock))
-               return -ERESTARTSYS;
-       ret = vb2_mmap(&common->buffer_queue, vma);
-       mutex_unlock(&common->lock);
-       return ret;
-}
-
-/*
- * vpif_poll: It is used for select/poll system call
- */
-static unsigned int vpif_poll(struct file *filep, poll_table *wait)
-{
-       struct vpif_fh *fh = filep->private_data;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       unsigned int res = 0;
-
-       if (common->started) {
-               mutex_lock(&common->lock);
-               res = vb2_poll(&common->buffer_queue, filep, wait);
-               mutex_unlock(&common->lock);
-       }
-
-       return res;
-}
-
-/*
- * vpif_open: It creates object of file handle structure and stores it in
- * private_data member of filepointer
- */
-static int vpif_open(struct file *filep)
-{
-       struct video_device *vdev = video_devdata(filep);
-       struct channel_obj *ch = video_get_drvdata(vdev);
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       struct vpif_fh *fh;
-
-       /* Allocate memory for the file handle object */
-       fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL);
-       if (fh == NULL) {
-               vpif_err("unable to allocate memory for file handle object\n");
-               return -ENOMEM;
-       }
-
-       if (mutex_lock_interruptible(&common->lock)) {
-               kfree(fh);
-               return -ERESTARTSYS;
-       }
-       /* store pointer to fh in private_data member of filep */
-       filep->private_data = fh;
-       fh->channel = ch;
-       fh->initialized = 0;
-       if (!ch->initialized) {
-               fh->initialized = 1;
-               ch->initialized = 1;
-               memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
-       }
-
-       /* Increment channel usrs counter */
-       atomic_inc(&ch->usrs);
-       /* Set io_allowed[VPIF_VIDEO_INDEX] member to false */
-       fh->io_allowed[VPIF_VIDEO_INDEX] = 0;
-       /* Initialize priority of this instance to default priority */
-       fh->prio = V4L2_PRIORITY_UNSET;
-       v4l2_prio_open(&ch->prio, &fh->prio);
-       mutex_unlock(&common->lock);
-
-       return 0;
-}
-
-/*
- * vpif_release: This function deletes buffer queue, frees the buffers and
- * the vpif file handle
- */
-static int vpif_release(struct file *filep)
-{
-       struct vpif_fh *fh = filep->private_data;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
-       mutex_lock(&common->lock);
-       /* if this instance is doing IO */
-       if (fh->io_allowed[VPIF_VIDEO_INDEX]) {
-               /* Reset io_usrs member of channel object */
-               common->io_usrs = 0;
-               /* Free buffers allocated */
-               vb2_queue_release(&common->buffer_queue);
-               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
-
-               common->numbuffers =
-                   config_params.numbuffers[ch->channel_id];
-       }
-
-       /* Decrement channel usrs counter */
-       atomic_dec(&ch->usrs);
-       /* If this file handle has initialize encoder device, reset it */
-       if (fh->initialized)
-               ch->initialized = 0;
-
-       /* Close the priority */
-       v4l2_prio_close(&ch->prio, fh->prio);
-       filep->private_data = NULL;
-       fh->initialized = 0;
-       mutex_unlock(&common->lock);
-       kfree(fh);
-
-       return 0;
-}
-
 /* functions implementing ioctls */
 /**
  * vpif_querycap() - QUERYCAP handler
@@ -834,7 +585,7 @@ static int vpif_querycap(struct file *file, void  *priv,
 
        cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
        cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-       snprintf(cap->driver, sizeof(cap->driver), "%s", dev_name(vpif_dev));
+       strlcpy(cap->driver, VPIF_DRIVER_NAME, sizeof(cap->driver));
        snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
                 dev_name(vpif_dev));
        strlcpy(cap->card, config->card_name, sizeof(cap->card));
@@ -845,24 +596,22 @@ static int vpif_querycap(struct file *file, void  *priv,
 static int vpif_enum_fmt_vid_out(struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *fmt)
 {
-       if (fmt->index != 0) {
-               vpif_err("Invalid format index\n");
+       if (fmt->index != 0)
                return -EINVAL;
-       }
 
        /* Fill in the information about format */
        fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
        strcpy(fmt->description, "YCbCr4:2:2 YC Planar");
        fmt->pixelformat = V4L2_PIX_FMT_YUV422P;
-
+       fmt->flags = 0;
        return 0;
 }
 
 static int vpif_g_fmt_vid_out(struct file *file, void *priv,
                                struct v4l2_format *fmt)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
 
        /* Check the validity of the buffer type */
@@ -875,193 +624,84 @@ static int vpif_g_fmt_vid_out(struct file *file, void *priv,
        return 0;
 }
 
-static int vpif_s_fmt_vid_out(struct file *file, void *priv,
+static int vpif_try_fmt_vid_out(struct file *file, void *priv,
                                struct v4l2_format *fmt)
 {
-       struct vpif_fh *fh = priv;
-       struct v4l2_pix_format *pixfmt;
-       struct channel_obj *ch = fh->channel;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       int ret = 0;
-
-       if ((VPIF_CHANNEL2_VIDEO == ch->channel_id)
-           || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) {
-               if (!fh->initialized) {
-                       vpif_dbg(1, debug, "Channel Busy\n");
-                       return -EBUSY;
-               }
+       struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
 
-               /* Check for the priority */
-               ret = v4l2_prio_check(&ch->prio, fh->prio);
-               if (0 != ret)
-                       return ret;
-               fh->initialized = 1;
-       }
+       /*
+        * to supress v4l-compliance warnings silently correct
+        * the pixelformat
+        */
+       if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P)
+               pixfmt->pixelformat = common->fmt.fmt.pix.pixelformat;
 
-       if (common->started) {
-               vpif_dbg(1, debug, "Streaming in progress\n");
-               return -EBUSY;
-       }
+       if (vpif_update_resolution(ch))
+               return -EINVAL;
 
-       pixfmt = &fmt->fmt.pix;
-       /* Check for valid field format */
-       ret = vpif_check_format(ch, pixfmt);
-       if (ret)
-               return ret;
+       pixfmt->colorspace = common->fmt.fmt.pix.colorspace;
+       pixfmt->field = common->fmt.fmt.pix.field;
+       pixfmt->bytesperline = common->fmt.fmt.pix.width;
+       pixfmt->width = common->fmt.fmt.pix.width;
+       pixfmt->height = common->fmt.fmt.pix.height;
+       pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height * 2;
+       pixfmt->priv = 0;
 
-       /* store the pix format in the channel object */
-       common->fmt.fmt.pix = *pixfmt;
-       /* store the format in the channel object */
-       common->fmt = *fmt;
        return 0;
 }
 
-static int vpif_try_fmt_vid_out(struct file *file, void *priv,
+static int vpif_s_fmt_vid_out(struct file *file, void *priv,
                                struct v4l2_format *fmt)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
        struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
-       int ret = 0;
-
-       ret = vpif_check_format(ch, pixfmt);
-       if (ret) {
-               *pixfmt = common->fmt.fmt.pix;
-               pixfmt->sizeimage = pixfmt->width * pixfmt->height * 2;
-       }
-
-       return ret;
-}
-
-static int vpif_reqbufs(struct file *file, void *priv,
-                       struct v4l2_requestbuffers *reqbuf)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common;
-       enum v4l2_field field;
-       struct vb2_queue *q;
-       u8 index = 0;
        int ret;
 
-       /* This file handle has not initialized the channel,
-          It is not allowed to do settings */
-       if ((VPIF_CHANNEL2_VIDEO == ch->channel_id)
-           || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) {
-               if (!fh->initialized) {
-                       vpif_err("Channel Busy\n");
-                       return -EBUSY;
-               }
-       }
-
-       if (V4L2_BUF_TYPE_VIDEO_OUTPUT != reqbuf->type)
-               return -EINVAL;
-
-       index = VPIF_VIDEO_INDEX;
-
-       common = &ch->common[index];
-
-       if (common->fmt.type != reqbuf->type || !vpif_dev)
-               return -EINVAL;
-       if (0 != common->io_usrs)
+       if (vb2_is_busy(&common->buffer_queue))
                return -EBUSY;
 
-       if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
-               if (common->fmt.fmt.pix.field == V4L2_FIELD_ANY)
-                       field = V4L2_FIELD_INTERLACED;
-               else
-                       field = common->fmt.fmt.pix.field;
-       } else {
-               field = V4L2_VBI_INTERLACED;
-       }
-       /* Initialize videobuf2 queue as per the buffer type */
-       common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
-       if (IS_ERR(common->alloc_ctx)) {
-               vpif_err("Failed to get the context\n");
-               return PTR_ERR(common->alloc_ctx);
-       }
-       q = &common->buffer_queue;
-       q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-       q->io_modes = VB2_MMAP | VB2_USERPTR;
-       q->drv_priv = fh;
-       q->ops = &video_qops;
-       q->mem_ops = &vb2_dma_contig_memops;
-       q->buf_struct_size = sizeof(struct vpif_disp_buffer);
-       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-       q->min_buffers_needed = 1;
-
-       ret = vb2_queue_init(q);
-       if (ret) {
-               vpif_err("vpif_display: vb2_queue_init() failed\n");
-               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
+       ret = vpif_try_fmt_vid_out(file, priv, fmt);
+       if (ret)
                return ret;
-       }
-       /* Set io allowed member of file handle to TRUE */
-       fh->io_allowed[index] = 1;
-       /* Increment io usrs member of channel object to 1 */
-       common->io_usrs = 1;
-       /* Store type of memory requested in channel object */
-       common->memory = reqbuf->memory;
-       INIT_LIST_HEAD(&common->dma_queue);
-       /* Allocate buffers */
-       return vb2_reqbufs(&common->buffer_queue, reqbuf);
-}
-
-static int vpif_querybuf(struct file *file, void *priv,
-                               struct v4l2_buffer *tbuf)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
 
-       if (common->fmt.type != tbuf->type)
-               return -EINVAL;
+       /* store the pix format in the channel object */
+       common->fmt.fmt.pix = *pixfmt;
 
-       return vb2_querybuf(&common->buffer_queue, tbuf);
+       /* store the format in the channel object */
+       common->fmt = *fmt;
+       return 0;
 }
 
-static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)
 {
-       struct vpif_fh *fh = NULL;
-       struct channel_obj *ch = NULL;
-       struct common_obj *common = NULL;
-
-       if (!buf || !priv)
-               return -EINVAL;
-
-       fh = priv;
-       ch = fh->channel;
-       if (!ch)
-               return -EINVAL;
+       struct vpif_display_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
+       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+       struct vpif_display_chan_config *chan_cfg;
+       struct v4l2_output output;
+       int ret;
 
-       common = &(ch->common[VPIF_VIDEO_INDEX]);
-       if (common->fmt.type != buf->type)
-               return -EINVAL;
+       if (config->chan_config[ch->channel_id].outputs == NULL)
+               return -ENODATA;
 
-       if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
-               vpif_err("fh->io_allowed\n");
-               return -EACCES;
-       }
+       chan_cfg = &config->chan_config[ch->channel_id];
+       output = chan_cfg->outputs[ch->output_idx].output;
+       if (output.capabilities != V4L2_OUT_CAP_STD)
+               return -ENODATA;
 
-       return vb2_qbuf(&common->buffer_queue, buf);
-}
+       if (vb2_is_busy(&common->buffer_queue))
+               return -EBUSY;
 
-static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       int ret = 0;
 
        if (!(std_id & VPIF_V4L2_STD))
                return -EINVAL;
 
-       if (common->started) {
-               vpif_err("streaming in progress\n");
-               return -EBUSY;
-       }
-
        /* Call encoder subdevice function to set the standard */
        ch->video.stdid = std_id;
        memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings));
@@ -1069,16 +709,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)
        if (vpif_update_resolution(ch))
                return -EINVAL;
 
-       if ((ch->vpifparams.std_info.width *
-               ch->vpifparams.std_info.height * 2) >
-               config_params.channel_bufsize[ch->channel_id]) {
-               vpif_err("invalid std for this size\n");
-               return -EINVAL;
-       }
-
        common->fmt.fmt.pix.bytesperline = common->fmt.fmt.pix.width;
-       /* Configure the default format information */
-       vpif_config_format(ch);
 
        ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video,
                                                s_std_output, std_id);
@@ -1087,7 +718,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)
                return ret;
        }
 
-       ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core,
+       ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video,
                                                        s_std, std_id);
        if (ret < 0)
                vpif_err("Failed to set standard for sub devices\n");
@@ -1096,132 +727,21 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)
 
 static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-
-       *std = ch->video.stdid;
-       return 0;
-}
-
-static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
-       return vb2_dqbuf(&common->buffer_queue, p,
-                                       (file->f_flags & O_NONBLOCK));
-}
-
-static int vpif_streamon(struct file *file, void *priv,
-                               enum v4l2_buf_type buftype)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
-       int ret = 0;
-
-       if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
-               vpif_err("buffer type not supported\n");
-               return -EINVAL;
-       }
-
-       if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
-               vpif_err("fh->io_allowed\n");
-               return -EACCES;
-       }
-
-       /* If Streaming is already started, return error */
-       if (common->started) {
-               vpif_err("channel->started\n");
-               return -EBUSY;
-       }
-
-       if ((ch->channel_id == VPIF_CHANNEL2_VIDEO
-               && oth_ch->common[VPIF_VIDEO_INDEX].started &&
-               ch->vpifparams.std_info.ycmux_mode == 0)
-               || ((ch->channel_id == VPIF_CHANNEL3_VIDEO)
-               && (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) {
-               vpif_err("other channel is using\n");
-               return -EBUSY;
-       }
-
-       ret = vpif_check_format(ch, &common->fmt.fmt.pix);
-       if (ret < 0)
-               return ret;
-
-       /* Call vb2_streamon to start streaming in videobuf2 */
-       ret = vb2_streamon(&common->buffer_queue, buftype);
-       if (ret < 0) {
-               vpif_err("vb2_streamon\n");
-               return ret;
-       }
-
-       return ret;
-}
-
-static int vpif_streamoff(struct file *file, void *priv,
-                               enum v4l2_buf_type buftype)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       struct vpif_display_config *vpif_config_data =
-                                       vpif_dev->platform_data;
-
-       if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
-               vpif_err("buffer type not supported\n");
-               return -EINVAL;
-       }
-
-       if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
-               vpif_err("fh->io_allowed\n");
-               return -EACCES;
-       }
-
-       if (!common->started) {
-               vpif_err("channel->started\n");
-               return -EINVAL;
-       }
-
-       if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
-               /* disable channel */
-               if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
-                       if (vpif_config_data->
-                               chan_config[VPIF_CHANNEL2_VIDEO].clip_en)
-                               channel2_clipping_enable(0);
-                       enable_channel2(0);
-                       channel2_intr_enable(0);
-               }
-               if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
-                                       (2 == common->started)) {
-                       if (vpif_config_data->
-                               chan_config[VPIF_CHANNEL3_VIDEO].clip_en)
-                               channel3_clipping_enable(0);
-                       enable_channel3(0);
-                       channel3_intr_enable(0);
-               }
-       }
-
-       common->started = 0;
-       return vb2_streamoff(&common->buffer_queue, buftype);
-}
+       struct vpif_display_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
+       struct vpif_display_chan_config *chan_cfg;
+       struct v4l2_output output;
 
-static int vpif_cropcap(struct file *file, void *priv,
-                       struct v4l2_cropcap *crop)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-       if (V4L2_BUF_TYPE_VIDEO_OUTPUT != crop->type)
-               return -EINVAL;
+       if (config->chan_config[ch->channel_id].outputs == NULL)
+               return -ENODATA;
 
-       crop->bounds.left = crop->bounds.top = 0;
-       crop->defrect.left = crop->defrect.top = 0;
-       crop->defrect.height = crop->bounds.height = common->height;
-       crop->defrect.width = crop->bounds.width = common->width;
+       chan_cfg = &config->chan_config[ch->channel_id];
+       output = chan_cfg->outputs[ch->output_idx].output;
+       if (output.capabilities != V4L2_OUT_CAP_STD)
+               return -ENODATA;
 
+       *std = ch->video.stdid;
        return 0;
 }
 
@@ -1230,9 +750,9 @@ static int vpif_enum_output(struct file *file, void *fh,
 {
 
        struct vpif_display_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct vpif_display_chan_config *chan_cfg;
-       struct vpif_fh *vpif_handler = fh;
-       struct channel_obj *ch = vpif_handler->channel;
 
        chan_cfg = &config->chan_config[ch->channel_id];
        if (output->index >= chan_cfg->output_count) {
@@ -1326,52 +846,32 @@ static int vpif_set_output(struct vpif_display_config *vpif_cfg,
 static int vpif_s_output(struct file *file, void *priv, unsigned int i)
 {
        struct vpif_display_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct vpif_display_chan_config *chan_cfg;
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
 
+       if (vb2_is_busy(&common->buffer_queue))
+               return -EBUSY;
+
        chan_cfg = &config->chan_config[ch->channel_id];
 
        if (i >= chan_cfg->output_count)
                return -EINVAL;
 
-       if (common->started) {
-               vpif_err("Streaming in progress\n");
-               return -EBUSY;
-       }
-
        return vpif_set_output(config, ch, i);
 }
 
 static int vpif_g_output(struct file *file, void *priv, unsigned int *i)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
 
        *i = ch->output_idx;
 
        return 0;
 }
 
-static int vpif_g_priority(struct file *file, void *priv, enum v4l2_priority *p)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-
-       *p = v4l2_prio_max(&ch->prio);
-
-       return 0;
-}
-
-static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p)
-{
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
-
-       return v4l2_prio_change(&ch->prio, &fh->prio, p);
-}
-
 /**
  * vpif_enum_dv_timings() - ENUM_DV_TIMINGS handler
  * @file: file ptr
@@ -1382,11 +882,24 @@ static int
 vpif_enum_dv_timings(struct file *file, void *priv,
                     struct v4l2_enum_dv_timings *timings)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct vpif_display_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
+       struct vpif_display_chan_config *chan_cfg;
+       struct v4l2_output output;
        int ret;
 
-       ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings);
+       if (config->chan_config[ch->channel_id].outputs == NULL)
+               return -ENODATA;
+
+       chan_cfg = &config->chan_config[ch->channel_id];
+       output = chan_cfg->outputs[ch->output_idx].output;
+       if (output.capabilities != V4L2_OUT_CAP_DV_TIMINGS)
+               return -ENODATA;
+
+       timings->pad = 0;
+
+       ret = v4l2_subdev_call(ch->sd, pad, enum_dv_timings, timings);
        if (ret == -ENOIOCTLCMD || ret == -ENODEV)
                return -EINVAL;
        return ret;
@@ -1401,14 +914,29 @@ vpif_enum_dv_timings(struct file *file, void *priv,
 static int vpif_s_dv_timings(struct file *file, void *priv,
                struct v4l2_dv_timings *timings)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct vpif_display_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
        struct vpif_params *vpifparams = &ch->vpifparams;
+       struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
        struct vpif_channel_config_params *std_info = &vpifparams->std_info;
        struct video_obj *vid_ch = &ch->video;
        struct v4l2_bt_timings *bt = &vid_ch->dv_timings.bt;
+       struct vpif_display_chan_config *chan_cfg;
+       struct v4l2_output output;
        int ret;
 
+       if (config->chan_config[ch->channel_id].outputs == NULL)
+               return -ENODATA;
+
+       chan_cfg = &config->chan_config[ch->channel_id];
+       output = chan_cfg->outputs[ch->output_idx].output;
+       if (output.capabilities != V4L2_OUT_CAP_DV_TIMINGS)
+               return -ENODATA;
+
+       if (vb2_is_busy(&common->buffer_queue))
+               return -EBUSY;
+
        if (timings->type != V4L2_DV_BT_656_1120) {
                vpif_dbg(2, debug, "Timing type not defined\n");
                return -EINVAL;
@@ -1490,13 +1018,27 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
 static int vpif_g_dv_timings(struct file *file, void *priv,
                struct v4l2_dv_timings *timings)
 {
-       struct vpif_fh *fh = priv;
-       struct channel_obj *ch = fh->channel;
+       struct vpif_display_config *config = vpif_dev->platform_data;
+       struct video_device *vdev = video_devdata(file);
+       struct channel_obj *ch = video_get_drvdata(vdev);
+       struct vpif_display_chan_config *chan_cfg;
        struct video_obj *vid_ch = &ch->video;
+       struct v4l2_output output;
+
+       if (config->chan_config[ch->channel_id].outputs == NULL)
+               goto error;
+
+       chan_cfg = &config->chan_config[ch->channel_id];
+       output = chan_cfg->outputs[ch->output_idx].output;
+
+       if (output.capabilities != V4L2_OUT_CAP_DV_TIMINGS)
+               goto error;
 
        *timings = vid_ch->dv_timings;
 
        return 0;
+error:
+       return -ENODATA;
 }
 
 /*
@@ -1516,83 +1058,49 @@ static int vpif_log_status(struct file *filep, void *priv)
 
 /* vpif display ioctl operations */
 static const struct v4l2_ioctl_ops vpif_ioctl_ops = {
-       .vidioc_querycap                = vpif_querycap,
-       .vidioc_g_priority              = vpif_g_priority,
-       .vidioc_s_priority              = vpif_s_priority,
+       .vidioc_querycap                = vpif_querycap,
        .vidioc_enum_fmt_vid_out        = vpif_enum_fmt_vid_out,
-       .vidioc_g_fmt_vid_out           = vpif_g_fmt_vid_out,
-       .vidioc_s_fmt_vid_out           = vpif_s_fmt_vid_out,
-       .vidioc_try_fmt_vid_out         = vpif_try_fmt_vid_out,
-       .vidioc_reqbufs                 = vpif_reqbufs,
-       .vidioc_querybuf                = vpif_querybuf,
-       .vidioc_qbuf                    = vpif_qbuf,
-       .vidioc_dqbuf                   = vpif_dqbuf,
-       .vidioc_streamon                = vpif_streamon,
-       .vidioc_streamoff               = vpif_streamoff,
-       .vidioc_s_std                   = vpif_s_std,
+       .vidioc_g_fmt_vid_out           = vpif_g_fmt_vid_out,
+       .vidioc_s_fmt_vid_out           = vpif_s_fmt_vid_out,
+       .vidioc_try_fmt_vid_out         = vpif_try_fmt_vid_out,
+
+       .vidioc_reqbufs                 = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs             = vb2_ioctl_create_bufs,
+       .vidioc_querybuf                = vb2_ioctl_querybuf,
+       .vidioc_qbuf                    = vb2_ioctl_qbuf,
+       .vidioc_dqbuf                   = vb2_ioctl_dqbuf,
+       .vidioc_expbuf                  = vb2_ioctl_expbuf,
+       .vidioc_streamon                = vb2_ioctl_streamon,
+       .vidioc_streamoff               = vb2_ioctl_streamoff,
+
+       .vidioc_s_std                   = vpif_s_std,
        .vidioc_g_std                   = vpif_g_std,
+
        .vidioc_enum_output             = vpif_enum_output,
        .vidioc_s_output                = vpif_s_output,
        .vidioc_g_output                = vpif_g_output,
-       .vidioc_cropcap                 = vpif_cropcap,
-       .vidioc_enum_dv_timings         = vpif_enum_dv_timings,
-       .vidioc_s_dv_timings            = vpif_s_dv_timings,
-       .vidioc_g_dv_timings            = vpif_g_dv_timings,
+
+       .vidioc_enum_dv_timings         = vpif_enum_dv_timings,
+       .vidioc_s_dv_timings            = vpif_s_dv_timings,
+       .vidioc_g_dv_timings            = vpif_g_dv_timings,
+
        .vidioc_log_status              = vpif_log_status,
 };
 
 static const struct v4l2_file_operations vpif_fops = {
        .owner          = THIS_MODULE,
-       .open           = vpif_open,
-       .release        = vpif_release,
+       .open           = v4l2_fh_open,
+       .release        = vb2_fop_release,
        .unlocked_ioctl = video_ioctl2,
-       .mmap           = vpif_mmap,
-       .poll           = vpif_poll
-};
-
-static struct video_device vpif_video_template = {
-       .name           = "vpif",
-       .fops           = &vpif_fops,
-       .ioctl_ops      = &vpif_ioctl_ops,
+       .mmap           = vb2_fop_mmap,
+       .poll           = vb2_fop_poll
 };
 
 /*Configure the channels, buffer sizei, request irq */
 static int initialize_vpif(void)
 {
        int free_channel_objects_index;
-       int free_buffer_channel_index;
-       int free_buffer_index;
-       int err = 0, i, j;
-
-       /* Default number of buffers should be 3 */
-       if ((ch2_numbuffers > 0) &&
-           (ch2_numbuffers < config_params.min_numbuffers))
-               ch2_numbuffers = config_params.min_numbuffers;
-       if ((ch3_numbuffers > 0) &&
-           (ch3_numbuffers < config_params.min_numbuffers))
-               ch3_numbuffers = config_params.min_numbuffers;
-
-       /* Set buffer size to min buffers size if invalid buffer size is
-        * given */
-       if (ch2_bufsize < config_params.min_bufsize[VPIF_CHANNEL2_VIDEO])
-               ch2_bufsize =
-                   config_params.min_bufsize[VPIF_CHANNEL2_VIDEO];
-       if (ch3_bufsize < config_params.min_bufsize[VPIF_CHANNEL3_VIDEO])
-               ch3_bufsize =
-                   config_params.min_bufsize[VPIF_CHANNEL3_VIDEO];
-
-       config_params.numbuffers[VPIF_CHANNEL2_VIDEO] = ch2_numbuffers;
-
-       if (ch2_numbuffers) {
-               config_params.channel_bufsize[VPIF_CHANNEL2_VIDEO] =
-                                                       ch2_bufsize;
-       }
-       config_params.numbuffers[VPIF_CHANNEL3_VIDEO] = ch3_numbuffers;
-
-       if (ch3_numbuffers) {
-               config_params.channel_bufsize[VPIF_CHANNEL3_VIDEO] =
-                                                       ch3_bufsize;
-       }
+       int err, i, j;
 
        /* Allocate memory for six channel objects */
        for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
@@ -1606,10 +1114,6 @@ static int initialize_vpif(void)
                }
        }
 
-       free_channel_objects_index = VPIF_DISPLAY_MAX_DEVICES;
-       free_buffer_channel_index = VPIF_DISPLAY_NUM_CHANNELS;
-       free_buffer_index = config_params.numbuffers[i - 1];
-
        return 0;
 
 vpif_init_free_channel_objects:
@@ -1638,21 +1142,18 @@ static int vpif_async_bound(struct v4l2_async_notifier *notifier,
 static int vpif_probe_complete(void)
 {
        struct common_obj *common;
+       struct video_device *vdev;
        struct channel_obj *ch;
+       struct vb2_queue *q;
        int j, err, k;
 
        for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
                ch = vpif_obj.dev[j];
                /* Initialize field of the channel objects */
-               atomic_set(&ch->usrs, 0);
                for (k = 0; k < VPIF_NUMOBJECTS; k++) {
-                       ch->common[k].numbuffers = 0;
                        common = &ch->common[k];
-                       common->io_usrs = 0;
-                       common->started = 0;
                        spin_lock_init(&common->irqlock);
                        mutex_init(&common->lock);
-                       common->numbuffers = 0;
                        common->set_addr = NULL;
                        common->ytop_off = 0;
                        common->ybtm_off = 0;
@@ -1661,38 +1162,71 @@ static int vpif_probe_complete(void)
                        common->cur_frm = NULL;
                        common->next_frm = NULL;
                        memset(&common->fmt, 0, sizeof(common->fmt));
-                       common->numbuffers = config_params.numbuffers[k];
                }
                ch->initialized = 0;
                if (vpif_obj.config->subdev_count)
                        ch->sd = vpif_obj.sd[0];
                ch->channel_id = j;
-               if (j < 2)
-                       ch->common[VPIF_VIDEO_INDEX].numbuffers =
-                           config_params.numbuffers[ch->channel_id];
-               else
-                       ch->common[VPIF_VIDEO_INDEX].numbuffers = 0;
 
                memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
 
-               /* Initialize prio member of channel object */
-               v4l2_prio_init(&ch->prio);
                ch->common[VPIF_VIDEO_INDEX].fmt.type =
                                                V4L2_BUF_TYPE_VIDEO_OUTPUT;
-               ch->video_dev->lock = &common->lock;
-               video_set_drvdata(ch->video_dev, ch);
 
                /* select output 0 */
                err = vpif_set_output(vpif_obj.config, ch, 0);
                if (err)
                        goto probe_out;
 
+               /* set initial format */
+               ch->video.stdid = V4L2_STD_525_60;
+               memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings));
+               vpif_update_resolution(ch);
+
+               /* Initialize vb2 queue */
+               q = &common->buffer_queue;
+               q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+               q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+               q->drv_priv = ch;
+               q->ops = &video_qops;
+               q->mem_ops = &vb2_dma_contig_memops;
+               q->buf_struct_size = sizeof(struct vpif_disp_buffer);
+               q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+               q->min_buffers_needed = 1;
+               q->lock = &common->lock;
+               err = vb2_queue_init(q);
+               if (err) {
+                       vpif_err("vpif_display: vb2_queue_init() failed\n");
+                       goto probe_out;
+               }
+
+               common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
+               if (IS_ERR(common->alloc_ctx)) {
+                       vpif_err("Failed to get the context\n");
+                       err = PTR_ERR(common->alloc_ctx);
+                       goto probe_out;
+               }
+
+               INIT_LIST_HEAD(&common->dma_queue);
+
                /* register video device */
                vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n",
                         (int)ch, (int)&ch->video_dev);
 
-               err = video_register_device(ch->video_dev,
-                                         VFL_TYPE_GRABBER, (j ? 3 : 2));
+               /* Initialize the video_device structure */
+               vdev = ch->video_dev;
+               strlcpy(vdev->name, VPIF_DRIVER_NAME, sizeof(vdev->name));
+               vdev->release = video_device_release;
+               vdev->fops = &vpif_fops;
+               vdev->ioctl_ops = &vpif_ioctl_ops;
+               vdev->v4l2_dev = &vpif_obj.v4l2_dev;
+               vdev->vfl_dir = VFL_DIR_TX;
+               vdev->queue = q;
+               vdev->lock = &common->lock;
+               set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags);
+               video_set_drvdata(ch->video_dev, ch);
+               err = video_register_device(vdev, VFL_TYPE_GRABBER,
+                                           (j ? 3 : 2));
                if (err < 0)
                        goto probe_out;
        }
@@ -1702,6 +1236,8 @@ static int vpif_probe_complete(void)
 probe_out:
        for (k = 0; k < j; k++) {
                ch = vpif_obj.dev[k];
+               common = &ch->common[k];
+               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
                video_unregister_device(ch->video_dev);
                video_device_release(ch->video_dev);
                ch->video_dev = NULL;
@@ -1728,7 +1264,6 @@ static __init int vpif_probe(struct platform_device *pdev)
        struct video_device *vfd;
        struct resource *res;
        int subdev_count;
-       size_t size;
 
        vpif_dev = &pdev->dev;
        err = initialize_vpif();
@@ -1746,7 +1281,7 @@ static __init int vpif_probe(struct platform_device *pdev)
 
        while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) {
                err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr,
-                                       IRQF_SHARED, "VPIF_Display",
+                                       IRQF_SHARED, VPIF_DRIVER_NAME,
                                        (void *)(&vpif_obj.dev[res_idx]->
                                        channel_id));
                if (err) {
@@ -1772,36 +1307,10 @@ static __init int vpif_probe(struct platform_device *pdev)
                        goto vpif_unregister;
                }
 
-               /* Initialize field of video device */
-               *vfd = vpif_video_template;
-               vfd->v4l2_dev = &vpif_obj.v4l2_dev;
-               vfd->release = video_device_release;
-               vfd->vfl_dir = VFL_DIR_TX;
-               snprintf(vfd->name, sizeof(vfd->name),
-                        "VPIF_Display_DRIVER_V%s",
-                        VPIF_DISPLAY_VERSION);
-
                /* Set video_dev to the video device */
                ch->video_dev = vfd;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res) {
-               size = resource_size(res);
-               /* The resources are divided into two equal memory and when
-                * we have HD output we can add them together
-                */
-               for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
-                       ch = vpif_obj.dev[j];
-                       ch->channel_id = j;
-
-                       /* only enabled if second resource exists */
-                       config_params.video_limit[ch->channel_id] = 0;
-                       if (size)
-                               config_params.video_limit[ch->channel_id] =
-                                                                       size/2;
-               }
-       }
        vpif_obj.config = pdev->dev.platform_data;
        subdev_count = vpif_obj.config->subdev_count;
        subdevdata = vpif_obj.config->subdevinfo;
@@ -1867,6 +1376,7 @@ vpif_unregister:
  */
 static int vpif_remove(struct platform_device *device)
 {
+       struct common_obj *common;
        struct channel_obj *ch;
        int i;
 
@@ -1877,6 +1387,8 @@ static int vpif_remove(struct platform_device *device)
        for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
                /* Get the pointer to the channel object */
                ch = vpif_obj.dev[i];
+               common = &ch->common[i];
+               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
                /* Unregister video device */
                video_unregister_device(ch->video_dev);
 
@@ -1887,7 +1399,7 @@ static int vpif_remove(struct platform_device *device)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int vpif_suspend(struct device *dev)
 {
        struct common_obj *common;
@@ -1898,18 +1410,20 @@ static int vpif_suspend(struct device *dev)
                /* Get the pointer to the channel object */
                ch = vpif_obj.dev[i];
                common = &ch->common[VPIF_VIDEO_INDEX];
+
+               if (!vb2_is_streaming(&common->buffer_queue))
+                       continue;
+
                mutex_lock(&common->lock);
-               if (atomic_read(&ch->usrs) && common->io_usrs) {
-                       /* Disable channel */
-                       if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
-                               enable_channel2(0);
-                               channel2_intr_enable(0);
-                       }
-                       if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
-                                       common->started == 2) {
-                               enable_channel3(0);
-                               channel3_intr_enable(0);
-                       }
+               /* Disable channel */
+               if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
+                       enable_channel2(0);
+                       channel2_intr_enable(0);
+               }
+               if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
+                       ycmux_mode == 2) {
+                       enable_channel3(0);
+                       channel3_intr_enable(0);
                }
                mutex_unlock(&common->lock);
        }
@@ -1928,18 +1442,20 @@ static int vpif_resume(struct device *dev)
                /* Get the pointer to the channel object */
                ch = vpif_obj.dev[i];
                common = &ch->common[VPIF_VIDEO_INDEX];
+
+               if (!vb2_is_streaming(&common->buffer_queue))
+                       continue;
+
                mutex_lock(&common->lock);
-               if (atomic_read(&ch->usrs) && common->io_usrs) {
-                       /* Enable channel */
-                       if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
-                               enable_channel2(1);
-                               channel2_intr_enable(1);
-                       }
-                       if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
-                                       common->started == 2) {
-                               enable_channel3(1);
-                               channel3_intr_enable(1);
-                       }
+               /* Enable channel */
+               if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
+                       enable_channel2(1);
+                       channel2_intr_enable(1);
+               }
+               if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
+                               ycmux_mode == 2) {
+                       enable_channel3(1);
+                       channel3_intr_enable(1);
                }
                mutex_unlock(&common->lock);
        }
@@ -1947,21 +1463,15 @@ static int vpif_resume(struct device *dev)
        return 0;
 }
 
-static const struct dev_pm_ops vpif_pm = {
-       .suspend        = vpif_suspend,
-       .resume         = vpif_resume,
-};
-
-#define vpif_pm_ops (&vpif_pm)
-#else
-#define vpif_pm_ops NULL
 #endif
 
+static SIMPLE_DEV_PM_OPS(vpif_pm_ops, vpif_suspend, vpif_resume);
+
 static __refdata struct platform_driver vpif_driver = {
        .driver = {
-                       .name   = "vpif_display",
+                       .name   = VPIF_DRIVER_NAME,
                        .owner  = THIS_MODULE,
-                       .pm     = vpif_pm_ops,
+                       .pm     = &vpif_pm_ops,
        },
        .probe  = vpif_probe,
        .remove = vpif_remove,
index 4d0485b..7b21a76 100644 (file)
@@ -13,8 +13,8 @@
  * GNU General Public License for more details.
  */
 
-#ifndef DAVINCIHD_DISPLAY_H
-#define DAVINCIHD_DISPLAY_H
+#ifndef VPIF_DISPLAY_H
+#define VPIF_DISPLAY_H
 
 /* Header files */
 #include <media/videobuf2-dma-contig.h>
@@ -67,17 +67,10 @@ struct vpif_disp_buffer {
 };
 
 struct common_obj {
-       /* Buffer specific parameters */
-       u8 *fbuffers[VIDEO_MAX_FRAME];          /* List of buffer pointers for
-                                                * storing frames */
-       u32 numbuffers;                         /* number of buffers */
        struct vpif_disp_buffer *cur_frm;       /* Pointer pointing to current
                                                 * vb2_buffer */
        struct vpif_disp_buffer *next_frm;      /* Pointer pointing to next
                                                 * vb2_buffer */
-       enum v4l2_memory memory;                /* This field keeps track of
-                                                * type of buffer exchange
-                                                * method user has selected */
        struct v4l2_format fmt;                 /* Used to store the format */
        struct vb2_queue buffer_queue;          /* Buffer queue used in
                                                 * video-buf */
@@ -90,10 +83,6 @@ struct common_obj {
        /* channel specific parameters */
        struct mutex lock;                      /* lock used to access this
                                                 * structure */
-       u32 io_usrs;                            /* number of users performing
-                                                * IO */
-       u8 started;                             /* Indicates whether streaming
-                                                * started */
        u32 ytop_off;                           /* offset of Y top from the
                                                 * starting of the buffer */
        u32 ybtm_off;                           /* offset of Y bottom from the
@@ -103,7 +92,7 @@ struct common_obj {
        u32 cbtm_off;                           /* offset of C bottom from the
                                                 * starting of the buffer */
        /* Function pointer to set the addresses */
-       void (*set_addr) (unsigned long, unsigned long,
+       void (*set_addr)(unsigned long, unsigned long,
                                unsigned long, unsigned long);
        u32 height;
        u32 width;
@@ -113,10 +102,6 @@ struct channel_obj {
        /* V4l2 specific parameters */
        struct video_device *video_dev; /* Identifies video device for
                                         * this channel */
-       struct v4l2_prio_state prio;    /* Used to keep track of state of
-                                        * the priority */
-       atomic_t usrs;                  /* number of open instances of
-                                        * the channel */
        u32 field_id;                   /* Indicates id of the field
                                         * which is being displayed */
        u8 initialized;                 /* flag to indicate whether
@@ -130,19 +115,6 @@ struct channel_obj {
        struct video_obj video;
 };
 
-/* File handle structure */
-struct vpif_fh {
-       struct channel_obj *channel;    /* pointer to channel object for
-                                        * opened device */
-       u8 io_allowed[VPIF_NUMOBJECTS]; /* Indicates whether this file handle
-                                        * is doing IO */
-       enum v4l2_priority prio;        /* Used to keep track priority of
-                                        * this instance */
-       u8 initialized;                 /* Used to keep track of whether this
-                                        * file handle has initialized
-                                        * channel or not */
-};
-
 /* vpif device structure */
 struct vpif_device {
        struct v4l2_device v4l2_dev;
@@ -152,12 +124,4 @@ struct vpif_device {
        struct vpif_display_config *config;
 };
 
-struct vpif_config_params {
-       u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
-       u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
-       u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS];
-       u32 video_limit[VPIF_DISPLAY_NUM_CHANNELS];
-       u8 min_numbuffers;
-};
-
-#endif                         /* DAVINCIHD_DISPLAY_H */
+#endif                         /* VPIF_DISPLAY_H */
index d0ea94f..e434f1f 100644 (file)
@@ -66,15 +66,13 @@ static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
        return ret > 0 ? 0 : ret;
 }
 
-static int gsc_m2m_stop_streaming(struct vb2_queue *q)
+static void gsc_m2m_stop_streaming(struct vb2_queue *q)
 {
        struct gsc_ctx *ctx = q->drv_priv;
 
        __gsc_m2m_job_abort(ctx);
 
        pm_runtime_put(&ctx->gsc_dev->pdev->dev);
-
-       return 0;
 }
 
 void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)
index e1b2ceb..5dcaa0a 100644 (file)
@@ -3,6 +3,7 @@ config VIDEO_SAMSUNG_EXYNOS4_IS
        bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver"
        depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
        depends on (PLAT_S5P || ARCH_EXYNOS)
+       depends on OF && COMMON_CLK
        help
          Say Y here to enable camera host interface devices for
          Samsung S5P and EXYNOS SoC series.
@@ -17,7 +18,7 @@ config VIDEO_S5P_FIMC
        depends on I2C
        select VIDEOBUF2_DMA_CONTIG
        select V4L2_MEM2MEM_DEV
-       select MFD_SYSCON if OF
+       select MFD_SYSCON
        select VIDEO_EXYNOS4_IS_COMMON
        help
          This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host
index 0ec210b..0eb34ec 100644 (file)
@@ -10,7 +10,7 @@
  */
 
 #include <linux/module.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
 #include "common.h"
 
 /* Called with the media graph mutex held or entity->stream_count > 0. */
index 92ae812..3d2babd 100644 (file)
@@ -294,15 +294,15 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
        return 0;
 }
 
-static int stop_streaming(struct vb2_queue *q)
+static void stop_streaming(struct vb2_queue *q)
 {
        struct fimc_ctx *ctx = q->drv_priv;
        struct fimc_dev *fimc = ctx->fimc_dev;
 
        if (!fimc_capture_active(fimc))
-               return -EINVAL;
+               return;
 
-       return fimc_stop_capture(fimc, false);
+       fimc_stop_capture(fimc, false);
 }
 
 int fimc_capture_suspend(struct fimc_dev *fimc)
index 25dbf5b..b70fd99 100644 (file)
@@ -56,8 +56,8 @@ static struct fimc_fmt fimc_formats[] = {
                .colplanes      = 1,
                .flags          = FMT_FLAGS_M2M,
        }, {
-               .name           = "ARGB8888, 32 bpp",
-               .fourcc         = V4L2_PIX_FMT_RGB32,
+               .name           = "BGRA8888, 32 bpp",
+               .fourcc         = V4L2_PIX_FMT_BGR32,
                .depth          = { 32 },
                .color          = FIMC_FMT_RGB888,
                .memplanes      = 1,
@@ -450,7 +450,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
        bool pix_hoff = ctx->fimc_dev->drv_data->dma_pix_hoff;
        u32 i, depth = 0;
 
-       for (i = 0; i < f->fmt->colplanes; i++)
+       for (i = 0; i < f->fmt->memplanes; i++)
                depth += f->fmt->depth[i];
 
        f->dma_offset.y_h = f->offs_h;
index 1790fb4..6c75c6c 100644 (file)
@@ -27,7 +27,7 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-mem2mem.h>
 #include <media/v4l2-mediabus.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
 
 #define dbg(fmt, args...) \
        pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args)
index 128b73b..5476dce 100644 (file)
@@ -367,6 +367,9 @@ static void fimc_is_free_cpu_memory(struct fimc_is *is)
 {
        struct device *dev = &is->pdev->dev;
 
+       if (is->memory.vaddr == NULL)
+               return;
+
        dma_free_coherent(dev, is->memory.size, is->memory.vaddr,
                          is->memory.paddr);
 }
index e92b4e1..93f9cf2 100644 (file)
@@ -30,7 +30,7 @@
 #include <media/v4l2-ioctl.h>
 #include <media/videobuf2-core.h>
 #include <media/videobuf2-dma-contig.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
 
 #include "common.h"
 #include "media-dev.h"
@@ -125,7 +125,7 @@ static int isp_video_capture_start_streaming(struct vb2_queue *q,
        return ret;
 }
 
-static int isp_video_capture_stop_streaming(struct vb2_queue *q)
+static void isp_video_capture_stop_streaming(struct vb2_queue *q)
 {
        struct fimc_isp *isp = vb2_get_drv_priv(q);
        struct fimc_is *is = fimc_isp_to_is(isp);
@@ -134,7 +134,7 @@ static int isp_video_capture_stop_streaming(struct vb2_queue *q)
 
        ret = fimc_pipeline_call(&isp->video_capture.ve, set_stream, 0);
        if (ret < 0)
-               return ret;
+               return;
 
        dma->cmd = DMA_OUTPUT_COMMAND_DISABLE;
        dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE;
@@ -155,7 +155,6 @@ static int isp_video_capture_stop_streaming(struct vb2_queue *q)
        clear_bit(ST_ISP_VID_CAP_STREAMING, &isp->state);
 
        isp->video_capture.buf_count = 0;
-       return 0;
 }
 
 static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb)
index 4dc55a1..b99be09 100644 (file)
@@ -24,7 +24,7 @@
 #include <media/videobuf2-core.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-mediabus.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
 
 extern int fimc_isp_debug;
 
index d0dc7ee..bc3ec7d 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/io.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
 
 #include "fimc-lite-reg.h"
 #include "fimc-lite.h"
index 3ad660b..a97d235 100644 (file)
@@ -30,7 +30,7 @@
 #include <media/v4l2-mem2mem.h>
 #include <media/videobuf2-core.h>
 #include <media/videobuf2-dma-contig.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
 
 #include "common.h"
 #include "fimc-core.h"
@@ -350,14 +350,14 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
        return 0;
 }
 
-static int stop_streaming(struct vb2_queue *q)
+static void stop_streaming(struct vb2_queue *q)
 {
        struct fimc_lite *fimc = q->drv_priv;
 
        if (!fimc_lite_active(fimc))
-               return -EINVAL;
+               return;
 
-       return fimc_lite_stop_capture(fimc, false);
+       fimc_lite_stop_capture(fimc, false);
 }
 
 static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
index 7428b2d..ea19dc7 100644 (file)
@@ -23,7 +23,7 @@
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-mediabus.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
 
 #define FIMC_LITE_DRV_NAME     "exynos-fimc-lite"
 #define FLITE_CLK_NAME         "flite"
index 36971d9..0ad1b6f 100644 (file)
@@ -85,7 +85,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
        return ret > 0 ? 0 : ret;
 }
 
-static int stop_streaming(struct vb2_queue *q)
+static void stop_streaming(struct vb2_queue *q)
 {
        struct fimc_ctx *ctx = q->drv_priv;
        int ret;
@@ -95,7 +95,6 @@ static int stop_streaming(struct vb2_queue *q)
                fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
 
        pm_runtime_put(&ctx->fimc_dev->pdev->dev);
-       return 0;
 }
 
 static void fimc_device_run(void *priv)
@@ -197,7 +196,7 @@ static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
 
        *num_planes = f->fmt->memplanes;
        for (i = 0; i < f->fmt->memplanes; i++) {
-               sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8;
+               sizes[i] = f->payload[i];
                allocators[i] = ctx->fimc_dev->alloc_ctx;
        }
        return 0;
@@ -342,7 +341,7 @@ static void __set_frame_format(struct fimc_frame *frame, struct fimc_fmt *fmt,
 {
        int i;
 
-       for (i = 0; i < fmt->colplanes; i++) {
+       for (i = 0; i < fmt->memplanes; i++) {
                frame->bytesperline[i] = pixm->plane_fmt[i].bytesperline;
                frame->payload[i] = pixm->plane_fmt[i].sizeimage;
        }
@@ -461,7 +460,7 @@ static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
        else
                halign = ffs(fimc->variant->min_vsize_align) - 1;
 
-       for (i = 0; i < f->fmt->colplanes; i++)
+       for (i = 0; i < f->fmt->memplanes; i++)
                depth += f->fmt->depth[i];
 
        v4l_bound_align_image(&cr->c.width, min_size, f->o_width,
index 1db8cb4..2d77fd8 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/io.h>
 #include <linux/regmap.h>
 
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
 #include "media-dev.h"
 
 #include "fimc-reg.h"
index e62211a..344718d 100644 (file)
@@ -31,7 +31,7 @@
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-of.h>
 #include <media/media-device.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
 
 #include "media-dev.h"
 #include "fimc-core.h"
 #include "fimc-lite.h"
 #include "mipi-csis.h"
 
-static int __fimc_md_set_camclk(struct fimc_md *fmd,
-                               struct fimc_source_info *si,
-                               bool on);
-
 /* Set up image sensor subdev -> FIMC capture node notifications. */
 static void __setup_sensor_notification(struct fimc_md *fmd,
                                        struct v4l2_subdev *sensor,
@@ -223,17 +219,10 @@ static int __fimc_pipeline_open(struct exynos_media_pipeline *ep,
                        return ret;
        }
 
-       ret = fimc_md_set_camclk(sd, true);
-       if (ret < 0)
-               goto err_wbclk;
-
        ret = fimc_pipeline_s_power(p, 1);
        if (!ret)
                return 0;
 
-       fimc_md_set_camclk(sd, false);
-
-err_wbclk:
        if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP])
                clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]);
 
@@ -259,7 +248,6 @@ static int __fimc_pipeline_close(struct exynos_media_pipeline *ep)
        }
 
        ret = fimc_pipeline_s_power(p, 0);
-       fimc_md_set_camclk(sd, false);
 
        fmd = entity_to_fimc_mdev(&sd->entity);
 
@@ -337,75 +325,14 @@ static void fimc_md_pipelines_free(struct fimc_md *fmd)
        }
 }
 
-/*
- * Sensor subdevice helper functions
- */
-static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
-                                               struct fimc_source_info *si)
-{
-       struct i2c_adapter *adapter;
-       struct v4l2_subdev *sd = NULL;
-
-       if (!si || !fmd)
-               return NULL;
-       /*
-        * If FIMC bus type is not Writeback FIFO assume it is same
-        * as sensor_bus_type.
-        */
-       si->fimc_bus_type = si->sensor_bus_type;
-
-       adapter = i2c_get_adapter(si->i2c_bus_num);
-       if (!adapter) {
-               v4l2_warn(&fmd->v4l2_dev,
-                         "Failed to get I2C adapter %d, deferring probe\n",
-                         si->i2c_bus_num);
-               return ERR_PTR(-EPROBE_DEFER);
-       }
-       sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter,
-                                               si->board_info, NULL);
-       if (IS_ERR_OR_NULL(sd)) {
-               i2c_put_adapter(adapter);
-               v4l2_warn(&fmd->v4l2_dev,
-                         "Failed to acquire subdev %s, deferring probe\n",
-                         si->board_info->type);
-               return ERR_PTR(-EPROBE_DEFER);
-       }
-       v4l2_set_subdev_hostdata(sd, si);
-       sd->grp_id = GRP_ID_SENSOR;
-
-       v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n",
-                 sd->name);
-       return sd;
-}
-
-static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
-{
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-       struct i2c_adapter *adapter;
-
-       if (!client || client->dev.of_node)
-               return;
-
-       v4l2_device_unregister_subdev(sd);
-
-       adapter = client->adapter;
-       i2c_unregister_device(client);
-       if (adapter)
-               i2c_put_adapter(adapter);
-}
-
-#ifdef CONFIG_OF
 /* Parse port node and register as a sub-device any sensor specified there. */
 static int fimc_md_parse_port_node(struct fimc_md *fmd,
                                   struct device_node *port,
                                   unsigned int index)
 {
+       struct fimc_source_info *pd = &fmd->sensor[index].pdata;
        struct device_node *rem, *ep, *np;
-       struct fimc_source_info *pd;
        struct v4l2_of_endpoint endpoint;
-       u32 val;
-
-       pd = &fmd->sensor[index].pdata;
 
        /* Assume here a port node can have only one endpoint node. */
        ep = of_get_next_child(port, NULL);
@@ -425,20 +352,6 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
                                                        ep->full_name);
                return 0;
        }
-       if (!of_property_read_u32(rem, "samsung,camclk-out", &val))
-               pd->clk_id = val;
-
-       if (!of_property_read_u32(rem, "clock-frequency", &val))
-               pd->clk_frequency = val;
-       else
-               pd->clk_frequency = DEFAULT_SENSOR_CLK_FREQ;
-
-       if (pd->clk_frequency == 0) {
-               v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n",
-                        rem->full_name);
-               of_node_put(rem);
-               return -EINVAL;
-       }
 
        if (fimc_input_is_parallel(endpoint.base.port)) {
                if (endpoint.bus_type == V4L2_MBUS_PARALLEL)
@@ -485,14 +398,26 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
 }
 
 /* Register all SoC external sub-devices */
-static int fimc_md_of_sensors_register(struct fimc_md *fmd,
-                                      struct device_node *np)
+static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
 {
        struct device_node *parent = fmd->pdev->dev.of_node;
        struct device_node *node, *ports;
        int index = 0;
        int ret;
 
+       /*
+        * Runtime resume one of the FIMC entities to make sure
+        * the sclk_cam clocks are not globally disabled.
+        */
+       if (!fmd->pmf)
+               return -ENXIO;
+
+       ret = pm_runtime_get_sync(fmd->pmf);
+       if (ret < 0)
+               return ret;
+
+       fmd->num_sensors = 0;
+
        /* Attach sensors linked to MIPI CSI-2 receivers */
        for_each_available_child_of_node(parent, node) {
                struct device_node *port;
@@ -506,14 +431,14 @@ static int fimc_md_of_sensors_register(struct fimc_md *fmd,
 
                ret = fimc_md_parse_port_node(fmd, port, index);
                if (ret < 0)
-                       return ret;
+                       goto rpm_put;
                index++;
        }
 
        /* Attach sensors listed in the parallel-ports node */
        ports = of_get_child_by_name(parent, "parallel-ports");
        if (!ports)
-               return 0;
+               goto rpm_put;
 
        for_each_child_of_node(ports, node) {
                ret = fimc_md_parse_port_node(fmd, node, index);
@@ -521,8 +446,9 @@ static int fimc_md_of_sensors_register(struct fimc_md *fmd,
                        break;
                index++;
        }
-
-       return 0;
+rpm_put:
+       pm_runtime_put(fmd->pmf);
+       return ret;
 }
 
 static int __of_get_csis_id(struct device_node *np)
@@ -535,68 +461,10 @@ static int __of_get_csis_id(struct device_node *np)
        of_property_read_u32(np, "reg", &reg);
        return reg - FIMC_INPUT_MIPI_CSI2_0;
 }
-#else
-#define fimc_md_of_sensors_register(fmd, np) (-ENOSYS)
-#define __of_get_csis_id(np) (-ENOSYS)
-#endif
-
-static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
-{
-       struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
-       struct device_node *of_node = fmd->pdev->dev.of_node;
-       int num_clients = 0;
-       int ret, i;
-
-       /*
-        * Runtime resume one of the FIMC entities to make sure
-        * the sclk_cam clocks are not globally disabled.
-        */
-       if (!fmd->pmf)
-               return -ENXIO;
-
-       ret = pm_runtime_get_sync(fmd->pmf);
-       if (ret < 0)
-               return ret;
-
-       if (of_node) {
-               fmd->num_sensors = 0;
-               ret = fimc_md_of_sensors_register(fmd, of_node);
-       } else if (pdata) {
-               WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor));
-               num_clients = min_t(u32, pdata->num_clients,
-                                   ARRAY_SIZE(fmd->sensor));
-               fmd->num_sensors = num_clients;
-
-               for (i = 0; i < num_clients; i++) {
-                       struct fimc_sensor_info *si = &fmd->sensor[i];
-                       struct v4l2_subdev *sd;
-
-                       si->pdata = pdata->source_info[i];
-                       ret = __fimc_md_set_camclk(fmd, &si->pdata, true);
-                       if (ret)
-                               break;
-                       sd = fimc_md_register_sensor(fmd, &si->pdata);
-                       ret = __fimc_md_set_camclk(fmd, &si->pdata, false);
-
-                       if (IS_ERR(sd)) {
-                               si->subdev = NULL;
-                               ret = PTR_ERR(sd);
-                               break;
-                       }
-                       si->subdev = sd;
-                       if (ret)
-                               break;
-               }
-       }
-
-       pm_runtime_put(fmd->pmf);
-       return ret;
-}
 
 /*
  * MIPI-CSIS, FIMC and FIMC-LITE platform devices registration.
  */
-
 static int register_fimc_lite_entity(struct fimc_md *fmd,
                                     struct fimc_lite *fimc_lite)
 {
@@ -753,35 +621,9 @@ dev_unlock:
        return ret;
 }
 
-static int fimc_md_pdev_match(struct device *dev, void *data)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       int plat_entity = -1;
-       int ret;
-       char *p;
-
-       if (!get_device(dev))
-               return -ENODEV;
-
-       if (!strcmp(pdev->name, CSIS_DRIVER_NAME)) {
-               plat_entity = IDX_CSIS;
-       } else {
-               p = strstr(pdev->name, "fimc");
-               if (p && *(p + 4) == 0)
-                       plat_entity = IDX_FIMC;
-       }
-
-       if (plat_entity >= 0)
-               ret = fimc_md_register_platform_entity(data, pdev,
-                                                      plat_entity);
-       put_device(dev);
-       return 0;
-}
-
 /* Register FIMC, FIMC-LITE and CSIS media entities */
-#ifdef CONFIG_OF
-static int fimc_md_register_of_platform_entities(struct fimc_md *fmd,
-                                                struct device_node *parent)
+static int fimc_md_register_platform_entities(struct fimc_md *fmd,
+                                             struct device_node *parent)
 {
        struct device_node *node;
        int ret = 0;
@@ -815,9 +657,6 @@ static int fimc_md_register_of_platform_entities(struct fimc_md *fmd,
 
        return ret;
 }
-#else
-#define fimc_md_register_of_platform_entities(fmd, node) (-ENOSYS)
-#endif
 
 static void fimc_md_unregister_entities(struct fimc_md *fmd)
 {
@@ -845,14 +684,6 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)
                v4l2_device_unregister_subdev(fmd->csis[i].sd);
                fmd->csis[i].sd = NULL;
        }
-       if (fmd->pdev->dev.of_node == NULL) {
-               for (i = 0; i < fmd->num_sensors; i++) {
-                       if (fmd->sensor[i].subdev == NULL)
-                               continue;
-                       fimc_md_unregister_sensor(fmd->sensor[i].subdev);
-                       fmd->sensor[i].subdev = NULL;
-               }
-       }
 
        if (fmd->fimc_is)
                v4l2_device_unregister_subdev(&fmd->fimc_is->isp.subdev);
@@ -1137,7 +968,7 @@ static void fimc_md_put_clocks(struct fimc_md *fmd)
 
 static int fimc_md_get_clocks(struct fimc_md *fmd)
 {
-       struct device *dev = NULL;
+       struct device *dev = &fmd->pdev->dev;
        char clk_name[32];
        struct clk *clock;
        int i, ret = 0;
@@ -1145,16 +976,12 @@ static int fimc_md_get_clocks(struct fimc_md *fmd)
        for (i = 0; i < FIMC_MAX_CAMCLKS; i++)
                fmd->camclk[i].clock = ERR_PTR(-EINVAL);
 
-       if (fmd->pdev->dev.of_node)
-               dev = &fmd->pdev->dev;
-
        for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
                snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i);
                clock = clk_get(dev, clk_name);
 
                if (IS_ERR(clock)) {
-                       dev_err(&fmd->pdev->dev, "Failed to get clock: %s\n",
-                                                               clk_name);
+                       dev_err(dev, "Failed to get clock: %s\n", clk_name);
                        ret = PTR_ERR(clock);
                        break;
                }
@@ -1188,86 +1015,6 @@ static int fimc_md_get_clocks(struct fimc_md *fmd)
        return ret;
 }
 
-static int __fimc_md_set_camclk(struct fimc_md *fmd,
-                               struct fimc_source_info *si,
-                               bool on)
-{
-       struct fimc_camclk_info *camclk;
-       int ret = 0;
-
-       /*
-        * When device tree is used the sensor drivers are supposed to
-        * control the clock themselves. This whole function will be
-        * removed once S5PV210 platform is converted to the device tree.
-        */
-       if (fmd->pdev->dev.of_node)
-               return 0;
-
-       if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf)
-               return -EINVAL;
-
-       camclk = &fmd->camclk[si->clk_id];
-
-       dbg("camclk %d, f: %lu, use_count: %d, on: %d",
-           si->clk_id, si->clk_frequency, camclk->use_count, on);
-
-       if (on) {
-               if (camclk->use_count > 0 &&
-                   camclk->frequency != si->clk_frequency)
-                       return -EINVAL;
-
-               if (camclk->use_count++ == 0) {
-                       clk_set_rate(camclk->clock, si->clk_frequency);
-                       camclk->frequency = si->clk_frequency;
-                       ret = pm_runtime_get_sync(fmd->pmf);
-                       if (ret < 0)
-                               return ret;
-                       ret = clk_prepare_enable(camclk->clock);
-                       dbg("Enabled camclk %d: f: %lu", si->clk_id,
-                           clk_get_rate(camclk->clock));
-               }
-               return ret;
-       }
-
-       if (WARN_ON(camclk->use_count == 0))
-               return 0;
-
-       if (--camclk->use_count == 0) {
-               clk_disable_unprepare(camclk->clock);
-               pm_runtime_put(fmd->pmf);
-               dbg("Disabled camclk %d", si->clk_id);
-       }
-       return ret;
-}
-
-/**
- * fimc_md_set_camclk - peripheral sensor clock setup
- * @sd: sensor subdev to configure sclk_cam clock for
- * @on: 1 to enable or 0 to disable the clock
- *
- * There are 2 separate clock outputs available in the SoC for external
- * image processors. These clocks are shared between all registered FIMC
- * devices to which sensors can be attached, either directly or through
- * the MIPI CSI receiver. The clock is allowed here to be used by
- * multiple sensors concurrently if they use same frequency.
- * This function should only be called when the graph mutex is held.
- */
-int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
-{
-       struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd);
-       struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity);
-
-       /*
-        * If there is a clock provider registered the sensors will
-        * handle their clock themselves, no need to control it on
-        * the host interface side.
-        */
-       if (fmd->clk_provider.num_clocks > 0)
-               return 0;
-
-       return __fimc_md_set_camclk(fmd, si, on);
-}
-
 static int __fimc_md_modify_pipeline(struct media_entity *entity, bool enable)
 {
        struct exynos_video_entity *ve;
@@ -1426,7 +1173,6 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd)
        return 0;
 }
 
-#ifdef CONFIG_OF
 static int cam_clk_prepare(struct clk_hw *hw)
 {
        struct cam_clk *camclk = to_cam_clk(hw);
@@ -1518,10 +1264,6 @@ err:
        fimc_md_unregister_clk_provider(fmd);
        return ret;
 }
-#else
-#define fimc_md_register_clk_provider(fmd) (0)
-#define fimc_md_unregister_clk_provider(fmd) (0)
-#endif
 
 static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
                                 struct v4l2_subdev *subdev,
@@ -1585,8 +1327,8 @@ static int fimc_md_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        spin_lock_init(&fmd->slock);
-       fmd->pdev = pdev;
        INIT_LIST_HEAD(&fmd->pipelines);
+       fmd->pdev = pdev;
 
        strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",
                sizeof(fmd->media_dev.model));
@@ -1599,6 +1341,7 @@ static int fimc_md_probe(struct platform_device *pdev)
        strlcpy(v4l2_dev->name, "s5p-fimc-md", sizeof(v4l2_dev->name));
 
        fmd->use_isp = fimc_md_is_isp_available(dev->of_node);
+       fmd->user_subdev_api = true;
 
        ret = v4l2_device_register(dev, &fmd->v4l2_dev);
        if (ret < 0) {
@@ -1616,8 +1359,6 @@ static int fimc_md_probe(struct platform_device *pdev)
        if (ret)
                goto err_md;
 
-       fmd->user_subdev_api = (dev->of_node != NULL);
-
        ret = fimc_md_get_pinctrl(fmd);
        if (ret < 0) {
                if (ret != EPROBE_DEFER)
@@ -1630,22 +1371,16 @@ static int fimc_md_probe(struct platform_device *pdev)
        /* Protect the media graph while we're registering entities */
        mutex_lock(&fmd->media_dev.graph_mutex);
 
-       if (dev->of_node)
-               ret = fimc_md_register_of_platform_entities(fmd, dev->of_node);
-       else
-               ret = bus_for_each_dev(&platform_bus_type, NULL, fmd,
-                                               fimc_md_pdev_match);
+       ret = fimc_md_register_platform_entities(fmd, dev->of_node);
        if (ret) {
                mutex_unlock(&fmd->media_dev.graph_mutex);
                goto err_clk;
        }
 
-       if (dev->platform_data || dev->of_node) {
-               ret = fimc_md_register_sensor_entities(fmd);
-               if (ret) {
-                       mutex_unlock(&fmd->media_dev.graph_mutex);
-                       goto err_m_ent;
-               }
+       ret = fimc_md_register_sensor_entities(fmd);
+       if (ret) {
+               mutex_unlock(&fmd->media_dev.graph_mutex);
+               goto err_m_ent;
        }
 
        mutex_unlock(&fmd->media_dev.graph_mutex);
index ee1e251..0321454 100644 (file)
@@ -19,7 +19,7 @@
 #include <media/media-entity.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-subdev.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
 
 #include "fimc-core.h"
 #include "fimc-lite.h"
index 3678ba5..ae54ef5 100644 (file)
 #include <linux/of.h>
 #include <linux/of_graph.h>
 #include <linux/phy/phy.h>
-#include <linux/platform_data/mipi-csis.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/videodev2.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
 #include <media/v4l2-of.h>
 #include <media/v4l2-subdev.h>
 
@@ -730,26 +729,6 @@ static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static int s5pcsis_get_platform_data(struct platform_device *pdev,
-                                    struct csis_state *state)
-{
-       struct s5p_platform_mipi_csis *pdata = pdev->dev.platform_data;
-
-       if (pdata == NULL) {
-               dev_err(&pdev->dev, "Platform data not specified\n");
-               return -EINVAL;
-       }
-
-       state->clk_frequency = pdata->clk_rate;
-       state->num_lanes = pdata->lanes;
-       state->hs_settle = pdata->hs_settle;
-       state->index = max(0, pdev->id);
-       state->max_num_lanes = state->index ? CSIS1_MAX_LANES :
-                                             CSIS0_MAX_LANES;
-       return 0;
-}
-
-#ifdef CONFIG_OF
 static int s5pcsis_parse_dt(struct platform_device *pdev,
                            struct csis_state *state)
 {
@@ -787,9 +766,6 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
 
        return 0;
 }
-#else
-#define s5pcsis_parse_dt(pdev, state) (-ENOSYS)
-#endif
 
 static int s5pcsis_pm_resume(struct device *dev, bool runtime);
 static const struct of_device_id s5pcsis_of_match[];
@@ -812,19 +788,14 @@ static int s5pcsis_probe(struct platform_device *pdev)
        spin_lock_init(&state->slock);
        state->pdev = pdev;
 
-       if (dev->of_node) {
-               of_id = of_match_node(s5pcsis_of_match, dev->of_node);
-               if (WARN_ON(of_id == NULL))
-                       return -EINVAL;
-
-               drv_data = of_id->data;
-               state->interrupt_mask = drv_data->interrupt_mask;
+       of_id = of_match_node(s5pcsis_of_match, dev->of_node);
+       if (WARN_ON(of_id == NULL))
+               return -EINVAL;
 
-               ret = s5pcsis_parse_dt(pdev, state);
-       } else {
-               ret = s5pcsis_get_platform_data(pdev, state);
-       }
+       drv_data = of_id->data;
+       state->interrupt_mask = drv_data->interrupt_mask;
 
+       ret = s5pcsis_parse_dt(pdev, state);
        if (ret < 0)
                return ret;
 
index dbf0ce3..d5dc198 100644 (file)
@@ -964,7 +964,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id)
        struct viu_fh *fh = priv;
 
        fh->dev->std = id;
-       decoder_call(fh->dev, core, s_std, id);
+       decoder_call(fh->dev, video, s_std, id);
        return 0;
 }
 
index 8b34c48..be4b512 100644 (file)
@@ -1156,7 +1156,7 @@ static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count)
        return mcam_read_setup(cam);
 }
 
-static int mcam_vb_stop_streaming(struct vb2_queue *vq)
+static void mcam_vb_stop_streaming(struct vb2_queue *vq)
 {
        struct mcam_camera *cam = vb2_get_drv_priv(vq);
        unsigned long flags;
@@ -1164,10 +1164,10 @@ static int mcam_vb_stop_streaming(struct vb2_queue *vq)
        if (cam->state == S_BUFWAIT) {
                /* They never gave us buffers */
                cam->state = S_IDLE;
-               return 0;
+               return;
        }
        if (cam->state != S_STREAMING)
-               return -EINVAL;
+               return;
        mcam_ctlr_stop_dma(cam);
        /*
         * Reset the CCIC PHY after stopping streaming,
@@ -1182,7 +1182,6 @@ static int mcam_vb_stop_streaming(struct vb2_queue *vq)
        spin_lock_irqsave(&cam->dev_lock, flags);
        INIT_LIST_HEAD(&cam->buffers);
        spin_unlock_irqrestore(&cam->dev_lock, flags);
-       return 0;
 }
 
 
index 4f3096b..0714070 100644 (file)
@@ -787,7 +787,7 @@ static int m2mtest_start_streaming(struct vb2_queue *q, unsigned count)
        return 0;
 }
 
-static int m2mtest_stop_streaming(struct vb2_queue *q)
+static void m2mtest_stop_streaming(struct vb2_queue *q)
 {
        struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
        struct vb2_buffer *vb;
@@ -799,12 +799,11 @@ static int m2mtest_stop_streaming(struct vb2_queue *q)
                else
                        vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
                if (vb == NULL)
-                       return 0;
+                       return;
                spin_lock_irqsave(&ctx->dev->irqlock, flags);
                v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
                spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
        }
-       return 0;
 }
 
 static struct vb2_ops m2mtest_qops = {
index 0b7480e..fa8f7ca 100644 (file)
@@ -207,10 +207,8 @@ struct emmaprp_dev {
        struct mutex            dev_mutex;
        spinlock_t              irqlock;
 
-       int                     irq_emma;
        void __iomem            *base_emma;
        struct clk              *clk_emma_ahb, *clk_emma_ipg;
-       struct resource         *res_emma;
 
        struct v4l2_m2m_dev     *m2m_dev;
        struct vb2_alloc_ctx    *alloc_ctx;
@@ -901,9 +899,8 @@ static int emmaprp_probe(struct platform_device *pdev)
 {
        struct emmaprp_dev *pcdev;
        struct video_device *vfd;
-       struct resource *res_emma;
-       int irq_emma;
-       int ret;
+       struct resource *res;
+       int irq, ret;
 
        pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
        if (!pcdev)
@@ -920,12 +917,10 @@ static int emmaprp_probe(struct platform_device *pdev)
        if (IS_ERR(pcdev->clk_emma_ahb))
                return PTR_ERR(pcdev->clk_emma_ahb);
 
-       irq_emma = platform_get_irq(pdev, 0);
-       res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (irq_emma < 0 || res_emma == NULL) {
-               dev_err(&pdev->dev, "Missing platform resources data\n");
-               return -ENODEV;
-       }
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       pcdev->base_emma = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(pcdev->base_emma))
+               return PTR_ERR(pcdev->base_emma);
 
        ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev);
        if (ret)
@@ -952,20 +947,11 @@ static int emmaprp_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, pcdev);
 
-       pcdev->base_emma = devm_ioremap_resource(&pdev->dev, res_emma);
-       if (IS_ERR(pcdev->base_emma)) {
-               ret = PTR_ERR(pcdev->base_emma);
-               goto rel_vdev;
-       }
-
-       pcdev->irq_emma = irq_emma;
-       pcdev->res_emma = res_emma;
-
-       if (devm_request_irq(&pdev->dev, pcdev->irq_emma, emmaprp_irq,
-                            0, MEM2MEM_NAME, pcdev) < 0) {
-               ret = -ENODEV;
+       irq = platform_get_irq(pdev, 0);
+       ret = devm_request_irq(&pdev->dev, irq, emmaprp_irq, 0,
+                              dev_name(&pdev->dev), pcdev);
+       if (ret)
                goto rel_vdev;
-       }
 
        pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
        if (IS_ERR(pcdev->alloc_ctx)) {
@@ -999,6 +985,8 @@ rel_vdev:
 unreg_dev:
        v4l2_device_unregister(&pcdev->v4l2_dev);
 
+       mutex_destroy(&pcdev->dev_mutex);
+
        return ret;
 }
 
@@ -1012,6 +1000,7 @@ static int emmaprp_remove(struct platform_device *pdev)
        v4l2_m2m_release(pcdev->m2m_dev);
        vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
        v4l2_device_unregister(&pcdev->v4l2_dev);
+       mutex_destroy(&pcdev->dev_mutex);
 
        return 0;
 }
index 4e4d163..deba425 100644 (file)
@@ -435,10 +435,10 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
        return 0;
 }
 
-static int stop_streaming(struct vb2_queue *vq)
+static void stop_streaming(struct vb2_queue *vq)
 {
        struct camif_vp *vp = vb2_get_drv_priv(vq);
-       return camif_stop_capture(vp);
+       camif_stop_capture(vp);
 }
 
 static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
index 8a18972..0dcb796 100644 (file)
@@ -192,8 +192,7 @@ static struct s5p_jpeg_fmt sjpeg_formats[] = {
                .colplanes      = 2,
                .h_align        = 4,
                .v_align        = 4,
-               .flags          = SJPEG_FMT_FLAG_ENC_OUTPUT |
-                                 SJPEG_FMT_FLAG_DEC_CAPTURE |
+               .flags          = SJPEG_FMT_FLAG_DEC_CAPTURE |
                                  SJPEG_FMT_FLAG_S5P |
                                  SJPEG_FMT_NON_RGB,
                .subsampling    = V4L2_JPEG_CHROMA_SUBSAMPLING_420,
@@ -959,7 +958,7 @@ static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
 static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx,
                                u32 pixelformat, unsigned int fmt_type)
 {
-       unsigned int k, fmt_flag, ver_flag;
+       unsigned int k, fmt_flag;
 
        if (ctx->mode == S5P_JPEG_ENCODE)
                fmt_flag = (fmt_type == FMT_TYPE_OUTPUT) ?
@@ -970,16 +969,11 @@ static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx,
                                SJPEG_FMT_FLAG_DEC_OUTPUT :
                                SJPEG_FMT_FLAG_DEC_CAPTURE;
 
-       if (ctx->jpeg->variant->version == SJPEG_S5P)
-               ver_flag = SJPEG_FMT_FLAG_S5P;
-       else
-               ver_flag = SJPEG_FMT_FLAG_EXYNOS4;
-
        for (k = 0; k < ARRAY_SIZE(sjpeg_formats); k++) {
                struct s5p_jpeg_fmt *fmt = &sjpeg_formats[k];
                if (fmt->fourcc == pixelformat &&
                    fmt->flags & fmt_flag &&
-                   fmt->flags & ver_flag) {
+                   fmt->flags & ctx->jpeg->variant->fmt_ver_flag) {
                        return fmt;
                }
        }
@@ -1069,15 +1063,17 @@ static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv,
                return -EINVAL;
        }
 
+       if ((ctx->jpeg->variant->version != SJPEG_EXYNOS4) ||
+           (ctx->mode != S5P_JPEG_DECODE))
+               goto exit;
+
        /*
         * The exynos4x12 device requires resulting YUV image
         * subsampling not to be lower than the input jpeg subsampling.
         * If this requirement is not met then downgrade the requested
         * capture format to the one with subsampling equal to the input jpeg.
         */
-       if ((ctx->jpeg->variant->version != SJPEG_S5P) &&
-           (ctx->mode == S5P_JPEG_DECODE) &&
-           (fmt->flags & SJPEG_FMT_NON_RGB) &&
+       if ((fmt->flags & SJPEG_FMT_NON_RGB) &&
            (fmt->subsampling < ctx->subsampling)) {
                ret = s5p_jpeg_adjust_fourcc_to_subsampling(ctx->subsampling,
                                                            fmt->fourcc,
@@ -1090,6 +1086,23 @@ static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv,
                                                        FMT_TYPE_CAPTURE);
        }
 
+       /*
+        * Decompression of a JPEG file with 4:2:0 subsampling and odd
+        * width to the YUV 4:2:0 compliant formats produces a raw image
+        * with broken luma component. Adjust capture format to RGB565
+        * in such a case.
+        */
+       if (ctx->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_420 &&
+           (ctx->out_q.w & 1) &&
+           (pix->pixelformat == V4L2_PIX_FMT_NV12 ||
+            pix->pixelformat == V4L2_PIX_FMT_NV21 ||
+            pix->pixelformat == V4L2_PIX_FMT_YUV420)) {
+               pix->pixelformat = V4L2_PIX_FMT_RGB565;
+               fmt = s5p_jpeg_find_format(ctx, pix->pixelformat,
+                                                       FMT_TYPE_CAPTURE);
+       }
+
+exit:
        return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_CAPTURE);
 }
 
@@ -1111,6 +1124,32 @@ static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv,
        return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_OUTPUT);
 }
 
+static int exynos4_jpeg_get_output_buffer_size(struct s5p_jpeg_ctx *ctx,
+                                               struct v4l2_format *f,
+                                               int fmt_depth)
+{
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       u32 pix_fmt = f->fmt.pix.pixelformat;
+       int w = pix->width, h = pix->height, wh_align;
+
+       if (pix_fmt == V4L2_PIX_FMT_RGB32 ||
+           pix_fmt == V4L2_PIX_FMT_NV24 ||
+           pix_fmt == V4L2_PIX_FMT_NV42 ||
+           pix_fmt == V4L2_PIX_FMT_NV12 ||
+           pix_fmt == V4L2_PIX_FMT_NV21 ||
+           pix_fmt == V4L2_PIX_FMT_YUV420)
+               wh_align = 4;
+       else
+               wh_align = 1;
+
+       jpeg_bound_align_image(&w, S5P_JPEG_MIN_WIDTH,
+                              S5P_JPEG_MAX_WIDTH, wh_align,
+                              &h, S5P_JPEG_MIN_HEIGHT,
+                              S5P_JPEG_MAX_HEIGHT, wh_align);
+
+       return w * h * fmt_depth >> 3;
+}
+
 static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)
 {
        struct vb2_queue *vq;
@@ -1137,10 +1176,24 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)
        q_data->fmt = s5p_jpeg_find_format(ct, pix->pixelformat, f_type);
        q_data->w = pix->width;
        q_data->h = pix->height;
-       if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG)
-               q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3;
-       else
+       if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) {
+               /*
+                * During encoding Exynos4x12 SoCs access wider memory area
+                * than it results from Image_x and Image_y values written to
+                * the JPEG_IMAGE_SIZE register. In order to avoid sysmmu
+                * page fault calculate proper buffer size in such a case.
+                */
+               if (ct->jpeg->variant->version == SJPEG_EXYNOS4 &&
+                   f_type == FMT_TYPE_OUTPUT && ct->mode == S5P_JPEG_ENCODE)
+                       q_data->size = exynos4_jpeg_get_output_buffer_size(ct,
+                                                       f,
+                                                       q_data->fmt->depth);
+               else
+                       q_data->size = q_data->w * q_data->h *
+                                               q_data->fmt->depth >> 3;
+       } else {
                q_data->size = pix->sizeimage;
+       }
 
        if (f_type == FMT_TYPE_OUTPUT) {
                ctrl_subs = v4l2_ctrl_find(&ct->ctrl_handler,
@@ -1182,8 +1235,7 @@ static int s5p_jpeg_g_selection(struct file *file, void *priv,
        struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
 
        if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
-           s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
-           ctx->jpeg->variant->version != SJPEG_S5P)
+           s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
 
        /* For JPEG blob active == default == bounds */
@@ -1571,7 +1623,7 @@ static struct v4l2_m2m_ops s5p_jpeg_m2m_ops = {
        .job_abort      = s5p_jpeg_job_abort,
 }
 ;
-static struct v4l2_m2m_ops exynos_jpeg_m2m_ops = {
+static struct v4l2_m2m_ops exynos4_jpeg_m2m_ops = {
        .device_run     = exynos4_jpeg_device_run,
        .job_ready      = s5p_jpeg_job_ready,
        .job_abort      = s5p_jpeg_job_abort,
@@ -1670,13 +1722,11 @@ static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
        return ret > 0 ? 0 : ret;
 }
 
-static int s5p_jpeg_stop_streaming(struct vb2_queue *q)
+static void s5p_jpeg_stop_streaming(struct vb2_queue *q)
 {
        struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q);
 
        pm_runtime_put(ctx->jpeg->dev);
-
-       return 0;
 }
 
 static struct vb2_ops s5p_jpeg_qops = {
@@ -1845,7 +1895,7 @@ static irqreturn_t exynos4_jpeg_irq(int irq, void *priv)
        return IRQ_HANDLED;
 }
 
-static void *jpeg_get_drv_data(struct platform_device *pdev);
+static void *jpeg_get_drv_data(struct device *dev);
 
 /*
  * ============================================================================
@@ -1857,18 +1907,14 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
 {
        struct s5p_jpeg *jpeg;
        struct resource *res;
-       struct v4l2_m2m_ops *samsung_jpeg_m2m_ops;
        int ret;
 
-       if (!pdev->dev.of_node)
-               return -ENODEV;
-
        /* JPEG IP abstraction struct */
        jpeg = devm_kzalloc(&pdev->dev, sizeof(struct s5p_jpeg), GFP_KERNEL);
        if (!jpeg)
                return -ENOMEM;
 
-       jpeg->variant = jpeg_get_drv_data(pdev);
+       jpeg->variant = jpeg_get_drv_data(&pdev->dev);
 
        mutex_init(&jpeg->lock);
        spin_lock_init(&jpeg->slock);
@@ -1911,13 +1957,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
                goto clk_get_rollback;
        }
 
-       if (jpeg->variant->version == SJPEG_S5P)
-               samsung_jpeg_m2m_ops = &s5p_jpeg_m2m_ops;
-       else
-               samsung_jpeg_m2m_ops = &exynos_jpeg_m2m_ops;
-
        /* mem2mem device */
-       jpeg->m2m_dev = v4l2_m2m_init(samsung_jpeg_m2m_ops);
+       jpeg->m2m_dev = v4l2_m2m_init(jpeg->variant->m2m_ops);
        if (IS_ERR(jpeg->m2m_dev)) {
                v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n");
                ret = PTR_ERR(jpeg->m2m_dev);
@@ -2102,15 +2143,18 @@ static const struct dev_pm_ops s5p_jpeg_pm_ops = {
        SET_RUNTIME_PM_OPS(s5p_jpeg_runtime_suspend, s5p_jpeg_runtime_resume, NULL)
 };
 
-#ifdef CONFIG_OF
 static struct s5p_jpeg_variant s5p_jpeg_drvdata = {
        .version        = SJPEG_S5P,
        .jpeg_irq       = s5p_jpeg_irq,
+       .m2m_ops        = &s5p_jpeg_m2m_ops,
+       .fmt_ver_flag   = SJPEG_FMT_FLAG_S5P,
 };
 
 static struct s5p_jpeg_variant exynos4_jpeg_drvdata = {
        .version        = SJPEG_EXYNOS4,
        .jpeg_irq       = exynos4_jpeg_irq,
+       .m2m_ops        = &exynos4_jpeg_m2m_ops,
+       .fmt_ver_flag   = SJPEG_FMT_FLAG_EXYNOS4,
 };
 
 static const struct of_device_id samsung_jpeg_match[] = {
@@ -2129,19 +2173,21 @@ static const struct of_device_id samsung_jpeg_match[] = {
 
 MODULE_DEVICE_TABLE(of, samsung_jpeg_match);
 
-static void *jpeg_get_drv_data(struct platform_device *pdev)
+static void *jpeg_get_drv_data(struct device *dev)
 {
        struct s5p_jpeg_variant *driver_data = NULL;
        const struct of_device_id *match;
 
-       match = of_match_node(of_match_ptr(samsung_jpeg_match),
-                                        pdev->dev.of_node);
+       if (!IS_ENABLED(CONFIG_OF) || !dev->of_node)
+               return &s5p_jpeg_drvdata;
+
+       match = of_match_node(samsung_jpeg_match, dev->of_node);
+
        if (match)
                driver_data = (struct s5p_jpeg_variant *)match->data;
 
        return driver_data;
 }
-#endif
 
 static struct platform_driver s5p_jpeg_driver = {
        .probe = s5p_jpeg_probe,
index f482dbf..3e47863 100644 (file)
@@ -117,8 +117,10 @@ struct s5p_jpeg {
 };
 
 struct s5p_jpeg_variant {
-       unsigned int    version;
-       irqreturn_t     (*jpeg_irq)(int irq, void *priv);
+       unsigned int            version;
+       unsigned int            fmt_ver_flag;
+       struct v4l2_m2m_ops     *m2m_ops;
+       irqreturn_t             (*jpeg_irq)(int irq, void *priv);
 };
 
 /**
index 8d0b686..51cb2dd 100644 (file)
 #define S5P_FIMV_D_SLICE_IF_ENABLE_V6          0xf4c4
 #define S5P_FIMV_D_PICTURE_TAG_V6              0xf4c8
 #define S5P_FIMV_D_STREAM_DATA_SIZE_V6         0xf4d0
+#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V6      0xf47c
 
 /* Display information register */
 #define S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6      0xf500
         (DIV_ROUND_UP((mbw) * (mbh), 32) * 16))
 #define S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6(w, h)    (((w) * 192) + 64)
 #define S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(w, h) \
-                       ((w) * ((h) * 64 + 144) + (2048/16 * (h) * 64) + \
-                        (2048/16 * 256 + 8320))
+                       ((w) * 144 + 8192 * (h) + 49216 + 1048576)
 #define S5P_FIMV_SCRATCH_BUF_SIZE_VC1_DEC_V6(w, h) \
                                                (2096 * ((w) + (h) + 1))
 #define S5P_FIMV_SCRATCH_BUF_SIZE_H263_DEC_V6(w, h)    ((w) * 400)
index ea5ec2a..1a5c6fd 100644 (file)
@@ -18,8 +18,6 @@
 #define S5P_FIMV_CODEC_VP8_ENC_V7      25
 
 /* Additional registers for v7 */
-#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V7              0xf47c
-
 #define S5P_FIMV_E_SOURCE_FIRST_ADDR_V7                        0xf9e0
 #define S5P_FIMV_E_SOURCE_SECOND_ADDR_V7               0xf9e4
 #define S5P_FIMV_E_SOURCE_THIRD_ADDR_V7                        0xf9e8
@@ -56,6 +54,7 @@
                        (SZ_1M + ((w) * 144) + (8192 * (h)) + 49216)
 
 #define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7(w, h) \
-                       (((w) * 48) + (((w) + 1) / 2 * 128) + 144 + 8192)
+                       (((w) * 48) + 8192 + ((((w) + 1) / 2) * 128) + 144 + \
+                       ((((((w) * 16) * ((h) * 16)) * 3) / 2) * 4))
 
 #endif /*_REGS_MFC_V7_H*/
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v8.h b/drivers/media/platform/s5p-mfc/regs-mfc-v8.h
new file mode 100644 (file)
index 0000000..cc7cbec
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Register definition file for Samsung MFC V8.x Interface (FIMV) driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.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 _REGS_MFC_V8_H
+#define _REGS_MFC_V8_H
+
+#include <linux/sizes.h>
+#include "regs-mfc-v7.h"
+
+/* Additional registers for v8 */
+#define S5P_FIMV_D_MVC_NUM_VIEWS_V8            0xf104
+#define S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8     0xf144
+#define S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8    0xf148
+#define S5P_FIMV_D_MV_BUFFER_SIZE_V8           0xf150
+
+#define S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8      0xf138
+#define S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8     0xf13c
+
+#define S5P_FIMV_D_FIRST_PLANE_DPB_V8          0xf160
+#define S5P_FIMV_D_SECOND_PLANE_DPB_V8         0xf260
+#define S5P_FIMV_D_MV_BUFFER_V8                        0xf460
+
+#define S5P_FIMV_D_NUM_MV_V8                   0xf134
+#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V8      0xf154
+
+#define S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V8      0xf560
+#define S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V8      0xf564
+
+#define S5P_FIMV_D_CPB_BUFFER_ADDR_V8          0xf5b0
+#define S5P_FIMV_D_CPB_BUFFER_SIZE_V8          0xf5b4
+#define S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V8 0xf5bc
+#define S5P_FIMV_D_CPB_BUFFER_OFFSET_V8                0xf5c0
+#define S5P_FIMV_D_SLICE_IF_ENABLE_V8          0xf5c4
+#define S5P_FIMV_D_STREAM_DATA_SIZE_V8         0xf5d0
+
+/* Display information register */
+#define S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V8      0xf600
+#define S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V8     0xf604
+
+/* Display status */
+#define S5P_FIMV_D_DISPLAY_STATUS_V8           0xf608
+
+#define S5P_FIMV_D_DISPLAY_FIRST_PLANE_ADDR_V8 0xf60c
+#define S5P_FIMV_D_DISPLAY_SECOND_PLANE_ADDR_V8        0xf610
+
+#define S5P_FIMV_D_DISPLAY_FRAME_TYPE_V8       0xf618
+#define S5P_FIMV_D_DISPLAY_CROP_INFO1_V8       0xf61c
+#define S5P_FIMV_D_DISPLAY_CROP_INFO2_V8       0xf620
+#define S5P_FIMV_D_DISPLAY_PICTURE_PROFILE_V8  0xf624
+
+/* Decoded picture information register */
+#define S5P_FIMV_D_DECODED_STATUS_V8           0xf644
+#define S5P_FIMV_D_DECODED_FIRST_PLANE_ADDR_V8 0xf648
+#define S5P_FIMV_D_DECODED_SECOND_PLANE_ADDR_V8        0xf64c
+#define S5P_FIMV_D_DECODED_THIRD_PLANE_ADDR_V8 0xf650
+#define S5P_FIMV_D_DECODED_FRAME_TYPE_V8       0xf654
+#define S5P_FIMV_D_DECODED_NAL_SIZE_V8          0xf664
+
+/* Returned value register for specific setting */
+#define S5P_FIMV_D_RET_PICTURE_TAG_TOP_V8      0xf674
+#define S5P_FIMV_D_RET_PICTURE_TAG_BOT_V8      0xf678
+#define S5P_FIMV_D_MVC_VIEW_ID_V8              0xf6d8
+
+/* SEI related information */
+#define S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V8     0xf6dc
+
+/* Encoder Registers */
+#define S5P_FIMV_E_FIXED_PICTURE_QP_V8         0xf794
+#define S5P_FIMV_E_RC_CONFIG_V8                        0xf798
+#define S5P_FIMV_E_RC_QP_BOUND_V8              0xf79c
+#define S5P_FIMV_E_RC_RPARAM_V8                        0xf7a4
+#define S5P_FIMV_E_MB_RC_CONFIG_V8             0xf7a8
+#define S5P_FIMV_E_PADDING_CTRL_V8             0xf7ac
+#define S5P_FIMV_E_MV_HOR_RANGE_V8             0xf7b4
+#define S5P_FIMV_E_MV_VER_RANGE_V8             0xf7b8
+
+#define S5P_FIMV_E_VBV_BUFFER_SIZE_V8          0xf78c
+#define S5P_FIMV_E_VBV_INIT_DELAY_V8           0xf790
+
+#define S5P_FIMV_E_ASPECT_RATIO_V8             0xfb4c
+#define S5P_FIMV_E_EXTENDED_SAR_V8             0xfb50
+#define S5P_FIMV_E_H264_OPTIONS_V8             0xfb54
+
+/* MFCv8 Context buffer sizes */
+#define MFC_CTX_BUF_SIZE_V8            (30 * SZ_1K)    /*  30KB */
+#define MFC_H264_DEC_CTX_BUF_SIZE_V8   (2 * SZ_1M)     /*  2MB */
+#define MFC_OTHER_DEC_CTX_BUF_SIZE_V8  (20 * SZ_1K)    /*  20KB */
+#define MFC_H264_ENC_CTX_BUF_SIZE_V8   (100 * SZ_1K)   /* 100KB */
+#define MFC_OTHER_ENC_CTX_BUF_SIZE_V8  (10 * SZ_1K)    /*  10KB */
+
+/* Buffer size defines */
+#define S5P_FIMV_TMV_BUFFER_SIZE_V8(w, h)      (((w) + 1) * ((h) + 1) * 8)
+
+#define S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V8(w, h)    (((w) * 704) + 2176)
+#define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V8(w, h) \
+               (((w) * 576 + (h) * 128)  + 4128)
+
+#define S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V8(w, h) \
+                       (((w) * 592) + 2336)
+#define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V8(w, h) \
+                       (((w) * 576) + 10512 + \
+                       ((((((w) * 16) * ((h) * 16)) * 3) / 2) * 4))
+#define S5P_FIMV_ME_BUFFER_SIZE_V8(imw, imh, mbw, mbh) \
+       ((DIV_ROUND_UP((mbw * 16), 64) *  DIV_ROUND_UP((mbh * 16), 64) * 256) \
+        + (DIV_ROUND_UP((mbw) * (mbh), 32) * 16))
+
+/* BUffer alignment defines */
+#define S5P_FIMV_D_ALIGN_PLANE_SIZE_V8 64
+
+/* MFCv8 variant defines */
+#define MAX_FW_SIZE_V8                 (SZ_1M)         /* 1MB */
+#define MAX_CPB_SIZE_V8                        (3 * SZ_1M)     /* 3MB */
+#define MFC_VERSION_V8                 0x80
+#define MFC_NUM_PORTS_V8               1
+
+#endif /*_REGS_MFC_V8_H*/
index 89356ae..4172318 100644 (file)
@@ -309,12 +309,15 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
 {
        struct s5p_mfc_dev *dev = ctx->dev;
        unsigned int dst_frame_status;
+       unsigned int dec_frame_status;
        struct s5p_mfc_buf *src_buf;
        unsigned long flags;
        unsigned int res_change;
 
        dst_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)
                                & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
+       dec_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dec_status, dev)
+                               & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
        res_change = (s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)
                                & S5P_FIMV_DEC_STATUS_RESOLUTION_MASK)
                                >> S5P_FIMV_DEC_STATUS_RESOLUTION_SHIFT;
@@ -339,16 +342,23 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
        /* All frames remaining in the buffer have been extracted  */
        if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
                if (ctx->state == MFCINST_RES_CHANGE_FLUSH) {
+                       static const struct v4l2_event ev_src_ch = {
+                               .type = V4L2_EVENT_SOURCE_CHANGE,
+                               .u.src_change.changes =
+                                       V4L2_EVENT_SRC_CH_RESOLUTION,
+                       };
+
                        s5p_mfc_handle_frame_all_extracted(ctx);
                        ctx->state = MFCINST_RES_CHANGE_END;
+                       v4l2_event_queue_fh(&ctx->fh, &ev_src_ch);
+
                        goto leave_handle_frame;
                } else {
                        s5p_mfc_handle_frame_all_extracted(ctx);
                }
        }
 
-       if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY ||
-               dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_ONLY)
+       if (dec_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY)
                s5p_mfc_handle_frame_copy_time(ctx);
 
        /* A frame has been decoded and is in the buffer  */
@@ -366,6 +376,7 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
                ctx->consumed_stream += s5p_mfc_hw_call(dev->mfc_ops,
                                                get_consumed_stream, dev);
                if (ctx->codec_mode != S5P_MFC_CODEC_H264_DEC &&
+                       ctx->codec_mode != S5P_MFC_CODEC_VP8_DEC &&
                        ctx->consumed_stream + STUFF_BYTE <
                        src_buf->b->v4l2_planes[0].bytesused) {
                        /* Run MFC again on the same buffer */
@@ -641,6 +652,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
 
        case S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET:
                clear_work_bit(ctx);
+               ctx->inst_no = MFC_NO_INSTANCE_SET;
                ctx->state = MFCINST_FREE;
                wake_up(&ctx->queue);
                goto irq_cleanup_hw;
@@ -761,7 +773,7 @@ static int s5p_mfc_open(struct file *file)
                goto err_bad_node;
        }
        ctx->fh.ctrl_handler = &ctx->ctrl_handler;
-       ctx->inst_no = -1;
+       ctx->inst_no = MFC_NO_INSTANCE_SET;
        /* Load firmware if this is the first instance */
        if (dev->num_inst == 1) {
                dev->watchdog_timer.expires = jiffies +
@@ -871,29 +883,11 @@ static int s5p_mfc_release(struct file *file)
        vb2_queue_release(&ctx->vq_dst);
        /* Mark context as idle */
        clear_work_bit_irqsave(ctx);
-       /* If instance was initialised then
+       /* If instance was initialised and not yet freed,
         * return instance and free resources */
-       if (ctx->inst_no != MFC_NO_INSTANCE_SET) {
+       if (ctx->state != MFCINST_FREE && ctx->state != MFCINST_INIT) {
                mfc_debug(2, "Has to free instance\n");
-               ctx->state = MFCINST_RETURN_INST;
-               set_work_bit_irqsave(ctx);
-               s5p_mfc_clean_ctx_int_flags(ctx);
-               s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
-               /* Wait until instance is returned or timeout occurred */
-               if (s5p_mfc_wait_for_done_ctx
-                   (ctx, S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0)) {
-                       s5p_mfc_clock_off();
-                       mfc_err("Err returning instance\n");
-               }
-               mfc_debug(2, "After free instance\n");
-               /* Free resources */
-               s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
-               s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
-               if (ctx->type == MFCINST_DECODER)
-                       s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer,
-                                       ctx);
-
-               ctx->inst_no = MFC_NO_INSTANCE_SET;
+               s5p_mfc_close_mfc_inst(dev, ctx);
        }
        /* hardware locking scheme */
        if (dev->curr_ctx == ctx->num)
@@ -1207,6 +1201,7 @@ static int s5p_mfc_probe(struct platform_device *pdev)
        /* Initialize HW ops and commands based on MFC version */
        s5p_mfc_init_hw_ops(dev);
        s5p_mfc_init_hw_cmds(dev);
+       s5p_mfc_init_regs(dev);
 
        pr_debug("%s--\n", __func__);
        return 0;
@@ -1352,6 +1347,7 @@ struct s5p_mfc_buf_align mfc_buf_align_v5 = {
 
 static struct s5p_mfc_variant mfc_drvdata_v5 = {
        .version        = MFC_VERSION,
+       .version_bit    = MFC_V5_BIT,
        .port_num       = MFC_NUM_PORTS,
        .buf_size       = &buf_size_v5,
        .buf_align      = &mfc_buf_align_v5,
@@ -1378,6 +1374,7 @@ struct s5p_mfc_buf_align mfc_buf_align_v6 = {
 
 static struct s5p_mfc_variant mfc_drvdata_v6 = {
        .version        = MFC_VERSION_V6,
+       .version_bit    = MFC_V6_BIT,
        .port_num       = MFC_NUM_PORTS_V6,
        .buf_size       = &buf_size_v6,
        .buf_align      = &mfc_buf_align_v6,
@@ -1404,12 +1401,40 @@ struct s5p_mfc_buf_align mfc_buf_align_v7 = {
 
 static struct s5p_mfc_variant mfc_drvdata_v7 = {
        .version        = MFC_VERSION_V7,
+       .version_bit    = MFC_V7_BIT,
        .port_num       = MFC_NUM_PORTS_V7,
        .buf_size       = &buf_size_v7,
        .buf_align      = &mfc_buf_align_v7,
        .fw_name        = "s5p-mfc-v7.fw",
 };
 
+struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = {
+       .dev_ctx        = MFC_CTX_BUF_SIZE_V8,
+       .h264_dec_ctx   = MFC_H264_DEC_CTX_BUF_SIZE_V8,
+       .other_dec_ctx  = MFC_OTHER_DEC_CTX_BUF_SIZE_V8,
+       .h264_enc_ctx   = MFC_H264_ENC_CTX_BUF_SIZE_V8,
+       .other_enc_ctx  = MFC_OTHER_ENC_CTX_BUF_SIZE_V8,
+};
+
+struct s5p_mfc_buf_size buf_size_v8 = {
+       .fw     = MAX_FW_SIZE_V8,
+       .cpb    = MAX_CPB_SIZE_V8,
+       .priv   = &mfc_buf_size_v8,
+};
+
+struct s5p_mfc_buf_align mfc_buf_align_v8 = {
+       .base = 0,
+};
+
+static struct s5p_mfc_variant mfc_drvdata_v8 = {
+       .version        = MFC_VERSION_V8,
+       .version_bit    = MFC_V8_BIT,
+       .port_num       = MFC_NUM_PORTS_V8,
+       .buf_size       = &buf_size_v8,
+       .buf_align      = &mfc_buf_align_v8,
+       .fw_name        = "s5p-mfc-v8.fw",
+};
+
 static struct platform_device_id mfc_driver_ids[] = {
        {
                .name = "s5p-mfc",
@@ -1423,6 +1448,9 @@ static struct platform_device_id mfc_driver_ids[] = {
        }, {
                .name = "s5p-mfc-v7",
                .driver_data = (unsigned long)&mfc_drvdata_v7,
+       }, {
+               .name = "s5p-mfc-v8",
+               .driver_data = (unsigned long)&mfc_drvdata_v8,
        },
        {},
 };
@@ -1438,6 +1466,9 @@ static const struct of_device_id exynos_mfc_match[] = {
        }, {
                .compatible = "samsung,mfc-v7",
                .data = &mfc_drvdata_v7,
+       }, {
+               .compatible = "samsung,mfc-v8",
+               .data = &mfc_drvdata_v8,
        },
        {},
 };
index 5c28cc3..b04360c 100644 (file)
@@ -23,8 +23,7 @@
 #include <media/v4l2-ioctl.h>
 #include <media/videobuf2-core.h>
 #include "regs-mfc.h"
-#include "regs-mfc-v6.h"
-#include "regs-mfc-v7.h"
+#include "regs-mfc-v8.h"
 
 /* Definitions related to MFC memory */
 
@@ -223,6 +222,7 @@ struct s5p_mfc_buf_align {
 struct s5p_mfc_variant {
        unsigned int version;
        unsigned int port_num;
+       u32 version_bit;
        struct s5p_mfc_buf_size *buf_size;
        struct s5p_mfc_buf_align *buf_align;
        char    *fw_name;
@@ -330,6 +330,7 @@ struct s5p_mfc_dev {
        int warn_start;
        struct s5p_mfc_hw_ops *mfc_ops;
        struct s5p_mfc_hw_cmds *mfc_cmds;
+       const struct s5p_mfc_regs *mfc_regs;
 };
 
 /**
@@ -663,6 +664,7 @@ struct s5p_mfc_fmt {
        u32 codec_mode;
        enum s5p_mfc_fmt_type type;
        u32 num_planes;
+       u32 versions;
 };
 
 /**
@@ -700,6 +702,13 @@ void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx);
                                (dev->variant->port_num ? 1 : 0) : 0) : 0)
 #define IS_TWOPORT(dev)                (dev->variant->port_num == 2 ? 1 : 0)
 #define IS_MFCV6_PLUS(dev)     (dev->variant->version >= 0x60 ? 1 : 0)
-#define IS_MFCV7(dev)          (dev->variant->version >= 0x70 ? 1 : 0)
+#define IS_MFCV7_PLUS(dev)     (dev->variant->version >= 0x70 ? 1 : 0)
+#define IS_MFCV8(dev)          (dev->variant->version >= 0x80 ? 1 : 0)
+
+#define MFC_V5_BIT     BIT(0)
+#define MFC_V6_BIT     BIT(1)
+#define MFC_V7_BIT     BIT(2)
+#define MFC_V8_BIT     BIT(3)
+
 
 #endif /* S5P_MFC_COMMON_H_ */
index ee05f2d..6c3f8f7 100644 (file)
@@ -400,3 +400,65 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
        return 0;
 }
 
+int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
+{
+       int ret = 0;
+
+       ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer, ctx);
+       if (ret) {
+               mfc_err("Failed allocating instance buffer\n");
+               goto err;
+       }
+
+       if (ctx->type == MFCINST_DECODER) {
+               ret = s5p_mfc_hw_call(dev->mfc_ops,
+                                       alloc_dec_temp_buffers, ctx);
+               if (ret) {
+                       mfc_err("Failed allocating temporary buffers\n");
+                       goto err_free_inst_buf;
+               }
+       }
+
+       set_work_bit_irqsave(ctx);
+       s5p_mfc_clean_ctx_int_flags(ctx);
+       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+       if (s5p_mfc_wait_for_done_ctx(ctx,
+               S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
+               /* Error or timeout */
+               mfc_err("Error getting instance from hardware\n");
+               ret = -EIO;
+               goto err_free_desc_buf;
+       }
+
+       mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+       return ret;
+
+err_free_desc_buf:
+       if (ctx->type == MFCINST_DECODER)
+               s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);
+err_free_inst_buf:
+       s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
+err:
+       return ret;
+}
+
+void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
+{
+       ctx->state = MFCINST_RETURN_INST;
+       set_work_bit_irqsave(ctx);
+       s5p_mfc_clean_ctx_int_flags(ctx);
+       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+       /* Wait until instance is returned or timeout occurred */
+       if (s5p_mfc_wait_for_done_ctx(ctx,
+                               S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0))
+               mfc_err("Err returning instance\n");
+
+       /* Free resources */
+       s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
+       s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
+       if (ctx->type == MFCINST_DECODER)
+               s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);
+
+       ctx->inst_no = MFC_NO_INSTANCE_SET;
+       ctx->state = MFCINST_FREE;
+}
index 6a9b6f8..8e5df04 100644 (file)
@@ -28,4 +28,7 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev);
 
 int s5p_mfc_reset(struct s5p_mfc_dev *dev);
 
+int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx);
+void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx);
+
 #endif /* S5P_MFC_CTRL_H */
index 8faf969..4d93835 100644 (file)
 #include <media/v4l2-event.h>
 #include <media/videobuf2-core.h>
 #include "s5p_mfc_common.h"
+#include "s5p_mfc_ctrl.h"
 #include "s5p_mfc_debug.h"
 #include "s5p_mfc_dec.h"
 #include "s5p_mfc_intr.h"
 #include "s5p_mfc_opr.h"
 #include "s5p_mfc_pm.h"
 
-#define DEF_SRC_FMT_DEC        V4L2_PIX_FMT_H264
-#define DEF_DST_FMT_DEC        V4L2_PIX_FMT_NV12MT_16X16
-
 static struct s5p_mfc_fmt formats[] = {
        {
                .name           = "4:2:0 2 Planes 16x16 Tiles",
@@ -41,6 +39,7 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_NONE,
                .type           = MFC_FMT_RAW,
                .num_planes     = 2,
+               .versions       = MFC_V6_BIT | MFC_V7_BIT,
        },
        {
                .name           = "4:2:0 2 Planes 64x32 Tiles",
@@ -48,6 +47,7 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_NONE,
                .type           = MFC_FMT_RAW,
                .num_planes     = 2,
+               .versions       = MFC_V5_BIT,
        },
        {
                .name           = "4:2:0 2 Planes Y/CbCr",
@@ -55,6 +55,7 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_NONE,
                .type           = MFC_FMT_RAW,
                .num_planes     = 2,
+               .versions       = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
        },
        {
                .name           = "4:2:0 2 Planes Y/CrCb",
@@ -62,6 +63,7 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_NONE,
                .type           = MFC_FMT_RAW,
                .num_planes     = 2,
+               .versions       = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
        },
        {
                .name           = "H264 Encoded Stream",
@@ -69,6 +71,8 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_H264_DEC,
                .type           = MFC_FMT_DEC,
                .num_planes     = 1,
+               .versions       = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+                                                               MFC_V8_BIT,
        },
        {
                .name           = "H264/MVC Encoded Stream",
@@ -76,6 +80,7 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_H264_MVC_DEC,
                .type           = MFC_FMT_DEC,
                .num_planes     = 1,
+               .versions       = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
        },
        {
                .name           = "H263 Encoded Stream",
@@ -83,6 +88,8 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_H263_DEC,
                .type           = MFC_FMT_DEC,
                .num_planes     = 1,
+               .versions       = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+                                                               MFC_V8_BIT,
        },
        {
                .name           = "MPEG1 Encoded Stream",
@@ -90,6 +97,8 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_MPEG2_DEC,
                .type           = MFC_FMT_DEC,
                .num_planes     = 1,
+               .versions       = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+                                                               MFC_V8_BIT,
        },
        {
                .name           = "MPEG2 Encoded Stream",
@@ -97,6 +106,8 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_MPEG2_DEC,
                .type           = MFC_FMT_DEC,
                .num_planes     = 1,
+               .versions       = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+                                                               MFC_V8_BIT,
        },
        {
                .name           = "MPEG4 Encoded Stream",
@@ -104,6 +115,8 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_MPEG4_DEC,
                .type           = MFC_FMT_DEC,
                .num_planes     = 1,
+               .versions       = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+                                                               MFC_V8_BIT,
        },
        {
                .name           = "XviD Encoded Stream",
@@ -111,6 +124,8 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_MPEG4_DEC,
                .type           = MFC_FMT_DEC,
                .num_planes     = 1,
+               .versions       = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+                                                               MFC_V8_BIT,
        },
        {
                .name           = "VC1 Encoded Stream",
@@ -118,6 +133,8 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_VC1_DEC,
                .type           = MFC_FMT_DEC,
                .num_planes     = 1,
+               .versions       = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+                                                               MFC_V8_BIT,
        },
        {
                .name           = "VC1 RCV Encoded Stream",
@@ -125,6 +142,8 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_VC1RCV_DEC,
                .type           = MFC_FMT_DEC,
                .num_planes     = 1,
+               .versions       = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+                                                               MFC_V8_BIT,
        },
        {
                .name           = "VP8 Encoded Stream",
@@ -132,6 +151,7 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_VP8_DEC,
                .type           = MFC_FMT_DEC,
                .num_planes     = 1,
+               .versions       = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
        },
 };
 
@@ -262,8 +282,10 @@ static int vidioc_querycap(struct file *file, void *priv,
 }
 
 /* Enumerate format */
-static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
+                                                       bool mplane, bool out)
 {
+       struct s5p_mfc_dev *dev = video_drvdata(file);
        struct s5p_mfc_fmt *fmt;
        int i, j = 0;
 
@@ -276,6 +298,8 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
                        continue;
                else if (!out && formats[i].type != MFC_FMT_RAW)
                        continue;
+               else if ((dev->variant->version_bit & formats[i].versions) == 0)
+                       continue;
 
                if (j == f->index)
                        break;
@@ -292,25 +316,25 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
 static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
                                                        struct v4l2_fmtdesc *f)
 {
-       return vidioc_enum_fmt(f, false, false);
+       return vidioc_enum_fmt(file, f, false, false);
 }
 
 static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
                                                        struct v4l2_fmtdesc *f)
 {
-       return vidioc_enum_fmt(f, true, false);
+       return vidioc_enum_fmt(file, f, true, false);
 }
 
-static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
                                                        struct v4l2_fmtdesc *f)
 {
-       return vidioc_enum_fmt(f, false, true);
+       return vidioc_enum_fmt(file, f, false, true);
 }
 
-static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv,
                                                        struct v4l2_fmtdesc *f)
 {
-       return vidioc_enum_fmt(f, true, true);
+       return vidioc_enum_fmt(file, f, true, true);
 }
 
 /* Get format */
@@ -386,11 +410,9 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
                        mfc_err("Unknown codec\n");
                        return -EINVAL;
                }
-               if (!IS_MFCV6_PLUS(dev)) {
-                       if (fmt->fourcc == V4L2_PIX_FMT_VP8) {
-                               mfc_err("Not supported format.\n");
-                               return -EINVAL;
-                       }
+               if ((dev->variant->version_bit & fmt->versions) == 0) {
+                       mfc_err("Unsupported format by this MFC version.\n");
+                       return -EINVAL;
                }
        } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
                fmt = find_format(f, MFC_FMT_RAW);
@@ -398,13 +420,8 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
                        mfc_err("Unsupported format for destination.\n");
                        return -EINVAL;
                }
-               if (IS_MFCV6_PLUS(dev) &&
-                               (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) {
-                       mfc_err("Not supported format.\n");
-                       return -EINVAL;
-               } else if (!IS_MFCV6_PLUS(dev) &&
-                               (fmt->fourcc != V4L2_PIX_FMT_NV12MT)) {
-                       mfc_err("Not supported format.\n");
+               if ((dev->variant->version_bit & fmt->versions) == 0) {
+                       mfc_err("Unsupported format by this MFC version.\n");
                        return -EINVAL;
                }
        }
@@ -462,104 +479,131 @@ out:
        return ret;
 }
 
-/* Reqeust buffers */
-static int vidioc_reqbufs(struct file *file, void *priv,
-                                         struct v4l2_requestbuffers *reqbufs)
+static int reqbufs_output(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
+                               struct v4l2_requestbuffers *reqbufs)
 {
-       struct s5p_mfc_dev *dev = video_drvdata(file);
-       struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
        int ret = 0;
 
-       if (reqbufs->memory != V4L2_MEMORY_MMAP) {
-               mfc_err("Only V4L2_MEMORY_MAP is supported\n");
-               return -EINVAL;
-       }
-       if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
-               /* Can only request buffers after an instance has been opened.*/
-               if (ctx->state == MFCINST_INIT) {
-                       ctx->src_bufs_cnt = 0;
-                       if (reqbufs->count == 0) {
-                               mfc_debug(2, "Freeing buffers\n");
-                               s5p_mfc_clock_on();
-                               ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
-                               s5p_mfc_clock_off();
-                               return ret;
-                       }
-                       /* Decoding */
-                       if (ctx->output_state != QUEUE_FREE) {
-                               mfc_err("Bufs have already been requested\n");
-                               return -EINVAL;
-                       }
-                       s5p_mfc_clock_on();
-                       ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
-                       s5p_mfc_clock_off();
-                       if (ret) {
-                               mfc_err("vb2_reqbufs on output failed\n");
-                               return ret;
-                       }
-                       mfc_debug(2, "vb2_reqbufs: %d\n", ret);
-                       ctx->output_state = QUEUE_BUFS_REQUESTED;
-               }
-       } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
-               ctx->dst_bufs_cnt = 0;
-               if (reqbufs->count == 0) {
-                       mfc_debug(2, "Freeing buffers\n");
-                       s5p_mfc_clock_on();
-                       ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
-                       s5p_mfc_clock_off();
-                       return ret;
+       s5p_mfc_clock_on();
+
+       if (reqbufs->count == 0) {
+               mfc_debug(2, "Freeing buffers\n");
+               ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+               if (ret)
+                       goto out;
+               s5p_mfc_close_mfc_inst(dev, ctx);
+               ctx->src_bufs_cnt = 0;
+               ctx->output_state = QUEUE_FREE;
+       } else if (ctx->output_state == QUEUE_FREE) {
+               /* Can only request buffers when we have a valid format set. */
+               WARN_ON(ctx->src_bufs_cnt != 0);
+               if (ctx->state != MFCINST_INIT) {
+                       mfc_err("Reqbufs called in an invalid state\n");
+                       ret = -EINVAL;
+                       goto out;
                }
-               if (ctx->capture_state != QUEUE_FREE) {
-                       mfc_err("Bufs have already been requested\n");
-                       return -EINVAL;
-               }
-               ctx->capture_state = QUEUE_BUFS_REQUESTED;
-               s5p_mfc_clock_on();
-               ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
-               s5p_mfc_clock_off();
+
+               mfc_debug(2, "Allocating %d buffers for OUTPUT queue\n",
+                               reqbufs->count);
+               ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+               if (ret)
+                       goto out;
+
+               ret = s5p_mfc_open_mfc_inst(dev, ctx);
                if (ret) {
-                       mfc_err("vb2_reqbufs on capture failed\n");
-                       return ret;
-               }
-               if (reqbufs->count < ctx->pb_count) {
-                       mfc_err("Not enough buffers allocated\n");
                        reqbufs->count = 0;
-                       s5p_mfc_clock_on();
-                       ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
-                       s5p_mfc_clock_off();
-                       return -ENOMEM;
+                       vb2_reqbufs(&ctx->vq_src, reqbufs);
+                       goto out;
                }
+
+               ctx->output_state = QUEUE_BUFS_REQUESTED;
+       } else {
+               mfc_err("Buffers have already been requested\n");
+               ret = -EINVAL;
+       }
+out:
+       s5p_mfc_clock_off();
+       if (ret)
+               mfc_err("Failed allocating buffers for OUTPUT queue\n");
+       return ret;
+}
+
+static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
+                               struct v4l2_requestbuffers *reqbufs)
+{
+       int ret = 0;
+
+       s5p_mfc_clock_on();
+
+       if (reqbufs->count == 0) {
+               mfc_debug(2, "Freeing buffers\n");
+               ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+               if (ret)
+                       goto out;
+               s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
+               ctx->dst_bufs_cnt = 0;
+       } else if (ctx->capture_state == QUEUE_FREE) {
+               WARN_ON(ctx->dst_bufs_cnt != 0);
+               mfc_debug(2, "Allocating %d buffers for CAPTURE queue\n",
+                               reqbufs->count);
+               ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+               if (ret)
+                       goto out;
+
+               ctx->capture_state = QUEUE_BUFS_REQUESTED;
                ctx->total_dpb_count = reqbufs->count;
+
                ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_codec_buffers, ctx);
                if (ret) {
                        mfc_err("Failed to allocate decoding buffers\n");
                        reqbufs->count = 0;
-                       s5p_mfc_clock_on();
-                       ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
-                       s5p_mfc_clock_off();
-                       return -ENOMEM;
-               }
-               if (ctx->dst_bufs_cnt == ctx->total_dpb_count) {
-                       ctx->capture_state = QUEUE_BUFS_MMAPED;
-               } else {
-                       mfc_err("Not all buffers passed to buf_init\n");
-                       reqbufs->count = 0;
-                       s5p_mfc_clock_on();
-                       ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
-                       s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers,
-                                       ctx);
-                       s5p_mfc_clock_off();
-                       return -ENOMEM;
+                       vb2_reqbufs(&ctx->vq_dst, reqbufs);
+                       ret = -ENOMEM;
+                       ctx->capture_state = QUEUE_FREE;
+                       goto out;
                }
+
+               WARN_ON(ctx->dst_bufs_cnt != ctx->total_dpb_count);
+               ctx->capture_state = QUEUE_BUFS_MMAPED;
+
                if (s5p_mfc_ctx_ready(ctx))
                        set_work_bit_irqsave(ctx);
                s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
-               s5p_mfc_wait_for_done_ctx(ctx,
-                                       S5P_MFC_R2H_CMD_INIT_BUFFERS_RET, 0);
+               s5p_mfc_wait_for_done_ctx(ctx, S5P_MFC_R2H_CMD_INIT_BUFFERS_RET,
+                                         0);
+       } else {
+               mfc_err("Buffers have already been requested\n");
+               ret = -EINVAL;
        }
+out:
+       s5p_mfc_clock_off();
+       if (ret)
+               mfc_err("Failed allocating buffers for CAPTURE queue\n");
        return ret;
 }
 
+/* Reqeust buffers */
+static int vidioc_reqbufs(struct file *file, void *priv,
+                                         struct v4l2_requestbuffers *reqbufs)
+{
+       struct s5p_mfc_dev *dev = video_drvdata(file);
+       struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+       if (reqbufs->memory != V4L2_MEMORY_MMAP) {
+               mfc_err("Only V4L2_MEMORY_MAP is supported\n");
+               return -EINVAL;
+       }
+
+       if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               return reqbufs_output(dev, ctx, reqbufs);
+       } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               return reqbufs_capture(dev, ctx, reqbufs);
+       } else {
+               mfc_err("Invalid type requested\n");
+               return -EINVAL;
+       }
+}
+
 /* Query buffer */
 static int vidioc_querybuf(struct file *file, void *priv,
                                                   struct v4l2_buffer *buf)
@@ -573,7 +617,7 @@ static int vidioc_querybuf(struct file *file, void *priv,
                return -EINVAL;
        }
        mfc_debug(2, "State: %d, buf->type: %d\n", ctx->state, buf->type);
-       if (ctx->state == MFCINST_INIT &&
+       if (ctx->state == MFCINST_GOT_INST &&
                        buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
                ret = vb2_querybuf(&ctx->vq_src, buf);
        } else if (ctx->state == MFCINST_RUNNING &&
@@ -649,39 +693,11 @@ static int vidioc_streamon(struct file *file, void *priv,
                           enum v4l2_buf_type type)
 {
        struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
-       struct s5p_mfc_dev *dev = ctx->dev;
        int ret = -EINVAL;
 
        mfc_debug_enter();
-       if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
-
-               if (ctx->state == MFCINST_INIT) {
-                       ctx->dst_bufs_cnt = 0;
-                       ctx->src_bufs_cnt = 0;
-                       ctx->capture_state = QUEUE_FREE;
-                       ctx->output_state = QUEUE_FREE;
-                       s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer,
-                                       ctx);
-                       s5p_mfc_hw_call(dev->mfc_ops, alloc_dec_temp_buffers,
-                                       ctx);
-                       set_work_bit_irqsave(ctx);
-                       s5p_mfc_clean_ctx_int_flags(ctx);
-                       s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
-
-                       if (s5p_mfc_wait_for_done_ctx(ctx,
-                               S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
-                               /* Error or timeout */
-                               mfc_err("Error getting instance from hardware\n");
-                               s5p_mfc_hw_call(dev->mfc_ops,
-                                               release_instance_buffer, ctx);
-                               s5p_mfc_hw_call(dev->mfc_ops,
-                                               release_dec_desc_buffer, ctx);
-                               return -EIO;
-                       }
-                       mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
-               }
+       if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
                ret = vb2_streamon(&ctx->vq_src, type);
-               }
        else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
                ret = vb2_streamon(&ctx->vq_dst, type);
        mfc_debug_leave();
@@ -851,6 +867,8 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh,
        switch (sub->type) {
        case V4L2_EVENT_EOS:
                return v4l2_event_subscribe(fh, sub, 2, NULL);
+       case V4L2_EVENT_SOURCE_CHANGE:
+               return v4l2_src_change_event_subscribe(fh, sub);
        default:
                return -EINVAL;
        }
@@ -1027,7 +1045,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
        return 0;
 }
 
-static int s5p_mfc_stop_streaming(struct vb2_queue *q)
+static void s5p_mfc_stop_streaming(struct vb2_queue *q)
 {
        unsigned long flags;
        struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
@@ -1071,7 +1089,6 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q)
        }
        if (aborted)
                ctx->state = MFCINST_RUNNING;
-       return 0;
 }
 
 
@@ -1191,9 +1208,14 @@ void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx)
 void s5p_mfc_dec_init(struct s5p_mfc_ctx *ctx)
 {
        struct v4l2_format f;
-       f.fmt.pix_mp.pixelformat = DEF_SRC_FMT_DEC;
+       f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
        ctx->src_fmt = find_format(&f, MFC_FMT_DEC);
-       f.fmt.pix_mp.pixelformat = DEF_DST_FMT_DEC;
+       if (IS_MFCV8(ctx->dev))
+               f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
+       else if (IS_MFCV6_PLUS(ctx->dev))
+               f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT_16X16;
+       else
+               f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT;
        ctx->dst_fmt = find_format(&f, MFC_FMT_RAW);
        mfc_debug(2, "Default src_fmt is %x, dest_fmt is %x\n",
                        (unsigned int)ctx->src_fmt, (unsigned int)ctx->dst_fmt);
index df83cd1..d26b248 100644 (file)
@@ -26,6 +26,7 @@
 #include <media/v4l2-ctrls.h>
 #include <media/videobuf2-core.h>
 #include "s5p_mfc_common.h"
+#include "s5p_mfc_ctrl.h"
 #include "s5p_mfc_debug.h"
 #include "s5p_mfc_enc.h"
 #include "s5p_mfc_intr.h"
@@ -41,6 +42,7 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_NONE,
                .type           = MFC_FMT_RAW,
                .num_planes     = 2,
+               .versions       = MFC_V6_BIT | MFC_V7_BIT,
        },
        {
                .name           = "4:2:0 2 Planes 64x32 Tiles",
@@ -48,6 +50,7 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_NONE,
                .type           = MFC_FMT_RAW,
                .num_planes     = 2,
+               .versions       = MFC_V5_BIT,
        },
        {
                .name           = "4:2:0 2 Planes Y/CbCr",
@@ -55,6 +58,8 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_NONE,
                .type           = MFC_FMT_RAW,
                .num_planes     = 2,
+               .versions       = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+                                                               MFC_V8_BIT,
        },
        {
                .name           = "4:2:0 2 Planes Y/CrCb",
@@ -62,6 +67,8 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_NONE,
                .type           = MFC_FMT_RAW,
                .num_planes     = 2,
+               .versions       = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+                                                               MFC_V8_BIT,
        },
        {
                .name           = "H264 Encoded Stream",
@@ -69,6 +76,8 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_H264_ENC,
                .type           = MFC_FMT_ENC,
                .num_planes     = 1,
+               .versions       = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+                                                               MFC_V8_BIT,
        },
        {
                .name           = "MPEG4 Encoded Stream",
@@ -76,6 +85,8 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_MPEG4_ENC,
                .type           = MFC_FMT_ENC,
                .num_planes     = 1,
+               .versions       = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+                                                               MFC_V8_BIT,
        },
        {
                .name           = "H263 Encoded Stream",
@@ -83,6 +94,8 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_H263_ENC,
                .type           = MFC_FMT_ENC,
                .num_planes     = 1,
+               .versions       = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+                                                               MFC_V8_BIT,
        },
        {
                .name           = "VP8 Encoded Stream",
@@ -90,6 +103,7 @@ static struct s5p_mfc_fmt formats[] = {
                .codec_mode     = S5P_MFC_CODEC_VP8_ENC,
                .type           = MFC_FMT_ENC,
                .num_planes     = 1,
+               .versions       = MFC_V7_BIT | MFC_V8_BIT,
        },
 };
 
@@ -772,13 +786,16 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
 
        if (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) {
                spin_lock_irqsave(&dev->irqlock, flags);
-               dst_mb = list_entry(ctx->dst_queue.next,
-                               struct s5p_mfc_buf, list);
-               list_del(&dst_mb->list);
-               ctx->dst_queue_cnt--;
-               vb2_set_plane_payload(dst_mb->b, 0,
-                       s5p_mfc_hw_call(dev->mfc_ops, get_enc_strm_size, dev));
-               vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE);
+               if (!list_empty(&ctx->dst_queue)) {
+                       dst_mb = list_entry(ctx->dst_queue.next,
+                                       struct s5p_mfc_buf, list);
+                       list_del(&dst_mb->list);
+                       ctx->dst_queue_cnt--;
+                       vb2_set_plane_payload(dst_mb->b, 0,
+                               s5p_mfc_hw_call(dev->mfc_ops, get_enc_strm_size,
+                                               dev));
+                       vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE);
+               }
                spin_unlock_irqrestore(&dev->irqlock, flags);
        }
 
@@ -883,8 +900,7 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
                mfc_debug(2, "enc src count: %d, enc ref count: %d\n",
                          ctx->src_queue_cnt, ctx->ref_queue_cnt);
        }
-       if (strm_size > 0) {
-               /* at least one more dest. buffers exist always  */
+       if ((ctx->dst_queue_cnt > 0) && (strm_size > 0)) {
                mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf,
                                                                        list);
                list_del(&mb_entry->list);
@@ -937,8 +953,10 @@ static int vidioc_querycap(struct file *file, void *priv,
        return 0;
 }
 
-static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
+                                                       bool mplane, bool out)
 {
+       struct s5p_mfc_dev *dev = video_drvdata(file);
        struct s5p_mfc_fmt *fmt;
        int i, j = 0;
 
@@ -951,6 +969,9 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
                        continue;
                else if (!out && formats[i].type != MFC_FMT_ENC)
                        continue;
+               else if ((dev->variant->version_bit & formats[i].versions) == 0)
+                       continue;
+
                if (j == f->index) {
                        fmt = &formats[i];
                        strlcpy(f->description, fmt->name,
@@ -966,25 +987,25 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
 static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
                                   struct v4l2_fmtdesc *f)
 {
-       return vidioc_enum_fmt(f, false, false);
+       return vidioc_enum_fmt(file, f, false, false);
 }
 
 static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
                                          struct v4l2_fmtdesc *f)
 {
-       return vidioc_enum_fmt(f, true, false);
+       return vidioc_enum_fmt(file, f, true, false);
 }
 
 static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
                                   struct v4l2_fmtdesc *f)
 {
-       return vidioc_enum_fmt(f, false, true);
+       return vidioc_enum_fmt(file, f, false, true);
 }
 
 static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
                                          struct v4l2_fmtdesc *f)
 {
-       return vidioc_enum_fmt(f, true, true);
+       return vidioc_enum_fmt(file, f, true, true);
 }
 
 static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
@@ -1035,16 +1056,14 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
                        mfc_err("failed to try output format\n");
                        return -EINVAL;
                }
-
-               if (!IS_MFCV7(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) {
-                       mfc_err("VP8 is supported only in MFC v7\n");
-                       return -EINVAL;
-               }
-
                if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {
                        mfc_err("must be set encoding output size\n");
                        return -EINVAL;
                }
+               if ((dev->variant->version_bit & fmt->versions) == 0) {
+                       mfc_err("Unsupported format by this MFC version.\n");
+                       return -EINVAL;
+               }
 
                pix_fmt_mp->plane_fmt[0].bytesperline =
                        pix_fmt_mp->plane_fmt[0].sizeimage;
@@ -1055,22 +1074,15 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
                        return -EINVAL;
                }
 
-               if (!IS_MFCV6_PLUS(dev)) {
-                       if (fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) {
-                               mfc_err("Not supported format.\n");
-                               return -EINVAL;
-                       }
-               } else if (IS_MFCV6_PLUS(dev)) {
-                       if (fmt->fourcc == V4L2_PIX_FMT_NV12MT) {
-                               mfc_err("Not supported format.\n");
-                               return -EINVAL;
-                       }
-               }
-
                if (fmt->num_planes != pix_fmt_mp->num_planes) {
                        mfc_err("failed to try output format\n");
                        return -EINVAL;
                }
+               if ((dev->variant->version_bit & fmt->versions) == 0) {
+                       mfc_err("Unsupported format by this MFC version.\n");
+                       return -EINVAL;
+               }
+
                v4l_bound_align_image(&pix_fmt_mp->width, 8, 1920, 1,
                        &pix_fmt_mp->height, 4, 1080, 1, 0);
        } else {
@@ -1104,20 +1116,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
                pix_fmt_mp->plane_fmt[0].bytesperline = 0;
                ctx->dst_bufs_cnt = 0;
                ctx->capture_state = QUEUE_FREE;
-               s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer, ctx);
-               set_work_bit_irqsave(ctx);
-               s5p_mfc_clean_ctx_int_flags(ctx);
-               s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
-               if (s5p_mfc_wait_for_done_ctx(ctx, \
-                               S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 1)) {
-                               /* Error or timeout */
-                       mfc_err("Error getting instance from hardware\n");
-                       s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer,
-                                       ctx);
-                       ret = -EIO;
-                       goto out;
-               }
-               mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+               ret = s5p_mfc_open_mfc_inst(dev, ctx);
        } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
                /* src_fmt is validated by call to vidioc_try_fmt */
                ctx->src_fmt = find_format(f, MFC_FMT_RAW);
@@ -1138,7 +1137,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
                ctx->output_state = QUEUE_FREE;
        } else {
                mfc_err("invalid buf type\n");
-               return -EINVAL;
+               ret = -EINVAL;
        }
 out:
        mfc_debug_leave();
@@ -1954,7 +1953,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
        return 0;
 }
 
-static int s5p_mfc_stop_streaming(struct vb2_queue *q)
+static void s5p_mfc_stop_streaming(struct vb2_queue *q)
 {
        unsigned long flags;
        struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
@@ -1983,7 +1982,6 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q)
                ctx->src_queue_cnt = 0;
        }
        spin_unlock_irqrestore(&dev->irqlock, flags);
-       return 0;
 }
 
 static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
index 3c01c33..c9a2274 100644 (file)
@@ -31,6 +31,12 @@ void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev)
        dev->mfc_ops = s5p_mfc_ops;
 }
 
+void s5p_mfc_init_regs(struct s5p_mfc_dev *dev)
+{
+       if (IS_MFCV6_PLUS(dev))
+               dev->mfc_regs = s5p_mfc_init_regs_v6_plus(dev);
+}
+
 int s5p_mfc_alloc_priv_buf(struct device *dev,
                                        struct s5p_mfc_priv_buf *b)
 {
index 754c540..7a7ad32 100644 (file)
 
 #include "s5p_mfc_common.h"
 
+struct s5p_mfc_regs {
+
+       /* codec common registers */
+       void *risc_on;
+       void *risc2host_int;
+       void *host2risc_int;
+       void *risc_base_address;
+       void *mfc_reset;
+       void *host2risc_command;
+       void *risc2host_command;
+       void *mfc_bus_reset_ctrl;
+       void *firmware_version;
+       void *instance_id;
+       void *codec_type;
+       void *context_mem_addr;
+       void *context_mem_size;
+       void *pixel_format;
+       void *metadata_enable;
+       void *mfc_version;
+       void *dbg_info_enable;
+       void *dbg_buffer_addr;
+       void *dbg_buffer_size;
+       void *hed_control;
+       void *mfc_timeout_value;
+       void *hed_shared_mem_addr;
+       void *dis_shared_mem_addr;/* only v7 */
+       void *ret_instance_id;
+       void *error_code;
+       void *dbg_buffer_output_size;
+       void *metadata_status;
+       void *metadata_addr_mb_info;
+       void *metadata_size_mb_info;
+       void *dbg_info_stage_counter;
+
+       /* decoder registers */
+       void *d_crc_ctrl;
+       void *d_dec_options;
+       void *d_display_delay;
+       void *d_set_frame_width;
+       void *d_set_frame_height;
+       void *d_sei_enable;
+       void *d_min_num_dpb;
+       void *d_min_first_plane_dpb_size;
+       void *d_min_second_plane_dpb_size;
+       void *d_min_third_plane_dpb_size;/* only v8 */
+       void *d_min_num_mv;
+       void *d_mvc_num_views;
+       void *d_min_num_dis;/* only v7 */
+       void *d_min_first_dis_size;/* only v7 */
+       void *d_min_second_dis_size;/* only v7 */
+       void *d_min_third_dis_size;/* only v7 */
+       void *d_post_filter_luma_dpb0;/*  v7 and v8 */
+       void *d_post_filter_luma_dpb1;/* v7 and v8 */
+       void *d_post_filter_luma_dpb2;/* only v7 */
+       void *d_post_filter_chroma_dpb0;/* v7 and v8 */
+       void *d_post_filter_chroma_dpb1;/* v7 and v8 */
+       void *d_post_filter_chroma_dpb2;/* only v7 */
+       void *d_num_dpb;
+       void *d_num_mv;
+       void *d_init_buffer_options;
+       void *d_first_plane_dpb_stride_size;/* only v8 */
+       void *d_second_plane_dpb_stride_size;/* only v8 */
+       void *d_third_plane_dpb_stride_size;/* only v8 */
+       void *d_first_plane_dpb_size;
+       void *d_second_plane_dpb_size;
+       void *d_third_plane_dpb_size;/* only v8 */
+       void *d_mv_buffer_size;
+       void *d_first_plane_dpb;
+       void *d_second_plane_dpb;
+       void *d_third_plane_dpb;
+       void *d_mv_buffer;
+       void *d_scratch_buffer_addr;
+       void *d_scratch_buffer_size;
+       void *d_metadata_buffer_addr;
+       void *d_metadata_buffer_size;
+       void *d_nal_start_options;/* v7 and v8 */
+       void *d_cpb_buffer_addr;
+       void *d_cpb_buffer_size;
+       void *d_available_dpb_flag_upper;
+       void *d_available_dpb_flag_lower;
+       void *d_cpb_buffer_offset;
+       void *d_slice_if_enable;
+       void *d_picture_tag;
+       void *d_stream_data_size;
+       void *d_dynamic_dpb_flag_upper;/* v7 and v8 */
+       void *d_dynamic_dpb_flag_lower;/* v7 and v8 */
+       void *d_display_frame_width;
+       void *d_display_frame_height;
+       void *d_display_status;
+       void *d_display_first_plane_addr;
+       void *d_display_second_plane_addr;
+       void *d_display_third_plane_addr;/* only v8 */
+       void *d_display_frame_type;
+       void *d_display_crop_info1;
+       void *d_display_crop_info2;
+       void *d_display_picture_profile;
+       void *d_display_luma_crc;/* v7 and v8 */
+       void *d_display_chroma0_crc;/* v7 and v8 */
+       void *d_display_chroma1_crc;/* only v8 */
+       void *d_display_luma_crc_top;/* only v6 */
+       void *d_display_chroma_crc_top;/* only v6 */
+       void *d_display_luma_crc_bot;/* only v6 */
+       void *d_display_chroma_crc_bot;/* only v6 */
+       void *d_display_aspect_ratio;
+       void *d_display_extended_ar;
+       void *d_decoded_frame_width;
+       void *d_decoded_frame_height;
+       void *d_decoded_status;
+       void *d_decoded_first_plane_addr;
+       void *d_decoded_second_plane_addr;
+       void *d_decoded_third_plane_addr;/* only v8 */
+       void *d_decoded_frame_type;
+       void *d_decoded_crop_info1;
+       void *d_decoded_crop_info2;
+       void *d_decoded_picture_profile;
+       void *d_decoded_nal_size;
+       void *d_decoded_luma_crc;
+       void *d_decoded_chroma0_crc;
+       void *d_decoded_chroma1_crc;/* only v8 */
+       void *d_ret_picture_tag_top;
+       void *d_ret_picture_tag_bot;
+       void *d_ret_picture_time_top;
+       void *d_ret_picture_time_bot;
+       void *d_chroma_format;
+       void *d_vc1_info;/* v7 and v8 */
+       void *d_mpeg4_info;
+       void *d_h264_info;
+       void *d_metadata_addr_concealed_mb;
+       void *d_metadata_size_concealed_mb;
+       void *d_metadata_addr_vc1_param;
+       void *d_metadata_size_vc1_param;
+       void *d_metadata_addr_sei_nal;
+       void *d_metadata_size_sei_nal;
+       void *d_metadata_addr_vui;
+       void *d_metadata_size_vui;
+       void *d_metadata_addr_mvcvui;/* v7 and v8 */
+       void *d_metadata_size_mvcvui;/* v7 and v8 */
+       void *d_mvc_view_id;
+       void *d_frame_pack_sei_avail;
+       void *d_frame_pack_arrgment_id;
+       void *d_frame_pack_sei_info;
+       void *d_frame_pack_grid_pos;
+       void *d_display_recovery_sei_info;/* v7 and v8 */
+       void *d_decoded_recovery_sei_info;/* v7 and v8 */
+       void *d_display_first_addr;/* only v7 */
+       void *d_display_second_addr;/* only v7 */
+       void *d_display_third_addr;/* only v7 */
+       void *d_decoded_first_addr;/* only v7 */
+       void *d_decoded_second_addr;/* only v7 */
+       void *d_decoded_third_addr;/* only v7 */
+       void *d_used_dpb_flag_upper;/* v7 and v8 */
+       void *d_used_dpb_flag_lower;/* v7 and v8 */
+
+       /* encoder registers */
+       void *e_frame_width;
+       void *e_frame_height;
+       void *e_cropped_frame_width;
+       void *e_cropped_frame_height;
+       void *e_frame_crop_offset;
+       void *e_enc_options;
+       void *e_picture_profile;
+       void *e_vbv_buffer_size;
+       void *e_vbv_init_delay;
+       void *e_fixed_picture_qp;
+       void *e_rc_config;
+       void *e_rc_qp_bound;
+       void *e_rc_qp_bound_pb;/* v7 and v8 */
+       void *e_rc_mode;
+       void *e_mb_rc_config;
+       void *e_padding_ctrl;
+       void *e_air_threshold;
+       void *e_mv_hor_range;
+       void *e_mv_ver_range;
+       void *e_num_dpb;
+       void *e_luma_dpb;
+       void *e_chroma_dpb;
+       void *e_me_buffer;
+       void *e_scratch_buffer_addr;
+       void *e_scratch_buffer_size;
+       void *e_tmv_buffer0;
+       void *e_tmv_buffer1;
+       void *e_ir_buffer_addr;/* v7 and v8 */
+       void *e_source_first_plane_addr;
+       void *e_source_second_plane_addr;
+       void *e_source_third_plane_addr;/* v7 and v8 */
+       void *e_source_first_plane_stride;/* v7 and v8 */
+       void *e_source_second_plane_stride;/* v7 and v8 */
+       void *e_source_third_plane_stride;/* v7 and v8 */
+       void *e_stream_buffer_addr;
+       void *e_stream_buffer_size;
+       void *e_roi_buffer_addr;
+       void *e_param_change;
+       void *e_ir_size;
+       void *e_gop_config;
+       void *e_mslice_mode;
+       void *e_mslice_size_mb;
+       void *e_mslice_size_bits;
+       void *e_frame_insertion;
+       void *e_rc_frame_rate;
+       void *e_rc_bit_rate;
+       void *e_rc_roi_ctrl;
+       void *e_picture_tag;
+       void *e_bit_count_enable;
+       void *e_max_bit_count;
+       void *e_min_bit_count;
+       void *e_metadata_buffer_addr;
+       void *e_metadata_buffer_size;
+       void *e_encoded_source_first_plane_addr;
+       void *e_encoded_source_second_plane_addr;
+       void *e_encoded_source_third_plane_addr;/* v7 and v8 */
+       void *e_stream_size;
+       void *e_slice_type;
+       void *e_picture_count;
+       void *e_ret_picture_tag;
+       void *e_stream_buffer_write_pointer; /*  only v6 */
+       void *e_recon_luma_dpb_addr;
+       void *e_recon_chroma_dpb_addr;
+       void *e_metadata_addr_enc_slice;
+       void *e_metadata_size_enc_slice;
+       void *e_mpeg4_options;
+       void *e_mpeg4_hec_period;
+       void *e_aspect_ratio;
+       void *e_extended_sar;
+       void *e_h264_options;
+       void *e_h264_options_2;/* v7 and v8 */
+       void *e_h264_lf_alpha_offset;
+       void *e_h264_lf_beta_offset;
+       void *e_h264_i_period;
+       void *e_h264_fmo_slice_grp_map_type;
+       void *e_h264_fmo_num_slice_grp_minus1;
+       void *e_h264_fmo_slice_grp_change_dir;
+       void *e_h264_fmo_slice_grp_change_rate_minus1;
+       void *e_h264_fmo_run_length_minus1_0;
+       void *e_h264_aso_slice_order_0;
+       void *e_h264_chroma_qp_offset;
+       void *e_h264_num_t_layer;
+       void *e_h264_hierarchical_qp_layer0;
+       void *e_h264_frame_packing_sei_info;
+       void *e_h264_nal_control;/* v7 and v8 */
+       void *e_mvc_frame_qp_view1;
+       void *e_mvc_rc_bit_rate_view1;
+       void *e_mvc_rc_qbound_view1;
+       void *e_mvc_rc_mode_view1;
+       void *e_mvc_inter_view_prediction_on;
+       void *e_vp8_options;/* v7 and v8 */
+       void *e_vp8_filter_options;/* v7 and v8 */
+       void *e_vp8_golden_frame_option;/* v7 and v8 */
+       void *e_vp8_num_t_layer;/* v7 and v8 */
+       void *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */
+       void *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */
+       void *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */
+};
+
 struct s5p_mfc_hw_ops {
        int (*alloc_dec_temp_buffers)(struct s5p_mfc_ctx *ctx);
        void (*release_dec_desc_buffer)(struct s5p_mfc_ctx *ctx);
@@ -80,6 +333,7 @@ struct s5p_mfc_hw_ops {
 };
 
 void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev);
+void s5p_mfc_init_regs(struct s5p_mfc_dev *dev);
 int s5p_mfc_alloc_priv_buf(struct device *dev,
                                        struct s5p_mfc_priv_buf *b);
 void s5p_mfc_release_priv_buf(struct device *dev,
index f64621a..4f5e0ea 100644 (file)
        } while (0)
 #endif /* S5P_MFC_DEBUG_REGWRITE */
 
-#define READL(offset)          readl(dev->regs_base + (offset))
-#define WRITEL(data, offset)   writel((data), dev->regs_base + (offset))
-#define OFFSETA(x)             (((x) - dev->port_a) >> S5P_FIMV_MEM_OFFSET)
-#define OFFSETB(x)             (((x) - dev->port_b) >> S5P_FIMV_MEM_OFFSET)
+#define READL(reg) \
+       (WARN_ON_ONCE(!(reg)) ? 0 : readl(reg))
+#define WRITEL(data, reg) \
+       (WARN_ON_ONCE(!(reg)) ? 0 : writel((data), (reg)))
 
 /* Allocate temporary buffers for decoding */
 static int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx)
@@ -77,7 +77,12 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
                          ctx->luma_size, ctx->chroma_size, ctx->mv_size);
                mfc_debug(2, "Totals bufs: %d\n", ctx->total_dpb_count);
        } else if (ctx->type == MFCINST_ENCODER) {
-               ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 *
+               if (IS_MFCV8(dev))
+                       ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 *
+                       ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V8(mb_width, mb_height),
+                       S5P_FIMV_TMV_BUFFER_ALIGN_V6);
+               else
+                       ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 *
                        ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V6(mb_width, mb_height),
                        S5P_FIMV_TMV_BUFFER_ALIGN_V6);
 
@@ -87,10 +92,16 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
                ctx->chroma_dpb_size = ALIGN((mb_width * mb_height) *
                                S5P_FIMV_CHROMA_MB_TO_PIXEL_V6,
                                S5P_FIMV_CHROMA_DPB_BUFFER_ALIGN_V6);
-               ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V6(
-                                       ctx->img_width, ctx->img_height,
-                                       mb_width, mb_height),
-                                       S5P_FIMV_ME_BUFFER_ALIGN_V6);
+               if (IS_MFCV8(dev))
+                       ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V8(
+                                               ctx->img_width, ctx->img_height,
+                                               mb_width, mb_height),
+                                               S5P_FIMV_ME_BUFFER_ALIGN_V6);
+               else
+                       ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V6(
+                                               ctx->img_width, ctx->img_height,
+                                               mb_width, mb_height),
+                                               S5P_FIMV_ME_BUFFER_ALIGN_V6);
 
                mfc_debug(2, "recon luma size: %d chroma size: %d\n",
                          ctx->luma_dpb_size, ctx->chroma_dpb_size);
@@ -102,8 +113,14 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
        switch (ctx->codec_mode) {
        case S5P_MFC_CODEC_H264_DEC:
        case S5P_MFC_CODEC_H264_MVC_DEC:
-               ctx->scratch_buf_size =
-                       S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6(
+               if (IS_MFCV8(dev))
+                       ctx->scratch_buf_size =
+                               S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V8(
+                                       mb_width,
+                                       mb_height);
+               else
+                       ctx->scratch_buf_size =
+                               S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6(
                                        mb_width,
                                        mb_height);
                ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
@@ -113,7 +130,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
                        (ctx->mv_count * ctx->mv_size);
                break;
        case S5P_MFC_CODEC_MPEG4_DEC:
-               if (IS_MFCV7(dev)) {
+               if (IS_MFCV7_PLUS(dev)) {
                        ctx->scratch_buf_size =
                                S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V7(
                                                mb_width,
@@ -153,19 +170,31 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
                ctx->bank1.size = ctx->scratch_buf_size;
                break;
        case S5P_MFC_CODEC_VP8_DEC:
-               ctx->scratch_buf_size =
-                       S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V6(
-                                       mb_width,
-                                       mb_height);
+               if (IS_MFCV8(dev))
+                       ctx->scratch_buf_size =
+                               S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V8(
+                                               mb_width,
+                                               mb_height);
+               else
+                       ctx->scratch_buf_size =
+                               S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V6(
+                                               mb_width,
+                                               mb_height);
                ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
                                S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
                ctx->bank1.size = ctx->scratch_buf_size;
                break;
        case S5P_MFC_CODEC_H264_ENC:
-               ctx->scratch_buf_size =
-                       S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V6(
+               if (IS_MFCV8(dev))
+                       ctx->scratch_buf_size =
+                               S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V8(
                                        mb_width,
                                        mb_height);
+               else
+                       ctx->scratch_buf_size =
+                               S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V6(
+                                               mb_width,
+                                               mb_height);
                ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
                                S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
                ctx->bank1.size =
@@ -189,10 +218,16 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
                ctx->bank2.size = 0;
                break;
        case S5P_MFC_CODEC_VP8_ENC:
-               ctx->scratch_buf_size =
-                       S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7(
+               if (IS_MFCV8(dev))
+                       ctx->scratch_buf_size =
+                               S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V8(
                                        mb_width,
                                        mb_height);
+               else
+                       ctx->scratch_buf_size =
+                               S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7(
+                                               mb_width,
+                                               mb_height);
                ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
                                S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
                ctx->bank1.size =
@@ -332,6 +367,12 @@ static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx)
 
        ctx->luma_size = calc_plane(ctx->img_width, ctx->img_height);
        ctx->chroma_size = calc_plane(ctx->img_width, (ctx->img_height >> 1));
+       if (IS_MFCV8(ctx->dev)) {
+               /* MFCv8 needs additional 64 bytes for luma,chroma dpb*/
+               ctx->luma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8;
+               ctx->chroma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8;
+       }
+
        if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||
                        ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) {
                ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V6(ctx->img_width,
@@ -354,7 +395,7 @@ static void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx)
        ctx->chroma_size = ALIGN((mb_width * mb_height) * 128, 256);
 
        /* MFCv7 needs pad bytes for Luma and Chroma */
-       if (IS_MFCV7(ctx->dev)) {
+       if (IS_MFCV7_PLUS(ctx->dev)) {
                ctx->luma_size += MFC_LUMA_PAD_BYTES_V7;
                ctx->chroma_size += MFC_CHROMA_PAD_BYTES_V7;
        }
@@ -366,16 +407,17 @@ static int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx,
                        unsigned int strm_size)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
        struct s5p_mfc_buf_size *buf_size = dev->variant->buf_size;
 
        mfc_debug_enter();
        mfc_debug(2, "inst_no: %d, buf_addr: 0x%08x,\n"
                "buf_size: 0x%08x (%d)\n",
                ctx->inst_no, buf_addr, strm_size, strm_size);
-       WRITEL(strm_size, S5P_FIMV_D_STREAM_DATA_SIZE_V6);
-       WRITEL(buf_addr, S5P_FIMV_D_CPB_BUFFER_ADDR_V6);
-       WRITEL(buf_size->cpb, S5P_FIMV_D_CPB_BUFFER_SIZE_V6);
-       WRITEL(start_num_byte, S5P_FIMV_D_CPB_BUFFER_OFFSET_V6);
+       WRITEL(strm_size, mfc_regs->d_stream_data_size);
+       WRITEL(buf_addr, mfc_regs->d_cpb_buffer_addr);
+       WRITEL(buf_size->cpb, mfc_regs->d_cpb_buffer_size);
+       WRITEL(start_num_byte, mfc_regs->d_cpb_buffer_offset);
 
        mfc_debug_leave();
        return 0;
@@ -387,6 +429,7 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
        unsigned int frame_size, i;
        unsigned int frame_size_ch, frame_size_mv;
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
        size_t buf_addr1;
        int buf_size1;
        int align_gap;
@@ -398,19 +441,27 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
        mfc_debug(2, "Total DPB COUNT: %d\n", ctx->total_dpb_count);
        mfc_debug(2, "Setting display delay to %d\n", ctx->display_delay);
 
-       WRITEL(ctx->total_dpb_count, S5P_FIMV_D_NUM_DPB_V6);
-       WRITEL(ctx->luma_size, S5P_FIMV_D_LUMA_DPB_SIZE_V6);
-       WRITEL(ctx->chroma_size, S5P_FIMV_D_CHROMA_DPB_SIZE_V6);
+       WRITEL(ctx->total_dpb_count, mfc_regs->d_num_dpb);
+       WRITEL(ctx->luma_size, mfc_regs->d_first_plane_dpb_size);
+       WRITEL(ctx->chroma_size, mfc_regs->d_second_plane_dpb_size);
+
+       WRITEL(buf_addr1, mfc_regs->d_scratch_buffer_addr);
+       WRITEL(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size);
+
+       if (IS_MFCV8(dev)) {
+               WRITEL(ctx->img_width,
+                       mfc_regs->d_first_plane_dpb_stride_size);
+               WRITEL(ctx->img_width,
+                       mfc_regs->d_second_plane_dpb_stride_size);
+       }
 
-       WRITEL(buf_addr1, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V6);
-       WRITEL(ctx->scratch_buf_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V6);
        buf_addr1 += ctx->scratch_buf_size;
        buf_size1 -= ctx->scratch_buf_size;
 
        if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
                        ctx->codec_mode == S5P_FIMV_CODEC_H264_MVC_DEC){
-               WRITEL(ctx->mv_size, S5P_FIMV_D_MV_BUFFER_SIZE_V6);
-               WRITEL(ctx->mv_count, S5P_FIMV_D_NUM_MV_V6);
+               WRITEL(ctx->mv_size, mfc_regs->d_mv_buffer_size);
+               WRITEL(ctx->mv_count, mfc_regs->d_num_mv);
        }
 
        frame_size = ctx->luma_size;
@@ -424,11 +475,11 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
                mfc_debug(2, "Luma %d: %x\n", i,
                                        ctx->dst_bufs[i].cookie.raw.luma);
                WRITEL(ctx->dst_bufs[i].cookie.raw.luma,
-                               S5P_FIMV_D_LUMA_DPB_V6 + i * 4);
+                               mfc_regs->d_first_plane_dpb + i * 4);
                mfc_debug(2, "\tChroma %d: %x\n", i,
                                        ctx->dst_bufs[i].cookie.raw.chroma);
                WRITEL(ctx->dst_bufs[i].cookie.raw.chroma,
-                               S5P_FIMV_D_CHROMA_DPB_V6 + i * 4);
+                               mfc_regs->d_second_plane_dpb + i * 4);
        }
        if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||
                        ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) {
@@ -441,7 +492,7 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
 
                        mfc_debug(2, "\tBuf1: %x, size: %d\n",
                                        buf_addr1, buf_size1);
-                       WRITEL(buf_addr1, S5P_FIMV_D_MV_BUFFER_V6 + i * 4);
+                       WRITEL(buf_addr1, mfc_regs->d_mv_buffer + i * 4);
                        buf_addr1 += frame_size_mv;
                        buf_size1 -= frame_size_mv;
                }
@@ -454,7 +505,7 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
                return -ENOMEM;
        }
 
-       WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+       WRITEL(ctx->inst_no, mfc_regs->instance_id);
        s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
                        S5P_FIMV_CH_INIT_BUFS_V6, NULL);
 
@@ -467,9 +518,10 @@ static int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx,
                unsigned long addr, unsigned int size)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
 
-       WRITEL(addr, S5P_FIMV_E_STREAM_BUFFER_ADDR_V6); /* 16B align */
-       WRITEL(size, S5P_FIMV_E_STREAM_BUFFER_SIZE_V6);
+       WRITEL(addr, mfc_regs->e_stream_buffer_addr); /* 16B align */
+       WRITEL(size, mfc_regs->e_stream_buffer_size);
 
        mfc_debug(2, "stream buf addr: 0x%08lx, size: 0x%d\n",
                  addr, size);
@@ -481,14 +533,10 @@ static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
                unsigned long y_addr, unsigned long c_addr)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
 
-       if (IS_MFCV7(dev)) {
-               WRITEL(y_addr, S5P_FIMV_E_SOURCE_FIRST_ADDR_V7);
-               WRITEL(c_addr, S5P_FIMV_E_SOURCE_SECOND_ADDR_V7);
-       } else {
-               WRITEL(y_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6);
-               WRITEL(c_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6);
-       }
+       WRITEL(y_addr, mfc_regs->e_source_first_plane_addr);
+       WRITEL(c_addr, mfc_regs->e_source_second_plane_addr);
 
        mfc_debug(2, "enc src y buf addr: 0x%08lx\n", y_addr);
        mfc_debug(2, "enc src c buf addr: 0x%08lx\n", c_addr);
@@ -498,18 +546,14 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
                unsigned long *y_addr, unsigned long *c_addr)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
        unsigned long enc_recon_y_addr, enc_recon_c_addr;
 
-       if (IS_MFCV7(dev)) {
-               *y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7);
-               *c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7);
-       } else {
-               *y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6);
-               *c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6);
-       }
+       *y_addr = READL(mfc_regs->e_encoded_source_first_plane_addr);
+       *c_addr = READL(mfc_regs->e_encoded_source_second_plane_addr);
 
-       enc_recon_y_addr = READL(S5P_FIMV_E_RECON_LUMA_DPB_ADDR_V6);
-       enc_recon_c_addr = READL(S5P_FIMV_E_RECON_CHROMA_DPB_ADDR_V6);
+       enc_recon_y_addr = READL(mfc_regs->e_recon_luma_dpb_addr);
+       enc_recon_c_addr = READL(mfc_regs->e_recon_chroma_dpb_addr);
 
        mfc_debug(2, "recon y addr: 0x%08lx\n", enc_recon_y_addr);
        mfc_debug(2, "recon c addr: 0x%08lx\n", enc_recon_c_addr);
@@ -519,6 +563,7 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
 static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
        size_t buf_addr1;
        int i, buf_size1;
 
@@ -530,24 +575,24 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)
        mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1);
 
        for (i = 0; i < ctx->pb_count; i++) {
-               WRITEL(buf_addr1, S5P_FIMV_E_LUMA_DPB_V6 + (4 * i));
+               WRITEL(buf_addr1, mfc_regs->e_luma_dpb + (4 * i));
                buf_addr1 += ctx->luma_dpb_size;
-               WRITEL(buf_addr1, S5P_FIMV_E_CHROMA_DPB_V6 + (4 * i));
+               WRITEL(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i));
                buf_addr1 += ctx->chroma_dpb_size;
-               WRITEL(buf_addr1, S5P_FIMV_E_ME_BUFFER_V6 + (4 * i));
+               WRITEL(buf_addr1, mfc_regs->e_me_buffer + (4 * i));
                buf_addr1 += ctx->me_buffer_size;
                buf_size1 -= (ctx->luma_dpb_size + ctx->chroma_dpb_size +
                        ctx->me_buffer_size);
        }
 
-       WRITEL(buf_addr1, S5P_FIMV_E_SCRATCH_BUFFER_ADDR_V6);
-       WRITEL(ctx->scratch_buf_size, S5P_FIMV_E_SCRATCH_BUFFER_SIZE_V6);
+       WRITEL(buf_addr1, mfc_regs->e_scratch_buffer_addr);
+       WRITEL(ctx->scratch_buf_size, mfc_regs->e_scratch_buffer_size);
        buf_addr1 += ctx->scratch_buf_size;
        buf_size1 -= ctx->scratch_buf_size;
 
-       WRITEL(buf_addr1, S5P_FIMV_E_TMV_BUFFER0_V6);
+       WRITEL(buf_addr1, mfc_regs->e_tmv_buffer0);
        buf_addr1 += ctx->tmv_buffer_size >> 1;
-       WRITEL(buf_addr1, S5P_FIMV_E_TMV_BUFFER1_V6);
+       WRITEL(buf_addr1, mfc_regs->e_tmv_buffer1);
        buf_addr1 += ctx->tmv_buffer_size >> 1;
        buf_size1 -= ctx->tmv_buffer_size;
 
@@ -558,7 +603,7 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)
                return -ENOMEM;
        }
 
-       WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+       WRITEL(ctx->inst_no, mfc_regs->instance_id);
        s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
                        S5P_FIMV_CH_INIT_BUFS_V6, NULL);
 
@@ -570,18 +615,19 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)
 static int s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
 
        /* multi-slice control */
        /* multi-slice MB number or bit size */
-       WRITEL(ctx->slice_mode, S5P_FIMV_E_MSLICE_MODE_V6);
+       WRITEL(ctx->slice_mode, mfc_regs->e_mslice_mode);
        if (ctx->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
-               WRITEL(ctx->slice_size.mb, S5P_FIMV_E_MSLICE_SIZE_MB_V6);
+               WRITEL(ctx->slice_size.mb, mfc_regs->e_mslice_size_mb);
        } else if (ctx->slice_mode ==
                        V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {
-               WRITEL(ctx->slice_size.bits, S5P_FIMV_E_MSLICE_SIZE_BITS_V6);
+               WRITEL(ctx->slice_size.bits, mfc_regs->e_mslice_size_bits);
        } else {
-               WRITEL(0x0, S5P_FIMV_E_MSLICE_SIZE_MB_V6);
-               WRITEL(0x0, S5P_FIMV_E_MSLICE_SIZE_BITS_V6);
+               WRITEL(0x0, mfc_regs->e_mslice_size_mb);
+               WRITEL(0x0, mfc_regs->e_mslice_size_bits);
        }
 
        return 0;
@@ -590,27 +636,28 @@ static int s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx)
 static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
        struct s5p_mfc_enc_params *p = &ctx->enc_params;
        unsigned int reg = 0;
 
        mfc_debug_enter();
 
        /* width */
-       WRITEL(ctx->img_width, S5P_FIMV_E_FRAME_WIDTH_V6); /* 16 align */
+       WRITEL(ctx->img_width, mfc_regs->e_frame_width); /* 16 align */
        /* height */
-       WRITEL(ctx->img_height, S5P_FIMV_E_FRAME_HEIGHT_V6); /* 16 align */
+       WRITEL(ctx->img_height, mfc_regs->e_frame_height); /* 16 align */
 
        /* cropped width */
-       WRITEL(ctx->img_width, S5P_FIMV_E_CROPPED_FRAME_WIDTH_V6);
+       WRITEL(ctx->img_width, mfc_regs->e_cropped_frame_width);
        /* cropped height */
-       WRITEL(ctx->img_height, S5P_FIMV_E_CROPPED_FRAME_HEIGHT_V6);
+       WRITEL(ctx->img_height, mfc_regs->e_cropped_frame_height);
        /* cropped offset */
-       WRITEL(0x0, S5P_FIMV_E_FRAME_CROP_OFFSET_V6);
+       WRITEL(0x0, mfc_regs->e_frame_crop_offset);
 
        /* pictype : IDR period */
        reg = 0;
        reg |= p->gop_size & 0xFFFF;
-       WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_gop_config);
 
        /* multi-slice control */
        /* multi-slice MB number or bit size */
@@ -618,65 +665,65 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
        reg = 0;
        if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
                reg |= (0x1 << 3);
-               WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+               WRITEL(reg, mfc_regs->e_enc_options);
                ctx->slice_size.mb = p->slice_mb;
        } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {
                reg |= (0x1 << 3);
-               WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+               WRITEL(reg, mfc_regs->e_enc_options);
                ctx->slice_size.bits = p->slice_bit;
        } else {
                reg &= ~(0x1 << 3);
-               WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+               WRITEL(reg, mfc_regs->e_enc_options);
        }
 
        s5p_mfc_set_slice_mode(ctx);
 
        /* cyclic intra refresh */
-       WRITEL(p->intra_refresh_mb, S5P_FIMV_E_IR_SIZE_V6);
-       reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+       WRITEL(p->intra_refresh_mb, mfc_regs->e_ir_size);
+       reg = READL(mfc_regs->e_enc_options);
        if (p->intra_refresh_mb == 0)
                reg &= ~(0x1 << 4);
        else
                reg |= (0x1 << 4);
-       WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_enc_options);
 
        /* 'NON_REFERENCE_STORE_ENABLE' for debugging */
-       reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+       reg = READL(mfc_regs->e_enc_options);
        reg &= ~(0x1 << 9);
-       WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_enc_options);
 
        /* memory structure cur. frame */
        if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) {
                /* 0: Linear, 1: 2D tiled*/
-               reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+               reg = READL(mfc_regs->e_enc_options);
                reg &= ~(0x1 << 7);
-               WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+               WRITEL(reg, mfc_regs->e_enc_options);
                /* 0: NV12(CbCr), 1: NV21(CrCb) */
-               WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6);
+               WRITEL(0x0, mfc_regs->pixel_format);
        } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV21M) {
                /* 0: Linear, 1: 2D tiled*/
-               reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+               reg = READL(mfc_regs->e_enc_options);
                reg &= ~(0x1 << 7);
-               WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+               WRITEL(reg, mfc_regs->e_enc_options);
                /* 0: NV12(CbCr), 1: NV21(CrCb) */
-               WRITEL(0x1, S5P_FIMV_PIXEL_FORMAT_V6);
+               WRITEL(0x1, mfc_regs->pixel_format);
        } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) {
                /* 0: Linear, 1: 2D tiled*/
-               reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+               reg = READL(mfc_regs->e_enc_options);
                reg |= (0x1 << 7);
-               WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+               WRITEL(reg, mfc_regs->e_enc_options);
                /* 0: NV12(CbCr), 1: NV21(CrCb) */
-               WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6);
+               WRITEL(0x0, mfc_regs->pixel_format);
        }
 
        /* memory structure recon. frame */
        /* 0: Linear, 1: 2D tiled */
-       reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+       reg = READL(mfc_regs->e_enc_options);
        reg |= (0x1 << 8);
-       WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_enc_options);
 
        /* padding control & value */
-       WRITEL(0x0, S5P_FIMV_E_PADDING_CTRL_V6);
+       WRITEL(0x0, mfc_regs->e_padding_ctrl);
        if (p->pad) {
                reg = 0;
                /** enable */
@@ -687,64 +734,64 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
                reg |= ((p->pad_cb & 0xFF) << 8);
                /** y value */
                reg |= p->pad_luma & 0xFF;
-               WRITEL(reg, S5P_FIMV_E_PADDING_CTRL_V6);
+               WRITEL(reg, mfc_regs->e_padding_ctrl);
        }
 
        /* rate control config. */
        reg = 0;
        /* frame-level rate control */
        reg |= ((p->rc_frame & 0x1) << 9);
-       WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_rc_config);
 
        /* bit rate */
        if (p->rc_frame)
                WRITEL(p->rc_bitrate,
-                       S5P_FIMV_E_RC_BIT_RATE_V6);
+                       mfc_regs->e_rc_bit_rate);
        else
-               WRITEL(1, S5P_FIMV_E_RC_BIT_RATE_V6);
+               WRITEL(1, mfc_regs->e_rc_bit_rate);
 
        /* reaction coefficient */
        if (p->rc_frame) {
                if (p->rc_reaction_coeff < TIGHT_CBR_MAX) /* tight CBR */
-                       WRITEL(1, S5P_FIMV_E_RC_RPARAM_V6);
+                       WRITEL(1, mfc_regs->e_rc_mode);
                else                                      /* loose CBR */
-                       WRITEL(2, S5P_FIMV_E_RC_RPARAM_V6);
+                       WRITEL(2, mfc_regs->e_rc_mode);
        }
 
        /* seq header ctrl */
-       reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+       reg = READL(mfc_regs->e_enc_options);
        reg &= ~(0x1 << 2);
        reg |= ((p->seq_hdr_mode & 0x1) << 2);
 
        /* frame skip mode */
        reg &= ~(0x3);
        reg |= (p->frame_skip_mode & 0x3);
-       WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_enc_options);
 
        /* 'DROP_CONTROL_ENABLE', disable */
-       reg = READL(S5P_FIMV_E_RC_CONFIG_V6);
+       reg = READL(mfc_regs->e_rc_config);
        reg &= ~(0x1 << 10);
-       WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_rc_config);
 
        /* setting for MV range [16, 256] */
        reg = (p->mv_h_range & S5P_FIMV_E_MV_RANGE_V6_MASK);
-       WRITEL(reg, S5P_FIMV_E_MV_HOR_RANGE_V6);
+       WRITEL(reg, mfc_regs->e_mv_hor_range);
 
        reg = (p->mv_v_range & S5P_FIMV_E_MV_RANGE_V6_MASK);
-       WRITEL(reg, S5P_FIMV_E_MV_VER_RANGE_V6);
+       WRITEL(reg, mfc_regs->e_mv_ver_range);
 
-       WRITEL(0x0, S5P_FIMV_E_FRAME_INSERTION_V6);
-       WRITEL(0x0, S5P_FIMV_E_ROI_BUFFER_ADDR_V6);
-       WRITEL(0x0, S5P_FIMV_E_PARAM_CHANGE_V6);
-       WRITEL(0x0, S5P_FIMV_E_RC_ROI_CTRL_V6);
-       WRITEL(0x0, S5P_FIMV_E_PICTURE_TAG_V6);
+       WRITEL(0x0, mfc_regs->e_frame_insertion);
+       WRITEL(0x0, mfc_regs->e_roi_buffer_addr);
+       WRITEL(0x0, mfc_regs->e_param_change);
+       WRITEL(0x0, mfc_regs->e_rc_roi_ctrl);
+       WRITEL(0x0, mfc_regs->e_picture_tag);
 
-       WRITEL(0x0, S5P_FIMV_E_BIT_COUNT_ENABLE_V6);
-       WRITEL(0x0, S5P_FIMV_E_MAX_BIT_COUNT_V6);
-       WRITEL(0x0, S5P_FIMV_E_MIN_BIT_COUNT_V6);
+       WRITEL(0x0, mfc_regs->e_bit_count_enable);
+       WRITEL(0x0, mfc_regs->e_max_bit_count);
+       WRITEL(0x0, mfc_regs->e_min_bit_count);
 
-       WRITEL(0x0, S5P_FIMV_E_METADATA_BUFFER_ADDR_V6);
-       WRITEL(0x0, S5P_FIMV_E_METADATA_BUFFER_SIZE_V6);
+       WRITEL(0x0, mfc_regs->e_metadata_buffer_addr);
+       WRITEL(0x0, mfc_regs->e_metadata_buffer_size);
 
        mfc_debug_leave();
 
@@ -754,6 +801,7 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
 static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
        struct s5p_mfc_enc_params *p = &ctx->enc_params;
        struct s5p_mfc_h264_enc_params *p_h264 = &p->codec.h264;
        unsigned int reg = 0;
@@ -764,10 +812,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
        s5p_mfc_set_enc_params(ctx);
 
        /* pictype : number of B */
-       reg = READL(S5P_FIMV_E_GOP_CONFIG_V6);
+       reg = READL(mfc_regs->e_gop_config);
        reg &= ~(0x3 << 16);
        reg |= ((p->num_b_frame & 0x3) << 16);
-       WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_gop_config);
 
        /* profile & level */
        reg = 0;
@@ -775,18 +823,19 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
        reg |= ((p_h264->level & 0xFF) << 8);
        /** profile - 0 ~ 3 */
        reg |= p_h264->profile & 0x3F;
-       WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6);
+       WRITEL(reg, mfc_regs->e_picture_profile);
 
        /* rate control config. */
-       reg = READL(S5P_FIMV_E_RC_CONFIG_V6);
+       reg = READL(mfc_regs->e_rc_config);
        /** macroblock level rate control */
        reg &= ~(0x1 << 8);
        reg |= ((p->rc_mb & 0x1) << 8);
-       WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_rc_config);
+
        /** frame QP */
        reg &= ~(0x3F);
        reg |= p_h264->rc_frame_qp & 0x3F;
-       WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_rc_config);
 
        /* max & min value of QP */
        reg = 0;
@@ -794,16 +843,16 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
        reg |= ((p_h264->rc_max_qp & 0x3F) << 8);
        /** min QP */
        reg |= p_h264->rc_min_qp & 0x3F;
-       WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6);
+       WRITEL(reg, mfc_regs->e_rc_qp_bound);
 
        /* other QPs */
-       WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+       WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
        if (!p->rc_frame && !p->rc_mb) {
                reg = 0;
                reg |= ((p_h264->rc_b_frame_qp & 0x3F) << 16);
                reg |= ((p_h264->rc_p_frame_qp & 0x3F) << 8);
                reg |= p_h264->rc_frame_qp & 0x3F;
-               WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+               WRITEL(reg, mfc_regs->e_fixed_picture_qp);
        }
 
        /* frame rate */
@@ -811,38 +860,38 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                reg = 0;
                reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
                reg |= p->rc_framerate_denom & 0xFFFF;
-               WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6);
+               WRITEL(reg, mfc_regs->e_rc_frame_rate);
        }
 
        /* vbv buffer size */
        if (p->frame_skip_mode ==
                        V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
                WRITEL(p_h264->cpb_size & 0xFFFF,
-                               S5P_FIMV_E_VBV_BUFFER_SIZE_V6);
+                               mfc_regs->e_vbv_buffer_size);
 
                if (p->rc_frame)
-                       WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6);
+                       WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
        }
 
        /* interlace */
        reg = 0;
        reg |= ((p_h264->interlace & 0x1) << 3);
-       WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_h264_options);
 
        /* height */
        if (p_h264->interlace) {
                WRITEL(ctx->img_height >> 1,
-                               S5P_FIMV_E_FRAME_HEIGHT_V6); /* 32 align */
+                               mfc_regs->e_frame_height); /* 32 align */
                /* cropped height */
                WRITEL(ctx->img_height >> 1,
-                               S5P_FIMV_E_CROPPED_FRAME_HEIGHT_V6);
+                               mfc_regs->e_cropped_frame_height);
        }
 
        /* loop filter ctrl */
-       reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+       reg = READL(mfc_regs->e_h264_options);
        reg &= ~(0x3 << 1);
        reg |= ((p_h264->loop_filter_mode & 0x3) << 1);
-       WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_h264_options);
 
        /* loopfilter alpha offset */
        if (p_h264->loop_filter_alpha < 0) {
@@ -852,7 +901,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                reg = 0x00;
                reg |= (p_h264->loop_filter_alpha & 0xF);
        }
-       WRITEL(reg, S5P_FIMV_E_H264_LF_ALPHA_OFFSET_V6);
+       WRITEL(reg, mfc_regs->e_h264_lf_alpha_offset);
 
        /* loopfilter beta offset */
        if (p_h264->loop_filter_beta < 0) {
@@ -862,28 +911,28 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                reg = 0x00;
                reg |= (p_h264->loop_filter_beta & 0xF);
        }
-       WRITEL(reg, S5P_FIMV_E_H264_LF_BETA_OFFSET_V6);
+       WRITEL(reg, mfc_regs->e_h264_lf_beta_offset);
 
        /* entropy coding mode */
-       reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+       reg = READL(mfc_regs->e_h264_options);
        reg &= ~(0x1);
        reg |= p_h264->entropy_mode & 0x1;
-       WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_h264_options);
 
        /* number of ref. picture */
-       reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+       reg = READL(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 7);
        reg |= (((p_h264->num_ref_pic_4p - 1) & 0x1) << 7);
-       WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_h264_options);
 
        /* 8x8 transform enable */
-       reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+       reg = READL(mfc_regs->e_h264_options);
        reg &= ~(0x3 << 12);
        reg |= ((p_h264->_8x8_transform & 0x3) << 12);
-       WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_h264_options);
 
        /* macroblock adaptive scaling features */
-       WRITEL(0x0, S5P_FIMV_E_MB_RC_CONFIG_V6);
+       WRITEL(0x0, mfc_regs->e_mb_rc_config);
        if (p->rc_mb) {
                reg = 0;
                /** dark region */
@@ -894,92 +943,95 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                reg |= ((p_h264->rc_mb_static & 0x1) << 1);
                /** high activity region */
                reg |= p_h264->rc_mb_activity & 0x1;
-               WRITEL(reg, S5P_FIMV_E_MB_RC_CONFIG_V6);
+               WRITEL(reg, mfc_regs->e_mb_rc_config);
        }
 
        /* aspect ratio VUI */
-       reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+       READL(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 5);
        reg |= ((p_h264->vui_sar & 0x1) << 5);
-       WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_h264_options);
 
-       WRITEL(0x0, S5P_FIMV_E_ASPECT_RATIO_V6);
-       WRITEL(0x0, S5P_FIMV_E_EXTENDED_SAR_V6);
+       WRITEL(0x0, mfc_regs->e_aspect_ratio);
+       WRITEL(0x0, mfc_regs->e_extended_sar);
        if (p_h264->vui_sar) {
                /* aspect ration IDC */
                reg = 0;
                reg |= p_h264->vui_sar_idc & 0xFF;
-               WRITEL(reg, S5P_FIMV_E_ASPECT_RATIO_V6);
+               WRITEL(reg, mfc_regs->e_aspect_ratio);
                if (p_h264->vui_sar_idc == 0xFF) {
                        /* extended SAR */
                        reg = 0;
                        reg |= (p_h264->vui_ext_sar_width & 0xFFFF) << 16;
                        reg |= p_h264->vui_ext_sar_height & 0xFFFF;
-                       WRITEL(reg, S5P_FIMV_E_EXTENDED_SAR_V6);
+                       WRITEL(reg, mfc_regs->e_extended_sar);
                }
        }
 
        /* intra picture period for H.264 open GOP */
        /* control */
-       reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+       READL(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 4);
        reg |= ((p_h264->open_gop & 0x1) << 4);
-       WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_h264_options);
+
        /* value */
-       WRITEL(0x0, S5P_FIMV_E_H264_I_PERIOD_V6);
+       WRITEL(0x0, mfc_regs->e_h264_i_period);
        if (p_h264->open_gop) {
                reg = 0;
                reg |= p_h264->open_gop_size & 0xFFFF;
-               WRITEL(reg, S5P_FIMV_E_H264_I_PERIOD_V6);
+               WRITEL(reg, mfc_regs->e_h264_i_period);
        }
 
        /* 'WEIGHTED_BI_PREDICTION' for B is disable */
-       reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+       READL(mfc_regs->e_h264_options);
        reg &= ~(0x3 << 9);
-       WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_h264_options);
 
        /* 'CONSTRAINED_INTRA_PRED_ENABLE' is disable */
-       reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+       READL(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 14);
-       WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_h264_options);
 
        /* ASO */
-       reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+       READL(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 6);
        reg |= ((p_h264->aso & 0x1) << 6);
-       WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_h264_options);
 
        /* hier qp enable */
-       reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+       READL(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 8);
        reg |= ((p_h264->open_gop & 0x1) << 8);
-       WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_h264_options);
        reg = 0;
        if (p_h264->hier_qp && p_h264->hier_qp_layer) {
                reg |= (p_h264->hier_qp_type & 0x1) << 0x3;
                reg |= p_h264->hier_qp_layer & 0x7;
-               WRITEL(reg, S5P_FIMV_E_H264_NUM_T_LAYER_V6);
+               WRITEL(reg, mfc_regs->e_h264_num_t_layer);
                /* QP value for each layer */
-               for (i = 0; i < (p_h264->hier_qp_layer & 0x7); i++)
+               for (i = 0; i < p_h264->hier_qp_layer &&
+                               i < ARRAY_SIZE(p_h264->hier_qp_layer_qp); i++) {
                        WRITEL(p_h264->hier_qp_layer_qp[i],
-                               S5P_FIMV_E_H264_HIERARCHICAL_QP_LAYER0_V6 +
-                               i * 4);
+                               mfc_regs->e_h264_hierarchical_qp_layer0
+                               + i * 4);
+               }
        }
        /* number of coding layer should be zero when hierarchical is disable */
-       WRITEL(reg, S5P_FIMV_E_H264_NUM_T_LAYER_V6);
+       WRITEL(reg, mfc_regs->e_h264_num_t_layer);
 
        /* frame packing SEI generation */
-       reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+       READL(mfc_regs->e_h264_options);
        reg &= ~(0x1 << 25);
        reg |= ((p_h264->sei_frame_packing & 0x1) << 25);
-       WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+       WRITEL(reg, mfc_regs->e_h264_options);
        if (p_h264->sei_frame_packing) {
                reg = 0;
                /** current frame0 flag */
                reg |= ((p_h264->sei_fp_curr_frame_0 & 0x1) << 2);
                /** arrangement type */
                reg |= p_h264->sei_fp_arrangement_type & 0x3;
-               WRITEL(reg, S5P_FIMV_E_H264_FRAME_PACKING_SEI_INFO_V6);
+               WRITEL(reg, mfc_regs->e_h264_frame_packing_sei_info);
        }
 
        if (p_h264->fmo) {
@@ -989,8 +1041,8 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                                p_h264->fmo_slice_grp = 4;
                        for (i = 0; i < (p_h264->fmo_slice_grp & 0xF); i++)
                                WRITEL(p_h264->fmo_run_len[i] - 1,
-                               S5P_FIMV_E_H264_FMO_RUN_LENGTH_MINUS1_0_V6 +
-                               i * 4);
+                                       mfc_regs->e_h264_fmo_run_length_minus1_0
+                                       + i * 4);
                        break;
                case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_SCATTERED_SLICES:
                        if (p_h264->fmo_slice_grp > 4)
@@ -1001,10 +1053,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                        if (p_h264->fmo_slice_grp > 2)
                                p_h264->fmo_slice_grp = 2;
                        WRITEL(p_h264->fmo_chg_dir & 0x1,
-                               S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_DIR_V6);
+                               mfc_regs->e_h264_fmo_slice_grp_change_dir);
                        /* the valid range is 0 ~ number of macroblocks -1 */
                        WRITEL(p_h264->fmo_chg_rate,
-                               S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_RATE_MINUS1_V6);
+                       mfc_regs->e_h264_fmo_slice_grp_change_rate_minus1);
                        break;
                default:
                        mfc_err("Unsupported map type for FMO: %d\n",
@@ -1015,11 +1067,11 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
                }
 
                WRITEL(p_h264->fmo_map_type,
-                               S5P_FIMV_E_H264_FMO_SLICE_GRP_MAP_TYPE_V6);
+                               mfc_regs->e_h264_fmo_slice_grp_map_type);
                WRITEL(p_h264->fmo_slice_grp - 1,
-                               S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1_V6);
+                               mfc_regs->e_h264_fmo_num_slice_grp_minus1);
        } else {
-               WRITEL(0, S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1_V6);
+               WRITEL(0, mfc_regs->e_h264_fmo_num_slice_grp_minus1);
        }
 
        mfc_debug_leave();
@@ -1030,6 +1082,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
 static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
        struct s5p_mfc_enc_params *p = &ctx->enc_params;
        struct s5p_mfc_mpeg4_enc_params *p_mpeg4 = &p->codec.mpeg4;
        unsigned int reg = 0;
@@ -1039,10 +1092,10 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
        s5p_mfc_set_enc_params(ctx);
 
        /* pictype : number of B */
-       reg = READL(S5P_FIMV_E_GOP_CONFIG_V6);
+       reg = READL(mfc_regs->e_gop_config);
        reg &= ~(0x3 << 16);
        reg |= ((p->num_b_frame & 0x3) << 16);
-       WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_gop_config);
 
        /* profile & level */
        reg = 0;
@@ -1050,18 +1103,19 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
        reg |= ((p_mpeg4->level & 0xFF) << 8);
        /** profile - 0 ~ 1 */
        reg |= p_mpeg4->profile & 0x3F;
-       WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6);
+       WRITEL(reg, mfc_regs->e_picture_profile);
 
        /* rate control config. */
-       reg = READL(S5P_FIMV_E_RC_CONFIG_V6);
+       reg = READL(mfc_regs->e_rc_config);
        /** macroblock level rate control */
        reg &= ~(0x1 << 8);
        reg |= ((p->rc_mb & 0x1) << 8);
-       WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_rc_config);
+
        /** frame QP */
        reg &= ~(0x3F);
        reg |= p_mpeg4->rc_frame_qp & 0x3F;
-       WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_rc_config);
 
        /* max & min value of QP */
        reg = 0;
@@ -1069,16 +1123,16 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
        reg |= ((p_mpeg4->rc_max_qp & 0x3F) << 8);
        /** min QP */
        reg |= p_mpeg4->rc_min_qp & 0x3F;
-       WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6);
+       WRITEL(reg, mfc_regs->e_rc_qp_bound);
 
        /* other QPs */
-       WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+       WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
        if (!p->rc_frame && !p->rc_mb) {
                reg = 0;
                reg |= ((p_mpeg4->rc_b_frame_qp & 0x3F) << 16);
                reg |= ((p_mpeg4->rc_p_frame_qp & 0x3F) << 8);
                reg |= p_mpeg4->rc_frame_qp & 0x3F;
-               WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+               WRITEL(reg, mfc_regs->e_fixed_picture_qp);
        }
 
        /* frame rate */
@@ -1086,21 +1140,21 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
                reg = 0;
                reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
                reg |= p->rc_framerate_denom & 0xFFFF;
-               WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6);
+               WRITEL(reg, mfc_regs->e_rc_frame_rate);
        }
 
        /* vbv buffer size */
        if (p->frame_skip_mode ==
                        V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
-               WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6);
+               WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
 
                if (p->rc_frame)
-                       WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6);
+                       WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
        }
 
        /* Disable HEC */
-       WRITEL(0x0, S5P_FIMV_E_MPEG4_OPTIONS_V6);
-       WRITEL(0x0, S5P_FIMV_E_MPEG4_HEC_PERIOD_V6);
+       WRITEL(0x0, mfc_regs->e_mpeg4_options);
+       WRITEL(0x0, mfc_regs->e_mpeg4_hec_period);
 
        mfc_debug_leave();
 
@@ -1110,6 +1164,7 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
 static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
        struct s5p_mfc_enc_params *p = &ctx->enc_params;
        struct s5p_mfc_mpeg4_enc_params *p_h263 = &p->codec.mpeg4;
        unsigned int reg = 0;
@@ -1122,18 +1177,19 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
        reg = 0;
        /** profile */
        reg |= (0x1 << 4);
-       WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6);
+       WRITEL(reg, mfc_regs->e_picture_profile);
 
        /* rate control config. */
-       reg = READL(S5P_FIMV_E_RC_CONFIG_V6);
+       reg = READL(mfc_regs->e_rc_config);
        /** macroblock level rate control */
        reg &= ~(0x1 << 8);
        reg |= ((p->rc_mb & 0x1) << 8);
-       WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_rc_config);
+
        /** frame QP */
        reg &= ~(0x3F);
        reg |= p_h263->rc_frame_qp & 0x3F;
-       WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_rc_config);
 
        /* max & min value of QP */
        reg = 0;
@@ -1141,16 +1197,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
        reg |= ((p_h263->rc_max_qp & 0x3F) << 8);
        /** min QP */
        reg |= p_h263->rc_min_qp & 0x3F;
-       WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6);
+       WRITEL(reg, mfc_regs->e_rc_qp_bound);
 
        /* other QPs */
-       WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+       WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
        if (!p->rc_frame && !p->rc_mb) {
                reg = 0;
                reg |= ((p_h263->rc_b_frame_qp & 0x3F) << 16);
                reg |= ((p_h263->rc_p_frame_qp & 0x3F) << 8);
                reg |= p_h263->rc_frame_qp & 0x3F;
-               WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+               WRITEL(reg, mfc_regs->e_fixed_picture_qp);
        }
 
        /* frame rate */
@@ -1158,16 +1214,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
                reg = 0;
                reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
                reg |= p->rc_framerate_denom & 0xFFFF;
-               WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6);
+               WRITEL(reg, mfc_regs->e_rc_frame_rate);
        }
 
        /* vbv buffer size */
        if (p->frame_skip_mode ==
                        V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
-               WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6);
+               WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
 
                if (p->rc_frame)
-                       WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6);
+                       WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
        }
 
        mfc_debug_leave();
@@ -1178,6 +1234,7 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
 static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
        struct s5p_mfc_enc_params *p = &ctx->enc_params;
        struct s5p_mfc_vp8_enc_params *p_vp8 = &p->codec.vp8;
        unsigned int reg = 0;
@@ -1188,57 +1245,57 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)
        s5p_mfc_set_enc_params(ctx);
 
        /* pictype : number of B */
-       reg = READL(S5P_FIMV_E_GOP_CONFIG_V6);
+       reg = READL(mfc_regs->e_gop_config);
        reg &= ~(0x3 << 16);
        reg |= ((p->num_b_frame & 0x3) << 16);
-       WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_gop_config);
 
        /* profile - 0 ~ 3 */
        reg = p_vp8->profile & 0x3;
-       WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6);
+       WRITEL(reg, mfc_regs->e_picture_profile);
 
        /* rate control config. */
-       reg = READL(S5P_FIMV_E_RC_CONFIG_V6);
+       reg = READL(mfc_regs->e_rc_config);
        /** macroblock level rate control */
        reg &= ~(0x1 << 8);
        reg |= ((p->rc_mb & 0x1) << 8);
-       WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_rc_config);
 
        /* frame rate */
        if (p->rc_frame && p->rc_framerate_num && p->rc_framerate_denom) {
                reg = 0;
                reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
                reg |= p->rc_framerate_denom & 0xFFFF;
-               WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6);
+               WRITEL(reg, mfc_regs->e_rc_frame_rate);
        }
 
        /* frame QP */
        reg &= ~(0x7F);
        reg |= p_vp8->rc_frame_qp & 0x7F;
-       WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+       WRITEL(reg, mfc_regs->e_rc_config);
 
        /* other QPs */
-       WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+       WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
        if (!p->rc_frame && !p->rc_mb) {
                reg = 0;
                reg |= ((p_vp8->rc_p_frame_qp & 0x7F) << 8);
                reg |= p_vp8->rc_frame_qp & 0x7F;
-               WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+               WRITEL(reg, mfc_regs->e_fixed_picture_qp);
        }
 
        /* max QP */
        reg = ((p_vp8->rc_max_qp & 0x7F) << 8);
        /* min QP */
        reg |= p_vp8->rc_min_qp & 0x7F;
-       WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6);
+       WRITEL(reg, mfc_regs->e_rc_qp_bound);
 
        /* vbv buffer size */
        if (p->frame_skip_mode ==
                        V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
-               WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6);
+               WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
 
                if (p->rc_frame)
-                       WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6);
+                       WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
        }
 
        /* VP8 specific params */
@@ -1260,7 +1317,7 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)
        }
        reg |= (val & 0xF) << 3;
        reg |= (p_vp8->num_ref & 0x2);
-       WRITEL(reg, S5P_FIMV_E_VP8_OPTIONS_V7);
+       WRITEL(reg, mfc_regs->e_vp8_options);
 
        mfc_debug_leave();
 
@@ -1271,6 +1328,7 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)
 static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
        unsigned int reg = 0;
        int fmo_aso_ctrl = 0;
 
@@ -1278,9 +1336,9 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
        mfc_debug(2, "InstNo: %d/%d\n", ctx->inst_no,
                        S5P_FIMV_CH_SEQ_HEADER_V6);
        mfc_debug(2, "BUFs: %08x %08x %08x\n",
-                 READL(S5P_FIMV_D_CPB_BUFFER_ADDR_V6),
-                 READL(S5P_FIMV_D_CPB_BUFFER_ADDR_V6),
-                 READL(S5P_FIMV_D_CPB_BUFFER_ADDR_V6));
+                 READL(mfc_regs->d_cpb_buffer_addr),
+                 READL(mfc_regs->d_cpb_buffer_addr),
+                 READL(mfc_regs->d_cpb_buffer_addr));
 
        /* FMO_ASO_CTRL - 0: Enable, 1: Disable */
        reg |= (fmo_aso_ctrl << S5P_FIMV_D_OPT_FMO_ASO_CTRL_MASK_V6);
@@ -1291,11 +1349,11 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
         * set to negative value. */
        if (ctx->display_delay >= 0) {
                reg |= (0x1 << S5P_FIMV_D_OPT_DDELAY_EN_SHIFT_V6);
-               WRITEL(ctx->display_delay, S5P_FIMV_D_DISPLAY_DELAY_V6);
+               WRITEL(ctx->display_delay, mfc_regs->d_display_delay);
        }
 
-       if (IS_MFCV7(dev)) {
-               WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6);
+       if (IS_MFCV7_PLUS(dev)) {
+               WRITEL(reg, mfc_regs->d_dec_options);
                reg = 0;
        }
 
@@ -1309,22 +1367,22 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
        if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16)
                reg |= (0x1 << S5P_FIMV_D_OPT_TILE_MODE_SHIFT_V6);
 
-       if (IS_MFCV7(dev))
-               WRITEL(reg, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V7);
+       if (IS_MFCV7_PLUS(dev))
+               WRITEL(reg, mfc_regs->d_init_buffer_options);
        else
-               WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6);
+               WRITEL(reg, mfc_regs->d_dec_options);
 
        /* 0: NV12(CbCr), 1: NV21(CrCb) */
        if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M)
-               WRITEL(0x1, S5P_FIMV_PIXEL_FORMAT_V6);
+               WRITEL(0x1, mfc_regs->pixel_format);
        else
-               WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6);
+               WRITEL(0x0, mfc_regs->pixel_format);
 
 
        /* sei parse */
-       WRITEL(ctx->sei_fp_parse & 0x1, S5P_FIMV_D_SEI_ENABLE_V6);
+       WRITEL(ctx->sei_fp_parse & 0x1, mfc_regs->d_sei_enable);
 
-       WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+       WRITEL(ctx->inst_no, mfc_regs->instance_id);
        s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
                        S5P_FIMV_CH_SEQ_HEADER_V6, NULL);
 
@@ -1335,11 +1393,12 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
 static inline void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
 
        if (flush) {
                dev->curr_ctx = ctx->num;
                s5p_mfc_clean_ctx_int_flags(ctx);
-               WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+               WRITEL(ctx->inst_no, mfc_regs->instance_id);
                s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
                                S5P_FIMV_H2R_CMD_FLUSH_V6, NULL);
        }
@@ -1350,11 +1409,12 @@ static int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx,
                        enum s5p_mfc_decode_arg last_frame)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
 
-       WRITEL(ctx->dec_dst_flag, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V6);
-       WRITEL(ctx->slice_interface & 0x1, S5P_FIMV_D_SLICE_IF_ENABLE_V6);
+       WRITEL(ctx->dec_dst_flag, mfc_regs->d_available_dpb_flag_lower);
+       WRITEL(ctx->slice_interface & 0x1, mfc_regs->d_slice_if_enable);
 
-       WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+       WRITEL(ctx->inst_no, mfc_regs->instance_id);
        /* Issue different commands to instance basing on whether it
         * is the last frame or not. */
        switch (last_frame) {
@@ -1378,6 +1438,7 @@ static int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx,
 static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
 
        if (ctx->codec_mode == S5P_MFC_CODEC_H264_ENC)
                s5p_mfc_set_enc_params_h264(ctx);
@@ -1393,13 +1454,13 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)
                return -EINVAL;
        }
 
-       /* Set stride lengths */
-       if (IS_MFCV7(dev)) {
-               WRITEL(ctx->img_width, S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7);
-               WRITEL(ctx->img_width, S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7);
+       /* Set stride lengths for v7 & above */
+       if (IS_MFCV7_PLUS(dev)) {
+               WRITEL(ctx->img_width, mfc_regs->e_source_first_plane_stride);
+               WRITEL(ctx->img_width, mfc_regs->e_source_second_plane_stride);
        }
 
-       WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+       WRITEL(ctx->inst_no, mfc_regs->instance_id);
        s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
                        S5P_FIMV_CH_SEQ_HEADER_V6, NULL);
 
@@ -1409,14 +1470,16 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)
 static int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
        struct s5p_mfc_enc_params *p = &ctx->enc_params;
        struct s5p_mfc_h264_enc_params *p_h264 = &p->codec.h264;
        int i;
 
        if (p_h264->aso) {
-               for (i = 0; i < 8; i++)
+               for (i = 0; i < ARRAY_SIZE(p_h264->aso_slice_order); i++) {
                        WRITEL(p_h264->aso_slice_order[i],
-                               S5P_FIMV_E_H264_ASO_SLICE_ORDER_0_V6 + i * 4);
+                               mfc_regs->e_h264_aso_slice_order_0 + i * 4);
+               }
        }
        return 0;
 }
@@ -1425,6 +1488,7 @@ static int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx)
 static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx)
 {
        struct s5p_mfc_dev *dev = ctx->dev;
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
 
        mfc_debug(2, "++\n");
 
@@ -1435,7 +1499,7 @@ static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx)
 
        s5p_mfc_set_slice_mode(ctx);
 
-       WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+       WRITEL(ctx->inst_no, mfc_regs->instance_id);
        s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
                        S5P_FIMV_CH_FRAME_START_V6, NULL);
 
@@ -1810,28 +1874,26 @@ static void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq)
 
 static void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev)
 {
-       mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD_V6);
-       mfc_write(dev, 0, S5P_FIMV_RISC2HOST_INT_V6);
+       const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
+       WRITEL(0, mfc_regs->risc2host_command);
+       WRITEL(0, mfc_regs->risc2host_int);
 }
 
 static void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data,
                unsigned int ofs)
 {
-       struct s5p_mfc_dev *dev = ctx->dev;
-
        s5p_mfc_clock_on();
-       WRITEL(data, ofs);
+       WRITEL(data, (void *)ofs);
        s5p_mfc_clock_off();
 }
 
 static unsigned int
 s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs)
 {
-       struct s5p_mfc_dev *dev = ctx->dev;
        int ret;
 
        s5p_mfc_clock_on();
-       ret = READL(ofs);
+       ret = READL((void *)ofs);
        s5p_mfc_clock_off();
 
        return ret;
@@ -1839,50 +1901,51 @@ s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs)
 
 static int s5p_mfc_get_dspl_y_adr_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_D_DISPLAY_LUMA_ADDR_V6);
+       return READL(dev->mfc_regs->d_display_first_plane_addr);
 }
 
 static int s5p_mfc_get_dec_y_adr_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_D_DECODED_LUMA_ADDR_V6);
+       return READL(dev->mfc_regs->d_decoded_first_plane_addr);
 }
 
 static int s5p_mfc_get_dspl_status_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_D_DISPLAY_STATUS_V6);
+       return READL(dev->mfc_regs->d_display_status);
 }
 
 static int s5p_mfc_get_dec_status_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_D_DECODED_STATUS_V6);
+       return READL(dev->mfc_regs->d_decoded_status);
 }
 
 static int s5p_mfc_get_dec_frame_type_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_D_DECODED_FRAME_TYPE_V6) &
+       return READL(dev->mfc_regs->d_decoded_frame_type) &
                S5P_FIMV_DECODE_FRAME_MASK_V6;
 }
 
 static int s5p_mfc_get_disp_frame_type_v6(struct s5p_mfc_ctx *ctx)
 {
-       return mfc_read(ctx->dev, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V6) &
+       struct s5p_mfc_dev *dev = ctx->dev;
+       return READL(dev->mfc_regs->d_display_frame_type) &
                S5P_FIMV_DECODE_FRAME_MASK_V6;
 }
 
 static int s5p_mfc_get_consumed_stream_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_D_DECODED_NAL_SIZE_V6);
+       return READL(dev->mfc_regs->d_decoded_nal_size);
 }
 
 static int s5p_mfc_get_int_reason_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_RISC2HOST_CMD_V6) &
+       return READL(dev->mfc_regs->risc2host_command) &
                S5P_FIMV_RISC2HOST_CMD_MASK;
 }
 
 static int s5p_mfc_get_int_err_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_ERROR_CODE_V6);
+       return READL(dev->mfc_regs->error_code);
 }
 
 static int s5p_mfc_err_dec_v6(unsigned int err)
@@ -1897,82 +1960,323 @@ static int s5p_mfc_err_dspl_v6(unsigned int err)
 
 static int s5p_mfc_get_img_width_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6);
+       return READL(dev->mfc_regs->d_display_frame_width);
 }
 
 static int s5p_mfc_get_img_height_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V6);
+       return READL(dev->mfc_regs->d_display_frame_height);
 }
 
 static int s5p_mfc_get_dpb_count_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_D_MIN_NUM_DPB_V6);
+       return READL(dev->mfc_regs->d_min_num_dpb);
 }
 
 static int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_D_MIN_NUM_MV_V6);
+       return READL(dev->mfc_regs->d_min_num_mv);
 }
 
 static int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_RET_INSTANCE_ID_V6);
+       return READL(dev->mfc_regs->ret_instance_id);
 }
 
 static int s5p_mfc_get_enc_dpb_count_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_E_NUM_DPB_V6);
+       return READL(dev->mfc_regs->e_num_dpb);
 }
 
 static int s5p_mfc_get_enc_strm_size_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_E_STREAM_SIZE_V6);
+       return READL(dev->mfc_regs->e_stream_size);
 }
 
 static int s5p_mfc_get_enc_slice_type_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_E_SLICE_TYPE_V6);
+       return READL(dev->mfc_regs->e_slice_type);
 }
 
 static int s5p_mfc_get_enc_pic_count_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_E_PICTURE_COUNT_V6);
+       return READL(dev->mfc_regs->e_picture_count);
 }
 
 static int s5p_mfc_get_sei_avail_status_v6(struct s5p_mfc_ctx *ctx)
 {
-       return mfc_read(ctx->dev, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V6);
+       struct s5p_mfc_dev *dev = ctx->dev;
+       return READL(dev->mfc_regs->d_frame_pack_sei_avail);
 }
 
 static int s5p_mfc_get_mvc_num_views_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_D_MVC_NUM_VIEWS_V6);
+       return READL(dev->mfc_regs->d_mvc_num_views);
 }
 
 static int s5p_mfc_get_mvc_view_id_v6(struct s5p_mfc_dev *dev)
 {
-       return mfc_read(dev, S5P_FIMV_D_MVC_VIEW_ID_V6);
+       return READL(dev->mfc_regs->d_mvc_view_id);
 }
 
 static unsigned int s5p_mfc_get_pic_type_top_v6(struct s5p_mfc_ctx *ctx)
 {
-       return s5p_mfc_read_info_v6(ctx, PIC_TIME_TOP_V6);
+       return s5p_mfc_read_info_v6(ctx,
+               (unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_top);
 }
 
 static unsigned int s5p_mfc_get_pic_type_bot_v6(struct s5p_mfc_ctx *ctx)
 {
-       return s5p_mfc_read_info_v6(ctx, PIC_TIME_BOT_V6);
+       return s5p_mfc_read_info_v6(ctx,
+               (unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_bot);
 }
 
 static unsigned int s5p_mfc_get_crop_info_h_v6(struct s5p_mfc_ctx *ctx)
 {
-       return s5p_mfc_read_info_v6(ctx, CROP_INFO_H_V6);
+       return s5p_mfc_read_info_v6(ctx,
+               (unsigned int) ctx->dev->mfc_regs->d_display_crop_info1);
 }
 
 static unsigned int s5p_mfc_get_crop_info_v_v6(struct s5p_mfc_ctx *ctx)
 {
-       return s5p_mfc_read_info_v6(ctx, CROP_INFO_V_V6);
+       return s5p_mfc_read_info_v6(ctx,
+               (unsigned int) ctx->dev->mfc_regs->d_display_crop_info2);
+}
+
+static struct s5p_mfc_regs mfc_regs;
+
+/* Initialize registers for MFC v6 onwards */
+const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev)
+{
+       memset(&mfc_regs, 0, sizeof(mfc_regs));
+
+#define S5P_MFC_REG_ADDR(dev, reg) ((dev)->regs_base + (reg))
+#define R(m, r) mfc_regs.m = S5P_MFC_REG_ADDR(dev, r)
+       /* codec common registers */
+       R(risc_on, S5P_FIMV_RISC_ON_V6);
+       R(risc2host_int, S5P_FIMV_RISC2HOST_INT_V6);
+       R(host2risc_int, S5P_FIMV_HOST2RISC_INT_V6);
+       R(risc_base_address, S5P_FIMV_RISC_BASE_ADDRESS_V6);
+       R(mfc_reset, S5P_FIMV_MFC_RESET_V6);
+       R(host2risc_command, S5P_FIMV_HOST2RISC_CMD_V6);
+       R(risc2host_command, S5P_FIMV_RISC2HOST_CMD_V6);
+       R(firmware_version, S5P_FIMV_FW_VERSION_V6);
+       R(instance_id, S5P_FIMV_INSTANCE_ID_V6);
+       R(codec_type, S5P_FIMV_CODEC_TYPE_V6);
+       R(context_mem_addr, S5P_FIMV_CONTEXT_MEM_ADDR_V6);
+       R(context_mem_size, S5P_FIMV_CONTEXT_MEM_SIZE_V6);
+       R(pixel_format, S5P_FIMV_PIXEL_FORMAT_V6);
+       R(ret_instance_id, S5P_FIMV_RET_INSTANCE_ID_V6);
+       R(error_code, S5P_FIMV_ERROR_CODE_V6);
+
+       /* decoder registers */
+       R(d_crc_ctrl, S5P_FIMV_D_CRC_CTRL_V6);
+       R(d_dec_options, S5P_FIMV_D_DEC_OPTIONS_V6);
+       R(d_display_delay, S5P_FIMV_D_DISPLAY_DELAY_V6);
+       R(d_sei_enable, S5P_FIMV_D_SEI_ENABLE_V6);
+       R(d_min_num_dpb, S5P_FIMV_D_MIN_NUM_DPB_V6);
+       R(d_min_num_mv, S5P_FIMV_D_MIN_NUM_MV_V6);
+       R(d_mvc_num_views, S5P_FIMV_D_MVC_NUM_VIEWS_V6);
+       R(d_num_dpb, S5P_FIMV_D_NUM_DPB_V6);
+       R(d_num_mv, S5P_FIMV_D_NUM_MV_V6);
+       R(d_init_buffer_options, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V6);
+       R(d_first_plane_dpb_size, S5P_FIMV_D_LUMA_DPB_SIZE_V6);
+       R(d_second_plane_dpb_size, S5P_FIMV_D_CHROMA_DPB_SIZE_V6);
+       R(d_mv_buffer_size, S5P_FIMV_D_MV_BUFFER_SIZE_V6);
+       R(d_first_plane_dpb, S5P_FIMV_D_LUMA_DPB_V6);
+       R(d_second_plane_dpb, S5P_FIMV_D_CHROMA_DPB_V6);
+       R(d_mv_buffer, S5P_FIMV_D_MV_BUFFER_V6);
+       R(d_scratch_buffer_addr, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V6);
+       R(d_scratch_buffer_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V6);
+       R(d_cpb_buffer_addr, S5P_FIMV_D_CPB_BUFFER_ADDR_V6);
+       R(d_cpb_buffer_size, S5P_FIMV_D_CPB_BUFFER_SIZE_V6);
+       R(d_available_dpb_flag_lower, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V6);
+       R(d_cpb_buffer_offset, S5P_FIMV_D_CPB_BUFFER_OFFSET_V6);
+       R(d_slice_if_enable, S5P_FIMV_D_SLICE_IF_ENABLE_V6);
+       R(d_stream_data_size, S5P_FIMV_D_STREAM_DATA_SIZE_V6);
+       R(d_display_frame_width, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6);
+       R(d_display_frame_height, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V6);
+       R(d_display_status, S5P_FIMV_D_DISPLAY_STATUS_V6);
+       R(d_display_first_plane_addr, S5P_FIMV_D_DISPLAY_LUMA_ADDR_V6);
+       R(d_display_second_plane_addr, S5P_FIMV_D_DISPLAY_CHROMA_ADDR_V6);
+       R(d_display_frame_type, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V6);
+       R(d_display_crop_info1, S5P_FIMV_D_DISPLAY_CROP_INFO1_V6);
+       R(d_display_crop_info2, S5P_FIMV_D_DISPLAY_CROP_INFO2_V6);
+       R(d_display_aspect_ratio, S5P_FIMV_D_DISPLAY_ASPECT_RATIO_V6);
+       R(d_display_extended_ar, S5P_FIMV_D_DISPLAY_EXTENDED_AR_V6);
+       R(d_decoded_status, S5P_FIMV_D_DECODED_STATUS_V6);
+       R(d_decoded_first_plane_addr, S5P_FIMV_D_DECODED_LUMA_ADDR_V6);
+       R(d_decoded_second_plane_addr, S5P_FIMV_D_DECODED_CHROMA_ADDR_V6);
+       R(d_decoded_frame_type, S5P_FIMV_D_DECODED_FRAME_TYPE_V6);
+       R(d_decoded_nal_size, S5P_FIMV_D_DECODED_NAL_SIZE_V6);
+       R(d_ret_picture_tag_top, S5P_FIMV_D_RET_PICTURE_TAG_TOP_V6);
+       R(d_ret_picture_tag_bot, S5P_FIMV_D_RET_PICTURE_TAG_BOT_V6);
+       R(d_h264_info, S5P_FIMV_D_H264_INFO_V6);
+       R(d_mvc_view_id, S5P_FIMV_D_MVC_VIEW_ID_V6);
+       R(d_frame_pack_sei_avail, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V6);
+
+       /* encoder registers */
+       R(e_frame_width, S5P_FIMV_E_FRAME_WIDTH_V6);
+       R(e_frame_height, S5P_FIMV_E_FRAME_HEIGHT_V6);
+       R(e_cropped_frame_width, S5P_FIMV_E_CROPPED_FRAME_WIDTH_V6);
+       R(e_cropped_frame_height, S5P_FIMV_E_CROPPED_FRAME_HEIGHT_V6);
+       R(e_frame_crop_offset, S5P_FIMV_E_FRAME_CROP_OFFSET_V6);
+       R(e_enc_options, S5P_FIMV_E_ENC_OPTIONS_V6);
+       R(e_picture_profile, S5P_FIMV_E_PICTURE_PROFILE_V6);
+       R(e_vbv_buffer_size, S5P_FIMV_E_VBV_BUFFER_SIZE_V6);
+       R(e_vbv_init_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6);
+       R(e_fixed_picture_qp, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+       R(e_rc_config, S5P_FIMV_E_RC_CONFIG_V6);
+       R(e_rc_qp_bound, S5P_FIMV_E_RC_QP_BOUND_V6);
+       R(e_rc_mode, S5P_FIMV_E_RC_RPARAM_V6);
+       R(e_mb_rc_config, S5P_FIMV_E_MB_RC_CONFIG_V6);
+       R(e_padding_ctrl, S5P_FIMV_E_PADDING_CTRL_V6);
+       R(e_mv_hor_range, S5P_FIMV_E_MV_HOR_RANGE_V6);
+       R(e_mv_ver_range, S5P_FIMV_E_MV_VER_RANGE_V6);
+       R(e_num_dpb, S5P_FIMV_E_NUM_DPB_V6);
+       R(e_luma_dpb, S5P_FIMV_E_LUMA_DPB_V6);
+       R(e_chroma_dpb, S5P_FIMV_E_CHROMA_DPB_V6);
+       R(e_me_buffer, S5P_FIMV_E_ME_BUFFER_V6);
+       R(e_scratch_buffer_addr, S5P_FIMV_E_SCRATCH_BUFFER_ADDR_V6);
+       R(e_scratch_buffer_size, S5P_FIMV_E_SCRATCH_BUFFER_SIZE_V6);
+       R(e_tmv_buffer0, S5P_FIMV_E_TMV_BUFFER0_V6);
+       R(e_tmv_buffer1, S5P_FIMV_E_TMV_BUFFER1_V6);
+       R(e_source_first_plane_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6);
+       R(e_source_second_plane_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6);
+       R(e_stream_buffer_addr, S5P_FIMV_E_STREAM_BUFFER_ADDR_V6);
+       R(e_stream_buffer_size, S5P_FIMV_E_STREAM_BUFFER_SIZE_V6);
+       R(e_roi_buffer_addr, S5P_FIMV_E_ROI_BUFFER_ADDR_V6);
+       R(e_param_change, S5P_FIMV_E_PARAM_CHANGE_V6);
+       R(e_ir_size, S5P_FIMV_E_IR_SIZE_V6);
+       R(e_gop_config, S5P_FIMV_E_GOP_CONFIG_V6);
+       R(e_mslice_mode, S5P_FIMV_E_MSLICE_MODE_V6);
+       R(e_mslice_size_mb, S5P_FIMV_E_MSLICE_SIZE_MB_V6);
+       R(e_mslice_size_bits, S5P_FIMV_E_MSLICE_SIZE_BITS_V6);
+       R(e_frame_insertion, S5P_FIMV_E_FRAME_INSERTION_V6);
+       R(e_rc_frame_rate, S5P_FIMV_E_RC_FRAME_RATE_V6);
+       R(e_rc_bit_rate, S5P_FIMV_E_RC_BIT_RATE_V6);
+       R(e_rc_roi_ctrl, S5P_FIMV_E_RC_ROI_CTRL_V6);
+       R(e_picture_tag, S5P_FIMV_E_PICTURE_TAG_V6);
+       R(e_bit_count_enable, S5P_FIMV_E_BIT_COUNT_ENABLE_V6);
+       R(e_max_bit_count, S5P_FIMV_E_MAX_BIT_COUNT_V6);
+       R(e_min_bit_count, S5P_FIMV_E_MIN_BIT_COUNT_V6);
+       R(e_metadata_buffer_addr, S5P_FIMV_E_METADATA_BUFFER_ADDR_V6);
+       R(e_metadata_buffer_size, S5P_FIMV_E_METADATA_BUFFER_SIZE_V6);
+       R(e_encoded_source_first_plane_addr,
+                       S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6);
+       R(e_encoded_source_second_plane_addr,
+                       S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6);
+       R(e_stream_size, S5P_FIMV_E_STREAM_SIZE_V6);
+       R(e_slice_type, S5P_FIMV_E_SLICE_TYPE_V6);
+       R(e_picture_count, S5P_FIMV_E_PICTURE_COUNT_V6);
+       R(e_ret_picture_tag, S5P_FIMV_E_RET_PICTURE_TAG_V6);
+       R(e_recon_luma_dpb_addr, S5P_FIMV_E_RECON_LUMA_DPB_ADDR_V6);
+       R(e_recon_chroma_dpb_addr, S5P_FIMV_E_RECON_CHROMA_DPB_ADDR_V6);
+       R(e_mpeg4_options, S5P_FIMV_E_MPEG4_OPTIONS_V6);
+       R(e_mpeg4_hec_period, S5P_FIMV_E_MPEG4_HEC_PERIOD_V6);
+       R(e_aspect_ratio, S5P_FIMV_E_ASPECT_RATIO_V6);
+       R(e_extended_sar, S5P_FIMV_E_EXTENDED_SAR_V6);
+       R(e_h264_options, S5P_FIMV_E_H264_OPTIONS_V6);
+       R(e_h264_lf_alpha_offset, S5P_FIMV_E_H264_LF_ALPHA_OFFSET_V6);
+       R(e_h264_lf_beta_offset, S5P_FIMV_E_H264_LF_BETA_OFFSET_V6);
+       R(e_h264_i_period, S5P_FIMV_E_H264_I_PERIOD_V6);
+       R(e_h264_fmo_slice_grp_map_type,
+                       S5P_FIMV_E_H264_FMO_SLICE_GRP_MAP_TYPE_V6);
+       R(e_h264_fmo_num_slice_grp_minus1,
+                       S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1_V6);
+       R(e_h264_fmo_slice_grp_change_dir,
+                       S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_DIR_V6);
+       R(e_h264_fmo_slice_grp_change_rate_minus1,
+                       S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_RATE_MINUS1_V6);
+       R(e_h264_fmo_run_length_minus1_0,
+                       S5P_FIMV_E_H264_FMO_RUN_LENGTH_MINUS1_0_V6);
+       R(e_h264_aso_slice_order_0, S5P_FIMV_E_H264_ASO_SLICE_ORDER_0_V6);
+       R(e_h264_num_t_layer, S5P_FIMV_E_H264_NUM_T_LAYER_V6);
+       R(e_h264_hierarchical_qp_layer0,
+                       S5P_FIMV_E_H264_HIERARCHICAL_QP_LAYER0_V6);
+       R(e_h264_frame_packing_sei_info,
+                       S5P_FIMV_E_H264_FRAME_PACKING_SEI_INFO_V6);
+
+       if (!IS_MFCV7_PLUS(dev))
+               goto done;
+
+       /* Initialize registers used in MFC v7+ */
+       R(e_source_first_plane_addr, S5P_FIMV_E_SOURCE_FIRST_ADDR_V7);
+       R(e_source_second_plane_addr, S5P_FIMV_E_SOURCE_SECOND_ADDR_V7);
+       R(e_source_third_plane_addr, S5P_FIMV_E_SOURCE_THIRD_ADDR_V7);
+       R(e_source_first_plane_stride, S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7);
+       R(e_source_second_plane_stride, S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7);
+       R(e_source_third_plane_stride, S5P_FIMV_E_SOURCE_THIRD_STRIDE_V7);
+       R(e_encoded_source_first_plane_addr,
+                       S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7);
+       R(e_encoded_source_second_plane_addr,
+                       S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7);
+       R(e_vp8_options, S5P_FIMV_E_VP8_OPTIONS_V7);
+
+       if (!IS_MFCV8(dev))
+               goto done;
+
+       /* Initialize registers used in MFC v8 only.
+        * Also, over-write the registers which have
+        * a different offset for MFC v8. */
+       R(d_stream_data_size, S5P_FIMV_D_STREAM_DATA_SIZE_V8);
+       R(d_cpb_buffer_addr, S5P_FIMV_D_CPB_BUFFER_ADDR_V8);
+       R(d_cpb_buffer_size, S5P_FIMV_D_CPB_BUFFER_SIZE_V8);
+       R(d_cpb_buffer_offset, S5P_FIMV_D_CPB_BUFFER_OFFSET_V8);
+       R(d_first_plane_dpb_size, S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8);
+       R(d_second_plane_dpb_size, S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8);
+       R(d_scratch_buffer_addr, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V8);
+       R(d_scratch_buffer_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V8);
+       R(d_first_plane_dpb_stride_size,
+                       S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8);
+       R(d_second_plane_dpb_stride_size,
+                       S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8);
+       R(d_mv_buffer_size, S5P_FIMV_D_MV_BUFFER_SIZE_V8);
+       R(d_num_mv, S5P_FIMV_D_NUM_MV_V8);
+       R(d_first_plane_dpb, S5P_FIMV_D_FIRST_PLANE_DPB_V8);
+       R(d_second_plane_dpb, S5P_FIMV_D_SECOND_PLANE_DPB_V8);
+       R(d_mv_buffer, S5P_FIMV_D_MV_BUFFER_V8);
+       R(d_init_buffer_options, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V8);
+       R(d_available_dpb_flag_lower, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V8);
+       R(d_slice_if_enable, S5P_FIMV_D_SLICE_IF_ENABLE_V8);
+       R(d_display_first_plane_addr, S5P_FIMV_D_DISPLAY_FIRST_PLANE_ADDR_V8);
+       R(d_display_second_plane_addr, S5P_FIMV_D_DISPLAY_SECOND_PLANE_ADDR_V8);
+       R(d_decoded_first_plane_addr, S5P_FIMV_D_DECODED_FIRST_PLANE_ADDR_V8);
+       R(d_decoded_second_plane_addr, S5P_FIMV_D_DECODED_SECOND_PLANE_ADDR_V8);
+       R(d_display_status, S5P_FIMV_D_DISPLAY_STATUS_V8);
+       R(d_decoded_status, S5P_FIMV_D_DECODED_STATUS_V8);
+       R(d_decoded_frame_type, S5P_FIMV_D_DECODED_FRAME_TYPE_V8);
+       R(d_display_frame_type, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V8);
+       R(d_decoded_nal_size, S5P_FIMV_D_DECODED_NAL_SIZE_V8);
+       R(d_display_frame_width, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V8);
+       R(d_display_frame_height, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V8);
+       R(d_frame_pack_sei_avail, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V8);
+       R(d_mvc_num_views, S5P_FIMV_D_MVC_NUM_VIEWS_V8);
+       R(d_mvc_view_id, S5P_FIMV_D_MVC_VIEW_ID_V8);
+       R(d_ret_picture_tag_top, S5P_FIMV_D_RET_PICTURE_TAG_TOP_V8);
+       R(d_ret_picture_tag_bot, S5P_FIMV_D_RET_PICTURE_TAG_BOT_V8);
+       R(d_display_crop_info1, S5P_FIMV_D_DISPLAY_CROP_INFO1_V8);
+       R(d_display_crop_info2, S5P_FIMV_D_DISPLAY_CROP_INFO2_V8);
+
+       /* encoder registers */
+       R(e_padding_ctrl, S5P_FIMV_E_PADDING_CTRL_V8);
+       R(e_rc_config, S5P_FIMV_E_RC_CONFIG_V8);
+       R(e_rc_mode, S5P_FIMV_E_RC_RPARAM_V8);
+       R(e_mv_hor_range, S5P_FIMV_E_MV_HOR_RANGE_V8);
+       R(e_mv_ver_range, S5P_FIMV_E_MV_VER_RANGE_V8);
+       R(e_rc_qp_bound, S5P_FIMV_E_RC_QP_BOUND_V8);
+       R(e_fixed_picture_qp, S5P_FIMV_E_FIXED_PICTURE_QP_V8);
+       R(e_vbv_buffer_size, S5P_FIMV_E_VBV_BUFFER_SIZE_V8);
+       R(e_vbv_init_delay, S5P_FIMV_E_VBV_INIT_DELAY_V8);
+       R(e_mb_rc_config, S5P_FIMV_E_MB_RC_CONFIG_V8);
+       R(e_aspect_ratio, S5P_FIMV_E_ASPECT_RATIO_V8);
+       R(e_extended_sar, S5P_FIMV_E_EXTENDED_SAR_V8);
+       R(e_h264_options, S5P_FIMV_E_H264_OPTIONS_V8);
+
+done:
+       return &mfc_regs;
+#undef S5P_MFC_REG_ADDR
+#undef R
 }
 
 /* Initialize opr function pointers for MFC v6 */
index ab164ef..8055848 100644 (file)
 #define FRAME_DELTA_H264_H263          1
 #define TIGHT_CBR_MAX                  10
 
-/* Definitions for shared memory compatibility */
-#define PIC_TIME_TOP_V6                S5P_FIMV_D_RET_PICTURE_TAG_TOP_V6
-#define PIC_TIME_BOT_V6                S5P_FIMV_D_RET_PICTURE_TAG_BOT_V6
-#define CROP_INFO_H_V6         S5P_FIMV_D_DISPLAY_CROP_INFO1_V6
-#define CROP_INFO_V_V6         S5P_FIMV_D_DISPLAY_CROP_INFO2_V6
-
 struct s5p_mfc_hw_ops *s5p_mfc_init_hw_ops_v6(void);
+const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev);
 #endif /* S5P_MFC_OPR_V6_H_ */
index 534722c..754740f 100644 (file)
@@ -674,6 +674,8 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd,
 static int hdmi_enum_dv_timings(struct v4l2_subdev *sd,
        struct v4l2_enum_dv_timings *timings)
 {
+       if (timings->pad != 0)
+               return -EINVAL;
        if (timings->index >= ARRAY_SIZE(hdmi_timings))
                return -EINVAL;
        timings->timings = hdmi_timings[timings->index].dv_timings;
@@ -687,8 +689,11 @@ static int hdmi_dv_timings_cap(struct v4l2_subdev *sd,
 {
        struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
 
+       if (cap->pad != 0)
+               return -EINVAL;
+
        /* Let the phy fill in the pixelclock range */
-       v4l2_subdev_call(hdev->phy_sd, video, dv_timings_cap, cap);
+       v4l2_subdev_call(hdev->phy_sd, pad, dv_timings_cap, cap);
        cap->type = V4L2_DV_BT_656_1120;
        cap->bt.min_width = 720;
        cap->bt.max_width = 1920;
@@ -707,12 +712,15 @@ static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = {
 static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = {
        .s_dv_timings = hdmi_s_dv_timings,
        .g_dv_timings = hdmi_g_dv_timings,
-       .enum_dv_timings = hdmi_enum_dv_timings,
-       .dv_timings_cap = hdmi_dv_timings_cap,
        .g_mbus_fmt = hdmi_g_mbus_fmt,
        .s_stream = hdmi_s_stream,
 };
 
+static const struct v4l2_subdev_pad_ops hdmi_sd_pad_ops = {
+       .enum_dv_timings = hdmi_enum_dv_timings,
+       .dv_timings_cap = hdmi_dv_timings_cap,
+};
+
 static const struct v4l2_subdev_ops hdmi_sd_ops = {
        .core = &hdmi_sd_core_ops,
        .video = &hdmi_sd_video_ops,
index e19a0af..c2f2e35 100644 (file)
@@ -225,6 +225,9 @@ static int hdmiphy_s_dv_timings(struct v4l2_subdev *sd,
 static int hdmiphy_dv_timings_cap(struct v4l2_subdev *sd,
        struct v4l2_dv_timings_cap *cap)
 {
+       if (cap->pad != 0)
+               return -EINVAL;
+
        cap->type = V4L2_DV_BT_656_1120;
        /* The phy only determines the pixelclock, leave the other values
         * at 0 to signify that we have no information for them. */
@@ -259,13 +262,17 @@ static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
 
 static const struct v4l2_subdev_video_ops hdmiphy_video_ops = {
        .s_dv_timings = hdmiphy_s_dv_timings,
-       .dv_timings_cap = hdmiphy_dv_timings_cap,
        .s_stream =  hdmiphy_s_stream,
 };
 
+static const struct v4l2_subdev_pad_ops hdmiphy_pad_ops = {
+       .dv_timings_cap = hdmiphy_dv_timings_cap,
+};
+
 static const struct v4l2_subdev_ops hdmiphy_ops = {
        .core = &hdmiphy_core_ops,
        .video = &hdmiphy_video_ops,
+       .pad = &hdmiphy_pad_ops,
 };
 
 static int hdmiphy_probe(struct i2c_client *client,
index a1ce55f..8a8dbc8 100644 (file)
@@ -509,9 +509,11 @@ static int mxr_enum_dv_timings(struct file *file, void *fh,
        struct mxr_device *mdev = layer->mdev;
        int ret;
 
+       timings->pad = 0;
+
        /* lock protects from changing sd_out */
        mutex_lock(&mdev->mutex);
-       ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_timings, timings);
+       ret = v4l2_subdev_call(to_outsd(mdev), pad, enum_dv_timings, timings);
        mutex_unlock(&mdev->mutex);
 
        return ret ? -EINVAL : 0;
@@ -567,9 +569,11 @@ static int mxr_dv_timings_cap(struct file *file, void *fh,
        struct mxr_device *mdev = layer->mdev;
        int ret;
 
+       cap->pad = 0;
+
        /* lock protects from changing sd_out */
        mutex_lock(&mdev->mutex);
-       ret = v4l2_subdev_call(to_outsd(mdev), video, dv_timings_cap, cap);
+       ret = v4l2_subdev_call(to_outsd(mdev), pad, dv_timings_cap, cap);
        mutex_unlock(&mdev->mutex);
 
        return ret ? -EINVAL : 0;
@@ -985,7 +989,7 @@ static void mxr_watchdog(unsigned long arg)
        spin_unlock_irqrestore(&layer->enq_slock, flags);
 }
 
-static int stop_streaming(struct vb2_queue *vq)
+static void stop_streaming(struct vb2_queue *vq)
 {
        struct mxr_layer *layer = vb2_get_drv_priv(vq);
        struct mxr_device *mdev = layer->mdev;
@@ -1031,7 +1035,6 @@ static int stop_streaming(struct vb2_queue *vq)
        mxr_streamer_put(mdev);
        /* allow changes in output configuration */
        mxr_output_put(mdev);
-       return 0;
 }
 
 static struct vb2_ops mxr_video_qops = {
index f0b6c90..38c723a 100644 (file)
@@ -406,7 +406,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
 }
 
 /* abort streaming and wait for last buffer */
-static int stop_streaming(struct vb2_queue *vq)
+static void stop_streaming(struct vb2_queue *vq)
 {
        struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
        struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -433,7 +433,7 @@ static int stop_streaming(struct vb2_queue *vq)
        if (time_after(jiffies, timeout)) {
                dev_err(icd->parent,
                        "Timeout waiting for finishing codec request\n");
-               return -ETIMEDOUT;
+               return;
        }
 
        /* Disable interrupts */
@@ -444,8 +444,6 @@ static int stop_streaming(struct vb2_queue *vq)
        ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE);
        if (ret < 0)
                dev_err(icd->parent, "Disable ISI timed out\n");
-
-       return ret;
 }
 
 static struct vb2_ops isi_video_qops = {
index 3e84480..b40bc2e 100644 (file)
@@ -741,7 +741,7 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
        return 0;
 }
 
-static int mx2_stop_streaming(struct vb2_queue *q)
+static void mx2_stop_streaming(struct vb2_queue *q)
 {
        struct soc_camera_device *icd = soc_camera_from_vb2q(q);
        struct soc_camera_host *ici =
@@ -773,8 +773,6 @@ static int mx2_stop_streaming(struct vb2_queue *q)
 
        dma_free_coherent(ici->v4l2_dev.dev,
                          pcdev->discard_size, b, pcdev->discard_buffer_dma);
-
-       return 0;
 }
 
 static struct vb2_ops mx2_videobuf_ops = {
index 9ed81ac..83315df 100644 (file)
@@ -406,7 +406,7 @@ static int mx3_videobuf_init(struct vb2_buffer *vb)
        return 0;
 }
 
-static int mx3_stop_streaming(struct vb2_queue *q)
+static void mx3_stop_streaming(struct vb2_queue *q)
 {
        struct soc_camera_device *icd = soc_camera_from_vb2q(q);
        struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -430,8 +430,6 @@ static int mx3_stop_streaming(struct vb2_queue *q)
        }
 
        spin_unlock_irqrestore(&mx3_cam->lock, flags);
-
-       return 0;
 }
 
 static struct vb2_ops mx3_videobuf_ops = {
index 704eee7..e594230 100644 (file)
@@ -513,7 +513,7 @@ static int rcar_vin_videobuf_init(struct vb2_buffer *vb)
        return 0;
 }
 
-static int rcar_vin_stop_streaming(struct vb2_queue *vq)
+static void rcar_vin_stop_streaming(struct vb2_queue *vq)
 {
        struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
        struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -524,8 +524,6 @@ static int rcar_vin_stop_streaming(struct vb2_queue *vq)
        list_for_each_safe(buf_head, tmp, &priv->capture)
                list_del_init(buf_head);
        spin_unlock_irq(&priv->lock);
-
-       return 0;
 }
 
 static struct vb2_ops rcar_vin_vb2_ops = {
index 3e75a46..20ad4a5 100644 (file)
@@ -471,7 +471,7 @@ static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
        return 0;
 }
 
-static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
+static void sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
 {
        struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq);
        struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -487,7 +487,7 @@ static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
 
        spin_unlock_irq(&pcdev->lock);
 
-       return sh_mobile_ceu_soft_reset(pcdev);
+       sh_mobile_ceu_soft_reset(pcdev);
 }
 
 static struct vb2_ops sh_mobile_ceu_videobuf_ops = {
index 4b8c024..7fec8cd 100644 (file)
@@ -314,7 +314,7 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id a)
        struct soc_camera_device *icd = file->private_data;
        struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 
-       return v4l2_subdev_call(sd, core, s_std, a);
+       return v4l2_subdev_call(sd, video, s_std, a);
 }
 
 static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a)
@@ -322,7 +322,7 @@ static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a)
        struct soc_camera_device *icd = file->private_data;
        struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
 
-       return v4l2_subdev_call(sd, core, g_std, a);
+       return v4l2_subdev_call(sd, video, g_std, a);
 }
 
 static int soc_camera_enum_framesizes(struct file *file, void *fh,
@@ -1277,6 +1277,8 @@ static int soc_camera_probe_finish(struct soc_camera_device *icd)
        sd->grp_id = soc_camera_grp_id(icd);
        v4l2_set_subdev_hostdata(sd, icd);
 
+       v4l2_subdev_call(sd, video, g_tvnorms, &icd->vdev->tvnorms);
+
        ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL);
        if (ret < 0)
                return ret;
@@ -1997,6 +1999,12 @@ static int soc_camera_video_start(struct soc_camera_device *icd)
                return -ENODEV;
 
        video_set_drvdata(icd->vdev, icd);
+       if (icd->vdev->tvnorms == 0) {
+               /* disable the STD API if there are no tvnorms defined */
+               v4l2_disable_ioctl(icd->vdev, VIDIOC_G_STD);
+               v4l2_disable_ioctl(icd->vdev, VIDIOC_S_STD);
+               v4l2_disable_ioctl(icd->vdev, VIDIOC_ENUMSTD);
+       }
        ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
        if (ret < 0) {
                dev_err(icd->pdev, "video_register_device failed: %d\n", ret);
index acfea50..940df40 100644 (file)
@@ -180,16 +180,16 @@ struct csc_data *csc_create(struct platform_device *pdev)
        csc->pdev = pdev;
 
        csc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                       "vpe_csc");
+                       "csc");
        if (csc->res == NULL) {
                dev_err(&pdev->dev, "missing platform resources data\n");
                return ERR_PTR(-ENODEV);
        }
 
        csc->base = devm_ioremap_resource(&pdev->dev, csc->res);
-       if (!csc->base) {
+       if (IS_ERR(csc->base)) {
                dev_err(&pdev->dev, "failed to ioremap\n");
-               return ERR_PTR(-ENOMEM);
+               return csc->base;
        }
 
        return csc;
index 93f0af5..6314171 100644 (file)
@@ -302,9 +302,9 @@ struct sc_data *sc_create(struct platform_device *pdev)
        }
 
        sc->base = devm_ioremap_resource(&pdev->dev, sc->res);
-       if (!sc->base) {
+       if (IS_ERR(sc->base)) {
                dev_err(&pdev->dev, "failed to ioremap\n");
-               return ERR_PTR(-ENOMEM);
+               return sc->base;
        }
 
        return sc;
index e8175e7..a51a013 100644 (file)
@@ -614,8 +614,17 @@ static void dump_dtd(struct vpdma_dtd *dtd)
 /*
  * append an outbound data transfer descriptor to the given descriptor list,
  * this sets up a 'client to memory' VPDMA transfer for the given VPDMA channel
+ *
+ * @list: vpdma desc list to which we add this decriptor
+ * @width: width of the image in pixels in memory
+ * @c_rect: compose params of output image
+ * @fmt: vpdma data format of the buffer
+ * dma_addr: dma address as seen by VPDMA
+ * chan: VPDMA channel
+ * flags: VPDMA flags to configure some descriptor fileds
  */
-void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect,
+void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width,
+               const struct v4l2_rect *c_rect,
                const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
                enum vpdma_channel chan, u32 flags)
 {
@@ -623,6 +632,7 @@ void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect,
        int field = 0;
        int notify = 1;
        int channel, next_chan;
+       struct v4l2_rect rect = *c_rect;
        int depth = fmt->depth;
        int stride;
        struct vpdma_dtd *dtd;
@@ -630,11 +640,15 @@ void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect,
        channel = next_chan = chan_info[chan].num;
 
        if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV &&
-                       fmt->data_type == DATA_TYPE_C420)
+                       fmt->data_type == DATA_TYPE_C420) {
+               rect.height >>= 1;
+               rect.top >>= 1;
                depth = 8;
+       }
 
-       stride = ALIGN((depth * c_rect->width) >> 3, VPDMA_STRIDE_ALIGN);
-       dma_addr += (c_rect->left * depth) >> 3;
+       stride = ALIGN((depth * width) >> 3, VPDMA_STRIDE_ALIGN);
+
+       dma_addr += rect.top * stride + (rect.left * depth >> 3);
 
        dtd = list->next;
        WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size));
@@ -664,31 +678,48 @@ void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect,
 /*
  * append an inbound data transfer descriptor to the given descriptor list,
  * this sets up a 'memory to client' VPDMA transfer for the given VPDMA channel
+ *
+ * @list: vpdma desc list to which we add this decriptor
+ * @width: width of the image in pixels in memory(not the cropped width)
+ * @c_rect: crop params of input image
+ * @fmt: vpdma data format of the buffer
+ * dma_addr: dma address as seen by VPDMA
+ * chan: VPDMA channel
+ * field: top or bottom field info of the input image
+ * flags: VPDMA flags to configure some descriptor fileds
+ * frame_width/height: the complete width/height of the image presented to the
+ *                     client (this makes sense when multiple channels are
+ *                     connected to the same client, forming a larger frame)
+ * start_h, start_v: position where the given channel starts providing pixel
+ *                     data to the client (makes sense when multiple channels
+ *                     contribute to the client)
  */
-void vpdma_add_in_dtd(struct vpdma_desc_list *list, int frame_width,
-               int frame_height, struct v4l2_rect *c_rect,
+void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width,
+               const struct v4l2_rect *c_rect,
                const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
-               enum vpdma_channel chan, int field, u32 flags)
+               enum vpdma_channel chan, int field, u32 flags, int frame_width,
+               int frame_height, int start_h, int start_v)
 {
        int priority = 0;
        int notify = 1;
        int depth = fmt->depth;
        int channel, next_chan;
+       struct v4l2_rect rect = *c_rect;
        int stride;
-       int height = c_rect->height;
        struct vpdma_dtd *dtd;
 
        channel = next_chan = chan_info[chan].num;
 
        if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV &&
                        fmt->data_type == DATA_TYPE_C420) {
-               height >>= 1;
-               frame_height >>= 1;
+               rect.height >>= 1;
+               rect.top >>= 1;
                depth = 8;
        }
 
-       stride = ALIGN((depth * c_rect->width) >> 3, VPDMA_STRIDE_ALIGN);
-       dma_addr += (c_rect->left * depth) >> 3;
+       stride = ALIGN((depth * width) >> 3, VPDMA_STRIDE_ALIGN);
+
+       dma_addr += rect.top * stride + (rect.left * depth >> 3);
 
        dtd = list->next;
        WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size));
@@ -701,13 +732,14 @@ void vpdma_add_in_dtd(struct vpdma_desc_list *list, int frame_width,
                                        !!(flags & VPDMA_DATA_ODD_LINE_SKIP),
                                        stride);
 
-       dtd->xfer_length_height = dtd_xfer_length_height(c_rect->width, height);
+       dtd->xfer_length_height = dtd_xfer_length_height(rect.width,
+                                       rect.height);
        dtd->start_addr = (u32) dma_addr;
        dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED),
                                DTD_DIR_IN, channel, priority, next_chan);
        dtd->frame_width_height = dtd_frame_width_height(frame_width,
                                        frame_height);
-       dtd->start_h_v = dtd_start_h_v(c_rect->left, c_rect->top);
+       dtd->start_h_v = dtd_start_h_v(start_h, start_v);
        dtd->client_attr0 = 0;
        dtd->client_attr1 = 0;
 
@@ -781,7 +813,7 @@ static void vpdma_firmware_cb(const struct firmware *f, void *context)
        /* already initialized */
        if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK,
                        VPDMA_LIST_RDY_SHFT)) {
-               vpdma->ready = true;
+               vpdma->cb(vpdma->pdev);
                return;
        }
 
@@ -811,7 +843,7 @@ static void vpdma_firmware_cb(const struct firmware *f, void *context)
                goto free_buf;
        }
 
-       vpdma->ready = true;
+       vpdma->cb(vpdma->pdev);
 
 free_buf:
        vpdma_unmap_desc_buf(vpdma, &fw_dma_buf);
@@ -839,7 +871,8 @@ static int vpdma_load_firmware(struct vpdma_data *vpdma)
        return 0;
 }
 
-struct vpdma_data *vpdma_create(struct platform_device *pdev)
+struct vpdma_data *vpdma_create(struct platform_device *pdev,
+               void (*cb)(struct platform_device *pdev))
 {
        struct resource *res;
        struct vpdma_data *vpdma;
@@ -854,6 +887,7 @@ struct vpdma_data *vpdma_create(struct platform_device *pdev)
        }
 
        vpdma->pdev = pdev;
+       vpdma->cb = cb;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpdma");
        if (res == NULL) {
index cf40f11..2bd8fb0 100644 (file)
@@ -35,8 +35,8 @@ struct vpdma_data {
 
        struct platform_device  *pdev;
 
-       /* tells whether vpdma firmware is loaded or not */
-       bool ready;
+       /* callback to VPE driver when the firmware is loaded */
+       void (*cb)(struct platform_device *pdev);
 };
 
 enum vpdma_data_format_type {
@@ -186,13 +186,15 @@ void vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client,
                struct vpdma_buf *adb);
 void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list,
                enum vpdma_channel chan);
-void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect,
+void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width,
+               const struct v4l2_rect *c_rect,
                const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
                enum vpdma_channel chan, u32 flags);
-void vpdma_add_in_dtd(struct vpdma_desc_list *list, int frame_width,
-               int frame_height, struct v4l2_rect *c_rect,
+void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width,
+               const struct v4l2_rect *c_rect,
                const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
-               enum vpdma_channel chan, int field, u32 flags);
+               enum vpdma_channel chan, int field, u32 flags, int frame_width,
+               int frame_height, int start_h, int start_v);
 
 /* vpdma list interrupt management */
 void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int list_num,
@@ -208,6 +210,7 @@ void vpdma_set_frame_start_event(struct vpdma_data *vpdma,
 void vpdma_dump_regs(struct vpdma_data *vpdma);
 
 /* initialize vpdma, passed with VPE's platform device pointer */
-struct vpdma_data *vpdma_create(struct platform_device *pdev);
+struct vpdma_data *vpdma_create(struct platform_device *pdev,
+               void (*cb)(struct platform_device *pdev));
 
 #endif
index 5c42188..972f43f 100644 (file)
@@ -410,8 +410,10 @@ static struct vpe_q_data *get_q_data(struct vpe_ctx *ctx,
 {
        switch (type) {
        case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
                return &ctx->q_data[Q_DATA_SRC];
        case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
                return &ctx->q_data[Q_DATA_DST];
        default:
                BUG();
@@ -986,7 +988,6 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port)
        struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_DST];
        const struct vpe_port_data *p_data = &port_data[port];
        struct vb2_buffer *vb = ctx->dst_vb;
-       struct v4l2_rect *c_rect = &q_data->c_rect;
        struct vpe_fmt *fmt = q_data->fmt;
        const struct vpdma_data_format *vpdma_fmt;
        int mv_buf_selector = !ctx->src_mv_buf_selector;
@@ -1015,8 +1016,8 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port)
        if (q_data->flags & Q_DATA_MODE_TILED)
                flags |= VPDMA_DATA_MODE_TILED;
 
-       vpdma_add_out_dtd(&ctx->desc_list, c_rect, vpdma_fmt, dma_addr,
-               p_data->channel, flags);
+       vpdma_add_out_dtd(&ctx->desc_list, q_data->width, &q_data->c_rect,
+               vpdma_fmt, dma_addr, p_data->channel, flags);
 }
 
 static void add_in_dtd(struct vpe_ctx *ctx, int port)
@@ -1024,11 +1025,11 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port)
        struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_SRC];
        const struct vpe_port_data *p_data = &port_data[port];
        struct vb2_buffer *vb = ctx->src_vbs[p_data->vb_index];
-       struct v4l2_rect *c_rect = &q_data->c_rect;
        struct vpe_fmt *fmt = q_data->fmt;
        const struct vpdma_data_format *vpdma_fmt;
        int mv_buf_selector = ctx->src_mv_buf_selector;
        int field = vb->v4l2_buf.field == V4L2_FIELD_BOTTOM;
+       int frame_width, frame_height;
        dma_addr_t dma_addr;
        u32 flags = 0;
 
@@ -1055,8 +1056,15 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port)
        if (q_data->flags & Q_DATA_MODE_TILED)
                flags |= VPDMA_DATA_MODE_TILED;
 
-       vpdma_add_in_dtd(&ctx->desc_list, q_data->width, q_data->height,
-               c_rect, vpdma_fmt, dma_addr, p_data->channel, field, flags);
+       frame_width = q_data->c_rect.width;
+       frame_height = q_data->c_rect.height;
+
+       if (p_data->vb_part && fmt->fourcc == V4L2_PIX_FMT_NV12)
+               frame_height /= 2;
+
+       vpdma_add_in_dtd(&ctx->desc_list, q_data->width, &q_data->c_rect,
+               vpdma_fmt, dma_addr, p_data->channel, field, flags, frame_width,
+               frame_height, 0, 0);
 }
 
 /*
@@ -1585,6 +1593,151 @@ static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
        return set_srcdst_params(ctx);
 }
 
+static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s)
+{
+       struct vpe_q_data *q_data;
+
+       if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+           (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
+               return -EINVAL;
+
+       q_data = get_q_data(ctx, s->type);
+       if (!q_data)
+               return -EINVAL;
+
+       switch (s->target) {
+       case V4L2_SEL_TGT_COMPOSE:
+               /*
+                * COMPOSE target is only valid for capture buffer type, return
+                * error for output buffer type
+                */
+               if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+                       return -EINVAL;
+               break;
+       case V4L2_SEL_TGT_CROP:
+               /*
+                * CROP target is only valid for output buffer type, return
+                * error for capture buffer type
+                */
+               if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       return -EINVAL;
+               break;
+       /*
+        * bound and default crop/compose targets are invalid targets to
+        * try/set
+        */
+       default:
+               return -EINVAL;
+       }
+
+       if (s->r.top < 0 || s->r.left < 0) {
+               vpe_err(ctx->dev, "negative values for top and left\n");
+               s->r.top = s->r.left = 0;
+       }
+
+       v4l_bound_align_image(&s->r.width, MIN_W, q_data->width, 1,
+               &s->r.height, MIN_H, q_data->height, H_ALIGN, S_ALIGN);
+
+       /* adjust left/top if cropping rectangle is out of bounds */
+       if (s->r.left + s->r.width > q_data->width)
+               s->r.left = q_data->width - s->r.width;
+       if (s->r.top + s->r.height > q_data->height)
+               s->r.top = q_data->height - s->r.height;
+
+       return 0;
+}
+
+static int vpe_g_selection(struct file *file, void *fh,
+               struct v4l2_selection *s)
+{
+       struct vpe_ctx *ctx = file2ctx(file);
+       struct vpe_q_data *q_data;
+       bool use_c_rect = false;
+
+       if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+           (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
+               return -EINVAL;
+
+       q_data = get_q_data(ctx, s->type);
+       if (!q_data)
+               return -EINVAL;
+
+       switch (s->target) {
+       case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+               if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+                       return -EINVAL;
+               break;
+       case V4L2_SEL_TGT_CROP_BOUNDS:
+       case V4L2_SEL_TGT_CROP_DEFAULT:
+               if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       return -EINVAL;
+               break;
+       case V4L2_SEL_TGT_COMPOSE:
+               if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+                       return -EINVAL;
+               use_c_rect = true;
+               break;
+       case V4L2_SEL_TGT_CROP:
+               if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       return -EINVAL;
+               use_c_rect = true;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (use_c_rect) {
+               /*
+                * for CROP/COMPOSE target type, return c_rect params from the
+                * respective buffer type
+                */
+               s->r = q_data->c_rect;
+       } else {
+               /*
+                * for DEFAULT/BOUNDS target type, return width and height from
+                * S_FMT of the respective buffer type
+                */
+               s->r.left = 0;
+               s->r.top = 0;
+               s->r.width = q_data->width;
+               s->r.height = q_data->height;
+       }
+
+       return 0;
+}
+
+
+static int vpe_s_selection(struct file *file, void *fh,
+               struct v4l2_selection *s)
+{
+       struct vpe_ctx *ctx = file2ctx(file);
+       struct vpe_q_data *q_data;
+       struct v4l2_selection sel = *s;
+       int ret;
+
+       ret = __vpe_try_selection(ctx, &sel);
+       if (ret)
+               return ret;
+
+       q_data = get_q_data(ctx, sel.type);
+       if (!q_data)
+               return -EINVAL;
+
+       if ((q_data->c_rect.left == sel.r.left) &&
+                       (q_data->c_rect.top == sel.r.top) &&
+                       (q_data->c_rect.width == sel.r.width) &&
+                       (q_data->c_rect.height == sel.r.height)) {
+               vpe_dbg(ctx->dev,
+                       "requested crop/compose values are already set\n");
+               return 0;
+       }
+
+       q_data->c_rect = sel.r;
+
+       return set_srcdst_params(ctx);
+}
+
 static int vpe_reqbufs(struct file *file, void *priv,
                       struct v4l2_requestbuffers *reqbufs)
 {
@@ -1672,6 +1825,9 @@ static const struct v4l2_ioctl_ops vpe_ioctl_ops = {
        .vidioc_try_fmt_vid_out_mplane  = vpe_try_fmt,
        .vidioc_s_fmt_vid_out_mplane    = vpe_s_fmt,
 
+       .vidioc_g_selection             = vpe_g_selection,
+       .vidioc_s_selection             = vpe_s_selection,
+
        .vidioc_reqbufs         = vpe_reqbufs,
        .vidioc_querybuf        = vpe_querybuf,
 
@@ -1784,7 +1940,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
 
        memset(src_vq, 0, sizeof(*src_vq));
        src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
-       src_vq->io_modes = VB2_MMAP;
+       src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
        src_vq->drv_priv = ctx;
        src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
        src_vq->ops = &vpe_qops;
@@ -1797,7 +1953,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
 
        memset(dst_vq, 0, sizeof(*dst_vq));
        dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-       dst_vq->io_modes = VB2_MMAP;
+       dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
        dst_vq->drv_priv = ctx;
        dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
        dst_vq->ops = &vpe_qops;
@@ -1831,11 +1987,6 @@ static int vpe_open(struct file *file)
 
        vpe_dbg(dev, "vpe_open\n");
 
-       if (!dev->vpdma->ready) {
-               vpe_err(dev, "vpdma firmware not loaded\n");
-               return -ENODEV;
-       }
-
        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
        if (!ctx)
                return -ENOMEM;
@@ -2055,10 +2206,40 @@ static void vpe_runtime_put(struct platform_device *pdev)
        WARN_ON(r < 0 && r != -ENOSYS);
 }
 
+static void vpe_fw_cb(struct platform_device *pdev)
+{
+       struct vpe_dev *dev = platform_get_drvdata(pdev);
+       struct video_device *vfd;
+       int ret;
+
+       vfd = &dev->vfd;
+       *vfd = vpe_videodev;
+       vfd->lock = &dev->dev_mutex;
+       vfd->v4l2_dev = &dev->v4l2_dev;
+
+       ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+       if (ret) {
+               vpe_err(dev, "Failed to register video device\n");
+
+               vpe_set_clock_enable(dev, 0);
+               vpe_runtime_put(pdev);
+               pm_runtime_disable(&pdev->dev);
+               v4l2_m2m_release(dev->m2m_dev);
+               vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+               v4l2_device_unregister(&dev->v4l2_dev);
+
+               return;
+       }
+
+       video_set_drvdata(vfd, dev);
+       snprintf(vfd->name, sizeof(vfd->name), "%s", vpe_videodev.name);
+       dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n",
+               vfd->num);
+}
+
 static int vpe_probe(struct platform_device *pdev)
 {
        struct vpe_dev *dev;
-       struct video_device *vfd;
        int ret, irq, func;
 
        dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
@@ -2139,28 +2320,12 @@ static int vpe_probe(struct platform_device *pdev)
                goto runtime_put;
        }
 
-       dev->vpdma = vpdma_create(pdev);
+       dev->vpdma = vpdma_create(pdev, vpe_fw_cb);
        if (IS_ERR(dev->vpdma)) {
                ret = PTR_ERR(dev->vpdma);
                goto runtime_put;
        }
 
-       vfd = &dev->vfd;
-       *vfd = vpe_videodev;
-       vfd->lock = &dev->dev_mutex;
-       vfd->v4l2_dev = &dev->v4l2_dev;
-
-       ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
-       if (ret) {
-               vpe_err(dev, "Failed to register video device\n");
-               goto runtime_put;
-       }
-
-       video_set_drvdata(vfd, dev);
-       snprintf(vfd->name, sizeof(vfd->name), "%s", vpe_videodev.name);
-       dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n",
-               vfd->num);
-
        return 0;
 
 runtime_put:
index ccdadd6..3cb2f35 100644 (file)
@@ -347,7 +347,7 @@ static int timblogiw_s_std(struct file *file, void  *priv, v4l2_std_id std)
        mutex_lock(&lw->lock);
 
        if (TIMBLOGIW_HAS_DECODER(lw))
-               err = v4l2_subdev_call(lw->sd_enc, core, s_std, std);
+               err = v4l2_subdev_call(lw->sd_enc, video, s_std, std);
 
        if (!err)
                fh->cur_norm = timblogiw_get_norm(std);
@@ -800,7 +800,7 @@ static int timblogiw_probe(struct platform_device *pdev)
        if (!pdata->encoder.module_name)
                dev_info(&pdev->dev, "Running without decoder\n");
 
-       lw = kzalloc(sizeof(*lw), GFP_KERNEL);
+       lw = devm_kzalloc(&pdev->dev, sizeof(*lw), GFP_KERNEL);
        if (!lw) {
                err = -ENOMEM;
                goto err;
@@ -820,7 +820,7 @@ static int timblogiw_probe(struct platform_device *pdev)
        strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name));
        err = v4l2_device_register(NULL, &lw->v4l2_dev);
        if (err)
-               goto err_register;
+               goto err;
 
        lw->video_dev.v4l2_dev = &lw->v4l2_dev;
 
@@ -837,8 +837,6 @@ static int timblogiw_probe(struct platform_device *pdev)
 
 err_request:
        v4l2_device_unregister(&lw->v4l2_dev);
-err_register:
-       kfree(lw);
 err:
        dev_err(&pdev->dev, "Failed to register: %d\n", err);
 
@@ -853,8 +851,6 @@ static int timblogiw_remove(struct platform_device *pdev)
 
        v4l2_device_unregister(&lw->v4l2_dev);
 
-       kfree(lw);
-
        return 0;
 }
 
index c6af974..470d353 100644 (file)
@@ -2586,7 +2586,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs)
                        }
                        if (data_norm == 3)
                                data_norm = VINO_DATA_NORM_PAL;
-                       ret = decoder_call(core, s_std, norm);
+                       ret = decoder_call(video, s_std, norm);
                }
 
                spin_lock_irqsave(&vino_drvdata->input_lock, flags);
@@ -2675,7 +2675,7 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input)
                                }
                                if (data_norm == 3)
                                        data_norm = VINO_DATA_NORM_PAL;
-                               ret = decoder_call(core, s_std, norm);
+                               ret = decoder_call(video, s_std, norm);
                        }
 
                        spin_lock_irqsave(&vino_drvdata->input_lock, flags);
@@ -2809,7 +2809,7 @@ static int vino_set_data_norm(struct vino_channel_settings *vcs,
                 * as it may take a while... */
 
                norm = vino_data_norms[data_norm].std;
-               err = decoder_call(core, s_std, norm);
+               err = decoder_call(video, s_std, norm);
 
                spin_lock_irqsave(&vino_drvdata->input_lock, *flags);
 
index 3890f4f..d00bf3d 100644 (file)
@@ -906,12 +906,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
 }
 
 /* abort streaming and wait for last buffer */
-static int stop_streaming(struct vb2_queue *vq)
+static void stop_streaming(struct vb2_queue *vq)
 {
        struct vivi_dev *dev = vb2_get_drv_priv(vq);
        dprintk(dev, 1, "%s\n", __func__);
        vivi_stop_generating(dev);
-       return 0;
 }
 
 static void vivi_lock(struct vb2_queue *vq)
index b48f135..a0595c1 100644 (file)
@@ -720,7 +720,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
        return 0;
 }
 
-static int vsp1_video_stop_streaming(struct vb2_queue *vq)
+static void vsp1_video_stop_streaming(struct vb2_queue *vq)
 {
        struct vsp1_video *video = vb2_get_drv_priv(vq);
        struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);
@@ -743,8 +743,6 @@ static int vsp1_video_stop_streaming(struct vb2_queue *vq)
        spin_lock_irqsave(&video->irqlock, flags);
        INIT_LIST_HEAD(&video->irqqueue);
        spin_unlock_irqrestore(&video->irqlock, flags);
-
-       return 0;
 }
 
 static struct vb2_ops vsp1_video_queue_qops = {
index 5d8f3d4..d5c1df3 100644 (file)
@@ -747,11 +747,19 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data,
                }
 
                /* outbound data */
-               pipe = usb_sndintpipe(ir->usbdev,
-                                     ir->usb_ep_out->bEndpointAddress);
-               usb_fill_int_urb(async_urb, ir->usbdev, pipe,
-                       async_buf, size, mce_async_callback,
-                       ir, ir->usb_ep_out->bInterval);
+               if (usb_endpoint_xfer_int(ir->usb_ep_out)) {
+                       pipe = usb_sndintpipe(ir->usbdev,
+                                        ir->usb_ep_out->bEndpointAddress);
+                       usb_fill_int_urb(async_urb, ir->usbdev, pipe, async_buf,
+                                        size, mce_async_callback, ir,
+                                        ir->usb_ep_out->bInterval);
+               } else {
+                       pipe = usb_sndbulkpipe(ir->usbdev,
+                                        ir->usb_ep_out->bEndpointAddress);
+                       usb_fill_bulk_urb(async_urb, ir->usbdev, pipe,
+                                        async_buf, size, mce_async_callback,
+                                        ir);
+               }
                memcpy(async_buf, data, size);
 
        } else if (urb_type == MCEUSB_RX) {
@@ -1269,32 +1277,26 @@ static int mceusb_dev_probe(struct usb_interface *intf,
        for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
                ep = &idesc->endpoint[i].desc;
 
-               if ((ep_in == NULL)
-                       && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
-                           == USB_DIR_IN)
-                       && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                           == USB_ENDPOINT_XFER_BULK)
-                       || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                           == USB_ENDPOINT_XFER_INT))) {
-
-                       ep_in = ep;
-                       ep_in->bmAttributes = USB_ENDPOINT_XFER_INT;
-                       ep_in->bInterval = 1;
-                       dev_dbg(&intf->dev, "acceptable inbound endpoint found");
+               if (ep_in == NULL) {
+                       if (usb_endpoint_is_bulk_in(ep)) {
+                               ep_in = ep;
+                               dev_dbg(&intf->dev, "acceptable bulk inbound endpoint found\n");
+                       } else if (usb_endpoint_is_int_in(ep)) {
+                               ep_in = ep;
+                               ep_in->bInterval = 1;
+                               dev_dbg(&intf->dev, "acceptable interrupt inbound endpoint found\n");
+                       }
                }
 
-               if ((ep_out == NULL)
-                       && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
-                           == USB_DIR_OUT)
-                       && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                           == USB_ENDPOINT_XFER_BULK)
-                       || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-                           == USB_ENDPOINT_XFER_INT))) {
-
-                       ep_out = ep;
-                       ep_out->bmAttributes = USB_ENDPOINT_XFER_INT;
-                       ep_out->bInterval = 1;
-                       dev_dbg(&intf->dev, "acceptable outbound endpoint found");
+               if (ep_out == NULL) {
+                       if (usb_endpoint_is_bulk_out(ep)) {
+                               ep_out = ep;
+                               dev_dbg(&intf->dev, "acceptable bulk outbound endpoint found\n");
+                       } else if (usb_endpoint_is_int_out(ep)) {
+                               ep_out = ep;
+                               ep_out->bInterval = 1;
+                               dev_dbg(&intf->dev, "acceptable interrupt outbound endpoint found\n");
+                       }
                }
        }
        if (ep_in == NULL) {
@@ -1302,7 +1304,10 @@ static int mceusb_dev_probe(struct usb_interface *intf,
                return -ENODEV;
        }
 
-       pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
+       if (usb_endpoint_xfer_int(ep_in))
+               pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
+       else
+               pipe = usb_rcvbulkpipe(dev, ep_in->bEndpointAddress);
        maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
 
        ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL);
index a128488..22b6b8b 100644 (file)
@@ -230,6 +230,13 @@ config MEDIA_TUNER_TUA9001
        help
          Infineon TUA 9001 silicon tuner driver.
 
+config MEDIA_TUNER_SI2157
+       tristate "Silicon Labs Si2157 silicon tuner"
+       depends on MEDIA_SUPPORT && I2C
+       default m if !MEDIA_SUBDRV_AUTOSELECT
+       help
+         Silicon Labs Si2157 silicon tuner driver.
+
 config MEDIA_TUNER_IT913X
        tristate "ITE Tech IT913x silicon tuner"
        depends on MEDIA_SUPPORT && I2C
index efe82a9..a6ff0c6 100644 (file)
@@ -31,6 +31,7 @@ obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
 obj-$(CONFIG_MEDIA_TUNER_E4000) += e4000.o
 obj-$(CONFIG_MEDIA_TUNER_FC2580) += fc2580.o
 obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o
+obj-$(CONFIG_MEDIA_TUNER_SI2157) += si2157.o
 obj-$(CONFIG_MEDIA_TUNER_M88TS2022) += m88ts2022.o
 obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o
 obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o
diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
new file mode 100644 (file)
index 0000000..271a752
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Silicon Labs Si2157 silicon tuner driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ */
+
+#include "si2157_priv.h"
+
+/* execute firmware command */
+static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd)
+{
+       int ret;
+       u8 buf[1];
+       unsigned long timeout;
+
+       mutex_lock(&s->i2c_mutex);
+
+       if (cmd->len) {
+               /* write cmd and args for firmware */
+               ret = i2c_master_send(s->client, cmd->args, cmd->len);
+               if (ret < 0) {
+                       goto err_mutex_unlock;
+               } else if (ret != cmd->len) {
+                       ret = -EREMOTEIO;
+                       goto err_mutex_unlock;
+               }
+       }
+
+       /* wait cmd execution terminate */
+       #define TIMEOUT 80
+       timeout = jiffies + msecs_to_jiffies(TIMEOUT);
+       while (!time_after(jiffies, timeout)) {
+               ret = i2c_master_recv(s->client, buf, 1);
+               if (ret < 0) {
+                       goto err_mutex_unlock;
+               } else if (ret != 1) {
+                       ret = -EREMOTEIO;
+                       goto err_mutex_unlock;
+               }
+
+               /* firmware ready? */
+               if ((buf[0] >> 7) & 0x01)
+                       break;
+       }
+
+       dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n", __func__,
+                       jiffies_to_msecs(jiffies) -
+                       (jiffies_to_msecs(timeout) - TIMEOUT));
+
+       if (!(buf[0] >> 7) & 0x01) {
+               ret = -ETIMEDOUT;
+               goto err_mutex_unlock;
+       } else {
+               ret = 0;
+       }
+
+err_mutex_unlock:
+       mutex_unlock(&s->i2c_mutex);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       return ret;
+}
+
+static int si2157_init(struct dvb_frontend *fe)
+{
+       struct si2157 *s = fe->tuner_priv;
+
+       dev_dbg(&s->client->dev, "%s:\n", __func__);
+
+       s->active = true;
+
+       return 0;
+}
+
+static int si2157_sleep(struct dvb_frontend *fe)
+{
+       struct si2157 *s = fe->tuner_priv;
+
+       dev_dbg(&s->client->dev, "%s:\n", __func__);
+
+       s->active = false;
+
+       return 0;
+}
+
+static int si2157_set_params(struct dvb_frontend *fe)
+{
+       struct si2157 *s = fe->tuner_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       int ret;
+       struct si2157_cmd cmd;
+
+       dev_dbg(&s->client->dev,
+                       "%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n",
+                       __func__, c->delivery_system, c->frequency,
+                       c->bandwidth_hz);
+
+       if (!s->active) {
+               ret = -EAGAIN;
+               goto err;
+       }
+
+       /* configure? */
+       cmd.args[0] = 0xc0;
+       cmd.args[1] = 0x00;
+       cmd.args[2] = 0x0c;
+       cmd.args[3] = 0x00;
+       cmd.args[4] = 0x00;
+       cmd.args[5] = 0x01;
+       cmd.args[6] = 0x01;
+       cmd.args[7] = 0x01;
+       cmd.args[8] = 0x01;
+       cmd.args[9] = 0x01;
+       cmd.args[10] = 0x01;
+       cmd.args[11] = 0x02;
+       cmd.args[12] = 0x00;
+       cmd.args[13] = 0x00;
+       cmd.args[14] = 0x01;
+       cmd.len = 15;
+       ret = si2157_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       cmd.args[0] = 0x02;
+       cmd.len = 1;
+       ret = si2157_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       cmd.args[0] = 0x01;
+       cmd.args[1] = 0x01;
+       cmd.len = 2;
+       ret = si2157_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       /* set frequency */
+       cmd.args[0] = 0x41;
+       cmd.args[1] = 0x00;
+       cmd.args[2] = 0x00;
+       cmd.args[3] = 0x00;
+       cmd.args[4] = (c->frequency >>  0) & 0xff;
+       cmd.args[5] = (c->frequency >>  8) & 0xff;
+       cmd.args[6] = (c->frequency >> 16) & 0xff;
+       cmd.args[7] = (c->frequency >> 24) & 0xff;
+       cmd.len = 8;
+       ret = si2157_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
+       return ret;
+}
+
+static const struct dvb_tuner_ops si2157_tuner_ops = {
+       .info = {
+               .name           = "Silicon Labs Si2157",
+               .frequency_min  = 110000000,
+               .frequency_max  = 862000000,
+       },
+
+       .init = si2157_init,
+       .sleep = si2157_sleep,
+       .set_params = si2157_set_params,
+};
+
+static int si2157_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct si2157_config *cfg = client->dev.platform_data;
+       struct dvb_frontend *fe = cfg->fe;
+       struct si2157 *s;
+       struct si2157_cmd cmd;
+       int ret;
+
+       s = kzalloc(sizeof(struct si2157), GFP_KERNEL);
+       if (!s) {
+               ret = -ENOMEM;
+               dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+               goto err;
+       }
+
+       s->client = client;
+       s->fe = cfg->fe;
+       mutex_init(&s->i2c_mutex);
+
+       /* check if the tuner is there */
+       cmd.len = 0;
+       ret = si2157_cmd_execute(s, &cmd);
+       if (ret)
+               goto err;
+
+       fe->tuner_priv = s;
+       memcpy(&fe->ops.tuner_ops, &si2157_tuner_ops,
+                       sizeof(struct dvb_tuner_ops));
+
+       i2c_set_clientdata(client, s);
+
+       dev_info(&s->client->dev,
+                       "%s: Silicon Labs Si2157 successfully attached\n",
+                       KBUILD_MODNAME);
+       return 0;
+err:
+       dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
+       kfree(s);
+
+       return ret;
+}
+
+static int si2157_remove(struct i2c_client *client)
+{
+       struct si2157 *s = i2c_get_clientdata(client);
+       struct dvb_frontend *fe = s->fe;
+
+       dev_dbg(&client->dev, "%s:\n", __func__);
+
+       memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
+       fe->tuner_priv = NULL;
+       kfree(s);
+
+       return 0;
+}
+
+static const struct i2c_device_id si2157_id[] = {
+       {"si2157", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, si2157_id);
+
+static struct i2c_driver si2157_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "si2157",
+       },
+       .probe          = si2157_probe,
+       .remove         = si2157_remove,
+       .id_table       = si2157_id,
+};
+
+module_i2c_driver(si2157_driver);
+
+MODULE_DESCRIPTION("Silicon Labs Si2157 silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/si2157.h b/drivers/media/tuners/si2157.h
new file mode 100644 (file)
index 0000000..f469a09
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Silicon Labs Si2157 silicon tuner driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.
+ */
+
+#ifndef SI2157_H
+#define SI2157_H
+
+#include <linux/kconfig.h>
+#include "dvb_frontend.h"
+
+/*
+ * I2C address
+ * 0x60
+ */
+struct si2157_config {
+       /*
+        * frontend
+        */
+       struct dvb_frontend *fe;
+};
+
+#endif
diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h
new file mode 100644 (file)
index 0000000..6cc6c6f
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Silicon Labs Si2157 silicon tuner driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.
+ */
+
+#ifndef SI2157_PRIV_H
+#define SI2157_PRIV_H
+
+#include "si2157.h"
+
+/* state struct */
+struct si2157 {
+       struct mutex i2c_mutex;
+       struct i2c_client *client;
+       struct dvb_frontend *fe;
+       bool active;
+};
+
+/* firmare command struct */
+#define SI2157_ARGLEN      30
+struct si2157_cmd {
+       u8 args[SI2157_ARGLEN];
+       unsigned len;
+};
+
+#endif
index 5cd09a6..2b3d514 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/moduleparam.h>
 #include <linux/videodev2.h>
 #include <linux/delay.h>
+#include <linux/workqueue.h>
 #include <linux/dvb/frontend.h>
 #include <linux/i2c.h>
 
@@ -65,26 +66,25 @@ struct xc5000_priv {
        u16 pll_register_no;
        u8 init_status_supported;
        u8 fw_checksum_supported;
+
+       struct dvb_frontend *fe;
+       struct delayed_work timer_sleep;
 };
 
 /* Misc Defines */
 #define MAX_TV_STANDARD                        24
 #define XC_MAX_I2C_WRITE_LENGTH                64
 
+/* Time to suspend after the .sleep callback is called */
+#define XC5000_SLEEP_TIME              5000 /* ms */
+
 /* Signal Types */
 #define XC_RF_MODE_AIR                 0
 #define XC_RF_MODE_CABLE               1
 
-/* Result codes */
-#define XC_RESULT_SUCCESS              0
-#define XC_RESULT_RESET_FAILURE                1
-#define XC_RESULT_I2C_WRITE_FAILURE    2
-#define XC_RESULT_I2C_READ_FAILURE     3
-#define XC_RESULT_OUT_OF_RANGE         5
-
 /* Product id */
 #define XC_PRODUCT_ID_FW_NOT_LOADED    0x2000
-#define XC_PRODUCT_ID_FW_LOADED        0x1388
+#define XC_PRODUCT_ID_FW_LOADED        0x1388
 
 /* Registers */
 #define XREG_INIT         0x00
@@ -152,16 +152,16 @@ struct xc5000_priv {
 
 */
 struct XC_TV_STANDARD {
-       char *Name;
-       u16 AudioMode;
-       u16 VideoMode;
+       char *name;
+       u16 audio_mode;
+       u16 video_mode;
 };
 
 /* Tuner standards */
 #define MN_NTSC_PAL_BTSC       0
 #define MN_NTSC_PAL_A2         1
 #define MN_NTSC_PAL_EIAJ       2
-#define MN_NTSC_PAL_Mono       3
+#define MN_NTSC_PAL_MONO       3
 #define BG_PAL_A2              4
 #define BG_PAL_NICAM           5
 #define BG_PAL_MONO            6
@@ -171,19 +171,19 @@ struct XC_TV_STANDARD {
 #define DK_PAL_NICAM           10
 #define DK_PAL_MONO            11
 #define DK_SECAM_A2DK1         12
-#define DK_SECAM_A2LDK3        13
-#define DK_SECAM_A2MONO        14
+#define DK_SECAM_A2LDK3                13
+#define DK_SECAM_A2MONO                14
 #define L_SECAM_NICAM          15
 #define LC_SECAM_NICAM         16
 #define DTV6                   17
 #define DTV8                   18
 #define DTV7_8                 19
 #define DTV7                   20
-#define FM_Radio_INPUT2        21
-#define FM_Radio_INPUT1        22
-#define FM_Radio_INPUT1_MONO   23
+#define FM_RADIO_INPUT2                21
+#define FM_RADIO_INPUT1                22
+#define FM_RADIO_INPUT1_MONO   23
 
-static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
+static struct XC_TV_STANDARD xc5000_standard[MAX_TV_STANDARD] = {
        {"M/N-NTSC/PAL-BTSC", 0x0400, 0x8020},
        {"M/N-NTSC/PAL-A2",   0x0600, 0x8020},
        {"M/N-NTSC/PAL-EIAJ", 0x0440, 0x8020},
@@ -249,7 +249,7 @@ static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)
 static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force);
 static int xc5000_is_firmware_loaded(struct dvb_frontend *fe);
 static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val);
-static int xc5000_TunerReset(struct dvb_frontend *fe);
+static int xc5000_tuner_reset(struct dvb_frontend *fe);
 
 static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
 {
@@ -258,9 +258,9 @@ static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
 
        if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
                printk(KERN_ERR "xc5000: I2C write failed (len=%i)\n", len);
-               return XC_RESULT_I2C_WRITE_FAILURE;
+               return -EREMOTEIO;
        }
-       return XC_RESULT_SUCCESS;
+       return 0;
 }
 
 #if 0
@@ -297,15 +297,10 @@ static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val)
        }
 
        *val = (bval[0] << 8) | bval[1];
-       return XC_RESULT_SUCCESS;
-}
-
-static void xc_wait(int wait_ms)
-{
-       msleep(wait_ms);
+       return 0;
 }
 
-static int xc5000_TunerReset(struct dvb_frontend *fe)
+static int xc5000_tuner_reset(struct dvb_frontend *fe)
 {
        struct xc5000_priv *priv = fe->tuner_priv;
        int ret;
@@ -320,43 +315,43 @@ static int xc5000_TunerReset(struct dvb_frontend *fe)
                                           XC5000_TUNER_RESET, 0);
                if (ret) {
                        printk(KERN_ERR "xc5000: reset failed\n");
-                       return XC_RESULT_RESET_FAILURE;
+                       return ret;
                }
        } else {
                printk(KERN_ERR "xc5000: no tuner reset callback function, fatal\n");
-               return XC_RESULT_RESET_FAILURE;
+               return -EINVAL;
        }
-       return XC_RESULT_SUCCESS;
+       return 0;
 }
 
-static int xc_write_reg(struct xc5000_priv *priv, u16 regAddr, u16 i2cData)
+static int xc_write_reg(struct xc5000_priv *priv, u16 reg_addr, u16 i2c_data)
 {
        u8 buf[4];
-       int WatchDogTimer = 100;
+       int watch_dog_timer = 100;
        int result;
 
-       buf[0] = (regAddr >> 8) & 0xFF;
-       buf[1] = regAddr & 0xFF;
-       buf[2] = (i2cData >> 8) & 0xFF;
-       buf[3] = i2cData & 0xFF;
+       buf[0] = (reg_addr >> 8) & 0xFF;
+       buf[1] = reg_addr & 0xFF;
+       buf[2] = (i2c_data >> 8) & 0xFF;
+       buf[3] = i2c_data & 0xFF;
        result = xc_send_i2c_data(priv, buf, 4);
-       if (result == XC_RESULT_SUCCESS) {
+       if (result == 0) {
                /* wait for busy flag to clear */
-               while ((WatchDogTimer > 0) && (result == XC_RESULT_SUCCESS)) {
+               while ((watch_dog_timer > 0) && (result == 0)) {
                        result = xc5000_readreg(priv, XREG_BUSY, (u16 *)buf);
-                       if (result == XC_RESULT_SUCCESS) {
+                       if (result == 0) {
                                if ((buf[0] == 0) && (buf[1] == 0)) {
                                        /* busy flag cleared */
                                        break;
                                } else {
-                                       xc_wait(5); /* wait 5 ms */
-                                       WatchDogTimer--;
+                                       msleep(5); /* wait 5 ms */
+                                       watch_dog_timer--;
                                }
                        }
                }
        }
-       if (WatchDogTimer <= 0)
-               result = XC_RESULT_I2C_WRITE_FAILURE;
+       if (watch_dog_timer <= 0)
+               result = -EREMOTEIO;
 
        return result;
 }
@@ -375,13 +370,13 @@ static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)
                len = i2c_sequence[index] * 256 + i2c_sequence[index+1];
                if (len == 0x0000) {
                        /* RESET command */
-                       result = xc5000_TunerReset(fe);
+                       result = xc5000_tuner_reset(fe);
                        index += 2;
-                       if (result != XC_RESULT_SUCCESS)
+                       if (result != 0)
                                return result;
                } else if (len & 0x8000) {
                        /* WAIT command */
-                       xc_wait(len & 0x7FFF);
+                       msleep(len & 0x7FFF);
                        index += 2;
                } else {
                        /* Send i2c data whilst ensuring individual transactions
@@ -404,7 +399,7 @@ static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)
                                result = xc_send_i2c_data(priv, buf,
                                        nbytes_to_send);
 
-                               if (result != XC_RESULT_SUCCESS)
+                               if (result != 0)
                                        return result;
 
                                pos += nbytes_to_send - 2;
@@ -412,7 +407,7 @@ static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)
                        index += len;
                }
        }
-       return XC_RESULT_SUCCESS;
+       return 0;
 }
 
 static int xc_initialize(struct xc5000_priv *priv)
@@ -421,29 +416,29 @@ static int xc_initialize(struct xc5000_priv *priv)
        return xc_write_reg(priv, XREG_INIT, 0);
 }
 
-static int xc_SetTVStandard(struct xc5000_priv *priv,
-       u16 VideoMode, u16 AudioMode, u8 RadioMode)
+static int xc_set_tv_standard(struct xc5000_priv *priv,
+       u16 video_mode, u16 audio_mode, u8 radio_mode)
 {
        int ret;
-       dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, VideoMode, AudioMode);
-       if (RadioMode) {
+       dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, video_mode, audio_mode);
+       if (radio_mode) {
                dprintk(1, "%s() Standard = %s\n",
                        __func__,
-                       XC5000_Standard[RadioMode].Name);
+                       xc5000_standard[radio_mode].name);
        } else {
                dprintk(1, "%s() Standard = %s\n",
                        __func__,
-                       XC5000_Standard[priv->video_standard].Name);
+                       xc5000_standard[priv->video_standard].name);
        }
 
-       ret = xc_write_reg(priv, XREG_VIDEO_MODE, VideoMode);
-       if (ret == XC_RESULT_SUCCESS)
-               ret = xc_write_reg(priv, XREG_AUDIO_MODE, AudioMode);
+       ret = xc_write_reg(priv, XREG_VIDEO_MODE, video_mode);
+       if (ret == 0)
+               ret = xc_write_reg(priv, XREG_AUDIO_MODE, audio_mode);
 
        return ret;
 }
 
-static int xc_SetSignalSource(struct xc5000_priv *priv, u16 rf_mode)
+static int xc_set_signal_source(struct xc5000_priv *priv, u16 rf_mode)
 {
        dprintk(1, "%s(%d) Source = %s\n", __func__, rf_mode,
                rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE");
@@ -459,7 +454,7 @@ static int xc_SetSignalSource(struct xc5000_priv *priv, u16 rf_mode)
 
 static const struct dvb_tuner_ops xc5000_tuner_ops;
 
-static int xc_set_RF_frequency(struct xc5000_priv *priv, u32 freq_hz)
+static int xc_set_rf_frequency(struct xc5000_priv *priv, u32 freq_hz)
 {
        u16 freq_code;
 
@@ -467,7 +462,7 @@ static int xc_set_RF_frequency(struct xc5000_priv *priv, u32 freq_hz)
 
        if ((freq_hz > xc5000_tuner_ops.info.frequency_max) ||
                (freq_hz < xc5000_tuner_ops.info.frequency_min))
-               return XC_RESULT_OUT_OF_RANGE;
+               return -EINVAL;
 
        freq_code = (u16)(freq_hz / 15625);
 
@@ -488,7 +483,7 @@ static int xc_set_IF_frequency(struct xc5000_priv *priv, u32 freq_khz)
 }
 
 
-static int xc_get_ADC_Envelope(struct xc5000_priv *priv, u16 *adc_envelope)
+static int xc_get_adc_envelope(struct xc5000_priv *priv, u16 *adc_envelope)
 {
        return xc5000_readreg(priv, XREG_ADC_ENV, adc_envelope);
 }
@@ -496,14 +491,14 @@ static int xc_get_ADC_Envelope(struct xc5000_priv *priv, u16 *adc_envelope)
 static int xc_get_frequency_error(struct xc5000_priv *priv, u32 *freq_error_hz)
 {
        int result;
-       u16 regData;
+       u16 reg_data;
        u32 tmp;
 
-       result = xc5000_readreg(priv, XREG_FREQ_ERROR, &regData);
-       if (result != XC_RESULT_SUCCESS)
+       result = xc5000_readreg(priv, XREG_FREQ_ERROR, &reg_data);
+       if (result != 0)
                return result;
 
-       tmp = (u32)regData;
+       tmp = (u32)reg_data;
        (*freq_error_hz) = (tmp * 15625) / 1000;
        return result;
 }
@@ -521,7 +516,7 @@ static int xc_get_version(struct xc5000_priv *priv,
        int result;
 
        result = xc5000_readreg(priv, XREG_VERSION, &data);
-       if (result != XC_RESULT_SUCCESS)
+       if (result != 0)
                return result;
 
        (*hw_majorversion) = (data >> 12) & 0x0F;
@@ -539,14 +534,14 @@ static int xc_get_buildversion(struct xc5000_priv *priv, u16 *buildrev)
 
 static int xc_get_hsync_freq(struct xc5000_priv *priv, u32 *hsync_freq_hz)
 {
-       u16 regData;
+       u16 reg_data;
        int result;
 
-       result = xc5000_readreg(priv, XREG_HSYNC_FREQ, &regData);
-       if (result != XC_RESULT_SUCCESS)
+       result = xc5000_readreg(priv, XREG_HSYNC_FREQ, &reg_data);
+       if (result != 0)
                return result;
 
-       (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100;
+       (*hsync_freq_hz) = ((reg_data & 0x0fff) * 763)/100;
        return result;
 }
 
@@ -570,19 +565,19 @@ static int xc_get_totalgain(struct xc5000_priv *priv, u16 *totalgain)
        return xc5000_readreg(priv, XREG_TOTALGAIN, totalgain);
 }
 
-static u16 WaitForLock(struct xc5000_priv *priv)
+static u16 wait_for_lock(struct xc5000_priv *priv)
 {
-       u16 lockState = 0;
-       int watchDogCount = 40;
-
-       while ((lockState == 0) && (watchDogCount > 0)) {
-               xc_get_lock_status(priv, &lockState);
-               if (lockState != 1) {
-                       xc_wait(5);
-                       watchDogCount--;
+       u16 lock_state = 0;
+       int watch_dog_count = 40;
+
+       while ((lock_state == 0) && (watch_dog_count > 0)) {
+               xc_get_lock_status(priv, &lock_state);
+               if (lock_state != 1) {
+                       msleep(5);
+                       watch_dog_count--;
                }
        }
-       return lockState;
+       return lock_state;
 }
 
 #define XC_TUNE_ANALOG  0
@@ -593,11 +588,11 @@ static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode)
 
        dprintk(1, "%s(%u)\n", __func__, freq_hz);
 
-       if (xc_set_RF_frequency(priv, freq_hz) != XC_RESULT_SUCCESS)
+       if (xc_set_rf_frequency(priv, freq_hz) != 0)
                return 0;
 
        if (mode == XC_TUNE_ANALOG) {
-               if (WaitForLock(priv) == 1)
+               if (wait_for_lock(priv) == 1)
                        found = 1;
        }
 
@@ -607,7 +602,7 @@ static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode)
 static int xc_set_xtal(struct dvb_frontend *fe)
 {
        struct xc5000_priv *priv = fe->tuner_priv;
-       int ret = XC_RESULT_SUCCESS;
+       int ret = 0;
 
        switch (priv->chip_id) {
        default:
@@ -649,23 +644,22 @@ static int xc5000_fwupload(struct dvb_frontend *fe)
                priv->i2c_props.adap->dev.parent);
        if (ret) {
                printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n");
-               ret = XC_RESULT_RESET_FAILURE;
                goto out;
        } else {
                printk(KERN_DEBUG "xc5000: firmware read %Zu bytes.\n",
                       fw->size);
-               ret = XC_RESULT_SUCCESS;
+               ret = 0;
        }
 
        if (fw->size != desired_fw->size) {
                printk(KERN_ERR "xc5000: firmware incorrect size\n");
-               ret = XC_RESULT_RESET_FAILURE;
+               ret = -EINVAL;
        } else {
                printk(KERN_INFO "xc5000: firmware uploading...\n");
                ret = xc_load_i2c_sequence(fe,  fw->data);
-               if (XC_RESULT_SUCCESS == ret)
+               if (0 == ret)
                        ret = xc_set_xtal(fe);
-               if (XC_RESULT_SUCCESS == ret)
+               if (0 == ret)
                        printk(KERN_INFO "xc5000: firmware upload complete...\n");
                else
                        printk(KERN_ERR "xc5000: firmware upload failed...\n");
@@ -695,9 +689,9 @@ static void xc_debug_dump(struct xc5000_priv *priv)
         * Frame Lines needs two frame times after initial lock
         * before it is valid.
         */
-       xc_wait(100);
+       msleep(100);
 
-       xc_get_ADC_Envelope(priv,  &adc_envelope);
+       xc_get_adc_envelope(priv,  &adc_envelope);
        dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope);
 
        xc_get_frequency_error(priv, &freq_error_hz);
@@ -744,7 +738,7 @@ static int xc5000_set_params(struct dvb_frontend *fe)
        u32 freq = fe->dtv_property_cache.frequency;
        u32 delsys  = fe->dtv_property_cache.delivery_system;
 
-       if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) {
+       if (xc_load_fw_and_init_tuner(fe, 0) != 0) {
                dprintk(1, "Unable to load firmware and init tuner\n");
                return -EINVAL;
        }
@@ -820,24 +814,24 @@ static int xc5000_set_params(struct dvb_frontend *fe)
        dprintk(1, "%s() frequency=%d (compensated to %d)\n",
                __func__, freq, priv->freq_hz);
 
-       ret = xc_SetSignalSource(priv, priv->rf_mode);
-       if (ret != XC_RESULT_SUCCESS) {
+       ret = xc_set_signal_source(priv, priv->rf_mode);
+       if (ret != 0) {
                printk(KERN_ERR
-                       "xc5000: xc_SetSignalSource(%d) failed\n",
+                       "xc5000: xc_set_signal_source(%d) failed\n",
                        priv->rf_mode);
                return -EREMOTEIO;
        }
 
-       ret = xc_SetTVStandard(priv,
-               XC5000_Standard[priv->video_standard].VideoMode,
-               XC5000_Standard[priv->video_standard].AudioMode, 0);
-       if (ret != XC_RESULT_SUCCESS) {
-               printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
+       ret = xc_set_tv_standard(priv,
+               xc5000_standard[priv->video_standard].video_mode,
+               xc5000_standard[priv->video_standard].audio_mode, 0);
+       if (ret != 0) {
+               printk(KERN_ERR "xc5000: xc_set_tv_standard failed\n");
                return -EREMOTEIO;
        }
 
        ret = xc_set_IF_frequency(priv, priv->if_khz);
-       if (ret != XC_RESULT_SUCCESS) {
+       if (ret != 0) {
                printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n",
                       priv->if_khz);
                return -EIO;
@@ -862,15 +856,15 @@ static int xc5000_is_firmware_loaded(struct dvb_frontend *fe)
        u16 id;
 
        ret = xc5000_readreg(priv, XREG_PRODUCT_ID, &id);
-       if (ret == XC_RESULT_SUCCESS) {
+       if (ret == 0) {
                if (id == XC_PRODUCT_ID_FW_NOT_LOADED)
-                       ret = XC_RESULT_RESET_FAILURE;
+                       ret = -ENOENT;
                else
-                       ret = XC_RESULT_SUCCESS;
+                       ret = 0;
        }
 
        dprintk(1, "%s() returns %s id = 0x%x\n", __func__,
-               ret == XC_RESULT_SUCCESS ? "True" : "False", id);
+               ret == 0 ? "True" : "False", id);
        return ret;
 }
 
@@ -936,19 +930,19 @@ static int xc5000_set_tv_freq(struct dvb_frontend *fe,
        }
 
 tune_channel:
-       ret = xc_SetSignalSource(priv, priv->rf_mode);
-       if (ret != XC_RESULT_SUCCESS) {
+       ret = xc_set_signal_source(priv, priv->rf_mode);
+       if (ret != 0) {
                printk(KERN_ERR
-                       "xc5000: xc_SetSignalSource(%d) failed\n",
+                       "xc5000: xc_set_signal_source(%d) failed\n",
                        priv->rf_mode);
                return -EREMOTEIO;
        }
 
-       ret = xc_SetTVStandard(priv,
-               XC5000_Standard[priv->video_standard].VideoMode,
-               XC5000_Standard[priv->video_standard].AudioMode, 0);
-       if (ret != XC_RESULT_SUCCESS) {
-               printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
+       ret = xc_set_tv_standard(priv,
+               xc5000_standard[priv->video_standard].video_mode,
+               xc5000_standard[priv->video_standard].audio_mode, 0);
+       if (ret != 0) {
+               printk(KERN_ERR "xc5000: xc_set_tv_standard failed\n");
                return -EREMOTEIO;
        }
 
@@ -966,7 +960,7 @@ tune_channel:
                        /* PLL is unlocked, force reload of the firmware */
                        dprintk(1, "xc5000: PLL not locked (0x%x).  Reloading...\n",
                                pll_lock_status);
-                       if (xc_load_fw_and_init_tuner(fe, 1) != XC_RESULT_SUCCESS) {
+                       if (xc_load_fw_and_init_tuner(fe, 1) != 0) {
                                printk(KERN_ERR "xc5000: Unable to reload fw\n");
                                return -EREMOTEIO;
                        }
@@ -993,11 +987,11 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe,
        }
 
        if (priv->radio_input == XC5000_RADIO_FM1)
-               radio_input = FM_Radio_INPUT1;
+               radio_input = FM_RADIO_INPUT1;
        else if  (priv->radio_input == XC5000_RADIO_FM2)
-               radio_input = FM_Radio_INPUT2;
+               radio_input = FM_RADIO_INPUT2;
        else if  (priv->radio_input == XC5000_RADIO_FM1_MONO)
-               radio_input = FM_Radio_INPUT1_MONO;
+               radio_input = FM_RADIO_INPUT1_MONO;
        else {
                dprintk(1, "%s() unknown radio input %d\n", __func__,
                        priv->radio_input);
@@ -1008,18 +1002,18 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe,
 
        priv->rf_mode = XC_RF_MODE_AIR;
 
-       ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode,
-                              XC5000_Standard[radio_input].AudioMode, radio_input);
+       ret = xc_set_tv_standard(priv, xc5000_standard[radio_input].video_mode,
+                              xc5000_standard[radio_input].audio_mode, radio_input);
 
-       if (ret != XC_RESULT_SUCCESS) {
-               printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
+       if (ret != 0) {
+               printk(KERN_ERR "xc5000: xc_set_tv_standard failed\n");
                return -EREMOTEIO;
        }
 
-       ret = xc_SetSignalSource(priv, priv->rf_mode);
-       if (ret != XC_RESULT_SUCCESS) {
+       ret = xc_set_signal_source(priv, priv->rf_mode);
+       if (ret != 0) {
                printk(KERN_ERR
-                       "xc5000: xc_SetSignalSource(%d) failed\n",
+                       "xc5000: xc_set_signal_source(%d) failed\n",
                        priv->rf_mode);
                return -EREMOTEIO;
        }
@@ -1044,7 +1038,7 @@ static int xc5000_set_analog_params(struct dvb_frontend *fe,
        if (priv->i2c_props.adap == NULL)
                return -EINVAL;
 
-       if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) {
+       if (xc_load_fw_and_init_tuner(fe, 0) != 0) {
                dprintk(1, "Unable to load firmware and init tuner\n");
                return -EINVAL;
        }
@@ -1105,23 +1099,25 @@ static int xc5000_get_status(struct dvb_frontend *fe, u32 *status)
 static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force)
 {
        struct xc5000_priv *priv = fe->tuner_priv;
-       int ret = XC_RESULT_SUCCESS;
+       int ret = 0;
        u16 pll_lock_status;
        u16 fw_ck;
 
-       if (force || xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
+       cancel_delayed_work(&priv->timer_sleep);
+
+       if (force || xc5000_is_firmware_loaded(fe) != 0) {
 
 fw_retry:
 
                ret = xc5000_fwupload(fe);
-               if (ret != XC_RESULT_SUCCESS)
+               if (ret != 0)
                        return ret;
 
                msleep(20);
 
                if (priv->fw_checksum_supported) {
                        if (xc5000_readreg(priv, XREG_FW_CHECKSUM, &fw_ck)
-                           != XC_RESULT_SUCCESS) {
+                           != 0) {
                                dprintk(1, "%s() FW checksum reading failed.\n",
                                        __func__);
                                goto fw_retry;
@@ -1137,7 +1133,7 @@ fw_retry:
                /* Start the tuner self-calibration process */
                ret |= xc_initialize(priv);
 
-               if (ret != XC_RESULT_SUCCESS)
+               if (ret != 0)
                        goto fw_retry;
 
                /* Wait for calibration to complete.
@@ -1145,10 +1141,10 @@ fw_retry:
                 * I2C transactions until calibration is complete.  This way we
                 * don't have to rely on clock stretching working.
                 */
-               xc_wait(100);
+               msleep(100);
 
                if (priv->init_status_supported) {
-                       if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck) != XC_RESULT_SUCCESS) {
+                       if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck) != 0) {
                                dprintk(1, "%s() FW failed reading init status.\n",
                                        __func__);
                                goto fw_retry;
@@ -1177,27 +1173,39 @@ fw_retry:
        return ret;
 }
 
-static int xc5000_sleep(struct dvb_frontend *fe)
+static void xc5000_do_timer_sleep(struct work_struct *timer_sleep)
 {
+       struct xc5000_priv *priv =container_of(timer_sleep, struct xc5000_priv,
+                                              timer_sleep.work);
+       struct dvb_frontend *fe = priv->fe;
        int ret;
 
        dprintk(1, "%s()\n", __func__);
 
-       /* Avoid firmware reload on slow devices */
-       if (no_poweroff)
-               return 0;
-
        /* According to Xceive technical support, the "powerdown" register
           was removed in newer versions of the firmware.  The "supported"
           way to sleep the tuner is to pull the reset pin low for 10ms */
-       ret = xc5000_TunerReset(fe);
-       if (ret != XC_RESULT_SUCCESS) {
+       ret = xc5000_tuner_reset(fe);
+       if (ret != 0)
                printk(KERN_ERR
                        "xc5000: %s() unable to shutdown tuner\n",
                        __func__);
-               return -EREMOTEIO;
-       } else
-               return XC_RESULT_SUCCESS;
+}
+
+static int xc5000_sleep(struct dvb_frontend *fe)
+{
+       struct xc5000_priv *priv = fe->tuner_priv;
+
+       dprintk(1, "%s()\n", __func__);
+
+       /* Avoid firmware reload on slow devices */
+       if (no_poweroff)
+               return 0;
+
+       schedule_delayed_work(&priv->timer_sleep,
+                             msecs_to_jiffies(XC5000_SLEEP_TIME));
+
+       return 0;
 }
 
 static int xc5000_init(struct dvb_frontend *fe)
@@ -1205,7 +1213,7 @@ static int xc5000_init(struct dvb_frontend *fe)
        struct xc5000_priv *priv = fe->tuner_priv;
        dprintk(1, "%s()\n", __func__);
 
-       if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) {
+       if (xc_load_fw_and_init_tuner(fe, 0) != 0) {
                printk(KERN_ERR "xc5000: Unable to initialise tuner\n");
                return -EREMOTEIO;
        }
@@ -1224,8 +1232,10 @@ static int xc5000_release(struct dvb_frontend *fe)
 
        mutex_lock(&xc5000_list_mutex);
 
-       if (priv)
+       if (priv) {
+               cancel_delayed_work(&priv->timer_sleep);
                hybrid_tuner_release_state(priv);
+       }
 
        mutex_unlock(&xc5000_list_mutex);
 
@@ -1297,6 +1307,8 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
                /* new tuner instance */
                priv->bandwidth = 6000000;
                fe->tuner_priv = priv;
+               priv->fe = fe;
+               INIT_DELAYED_WORK(&priv->timer_sleep, xc5000_do_timer_sleep);
                break;
        default:
                /* existing tuner instance */
@@ -1327,7 +1339,7 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
        /* Check if firmware has been loaded. It is possible that another
           instance of the driver has loaded the firmware.
         */
-       if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != XC_RESULT_SUCCESS)
+       if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != 0)
                goto fail;
 
        switch (id) {
index 4ae8b10..d8b5d94 100644 (file)
@@ -114,16 +114,20 @@ static void urb_completion(struct urb *purb)
        int ptype = usb_pipetype(purb->pipe);
        unsigned char *ptr;
 
-       dprintk(2, "%s()\n", __func__);
+       dprintk(2, "%s: %d\n", __func__, purb->actual_length);
 
-       if (!dev)
+       if (!dev) {
+               dprintk(2, "%s: no dev!\n", __func__);
                return;
+       }
 
-       if (dev->urb_streaming == 0)
+       if (dev->urb_streaming == 0) {
+               dprintk(2, "%s: not streaming!\n", __func__);
                return;
+       }
 
        if (ptype != PIPE_BULK) {
-               printk(KERN_ERR "%s() Unsupported URB type %d\n",
+               printk(KERN_ERR "%s: Unsupported URB type %d\n",
                       __func__, ptype);
                return;
        }
@@ -252,8 +256,6 @@ static void au0828_stop_transport(struct au0828_dev *dev, int full_stop)
        au0828_write(dev, 0x60b, 0x00);
 }
 
-
-
 static int au0828_dvb_start_feed(struct dvb_demux_feed *feed)
 {
        struct dvb_demux *demux = feed->demux;
@@ -296,6 +298,8 @@ static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed)
        dprintk(1, "%s()\n", __func__);
 
        if (dvb) {
+               cancel_work_sync(&dev->restart_streaming);
+
                mutex_lock(&dvb->lock);
                dvb->stop_count++;
                dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__,
@@ -338,6 +342,41 @@ static void au0828_restart_dvb_streaming(struct work_struct *work)
        mutex_unlock(&dvb->lock);
 }
 
+static int au0828_set_frontend(struct dvb_frontend *fe)
+{
+       struct au0828_dev *dev = fe->dvb->priv;
+       struct au0828_dvb *dvb = &dev->dvb;
+       int ret, was_streaming;
+
+       mutex_lock(&dvb->lock);
+       was_streaming = dev->urb_streaming;
+       if (was_streaming) {
+               au0828_stop_transport(dev, 1);
+
+               /*
+                * We can't hold a mutex here, as the restart_streaming
+                * kthread may also hold it.
+                */
+               mutex_unlock(&dvb->lock);
+               cancel_work_sync(&dev->restart_streaming);
+               mutex_lock(&dvb->lock);
+
+               stop_urb_transfer(dev);
+       }
+       mutex_unlock(&dvb->lock);
+
+       ret = dvb->set_frontend(fe);
+
+       if (was_streaming) {
+               mutex_lock(&dvb->lock);
+               au0828_start_transport(dev);
+               start_urb_transfer(dev);
+               mutex_unlock(&dvb->lock);
+       }
+
+       return ret;
+}
+
 static int dvb_register(struct au0828_dev *dev)
 {
        struct au0828_dvb *dvb = &dev->dvb;
@@ -382,6 +421,10 @@ static int dvb_register(struct au0828_dev *dev)
                goto fail_frontend;
        }
 
+       /* Hook dvb frontend */
+       dvb->set_frontend = dvb->frontend->ops.set_frontend;
+       dvb->frontend->ops.set_frontend = au0828_set_frontend;
+
        /* register demux stuff */
        dvb->demux.dmx.capabilities =
                DMX_TS_FILTERING | DMX_SECTION_FILTERING |
@@ -471,6 +514,8 @@ void au0828_dvb_unregister(struct au0828_dev *dev)
        if (dvb->frontend == NULL)
                return;
 
+       cancel_work_sync(&dev->restart_streaming);
+
        dvb_net_release(&dvb->net);
        dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
        dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
index f615454..9038194 100644 (file)
@@ -1109,7 +1109,7 @@ static void au0828_init_tuner(struct au0828_dev *dev)
        /* If we've never sent the standard in tuner core, do so now.
           We don't do this at device probe because we don't want to
           incur the cost of a firmware load */
-       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->std);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->std);
        v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
        i2c_gate_ctrl(dev, 0);
 }
@@ -1368,7 +1368,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
           have to make the au0828 bridge adjust the size of its capture
           buffer, which is currently hardcoded at 720x480 */
 
-       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, norm);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, norm);
 
        i2c_gate_ctrl(dev, 0);
 
index 5439772..7112b9d 100644 (file)
@@ -104,6 +104,8 @@ struct au0828_dvb {
        int feeding;
        int start_count;
        int stop_count;
+
+       int (*set_frontend)(struct dvb_frontend *fe);
 };
 
 enum au0828_stream_state {
index 2f63029..30a0c69 100644 (file)
@@ -1516,7 +1516,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id)
                dev->ts1.height = 576;
                cx2341x_handler_set_50hz(&dev->mpeg_ctrl_handler, true);
        }
-       call_all(dev, core, s_std, dev->norm);
+       call_all(dev, video, s_std, dev->norm);
        /* do mode control overrides */
        cx231xx_do_mode_ctrl_overrides(dev);
 
index 9906261..1f87513 100644 (file)
@@ -1009,7 +1009,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
        dev->width = 720;
        dev->height = (dev->norm & V4L2_STD_625_50) ? 576 : 480;
 
-       call_all(dev, core, s_std, dev->norm);
+       call_all(dev, video, s_std, dev->norm);
 
        /* We need to reset basic properties in the decoder related to
           resolution (since a standard change effects things like the number
@@ -1108,7 +1108,7 @@ int cx231xx_s_input(struct file *file, void *priv, unsigned int i)
                /* There's a tuner, so reset the standard and put it on the
                   last known frequency (since it was probably powered down
                   until now */
-               call_all(dev, core, s_std, dev->norm);
+               call_all(dev, video, s_std, dev->norm);
        }
 
        return 0;
@@ -2099,7 +2099,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev)
        /* Set the initial input */
        video_mux(dev, dev->video_input);
 
-       call_all(dev, core, s_std, dev->norm);
+       call_all(dev, video, s_std, dev->norm);
 
        v4l2_ctrl_handler_init(&dev->ctrl_handler, 10);
        v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 5);
index dcbd392..a676e44 100644 (file)
@@ -1537,6 +1537,12 @@ static const struct usb_device_id rtl28xxu_id_table[] = {
                &rtl2832u_props, "Crypto ReDi PC 50 A", NULL) },
        { DVB_USB_DEVICE(USB_VID_KYE, 0x707f,
                &rtl2832u_props, "Genius TVGo DVB-T03", NULL) },
+       { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd395,
+               &rtl2832u_props, "Peak DVB-T USB", NULL) },
+       { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20_RTL2832U,
+               &rtl2832u_props, "Sveon STV20", NULL) },
+       { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV27,
+               &rtl2832u_props, "Sveon STV27", NULL) },
 
        /* RTL2832P devices: */
        { DVB_USB_DEVICE(USB_VID_HANFTEK, 0x0131,
index c11138e..0df52ab 100644 (file)
@@ -1088,6 +1088,7 @@ static struct usb_device_id az6027_usb_table[] = {
        { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI_V1) },
        { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI_V2) },
        { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_SAT) },
+       { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_SAT_V2) },
        { },
 };
 
@@ -1136,7 +1137,7 @@ static struct dvb_usb_device_properties az6027_properties = {
 
        .i2c_algo         = &az6027_i2c_algo,
 
-       .num_device_descs = 6,
+       .num_device_descs = 7,
        .devices = {
                {
                        .name = "AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)",
@@ -1162,6 +1163,10 @@ static struct dvb_usb_device_properties az6027_properties = {
                        .name = "Elgato EyeTV Sat",
                        .cold_ids = { &az6027_usb_table[5], NULL },
                        .warm_ids = { NULL },
+               }, {
+                       .name = "Elgato EyeTV Sat",
+                       .cold_ids = { &az6027_usb_table[6], NULL },
+                       .warm_ids = { NULL },
                },
                { NULL },
        }
index 637b612..927617d 100644 (file)
@@ -59,7 +59,7 @@ extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8
 extern int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3);
 extern int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen);
 extern int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw);
-extern int dib0700_rc_setup(struct dvb_usb_device *d);
+extern int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf);
 extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff);
 extern struct i2c_algorithm dib0700_i2c_algo;
 extern int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props,
index bf2a908..c14285f 100644 (file)
@@ -754,17 +754,20 @@ resubmit:
        usb_submit_urb(purb, GFP_ATOMIC);
 }
 
-int dib0700_rc_setup(struct dvb_usb_device *d)
+int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf)
 {
        struct dib0700_state *st = d->priv;
        struct urb *purb;
-       int ret;
+       const struct usb_endpoint_descriptor *e;
+       int ret, rc_ep = 1;
+       unsigned int pipe = 0;
 
        /* Poll-based. Don't initialize bulk mode */
-       if (st->fw_version < 0x10200)
+       if (st->fw_version < 0x10200 || !intf)
                return 0;
 
        /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */
+
        purb = usb_alloc_urb(0, GFP_KERNEL);
        if (purb == NULL) {
                err("rc usb alloc urb failed");
@@ -779,9 +782,35 @@ int dib0700_rc_setup(struct dvb_usb_device *d)
        }
 
        purb->status = -EINPROGRESS;
-       usb_fill_bulk_urb(purb, d->udev, usb_rcvbulkpipe(d->udev, 1),
-                         purb->transfer_buffer, RC_MSG_SIZE_V1_20,
-                         dib0700_rc_urb_completion, d);
+
+       /*
+        * Some devices like the Hauppauge NovaTD model 52009 use an interrupt
+        * endpoint, while others use a bulk one.
+        */
+       e = &intf->altsetting[0].endpoint[rc_ep].desc;
+       if (usb_endpoint_dir_in(e)) {
+               if (usb_endpoint_xfer_bulk(e)) {
+                       pipe = usb_rcvbulkpipe(d->udev, rc_ep);
+                       usb_fill_bulk_urb(purb, d->udev, pipe,
+                                         purb->transfer_buffer,
+                                         RC_MSG_SIZE_V1_20,
+                                         dib0700_rc_urb_completion, d);
+
+               } else if (usb_endpoint_xfer_int(e)) {
+                       pipe = usb_rcvintpipe(d->udev, rc_ep);
+                       usb_fill_int_urb(purb, d->udev, pipe,
+                                         purb->transfer_buffer,
+                                         RC_MSG_SIZE_V1_20,
+                                         dib0700_rc_urb_completion, d, 1);
+               }
+       }
+
+       if (!pipe) {
+               err("There's no endpoint for remote controller");
+               kfree(purb->transfer_buffer);
+               usb_free_urb(purb);
+               return 0;
+       }
 
        ret = usb_submit_urb(purb, GFP_ATOMIC);
        if (ret) {
@@ -820,7 +849,7 @@ static int dib0700_probe(struct usb_interface *intf,
                        else
                                dev->props.rc.core.bulk_mode = false;
 
-                       dib0700_rc_setup(dev);
+                       dib0700_rc_setup(dev, intf);
 
                        return 0;
                }
index 829323e..10e0db8 100644 (file)
@@ -514,7 +514,7 @@ static int dib0700_rc_query_old_firmware(struct dvb_usb_device *d)
 
        /* info("%d: %2X %2X %2X %2X",dvb_usb_dib0700_ir_proto,(int)key[3-2],(int)key[3-3],(int)key[3-1],(int)key[3]);  */
 
-       dib0700_rc_setup(d); /* reset ir sensor data to prevent false events */
+       dib0700_rc_setup(d, NULL); /* reset ir sensor data to prevent false events */
 
        d->last_event = 0;
        switch (d->props.rc.core.protocol) {
index 98d24ae..d947e03 100644 (file)
@@ -214,10 +214,10 @@ static void technisat_usb2_frontend_reset(struct usb_device *udev)
 
 /* LED control */
 enum technisat_usb2_led_state {
-       LED_OFF,
-       LED_BLINK,
-       LED_ON,
-       LED_UNDEFINED
+       TECH_LED_OFF,
+       TECH_LED_BLINK,
+       TECH_LED_ON,
+       TECH_LED_UNDEFINED
 };
 
 static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum technisat_usb2_led_state state)
@@ -229,14 +229,14 @@ static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum techni
                0
        };
 
-       if (disable_led_control && state != LED_OFF)
+       if (disable_led_control && state != TECH_LED_OFF)
                return 0;
 
        switch (state) {
-       case LED_ON:
+       case TECH_LED_ON:
                led[1] = 0x82;
                break;
-       case LED_BLINK:
+       case TECH_LED_BLINK:
                led[1] = 0x82;
                if (red) {
                        led[2] = 0x02;
@@ -251,7 +251,7 @@ static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum techni
                break;
 
        default:
-       case LED_OFF:
+       case TECH_LED_OFF:
                led[1] = 0x80;
                break;
        }
@@ -310,11 +310,11 @@ static void technisat_usb2_green_led_control(struct work_struct *work)
                                goto schedule;
 
                        if (ber > 1000)
-                               technisat_usb2_set_led(state->dev, 0, LED_BLINK);
+                               technisat_usb2_set_led(state->dev, 0, TECH_LED_BLINK);
                        else
-                               technisat_usb2_set_led(state->dev, 0, LED_ON);
+                               technisat_usb2_set_led(state->dev, 0, TECH_LED_ON);
                } else
-                       technisat_usb2_set_led(state->dev, 0, LED_OFF);
+                       technisat_usb2_set_led(state->dev, 0, TECH_LED_OFF);
        }
 
 schedule:
@@ -365,9 +365,9 @@ static int technisat_usb2_power_ctrl(struct dvb_usb_device *d, int level)
                return 0;
 
        /* green led is turned off in any case - will be turned on when tuning */
-       technisat_usb2_set_led(d, 0, LED_OFF);
+       technisat_usb2_set_led(d, 0, TECH_LED_OFF);
        /* red led is turned on all the time */
-       technisat_usb2_set_led(d, 1, LED_ON);
+       technisat_usb2_set_led(d, 1, TECH_LED_ON);
        return 0;
 }
 
@@ -667,7 +667,7 @@ static int technisat_usb2_rc_query(struct dvb_usb_device *d)
                return 0;
 
        if (!disable_led_control)
-               technisat_usb2_set_led(d, 1, LED_BLINK);
+               technisat_usb2_set_led(d, 1, TECH_LED_BLINK);
 
        return 0;
 }
index d23a912..f5d7198 100644 (file)
@@ -57,6 +57,8 @@ config VIDEO_EM28XX_DVB
        select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
        select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_DRX39XYJ if MEDIA_SUBDRV_AUTOSELECT
+       select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT
+       select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
        ---help---
          This adds support for DVB cards based on the
          Empiatech em28xx chips.
index 342490f..e881ef7 100644 (file)
@@ -92,7 +92,7 @@ static void em28xx_audio_isocirq(struct urb *urb)
 
        if (dev->disconnected) {
                dprintk("device disconnected while streaming. URB status=%d.\n", urb->status);
-               atomic_set(&dev->stream_started, 0);
+               atomic_set(&dev->adev.stream_started, 0);
                return;
        }
 
@@ -109,7 +109,7 @@ static void em28xx_audio_isocirq(struct urb *urb)
                break;
        }
 
-       if (atomic_read(&dev->stream_started) == 0)
+       if (atomic_read(&dev->adev.stream_started) == 0)
                return;
 
        if (dev->adev.capture_pcm_substream) {
@@ -185,7 +185,7 @@ static int em28xx_init_audio_isoc(struct em28xx *dev)
                        em28xx_errdev("submit of audio urb failed (error=%i)\n",
                                      errCode);
                        em28xx_deinit_isoc_audio(dev);
-                       atomic_set(&dev->stream_started, 0);
+                       atomic_set(&dev->adev.stream_started, 0);
                        return errCode;
                }
 
@@ -332,9 +332,9 @@ static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
        dev->mute = 1;
        mutex_lock(&dev->lock);
        dev->adev.users--;
-       if (atomic_read(&dev->stream_started) > 0) {
-               atomic_set(&dev->stream_started, 0);
-               schedule_work(&dev->wq_trigger);
+       if (atomic_read(&dev->adev.stream_started) > 0) {
+               atomic_set(&dev->adev.stream_started, 0);
+               schedule_work(&dev->adev.wq_trigger);
        }
 
        em28xx_audio_analog_set(dev);
@@ -381,12 +381,13 @@ static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
 static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)
 {
        struct em28xx *dev = snd_pcm_substream_chip(substream);
+       struct em28xx_audio *adev = &dev->adev;
 
        dprintk("Stop capture, if needed\n");
 
-       if (atomic_read(&dev->stream_started) > 0) {
-               atomic_set(&dev->stream_started, 0);
-               schedule_work(&dev->wq_trigger);
+       if (atomic_read(&adev->stream_started) > 0) {
+               atomic_set(&adev->stream_started, 0);
+               schedule_work(&adev->wq_trigger);
        }
 
        return 0;
@@ -407,9 +408,11 @@ static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
 
 static void audio_trigger(struct work_struct *work)
 {
-       struct em28xx *dev = container_of(work, struct em28xx, wq_trigger);
+       struct em28xx_audio *adev =
+                           container_of(work, struct em28xx_audio, wq_trigger);
+       struct em28xx *dev = container_of(adev, struct em28xx, adev);
 
-       if (atomic_read(&dev->stream_started)) {
+       if (atomic_read(&adev->stream_started)) {
                dprintk("starting capture");
                em28xx_init_audio_isoc(dev);
        } else {
@@ -431,17 +434,17 @@ static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
        case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
        case SNDRV_PCM_TRIGGER_START:
-               atomic_set(&dev->stream_started, 1);
+               atomic_set(&dev->adev.stream_started, 1);
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
        case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
        case SNDRV_PCM_TRIGGER_STOP:
-               atomic_set(&dev->stream_started, 0);
+               atomic_set(&dev->adev.stream_started, 0);
                break;
        default:
                retval = -EINVAL;
        }
-       schedule_work(&dev->wq_trigger);
+       schedule_work(&dev->adev.wq_trigger);
        return retval;
 }
 
@@ -928,7 +931,7 @@ static int em28xx_audio_init(struct em28xx *dev)
        strcpy(card->shortname, "Em28xx Audio");
        strcpy(card->longname, "Empia Em28xx Audio");
 
-       INIT_WORK(&dev->wq_trigger, audio_trigger);
+       INIT_WORK(&adev->wq_trigger, audio_trigger);
 
        if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
                em28xx_cvol_new(card, dev, "Video", AC97_VIDEO);
@@ -983,7 +986,7 @@ static int em28xx_audio_fini(struct em28xx *dev)
 
        if (dev->adev.sndcard) {
                snd_card_disconnect(dev->adev.sndcard);
-               flush_work(&dev->wq_trigger);
+               flush_work(&dev->adev.wq_trigger);
 
                em28xx_audio_free_urb(dev);
 
@@ -1005,7 +1008,7 @@ static int em28xx_audio_suspend(struct em28xx *dev)
 
        em28xx_info("Suspending audio extension");
        em28xx_deinit_isoc_audio(dev);
-       atomic_set(&dev->stream_started, 0);
+       atomic_set(&dev->adev.stream_started, 0);
        return 0;
 }
 
@@ -1019,7 +1022,7 @@ static int em28xx_audio_resume(struct em28xx *dev)
 
        em28xx_info("Resuming audio extension");
        /* Nothing to do other than schedule_work() ?? */
-       schedule_work(&dev->wq_trigger);
+       schedule_work(&dev->adev.wq_trigger);
        return 0;
 }
 
index 505e050..12d4c03 100644 (file)
@@ -330,13 +330,14 @@ int em28xx_init_camera(struct em28xx *dev)
        char clk_name[V4L2_SUBDEV_NAME_SIZE];
        struct i2c_client *client = &dev->i2c_client[dev->def_i2c_bus];
        struct i2c_adapter *adap = &dev->i2c_adap[dev->def_i2c_bus];
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
        int ret = 0;
 
        v4l2_clk_name_i2c(clk_name, sizeof(clk_name),
                          i2c_adapter_id(adap), client->addr);
-       dev->clk = v4l2_clk_register_fixed(clk_name, "mclk", -EINVAL);
-       if (IS_ERR(dev->clk))
-               return PTR_ERR(dev->clk);
+       v4l2->clk = v4l2_clk_register_fixed(clk_name, "mclk", -EINVAL);
+       if (IS_ERR(v4l2->clk))
+               return PTR_ERR(v4l2->clk);
 
        switch (dev->em28xx_sensor) {
        case EM28XX_MT9V011:
@@ -348,8 +349,8 @@ int em28xx_init_camera(struct em28xx *dev)
                        .platform_data = &pdata,
                };
 
-               dev->sensor_xres = 640;
-               dev->sensor_yres = 480;
+               v4l2->sensor_xres = 640;
+               v4l2->sensor_yres = 480;
 
                /*
                 * FIXME: mt9v011 uses I2S speed as xtal clk - at least with
@@ -362,41 +363,41 @@ int em28xx_init_camera(struct em28xx *dev)
                 */
                dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ;
                em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk);
-               dev->sensor_xtal = 4300000;
-               pdata.xtal = dev->sensor_xtal;
+               v4l2->sensor_xtal = 4300000;
+               pdata.xtal = v4l2->sensor_xtal;
                if (NULL ==
-                   v4l2_i2c_new_subdev_board(&dev->v4l2_dev, adap,
+                   v4l2_i2c_new_subdev_board(&dev->v4l2->v4l2_dev, adap,
                                              &mt9v011_info, NULL)) {
                        ret = -ENODEV;
                        break;
                }
                /* probably means GRGB 16 bit bayer */
-               dev->vinmode = 0x0d;
-               dev->vinctl = 0x00;
+               v4l2->vinmode = 0x0d;
+               v4l2->vinctl = 0x00;
 
                break;
        }
        case EM28XX_MT9M001:
-               dev->sensor_xres = 1280;
-               dev->sensor_yres = 1024;
+               v4l2->sensor_xres = 1280;
+               v4l2->sensor_yres = 1024;
 
                em28xx_initialize_mt9m001(dev);
 
                /* probably means BGGR 16 bit bayer */
-               dev->vinmode = 0x0c;
-               dev->vinctl = 0x00;
+               v4l2->vinmode = 0x0c;
+               v4l2->vinctl = 0x00;
 
                break;
        case EM28XX_MT9M111:
-               dev->sensor_xres = 640;
-               dev->sensor_yres = 512;
+               v4l2->sensor_xres = 640;
+               v4l2->sensor_yres = 512;
 
                dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ;
                em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk);
                em28xx_initialize_mt9m111(dev);
 
-               dev->vinmode = 0x0a;
-               dev->vinctl = 0x00;
+               v4l2->vinmode = 0x0a;
+               v4l2->vinctl = 0x00;
 
                break;
        case EM28XX_OV2640:
@@ -418,11 +419,11 @@ int em28xx_init_camera(struct em28xx *dev)
                 * - adjust bridge xclk
                 * - disable 16 bit (12 bit) output formats on high resolutions
                 */
-               dev->sensor_xres = 640;
-               dev->sensor_yres = 480;
+               v4l2->sensor_xres = 640;
+               v4l2->sensor_yres = 480;
 
                subdev =
-                    v4l2_i2c_new_subdev_board(&dev->v4l2_dev, adap,
+                    v4l2_i2c_new_subdev_board(&dev->v4l2->v4l2_dev, adap,
                                               &ov2640_info, NULL);
                if (NULL == subdev) {
                        ret = -ENODEV;
@@ -437,8 +438,8 @@ int em28xx_init_camera(struct em28xx *dev)
                /* NOTE: for UXGA=1600x1200 switch to 12MHz */
                dev->board.xclk = EM28XX_XCLK_FREQUENCY_24MHZ;
                em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk);
-               dev->vinmode = 0x08;
-               dev->vinctl = 0x00;
+               v4l2->vinmode = 0x08;
+               v4l2->vinctl = 0x00;
 
                break;
        }
@@ -448,8 +449,8 @@ int em28xx_init_camera(struct em28xx *dev)
        }
 
        if (ret < 0) {
-               v4l2_clk_unregister_fixed(dev->clk);
-               dev->clk = NULL;
+               v4l2_clk_unregister_fixed(v4l2->clk);
+               v4l2->clk = NULL;
        }
 
        return ret;
index 50aa5a5..15ad470 100644 (file)
@@ -467,6 +467,18 @@ static struct em28xx_reg_seq speedlink_vad_laplace_reg_seq[] = {
        {       -1,                     -1,     -1,     -1},
 };
 
+static struct em28xx_reg_seq pctv_292e[] = {
+       {EM2874_R80_GPIO_P0_CTRL,      0xff, 0xff,      0},
+       {0x0d,                         0xff, 0xff,    950},
+       {EM2874_R80_GPIO_P0_CTRL,      0xbd, 0xff,    100},
+       {EM2874_R80_GPIO_P0_CTRL,      0xfd, 0xff,    410},
+       {EM2874_R80_GPIO_P0_CTRL,      0x7d, 0xff,    300},
+       {EM2874_R80_GPIO_P0_CTRL,      0x7c, 0xff,     60},
+       {0x0d,                         0x42, 0xff,     50},
+       {EM2874_R5F_TS_ENABLE,         0x85, 0xff,      0},
+       {-1,                             -1,   -1,     -1},
+};
+
 /*
  *  Button definitions
  */
@@ -2220,6 +2232,17 @@ struct em28xx_board em28xx_boards[] = {
                .has_dvb       = 1,
                .ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
        },
+       /* 2013:025f PCTV tripleStick (292e).
+        * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2157 */
+       [EM28178_BOARD_PCTV_292E] = {
+               .name          = "PCTV tripleStick (292e)",
+               .def_i2c_bus   = 1,
+               .i2c_speed     = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = pctv_292e,
+               .has_dvb       = 1,
+               .ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
+       },
 };
 EXPORT_SYMBOL_GPL(em28xx_boards);
 
@@ -2397,6 +2420,8 @@ struct usb_device_id em28xx_id_table[] = {
                        .driver_info = EM2765_BOARD_SPEEDLINK_VAD_LAPLACE },
        { USB_DEVICE(0x2013, 0x0258),
                        .driver_info = EM28178_BOARD_PCTV_461E },
+       { USB_DEVICE(0x2013, 0x025f),
+                       .driver_info = EM28178_BOARD_PCTV_292E },
        { },
 };
 MODULE_DEVICE_TABLE(usb, em28xx_id_table);
@@ -2682,8 +2707,6 @@ static void em28xx_card_setup(struct em28xx *dev)
        if (dev->board.is_webcam) {
                if (em28xx_detect_sensor(dev) < 0)
                        dev->board.is_webcam = 0;
-               else
-                       dev->progressive = 1;
        }
 
        switch (dev->model) {
@@ -2718,11 +2741,6 @@ static void em28xx_card_setup(struct em28xx *dev)
                    dev->board.name, dev->model);
 
        dev->tuner_type = em28xx_boards[dev->model].tuner_type;
-       if (em28xx_boards[dev->model].tuner_addr)
-               dev->tuner_addr = em28xx_boards[dev->model].tuner_addr;
-
-       if (em28xx_boards[dev->model].tda9887_conf)
-               dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf;
 
        /* request some modules */
        switch (dev->model) {
@@ -2991,8 +3009,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
        const char *chip_name = default_chip_name;
 
        dev->udev = udev;
-       mutex_init(&dev->vb_queue_lock);
-       mutex_init(&dev->vb_vbi_queue_lock);
        mutex_init(&dev->ctrl_urb_lock);
        spin_lock_init(&dev->slock);
 
@@ -3416,15 +3432,14 @@ static int em28xx_usb_probe(struct usb_interface *interface,
 
        /* Select USB transfer types to use */
        if (has_video) {
-           if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk))
-               dev->analog_xfer_bulk = 1;
-           em28xx_info("analog set to %s mode.\n",
-                       dev->analog_xfer_bulk ? "bulk" : "isoc");
+               if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk))
+                       dev->analog_xfer_bulk = 1;
+               em28xx_info("analog set to %s mode.\n",
+                           dev->analog_xfer_bulk ? "bulk" : "isoc");
        }
        if (has_dvb) {
-           if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk))
-               dev->dvb_xfer_bulk = 1;
-
+               if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk))
+                       dev->dvb_xfer_bulk = 1;
                em28xx_info("dvb set to %s mode.\n",
                            dev->dvb_xfer_bulk ? "bulk" : "isoc");
        }
index f599b18..a121ed9 100644 (file)
@@ -55,6 +55,8 @@
 #include "mb86a20s.h"
 #include "m88ds3103.h"
 #include "m88ts2022.h"
+#include "si2168.h"
+#include "si2157.h"
 
 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
 MODULE_LICENSE("GPL");
@@ -93,6 +95,7 @@ struct em28xx_dvb {
        struct semaphore      pll_mutex;
        bool                    dont_attach_fe1;
        int                     lna_gpio;
+       struct i2c_client       *i2c_client_demod;
        struct i2c_client       *i2c_client_tuner;
 };
 
@@ -743,6 +746,21 @@ static int em28xx_pctv_290e_set_lna(struct dvb_frontend *fe)
 #endif
 }
 
+static int em28xx_pctv_292e_set_lna(struct dvb_frontend *fe)
+{
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       struct em28xx_i2c_bus *i2c_bus = fe->dvb->priv;
+       struct em28xx *dev = i2c_bus->dev;
+       u8 lna;
+
+       if (c->lna == 1)
+               lna = 0x01;
+       else
+               lna = 0x00;
+
+       return em28xx_write_reg_bits(dev, EM2874_R80_GPIO_P0_CTRL, lna, 0x01);
+}
+
 static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe)
 {
        /* Values extracted from a USB trace of the Terratec Windows driver */
@@ -1496,6 +1514,63 @@ static int em28xx_dvb_init(struct em28xx *dev)
                        dvb->i2c_client_tuner = client;
                }
                break;
+       case EM28178_BOARD_PCTV_292E:
+               {
+                       struct i2c_adapter *adapter;
+                       struct i2c_client *client;
+                       struct i2c_board_info info;
+                       struct si2168_config si2168_config;
+                       struct si2157_config si2157_config;
+
+                       /* attach demod */
+                       si2168_config.i2c_adapter = &adapter;
+                       si2168_config.fe = &dvb->fe[0];
+                       memset(&info, 0, sizeof(struct i2c_board_info));
+                       strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+                       info.addr = 0x64;
+                       info.platform_data = &si2168_config;
+                       request_module(info.type);
+                       client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);
+                       if (client == NULL || client->dev.driver == NULL) {
+                               result = -ENODEV;
+                               goto out_free;
+                       }
+
+                       if (!try_module_get(client->dev.driver->owner)) {
+                               i2c_unregister_device(client);
+                               result = -ENODEV;
+                               goto out_free;
+                       }
+
+                       dvb->i2c_client_demod = client;
+
+                       /* attach tuner */
+                       si2157_config.fe = dvb->fe[0];
+                       memset(&info, 0, sizeof(struct i2c_board_info));
+                       strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+                       info.addr = 0x60;
+                       info.platform_data = &si2157_config;
+                       request_module(info.type);
+                       client = i2c_new_device(adapter, &info);
+                       if (client == NULL || client->dev.driver == NULL) {
+                               module_put(dvb->i2c_client_demod->dev.driver->owner);
+                               i2c_unregister_device(dvb->i2c_client_demod);
+                               result = -ENODEV;
+                               goto out_free;
+                       }
+
+                       if (!try_module_get(client->dev.driver->owner)) {
+                               i2c_unregister_device(client);
+                               module_put(dvb->i2c_client_demod->dev.driver->owner);
+                               i2c_unregister_device(dvb->i2c_client_demod);
+                               result = -ENODEV;
+                               goto out_free;
+                       }
+
+                       dvb->i2c_client_tuner = client;
+                       dvb->fe[0]->ops.set_lna = em28xx_pctv_292e_set_lna;
+               }
+               break;
        default:
                em28xx_errdev("/2: The frontend of your DVB/ATSC card"
                                " isn't supported yet\n");
@@ -1582,6 +1657,13 @@ static int em28xx_dvb_fini(struct em28xx *dev)
                i2c_unregister_device(client);
        }
 
+       /* remove I2C demod */
+       client = dvb->i2c_client_demod;
+       if (client) {
+               module_put(client->dev.driver->owner);
+               i2c_unregister_device(client);
+       }
+
        em28xx_unregister_dvb(dvb);
        kfree(dvb);
        dev->dvb = NULL;
@@ -1647,6 +1729,13 @@ static int em28xx_dvb_resume(struct em28xx *dev)
                        i2c_unregister_device(client);
                }
 
+               /* remove I2C demod */
+               client = dvb->i2c_client_demod;
+               if (client) {
+                       module_put(client->dev.driver->owner);
+                       i2c_unregister_device(client);
+               }
+
                em28xx_unregister_dvb(dvb);
                kfree(dvb);
                dev->dvb = NULL;
index ba6433c..b58d4eb 100644 (file)
@@ -939,7 +939,6 @@ int em28xx_i2c_register(struct em28xx *dev, unsigned bus,
        dev->i2c_bus[bus].algo_type = algo_type;
        dev->i2c_bus[bus].dev = dev;
        dev->i2c_adap[bus].algo_data = &dev->i2c_bus[bus];
-       i2c_set_adapdata(&dev->i2c_adap[bus], &dev->v4l2_dev);
 
        retval = i2c_add_adapter(&dev->i2c_adap[bus]);
        if (retval < 0) {
index bce4386..432862c 100644 (file)
@@ -16,5 +16,5 @@
 
 
 int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count);
-int em28xx_stop_vbi_streaming(struct vb2_queue *vq);
+void em28xx_stop_vbi_streaming(struct vb2_queue *vq);
 extern struct vb2_ops em28xx_vbi_qops;
index db3d655..6d7f657 100644 (file)
@@ -47,12 +47,13 @@ static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
                           unsigned int sizes[], void *alloc_ctxs[])
 {
        struct em28xx *dev = vb2_get_drv_priv(vq);
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
        unsigned long size;
 
        if (fmt)
                size = fmt->fmt.pix.sizeimage;
        else
-               size = dev->vbi_width * dev->vbi_height * 2;
+               size = v4l2->vbi_width * v4l2->vbi_height * 2;
 
        if (0 == *nbuffers)
                *nbuffers = 32;
@@ -69,11 +70,12 @@ static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
 
 static int vbi_buffer_prepare(struct vb2_buffer *vb)
 {
-       struct em28xx        *dev = vb2_get_drv_priv(vb->vb2_queue);
-       struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
+       struct em28xx        *dev  = vb2_get_drv_priv(vb->vb2_queue);
+       struct em28xx_v4l2   *v4l2 = dev->v4l2;
+       struct em28xx_buffer *buf  = container_of(vb, struct em28xx_buffer, vb);
        unsigned long        size;
 
-       size = dev->vbi_width * dev->vbi_height * 2;
+       size = v4l2->vbi_width * v4l2->vbi_height * 2;
 
        if (vb2_plane_size(vb, 0) < size) {
                printk(KERN_INFO "%s data will not fit into plane (%lu < %lu)\n",
index 0856e5d..f6b49c9 100644 (file)
@@ -141,6 +141,33 @@ static struct em28xx_fmt format[] = {
        },
 };
 
+/*FIXME: maxw should be dependent of alt mode */
+static inline unsigned int norm_maxw(struct em28xx *dev)
+{
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
+
+       if (dev->board.is_webcam)
+               return v4l2->sensor_xres;
+
+       if (dev->board.max_range_640_480)
+               return 640;
+
+       return 720;
+}
+
+static inline unsigned int norm_maxh(struct em28xx *dev)
+{
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
+
+       if (dev->board.is_webcam)
+               return v4l2->sensor_yres;
+
+       if (dev->board.max_range_640_480)
+               return 480;
+
+       return (v4l2->norm & V4L2_STD_625_50) ? 576 : 480;
+}
+
 static int em28xx_vbi_supported(struct em28xx *dev)
 {
        /* Modprobe option to manually disable */
@@ -166,10 +193,11 @@ static int em28xx_vbi_supported(struct em28xx *dev)
  */
 static void em28xx_wake_i2c(struct em28xx *dev)
 {
-       v4l2_device_call_all(&dev->v4l2_dev, 0, core,  reset, 0);
-       v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
+       struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev;
+       v4l2_device_call_all(v4l2_dev, 0, core,  reset, 0);
+       v4l2_device_call_all(v4l2_dev, 0, video, s_routing,
                        INPUT(dev->ctl_input)->vmux, 0, 0);
-       v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
+       v4l2_device_call_all(v4l2_dev, 0, video, s_stream, 0);
 }
 
 static int em28xx_colorlevels_set_default(struct em28xx *dev)
@@ -194,8 +222,9 @@ static int em28xx_set_outfmt(struct em28xx *dev)
 {
        int ret;
        u8 fmt, vinctrl;
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
 
-       fmt = dev->format->reg;
+       fmt = v4l2->format->reg;
        if (!dev->is_em25xx)
                fmt |= 0x20;
        /*
@@ -211,20 +240,20 @@ static int em28xx_set_outfmt(struct em28xx *dev)
        if (ret < 0)
                return ret;
 
-       ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode);
+       ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, v4l2->vinmode);
        if (ret < 0)
                return ret;
 
-       vinctrl = dev->vinctl;
+       vinctrl = v4l2->vinctl;
        if (em28xx_vbi_supported(dev) == 1) {
                vinctrl |= EM28XX_VINCTRL_VBI_RAW;
                em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00);
-               em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, dev->vbi_width/4);
-               em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, dev->vbi_height);
-               if (dev->norm & V4L2_STD_525_60) {
+               em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, v4l2->vbi_width/4);
+               em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, v4l2->vbi_height);
+               if (v4l2->norm & V4L2_STD_525_60) {
                        /* NTSC */
                        em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09);
-               } else if (dev->norm & V4L2_STD_625_50) {
+               } else if (v4l2->norm & V4L2_STD_625_50) {
                        /* PAL */
                        em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07);
                }
@@ -274,7 +303,7 @@ static void em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart,
 
 static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v)
 {
-       u8 mode;
+       u8 mode = 0x00;
        /* the em2800 scaler only supports scaling down to 50% */
 
        if (dev->board.is_em2800) {
@@ -293,22 +322,22 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v)
                   to work correctly */
                mode = (h || v) ? 0x30 : 0x00;
        }
-       return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30);
+       return em28xx_write_reg(dev, EM28XX_R26_COMPR, mode);
 }
 
 /* FIXME: this only function read values from dev */
 static int em28xx_resolution_set(struct em28xx *dev)
 {
-       int width, height;
-       width = norm_maxw(dev);
-       height = norm_maxh(dev);
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
+       int width = norm_maxw(dev);
+       int height = norm_maxh(dev);
 
        /* Properly setup VBI */
-       dev->vbi_width = 720;
-       if (dev->norm & V4L2_STD_525_60)
-               dev->vbi_height = 12;
+       v4l2->vbi_width = 720;
+       if (v4l2->norm & V4L2_STD_525_60)
+               v4l2->vbi_height = 12;
        else
-               dev->vbi_height = 18;
+               v4l2->vbi_height = 18;
 
        em28xx_set_outfmt(dev);
 
@@ -326,15 +355,16 @@ static int em28xx_resolution_set(struct em28xx *dev)
        else
                em28xx_capture_area_set(dev, 0, 0, width, height);
 
-       return em28xx_scaler_set(dev, dev->hscale, dev->vscale);
+       return em28xx_scaler_set(dev, v4l2->hscale, v4l2->vscale);
 }
 
 /* Set USB alternate setting for analog video */
 static int em28xx_set_alternate(struct em28xx *dev)
 {
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
        int errCode;
        int i;
-       unsigned int min_pkt_size = dev->width * 2 + 4;
+       unsigned int min_pkt_size = v4l2->width * 2 + 4;
 
        /* NOTE: for isoc transfers, only alt settings > 0 are allowed
                 bulk transfers seem to work only with alt=0 ! */
@@ -351,7 +381,7 @@ static int em28xx_set_alternate(struct em28xx *dev)
           the frame size should be increased, otherwise, only
           green screen will be received.
         */
-       if (dev->width * 2 * dev->height > 720 * 240 * 2)
+       if (v4l2->width * 2 * v4l2->height > 720 * 240 * 2)
                min_pkt_size *= 2;
 
        for (i = 0; i < dev->num_alt; i++) {
@@ -404,7 +434,7 @@ static inline void finish_buffer(struct em28xx *dev,
 {
        em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field);
 
-       buf->vb.v4l2_buf.sequence = dev->field_count++;
+       buf->vb.v4l2_buf.sequence = dev->v4l2->field_count++;
        buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
        v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
 
@@ -419,9 +449,10 @@ static void em28xx_copy_video(struct em28xx *dev,
                              unsigned char *usb_buf,
                              unsigned long len)
 {
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
        void *fieldstart, *startwrite, *startread;
        int  linesdone, currlinedone, offset, lencopy, remain;
-       int bytesperline = dev->width << 1;
+       int bytesperline = v4l2->width << 1;
 
        if (buf->pos + len > buf->length)
                len = buf->length - buf->pos;
@@ -429,7 +460,7 @@ static void em28xx_copy_video(struct em28xx *dev,
        startread = usb_buf;
        remain = len;
 
-       if (dev->progressive || buf->top_field)
+       if (v4l2->progressive || buf->top_field)
                fieldstart = buf->vb_buf;
        else /* interlaced mode, even nr. of lines */
                fieldstart = buf->vb_buf + bytesperline;
@@ -437,7 +468,7 @@ static void em28xx_copy_video(struct em28xx *dev,
        linesdone = buf->pos / bytesperline;
        currlinedone = buf->pos % bytesperline;
 
-       if (dev->progressive)
+       if (v4l2->progressive)
                offset = linesdone * bytesperline + currlinedone;
        else
                offset = linesdone * bytesperline * 2 + currlinedone;
@@ -461,7 +492,7 @@ static void em28xx_copy_video(struct em28xx *dev,
        remain -= lencopy;
 
        while (remain > 0) {
-               if (dev->progressive)
+               if (v4l2->progressive)
                        startwrite += lencopy;
                else
                        startwrite += lencopy + bytesperline;
@@ -507,7 +538,7 @@ static void em28xx_copy_vbi(struct em28xx *dev,
        offset = buf->pos;
        /* Make sure the bottom field populates the second half of the frame */
        if (buf->top_field == 0)
-               offset += dev->vbi_width * dev->vbi_height;
+               offset += dev->v4l2->vbi_width * dev->v4l2->vbi_height;
 
        memcpy(buf->vb_buf + offset, usb_buf, len);
        buf->pos += len;
@@ -583,13 +614,15 @@ finish_field_prepare_next(struct em28xx *dev,
                          struct em28xx_buffer *buf,
                          struct em28xx_dmaqueue *dma_q)
 {
-       if (dev->progressive || dev->top_field) { /* Brand new frame */
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
+
+       if (v4l2->progressive || v4l2->top_field) { /* Brand new frame */
                if (buf != NULL)
                        finish_buffer(dev, buf);
                buf = get_next_buf(dev, dma_q);
        }
        if (buf != NULL) {
-               buf->top_field = dev->top_field;
+               buf->top_field = v4l2->top_field;
                buf->pos = 0;
        }
 
@@ -603,6 +636,7 @@ static inline void process_frame_data_em28xx(struct em28xx *dev,
                                             unsigned char *data_pkt,
                                             unsigned int  data_len)
 {
+       struct em28xx_v4l2      *v4l2 = dev->v4l2;
        struct em28xx_buffer    *buf = dev->usb_ctl.vid_buf;
        struct em28xx_buffer    *vbi_buf = dev->usb_ctl.vbi_buf;
        struct em28xx_dmaqueue  *dma_q = &dev->vidq;
@@ -622,17 +656,17 @@ static inline void process_frame_data_em28xx(struct em28xx *dev,
                        data_len -= 4;
                } else if (data_pkt[0] == 0x33 && data_pkt[1] == 0x95) {
                        /* Field start (VBI mode) */
-                       dev->capture_type = 0;
-                       dev->vbi_read = 0;
+                       v4l2->capture_type = 0;
+                       v4l2->vbi_read = 0;
                        em28xx_isocdbg("VBI START HEADER !!!\n");
-                       dev->top_field = !(data_pkt[2] & 1);
+                       v4l2->top_field = !(data_pkt[2] & 1);
                        data_pkt += 4;
                        data_len -= 4;
                } else if (data_pkt[0] == 0x22 && data_pkt[1] == 0x5a) {
                        /* Field start (VBI disabled) */
-                       dev->capture_type = 2;
+                       v4l2->capture_type = 2;
                        em28xx_isocdbg("VIDEO START HEADER !!!\n");
-                       dev->top_field = !(data_pkt[2] & 1);
+                       v4l2->top_field = !(data_pkt[2] & 1);
                        data_pkt += 4;
                        data_len -= 4;
                }
@@ -640,37 +674,37 @@ static inline void process_frame_data_em28xx(struct em28xx *dev,
        /* NOTE: With bulk transfers, intermediate data packets
         * have no continuation header */
 
-       if (dev->capture_type == 0) {
+       if (v4l2->capture_type == 0) {
                vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q);
                dev->usb_ctl.vbi_buf = vbi_buf;
-               dev->capture_type = 1;
+               v4l2->capture_type = 1;
        }
 
-       if (dev->capture_type == 1) {
-               int vbi_size = dev->vbi_width * dev->vbi_height;
-               int vbi_data_len = ((dev->vbi_read + data_len) > vbi_size) ?
-                                  (vbi_size - dev->vbi_read) : data_len;
+       if (v4l2->capture_type == 1) {
+               int vbi_size = v4l2->vbi_width * v4l2->vbi_height;
+               int vbi_data_len = ((v4l2->vbi_read + data_len) > vbi_size) ?
+                                  (vbi_size - v4l2->vbi_read) : data_len;
 
                /* Copy VBI data */
                if (vbi_buf != NULL)
                        em28xx_copy_vbi(dev, vbi_buf, data_pkt, vbi_data_len);
-               dev->vbi_read += vbi_data_len;
+               v4l2->vbi_read += vbi_data_len;
 
                if (vbi_data_len < data_len) {
                        /* Continue with copying video data */
-                       dev->capture_type = 2;
+                       v4l2->capture_type = 2;
                        data_pkt += vbi_data_len;
                        data_len -= vbi_data_len;
                }
        }
 
-       if (dev->capture_type == 2) {
+       if (v4l2->capture_type == 2) {
                buf = finish_field_prepare_next(dev, buf, dma_q);
                dev->usb_ctl.vid_buf = buf;
-               dev->capture_type = 3;
+               v4l2->capture_type = 3;
        }
 
-       if (dev->capture_type == 3 && buf != NULL && data_len > 0)
+       if (v4l2->capture_type == 3 && buf != NULL && data_len > 0)
                em28xx_copy_video(dev, buf, data_pkt, data_len);
 }
 
@@ -683,6 +717,7 @@ static inline void process_frame_data_em25xx(struct em28xx *dev,
 {
        struct em28xx_buffer    *buf = dev->usb_ctl.vid_buf;
        struct em28xx_dmaqueue  *dmaq = &dev->vidq;
+       struct em28xx_v4l2      *v4l2 = dev->v4l2;
        bool frame_end = 0;
 
        /* Check for header */
@@ -691,7 +726,7 @@ static inline void process_frame_data_em25xx(struct em28xx *dev,
        if (data_len >= 2) {    /* em25xx header is only 2 bytes long */
                if ((data_pkt[0] == EM25XX_FRMDATAHDR_BYTE1) &&
                    ((data_pkt[1] & ~EM25XX_FRMDATAHDR_BYTE2_MASK) == 0x00)) {
-                       dev->top_field = !(data_pkt[1] &
+                       v4l2->top_field = !(data_pkt[1] &
                                           EM25XX_FRMDATAHDR_BYTE2_FRAME_ID);
                        frame_end = data_pkt[1] &
                                    EM25XX_FRMDATAHDR_BYTE2_FRAME_END;
@@ -841,12 +876,14 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
                       unsigned int sizes[], void *alloc_ctxs[])
 {
        struct em28xx *dev = vb2_get_drv_priv(vq);
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
        unsigned long size;
 
        if (fmt)
                size = fmt->fmt.pix.sizeimage;
        else
-               size = (dev->width * dev->height * dev->format->depth + 7) >> 3;
+               size =
+                   (v4l2->width * v4l2->height * v4l2->format->depth + 7) >> 3;
 
        if (size == 0)
                return -EINVAL;
@@ -864,12 +901,13 @@ static int
 buffer_prepare(struct vb2_buffer *vb)
 {
        struct em28xx        *dev = vb2_get_drv_priv(vb->vb2_queue);
+       struct em28xx_v4l2   *v4l2 = dev->v4l2;
        struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
        unsigned long size;
 
        em28xx_videodbg("%s, field=%d\n", __func__, vb->v4l2_buf.field);
 
-       size = (dev->width * dev->height * dev->format->depth + 7) >> 3;
+       size = (v4l2->width * v4l2->height * v4l2->format->depth + 7) >> 3;
 
        if (vb2_plane_size(vb, 0) < size) {
                em28xx_videodbg("%s data will not fit into plane (%lu < %lu)\n",
@@ -884,6 +922,7 @@ buffer_prepare(struct vb2_buffer *vb)
 int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
 {
        struct em28xx *dev = vb2_get_drv_priv(vq);
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
        struct v4l2_frequency f;
        int rc = 0;
 
@@ -895,7 +934,7 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
        if (rc)
                return rc;
 
-       if (dev->streaming_users == 0) {
+       if (v4l2->streaming_users == 0) {
                /* First active streaming user, so allocate all the URBs */
 
                /* Allocate the USB bandwidth */
@@ -906,7 +945,7 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
                */
                em28xx_wake_i2c(dev);
 
-               dev->capture_type = -1;
+               v4l2->capture_type = -1;
                rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE,
                                          dev->analog_xfer_bulk,
                                          EM28XX_NUM_BUFS,
@@ -924,22 +963,24 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
 
                /* Ask tuner to go to analog or radio mode */
                memset(&f, 0, sizeof(f));
-               f.frequency = dev->ctl_freq;
+               f.frequency = v4l2->frequency;
                if (vq->owner && vq->owner->vdev->vfl_type == VFL_TYPE_RADIO)
                        f.type = V4L2_TUNER_RADIO;
                else
                        f.type = V4L2_TUNER_ANALOG_TV;
-               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
+               v4l2_device_call_all(&v4l2->v4l2_dev,
+                                    0, tuner, s_frequency, &f);
        }
 
-       dev->streaming_users++;
+       v4l2->streaming_users++;
 
        return rc;
 }
 
-static int em28xx_stop_streaming(struct vb2_queue *vq)
+static void em28xx_stop_streaming(struct vb2_queue *vq)
 {
        struct em28xx *dev = vb2_get_drv_priv(vq);
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
        struct em28xx_dmaqueue *vidq = &dev->vidq;
        unsigned long flags = 0;
 
@@ -947,7 +988,7 @@ static int em28xx_stop_streaming(struct vb2_queue *vq)
 
        res_free(dev, vq->type);
 
-       if (dev->streaming_users-- == 1) {
+       if (v4l2->streaming_users-- == 1) {
                /* Last active user, so shutdown all the URBS */
                em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE);
        }
@@ -961,13 +1002,12 @@ static int em28xx_stop_streaming(struct vb2_queue *vq)
        }
        dev->usb_ctl.vid_buf = NULL;
        spin_unlock_irqrestore(&dev->slock, flags);
-
-       return 0;
 }
 
-int em28xx_stop_vbi_streaming(struct vb2_queue *vq)
+void em28xx_stop_vbi_streaming(struct vb2_queue *vq)
 {
        struct em28xx *dev = vb2_get_drv_priv(vq);
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
        struct em28xx_dmaqueue *vbiq = &dev->vbiq;
        unsigned long flags = 0;
 
@@ -975,7 +1015,7 @@ int em28xx_stop_vbi_streaming(struct vb2_queue *vq)
 
        res_free(dev, vq->type);
 
-       if (dev->streaming_users-- == 1) {
+       if (v4l2->streaming_users-- == 1) {
                /* Last active user, so shutdown all the URBS */
                em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE);
        }
@@ -989,8 +1029,6 @@ int em28xx_stop_vbi_streaming(struct vb2_queue *vq)
        }
        dev->usb_ctl.vbi_buf = NULL;
        spin_unlock_irqrestore(&dev->slock, flags);
-
-       return 0;
 }
 
 static void
@@ -1024,9 +1062,10 @@ static int em28xx_vb2_setup(struct em28xx *dev)
 {
        int rc;
        struct vb2_queue *q;
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
 
        /* Setup Videobuf2 for Video capture */
-       q = &dev->vb_vidq;
+       q = &v4l2->vb_vidq;
        q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
@@ -1040,7 +1079,7 @@ static int em28xx_vb2_setup(struct em28xx *dev)
                return rc;
 
        /* Setup Videobuf2 for VBI capture */
-       q = &dev->vb_vbiq;
+       q = &v4l2->vb_vbiq;
        q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
        q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
@@ -1060,6 +1099,7 @@ static int em28xx_vb2_setup(struct em28xx *dev)
 
 static void video_mux(struct em28xx *dev, int index)
 {
+       struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev;
        dev->ctl_input = index;
        dev->ctl_ainput = INPUT(index)->amux;
        dev->ctl_aoutput = INPUT(index)->aout;
@@ -1067,21 +1107,21 @@ static void video_mux(struct em28xx *dev, int index)
        if (!dev->ctl_aoutput)
                dev->ctl_aoutput = EM28XX_AOUT_MASTER;
 
-       v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
+       v4l2_device_call_all(v4l2_dev, 0, video, s_routing,
                        INPUT(index)->vmux, 0, 0);
 
        if (dev->board.has_msp34xx) {
                if (dev->i2s_speed) {
-                       v4l2_device_call_all(&dev->v4l2_dev, 0, audio,
+                       v4l2_device_call_all(v4l2_dev, 0, audio,
                                s_i2s_clock_freq, dev->i2s_speed);
                }
                /* Note: this is msp3400 specific */
-               v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
+               v4l2_device_call_all(v4l2_dev, 0, audio, s_routing,
                         dev->ctl_ainput, MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0);
        }
 
        if (dev->board.adecoder != EM28XX_NOADECODER) {
-               v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
+               v4l2_device_call_all(v4l2_dev, 0, audio, s_routing,
                        dev->ctl_ainput, dev->ctl_aoutput, 0);
        }
 
@@ -1112,7 +1152,9 @@ static void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv)
 
 static int em28xx_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct em28xx *dev = container_of(ctrl->handler, struct em28xx, ctrl_handler);
+       struct em28xx_v4l2 *v4l2 =
+                 container_of(ctrl->handler, struct em28xx_v4l2, ctrl_handler);
+       struct em28xx *dev = v4l2->dev;
        int ret = -EINVAL;
 
        switch (ctrl->id) {
@@ -1187,19 +1229,20 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
 {
        struct em28xx_fh      *fh  = priv;
        struct em28xx         *dev = fh->dev;
+       struct em28xx_v4l2    *v4l2 = dev->v4l2;
 
-       f->fmt.pix.width = dev->width;
-       f->fmt.pix.height = dev->height;
-       f->fmt.pix.pixelformat = dev->format->fourcc;
-       f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
-       f->fmt.pix.sizeimage = f->fmt.pix.bytesperline  * dev->height;
+       f->fmt.pix.width = v4l2->width;
+       f->fmt.pix.height = v4l2->height;
+       f->fmt.pix.pixelformat = v4l2->format->fourcc;
+       f->fmt.pix.bytesperline = (v4l2->width * v4l2->format->depth + 7) >> 3;
+       f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * v4l2->height;
        f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
 
        /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
-       if (dev->progressive)
+       if (v4l2->progressive)
                f->fmt.pix.field = V4L2_FIELD_NONE;
        else
-               f->fmt.pix.field = dev->interlaced ?
+               f->fmt.pix.field = v4l2->interlaced_fieldmode ?
                           V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
        return 0;
 }
@@ -1220,6 +1263,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
 {
        struct em28xx_fh      *fh    = priv;
        struct em28xx         *dev   = fh->dev;
+       struct em28xx_v4l2    *v4l2  = dev->v4l2;
        unsigned int          width  = f->fmt.pix.width;
        unsigned int          height = f->fmt.pix.height;
        unsigned int          maxw   = norm_maxw(dev);
@@ -1261,10 +1305,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
        f->fmt.pix.bytesperline = (width * fmt->depth + 7) >> 3;
        f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
        f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-       if (dev->progressive)
+       if (v4l2->progressive)
                f->fmt.pix.field = V4L2_FIELD_NONE;
        else
-               f->fmt.pix.field = dev->interlaced ?
+               f->fmt.pix.field = v4l2->interlaced_fieldmode ?
                           V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
        f->fmt.pix.priv = 0;
 
@@ -1275,17 +1319,19 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc,
                                   unsigned width, unsigned height)
 {
        struct em28xx_fmt     *fmt;
+       struct em28xx_v4l2    *v4l2 = dev->v4l2;
 
        fmt = format_by_fourcc(fourcc);
        if (!fmt)
                return -EINVAL;
 
-       dev->format = fmt;
-       dev->width  = width;
-       dev->height = height;
+       v4l2->format = fmt;
+       v4l2->width  = width;
+       v4l2->height = height;
 
        /* set new image size */
-       size_to_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
+       size_to_scale(dev, v4l2->width, v4l2->height,
+                          &v4l2->hscale, &v4l2->vscale);
 
        em28xx_resolution_set(dev);
 
@@ -1296,8 +1342,9 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                        struct v4l2_format *f)
 {
        struct em28xx *dev = video_drvdata(file);
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
 
-       if (dev->streaming_users > 0)
+       if (v4l2->streaming_users > 0)
                return -EBUSY;
 
        vidioc_try_fmt_vid_cap(file, priv, f);
@@ -1311,7 +1358,7 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
        struct em28xx_fh   *fh  = priv;
        struct em28xx      *dev = fh->dev;
 
-       *norm = dev->norm;
+       *norm = dev->v4l2->norm;
 
        return 0;
 }
@@ -1321,24 +1368,25 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
        struct em28xx_fh   *fh  = priv;
        struct em28xx      *dev = fh->dev;
 
-       v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
+       v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, video, querystd, norm);
 
        return 0;
 }
 
 static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
 {
-       struct em28xx_fh   *fh  = priv;
-       struct em28xx      *dev = fh->dev;
+       struct em28xx_fh   *fh   = priv;
+       struct em28xx      *dev  = fh->dev;
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
        struct v4l2_format f;
 
-       if (norm == dev->norm)
+       if (norm == v4l2->norm)
                return 0;
 
-       if (dev->streaming_users > 0)
+       if (v4l2->streaming_users > 0)
                return -EBUSY;
 
-       dev->norm = norm;
+       v4l2->norm = norm;
 
        /* Adjusts width/height, if needed */
        f.fmt.pix.width = 720;
@@ -1346,12 +1394,13 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
        vidioc_try_fmt_vid_cap(file, priv, &f);
 
        /* set new image size */
-       dev->width = f.fmt.pix.width;
-       dev->height = f.fmt.pix.height;
-       size_to_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
+       v4l2->width = f.fmt.pix.width;
+       v4l2->height = f.fmt.pix.height;
+       size_to_scale(dev, v4l2->width, v4l2->height,
+                          &v4l2->hscale, &v4l2->vscale);
 
        em28xx_resolution_set(dev);
-       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+       v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm);
 
        return 0;
 }
@@ -1359,16 +1408,17 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
 static int vidioc_g_parm(struct file *file, void *priv,
                         struct v4l2_streamparm *p)
 {
-       struct em28xx_fh   *fh  = priv;
-       struct em28xx      *dev = fh->dev;
+       struct em28xx_fh   *fh   = priv;
+       struct em28xx      *dev  = fh->dev;
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
        int rc = 0;
 
        p->parm.capture.readbuffers = EM28XX_MIN_BUF;
        if (dev->board.is_webcam)
-               rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0,
+               rc = v4l2_device_call_until_err(&v4l2->v4l2_dev, 0,
                                                video, g_parm, p);
        else
-               v4l2_video_std_frame_period(dev->norm,
+               v4l2_video_std_frame_period(v4l2->norm,
                                                 &p->parm.capture.timeperframe);
 
        return rc;
@@ -1381,7 +1431,8 @@ static int vidioc_s_parm(struct file *file, void *priv,
        struct em28xx      *dev = fh->dev;
 
        p->parm.capture.readbuffers = EM28XX_MIN_BUF;
-       return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p);
+       return v4l2_device_call_until_err(&dev->v4l2->v4l2_dev,
+                                         0, video, s_parm, p);
 }
 
 static const char *iname[] = {
@@ -1418,7 +1469,7 @@ static int vidioc_enum_input(struct file *file, void *priv,
                (EM28XX_VMUX_CABLE == INPUT(n)->type))
                i->type = V4L2_INPUT_TYPE_TUNER;
 
-       i->std = dev->vdev->tvnorms;
+       i->std = dev->v4l2->vdev->tvnorms;
        /* webcams do not have the STD API */
        if (dev->board.is_webcam)
                i->capabilities = 0;
@@ -1520,7 +1571,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
 
        strcpy(t->name, "Tuner");
 
-       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+       v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t);
        return 0;
 }
 
@@ -1533,7 +1584,7 @@ static int vidioc_s_tuner(struct file *file, void *priv,
        if (0 != t->index)
                return -EINVAL;
 
-       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
+       v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t);
        return 0;
 }
 
@@ -1542,27 +1593,29 @@ static int vidioc_g_frequency(struct file *file, void *priv,
 {
        struct em28xx_fh      *fh  = priv;
        struct em28xx         *dev = fh->dev;
+       struct em28xx_v4l2    *v4l2 = dev->v4l2;
 
        if (0 != f->tuner)
                return -EINVAL;
 
-       f->frequency = dev->ctl_freq;
+       f->frequency = v4l2->frequency;
        return 0;
 }
 
 static int vidioc_s_frequency(struct file *file, void *priv,
                                const struct v4l2_frequency *f)
 {
-       struct v4l2_frequency new_freq = *f;
-       struct em28xx_fh      *fh  = priv;
-       struct em28xx         *dev = fh->dev;
+       struct v4l2_frequency  new_freq = *f;
+       struct em28xx_fh          *fh   = priv;
+       struct em28xx             *dev  = fh->dev;
+       struct em28xx_v4l2        *v4l2 = dev->v4l2;
 
        if (0 != f->tuner)
                return -EINVAL;
 
-       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f);
-       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, &new_freq);
-       dev->ctl_freq = new_freq.frequency;
+       v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f);
+       v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, g_frequency, &new_freq);
+       v4l2->frequency = new_freq.frequency;
 
        return 0;
 }
@@ -1579,7 +1632,8 @@ static int vidioc_g_chip_info(struct file *file, void *priv,
        if (chip->match.addr == 1)
                strlcpy(chip->name, "ac97", sizeof(chip->name));
        else
-               strlcpy(chip->name, dev->v4l2_dev.name, sizeof(chip->name));
+               strlcpy(chip->name,
+                       dev->v4l2->v4l2_dev.name, sizeof(chip->name));
        return 0;
 }
 
@@ -1660,9 +1714,10 @@ static int vidioc_s_register(struct file *file, void *priv,
 static int vidioc_querycap(struct file *file, void  *priv,
                                        struct v4l2_capability *cap)
 {
-       struct video_device *vdev = video_devdata(file);
-       struct em28xx_fh      *fh  = priv;
-       struct em28xx         *dev = fh->dev;
+       struct video_device   *vdev = video_devdata(file);
+       struct em28xx_fh      *fh   = priv;
+       struct em28xx         *dev  = fh->dev;
+       struct em28xx_v4l2    *v4l2 = dev->v4l2;
 
        strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
        strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
@@ -1684,9 +1739,9 @@ static int vidioc_querycap(struct file *file, void  *priv,
 
        cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS |
                V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
-       if (dev->vbi_dev)
+       if (v4l2->vbi_dev)
                cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
-       if (dev->radio_dev)
+       if (v4l2->radio_dev)
                cap->capabilities |= V4L2_CAP_RADIO;
        return 0;
 }
@@ -1751,24 +1806,25 @@ static int vidioc_enum_framesizes(struct file *file, void *priv,
 static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
                                struct v4l2_format *format)
 {
-       struct em28xx_fh      *fh  = priv;
-       struct em28xx         *dev = fh->dev;
+       struct em28xx_fh      *fh   = priv;
+       struct em28xx         *dev  = fh->dev;
+       struct em28xx_v4l2    *v4l2 = dev->v4l2;
 
-       format->fmt.vbi.samples_per_line = dev->vbi_width;
+       format->fmt.vbi.samples_per_line = v4l2->vbi_width;
        format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
        format->fmt.vbi.offset = 0;
        format->fmt.vbi.flags = 0;
        format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
-       format->fmt.vbi.count[0] = dev->vbi_height;
-       format->fmt.vbi.count[1] = dev->vbi_height;
+       format->fmt.vbi.count[0] = v4l2->vbi_height;
+       format->fmt.vbi.count[1] = v4l2->vbi_height;
        memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved));
 
        /* Varies by video standard (NTSC, PAL, etc.) */
-       if (dev->norm & V4L2_STD_525_60) {
+       if (v4l2->norm & V4L2_STD_525_60) {
                /* NTSC */
                format->fmt.vbi.start[0] = 10;
                format->fmt.vbi.start[1] = 273;
-       } else if (dev->norm & V4L2_STD_625_50) {
+       } else if (v4l2->norm & V4L2_STD_625_50) {
                /* PAL */
                format->fmt.vbi.start[0] = 6;
                format->fmt.vbi.start[1] = 318;
@@ -1791,7 +1847,7 @@ static int radio_g_tuner(struct file *file, void *priv,
 
        strcpy(t->name, "Radio");
 
-       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+       v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t);
 
        return 0;
 }
@@ -1804,11 +1860,26 @@ static int radio_s_tuner(struct file *file, void *priv,
        if (0 != t->index)
                return -EINVAL;
 
-       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
+       v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t);
 
        return 0;
 }
 
+/*
+ * em28xx_free_v4l2() - Free struct em28xx_v4l2
+ *
+ * @ref: struct kref for struct em28xx_v4l2
+ *
+ * Called when all users of struct em28xx_v4l2 are gone
+ */
+static void em28xx_free_v4l2(struct kref *ref)
+{
+       struct em28xx_v4l2 *v4l2 = container_of(ref, struct em28xx_v4l2, ref);
+
+       v4l2->dev->v4l2 = NULL;
+       kfree(v4l2);
+}
+
 /*
  * em28xx_v4l2_open()
  * inits the device and starts isoc transfer
@@ -1817,6 +1888,7 @@ static int em28xx_v4l2_open(struct file *filp)
 {
        struct video_device *vdev = video_devdata(filp);
        struct em28xx *dev = video_drvdata(filp);
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
        enum v4l2_buf_type fh_type = 0;
        struct em28xx_fh *fh;
 
@@ -1835,7 +1907,7 @@ static int em28xx_v4l2_open(struct file *filp)
 
        em28xx_videodbg("open dev=%s type=%s users=%d\n",
                        video_device_node_name(vdev), v4l2_type_names[fh_type],
-                       dev->users);
+                       v4l2->users);
 
        if (mutex_lock_interruptible(&dev->lock))
                return -ERESTARTSYS;
@@ -1850,7 +1922,7 @@ static int em28xx_v4l2_open(struct file *filp)
        fh->type = fh_type;
        filp->private_data = fh;
 
-       if (dev->users == 0) {
+       if (v4l2->users == 0) {
                em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
 
                if (vdev->vfl_type != VFL_TYPE_RADIO)
@@ -1865,11 +1937,12 @@ static int em28xx_v4l2_open(struct file *filp)
 
        if (vdev->vfl_type == VFL_TYPE_RADIO) {
                em28xx_videodbg("video_open: setting radio device\n");
-               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio);
+               v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_radio);
        }
 
        kref_get(&dev->ref);
-       dev->users++;
+       kref_get(&v4l2->ref);
+       v4l2->users++;
 
        mutex_unlock(&dev->lock);
        v4l2_fh_add(&fh->fh);
@@ -1884,6 +1957,8 @@ static int em28xx_v4l2_open(struct file *filp)
 */
 static int em28xx_v4l2_fini(struct em28xx *dev)
 {
+       struct em28xx_v4l2 *v4l2 = dev->v4l2;
+
        if (dev->is_audio_only) {
                /* Shouldn't initialize IR for this interface */
                return 0;
@@ -1894,39 +1969,45 @@ static int em28xx_v4l2_fini(struct em28xx *dev)
                return 0;
        }
 
+       if (v4l2 == NULL)
+               return 0;
+
        em28xx_info("Closing video extension");
 
        mutex_lock(&dev->lock);
 
-       v4l2_device_disconnect(&dev->v4l2_dev);
+       v4l2_device_disconnect(&v4l2->v4l2_dev);
 
        em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE);
 
-       if (dev->radio_dev) {
+       if (v4l2->radio_dev) {
                em28xx_info("V4L2 device %s deregistered\n",
-                           video_device_node_name(dev->radio_dev));
-               video_unregister_device(dev->radio_dev);
+                           video_device_node_name(v4l2->radio_dev));
+               video_unregister_device(v4l2->radio_dev);
        }
-       if (dev->vbi_dev) {
+       if (v4l2->vbi_dev) {
                em28xx_info("V4L2 device %s deregistered\n",
-                           video_device_node_name(dev->vbi_dev));
-               video_unregister_device(dev->vbi_dev);
+                           video_device_node_name(v4l2->vbi_dev));
+               video_unregister_device(v4l2->vbi_dev);
        }
-       if (dev->vdev) {
+       if (v4l2->vdev) {
                em28xx_info("V4L2 device %s deregistered\n",
-                           video_device_node_name(dev->vdev));
-               video_unregister_device(dev->vdev);
+                           video_device_node_name(v4l2->vdev));
+               video_unregister_device(v4l2->vdev);
        }
 
-       v4l2_ctrl_handler_free(&dev->ctrl_handler);
-       v4l2_device_unregister(&dev->v4l2_dev);
+       v4l2_ctrl_handler_free(&v4l2->ctrl_handler);
+       v4l2_device_unregister(&v4l2->v4l2_dev);
 
-       if (dev->clk) {
-               v4l2_clk_unregister_fixed(dev->clk);
-               dev->clk = NULL;
+       if (v4l2->clk) {
+               v4l2_clk_unregister_fixed(v4l2->clk);
+               v4l2->clk = NULL;
        }
 
+       kref_put(&v4l2->ref, em28xx_free_v4l2);
+
        mutex_unlock(&dev->lock);
+
        kref_put(&dev->ref, em28xx_free_device);
 
        return 0;
@@ -1965,22 +2046,23 @@ static int em28xx_v4l2_resume(struct em28xx *dev)
  */
 static int em28xx_v4l2_close(struct file *filp)
 {
-       struct em28xx_fh *fh  = filp->private_data;
-       struct em28xx    *dev = fh->dev;
+       struct em28xx_fh      *fh   = filp->private_data;
+       struct em28xx         *dev  = fh->dev;
+       struct em28xx_v4l2    *v4l2 = dev->v4l2;
        int              errCode;
 
-       em28xx_videodbg("users=%d\n", dev->users);
+       em28xx_videodbg("users=%d\n", v4l2->users);
 
        vb2_fop_release(filp);
        mutex_lock(&dev->lock);
 
-       if (dev->users == 1) {
+       if (v4l2->users == 1) {
                /* No sense to try to write to the device */
                if (dev->disconnected)
                        goto exit;
 
                /* Save some power by putting tuner to sleep */
-               v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
+               v4l2_device_call_all(&v4l2->v4l2_dev, 0, core, s_power, 0);
 
                /* do this before setting alternate! */
                em28xx_set_mode(dev, EM28XX_SUSPEND);
@@ -1996,30 +2078,14 @@ static int em28xx_v4l2_close(struct file *filp)
        }
 
 exit:
-       dev->users--;
+       v4l2->users--;
+       kref_put(&v4l2->ref, em28xx_free_v4l2);
        mutex_unlock(&dev->lock);
        kref_put(&dev->ref, em28xx_free_device);
 
        return 0;
 }
 
-/*
- * em28xx_videodevice_release()
- * called when the last user of the video device exits and frees the memeory
- */
-static void em28xx_videodevice_release(struct video_device *vdev)
-{
-       struct em28xx *dev = video_get_drvdata(vdev);
-
-       video_device_release(vdev);
-       if (vdev == dev->vdev)
-               dev->vdev = NULL;
-       else if (vdev == dev->vbi_dev)
-               dev->vbi_dev = NULL;
-       else if (vdev == dev->radio_dev)
-               dev->radio_dev = NULL;
-}
-
 static const struct v4l2_file_operations em28xx_v4l_fops = {
        .owner         = THIS_MODULE,
        .open          = em28xx_v4l2_open,
@@ -2076,7 +2142,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
 static const struct video_device em28xx_video_template = {
        .fops           = &em28xx_v4l_fops,
        .ioctl_ops      = &video_ioctl_ops,
-       .release        = em28xx_videodevice_release,
+       .release        = video_device_release,
        .tvnorms        = V4L2_STD_ALL,
 };
 
@@ -2105,7 +2171,7 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = {
 static struct video_device em28xx_radio_template = {
        .fops           = &radio_fops,
        .ioctl_ops      = &radio_ioctl_ops,
-       .release        = em28xx_videodevice_release,
+       .release        = video_device_release,
 };
 
 /* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */
@@ -2139,7 +2205,7 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev,
                return NULL;
 
        *vfd            = *template;
-       vfd->v4l2_dev   = &dev->v4l2_dev;
+       vfd->v4l2_dev   = &dev->v4l2->v4l2_dev;
        vfd->debug      = video_debug;
        vfd->lock       = &dev->lock;
        set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
@@ -2153,13 +2219,12 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev,
        return vfd;
 }
 
-static void em28xx_tuner_setup(struct em28xx *dev)
+static void em28xx_tuner_setup(struct em28xx *dev, unsigned short tuner_addr)
 {
-       struct tuner_setup           tun_setup;
-       struct v4l2_frequency        f;
-
-       if (dev->tuner_type == TUNER_ABSENT)
-               return;
+       struct em28xx_v4l2      *v4l2 = dev->v4l2;
+       struct v4l2_device      *v4l2_dev = &v4l2->v4l2_dev;
+       struct tuner_setup      tun_setup;
+       struct v4l2_frequency   f;
 
        memset(&tun_setup, 0, sizeof(tun_setup));
 
@@ -2170,23 +2235,26 @@ static void em28xx_tuner_setup(struct em28xx *dev)
                tun_setup.type = dev->board.radio.type;
                tun_setup.addr = dev->board.radio_addr;
 
-               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+               v4l2_device_call_all(v4l2_dev,
+                                    0, tuner, s_type_addr, &tun_setup);
        }
 
        if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) {
                tun_setup.type   = dev->tuner_type;
-               tun_setup.addr   = dev->tuner_addr;
+               tun_setup.addr   = tuner_addr;
 
-               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+               v4l2_device_call_all(v4l2_dev,
+                                    0, tuner, s_type_addr, &tun_setup);
        }
 
-       if (dev->tda9887_conf) {
+       if (dev->board.tda9887_conf) {
                struct v4l2_priv_tun_config tda9887_cfg;
 
                tda9887_cfg.tuner = TUNER_TDA9887;
-               tda9887_cfg.priv = &dev->tda9887_conf;
+               tda9887_cfg.priv = &dev->board.tda9887_conf;
 
-               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg);
+               v4l2_device_call_all(v4l2_dev,
+                                    0, tuner, s_config, &tda9887_cfg);
        }
 
        if (dev->tuner_type == TUNER_XC2028) {
@@ -2201,15 +2269,15 @@ static void em28xx_tuner_setup(struct em28xx *dev)
                xc2028_cfg.tuner = TUNER_XC2028;
                xc2028_cfg.priv  = &ctl;
 
-               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg);
+               v4l2_device_call_all(v4l2_dev, 0, tuner, s_config, &xc2028_cfg);
        }
 
        /* configure tuner */
        f.tuner = 0;
        f.type = V4L2_TUNER_ANALOG_TV;
        f.frequency = 9076;     /* just a magic number */
-       dev->ctl_freq = f.frequency;
-       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
+       v4l2->frequency = f.frequency;
+       v4l2_device_call_all(v4l2_dev, 0, tuner, s_frequency, &f);
 }
 
 static int em28xx_v4l2_init(struct em28xx *dev)
@@ -2217,7 +2285,8 @@ static int em28xx_v4l2_init(struct em28xx *dev)
        u8 val;
        int ret;
        unsigned int maxw;
-       struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
+       struct v4l2_ctrl_handler *hdl;
+       struct em28xx_v4l2 *v4l2;
 
        if (dev->is_audio_only) {
                /* Shouldn't initialize IR for this interface */
@@ -2233,71 +2302,93 @@ static int em28xx_v4l2_init(struct em28xx *dev)
 
        mutex_lock(&dev->lock);
 
-       ret = v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
+       v4l2 = kzalloc(sizeof(struct em28xx_v4l2), GFP_KERNEL);
+       if (v4l2 == NULL) {
+               em28xx_info("em28xx_v4l: memory allocation failed\n");
+               mutex_unlock(&dev->lock);
+               return -ENOMEM;
+       }
+       kref_init(&v4l2->ref);
+       v4l2->dev = dev;
+       dev->v4l2 = v4l2;
+
+       ret = v4l2_device_register(&dev->udev->dev, &v4l2->v4l2_dev);
        if (ret < 0) {
                em28xx_errdev("Call to v4l2_device_register() failed!\n");
                goto err;
        }
 
+       hdl = &v4l2->ctrl_handler;
        v4l2_ctrl_handler_init(hdl, 8);
-       dev->v4l2_dev.ctrl_handler = hdl;
+       v4l2->v4l2_dev.ctrl_handler = hdl;
+
+       if (dev->board.is_webcam)
+               v4l2->progressive = 1;
 
        /*
         * Default format, used for tvp5150 or saa711x output formats
         */
-       dev->vinmode = 0x10;
-       dev->vinctl  = EM28XX_VINCTRL_INTERLACED |
-                      EM28XX_VINCTRL_CCIR656_ENABLE;
+       v4l2->vinmode = 0x10;
+       v4l2->vinctl  = EM28XX_VINCTRL_INTERLACED |
+                       EM28XX_VINCTRL_CCIR656_ENABLE;
 
        /* request some modules */
 
        if (dev->board.has_msp34xx)
-               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
-                       "msp3400", 0, msp3400_addrs);
+               v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
+                                   &dev->i2c_adap[dev->def_i2c_bus],
+                                   "msp3400", 0, msp3400_addrs);
 
        if (dev->board.decoder == EM28XX_SAA711X)
-               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
-                       "saa7115_auto", 0, saa711x_addrs);
+               v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
+                                   &dev->i2c_adap[dev->def_i2c_bus],
+                                   "saa7115_auto", 0, saa711x_addrs);
 
        if (dev->board.decoder == EM28XX_TVP5150)
-               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
-                       "tvp5150", 0, tvp5150_addrs);
+               v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
+                                   &dev->i2c_adap[dev->def_i2c_bus],
+                                   "tvp5150", 0, tvp5150_addrs);
 
        if (dev->board.adecoder == EM28XX_TVAUDIO)
-               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
-                       "tvaudio", dev->board.tvaudio_addr, NULL);
+               v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
+                                   &dev->i2c_adap[dev->def_i2c_bus],
+                                   "tvaudio", dev->board.tvaudio_addr, NULL);
 
        /* Initialize tuner and camera */
 
        if (dev->board.tuner_type != TUNER_ABSENT) {
-               int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
+               unsigned short tuner_addr = dev->board.tuner_addr;
+               int has_demod = (dev->board.tda9887_conf & TDA9887_PRESENT);
 
                if (dev->board.radio.type)
-                       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
-                               "tuner", dev->board.radio_addr, NULL);
+                       v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
+                                         &dev->i2c_adap[dev->def_i2c_bus],
+                                         "tuner", dev->board.radio_addr, NULL);
 
                if (has_demod)
-                       v4l2_i2c_new_subdev(&dev->v4l2_dev,
+                       v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
                                &dev->i2c_adap[dev->def_i2c_bus], "tuner",
                                0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
-               if (dev->tuner_addr == 0) {
+               if (tuner_addr == 0) {
                        enum v4l2_i2c_tuner_type type =
                                has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
                        struct v4l2_subdev *sd;
 
-                       sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+                       sd = v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
                                &dev->i2c_adap[dev->def_i2c_bus], "tuner",
                                0, v4l2_i2c_tuner_addrs(type));
 
                        if (sd)
-                               dev->tuner_addr = v4l2_i2c_subdev_addr(sd);
+                               tuner_addr = v4l2_i2c_subdev_addr(sd);
                } else {
-                       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus],
-                               "tuner", dev->tuner_addr, NULL);
+                       v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
+                                           &dev->i2c_adap[dev->def_i2c_bus],
+                                           "tuner", tuner_addr, NULL);
                }
+
+               em28xx_tuner_setup(dev, tuner_addr);
        }
 
-       em28xx_tuner_setup(dev);
        if (dev->em28xx_sensor != EM28XX_NOSENSOR)
                em28xx_init_camera(dev);
 
@@ -2348,12 +2439,12 @@ static int em28xx_v4l2_init(struct em28xx *dev)
        }
 
        /* set default norm */
-       dev->norm = V4L2_STD_PAL;
-       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
-       dev->interlaced = EM28XX_INTERLACED_DEFAULT;
+       v4l2->norm = V4L2_STD_PAL;
+       v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm);
+       v4l2->interlaced_fieldmode = EM28XX_INTERLACED_DEFAULT;
 
        /* Analog specific initialization */
-       dev->format = &format[0];
+       v4l2->format = &format[0];
 
        maxw = norm_maxw(dev);
        /* MaxPacketSize for em2800 is too small to capture at full resolution
@@ -2376,74 +2467,75 @@ static int em28xx_v4l2_init(struct em28xx *dev)
                         (EM28XX_XCLK_AUDIO_UNMUTE | val));
 
        em28xx_set_outfmt(dev);
-       em28xx_compression_disable(dev);
 
        /* Add image controls */
        /* NOTE: at this point, the subdevices are already registered, so bridge
         * controls are only added/enabled when no subdevice provides them */
-       if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_CONTRAST))
-               v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops,
+       if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_CONTRAST))
+               v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
                                  V4L2_CID_CONTRAST,
                                  0, 0x1f, 1, CONTRAST_DEFAULT);
-       if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_BRIGHTNESS))
-               v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops,
+       if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_BRIGHTNESS))
+               v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
                                  V4L2_CID_BRIGHTNESS,
                                  -0x80, 0x7f, 1, BRIGHTNESS_DEFAULT);
-       if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_SATURATION))
-               v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops,
+       if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_SATURATION))
+               v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
                                  V4L2_CID_SATURATION,
                                  0, 0x1f, 1, SATURATION_DEFAULT);
-       if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_BLUE_BALANCE))
-               v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops,
+       if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_BLUE_BALANCE))
+               v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
                                  V4L2_CID_BLUE_BALANCE,
                                  -0x30, 0x30, 1, BLUE_BALANCE_DEFAULT);
-       if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_RED_BALANCE))
-               v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops,
+       if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_RED_BALANCE))
+               v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
                                  V4L2_CID_RED_BALANCE,
                                  -0x30, 0x30, 1, RED_BALANCE_DEFAULT);
-       if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_SHARPNESS))
-               v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops,
+       if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_SHARPNESS))
+               v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
                                  V4L2_CID_SHARPNESS,
                                  0, 0x0f, 1, SHARPNESS_DEFAULT);
 
        /* Reset image controls */
        em28xx_colorlevels_set_default(dev);
-       v4l2_ctrl_handler_setup(&dev->ctrl_handler);
-       ret = dev->ctrl_handler.error;
+       v4l2_ctrl_handler_setup(hdl);
+       ret = hdl->error;
        if (ret)
                goto unregister_dev;
 
        /* allocate and fill video video_device struct */
-       dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video");
-       if (!dev->vdev) {
+       v4l2->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video");
+       if (!v4l2->vdev) {
                em28xx_errdev("cannot allocate video_device.\n");
                ret = -ENODEV;
                goto unregister_dev;
        }
-       dev->vdev->queue = &dev->vb_vidq;
-       dev->vdev->queue->lock = &dev->vb_queue_lock;
+       mutex_init(&v4l2->vb_queue_lock);
+       mutex_init(&v4l2->vb_vbi_queue_lock);
+       v4l2->vdev->queue = &v4l2->vb_vidq;
+       v4l2->vdev->queue->lock = &v4l2->vb_queue_lock;
 
        /* disable inapplicable ioctls */
        if (dev->board.is_webcam) {
-               v4l2_disable_ioctl(dev->vdev, VIDIOC_QUERYSTD);
-               v4l2_disable_ioctl(dev->vdev, VIDIOC_G_STD);
-               v4l2_disable_ioctl(dev->vdev, VIDIOC_S_STD);
+               v4l2_disable_ioctl(v4l2->vdev, VIDIOC_QUERYSTD);
+               v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_STD);
+               v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_STD);
        } else {
-               v4l2_disable_ioctl(dev->vdev, VIDIOC_S_PARM);
+               v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_PARM);
        }
        if (dev->tuner_type == TUNER_ABSENT) {
-               v4l2_disable_ioctl(dev->vdev, VIDIOC_G_TUNER);
-               v4l2_disable_ioctl(dev->vdev, VIDIOC_S_TUNER);
-               v4l2_disable_ioctl(dev->vdev, VIDIOC_G_FREQUENCY);
-               v4l2_disable_ioctl(dev->vdev, VIDIOC_S_FREQUENCY);
+               v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_TUNER);
+               v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_TUNER);
+               v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_FREQUENCY);
+               v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_FREQUENCY);
        }
        if (!dev->audio_mode.has_audio) {
-               v4l2_disable_ioctl(dev->vdev, VIDIOC_G_AUDIO);
-               v4l2_disable_ioctl(dev->vdev, VIDIOC_S_AUDIO);
+               v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_AUDIO);
+               v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_AUDIO);
        }
 
        /* register v4l2 video video_device */
-       ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
+       ret = video_register_device(v4l2->vdev, VFL_TYPE_GRABBER,
                                       video_nr[dev->devno]);
        if (ret) {
                em28xx_errdev("unable to register video device (error=%i).\n",
@@ -2453,27 +2545,27 @@ static int em28xx_v4l2_init(struct em28xx *dev)
 
        /* Allocate and fill vbi video_device struct */
        if (em28xx_vbi_supported(dev) == 1) {
-               dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template,
+               v4l2->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template,
                                                "vbi");
 
-               dev->vbi_dev->queue = &dev->vb_vbiq;
-               dev->vbi_dev->queue->lock = &dev->vb_vbi_queue_lock;
+               v4l2->vbi_dev->queue = &v4l2->vb_vbiq;
+               v4l2->vbi_dev->queue->lock = &v4l2->vb_vbi_queue_lock;
 
                /* disable inapplicable ioctls */
-               v4l2_disable_ioctl(dev->vdev, VIDIOC_S_PARM);
+               v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_PARM);
                if (dev->tuner_type == TUNER_ABSENT) {
-                       v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_G_TUNER);
-                       v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_S_TUNER);
-                       v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_G_FREQUENCY);
-                       v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_S_FREQUENCY);
+                       v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_TUNER);
+                       v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_TUNER);
+                       v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_FREQUENCY);
+                       v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_FREQUENCY);
                }
                if (!dev->audio_mode.has_audio) {
-                       v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_G_AUDIO);
-                       v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_S_AUDIO);
+                       v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_AUDIO);
+                       v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_AUDIO);
                }
 
                /* register v4l2 vbi video_device */
-               ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+               ret = video_register_device(v4l2->vbi_dev, VFL_TYPE_VBI,
                                            vbi_nr[dev->devno]);
                if (ret < 0) {
                        em28xx_errdev("unable to register vbi device\n");
@@ -2482,32 +2574,32 @@ static int em28xx_v4l2_init(struct em28xx *dev)
        }
 
        if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
-               dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template,
-                                                 "radio");
-               if (!dev->radio_dev) {
+               v4l2->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template,
+                                                  "radio");
+               if (!v4l2->radio_dev) {
                        em28xx_errdev("cannot allocate video_device.\n");
                        ret = -ENODEV;
                        goto unregister_dev;
                }
-               ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+               ret = video_register_device(v4l2->radio_dev, VFL_TYPE_RADIO,
                                            radio_nr[dev->devno]);
                if (ret < 0) {
                        em28xx_errdev("can't register radio device\n");
                        goto unregister_dev;
                }
                em28xx_info("Registered radio device as %s\n",
-                           video_device_node_name(dev->radio_dev));
+                           video_device_node_name(v4l2->radio_dev));
        }
 
        em28xx_info("V4L2 video device registered as %s\n",
-                   video_device_node_name(dev->vdev));
+                   video_device_node_name(v4l2->vdev));
 
-       if (dev->vbi_dev)
+       if (v4l2->vbi_dev)
                em28xx_info("V4L2 VBI device registered as %s\n",
-                           video_device_node_name(dev->vbi_dev));
+                           video_device_node_name(v4l2->vbi_dev));
 
        /* Save some power by putting tuner to sleep */
-       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
+       v4l2_device_call_all(&v4l2->v4l2_dev, 0, core, s_power, 0);
 
        /* initialize videobuf2 stuff */
        em28xx_vb2_setup(dev);
@@ -2520,9 +2612,11 @@ static int em28xx_v4l2_init(struct em28xx *dev)
        return 0;
 
 unregister_dev:
-       v4l2_ctrl_handler_free(&dev->ctrl_handler);
-       v4l2_device_unregister(&dev->v4l2_dev);
+       v4l2_ctrl_handler_free(&v4l2->ctrl_handler);
+       v4l2_device_unregister(&v4l2->v4l2_dev);
 err:
+       dev->v4l2 = NULL;
+       kref_put(&v4l2->ref, em28xx_free_v4l2);
        mutex_unlock(&dev->lock);
        return ret;
 }
index 2051fc9..b4c837d 100644 (file)
 #define EM2765_BOARD_SPEEDLINK_VAD_LAPLACE       91
 #define EM28178_BOARD_PCTV_461E                   92
 #define EM2874_BOARD_KWORLD_UB435Q_V3            93
+#define EM28178_BOARD_PCTV_292E                   94
 
 /* Limits minimum and default number of buffers */
 #define EM28XX_MIN_BUF 4
@@ -497,6 +498,60 @@ struct em28xx_eeprom {
 #define EM28XX_RESOURCE_VIDEO 0x01
 #define EM28XX_RESOURCE_VBI   0x02
 
+struct em28xx_v4l2 {
+       struct kref ref;
+       struct em28xx *dev;
+
+       struct v4l2_device v4l2_dev;
+       struct v4l2_ctrl_handler ctrl_handler;
+       struct v4l2_clk *clk;
+
+       struct video_device *vdev;
+       struct video_device *vbi_dev;
+       struct video_device *radio_dev;
+
+       /* Videobuf2 */
+       struct vb2_queue vb_vidq;
+       struct vb2_queue vb_vbiq;
+       struct mutex vb_queue_lock;
+       struct mutex vb_vbi_queue_lock;
+
+       u8 vinmode;
+       u8 vinctl;
+
+       /* Camera specific fields */
+       int sensor_xres;
+       int sensor_yres;
+       int sensor_xtal;
+
+       int users;              /* user count for exclusive use */
+       int streaming_users;    /* number of actively streaming users */
+
+       u32 frequency;          /* selected tuner frequency */
+
+       struct em28xx_fmt *format;
+       v4l2_std_id norm;       /* selected tv norm */
+
+       /* Progressive/interlaced mode */
+       bool progressive;
+       int interlaced_fieldmode; /* 1=interlaced fields, 0=just top fields */
+       /* FIXME: everything else than interlaced_fieldmode=1 doesn't work */
+
+       /* Frame properties */
+       int width;              /* current frame width */
+       int height;             /* current frame height */
+       unsigned hscale;        /* horizontal scale factor (see datasheet) */
+       unsigned vscale;        /* vertical scale factor (see datasheet) */
+       unsigned int vbi_width;
+       unsigned int vbi_height; /* lines per field */
+
+       /* Capture state tracking */
+       int capture_type;
+       bool top_field;
+       int vbi_read;
+       unsigned int field_count;
+};
+
 struct em28xx_audio {
        char name[50];
        unsigned num_urb;
@@ -513,6 +568,10 @@ struct em28xx_audio {
 
        int users;
        spinlock_t slock;
+
+       /* Controls streaming */
+       struct work_struct wq_trigger;  /* trigger to start/stop audio */
+       atomic_t       stream_started;  /* stream should be running if true */
 };
 
 struct em28xx;
@@ -541,6 +600,12 @@ struct em28xx_i2c_bus {
 struct em28xx {
        struct kref ref;
 
+       /* Sub-module data */
+       struct em28xx_v4l2 *v4l2;
+       struct em28xx_dvb *dvb;
+       struct em28xx_audio adev;
+       struct em28xx_IR *ir;
+
        /* generic device properties */
        char name[30];          /* name (including minor) of the device */
        int model;              /* index in the device_data struct */
@@ -554,29 +619,9 @@ struct em28xx {
        unsigned int has_alsa_audio:1;
        unsigned int is_audio_only:1;
 
-       struct v4l2_device v4l2_dev;
-       struct v4l2_ctrl_handler ctrl_handler;
-       struct v4l2_clk *clk;
        struct em28xx_board board;
 
-       /* Webcam specific fields */
-       enum em28xx_sensor em28xx_sensor;
-       int sensor_xres, sensor_yres;
-       int sensor_xtal;
-
-       /* Progressive (non-interlaced) mode */
-       int progressive;
-
-       /* Vinmode/Vinctl used at the driver */
-       int vinmode, vinctl;
-
-       /* Controls audio streaming */
-       struct work_struct wq_trigger;  /* Trigger to start/stop audio for alsa module */
-       atomic_t       stream_started;  /* stream should be running if true */
-
-       struct em28xx_fmt *format;
-
-       struct em28xx_IR *ir;
+       enum em28xx_sensor em28xx_sensor;       /* camera specific */
 
        /* Some older em28xx chips needs a waiting time after writing */
        unsigned int wait_after_write;
@@ -588,8 +633,6 @@ struct em28xx {
        struct em28xx_audio_mode audio_mode;
 
        int tuner_type;         /* type of the tuner */
-       int tuner_addr;         /* tuner address */
-       int tda9887_conf;
 
        /* i2c i/o */
        struct i2c_adapter i2c_adap[NUM_I2C_BUSES];
@@ -602,52 +645,21 @@ struct em28xx {
        struct rt_mutex i2c_bus_lock;
 
        /* video for linux */
-       int users;              /* user count for exclusive use */
-       int streaming_users;    /* Number of actively streaming users */
-       struct video_device *vdev;      /* video for linux device struct */
-       v4l2_std_id norm;       /* selected tv norm */
-       int ctl_freq;           /* selected frequency */
        unsigned int ctl_input; /* selected input */
        unsigned int ctl_ainput;/* selected audio input */
        unsigned int ctl_aoutput;/* selected audio output */
        int mute;
        int volume;
-       /* frame properties */
-       int width;              /* current frame width */
-       int height;             /* current frame height */
-       unsigned hscale;        /* horizontal scale factor (see datasheet) */
-       unsigned vscale;        /* vertical scale factor (see datasheet) */
-       int interlaced;         /* 1=interlace fileds, 0=just top fileds */
-       unsigned int video_bytesread;   /* Number of bytes read */
 
        unsigned long hash;     /* eeprom hash - for boards with generic ID */
        unsigned long i2c_hash; /* i2c devicelist hash -
                                   for boards with generic ID */
 
-       struct em28xx_audio adev;
-
-       /* capture state tracking */
-       int capture_type;
-       unsigned char top_field:1;
-       int vbi_read;
-       unsigned int vbi_width;
-       unsigned int vbi_height; /* lines per field */
-
        struct work_struct         request_module_wk;
 
        /* locks */
        struct mutex lock;
        struct mutex ctrl_urb_lock;     /* protects urb_buf */
-       /* spinlock_t queue_lock; */
-       struct list_head inqueue, outqueue;
-       struct video_device *vbi_dev;
-       struct video_device *radio_dev;
-
-       /* Videobuf2 */
-       struct vb2_queue vb_vidq;
-       struct vb2_queue vb_vbiq;
-       struct mutex vb_queue_lock;
-       struct mutex vb_vbi_queue_lock;
 
        /* resources in use */
        unsigned int resources;
@@ -662,9 +674,6 @@ struct em28xx {
        struct em28xx_usb_ctl usb_ctl;
        spinlock_t slock;
 
-       unsigned int field_count;
-       unsigned int vbi_field_count;
-
        /* usb transfer */
        struct usb_device *udev;        /* the usb device */
        u8 ifnum;               /* number of the assigned usb interface */
@@ -708,8 +717,6 @@ struct em28xx {
        /* Snapshot button input device */
        char snapshot_button_path[30];  /* path of the input dev */
        struct input_dev *sbutton_input_dev;
-
-       struct em28xx_dvb *dvb;
 };
 
 #define kref_to_dev(d) container_of(d, struct em28xx, ref)
@@ -797,32 +804,4 @@ int em28xx_init_camera(struct em28xx *dev);
        printk(KERN_WARNING "%s: "fmt,\
                        dev->name , ##arg); } while (0)
 
-static inline int em28xx_compression_disable(struct em28xx *dev)
-{
-       /* side effect of disabling scaler and mixer */
-       return em28xx_write_reg(dev, EM28XX_R26_COMPR, 0x00);
-}
-
-/*FIXME: maxw should be dependent of alt mode */
-static inline unsigned int norm_maxw(struct em28xx *dev)
-{
-       if (dev->board.is_webcam)
-               return dev->sensor_xres;
-
-       if (dev->board.max_range_640_480)
-               return 640;
-
-       return 720;
-}
-
-static inline unsigned int norm_maxh(struct em28xx *dev)
-{
-       if (dev->board.is_webcam)
-               return dev->sensor_yres;
-
-       if (dev->board.max_range_640_480)
-               return 480;
-
-       return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
-}
 #endif
index 4f0c6d5..eed10d7 100644 (file)
@@ -50,6 +50,16 @@ config USB_GSPCA_CPIA1
          To compile this driver as a module, choose M here: the
          module will be called gspca_cpia1.
 
+config USB_GSPCA_DTCS033
+       tristate "DTCS033 (Scopium) USB Astro-Camera Driver"
+       depends on VIDEO_V4L2 && USB_GSPCA
+       help
+         Say Y here if you want support for the Scopium camera
+         for planetary astrophotography.
+
+         To compile this driver as a module, choose M here: the
+         module will be called gspca_dtcs033.
+
 config USB_GSPCA_ETOMS
        tristate "Etoms USB Camera Driver"
        depends on VIDEO_V4L2 && USB_GSPCA
index 5855131..f46975e 100644 (file)
@@ -2,6 +2,7 @@ obj-$(CONFIG_USB_GSPCA)          += gspca_main.o
 obj-$(CONFIG_USB_GSPCA_BENQ)     += gspca_benq.o
 obj-$(CONFIG_USB_GSPCA_CONEX)    += gspca_conex.o
 obj-$(CONFIG_USB_GSPCA_CPIA1)    += gspca_cpia1.o
+obj-$(CONFIG_USB_GSPCA_DTCS033)  += gspca_dtcs033.o
 obj-$(CONFIG_USB_GSPCA_ETOMS)    += gspca_etoms.o
 obj-$(CONFIG_USB_GSPCA_FINEPIX)  += gspca_finepix.o
 obj-$(CONFIG_USB_GSPCA_JEILINJ)  += gspca_jeilinj.o
@@ -48,6 +49,7 @@ gspca_main-objs     := gspca.o autogain_functions.o
 gspca_benq-objs     := benq.o
 gspca_conex-objs    := conex.o
 gspca_cpia1-objs    := cpia1.o
+gspca_dtcs033-objs  := dtcs033.o
 gspca_etoms-objs    := etoms.o
 gspca_finepix-objs  := finepix.o
 gspca_jeilinj-objs  := jeilinj.o
diff --git a/drivers/media/usb/gspca/dtcs033.c b/drivers/media/usb/gspca/dtcs033.c
new file mode 100644 (file)
index 0000000..96bfd4e
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * Subdriver for Scopium astro-camera (DTCS033, 0547:7303)
+ *
+ * Copyright (C) 2014 Robert Butora (robert.butora.fi@gmail.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
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define MODULE_NAME "dtcs033"
+#include "gspca.h"
+
+MODULE_AUTHOR("Robert Butora <robert.butora.fi@gmail.com>");
+MODULE_DESCRIPTION("Scopium DTCS033 astro-cam USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+struct dtcs033_usb_requests {
+       u8 bRequestType;
+       u8 bRequest;
+       u16 wValue;
+       u16 wIndex;
+       u16 wLength;
+};
+
+/* send a usb request */
+static void reg_rw(struct gspca_dev *gspca_dev,
+               u8 bRequestType, u8 bRequest,
+               u16 wValue, u16 wIndex, u16 wLength)
+{
+       struct usb_device *udev = gspca_dev->dev;
+       int ret;
+
+       if (gspca_dev->usb_err < 0)
+               return;
+
+       ret = usb_control_msg(udev,
+               usb_rcvctrlpipe(udev, 0),
+               bRequest,
+               bRequestType,
+               wValue, wIndex,
+               gspca_dev->usb_buf, wLength, 500);
+
+       if (ret < 0) {
+               gspca_dev->usb_err = ret;
+               pr_err("usb_control_msg error %d\n", ret);
+       }
+
+       return;
+}
+/* send several usb in/out requests */
+static int reg_reqs(struct gspca_dev *gspca_dev,
+                   const struct dtcs033_usb_requests *preqs, int n_reqs)
+{
+       int i = 0;
+       const struct dtcs033_usb_requests *preq;
+
+       while ((i < n_reqs) && (gspca_dev->usb_err >= 0)) {
+
+               preq = &preqs[i];
+
+               reg_rw(gspca_dev, preq->bRequestType, preq->bRequest,
+                       preq->wValue, preq->wIndex, preq->wLength);
+
+               if (gspca_dev->usb_err < 0) {
+
+                       PERR("usb error request no: %d / %d\n",
+                               i, n_reqs);
+               } else if (preq->bRequestType & USB_DIR_IN) {
+
+                       PDEBUG(D_STREAM,
+                       "USB IN (%d) returned[%d] %02X %02X %02X %s",
+                               i,
+                               preq->wLength,
+                               gspca_dev->usb_buf[0],
+                               gspca_dev->usb_buf[1],
+                               gspca_dev->usb_buf[2],
+                               preq->wLength > 3 ? "...\n" : "\n");
+               }
+
+               i++;
+       }
+       return gspca_dev->usb_err;
+}
+
+/* -- subdriver interface implementation -- */
+
+#define DT_COLS (640)
+static const struct v4l2_pix_format dtcs033_mode[] = {
+       /* raw Bayer patterned output */
+       {DT_COLS, 480, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+               .bytesperline = DT_COLS,
+               .sizeimage = DT_COLS*480,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+       },
+       /* this mode will demosaic the Bayer pattern */
+       {DT_COLS, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
+               .bytesperline = DT_COLS,
+               .sizeimage = DT_COLS*480,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+       }
+};
+
+/* config called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+               const struct usb_device_id *id)
+{
+       gspca_dev->cam.cam_mode = dtcs033_mode;
+       gspca_dev->cam.nmodes = ARRAY_SIZE(dtcs033_mode);
+
+       gspca_dev->cam.bulk = 1;
+       gspca_dev->cam.bulk_nurbs = 1;
+       gspca_dev->cam.bulk_size = DT_COLS*512;
+
+       return 0;
+}
+
+/* init called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+       return 0;
+}
+
+/* start stop the camera */
+static int  dtcs033_start(struct gspca_dev *gspca_dev);
+static void dtcs033_stopN(struct gspca_dev *gspca_dev);
+
+/* intercept camera image data */
+static void dtcs033_pkt_scan(struct gspca_dev *gspca_dev,
+                       u8 *data,  /* packet data */
+                       int len)   /* packet data length */
+{
+       /* drop incomplete frames */
+       if (len != DT_COLS*512) {
+               gspca_dev->last_packet_type = DISCARD_PACKET;
+               /* gspca.c: discard invalidates the whole frame. */
+               return;
+       }
+
+       /* forward complete frames */
+       gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+       gspca_frame_add(gspca_dev, INTER_PACKET,
+               data + 16*DT_COLS,
+               len  - 32*DT_COLS); /* skip first & last 16 lines */
+       gspca_frame_add(gspca_dev, LAST_PACKET,  NULL, 0);
+
+       return;
+}
+
+/* -- controls: exposure and gain -- */
+
+static void dtcs033_setexposure(struct gspca_dev *gspca_dev,
+                       s32 expo, s32 gain)
+{
+       /* gain [dB] encoding */
+       u16 sGain   = (u16)gain;
+       u16 gainVal = 224+(sGain-14)*(768-224)/(33-14);
+       u16 wIndex =  0x0100|(0x00FF&gainVal);
+       u16 wValue = (0xFF00&gainVal)>>8;
+
+       /* exposure time [msec] encoding */
+       u16 sXTime   = (u16)expo;
+       u16 xtimeVal = (524*(150-(sXTime-1)))/150;
+
+       const u8 bRequestType =
+               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+       const u8 bRequest = 0x18;
+
+       reg_rw(gspca_dev,
+               bRequestType, bRequest, wValue, wIndex, 0);
+       if (gspca_dev->usb_err < 0)
+               PERR("usb error in setexposure(gain) sequence.\n");
+
+       reg_rw(gspca_dev,
+               bRequestType, bRequest, (xtimeVal<<4), 0x6300, 0);
+       if (gspca_dev->usb_err < 0)
+               PERR("usb error in setexposure(time) sequence.\n");
+}
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;/* !! must be the first item */
+
+       /* exposure & gain controls */
+       struct {
+               struct v4l2_ctrl *exposure;
+               struct v4l2_ctrl *gain;
+       };
+};
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct gspca_dev *gspca_dev =
+       container_of(ctrl->handler,
+               struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       gspca_dev->usb_err = 0;
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_EXPOSURE:
+               dtcs033_setexposure(gspca_dev,
+                               ctrl->val, sd->gain->val);
+               break;
+       case V4L2_CID_GAIN:
+               dtcs033_setexposure(gspca_dev,
+                               sd->exposure->val, ctrl->val);
+               break;
+       }
+       return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
+
+static int dtcs033_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 2);
+       /*                               min max step default */
+       sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                               V4L2_CID_EXPOSURE,
+                               1,  150,  1,  75);/* [msec] */
+       sd->gain     = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                               V4L2_CID_GAIN,
+                               14,  33,  1,  24);/* [dB] */
+       if (hdl->error) {
+               PERR("Could not initialize controls: %d\n",
+                       hdl->error);
+               return hdl->error;
+       }
+
+       v4l2_ctrl_cluster(2, &sd->exposure);
+       return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name     = MODULE_NAME,
+       .config   = sd_config,
+       .init     = sd_init,
+       .start    = dtcs033_start,
+       .stopN    = dtcs033_stopN,
+       .pkt_scan = dtcs033_pkt_scan,
+       .init_controls = dtcs033_init_controls,
+};
+
+/* -- module initialisation -- */
+
+static const struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x0547, 0x7303)},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* device connect */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id,
+                       &sd_desc, sizeof(struct sd),
+                       THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name       = MODULE_NAME,
+       .id_table   = device_table,
+       .probe      = sd_probe,
+       .disconnect   = gspca_disconnect,
+#ifdef CONFIG_PM
+       .suspend      = gspca_suspend,
+       .resume       = gspca_resume,
+       .reset_resume = gspca_resume,
+#endif
+};
+module_usb_driver(sd_driver);
+
+
+/* ---------------------------------------------------------
+ USB requests to start/stop the camera [USB 2.0 spec Ch.9].
+
+ bRequestType :
+ 0x40 =  USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0xC0 =  USB_DIR_IN  | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+*/
+static const struct dtcs033_usb_requests dtcs033_start_reqs[] = {
+/* -- bRequest,wValue,wIndex,wLength */
+{ 0x40, 0x01, 0x0001, 0x000F, 0x0000 },
+{ 0x40, 0x01, 0x0000, 0x000F, 0x0000 },
+{ 0x40, 0x01, 0x0001, 0x000F, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x7F00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1001, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0004, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x7F01, 0x0000 },
+{ 0x40, 0x18, 0x30E0, 0x0009, 0x0000 },
+{ 0x40, 0x18, 0x0500, 0x012C, 0x0000 },
+{ 0x40, 0x18, 0x0380, 0x0200, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x035C, 0x0000 },
+{ 0x40, 0x18, 0x05C0, 0x0438, 0x0000 },
+{ 0x40, 0x18, 0x0440, 0x0500, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0668, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0700, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0800, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0900, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0A00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0B00, 0x0000 },
+{ 0x40, 0x18, 0x30E0, 0x6009, 0x0000 },
+{ 0x40, 0x18, 0x0500, 0x612C, 0x0000 },
+{ 0x40, 0x18, 0x2090, 0x6274, 0x0000 },
+{ 0x40, 0x18, 0x05C0, 0x6338, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6400, 0x0000 },
+{ 0x40, 0x18, 0x05C0, 0x6538, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6600, 0x0000 },
+{ 0x40, 0x18, 0x0680, 0x6744, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6800, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6900, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6A00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6B00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6C00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6D00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6E00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x808C, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0x8101, 0x0000 },
+{ 0x40, 0x18, 0x30E0, 0x8200, 0x0000 },
+{ 0x40, 0x18, 0x0810, 0x832C, 0x0000 },
+{ 0x40, 0x18, 0x0680, 0x842B, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x8500, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x8600, 0x0000 },
+{ 0x40, 0x18, 0x0280, 0x8715, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x880C, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0x8901, 0x0000 },
+{ 0x40, 0x18, 0x30E0, 0x8A00, 0x0000 },
+{ 0x40, 0x18, 0x0810, 0x8B2C, 0x0000 },
+{ 0x40, 0x18, 0x0680, 0x8C2B, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x8D00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x8E00, 0x0000 },
+{ 0x40, 0x18, 0x0280, 0x8F15, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0xD040, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0xD100, 0x0000 },
+{ 0x40, 0x18, 0x00B0, 0xD20A, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0xD300, 0x0000 },
+{ 0x40, 0x18, 0x30E2, 0xD40D, 0x0000 },
+{ 0x40, 0x18, 0x0001, 0xD5C0, 0x0000 },
+{ 0x40, 0x18, 0x00A0, 0xD60A, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0xD700, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x7F00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1501, 0x0000 },
+{ 0x40, 0x18, 0x0001, 0x01FF, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0200, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0304, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1101, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1201, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1300, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1400, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1601, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1800, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1900, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1A00, 0x0000 },
+{ 0x40, 0x18, 0x2000, 0x1B00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1C00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x2100, 0x0000 },
+{ 0x40, 0x18, 0x00C0, 0x228E, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x3001, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0x3101, 0x0000 },
+{ 0x40, 0x18, 0x0008, 0x3301, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x3400, 0x0000 },
+{ 0x40, 0x18, 0x0012, 0x3549, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x3620, 0x0000 },
+{ 0x40, 0x18, 0x0001, 0x3700, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x4000, 0x0000 },
+{ 0x40, 0x18, 0xFFFF, 0x41FF, 0x0000 },
+{ 0x40, 0x18, 0xFFFF, 0x42FF, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x500F, 0x0000 },
+{ 0x40, 0x18, 0x2272, 0x5108, 0x0000 },
+{ 0x40, 0x18, 0x2272, 0x5208, 0x0000 },
+{ 0x40, 0x18, 0xFFFF, 0x53FF, 0x0000 },
+{ 0x40, 0x18, 0xFFFF, 0x54FF, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6000, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6102, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0x6214, 0x0000 },
+{ 0x40, 0x18, 0x0C80, 0x6300, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6401, 0x0000 },
+{ 0x40, 0x18, 0x0680, 0x6551, 0x0000 },
+{ 0x40, 0x18, 0xFFFF, 0x66FF, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6702, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0x6800, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6900, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6A00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6B00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6C00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6D01, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6E00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x6F00, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x7000, 0x0000 },
+{ 0x40, 0x18, 0x0001, 0x7118, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x2001, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1101, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1301, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1300, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1501, 0x0000 },
+{ 0xC0, 0x11, 0x0000, 0x24C0, 0x0003 },
+{ 0x40, 0x18, 0x0000, 0x3000, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x3620, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x1501, 0x0000 },
+{ 0x40, 0x18, 0x0010, 0x6300, 0x0000 },
+{ 0x40, 0x18, 0x0002, 0x01F0, 0x0000 },
+{ 0x40, 0x01, 0x0003, 0x000F, 0x0000 }
+};
+
+static const struct dtcs033_usb_requests dtcs033_stop_reqs[] = {
+/* -- bRequest,wValue,wIndex,wLength */
+{ 0x40, 0x01, 0x0001, 0x000F, 0x0000 },
+{ 0x40, 0x01, 0x0000, 0x000F, 0x0000 },
+{ 0x40, 0x18, 0x0000, 0x0003, 0x0000 }
+};
+static int dtcs033_start(struct gspca_dev *gspca_dev)
+{
+       return reg_reqs(gspca_dev, dtcs033_start_reqs,
+               ARRAY_SIZE(dtcs033_start_reqs));
+}
+
+static void dtcs033_stopN(struct gspca_dev *gspca_dev)
+{
+       reg_reqs(gspca_dev, dtcs033_stop_reqs,
+               ARRAY_SIZE(dtcs033_stop_reqs));
+       return;
+}
index 2edda6b..a785828 100644 (file)
@@ -35,32 +35,34 @@ static u8 dat_hvflip5[] = {0x8c, 0xa1, 0x03};
 static u8 dat_hvflip6[] = {0x90, 0x00, 0x06};
 
 static struct idxdata tbl_middle_hvflip_low[] = {
-       {0x33, "\x90\x00\x06"},
-       {6, "\xff\xff\xff"},
-       {0x33, "\x90\x00\x06"},
-       {6, "\xff\xff\xff"},
-       {0x33, "\x90\x00\x06"},
-       {6, "\xff\xff\xff"},
-       {0x33, "\x90\x00\x06"},
-       {6, "\xff\xff\xff"},
+       {0x33, {0x90, 0x00, 0x06}},
+       {6, {0xff, 0xff, 0xff}},
+       {0x33, {0x90, 0x00, 0x06}},
+       {6, {0xff, 0xff, 0xff}},
+       {0x33, {0x90, 0x00, 0x06}},
+       {6, {0xff, 0xff, 0xff}},
+       {0x33, {0x90, 0x00, 0x06}},
+       {6, {0xff, 0xff, 0xff}},
 };
 
 static struct idxdata tbl_middle_hvflip_big[] = {
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa1\x20"},
-       {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"},
-       {102, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x20"},
-       {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x01}},
+       {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x00}},
+       {102, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x02}},
+       {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x72}},
+       {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x01}},
 };
 
 static struct idxdata tbl_end_hvflip[] = {
-       {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"},
-       {6, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"},
-       {6, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"},
-       {6, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"},
+       {0x33, {0x8c, 0xa1, 0x02}}, {0x33, {0x90, 0x00, 0x1f}},
+       {6, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x02}}, {0x33, {0x90, 0x00, 0x1f}},
+       {6, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x02}}, {0x33, {0x90, 0x00, 0x1f}},
+       {6, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x02}}, {0x33, {0x90, 0x00, 0x1f}},
 };
 
 static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 };
@@ -82,197 +84,267 @@ static struct validx tbl_common_0B[] = {
 };
 
 static struct idxdata tbl_common_3B[] = {
-       {0x33, "\x86\x25\x01"}, {0x33, "\x86\x25\x00"},
-       {2, "\xff\xff\xff"},
-       {0x30, "\x1a\x0a\xcc"}, {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"},
-       {6, "\xff\xff\xff"}, /* 12 */
-       {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
-       {2, "\xff\xff\xff"}, /* - */
-       {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\x22\x23"},
-       {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0f"}, {0x33, "\x90\x00\x0d"},
-       {0x33, "\x8c\xa2\x10"}, {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x11"},
-       {0x33, "\x90\x00\x07"}, {0x33, "\xf4\x03\x1d"}, {0x35, "\xa2\x00\xe2"},
-       {0x33, "\x8c\xab\x05"}, {0x33, "\x90\x00\x01"}, {0x32, "\x6e\x00\x86"},
-       {0x32, "\x70\x0f\xaa"}, {0x32, "\x72\x0f\xe4"}, {0x33, "\x8c\xa3\x4a"},
-       {0x33, "\x90\x00\x5a"}, {0x33, "\x8c\xa3\x4b"}, {0x33, "\x90\x00\xa6"},
-       {0x33, "\x8c\xa3\x61"}, {0x33, "\x90\x00\xc8"}, {0x33, "\x8c\xa3\x62"},
-       {0x33, "\x90\x00\xe1"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
-       {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
-       {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
-       {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"},
-       {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"},
-       {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"},
-       {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"},
-       {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"},
-       {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"},
-       {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"},
-       {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"},
-       {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"},
-       {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"},
-       {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"},
-       {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"},
-       {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"},
-       {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"},
-       {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"},
-       {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"},
-       {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"},
-       {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"},
-       {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"},
-       {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"},
-       {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"},
-       {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"},
-       {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"},
-       {0x33, "\x78\x00\x00"},
-       {2, "\xff\xff\xff"},
-       {0x35, "\xb8\x1f\x20"}, {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x10"},
-       {0x33, "\x8c\xa2\x07"}, {0x33, "\x90\x00\x08"}, {0x33, "\x8c\xa2\x42"},
-       {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x4a"}, {0x33, "\x90\x00\x8c"},
-       {0x35, "\xba\xfa\x08"}, {0x33, "\x8c\xa2\x02"}, {0x33, "\x90\x00\x22"},
-       {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, {0x33, "\x8c\xa4\x04"},
-       {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, {0x33, "\x90\x00\x00"},
-       {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0c"},
-       {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, {0x33, "\x90\x00\x04"},
-       {0x33, "\x8c\xa2\x14"}, {0x33, "\x90\x00\x20"}, {0x33, "\x8c\xa1\x03"},
-       {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"},
-       {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x25"},
-       {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x39"}, {0x33, "\x90\x21\x11"},
-       {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x47"},
-       {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x02\x84"},
-       {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x01\xe2"}, {0x33, "\x8c\x27\x07"},
-       {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x09"}, {0x33, "\x90\x04\xb0"},
-       {0x33, "\x8c\x27\x0d"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x0f"},
-       {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x11"}, {0x33, "\x90\x04\xbd"},
-       {0x33, "\x8c\x27\x13"}, {0x33, "\x90\x06\x4d"}, {0x33, "\x8c\x27\x15"},
-       {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"},
-       {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, {0x33, "\x8c\x27\x1b"},
-       {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x1d"}, {0x33, "\x90\x01\x02"},
-       {0x33, "\x8c\x27\x1f"}, {0x33, "\x90\x02\x79"}, {0x33, "\x8c\x27\x21"},
-       {0x33, "\x90\x01\x55"}, {0x33, "\x8c\x27\x23"}, {0x33, "\x90\x02\x85"},
-       {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x27"},
-       {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x29"}, {0x33, "\x90\x20\x20"},
-       {0x33, "\x8c\x27\x2b"}, {0x33, "\x90\x10\x20"}, {0x33, "\x8c\x27\x2d"},
-       {0x33, "\x90\x20\x07"}, {0x33, "\x8c\x27\x2f"}, {0x33, "\x90\x00\x04"},
-       {0x33, "\x8c\x27\x31"}, {0x33, "\x90\x00\x04"}, {0x33, "\x8c\x27\x33"},
-       {0x33, "\x90\x04\xbb"}, {0x33, "\x8c\x27\x35"}, {0x33, "\x90\x06\x4b"},
-       {0x33, "\x8c\x27\x37"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x39"},
-       {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3b"}, {0x33, "\x90\x00\x24"},
-       {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x41"},
-       {0x33, "\x90\x01\x69"}, {0x33, "\x8c\x27\x45"}, {0x33, "\x90\x04\xed"},
-       {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x51"},
-       {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x53"}, {0x33, "\x90\x03\x20"},
-       {0x33, "\x8c\x27\x55"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x57"},
-       {0x33, "\x90\x02\x58"}, {0x33, "\x8c\x27\x5f"}, {0x33, "\x90\x00\x00"},
-       {0x33, "\x8c\x27\x61"}, {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x63"},
-       {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x65"}, {0x33, "\x90\x04\xb0"},
-       {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa1"}, {0x33, "\x8c\xa4\x08"},
-       {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"},
-       {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"},
-       {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\xa1"},
-       {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc1"}, {0x33, "\x8c\x24\x15"},
-       {0x33, "\x90\x00\x6a"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\x80"},
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
-       {2, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
-       {3, "\xff\xff\xff"},
+       {0x33, {0x86, 0x25, 0x01}}, {0x33, {0x86, 0x25, 0x00}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x30, {0x1a, 0x0a, 0xcc}}, {0x32, {0x02, 0x00, 0x08}},
+       {0x33, {0xf4, 0x03, 0x1d}},
+       {6, {0xff, 0xff, 0xff}}, /* 12 */
+       {0x34, {0x1e, 0x8f, 0x09}}, {0x34, {0x1c, 0x01, 0x28}},
+       {0x34, {0x1e, 0x8f, 0x09}},
+       {2, {0xff, 0xff, 0xff}}, /* - */
+       {0x34, {0x1e, 0x8f, 0x09}}, {0x32, {0x14, 0x06, 0xe6}},
+       {0x33, {0x8c, 0x22, 0x23}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0xa2, 0x0f}}, {0x33, {0x90, 0x00, 0x0d}},
+       {0x33, {0x8c, 0xa2, 0x10}}, {0x33, {0x90, 0x00, 0x0b}},
+       {0x33, {0x8c, 0xa2, 0x11}}, {0x33, {0x90, 0x00, 0x07}},
+       {0x33, {0xf4, 0x03, 0x1d}}, {0x35, {0xa2, 0x00, 0xe2}},
+       {0x33, {0x8c, 0xab, 0x05}}, {0x33, {0x90, 0x00, 0x01}},
+       {0x32, {0x6e, 0x00, 0x86}}, {0x32, {0x70, 0x0f, 0xaa}},
+       {0x32, {0x72, 0x0f, 0xe4}}, {0x33, {0x8c, 0xa3, 0x4a}},
+       {0x33, {0x90, 0x00, 0x5a}}, {0x33, {0x8c, 0xa3, 0x4b}},
+       {0x33, {0x90, 0x00, 0xa6}}, {0x33, {0x8c, 0xa3, 0x61}},
+       {0x33, {0x90, 0x00, 0xc8}}, {0x33, {0x8c, 0xa3, 0x62}},
+       {0x33, {0x90, 0x00, 0xe1}}, {0x34, {0xce, 0x01, 0xa8}},
+       {0x34, {0xd0, 0x66, 0x33}}, {0x34, {0xd2, 0x31, 0x9a}},
+       {0x34, {0xd4, 0x94, 0x63}}, {0x34, {0xd6, 0x4b, 0x25}},
+       {0x34, {0xd8, 0x26, 0x70}}, {0x34, {0xda, 0x72, 0x4c}},
+       {0x34, {0xdc, 0xff, 0x04}}, {0x34, {0xde, 0x01, 0x5b}},
+       {0x34, {0xe6, 0x01, 0x13}}, {0x34, {0xee, 0x0b, 0xf0}},
+       {0x34, {0xf6, 0x0b, 0xa4}}, {0x35, {0x00, 0xf6, 0xe7}},
+       {0x35, {0x08, 0x0d, 0xfd}}, {0x35, {0x10, 0x25, 0x63}},
+       {0x35, {0x18, 0x35, 0x6c}}, {0x35, {0x20, 0x42, 0x7e}},
+       {0x35, {0x28, 0x19, 0x44}}, {0x35, {0x30, 0x39, 0xd4}},
+       {0x35, {0x38, 0xf5, 0xa8}}, {0x35, {0x4c, 0x07, 0x90}},
+       {0x35, {0x44, 0x07, 0xb8}}, {0x35, {0x5c, 0x06, 0x88}},
+       {0x35, {0x54, 0x07, 0xff}}, {0x34, {0xe0, 0x01, 0x52}},
+       {0x34, {0xe8, 0x00, 0xcc}}, {0x34, {0xf0, 0x0d, 0x83}},
+       {0x34, {0xf8, 0x0c, 0xb3}}, {0x35, {0x02, 0xfe, 0xba}},
+       {0x35, {0x0a, 0x04, 0xe0}}, {0x35, {0x12, 0x1c, 0x63}},
+       {0x35, {0x1a, 0x2b, 0x5a}}, {0x35, {0x22, 0x32, 0x5e}},
+       {0x35, {0x2a, 0x0d, 0x28}}, {0x35, {0x32, 0x2c, 0x02}},
+       {0x35, {0x3a, 0xf4, 0xfa}}, {0x35, {0x4e, 0x07, 0xef}},
+       {0x35, {0x46, 0x07, 0x88}}, {0x35, {0x5e, 0x07, 0xc1}},
+       {0x35, {0x56, 0x04, 0x64}}, {0x34, {0xe4, 0x01, 0x15}},
+       {0x34, {0xec, 0x00, 0x82}}, {0x34, {0xf4, 0x0c, 0xce}},
+       {0x34, {0xfc, 0x0c, 0xba}}, {0x35, {0x06, 0x1f, 0x02}},
+       {0x35, {0x0e, 0x02, 0xe3}}, {0x35, {0x16, 0x1a, 0x50}},
+       {0x35, {0x1e, 0x24, 0x39}}, {0x35, {0x26, 0x23, 0x4c}},
+       {0x35, {0x2e, 0xf9, 0x1b}}, {0x35, {0x36, 0x23, 0x19}},
+       {0x35, {0x3e, 0x12, 0x08}}, {0x35, {0x52, 0x07, 0x22}},
+       {0x35, {0x4a, 0x03, 0xd3}}, {0x35, {0x62, 0x06, 0x54}},
+       {0x35, {0x5a, 0x04, 0x5d}}, {0x34, {0xe2, 0x01, 0x04}},
+       {0x34, {0xea, 0x00, 0xa0}}, {0x34, {0xf2, 0x0c, 0xbc}},
+       {0x34, {0xfa, 0x0c, 0x5b}}, {0x35, {0x04, 0x17, 0xf2}},
+       {0x35, {0x0c, 0x02, 0x08}}, {0x35, {0x14, 0x28, 0x43}},
+       {0x35, {0x1c, 0x28, 0x62}}, {0x35, {0x24, 0x2b, 0x60}},
+       {0x35, {0x2c, 0x07, 0x33}}, {0x35, {0x34, 0x1f, 0xb0}},
+       {0x35, {0x3c, 0xed, 0xcd}}, {0x35, {0x50, 0x00, 0x06}},
+       {0x35, {0x48, 0x07, 0xff}}, {0x35, {0x60, 0x05, 0x89}},
+       {0x35, {0x58, 0x07, 0xff}}, {0x35, {0x40, 0x00, 0xa0}},
+       {0x35, {0x42, 0x00, 0x00}}, {0x32, {0x10, 0x01, 0xfc}},
+       {0x33, {0x8c, 0xa1, 0x18}}, {0x33, {0x90, 0x00, 0x3c}},
+       {0x33, {0x78, 0x00, 0x00}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x35, {0xb8, 0x1f, 0x20}}, {0x33, {0x8c, 0xa2, 0x06}},
+       {0x33, {0x90, 0x00, 0x10}}, {0x33, {0x8c, 0xa2, 0x07}},
+       {0x33, {0x90, 0x00, 0x08}}, {0x33, {0x8c, 0xa2, 0x42}},
+       {0x33, {0x90, 0x00, 0x0b}}, {0x33, {0x8c, 0xa2, 0x4a}},
+       {0x33, {0x90, 0x00, 0x8c}}, {0x35, {0xba, 0xfa, 0x08}},
+       {0x33, {0x8c, 0xa2, 0x02}}, {0x33, {0x90, 0x00, 0x22}},
+       {0x33, {0x8c, 0xa2, 0x03}}, {0x33, {0x90, 0x00, 0xbb}},
+       {0x33, {0x8c, 0xa4, 0x04}}, {0x33, {0x90, 0x00, 0x80}},
+       {0x33, {0x8c, 0xa7, 0x9d}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0xa7, 0x9e}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0xa2, 0x0c}}, {0x33, {0x90, 0x00, 0x17}},
+       {0x33, {0x8c, 0xa2, 0x15}}, {0x33, {0x90, 0x00, 0x04}},
+       {0x33, {0x8c, 0xa2, 0x14}}, {0x33, {0x90, 0x00, 0x20}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0x27, 0x17}}, {0x33, {0x90, 0x21, 0x11}},
+       {0x33, {0x8c, 0x27, 0x1b}}, {0x33, {0x90, 0x02, 0x4f}},
+       {0x33, {0x8c, 0x27, 0x25}}, {0x33, {0x90, 0x06, 0x0f}},
+       {0x33, {0x8c, 0x27, 0x39}}, {0x33, {0x90, 0x21, 0x11}},
+       {0x33, {0x8c, 0x27, 0x3d}}, {0x33, {0x90, 0x01, 0x20}},
+       {0x33, {0x8c, 0x27, 0x47}}, {0x33, {0x90, 0x09, 0x4c}},
+       {0x33, {0x8c, 0x27, 0x03}}, {0x33, {0x90, 0x02, 0x84}},
+       {0x33, {0x8c, 0x27, 0x05}}, {0x33, {0x90, 0x01, 0xe2}},
+       {0x33, {0x8c, 0x27, 0x07}}, {0x33, {0x90, 0x06, 0x40}},
+       {0x33, {0x8c, 0x27, 0x09}}, {0x33, {0x90, 0x04, 0xb0}},
+       {0x33, {0x8c, 0x27, 0x0d}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0x27, 0x0f}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0x27, 0x11}}, {0x33, {0x90, 0x04, 0xbd}},
+       {0x33, {0x8c, 0x27, 0x13}}, {0x33, {0x90, 0x06, 0x4d}},
+       {0x33, {0x8c, 0x27, 0x15}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0x27, 0x17}}, {0x33, {0x90, 0x21, 0x11}},
+       {0x33, {0x8c, 0x27, 0x19}}, {0x33, {0x90, 0x04, 0x6c}},
+       {0x33, {0x8c, 0x27, 0x1b}}, {0x33, {0x90, 0x02, 0x4f}},
+       {0x33, {0x8c, 0x27, 0x1d}}, {0x33, {0x90, 0x01, 0x02}},
+       {0x33, {0x8c, 0x27, 0x1f}}, {0x33, {0x90, 0x02, 0x79}},
+       {0x33, {0x8c, 0x27, 0x21}}, {0x33, {0x90, 0x01, 0x55}},
+       {0x33, {0x8c, 0x27, 0x23}}, {0x33, {0x90, 0x02, 0x85}},
+       {0x33, {0x8c, 0x27, 0x25}}, {0x33, {0x90, 0x06, 0x0f}},
+       {0x33, {0x8c, 0x27, 0x27}}, {0x33, {0x90, 0x20, 0x20}},
+       {0x33, {0x8c, 0x27, 0x29}}, {0x33, {0x90, 0x20, 0x20}},
+       {0x33, {0x8c, 0x27, 0x2b}}, {0x33, {0x90, 0x10, 0x20}},
+       {0x33, {0x8c, 0x27, 0x2d}}, {0x33, {0x90, 0x20, 0x07}},
+       {0x33, {0x8c, 0x27, 0x2f}}, {0x33, {0x90, 0x00, 0x04}},
+       {0x33, {0x8c, 0x27, 0x31}}, {0x33, {0x90, 0x00, 0x04}},
+       {0x33, {0x8c, 0x27, 0x33}}, {0x33, {0x90, 0x04, 0xbb}},
+       {0x33, {0x8c, 0x27, 0x35}}, {0x33, {0x90, 0x06, 0x4b}},
+       {0x33, {0x8c, 0x27, 0x37}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0x27, 0x39}}, {0x33, {0x90, 0x21, 0x11}},
+       {0x33, {0x8c, 0x27, 0x3b}}, {0x33, {0x90, 0x00, 0x24}},
+       {0x33, {0x8c, 0x27, 0x3d}}, {0x33, {0x90, 0x01, 0x20}},
+       {0x33, {0x8c, 0x27, 0x41}}, {0x33, {0x90, 0x01, 0x69}},
+       {0x33, {0x8c, 0x27, 0x45}}, {0x33, {0x90, 0x04, 0xed}},
+       {0x33, {0x8c, 0x27, 0x47}}, {0x33, {0x90, 0x09, 0x4c}},
+       {0x33, {0x8c, 0x27, 0x51}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0x27, 0x53}}, {0x33, {0x90, 0x03, 0x20}},
+       {0x33, {0x8c, 0x27, 0x55}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0x27, 0x57}}, {0x33, {0x90, 0x02, 0x58}},
+       {0x33, {0x8c, 0x27, 0x5f}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0x27, 0x61}}, {0x33, {0x90, 0x06, 0x40}},
+       {0x33, {0x8c, 0x27, 0x63}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0x27, 0x65}}, {0x33, {0x90, 0x04, 0xb0}},
+       {0x33, {0x8c, 0x22, 0x2e}}, {0x33, {0x90, 0x00, 0xa1}},
+       {0x33, {0x8c, 0xa4, 0x08}}, {0x33, {0x90, 0x00, 0x1f}},
+       {0x33, {0x8c, 0xa4, 0x09}}, {0x33, {0x90, 0x00, 0x21}},
+       {0x33, {0x8c, 0xa4, 0x0a}}, {0x33, {0x90, 0x00, 0x25}},
+       {0x33, {0x8c, 0xa4, 0x0b}}, {0x33, {0x90, 0x00, 0x27}},
+       {0x33, {0x8c, 0x24, 0x11}}, {0x33, {0x90, 0x00, 0xa1}},
+       {0x33, {0x8c, 0x24, 0x13}}, {0x33, {0x90, 0x00, 0xc1}},
+       {0x33, {0x8c, 0x24, 0x15}}, {0x33, {0x90, 0x00, 0x6a}},
+       {0x33, {0x8c, 0x24, 0x17}}, {0x33, {0x90, 0x00, 0x80}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
+       {3, {0xff, 0xff, 0xff}},
 };
 
 static struct idxdata tbl_init_post_alt_low1[] = {
-       {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"},
-       {0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"},
-       {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"},
-       {0x33, "\x90\x00\x1d"}, {0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x20"},
-       {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\x81"}, {0x33, "\x8c\x24\x13"},
-       {0x33, "\x90\x00\x9b"},
+       {0x33, {0x8c, 0x27, 0x15}}, {0x33, {0x90, 0x00, 0x25}},
+       {0x33, {0x8c, 0x22, 0x2e}}, {0x33, {0x90, 0x00, 0x81}},
+       {0x33, {0x8c, 0xa4, 0x08}}, {0x33, {0x90, 0x00, 0x17}},
+       {0x33, {0x8c, 0xa4, 0x09}}, {0x33, {0x90, 0x00, 0x1a}},
+       {0x33, {0x8c, 0xa4, 0x0a}}, {0x33, {0x90, 0x00, 0x1d}},
+       {0x33, {0x8c, 0xa4, 0x0b}}, {0x33, {0x90, 0x00, 0x20}},
+       {0x33, {0x8c, 0x24, 0x11}}, {0x33, {0x90, 0x00, 0x81}},
+       {0x33, {0x8c, 0x24, 0x13}}, {0x33, {0x90, 0x00, 0x9b}},
 };
 
 static struct idxdata tbl_init_post_alt_low2[] = {
-       {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"},
-       {0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
-       {2, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
-       {2, "\xff\xff\xff"},
+       {0x33, {0x8c, 0x27, 0x03}}, {0x33, {0x90, 0x03, 0x24}},
+       {0x33, {0x8c, 0x27, 0x05}}, {0x33, {0x90, 0x02, 0x58}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
+       {2, {0xff, 0xff, 0xff}},
 };
 
 static struct idxdata tbl_init_post_alt_low3[] = {
-       {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
-       {2, "\xff\xff\xff"},
-       {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"},
-       {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"},
-       {0x33, "\x2e\x01\x00"}, {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"},
-       {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x95"}, {0x33, "\x90\x01\x00"},
-       {2, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"},
-       {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
-       {2, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"},
-       {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"},
-       {2, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
-       {2, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
-       {2, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
-       {2, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
+       {0x34, {0x1e, 0x8f, 0x09}}, {0x34, {0x1c, 0x01, 0x28}},
+       {0x34, {0x1e, 0x8f, 0x09}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x34, {0x1e, 0x8f, 0x09}}, {0x32, {0x14, 0x06, 0xe6}},
+       {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x01}},
+       {0x33, {0x2e, 0x01, 0x00}}, {0x34, {0x04, 0x00, 0x2a}},
+       {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0x27, 0x95}}, {0x33, {0x90, 0x01, 0x00}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x72}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x02}},
+       {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x01}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x01}},
+       {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x00}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
 };
 
 static struct idxdata tbl_init_post_alt_big[] = {
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
-       {2, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
-       {2, "\xff\xff\xff"},
-       {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
-       {2, "\xff\xff\xff"},
-       {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x03"},
-       {0x33, "\x90\x00\x05"},
-       {2, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
-       {2, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
-       {2, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, {0x33, "\x8c\xa1\x20"},
-       {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x30"}, {0x33, "\x90\x00\x03"},
-       {0x33, "\x8c\xa1\x31"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x32"},
-       {0x33, "\x90\x00\x03"}, {0x33, "\x8c\xa1\x34"}, {0x33, "\x90\x00\x03"},
-       {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x2e\x01\x00"},
-       {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
-       {0x33, "\x8c\x27\x97"}, {0x33, "\x90\x01\x00"},
-       {51, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"},
-       {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"},
-       {51, "\xff\xff\xff"},
-       {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"},
-       {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
-       {51, "\xff\xff\xff"},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x34, {0x1e, 0x8f, 0x09}}, {0x34, {0x1c, 0x01, 0x28}},
+       {0x34, {0x1e, 0x8f, 0x09}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x34, {0x1e, 0x8f, 0x09}}, {0x32, {0x14, 0x06, 0xe6}},
+       {0x33, {0x8c, 0xa1, 0x03}},
+       {0x33, {0x90, 0x00, 0x05}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}},
+       {2, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}},
+       {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x72}},
+       {0x33, {0x8c, 0xa1, 0x30}}, {0x33, {0x90, 0x00, 0x03}},
+       {0x33, {0x8c, 0xa1, 0x31}}, {0x33, {0x90, 0x00, 0x02}},
+       {0x33, {0x8c, 0xa1, 0x32}}, {0x33, {0x90, 0x00, 0x03}},
+       {0x33, {0x8c, 0xa1, 0x34}}, {0x33, {0x90, 0x00, 0x03}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x02}},
+       {0x33, {0x2e, 0x01, 0x00}}, {0x34, {0x04, 0x00, 0x2a}},
+       {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x01}},
+       {0x33, {0x8c, 0x27, 0x97}}, {0x33, {0x90, 0x01, 0x00}},
+       {51, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x00}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x01}},
+       {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x00}},
+       {51, {0xff, 0xff, 0xff}},
+       {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x72}},
+       {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x02}},
+       {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x01}},
+       {51, {0xff, 0xff, 0xff}},
 };
 
 static struct idxdata tbl_init_post_alt_3B[] = {
-       {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
-       {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
-       {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
-       {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"},
-       {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"},
-       {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"},
-       {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"},
-       {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"},
-       {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"},
-       {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"},
-       {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"},
-       {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"},
-       {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"},
-       {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"},
-       {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"},
-       {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"},
-       {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"},
-       {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"},
-       {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"},
-       {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"},
-       {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"},
-       {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"},
-       {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"},
-       {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"},
-       {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"},
-       {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"},
+       {0x32, {0x10, 0x01, 0xf8}}, {0x34, {0xce, 0x01, 0xa8}},
+       {0x34, {0xd0, 0x66, 0x33}}, {0x34, {0xd2, 0x31, 0x9a}},
+       {0x34, {0xd4, 0x94, 0x63}}, {0x34, {0xd6, 0x4b, 0x25}},
+       {0x34, {0xd8, 0x26, 0x70}}, {0x34, {0xda, 0x72, 0x4c}},
+       {0x34, {0xdc, 0xff, 0x04}}, {0x34, {0xde, 0x01, 0x5b}},
+       {0x34, {0xe6, 0x01, 0x13}}, {0x34, {0xee, 0x0b, 0xf0}},
+       {0x34, {0xf6, 0x0b, 0xa4}}, {0x35, {0x00, 0xf6, 0xe7}},
+       {0x35, {0x08, 0x0d, 0xfd}}, {0x35, {0x10, 0x25, 0x63}},
+       {0x35, {0x18, 0x35, 0x6c}}, {0x35, {0x20, 0x42, 0x7e}},
+       {0x35, {0x28, 0x19, 0x44}}, {0x35, {0x30, 0x39, 0xd4}},
+       {0x35, {0x38, 0xf5, 0xa8}}, {0x35, {0x4c, 0x07, 0x90}},
+       {0x35, {0x44, 0x07, 0xb8}}, {0x35, {0x5c, 0x06, 0x88}},
+       {0x35, {0x54, 0x07, 0xff}}, {0x34, {0xe0, 0x01, 0x52}},
+       {0x34, {0xe8, 0x00, 0xcc}}, {0x34, {0xf0, 0x0d, 0x83}},
+       {0x34, {0xf8, 0x0c, 0xb3}}, {0x35, {0x02, 0xfe, 0xba}},
+       {0x35, {0x0a, 0x04, 0xe0}}, {0x35, {0x12, 0x1c, 0x63}},
+       {0x35, {0x1a, 0x2b, 0x5a}}, {0x35, {0x22, 0x32, 0x5e}},
+       {0x35, {0x2a, 0x0d, 0x28}}, {0x35, {0x32, 0x2c, 0x02}},
+       {0x35, {0x3a, 0xf4, 0xfa}}, {0x35, {0x4e, 0x07, 0xef}},
+       {0x35, {0x46, 0x07, 0x88}}, {0x35, {0x5e, 0x07, 0xc1}},
+       {0x35, {0x56, 0x04, 0x64}}, {0x34, {0xe4, 0x01, 0x15}},
+       {0x34, {0xec, 0x00, 0x82}}, {0x34, {0xf4, 0x0c, 0xce}},
+       {0x34, {0xfc, 0x0c, 0xba}}, {0x35, {0x06, 0x1f, 0x02}},
+       {0x35, {0x0e, 0x02, 0xe3}}, {0x35, {0x16, 0x1a, 0x50}},
+       {0x35, {0x1e, 0x24, 0x39}}, {0x35, {0x26, 0x23, 0x4c}},
+       {0x35, {0x2e, 0xf9, 0x1b}}, {0x35, {0x36, 0x23, 0x19}},
+       {0x35, {0x3e, 0x12, 0x08}}, {0x35, {0x52, 0x07, 0x22}},
+       {0x35, {0x4a, 0x03, 0xd3}}, {0x35, {0x62, 0x06, 0x54}},
+       {0x35, {0x5a, 0x04, 0x5d}}, {0x34, {0xe2, 0x01, 0x04}},
+       {0x34, {0xea, 0x00, 0xa0}}, {0x34, {0xf2, 0x0c, 0xbc}},
+       {0x34, {0xfa, 0x0c, 0x5b}}, {0x35, {0x04, 0x17, 0xf2}},
+       {0x35, {0x0c, 0x02, 0x08}}, {0x35, {0x14, 0x28, 0x43}},
+       {0x35, {0x1c, 0x28, 0x62}}, {0x35, {0x24, 0x2b, 0x60}},
+       {0x35, {0x2c, 0x07, 0x33}}, {0x35, {0x34, 0x1f, 0xb0}},
+       {0x35, {0x3c, 0xed, 0xcd}}, {0x35, {0x50, 0x00, 0x06}},
+       {0x35, {0x48, 0x07, 0xff}}, {0x35, {0x60, 0x05, 0x89}},
+       {0x35, {0x58, 0x07, 0xff}}, {0x35, {0x40, 0x00, 0xa0}},
+       {0x35, {0x42, 0x00, 0x00}}, {0x32, {0x10, 0x01, 0xfc}},
+       {0x33, {0x8c, 0xa1, 0x18}}, {0x33, {0x90, 0x00, 0x3c}},
 };
 
 static u8 *dat_640  = "\xd0\x02\xd1\x08\xd2\xe1\xd3\x02\xd4\x10\xd5\x81";
index ea05f67..9623b62 100644 (file)
@@ -2910,7 +2910,7 @@ static void pvr2_subdev_update(struct pvr2_hdw *hdw)
                        v4l2_std_id vs;
                        vs = hdw->std_mask_cur;
                        v4l2_device_call_all(&hdw->v4l2_dev, 0,
-                                            core, s_std, vs);
+                                            video, s_std, vs);
                        pvr2_hdw_cx25840_vbi_hack(hdw);
                }
                hdw->tuner_signal_stale = !0;
index 84a6720..a73b0bc 100644 (file)
@@ -681,12 +681,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
        return r;
 }
 
-static int stop_streaming(struct vb2_queue *vq)
+static void stop_streaming(struct vb2_queue *vq)
 {
        struct pwc_device *pdev = vb2_get_drv_priv(vq);
 
-       if (mutex_lock_interruptible(&pdev->v4l2_lock))
-               return -ERESTARTSYS;
+       mutex_lock(&pdev->v4l2_lock);
        if (pdev->udev) {
                pwc_set_leds(pdev, 0, 0);
                pwc_camera_power(pdev, 0);
@@ -695,8 +694,6 @@ static int stop_streaming(struct vb2_queue *vq)
 
        pwc_cleanup_queued_bufs(pdev);
        mutex_unlock(&pdev->v4l2_lock);
-
-       return 0;
 }
 
 static struct vb2_ops pwc_vb_queue_ops = {
index 1d4ba2b..a44466b 100644 (file)
@@ -714,7 +714,7 @@ static void buffer_queue(struct vb2_buffer *vb)
 }
 
 static int start_streaming(struct vb2_queue *vq, unsigned int count);
-static int stop_streaming(struct vb2_queue *vq);
+static void stop_streaming(struct vb2_queue *vq);
 
 static struct vb2_ops s2255_video_qops = {
        .queue_setup = queue_setup,
@@ -1109,7 +1109,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
 }
 
 /* abort streaming and wait for last buffer */
-static int stop_streaming(struct vb2_queue *vq)
+static void stop_streaming(struct vb2_queue *vq)
 {
        struct s2255_vc *vc = vb2_get_drv_priv(vq);
        struct s2255_buffer *buf, *node;
@@ -1123,7 +1123,6 @@ static int stop_streaming(struct vb2_queue *vq)
                        buf, buf->vb.v4l2_buf.index);
        }
        spin_unlock_irqrestore(&vc->qlock, flags);
-       return 0;
 }
 
 static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i)
@@ -1522,7 +1521,7 @@ static void s2255_destroy(struct s2255_dev *dev)
        /* board shutdown stops the read pipe if it is running */
        s2255_board_shutdown(dev);
        /* make sure firmware still not trying to load */
-       del_timer(&dev->timer);  /* only started in .probe and .open */
+       del_timer_sync(&dev->timer);  /* only started in .probe and .open */
        if (dev->fw_data->fw_urb) {
                usb_kill_urb(dev->fw_data->fw_urb);
                usb_free_urb(dev->fw_data->fw_urb);
@@ -2243,7 +2242,7 @@ static int s2255_probe(struct usb_interface *interface,
        dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL);
        if (dev->cmdbuf == NULL) {
                s2255_dev_err(&interface->dev, "out of memory\n");
-               return -ENOMEM;
+               goto errorFWDATA1;
        }
 
        atomic_set(&dev->num_channels, 0);
@@ -2352,7 +2351,7 @@ errorREQFW:
 errorFWDATA2:
        usb_free_urb(dev->fw_data->fw_urb);
 errorFWURB:
-       del_timer(&dev->timer);
+       del_timer_sync(&dev->timer);
 errorEP:
        usb_put_dev(dev->udev);
 errorUDEV:
index 34a26e0..03504dc 100644 (file)
@@ -67,17 +67,25 @@ int stk1160_read_reg(struct stk1160 *dev, u16 reg, u8 *value)
 {
        int ret;
        int pipe = usb_rcvctrlpipe(dev->udev, 0);
+       u8 *buf;
 
        *value = 0;
+
+       buf = kmalloc(sizeof(u8), GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
        ret = usb_control_msg(dev->udev, pipe, 0x00,
                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                       0x00, reg, value, sizeof(u8), HZ);
+                       0x00, reg, buf, sizeof(u8), HZ);
        if (ret < 0) {
                stk1160_err("read failed on reg 0x%x (%d)\n",
                        reg, ret);
+               kfree(buf);
                return ret;
        }
 
+       *value = *buf;
+       kfree(buf);
        return 0;
 }
 
index 37bc00f..5461341 100644 (file)
@@ -406,7 +406,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
 
        stk1160_set_std(dev);
 
-       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std,
+       v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std,
                        dev->norm);
 
        return 0;
@@ -583,10 +583,10 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
 }
 
 /* abort streaming and wait for last buffer */
-static int stop_streaming(struct vb2_queue *vq)
+static void stop_streaming(struct vb2_queue *vq)
 {
        struct stk1160 *dev = vb2_get_drv_priv(vq);
-       return stk1160_stop_streaming(dev);
+       stk1160_stop_streaming(dev);
 }
 
 static struct vb2_ops stk1160_video_qops = {
@@ -682,7 +682,7 @@ int stk1160_video_register(struct stk1160 *dev)
        dev->fmt = &format[0];
        stk1160_set_std(dev);
 
-       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std,
+       v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std,
                        dev->norm);
 
        video_set_drvdata(&dev->vdev, dev);
index 05b05b1..abdea48 100644 (file)
@@ -143,7 +143,6 @@ struct stk1160 {
        int num_alt;
 
        struct stk1160_isoc_ctl isoc_ctl;
-       char urb_buf[255];       /* urb control msg buffer */
 
        /* frame properties */
        int width;                /* current frame width */
index 1ccaadd..2e8c3af 100644 (file)
@@ -1120,7 +1120,7 @@ static int tm6000_init_dev(struct tm6000_core *dev)
        tm6000_config_tuner(dev);
 
        /* Set video standard */
-       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm);
 
        /* Set tuner frequency - also loads firmware on xc2028/xc3028 */
        f.tuner = 0;
index cc1aa14..e6b3d5d 100644 (file)
@@ -1071,7 +1071,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
        if (rc < 0)
                return rc;
 
-       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm);
 
        return 0;
 }
index 20365bd..2967e80 100644 (file)
@@ -634,15 +634,12 @@ static int usbtv_start_streaming(struct vb2_queue *vq, unsigned int count)
        return usbtv_start(usbtv);
 }
 
-static int usbtv_stop_streaming(struct vb2_queue *vq)
+static void usbtv_stop_streaming(struct vb2_queue *vq)
 {
        struct usbtv *usbtv = vb2_get_drv_priv(vq);
 
-       if (usbtv->udev == NULL)
-               return -ENODEV;
-
-       usbtv_stop(usbtv);
-       return 0;
+       if (usbtv->udev)
+               usbtv_stop(usbtv);
 }
 
 static struct vb2_ops usbtv_vb2_ops = {
index 5c9e312..68bc961 100644 (file)
@@ -597,7 +597,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id)
 
        usbvision->tvnorm_id = id;
 
-       call_all(usbvision, core, s_std, usbvision->tvnorm_id);
+       call_all(usbvision, video, s_std, usbvision->tvnorm_id);
        /* propagate the change to the decoder */
        usbvision_muxsel(usbvision, usbvision->ctl_input);
 
index 8d52baf..9144a2f 100644 (file)
@@ -361,6 +361,14 @@ static int uvc_commit_video(struct uvc_streaming *stream,
  * Clocks and timestamps
  */
 
+static inline void uvc_video_get_ts(struct timespec *ts)
+{
+       if (uvc_clock_param == CLOCK_MONOTONIC)
+               ktime_get_ts(ts);
+       else
+               ktime_get_real_ts(ts);
+}
+
 static void
 uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
                       const __u8 *data, int len)
@@ -420,7 +428,7 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
        stream->clock.last_sof = dev_sof;
 
        host_sof = usb_get_current_frame_number(stream->dev->udev);
-       ktime_get_ts(&ts);
+       uvc_video_get_ts(&ts);
 
        /* The UVC specification allows device implementations that can't obtain
         * the USB frame number to keep their own frame counters as long as they
@@ -1011,10 +1019,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
                        return -ENODATA;
                }
 
-               if (uvc_clock_param == CLOCK_MONOTONIC)
-                       ktime_get_ts(&ts);
-               else
-                       ktime_get_real_ts(&ts);
+               uvc_video_get_ts(&ts);
 
                buf->buf.v4l2_buf.sequence = stream->sequence;
                buf->buf.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
@@ -1132,6 +1137,17 @@ static int uvc_video_encode_data(struct uvc_streaming *stream,
  * URB handling
  */
 
+/*
+ * Set error flag for incomplete buffer.
+ */
+static void uvc_video_validate_buffer(const struct uvc_streaming *stream,
+                                     struct uvc_buffer *buf)
+{
+       if (buf->length != buf->bytesused &&
+           !(stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED))
+               buf->error = 1;
+}
+
 /*
  * Completion handler for video URBs.
  */
@@ -1156,9 +1172,11 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
                do {
                        ret = uvc_video_decode_start(stream, buf, mem,
                                urb->iso_frame_desc[i].actual_length);
-                       if (ret == -EAGAIN)
+                       if (ret == -EAGAIN) {
+                               uvc_video_validate_buffer(stream, buf);
                                buf = uvc_queue_next_buffer(&stream->queue,
                                                            buf);
+                       }
                } while (ret == -EAGAIN);
 
                if (ret < 0)
@@ -1173,11 +1191,7 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
                        urb->iso_frame_desc[i].actual_length);
 
                if (buf->state == UVC_BUF_STATE_READY) {
-                       if (buf->length != buf->bytesused &&
-                           !(stream->cur_format->flags &
-                             UVC_FMT_FLAG_COMPRESSED))
-                               buf->error = 1;
-
+                       uvc_video_validate_buffer(stream, buf);
                        buf = uvc_queue_next_buffer(&stream->queue, buf);
                }
        }
index 2189bfb..9ca0f8d 100644 (file)
@@ -83,3 +83,7 @@ config VIDEOBUF2_DMA_SG
        #depends on HAS_DMA
        select VIDEOBUF2_CORE
        select VIDEOBUF2_MEMOPS
+
+config VIDEOBUF2_DVB
+       tristate
+       select VIDEOBUF2_CORE
index c6ae7ba..63d29f2 100644 (file)
@@ -33,6 +33,7 @@ obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o
 obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o
 obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o
 obj-$(CONFIG_VIDEOBUF2_DMA_SG) += videobuf2-dma-sg.o
+obj-$(CONFIG_VIDEOBUF2_DVB) += videobuf2-dvb.o
 
 ccflags-y += -I$(srctree)/drivers/media/dvb-core
 ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
index 20c0922..06c18ba 100644 (file)
@@ -1301,7 +1301,6 @@ static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg)
 
 static const struct v4l2_subdev_core_ops tuner_core_ops = {
        .log_status = tuner_log_status,
-       .s_std = tuner_s_std,
        .s_power = tuner_s_power,
 };
 
@@ -1315,9 +1314,14 @@ static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
        .s_config = tuner_s_config,
 };
 
+static const struct v4l2_subdev_video_ops tuner_video_ops = {
+       .s_std = tuner_s_std,
+};
+
 static const struct v4l2_subdev_ops tuner_ops = {
        .core = &tuner_core_ops,
        .tuner = &tuner_tuner_ops,
+       .video = &tuner_video_ops,
 };
 
 /*
index 02d1b63..015f92a 100644 (file)
@@ -158,7 +158,17 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
        /* Warn if we apparently re-register a subdev */
        WARN_ON(sd->v4l2_dev != NULL);
 
-       if (!try_module_get(sd->owner))
+       /*
+        * The reason to acquire the module here is to avoid unloading
+        * a module of sub-device which is registered to a media
+        * device. To make it possible to unload modules for media
+        * devices that also register sub-devices, do not
+        * try_module_get() such sub-device owners.
+        */
+       sd->owner_v4l2_dev = v4l2_dev->dev && v4l2_dev->dev->driver &&
+               sd->owner == v4l2_dev->dev->driver->owner;
+
+       if (!sd->owner_v4l2_dev && !try_module_get(sd->owner))
                return -ENODEV;
 
        sd->v4l2_dev = v4l2_dev;
@@ -192,7 +202,8 @@ error_unregister:
        if (sd->internal_ops && sd->internal_ops->unregistered)
                sd->internal_ops->unregistered(sd);
 error_module:
-       module_put(sd->owner);
+       if (!sd->owner_v4l2_dev)
+               module_put(sd->owner);
        sd->v4l2_dev = NULL;
        return err;
 }
@@ -280,6 +291,7 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
        }
 #endif
        video_unregister_device(sd->devnode);
-       module_put(sd->owner);
+       if (!sd->owner_v4l2_dev)
+               module_put(sd->owner);
 }
 EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
index 48b20df..4ae54ca 100644 (file)
@@ -131,6 +131,17 @@ const struct v4l2_dv_timings v4l2_dv_timings_presets[] = {
        V4L2_DV_BT_DMT_2560X1600P75,
        V4L2_DV_BT_DMT_2560X1600P85,
        V4L2_DV_BT_DMT_2560X1600P120_RB,
+       V4L2_DV_BT_CEA_3840X2160P24,
+       V4L2_DV_BT_CEA_3840X2160P25,
+       V4L2_DV_BT_CEA_3840X2160P30,
+       V4L2_DV_BT_CEA_3840X2160P50,
+       V4L2_DV_BT_CEA_3840X2160P60,
+       V4L2_DV_BT_CEA_4096X2160P24,
+       V4L2_DV_BT_CEA_4096X2160P25,
+       V4L2_DV_BT_CEA_4096X2160P30,
+       V4L2_DV_BT_CEA_4096X2160P50,
+       V4L2_DV_BT_DMT_4096X2160P59_94_RB,
+       V4L2_DV_BT_CEA_4096X2160P60,
        { }
 };
 EXPORT_SYMBOL_GPL(v4l2_dv_timings_presets);
index 86dcb54..8761aab 100644 (file)
@@ -318,3 +318,39 @@ int v4l2_event_subdev_unsubscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh,
        return v4l2_event_unsubscribe(fh, sub);
 }
 EXPORT_SYMBOL_GPL(v4l2_event_subdev_unsubscribe);
+
+static void v4l2_event_src_replace(struct v4l2_event *old,
+                               const struct v4l2_event *new)
+{
+       u32 old_changes = old->u.src_change.changes;
+
+       old->u.src_change = new->u.src_change;
+       old->u.src_change.changes |= old_changes;
+}
+
+static void v4l2_event_src_merge(const struct v4l2_event *old,
+                               struct v4l2_event *new)
+{
+       new->u.src_change.changes |= old->u.src_change.changes;
+}
+
+static const struct v4l2_subscribed_event_ops v4l2_event_src_ch_ops = {
+       .replace = v4l2_event_src_replace,
+       .merge = v4l2_event_src_merge,
+};
+
+int v4l2_src_change_event_subscribe(struct v4l2_fh *fh,
+                               const struct v4l2_event_subscription *sub)
+{
+       if (sub->type == V4L2_EVENT_SOURCE_CHANGE)
+               return v4l2_event_subscribe(fh, sub, 0, &v4l2_event_src_ch_ops);
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(v4l2_src_change_event_subscribe);
+
+int v4l2_src_change_event_subdev_subscribe(struct v4l2_subdev *sd,
+               struct v4l2_fh *fh, struct v4l2_event_subscription *sub)
+{
+       return v4l2_src_change_event_subscribe(fh, sub);
+}
+EXPORT_SYMBOL_GPL(v4l2_src_change_event_subdev_subscribe);
index d9113cc..16bffd8 100644 (file)
@@ -562,7 +562,7 @@ static void v4l_print_cropcap(const void *arg, bool write_only)
        const struct v4l2_cropcap *p = arg;
 
        pr_cont("type=%s, bounds wxh=%dx%d, x,y=%d,%d, "
-               "defrect wxh=%dx%d, x,y=%d,%d\n, "
+               "defrect wxh=%dx%d, x,y=%d,%d, "
                "pixelaspect %d/%d\n",
                prt_names(p->type, v4l2_type_names),
                p->bounds.width, p->bounds.height,
@@ -2260,7 +2260,7 @@ done:
 }
 
 static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
-                           void * __user *user_ptr, void ***kernel_ptr)
+                           void __user **user_ptr, void ***kernel_ptr)
 {
        int ret = 0;
 
@@ -2277,7 +2277,7 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
                                break;
                        }
                        *user_ptr = (void __user *)buf->m.planes;
-                       *kernel_ptr = (void *)&buf->m.planes;
+                       *kernel_ptr = (void **)&buf->m.planes;
                        *array_size = sizeof(struct v4l2_plane) * buf->length;
                        ret = 1;
                }
@@ -2294,7 +2294,7 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
                                break;
                        }
                        *user_ptr = (void __user *)edid->edid;
-                       *kernel_ptr = (void *)&edid->edid;
+                       *kernel_ptr = (void **)&edid->edid;
                        *array_size = edid->blocks * 128;
                        ret = 1;
                }
@@ -2312,7 +2312,7 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
                                break;
                        }
                        *user_ptr = (void __user *)ctrls->controls;
-                       *kernel_ptr = (void *)&ctrls->controls;
+                       *kernel_ptr = (void **)&ctrls->controls;
                        *array_size = sizeof(struct v4l2_ext_control)
                                    * ctrls->count;
                        ret = 1;
@@ -2412,7 +2412,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
        }
 
        if (has_array_args) {
-               *kernel_ptr = user_ptr;
+               *kernel_ptr = (void __force *)user_ptr;
                if (copy_to_user(user_ptr, mbuf, array_size))
                        err = -EFAULT;
                goto out_array_args;
index aea84ac..058c1a6 100644 (file)
@@ -305,11 +305,23 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                                        fse);
        }
 
-       case VIDIOC_SUBDEV_G_FRAME_INTERVAL:
+       case VIDIOC_SUBDEV_G_FRAME_INTERVAL: {
+               struct v4l2_subdev_frame_interval *fi = arg;
+
+               if (fi->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
                return v4l2_subdev_call(sd, video, g_frame_interval, arg);
+       }
+
+       case VIDIOC_SUBDEV_S_FRAME_INTERVAL: {
+               struct v4l2_subdev_frame_interval *fi = arg;
+
+               if (fi->pad >= sd->entity.num_pads)
+                       return -EINVAL;
 
-       case VIDIOC_SUBDEV_S_FRAME_INTERVAL:
                return v4l2_subdev_call(sd, video, s_frame_interval, arg);
+       }
 
        case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {
                struct v4l2_subdev_frame_interval_enum *fie = arg;
@@ -349,11 +361,54 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                        sd, pad, set_selection, subdev_fh, sel);
        }
 
-       case VIDIOC_G_EDID:
-               return v4l2_subdev_call(sd, pad, get_edid, arg);
+       case VIDIOC_G_EDID: {
+               struct v4l2_subdev_edid *edid = arg;
+
+               if (edid->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+               if (edid->blocks && edid->edid == NULL)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, get_edid, edid);
+       }
+
+       case VIDIOC_S_EDID: {
+               struct v4l2_subdev_edid *edid = arg;
+
+               if (edid->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+               if (edid->blocks && edid->edid == NULL)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, set_edid, edid);
+       }
+
+       case VIDIOC_SUBDEV_DV_TIMINGS_CAP: {
+               struct v4l2_dv_timings_cap *cap = arg;
+
+               if (cap->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, dv_timings_cap, cap);
+       }
+
+       case VIDIOC_SUBDEV_ENUM_DV_TIMINGS: {
+               struct v4l2_enum_dv_timings *dvt = arg;
+
+               if (dvt->pad >= sd->entity.num_pads)
+                       return -EINVAL;
+
+               return v4l2_subdev_call(sd, pad, enum_dv_timings, dvt);
+       }
+
+       case VIDIOC_SUBDEV_QUERY_DV_TIMINGS:
+               return v4l2_subdev_call(sd, video, query_dv_timings, arg);
+
+       case VIDIOC_SUBDEV_G_DV_TIMINGS:
+               return v4l2_subdev_call(sd, video, g_dv_timings, arg);
 
-       case VIDIOC_S_EDID:
-               return v4l2_subdev_call(sd, pad, set_edid, arg);
+       case VIDIOC_SUBDEV_S_DV_TIMINGS:
+               return v4l2_subdev_call(sd, video, s_dv_timings, arg);
 #endif
        default:
                return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
index 7e6b209..bf80f0f 100644 (file)
@@ -305,7 +305,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
        /* Try to remap memory */
        size = vma->vm_end - vma->vm_start;
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-       retval = vm_iomap_memory(vma, vma->vm_start, size);
+       retval = vm_iomap_memory(vma, mem->dma_handle, size);
        if (retval) {
                dev_err(q->dev, "mmap: remap failed with error %d. ",
                        retval);
index f9059bb..349e659 100644 (file)
@@ -6,6 +6,9 @@
  * Author: Pawel Osciak <pawel@osciak.com>
  *        Marek Szyprowski <m.szyprowski@samsung.com>
  *
+ * The vb2_thread implementation was based on code from videobuf-dvb.c:
+ *     (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ *
  * 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.
 #include <linux/poll.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
 
 #include <media/v4l2-dev.h>
 #include <media/v4l2-fh.h>
 #include <media/v4l2-event.h>
+#include <media/v4l2-common.h>
 #include <media/videobuf2-core.h>
 
 static int debug;
 module_param(debug, int, 0644);
 
-#define dprintk(level, fmt, arg...)                                    \
-       do {                                                            \
-               if (debug >= level)                                     \
-                       printk(KERN_DEBUG "vb2: " fmt, ## arg);         \
+#define dprintk(level, fmt, arg...)                                          \
+       do {                                                                  \
+               if (debug >= level)                                           \
+                       pr_debug("vb2: %s: " fmt, __func__, ## arg); \
        } while (0)
 
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 
 /*
- * If advanced debugging is on, then count how often each op is called,
- * which can either be per-buffer or per-queue.
+ * If advanced debugging is on, then count how often each op is called
+ * successfully, which can either be per-buffer or per-queue.
  *
- * If the op failed then the 'fail_' variant is called to decrease the
- * counter. That makes it easy to check that the 'init' and 'cleanup'
+ * This makes it easy to check that the 'init' and 'cleanup'
  * (and variations thereof) stay balanced.
  */
 
+#define log_memop(vb, op)                                              \
+       dprintk(2, "call_memop(%p, %d, %s)%s\n",                        \
+               (vb)->vb2_queue, (vb)->v4l2_buf.index, #op,             \
+               (vb)->vb2_queue->mem_ops->op ? "" : " (nop)")
+
 #define call_memop(vb, op, args...)                                    \
 ({                                                                     \
        struct vb2_queue *_q = (vb)->vb2_queue;                         \
-       dprintk(2, "call_memop(%p, %d, %s)%s\n",                        \
-               _q, (vb)->v4l2_buf.index, #op,                          \
-               _q->mem_ops->op ? "" : " (nop)");                       \
+       int err;                                                        \
+                                                                       \
+       log_memop(vb, op);                                              \
+       err = _q->mem_ops->op ? _q->mem_ops->op(args) : 0;              \
+       if (!err)                                                       \
+               (vb)->cnt_mem_ ## op++;                                 \
+       err;                                                            \
+})
+
+#define call_ptr_memop(vb, op, args...)                                        \
+({                                                                     \
+       struct vb2_queue *_q = (vb)->vb2_queue;                         \
+       void *ptr;                                                      \
+                                                                       \
+       log_memop(vb, op);                                              \
+       ptr = _q->mem_ops->op ? _q->mem_ops->op(args) : NULL;           \
+       if (!IS_ERR_OR_NULL(ptr))                                       \
+               (vb)->cnt_mem_ ## op++;                                 \
+       ptr;                                                            \
+})
+
+#define call_void_memop(vb, op, args...)                               \
+({                                                                     \
+       struct vb2_queue *_q = (vb)->vb2_queue;                         \
+                                                                       \
+       log_memop(vb, op);                                              \
+       if (_q->mem_ops->op)                                            \
+               _q->mem_ops->op(args);                                  \
        (vb)->cnt_mem_ ## op++;                                         \
-       _q->mem_ops->op ? _q->mem_ops->op(args) : 0;                    \
 })
-#define fail_memop(vb, op) ((vb)->cnt_mem_ ## op--)
+
+#define log_qop(q, op)                                                 \
+       dprintk(2, "call_qop(%p, %s)%s\n", q, #op,                      \
+               (q)->ops->op ? "" : " (nop)")
 
 #define call_qop(q, op, args...)                                       \
 ({                                                                     \
-       dprintk(2, "call_qop(%p, %s)%s\n", q, #op,                      \
-               (q)->ops->op ? "" : " (nop)");                          \
+       int err;                                                        \
+                                                                       \
+       log_qop(q, op);                                                 \
+       err = (q)->ops->op ? (q)->ops->op(args) : 0;                    \
+       if (!err)                                                       \
+               (q)->cnt_ ## op++;                                      \
+       err;                                                            \
+})
+
+#define call_void_qop(q, op, args...)                                  \
+({                                                                     \
+       log_qop(q, op);                                                 \
+       if ((q)->ops->op)                                               \
+               (q)->ops->op(args);                                     \
        (q)->cnt_ ## op++;                                              \
-       (q)->ops->op ? (q)->ops->op(args) : 0;                          \
 })
-#define fail_qop(q, op) ((q)->cnt_ ## op--)
+
+#define log_vb_qop(vb, op, args...)                                    \
+       dprintk(2, "call_vb_qop(%p, %d, %s)%s\n",                       \
+               (vb)->vb2_queue, (vb)->v4l2_buf.index, #op,             \
+               (vb)->vb2_queue->ops->op ? "" : " (nop)")
 
 #define call_vb_qop(vb, op, args...)                                   \
 ({                                                                     \
-       struct vb2_queue *_q = (vb)->vb2_queue;                         \
-       dprintk(2, "call_vb_qop(%p, %d, %s)%s\n",                       \
-               _q, (vb)->v4l2_buf.index, #op,                          \
-               _q->ops->op ? "" : " (nop)");                           \
+       int err;                                                        \
+                                                                       \
+       log_vb_qop(vb, op);                                             \
+       err = (vb)->vb2_queue->ops->op ?                                \
+               (vb)->vb2_queue->ops->op(args) : 0;                     \
+       if (!err)                                                       \
+               (vb)->cnt_ ## op++;                                     \
+       err;                                                            \
+})
+
+#define call_void_vb_qop(vb, op, args...)                              \
+({                                                                     \
+       log_vb_qop(vb, op);                                             \
+       if ((vb)->vb2_queue->ops->op)                                   \
+               (vb)->vb2_queue->ops->op(args);                         \
        (vb)->cnt_ ## op++;                                             \
-       _q->ops->op ? _q->ops->op(args) : 0;                            \
 })
-#define fail_vb_qop(vb, op) ((vb)->cnt_ ## op--)
 
 #else
 
 #define call_memop(vb, op, args...)                                    \
-       ((vb)->vb2_queue->mem_ops->op ? (vb)->vb2_queue->mem_ops->op(args) : 0)
-#define fail_memop(vb, op)
+       ((vb)->vb2_queue->mem_ops->op ?                                 \
+               (vb)->vb2_queue->mem_ops->op(args) : 0)
+
+#define call_ptr_memop(vb, op, args...)                                        \
+       ((vb)->vb2_queue->mem_ops->op ?                                 \
+               (vb)->vb2_queue->mem_ops->op(args) : NULL)
+
+#define call_void_memop(vb, op, args...)                               \
+       do {                                                            \
+               if ((vb)->vb2_queue->mem_ops->op)                       \
+                       (vb)->vb2_queue->mem_ops->op(args);             \
+       } while (0)
 
 #define call_qop(q, op, args...)                                       \
        ((q)->ops->op ? (q)->ops->op(args) : 0)
-#define fail_qop(q, op)
+
+#define call_void_qop(q, op, args...)                                  \
+       do {                                                            \
+               if ((q)->ops->op)                                       \
+                       (q)->ops->op(args);                             \
+       } while (0)
 
 #define call_vb_qop(vb, op, args...)                                   \
        ((vb)->vb2_queue->ops->op ? (vb)->vb2_queue->ops->op(args) : 0)
-#define fail_vb_qop(vb, op)
+
+#define call_void_vb_qop(vb, op, args...)                              \
+       do {                                                            \
+               if ((vb)->vb2_queue->ops->op)                           \
+                       (vb)->vb2_queue->ops->op(args);                 \
+       } while (0)
 
 #endif
 
@@ -118,7 +199,7 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
        for (plane = 0; plane < vb->num_planes; ++plane) {
                unsigned long size = PAGE_ALIGN(q->plane_sizes[plane]);
 
-               mem_priv = call_memop(vb, alloc, q->alloc_ctx[plane],
+               mem_priv = call_ptr_memop(vb, alloc, q->alloc_ctx[plane],
                                      size, q->gfp_flags);
                if (IS_ERR_OR_NULL(mem_priv))
                        goto free;
@@ -130,10 +211,9 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
 
        return 0;
 free:
-       fail_memop(vb, alloc);
        /* Free already allocated memory if one of the allocations failed */
        for (; plane > 0; --plane) {
-               call_memop(vb, put, vb->planes[plane - 1].mem_priv);
+               call_void_memop(vb, put, vb->planes[plane - 1].mem_priv);
                vb->planes[plane - 1].mem_priv = NULL;
        }
 
@@ -148,9 +228,9 @@ static void __vb2_buf_mem_free(struct vb2_buffer *vb)
        unsigned int plane;
 
        for (plane = 0; plane < vb->num_planes; ++plane) {
-               call_memop(vb, put, vb->planes[plane].mem_priv);
+               call_void_memop(vb, put, vb->planes[plane].mem_priv);
                vb->planes[plane].mem_priv = NULL;
-               dprintk(3, "Freed plane %d of buffer %d\n", plane,
+               dprintk(3, "freed plane %d of buffer %d\n", plane,
                        vb->v4l2_buf.index);
        }
 }
@@ -165,7 +245,7 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
 
        for (plane = 0; plane < vb->num_planes; ++plane) {
                if (vb->planes[plane].mem_priv)
-                       call_memop(vb, put_userptr, vb->planes[plane].mem_priv);
+                       call_void_memop(vb, put_userptr, vb->planes[plane].mem_priv);
                vb->planes[plane].mem_priv = NULL;
        }
 }
@@ -180,9 +260,9 @@ static void __vb2_plane_dmabuf_put(struct vb2_buffer *vb, struct vb2_plane *p)
                return;
 
        if (p->dbuf_mapped)
-               call_memop(vb, unmap_dmabuf, p->mem_priv);
+               call_void_memop(vb, unmap_dmabuf, p->mem_priv);
 
-       call_memop(vb, detach_dmabuf, p->mem_priv);
+       call_void_memop(vb, detach_dmabuf, p->mem_priv);
        dma_buf_put(p->dbuf);
        memset(p, 0, sizeof(*p));
 }
@@ -245,7 +325,7 @@ static void __setup_offsets(struct vb2_queue *q, unsigned int n)
                for (plane = 0; plane < vb->num_planes; ++plane) {
                        vb->v4l2_planes[plane].m.mem_offset = off;
 
-                       dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n",
+                       dprintk(3, "buffer %d, plane %d offset 0x%08lx\n",
                                        buffer, plane, off);
 
                        off += vb->v4l2_planes[plane].length;
@@ -272,7 +352,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
                /* Allocate videobuf buffer structures */
                vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
                if (!vb) {
-                       dprintk(1, "Memory alloc for buffer struct failed\n");
+                       dprintk(1, "memory alloc for buffer struct failed\n");
                        break;
                }
 
@@ -291,7 +371,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
                if (memory == V4L2_MEMORY_MMAP) {
                        ret = __vb2_buf_mem_alloc(vb);
                        if (ret) {
-                               dprintk(1, "Failed allocating memory for "
+                               dprintk(1, "failed allocating memory for "
                                                "buffer %d\n", buffer);
                                kfree(vb);
                                break;
@@ -303,9 +383,8 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
                         */
                        ret = call_vb_qop(vb, buf_init, vb);
                        if (ret) {
-                               dprintk(1, "Buffer %d %p initialization"
+                               dprintk(1, "buffer %d %p initialization"
                                        " failed\n", buffer, vb);
-                               fail_vb_qop(vb, buf_init);
                                __vb2_buf_mem_free(vb);
                                kfree(vb);
                                break;
@@ -319,7 +398,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
        if (memory == V4L2_MEMORY_MMAP)
                __setup_offsets(q, buffer);
 
-       dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
+       dprintk(1, "allocated %d buffers, %d plane(s) each\n",
                        buffer, num_planes);
 
        return buffer;
@@ -371,7 +450,7 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
                if (q->bufs[buffer] == NULL)
                        continue;
                if (q->bufs[buffer]->state == VB2_BUF_STATE_PREPARING) {
-                       dprintk(1, "reqbufs: preparing buffers, cannot free\n");
+                       dprintk(1, "preparing buffers, cannot free\n");
                        return -EAGAIN;
                }
        }
@@ -382,7 +461,7 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
                struct vb2_buffer *vb = q->bufs[buffer];
 
                if (vb && vb->planes[0].mem_priv)
-                       call_vb_qop(vb, buf_cleanup, vb);
+                       call_void_vb_qop(vb, buf_cleanup, vb);
        }
 
        /* Release video buffer memory */
@@ -476,13 +555,13 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer
 
        /* Is memory for copying plane information present? */
        if (NULL == b->m.planes) {
-               dprintk(1, "Multi-planar buffer passed but "
+               dprintk(1, "multi-planar buffer passed but "
                           "planes array not provided\n");
                return -EINVAL;
        }
 
        if (b->length < vb->num_planes || b->length > VIDEO_MAX_PLANES) {
-               dprintk(1, "Incorrect planes array length, "
+               dprintk(1, "incorrect planes array length, "
                           "expected %d, got %d\n", vb->num_planes, b->length);
                return -EINVAL;
        }
@@ -656,12 +735,12 @@ int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
        int ret;
 
        if (b->type != q->type) {
-               dprintk(1, "querybuf: wrong buffer type\n");
+               dprintk(1, "wrong buffer type\n");
                return -EINVAL;
        }
 
        if (b->index >= q->num_buffers) {
-               dprintk(1, "querybuf: buffer index out of range\n");
+               dprintk(1, "buffer index out of range\n");
                return -EINVAL;
        }
        vb = q->bufs[b->index];
@@ -721,12 +800,12 @@ static int __verify_memory_type(struct vb2_queue *q,
 {
        if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR &&
            memory != V4L2_MEMORY_DMABUF) {
-               dprintk(1, "reqbufs: unsupported memory type\n");
+               dprintk(1, "unsupported memory type\n");
                return -EINVAL;
        }
 
        if (type != q->type) {
-               dprintk(1, "reqbufs: requested type is incorrect\n");
+               dprintk(1, "requested type is incorrect\n");
                return -EINVAL;
        }
 
@@ -735,17 +814,17 @@ static int __verify_memory_type(struct vb2_queue *q,
         * are available.
         */
        if (memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
-               dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
+               dprintk(1, "MMAP for current setup unsupported\n");
                return -EINVAL;
        }
 
        if (memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
-               dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
+               dprintk(1, "USERPTR for current setup unsupported\n");
                return -EINVAL;
        }
 
        if (memory == V4L2_MEMORY_DMABUF && __verify_dmabuf_ops(q)) {
-               dprintk(1, "reqbufs: DMABUF for current setup unsupported\n");
+               dprintk(1, "DMABUF for current setup unsupported\n");
                return -EINVAL;
        }
 
@@ -754,8 +833,8 @@ static int __verify_memory_type(struct vb2_queue *q,
         * create_bufs is called with count == 0, but count == 0 should still
         * do the memory and type validation.
         */
-       if (q->fileio) {
-               dprintk(1, "reqbufs: file io in progress\n");
+       if (vb2_fileio_is_active(q)) {
+               dprintk(1, "file io in progress\n");
                return -EBUSY;
        }
        return 0;
@@ -790,7 +869,7 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
        int ret;
 
        if (q->streaming) {
-               dprintk(1, "reqbufs: streaming active\n");
+               dprintk(1, "streaming active\n");
                return -EBUSY;
        }
 
@@ -800,7 +879,7 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
                 * are not in use and can be freed.
                 */
                if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
-                       dprintk(1, "reqbufs: memory in use, cannot free\n");
+                       dprintk(1, "memory in use, cannot free\n");
                        return -EBUSY;
                }
 
@@ -826,7 +905,7 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
         * Make sure the requested values and current defaults are sane.
         */
        num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
-       num_buffers = max_t(unsigned int, req->count, q->min_buffers_needed);
+       num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed);
        memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
        memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
        q->memory = req->memory;
@@ -837,15 +916,13 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
         */
        ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,
                       q->plane_sizes, q->alloc_ctx);
-       if (ret) {
-               fail_qop(q, queue_setup);
+       if (ret)
                return ret;
-       }
 
        /* Finally, allocate buffers and video memory */
        allocated_buffers = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
        if (allocated_buffers == 0) {
-               dprintk(1, "Memory allocation failed\n");
+               dprintk(1, "memory allocation failed\n");
                return -ENOMEM;
        }
 
@@ -864,8 +941,6 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
 
                ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
                               &num_planes, q->plane_sizes, q->alloc_ctx);
-               if (ret)
-                       fail_qop(q, queue_setup);
 
                if (!ret && allocated_buffers < num_buffers)
                        ret = -ENOMEM;
@@ -931,8 +1006,7 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create
        int ret;
 
        if (q->num_buffers == VIDEO_MAX_FRAME) {
-               dprintk(1, "%s(): maximum number of buffers already allocated\n",
-                       __func__);
+               dprintk(1, "maximum number of buffers already allocated\n");
                return -ENOBUFS;
        }
 
@@ -950,16 +1024,14 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create
         */
        ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
                       &num_planes, q->plane_sizes, q->alloc_ctx);
-       if (ret) {
-               fail_qop(q, queue_setup);
+       if (ret)
                return ret;
-       }
 
        /* Finally, allocate buffers and video memory */
        allocated_buffers = __vb2_queue_alloc(q, create->memory, num_buffers,
                                num_planes);
        if (allocated_buffers == 0) {
-               dprintk(1, "Memory allocation failed\n");
+               dprintk(1, "memory allocation failed\n");
                return -ENOMEM;
        }
 
@@ -975,8 +1047,6 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create
                 */
                ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
                               &num_planes, q->plane_sizes, q->alloc_ctx);
-               if (ret)
-                       fail_qop(q, queue_setup);
 
                if (!ret && allocated_buffers < num_buffers)
                        ret = -ENOMEM;
@@ -1038,7 +1108,7 @@ void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
        if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv)
                return NULL;
 
-       return call_memop(vb, vaddr, vb->planes[plane_no].mem_priv);
+       return call_ptr_memop(vb, vaddr, vb->planes[plane_no].mem_priv);
 
 }
 EXPORT_SYMBOL_GPL(vb2_plane_vaddr);
@@ -1059,7 +1129,7 @@ void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no)
        if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv)
                return NULL;
 
-       return call_memop(vb, cookie, vb->planes[plane_no].mem_priv);
+       return call_ptr_memop(vb, cookie, vb->planes[plane_no].mem_priv);
 }
 EXPORT_SYMBOL_GPL(vb2_plane_cookie);
 
@@ -1094,9 +1164,8 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
        if (!q->start_streaming_called) {
                if (WARN_ON(state != VB2_BUF_STATE_QUEUED))
                        state = VB2_BUF_STATE_QUEUED;
-       } else if (!WARN_ON(!q->start_streaming_called)) {
-               if (WARN_ON(state != VB2_BUF_STATE_DONE &&
-                           state != VB2_BUF_STATE_ERROR))
+       } else if (WARN_ON(state != VB2_BUF_STATE_DONE &&
+                          state != VB2_BUF_STATE_ERROR)) {
                        state = VB2_BUF_STATE_ERROR;
        }
 
@@ -1107,12 +1176,12 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
         */
        vb->cnt_buf_done++;
 #endif
-       dprintk(4, "Done processing on buffer %d, state: %d\n",
+       dprintk(4, "done processing on buffer %d, state: %d\n",
                        vb->v4l2_buf.index, state);
 
        /* sync buffers */
        for (plane = 0; plane < vb->num_planes; ++plane)
-               call_memop(vb, finish, vb->planes[plane].mem_priv);
+               call_void_memop(vb, finish, vb->planes[plane].mem_priv);
 
        /* Add the buffer to the done buffers list */
        spin_lock_irqsave(&q->done_lock, flags);
@@ -1143,15 +1212,30 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
        if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
                /* Fill in driver-provided information for OUTPUT types */
                if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+                       bool bytesused_is_used;
+
+                       /* Check if bytesused == 0 for all planes */
+                       for (plane = 0; plane < vb->num_planes; ++plane)
+                               if (b->m.planes[plane].bytesused)
+                                       break;
+                       bytesused_is_used = plane < vb->num_planes;
+
                        /*
                         * Will have to go up to b->length when API starts
                         * accepting variable number of planes.
+                        *
+                        * If bytesused_is_used is false, then fall back to the
+                        * full buffer size. In that case userspace clearly
+                        * never bothered to set it and it's a safe assumption
+                        * that they really meant to use the full plane sizes.
                         */
                        for (plane = 0; plane < vb->num_planes; ++plane) {
-                               v4l2_planes[plane].bytesused =
-                                       b->m.planes[plane].bytesused;
-                               v4l2_planes[plane].data_offset =
-                                       b->m.planes[plane].data_offset;
+                               struct v4l2_plane *pdst = &v4l2_planes[plane];
+                               struct v4l2_plane *psrc = &b->m.planes[plane];
+
+                               pdst->bytesused = bytesused_is_used ?
+                                       psrc->bytesused : psrc->length;
+                               pdst->data_offset = psrc->data_offset;
                        }
                }
 
@@ -1169,8 +1253,6 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
                                        b->m.planes[plane].m.fd;
                                v4l2_planes[plane].length =
                                        b->m.planes[plane].length;
-                               v4l2_planes[plane].data_offset =
-                                       b->m.planes[plane].data_offset;
                        }
                }
        } else {
@@ -1179,11 +1261,15 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
                 * so fill in relevant v4l2_buffer struct fields instead.
                 * In videobuf we use our internal V4l2_planes struct for
                 * single-planar buffers as well, for simplicity.
+                *
+                * If bytesused == 0, then fall back to the full buffer size
+                * as that's a sensible default.
                 */
-               if (V4L2_TYPE_IS_OUTPUT(b->type)) {
-                       v4l2_planes[0].bytesused = b->bytesused;
-                       v4l2_planes[0].data_offset = 0;
-               }
+               if (V4L2_TYPE_IS_OUTPUT(b->type))
+                       v4l2_planes[0].bytesused =
+                               b->bytesused ? b->bytesused : b->length;
+               else
+                       v4l2_planes[0].bytesused = 0;
 
                if (b->memory == V4L2_MEMORY_USERPTR) {
                        v4l2_planes[0].m.userptr = b->m.userptr;
@@ -1193,9 +1279,7 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
                if (b->memory == V4L2_MEMORY_DMABUF) {
                        v4l2_planes[0].m.fd = b->m.fd;
                        v4l2_planes[0].length = b->length;
-                       v4l2_planes[0].data_offset = 0;
                }
-
        }
 
        /* Zero flags that the vb2 core handles */
@@ -1225,6 +1309,15 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
        }
 }
 
+/**
+ * __qbuf_mmap() - handle qbuf of an MMAP buffer
+ */
+static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+       __fill_vb2_buffer(vb, b, vb->v4l2_planes);
+       return call_vb_qop(vb, buf_prepare, vb);
+}
+
 /**
  * __qbuf_userptr() - handle qbuf of a USERPTR buffer
  */
@@ -1238,6 +1331,7 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
        int write = !V4L2_TYPE_IS_OUTPUT(q->type);
        bool reacquired = vb->planes[0].mem_priv == NULL;
 
+       memset(planes, 0, sizeof(planes[0]) * vb->num_planes);
        /* Copy relevant information provided by the userspace */
        __fill_vb2_buffer(vb, b, planes);
 
@@ -1248,12 +1342,12 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
                    && vb->v4l2_planes[plane].length == planes[plane].length)
                        continue;
 
-               dprintk(3, "qbuf: userspace address for plane %d changed, "
+               dprintk(3, "userspace address for plane %d changed, "
                                "reacquiring memory\n", plane);
 
                /* Check if the provided plane buffer is large enough */
                if (planes[plane].length < q->plane_sizes[plane]) {
-                       dprintk(1, "qbuf: provided buffer size %u is less than "
+                       dprintk(1, "provided buffer size %u is less than "
                                                "setup size %u for plane %d\n",
                                                planes[plane].length,
                                                q->plane_sizes[plane], plane);
@@ -1265,22 +1359,21 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
                if (vb->planes[plane].mem_priv) {
                        if (!reacquired) {
                                reacquired = true;
-                               call_vb_qop(vb, buf_cleanup, vb);
+                               call_void_vb_qop(vb, buf_cleanup, vb);
                        }
-                       call_memop(vb, put_userptr, vb->planes[plane].mem_priv);
+                       call_void_memop(vb, put_userptr, vb->planes[plane].mem_priv);
                }
 
                vb->planes[plane].mem_priv = NULL;
                memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane));
 
                /* Acquire each plane's memory */
-               mem_priv = call_memop(vb, get_userptr, q->alloc_ctx[plane],
+               mem_priv = call_ptr_memop(vb, get_userptr, q->alloc_ctx[plane],
                                      planes[plane].m.userptr,
                                      planes[plane].length, write);
                if (IS_ERR_OR_NULL(mem_priv)) {
-                       dprintk(1, "qbuf: failed acquiring userspace "
+                       dprintk(1, "failed acquiring userspace "
                                                "memory for plane %d\n", plane);
-                       fail_memop(vb, get_userptr);
                        ret = mem_priv ? PTR_ERR(mem_priv) : -EINVAL;
                        goto err;
                }
@@ -1302,17 +1395,15 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
                 */
                ret = call_vb_qop(vb, buf_init, vb);
                if (ret) {
-                       dprintk(1, "qbuf: buffer initialization failed\n");
-                       fail_vb_qop(vb, buf_init);
+                       dprintk(1, "buffer initialization failed\n");
                        goto err;
                }
        }
 
        ret = call_vb_qop(vb, buf_prepare, vb);
        if (ret) {
-               dprintk(1, "qbuf: buffer preparation failed\n");
-               fail_vb_qop(vb, buf_prepare);
-               call_vb_qop(vb, buf_cleanup, vb);
+               dprintk(1, "buffer preparation failed\n");
+               call_void_vb_qop(vb, buf_cleanup, vb);
                goto err;
        }
 
@@ -1321,7 +1412,7 @@ err:
        /* In case of errors, release planes that were already acquired */
        for (plane = 0; plane < vb->num_planes; ++plane) {
                if (vb->planes[plane].mem_priv)
-                       call_memop(vb, put_userptr, vb->planes[plane].mem_priv);
+                       call_void_memop(vb, put_userptr, vb->planes[plane].mem_priv);
                vb->planes[plane].mem_priv = NULL;
                vb->v4l2_planes[plane].m.userptr = 0;
                vb->v4l2_planes[plane].length = 0;
@@ -1330,20 +1421,6 @@ err:
        return ret;
 }
 
-/**
- * __qbuf_mmap() - handle qbuf of an MMAP buffer
- */
-static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b)
-{
-       int ret;
-
-       __fill_vb2_buffer(vb, b, vb->v4l2_planes);
-       ret = call_vb_qop(vb, buf_prepare, vb);
-       if (ret)
-               fail_vb_qop(vb, buf_prepare);
-       return ret;
-}
-
 /**
  * __qbuf_dmabuf() - handle qbuf of a DMABUF buffer
  */
@@ -1357,6 +1434,7 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
        int write = !V4L2_TYPE_IS_OUTPUT(q->type);
        bool reacquired = vb->planes[0].mem_priv == NULL;
 
+       memset(planes, 0, sizeof(planes[0]) * vb->num_planes);
        /* Copy relevant information provided by the userspace */
        __fill_vb2_buffer(vb, b, planes);
 
@@ -1364,7 +1442,7 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
                struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd);
 
                if (IS_ERR_OR_NULL(dbuf)) {
-                       dprintk(1, "qbuf: invalid dmabuf fd for plane %d\n",
+                       dprintk(1, "invalid dmabuf fd for plane %d\n",
                                plane);
                        ret = -EINVAL;
                        goto err;
@@ -1374,9 +1452,8 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
                if (planes[plane].length == 0)
                        planes[plane].length = dbuf->size;
 
-               if (planes[plane].length < planes[plane].data_offset +
-                   q->plane_sizes[plane]) {
-                       dprintk(1, "qbuf: invalid dmabuf length for plane %d\n",
+               if (planes[plane].length < q->plane_sizes[plane]) {
+                       dprintk(1, "invalid dmabuf length for plane %d\n",
                                plane);
                        ret = -EINVAL;
                        goto err;
@@ -1389,11 +1466,11 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
                        continue;
                }
 
-               dprintk(1, "qbuf: buffer for plane %d changed\n", plane);
+               dprintk(1, "buffer for plane %d changed\n", plane);
 
                if (!reacquired) {
                        reacquired = true;
-                       call_vb_qop(vb, buf_cleanup, vb);
+                       call_void_vb_qop(vb, buf_cleanup, vb);
                }
 
                /* Release previously acquired memory if present */
@@ -1401,11 +1478,10 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
                memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane));
 
                /* Acquire each plane's memory */
-               mem_priv = call_memop(vb, attach_dmabuf, q->alloc_ctx[plane],
+               mem_priv = call_ptr_memop(vb, attach_dmabuf, q->alloc_ctx[plane],
                        dbuf, planes[plane].length, write);
                if (IS_ERR(mem_priv)) {
-                       dprintk(1, "qbuf: failed to attach dmabuf\n");
-                       fail_memop(vb, attach_dmabuf);
+                       dprintk(1, "failed to attach dmabuf\n");
                        ret = PTR_ERR(mem_priv);
                        dma_buf_put(dbuf);
                        goto err;
@@ -1422,9 +1498,8 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
        for (plane = 0; plane < vb->num_planes; ++plane) {
                ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv);
                if (ret) {
-                       dprintk(1, "qbuf: failed to map dmabuf for plane %d\n",
+                       dprintk(1, "failed to map dmabuf for plane %d\n",
                                plane);
-                       fail_memop(vb, map_dmabuf);
                        goto err;
                }
                vb->planes[plane].dbuf_mapped = 1;
@@ -1444,17 +1519,15 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
                 */
                ret = call_vb_qop(vb, buf_init, vb);
                if (ret) {
-                       dprintk(1, "qbuf: buffer initialization failed\n");
-                       fail_vb_qop(vb, buf_init);
+                       dprintk(1, "buffer initialization failed\n");
                        goto err;
                }
        }
 
        ret = call_vb_qop(vb, buf_prepare, vb);
        if (ret) {
-               dprintk(1, "qbuf: buffer preparation failed\n");
-               fail_vb_qop(vb, buf_prepare);
-               call_vb_qop(vb, buf_cleanup, vb);
+               dprintk(1, "buffer preparation failed\n");
+               call_void_vb_qop(vb, buf_cleanup, vb);
                goto err;
        }
 
@@ -1479,9 +1552,9 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
 
        /* sync buffers */
        for (plane = 0; plane < vb->num_planes; ++plane)
-               call_memop(vb, prepare, vb->planes[plane].mem_priv);
+               call_void_memop(vb, prepare, vb->planes[plane].mem_priv);
 
-       call_vb_qop(vb, buf_queue, vb);
+       call_void_vb_qop(vb, buf_queue, vb);
 }
 
 static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
@@ -1492,10 +1565,22 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 
        ret = __verify_length(vb, b);
        if (ret < 0) {
-               dprintk(1, "%s(): plane parameters verification failed: %d\n",
-                       __func__, ret);
+               dprintk(1, "plane parameters verification failed: %d\n", ret);
                return ret;
        }
+       if (b->field == V4L2_FIELD_ALTERNATE && V4L2_TYPE_IS_OUTPUT(q->type)) {
+               /*
+                * If the format's field is ALTERNATE, then the buffer's field
+                * should be either TOP or BOTTOM, not ALTERNATE since that
+                * makes no sense. The driver has to know whether the
+                * buffer represents a top or a bottom field in order to
+                * program any DMA correctly. Using ALTERNATE is wrong, since
+                * that just says that it is either a top or a bottom field,
+                * but not which of the two it is.
+                */
+               dprintk(1, "the field is incorrectly set to ALTERNATE for an output buffer\n");
+               return -EINVAL;
+       }
 
        vb->state = VB2_BUF_STATE_PREPARING;
        vb->v4l2_buf.timestamp.tv_sec = 0;
@@ -1520,9 +1605,9 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
                 * mmap_sem and then takes the driver's lock again.
                 */
                mmap_sem = &current->mm->mmap_sem;
-               call_qop(q, wait_prepare, q);
+               call_void_qop(q, wait_prepare, q);
                down_read(mmap_sem);
-               call_qop(q, wait_finish, q);
+               call_void_qop(q, wait_finish, q);
 
                ret = __qbuf_userptr(vb, b);
 
@@ -1537,7 +1622,7 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
        }
 
        if (ret)
-               dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
+               dprintk(1, "buffer preparation failed: %d\n", ret);
        vb->state = ret ? VB2_BUF_STATE_DEQUEUED : VB2_BUF_STATE_PREPARED;
 
        return ret;
@@ -1547,23 +1632,23 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
                                    const char *opname)
 {
        if (b->type != q->type) {
-               dprintk(1, "%s(): invalid buffer type\n", opname);
+               dprintk(1, "%s: invalid buffer type\n", opname);
                return -EINVAL;
        }
 
        if (b->index >= q->num_buffers) {
-               dprintk(1, "%s(): buffer index out of range\n", opname);
+               dprintk(1, "%s: buffer index out of range\n", opname);
                return -EINVAL;
        }
 
        if (q->bufs[b->index] == NULL) {
                /* Should never happen */
-               dprintk(1, "%s(): buffer is NULL\n", opname);
+               dprintk(1, "%s: buffer is NULL\n", opname);
                return -EINVAL;
        }
 
        if (b->memory != q->memory) {
-               dprintk(1, "%s(): invalid memory type\n", opname);
+               dprintk(1, "%s: invalid memory type\n", opname);
                return -EINVAL;
        }
 
@@ -1590,8 +1675,8 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
        struct vb2_buffer *vb;
        int ret;
 
-       if (q->fileio) {
-               dprintk(1, "%s(): file io in progress\n", __func__);
+       if (vb2_fileio_is_active(q)) {
+               dprintk(1, "file io in progress\n");
                return -EBUSY;
        }
 
@@ -1601,7 +1686,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
 
        vb = q->bufs[b->index];
        if (vb->state != VB2_BUF_STATE_DEQUEUED) {
-               dprintk(1, "%s(): invalid buffer state %d\n", __func__,
+               dprintk(1, "invalid buffer state %d\n",
                        vb->state);
                return -EINVAL;
        }
@@ -1611,7 +1696,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
                /* Fill buffer information for the userspace */
                __fill_v4l2_buffer(vb, b);
 
-               dprintk(1, "%s() of buffer %d succeeded\n", __func__, vb->v4l2_buf.index);
+               dprintk(1, "prepare of buffer %d succeeded\n", vb->v4l2_buf.index);
        }
        return ret;
 }
@@ -1647,8 +1732,7 @@ static int vb2_start_streaming(struct vb2_queue *q)
        if (!ret)
                return 0;
 
-       fail_qop(q, start_streaming);
-       dprintk(1, "qbuf: driver refused to start streaming\n");
+       dprintk(1, "driver refused to start streaming\n");
        if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
                unsigned i;
 
@@ -1686,11 +1770,10 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
        case VB2_BUF_STATE_PREPARED:
                break;
        case VB2_BUF_STATE_PREPARING:
-               dprintk(1, "qbuf: buffer still being prepared\n");
+               dprintk(1, "buffer still being prepared\n");
                return -EINVAL;
        default:
-               dprintk(1, "%s(): invalid buffer state %d\n", __func__,
-                       vb->state);
+               dprintk(1, "invalid buffer state %d\n", vb->state);
                return -EINVAL;
        }
 
@@ -1737,7 +1820,7 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
                        return ret;
        }
 
-       dprintk(1, "%s() of buffer %d succeeded\n", __func__, vb->v4l2_buf.index);
+       dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
        return 0;
 }
 
@@ -1760,8 +1843,8 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
  */
 int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
 {
-       if (q->fileio) {
-               dprintk(1, "%s(): file io in progress\n", __func__);
+       if (vb2_fileio_is_active(q)) {
+               dprintk(1, "file io in progress\n");
                return -EBUSY;
        }
 
@@ -1790,7 +1873,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
                int ret;
 
                if (!q->streaming) {
-                       dprintk(1, "Streaming off, will not wait for buffers\n");
+                       dprintk(1, "streaming off, will not wait for buffers\n");
                        return -EINVAL;
                }
 
@@ -1802,7 +1885,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
                }
 
                if (nonblocking) {
-                       dprintk(1, "Nonblocking and no buffers to dequeue, "
+                       dprintk(1, "nonblocking and no buffers to dequeue, "
                                                                "will not wait\n");
                        return -EAGAIN;
                }
@@ -1812,12 +1895,12 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
                 * become ready or for streamoff. Driver's lock is released to
                 * allow streamoff or qbuf to be called while waiting.
                 */
-               call_qop(q, wait_prepare, q);
+               call_void_qop(q, wait_prepare, q);
 
                /*
                 * All locks have been released, it is safe to sleep now.
                 */
-               dprintk(3, "Will sleep waiting for buffers\n");
+               dprintk(3, "will sleep waiting for buffers\n");
                ret = wait_event_interruptible(q->done_wq,
                                !list_empty(&q->done_list) || !q->streaming);
 
@@ -1825,9 +1908,9 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
                 * We need to reevaluate both conditions again after reacquiring
                 * the locks or return an error if one occurred.
                 */
-               call_qop(q, wait_finish, q);
+               call_void_qop(q, wait_finish, q);
                if (ret) {
-                       dprintk(1, "Sleep was interrupted\n");
+                       dprintk(1, "sleep was interrupted\n");
                        return ret;
                }
        }
@@ -1882,7 +1965,7 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
 int vb2_wait_for_all_buffers(struct vb2_queue *q)
 {
        if (!q->streaming) {
-               dprintk(1, "Streaming off, will not wait for buffers\n");
+               dprintk(1, "streaming off, will not wait for buffers\n");
                return -EINVAL;
        }
 
@@ -1911,7 +1994,7 @@ static void __vb2_dqbuf(struct vb2_buffer *vb)
                for (i = 0; i < vb->num_planes; ++i) {
                        if (!vb->planes[i].dbuf_mapped)
                                continue;
-                       call_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv);
+                       call_void_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv);
                        vb->planes[i].dbuf_mapped = 0;
                }
 }
@@ -1922,7 +2005,7 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
        int ret;
 
        if (b->type != q->type) {
-               dprintk(1, "dqbuf: invalid buffer type\n");
+               dprintk(1, "invalid buffer type\n");
                return -EINVAL;
        }
        ret = __vb2_get_done_vb(q, &vb, b, nonblocking);
@@ -1931,17 +2014,17 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
 
        switch (vb->state) {
        case VB2_BUF_STATE_DONE:
-               dprintk(3, "dqbuf: Returning done buffer\n");
+               dprintk(3, "returning done buffer\n");
                break;
        case VB2_BUF_STATE_ERROR:
-               dprintk(3, "dqbuf: Returning done buffer with errors\n");
+               dprintk(3, "returning done buffer with errors\n");
                break;
        default:
-               dprintk(1, "dqbuf: Invalid buffer state\n");
+               dprintk(1, "invalid buffer state\n");
                return -EINVAL;
        }
 
-       call_vb_qop(vb, buf_finish, vb);
+       call_void_vb_qop(vb, buf_finish, vb);
 
        /* Fill buffer information for the userspace */
        __fill_v4l2_buffer(vb, b);
@@ -1980,8 +2063,8 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n
  */
 int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
 {
-       if (q->fileio) {
-               dprintk(1, "dqbuf: file io in progress\n");
+       if (vb2_fileio_is_active(q)) {
+               dprintk(1, "file io in progress\n");
                return -EBUSY;
        }
        return vb2_internal_dqbuf(q, b, nonblocking);
@@ -2003,10 +2086,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
         * buffers.
         */
        if (q->start_streaming_called)
-               call_qop(q, stop_streaming, q);
-       q->streaming = 0;
-       q->start_streaming_called = 0;
-       q->queued_count = 0;
+               call_void_qop(q, stop_streaming, q);
 
        if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
                for (i = 0; i < q->num_buffers; ++i)
@@ -2016,6 +2096,10 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
                WARN_ON(atomic_read(&q->owned_by_drv_count));
        }
 
+       q->streaming = 0;
+       q->start_streaming_called = 0;
+       q->queued_count = 0;
+
        /*
         * Remove all buffers from videobuf's list...
         */
@@ -2042,7 +2126,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
 
                if (vb->state != VB2_BUF_STATE_DEQUEUED) {
                        vb->state = VB2_BUF_STATE_PREPARED;
-                       call_vb_qop(vb, buf_finish, vb);
+                       call_void_vb_qop(vb, buf_finish, vb);
                }
                __vb2_dqbuf(vb);
        }
@@ -2053,26 +2137,22 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
        int ret;
 
        if (type != q->type) {
-               dprintk(1, "streamon: invalid stream type\n");
+               dprintk(1, "invalid stream type\n");
                return -EINVAL;
        }
 
        if (q->streaming) {
-               dprintk(3, "streamon successful: already streaming\n");
+               dprintk(3, "already streaming\n");
                return 0;
        }
 
        if (!q->num_buffers) {
-               dprintk(1, "streamon: no buffers have been allocated\n");
+               dprintk(1, "no buffers have been allocated\n");
                return -EINVAL;
        }
 
-       if (!q->num_buffers) {
-               dprintk(1, "streamon: no buffers have been allocated\n");
-               return -EINVAL;
-       }
        if (q->num_buffers < q->min_buffers_needed) {
-               dprintk(1, "streamon: need at least %u allocated buffers\n",
+               dprintk(1, "need at least %u allocated buffers\n",
                                q->min_buffers_needed);
                return -EINVAL;
        }
@@ -2091,7 +2171,7 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
 
        q->streaming = 1;
 
-       dprintk(3, "Streamon successful\n");
+       dprintk(3, "successful\n");
        return 0;
 }
 
@@ -2110,8 +2190,8 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
  */
 int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
 {
-       if (q->fileio) {
-               dprintk(1, "streamon: file io in progress\n");
+       if (vb2_fileio_is_active(q)) {
+               dprintk(1, "file io in progress\n");
                return -EBUSY;
        }
        return vb2_internal_streamon(q, type);
@@ -2121,7 +2201,7 @@ EXPORT_SYMBOL_GPL(vb2_streamon);
 static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
 {
        if (type != q->type) {
-               dprintk(1, "streamoff: invalid stream type\n");
+               dprintk(1, "invalid stream type\n");
                return -EINVAL;
        }
 
@@ -2136,7 +2216,7 @@ static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
         */
        __vb2_queue_cancel(q);
 
-       dprintk(3, "Streamoff successful\n");
+       dprintk(3, "successful\n");
        return 0;
 }
 
@@ -2157,8 +2237,8 @@ static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
  */
 int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
 {
-       if (q->fileio) {
-               dprintk(1, "streamoff: file io in progress\n");
+       if (vb2_fileio_is_active(q)) {
+               dprintk(1, "file io in progress\n");
                return -EBUSY;
        }
        return vb2_internal_streamoff(q, type);
@@ -2211,22 +2291,22 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb)
        struct dma_buf *dbuf;
 
        if (q->memory != V4L2_MEMORY_MMAP) {
-               dprintk(1, "Queue is not currently set up for mmap\n");
+               dprintk(1, "queue is not currently set up for mmap\n");
                return -EINVAL;
        }
 
        if (!q->mem_ops->get_dmabuf) {
-               dprintk(1, "Queue does not support DMA buffer exporting\n");
+               dprintk(1, "queue does not support DMA buffer exporting\n");
                return -EINVAL;
        }
 
        if (eb->flags & ~(O_CLOEXEC | O_ACCMODE)) {
-               dprintk(1, "Queue does support only O_CLOEXEC and access mode flags\n");
+               dprintk(1, "queue does support only O_CLOEXEC and access mode flags\n");
                return -EINVAL;
        }
 
        if (eb->type != q->type) {
-               dprintk(1, "qbuf: invalid buffer type\n");
+               dprintk(1, "invalid buffer type\n");
                return -EINVAL;
        }
 
@@ -2242,13 +2322,17 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb)
                return -EINVAL;
        }
 
+       if (vb2_fileio_is_active(q)) {
+               dprintk(1, "expbuf: file io in progress\n");
+               return -EBUSY;
+       }
+
        vb_plane = &vb->planes[eb->plane];
 
-       dbuf = call_memop(vb, get_dmabuf, vb_plane->mem_priv, eb->flags & O_ACCMODE);
+       dbuf = call_ptr_memop(vb, get_dmabuf, vb_plane->mem_priv, eb->flags & O_ACCMODE);
        if (IS_ERR_OR_NULL(dbuf)) {
-               dprintk(1, "Failed to export buffer %d, plane %d\n",
+               dprintk(1, "failed to export buffer %d, plane %d\n",
                        eb->index, eb->plane);
-               fail_memop(vb, get_dmabuf);
                return -EINVAL;
        }
 
@@ -2291,12 +2375,12 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
 {
        unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
        struct vb2_buffer *vb;
-       unsigned int buffer, plane;
+       unsigned int buffer = 0, plane = 0;
        int ret;
        unsigned long length;
 
        if (q->memory != V4L2_MEMORY_MMAP) {
-               dprintk(1, "Queue is not currently set up for mmap\n");
+               dprintk(1, "queue is not currently set up for mmap\n");
                return -EINVAL;
        }
 
@@ -2304,20 +2388,24 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
         * Check memory area access mode.
         */
        if (!(vma->vm_flags & VM_SHARED)) {
-               dprintk(1, "Invalid vma flags, VM_SHARED needed\n");
+               dprintk(1, "invalid vma flags, VM_SHARED needed\n");
                return -EINVAL;
        }
        if (V4L2_TYPE_IS_OUTPUT(q->type)) {
                if (!(vma->vm_flags & VM_WRITE)) {
-                       dprintk(1, "Invalid vma flags, VM_WRITE needed\n");
+                       dprintk(1, "invalid vma flags, VM_WRITE needed\n");
                        return -EINVAL;
                }
        } else {
                if (!(vma->vm_flags & VM_READ)) {
-                       dprintk(1, "Invalid vma flags, VM_READ needed\n");
+                       dprintk(1, "invalid vma flags, VM_READ needed\n");
                        return -EINVAL;
                }
        }
+       if (vb2_fileio_is_active(q)) {
+               dprintk(1, "mmap: file io in progress\n");
+               return -EBUSY;
+       }
 
        /*
         * Find the plane corresponding to the offset passed by userspace.
@@ -2341,12 +2429,10 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
        }
 
        ret = call_memop(vb, mmap, vb->planes[plane].mem_priv, vma);
-       if (ret) {
-               fail_memop(vb, mmap);
+       if (ret)
                return ret;
-       }
 
-       dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
+       dprintk(3, "buffer %d, plane %d successfully mapped\n", buffer, plane);
        return 0;
 }
 EXPORT_SYMBOL_GPL(vb2_mmap);
@@ -2364,7 +2450,7 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
        int ret;
 
        if (q->memory != V4L2_MEMORY_MMAP) {
-               dprintk(1, "Queue is not currently set up for mmap\n");
+               dprintk(1, "queue is not currently set up for mmap\n");
                return -EINVAL;
        }
 
@@ -2429,7 +2515,7 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
        /*
         * Start file I/O emulator only if streaming API has not been used yet.
         */
-       if (q->num_buffers == 0 && q->fileio == NULL) {
+       if (q->num_buffers == 0 && !vb2_fileio_is_active(q)) {
                if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) &&
                                (req_events & (POLLIN | POLLRDNORM))) {
                        if (__vb2_init_fileio(q, 1))
@@ -2574,6 +2660,7 @@ struct vb2_fileio_buf {
  */
 struct vb2_fileio_data {
        struct v4l2_requestbuffers req;
+       struct v4l2_plane p;
        struct v4l2_buffer b;
        struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME];
        unsigned int cur_index;
@@ -2634,7 +2721,8 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
        fileio->req.count = count;
        fileio->req.memory = V4L2_MEMORY_MMAP;
        fileio->req.type = q->type;
-       ret = vb2_reqbufs(q, &fileio->req);
+       q->fileio = fileio;
+       ret = __reqbufs(q, &fileio->req);
        if (ret)
                goto err_kfree;
 
@@ -2663,16 +2751,24 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
         * Read mode requires pre queuing of all buffers.
         */
        if (read) {
+               bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(q->type);
+
                /*
                 * Queue all buffers.
                 */
                for (i = 0; i < q->num_buffers; i++) {
                        struct v4l2_buffer *b = &fileio->b;
+
                        memset(b, 0, sizeof(*b));
                        b->type = q->type;
+                       if (is_multiplanar) {
+                               memset(&fileio->p, 0, sizeof(fileio->p));
+                               b->m.planes = &fileio->p;
+                               b->length = 1;
+                       }
                        b->memory = q->memory;
                        b->index = i;
-                       ret = vb2_qbuf(q, b);
+                       ret = vb2_internal_qbuf(q, b);
                        if (ret)
                                goto err_reqbufs;
                        fileio->bufs[i].queued = 1;
@@ -2688,19 +2784,18 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
        /*
         * Start streaming.
         */
-       ret = vb2_streamon(q, q->type);
+       ret = vb2_internal_streamon(q, q->type);
        if (ret)
                goto err_reqbufs;
 
-       q->fileio = fileio;
-
        return ret;
 
 err_reqbufs:
        fileio->req.count = 0;
-       vb2_reqbufs(q, &fileio->req);
+       __reqbufs(q, &fileio->req);
 
 err_kfree:
+       q->fileio = NULL;
        kfree(fileio);
        return ret;
 }
@@ -2738,9 +2833,18 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
 {
        struct vb2_fileio_data *fileio;
        struct vb2_fileio_buf *buf;
+       bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(q->type);
+       /*
+        * When using write() to write data to an output video node the vb2 core
+        * should set timestamps if V4L2_BUF_FLAG_TIMESTAMP_COPY is set. Nobody
+        * else is able to provide this information with the write() operation.
+        */
+       bool set_timestamp = !read &&
+               (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) ==
+               V4L2_BUF_FLAG_TIMESTAMP_COPY;
        int ret, index;
 
-       dprintk(3, "file io: mode %s, offset %ld, count %zd, %sblocking\n",
+       dprintk(3, "mode %s, offset %ld, count %zd, %sblocking\n",
                read ? "read" : "write", (long)*ppos, count,
                nonblock ? "non" : "");
 
@@ -2750,9 +2854,9 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
        /*
         * Initialize emulator on first call.
         */
-       if (!q->fileio) {
+       if (!vb2_fileio_is_active(q)) {
                ret = __vb2_init_fileio(q, read);
-               dprintk(3, "file io: vb2_init_fileio result: %d\n", ret);
+               dprintk(3, "vb2_init_fileio result: %d\n", ret);
                if (ret)
                        return ret;
        }
@@ -2769,8 +2873,13 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
                memset(&fileio->b, 0, sizeof(fileio->b));
                fileio->b.type = q->type;
                fileio->b.memory = q->memory;
+               if (is_multiplanar) {
+                       memset(&fileio->p, 0, sizeof(fileio->p));
+                       fileio->b.m.planes = &fileio->p;
+                       fileio->b.length = 1;
+               }
                ret = vb2_internal_dqbuf(q, &fileio->b, nonblock);
-               dprintk(5, "file io: vb2_dqbuf result: %d\n", ret);
+               dprintk(5, "vb2_dqbuf result: %d\n", ret);
                if (ret)
                        return ret;
                fileio->dq_count += 1;
@@ -2800,14 +2909,14 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
        /*
         * Transfer data to userspace.
         */
-       dprintk(3, "file io: copying %zd bytes - buffer %d, offset %u\n",
+       dprintk(3, "copying %zd bytes - buffer %d, offset %u\n",
                count, index, buf->pos);
        if (read)
                ret = copy_to_user(data, buf->vaddr + buf->pos, count);
        else
                ret = copy_from_user(buf->vaddr + buf->pos, data, count);
        if (ret) {
-               dprintk(3, "file io: error copying data\n");
+               dprintk(3, "error copying data\n");
                return -EFAULT;
        }
 
@@ -2827,7 +2936,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
                 */
                if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) &&
                    fileio->dq_count == 1) {
-                       dprintk(3, "file io: read limit reached\n");
+                       dprintk(3, "read limit reached\n");
                        return __vb2_cleanup_fileio(q);
                }
 
@@ -2839,8 +2948,16 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
                fileio->b.memory = q->memory;
                fileio->b.index = index;
                fileio->b.bytesused = buf->pos;
+               if (is_multiplanar) {
+                       memset(&fileio->p, 0, sizeof(fileio->p));
+                       fileio->p.bytesused = buf->pos;
+                       fileio->b.m.planes = &fileio->p;
+                       fileio->b.length = 1;
+               }
+               if (set_timestamp)
+                       v4l2_get_timestamp(&fileio->b.timestamp);
                ret = vb2_internal_qbuf(q, &fileio->b);
-               dprintk(5, "file io: vb2_dbuf result: %d\n", ret);
+               dprintk(5, "vb2_dbuf result: %d\n", ret);
                if (ret)
                        return ret;
 
@@ -2890,6 +3007,147 @@ size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
 }
 EXPORT_SYMBOL_GPL(vb2_write);
 
+struct vb2_threadio_data {
+       struct task_struct *thread;
+       vb2_thread_fnc fnc;
+       void *priv;
+       bool stop;
+};
+
+static int vb2_thread(void *data)
+{
+       struct vb2_queue *q = data;
+       struct vb2_threadio_data *threadio = q->threadio;
+       struct vb2_fileio_data *fileio = q->fileio;
+       bool set_timestamp = false;
+       int prequeue = 0;
+       int index = 0;
+       int ret = 0;
+
+       if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+               prequeue = q->num_buffers;
+               set_timestamp =
+                       (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) ==
+                       V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       }
+
+       set_freezable();
+
+       for (;;) {
+               struct vb2_buffer *vb;
+
+               /*
+                * Call vb2_dqbuf to get buffer back.
+                */
+               memset(&fileio->b, 0, sizeof(fileio->b));
+               fileio->b.type = q->type;
+               fileio->b.memory = q->memory;
+               if (prequeue) {
+                       fileio->b.index = index++;
+                       prequeue--;
+               } else {
+                       call_void_qop(q, wait_finish, q);
+                       ret = vb2_internal_dqbuf(q, &fileio->b, 0);
+                       call_void_qop(q, wait_prepare, q);
+                       dprintk(5, "file io: vb2_dqbuf result: %d\n", ret);
+               }
+               if (threadio->stop)
+                       break;
+               if (ret)
+                       break;
+               try_to_freeze();
+
+               vb = q->bufs[fileio->b.index];
+               if (!(fileio->b.flags & V4L2_BUF_FLAG_ERROR))
+                       ret = threadio->fnc(vb, threadio->priv);
+               if (ret)
+                       break;
+               call_void_qop(q, wait_finish, q);
+               if (set_timestamp)
+                       v4l2_get_timestamp(&fileio->b.timestamp);
+               ret = vb2_internal_qbuf(q, &fileio->b);
+               call_void_qop(q, wait_prepare, q);
+               if (ret)
+                       break;
+       }
+
+       /* Hmm, linux becomes *very* unhappy without this ... */
+       while (!kthread_should_stop()) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule();
+       }
+       return 0;
+}
+
+/*
+ * This function should not be used for anything else but the videobuf2-dvb
+ * support. If you think you have another good use-case for this, then please
+ * contact the linux-media mailinglist first.
+ */
+int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv,
+                    const char *thread_name)
+{
+       struct vb2_threadio_data *threadio;
+       int ret = 0;
+
+       if (q->threadio)
+               return -EBUSY;
+       if (vb2_is_busy(q))
+               return -EBUSY;
+       if (WARN_ON(q->fileio))
+               return -EBUSY;
+
+       threadio = kzalloc(sizeof(*threadio), GFP_KERNEL);
+       if (threadio == NULL)
+               return -ENOMEM;
+       threadio->fnc = fnc;
+       threadio->priv = priv;
+
+       ret = __vb2_init_fileio(q, !V4L2_TYPE_IS_OUTPUT(q->type));
+       dprintk(3, "file io: vb2_init_fileio result: %d\n", ret);
+       if (ret)
+               goto nomem;
+       q->threadio = threadio;
+       threadio->thread = kthread_run(vb2_thread, q, "vb2-%s", thread_name);
+       if (IS_ERR(threadio->thread)) {
+               ret = PTR_ERR(threadio->thread);
+               threadio->thread = NULL;
+               goto nothread;
+       }
+       return 0;
+
+nothread:
+       __vb2_cleanup_fileio(q);
+nomem:
+       kfree(threadio);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_thread_start);
+
+int vb2_thread_stop(struct vb2_queue *q)
+{
+       struct vb2_threadio_data *threadio = q->threadio;
+       struct vb2_fileio_data *fileio = q->fileio;
+       int err;
+
+       if (threadio == NULL)
+               return 0;
+       call_void_qop(q, wait_finish, q);
+       threadio->stop = true;
+       vb2_internal_streamoff(q, q->type);
+       call_void_qop(q, wait_prepare, q);
+       q->fileio = NULL;
+       fileio->req.count = 0;
+       vb2_reqbufs(q, &fileio->req);
+       kfree(fileio);
+       err = kthread_stop(threadio->thread);
+       threadio->thread = NULL;
+       kfree(threadio);
+       q->fileio = NULL;
+       q->threadio = NULL;
+       return err;
+}
+EXPORT_SYMBOL_GPL(vb2_thread_stop);
 
 /*
  * The following functions are not part of the vb2 core API, but are helper
@@ -3116,7 +3374,7 @@ unsigned int vb2_fop_poll(struct file *file, poll_table *wait)
 
        /* Try to be smart: only lock if polling might start fileio,
           otherwise locking will only introduce unwanted delays. */
-       if (q->num_buffers == 0 && q->fileio == NULL) {
+       if (q->num_buffers == 0 && !vb2_fileio_is_active(q)) {
                if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) &&
                                (req_events & (POLLIN | POLLRDNORM)))
                        must_lock = true;
index c779f21..adefc31 100644 (file)
@@ -211,7 +211,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
                     ++num_pages_from_user, vaddr += PAGE_SIZE) {
                        unsigned long pfn;
 
-                       if (follow_pfn(buf->vma, vaddr, &pfn)) {
+                       if (follow_pfn(vma, vaddr, &pfn)) {
                                dprintk(1, "no page for address %lu\n", vaddr);
                                break;
                        }
diff --git a/drivers/media/v4l2-core/videobuf2-dvb.c b/drivers/media/v4l2-core/videobuf2-dvb.c
new file mode 100644 (file)
index 0000000..d092698
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ *
+ * some helper function for simple DVB cards which simply DMA the
+ * complete transport stream and let the computer sort everything else
+ * (i.e. we are using the software demux, ...).  Also uses the
+ * video-buf to manage DMA buffers.
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ *
+ * 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/device.h>
+#include <linux/slab.h>
+
+#include <media/videobuf2-dvb.h>
+
+/* ------------------------------------------------------------------ */
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static int dvb_fnc(struct vb2_buffer *vb, void *priv)
+{
+       struct vb2_dvb *dvb = priv;
+
+       dvb_dmx_swfilter(&dvb->demux, vb2_plane_vaddr(vb, 0),
+                                     vb2_get_plane_payload(vb, 0));
+       return 0;
+}
+
+static int vb2_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+       struct dvb_demux *demux = feed->demux;
+       struct vb2_dvb *dvb = demux->priv;
+       int rc = 0;
+
+       if (!demux->dmx.frontend)
+               return -EINVAL;
+
+       mutex_lock(&dvb->lock);
+       dvb->nfeeds++;
+
+       if (!dvb->dvbq.threadio) {
+               rc = vb2_thread_start(&dvb->dvbq, dvb_fnc, dvb, dvb->name);
+               if (rc)
+                       dvb->nfeeds--;
+       }
+       if (!rc)
+               rc = dvb->nfeeds;
+       mutex_unlock(&dvb->lock);
+       return rc;
+}
+
+static int vb2_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+       struct dvb_demux *demux = feed->demux;
+       struct vb2_dvb *dvb = demux->priv;
+       int err = 0;
+
+       mutex_lock(&dvb->lock);
+       dvb->nfeeds--;
+       if (0 == dvb->nfeeds)
+               err = vb2_thread_stop(&dvb->dvbq);
+       mutex_unlock(&dvb->lock);
+       return err;
+}
+
+static int vb2_dvb_register_adapter(struct vb2_dvb_frontends *fe,
+                         struct module *module,
+                         void *adapter_priv,
+                         struct device *device,
+                         char *adapter_name,
+                         short *adapter_nr,
+                         int mfe_shared)
+{
+       int result;
+
+       mutex_init(&fe->lock);
+
+       /* register adapter */
+       result = dvb_register_adapter(&fe->adapter, adapter_name, module,
+               device, adapter_nr);
+       if (result < 0) {
+               pr_warn("%s: dvb_register_adapter failed (errno = %d)\n",
+                      adapter_name, result);
+       }
+       fe->adapter.priv = adapter_priv;
+       fe->adapter.mfe_shared = mfe_shared;
+
+       return result;
+}
+
+static int vb2_dvb_register_frontend(struct dvb_adapter *adapter,
+       struct vb2_dvb *dvb)
+{
+       int result;
+
+       /* register frontend */
+       result = dvb_register_frontend(adapter, dvb->frontend);
+       if (result < 0) {
+               pr_warn("%s: dvb_register_frontend failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_frontend;
+       }
+
+       /* register demux stuff */
+       dvb->demux.dmx.capabilities =
+               DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+               DMX_MEMORY_BASED_FILTERING;
+       dvb->demux.priv       = dvb;
+       dvb->demux.filternum  = 256;
+       dvb->demux.feednum    = 256;
+       dvb->demux.start_feed = vb2_dvb_start_feed;
+       dvb->demux.stop_feed  = vb2_dvb_stop_feed;
+       result = dvb_dmx_init(&dvb->demux);
+       if (result < 0) {
+               pr_warn("%s: dvb_dmx_init failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_dmx;
+       }
+
+       dvb->dmxdev.filternum    = 256;
+       dvb->dmxdev.demux        = &dvb->demux.dmx;
+       dvb->dmxdev.capabilities = 0;
+       result = dvb_dmxdev_init(&dvb->dmxdev, adapter);
+
+       if (result < 0) {
+               pr_warn("%s: dvb_dmxdev_init failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_dmxdev;
+       }
+
+       dvb->fe_hw.source = DMX_FRONTEND_0;
+       result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+       if (result < 0) {
+               pr_warn("%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
+                      dvb->name, result);
+               goto fail_fe_hw;
+       }
+
+       dvb->fe_mem.source = DMX_MEMORY_FE;
+       result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+       if (result < 0) {
+               pr_warn("%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
+                      dvb->name, result);
+               goto fail_fe_mem;
+       }
+
+       result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+       if (result < 0) {
+               pr_warn("%s: connect_frontend failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_fe_conn;
+       }
+
+       /* register network adapter */
+       result = dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx);
+       if (result < 0) {
+               pr_warn("%s: dvb_net_init failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_fe_conn;
+       }
+       return 0;
+
+fail_fe_conn:
+       dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+fail_fe_mem:
+       dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+fail_fe_hw:
+       dvb_dmxdev_release(&dvb->dmxdev);
+fail_dmxdev:
+       dvb_dmx_release(&dvb->demux);
+fail_dmx:
+       dvb_unregister_frontend(dvb->frontend);
+fail_frontend:
+       dvb_frontend_detach(dvb->frontend);
+       dvb->frontend = NULL;
+
+       return result;
+}
+
+/* ------------------------------------------------------------------ */
+/* Register a single adapter and one or more frontends */
+int vb2_dvb_register_bus(struct vb2_dvb_frontends *f,
+                        struct module *module,
+                        void *adapter_priv,
+                        struct device *device,
+                        short *adapter_nr,
+                        int mfe_shared)
+{
+       struct list_head *list, *q;
+       struct vb2_dvb_frontend *fe;
+       int res;
+
+       fe = vb2_dvb_get_frontend(f, 1);
+       if (!fe) {
+               pr_warn("Unable to register the adapter which has no frontends\n");
+               return -EINVAL;
+       }
+
+       /* Bring up the adapter */
+       res = vb2_dvb_register_adapter(f, module, adapter_priv, device,
+               fe->dvb.name, adapter_nr, mfe_shared);
+       if (res < 0) {
+               pr_warn("vb2_dvb_register_adapter failed (errno = %d)\n", res);
+               return res;
+       }
+
+       /* Attach all of the frontends to the adapter */
+       mutex_lock(&f->lock);
+       list_for_each_safe(list, q, &f->felist) {
+               fe = list_entry(list, struct vb2_dvb_frontend, felist);
+               res = vb2_dvb_register_frontend(&f->adapter, &fe->dvb);
+               if (res < 0) {
+                       pr_warn("%s: vb2_dvb_register_frontend failed (errno = %d)\n",
+                               fe->dvb.name, res);
+                       goto err;
+               }
+       }
+       mutex_unlock(&f->lock);
+       return 0;
+
+err:
+       mutex_unlock(&f->lock);
+       vb2_dvb_unregister_bus(f);
+       return res;
+}
+EXPORT_SYMBOL(vb2_dvb_register_bus);
+
+void vb2_dvb_unregister_bus(struct vb2_dvb_frontends *f)
+{
+       vb2_dvb_dealloc_frontends(f);
+
+       dvb_unregister_adapter(&f->adapter);
+}
+EXPORT_SYMBOL(vb2_dvb_unregister_bus);
+
+struct vb2_dvb_frontend *vb2_dvb_get_frontend(
+       struct vb2_dvb_frontends *f, int id)
+{
+       struct list_head *list, *q;
+       struct vb2_dvb_frontend *fe, *ret = NULL;
+
+       mutex_lock(&f->lock);
+
+       list_for_each_safe(list, q, &f->felist) {
+               fe = list_entry(list, struct vb2_dvb_frontend, felist);
+               if (fe->id == id) {
+                       ret = fe;
+                       break;
+               }
+       }
+
+       mutex_unlock(&f->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(vb2_dvb_get_frontend);
+
+int vb2_dvb_find_frontend(struct vb2_dvb_frontends *f,
+       struct dvb_frontend *p)
+{
+       struct list_head *list, *q;
+       struct vb2_dvb_frontend *fe = NULL;
+       int ret = 0;
+
+       mutex_lock(&f->lock);
+
+       list_for_each_safe(list, q, &f->felist) {
+               fe = list_entry(list, struct vb2_dvb_frontend, felist);
+               if (fe->dvb.frontend == p) {
+                       ret = fe->id;
+                       break;
+               }
+       }
+
+       mutex_unlock(&f->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(vb2_dvb_find_frontend);
+
+struct vb2_dvb_frontend *vb2_dvb_alloc_frontend(
+       struct vb2_dvb_frontends *f, int id)
+{
+       struct vb2_dvb_frontend *fe;
+
+       fe = kzalloc(sizeof(struct vb2_dvb_frontend), GFP_KERNEL);
+       if (fe == NULL)
+               return NULL;
+
+       fe->id = id;
+       mutex_init(&fe->dvb.lock);
+
+       mutex_lock(&f->lock);
+       list_add_tail(&fe->felist, &f->felist);
+       mutex_unlock(&f->lock);
+       return fe;
+}
+EXPORT_SYMBOL(vb2_dvb_alloc_frontend);
+
+void vb2_dvb_dealloc_frontends(struct vb2_dvb_frontends *f)
+{
+       struct list_head *list, *q;
+       struct vb2_dvb_frontend *fe;
+
+       mutex_lock(&f->lock);
+       list_for_each_safe(list, q, &f->felist) {
+               fe = list_entry(list, struct vb2_dvb_frontend, felist);
+               if (fe->dvb.net.dvbdev) {
+                       dvb_net_release(&fe->dvb.net);
+                       fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
+                               &fe->dvb.fe_mem);
+                       fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
+                               &fe->dvb.fe_hw);
+                       dvb_dmxdev_release(&fe->dvb.dmxdev);
+                       dvb_dmx_release(&fe->dvb.demux);
+                       dvb_unregister_frontend(fe->dvb.frontend);
+               }
+               if (fe->dvb.frontend)
+                       /* always allocated, may have been reset */
+                       dvb_frontend_detach(fe->dvb.frontend);
+               list_del(list); /* remove list entry */
+               kfree(fe);      /* free frontend allocation */
+       }
+       mutex_unlock(&f->lock);
+}
+EXPORT_SYMBOL(vb2_dvb_dealloc_frontends);
index 7694e07..b11fdd6 100644 (file)
@@ -1734,18 +1734,17 @@ static struct cpufreq_frequency_table db8500_cpufreq_table[] = {
 
 static long round_armss_rate(unsigned long rate)
 {
+       struct cpufreq_frequency_table *pos;
        long freq = 0;
-       int i = 0;
 
        /* cpufreq table frequencies is in KHz. */
        rate = rate / 1000;
 
        /* Find the corresponding arm opp from the cpufreq table. */
-       while (db8500_cpufreq_table[i].frequency != CPUFREQ_TABLE_END) {
-               freq = db8500_cpufreq_table[i].frequency;
+       cpufreq_for_each_entry(pos, db8500_cpufreq_table) {
+               freq = pos->frequency;
                if (freq == rate)
                        break;
-               i++;
        }
 
        /* Return the last valid value, even if a match was not found. */
@@ -1886,23 +1885,21 @@ static void set_clock_rate(u8 clock, unsigned long rate)
 
 static int set_armss_rate(unsigned long rate)
 {
-       int i = 0;
+       struct cpufreq_frequency_table *pos;
 
        /* cpufreq table frequencies is in KHz. */
        rate = rate / 1000;
 
        /* Find the corresponding arm opp from the cpufreq table. */
-       while (db8500_cpufreq_table[i].frequency != CPUFREQ_TABLE_END) {
-               if (db8500_cpufreq_table[i].frequency == rate)
+       cpufreq_for_each_entry(pos, db8500_cpufreq_table)
+               if (pos->frequency == rate)
                        break;
-               i++;
-       }
 
-       if (db8500_cpufreq_table[i].frequency != rate)
+       if (pos->frequency != rate)
                return -EINVAL;
 
        /* Set the new arm opp. */
-       return db8500_prcmu_set_arm_opp(db8500_cpufreq_table[i].driver_data);
+       return db8500_prcmu_set_arm_opp(pos->driver_data);
 }
 
 static int set_plldsi_rate(unsigned long rate)
index 06e64b6..0c6c21c 100644 (file)
@@ -673,9 +673,13 @@ int mc13xxx_common_init(struct device *dev)
        if (mc13xxx->flags & MC13XXX_USE_ADC)
                mc13xxx_add_subdevice(mc13xxx, "%s-adc");
 
-       if (mc13xxx->flags & MC13XXX_USE_CODEC)
-               mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec",
-                                       pdata->codec, sizeof(*pdata->codec));
+       if (mc13xxx->flags & MC13XXX_USE_CODEC) {
+               if (pdata)
+                       mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec",
+                               pdata->codec, sizeof(*pdata->codec));
+               else
+                       mc13xxx_add_subdevice(mc13xxx, "%s-codec");
+       }
 
        if (mc13xxx->flags & MC13XXX_USE_RTC)
                mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
index cadf52e..e3fe9a2 100644 (file)
@@ -217,21 +217,17 @@ crc_init_out:
 static u32 sh_sir_find_sclk(struct clk *irda_clk)
 {
        struct cpufreq_frequency_table *freq_table = irda_clk->freq_table;
+       struct cpufreq_frequency_table *pos;
        struct clk *pclk = clk_get(NULL, "peripheral_clk");
        u32 limit, min = 0xffffffff, tmp;
-       int i, index = 0;
+       int index = 0;
 
        limit = clk_get_rate(pclk);
        clk_put(pclk);
 
        /* IrDA can not set over peripheral_clk */
-       for (i = 0;
-            freq_table[i].frequency != CPUFREQ_TABLE_END;
-            i++) {
-               u32 freq = freq_table[i].frequency;
-
-               if (freq == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       cpufreq_for_each_valid_entry(pos, freq_table) {
+               u32 freq = pos->frequency;
 
                /* IrDA should not over peripheral_clk */
                if (freq > limit)
@@ -240,7 +236,7 @@ static u32 sh_sir_find_sclk(struct clk *irda_clk)
                tmp = freq % SCLK_BASE;
                if (tmp < min) {
                        min = tmp;
-                       index = i;
+                       index = pos - freq_table;
                }
        }
 
index 4fcc96a..f51d5ca 100644 (file)
@@ -1265,7 +1265,7 @@ struct wmi_resource_config {
         */
        __le32 rx_decap_mode;
 
-       /* what is the maximum scan requests than can be queued */
+       /* what is the maximum number of scan requests that can be queued */
        __le32 scan_max_pending_reqs;
 
        /* maximum VDEV that could use BMISS offload */
@@ -1450,7 +1450,7 @@ struct wmi_resource_config_10x {
         */
        __le32 rx_decap_mode;
 
-       /* what is the maximum scan requests than can be queued */
+       /* what is the maximum number of scan requests that can be queued */
        __le32 scan_max_pending_reqs;
 
        /* maximum VDEV that could use BMISS offload */
index b5f2265..5c702ae 100644 (file)
@@ -1068,7 +1068,7 @@ struct wmi_power_mode_cmd {
 } __packed;
 
 /*
- * Policy to determnine whether power save failure event should be sent to
+ * Policy to determine whether power save failure event should be sent to
  * host during scanning
  */
 enum power_save_fail_event_policy {
index 889005f..2dcb054 100644 (file)
@@ -20,6 +20,7 @@ config OF_SELFTEST
 config OF_FLATTREE
        bool
        select DTC
+       select LIBFDT
 
 config OF_EARLY_FLATTREE
        bool
index ed9660a..099b1fb 100644 (file)
@@ -1,5 +1,6 @@
 obj-y = base.o device.o platform.o
 obj-$(CONFIG_OF_FLATTREE) += fdt.o
+obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
 obj-$(CONFIG_OF_PROMTREE) += pdt.o
 obj-$(CONFIG_OF_ADDRESS)  += address.o
 obj-$(CONFIG_OF_IRQ)    += irq.o
@@ -10,3 +11,6 @@ obj-$(CONFIG_OF_PCI)  += of_pci.o
 obj-$(CONFIG_OF_PCI_IRQ)  += of_pci_irq.o
 obj-$(CONFIG_OF_MTD)   += of_mtd.o
 obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
+
+CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
+CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt
index cb4242a..95351b2 100644 (file)
@@ -498,8 +498,7 @@ static u64 __of_translate_address(struct device_node *dev,
        /* Count address cells & copy address locally */
        bus->count_cells(dev, &na, &ns);
        if (!OF_CHECK_COUNTS(na, ns)) {
-               printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
-                      of_node_full_name(dev));
+               pr_debug("OF: Bad cell count for %s\n", of_node_full_name(dev));
                goto bail;
        }
        memcpy(addr, in_addr, na * 4);
@@ -564,25 +563,6 @@ u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr)
 }
 EXPORT_SYMBOL(of_translate_dma_address);
 
-bool of_can_translate_address(struct device_node *dev)
-{
-       struct device_node *parent;
-       struct of_bus *bus;
-       int na, ns;
-
-       parent = of_get_parent(dev);
-       if (parent == NULL)
-               return false;
-
-       bus = of_match_bus(parent);
-       bus->count_cells(dev, &na, &ns);
-
-       of_node_put(parent);
-
-       return OF_CHECK_COUNTS(na, ns);
-}
-EXPORT_SYMBOL(of_can_translate_address);
-
 const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
                    unsigned int *flags)
 {
index 32e969d..8368d96 100644 (file)
@@ -695,6 +695,25 @@ struct device_node *of_get_next_parent(struct device_node *node)
 }
 EXPORT_SYMBOL(of_get_next_parent);
 
+static struct device_node *__of_get_next_child(const struct device_node *node,
+                                               struct device_node *prev)
+{
+       struct device_node *next;
+
+       if (!node)
+               return NULL;
+
+       next = prev ? prev->sibling : node->child;
+       for (; next; next = next->sibling)
+               if (of_node_get(next))
+                       break;
+       of_node_put(prev);
+       return next;
+}
+#define __for_each_child_of_node(parent, child) \
+       for (child = __of_get_next_child(parent, NULL); child != NULL; \
+            child = __of_get_next_child(parent, child))
+
 /**
  *     of_get_next_child - Iterate a node childs
  *     @node:  parent node
@@ -710,11 +729,7 @@ struct device_node *of_get_next_child(const struct device_node *node,
        unsigned long flags;
 
        raw_spin_lock_irqsave(&devtree_lock, flags);
-       next = prev ? prev->sibling : node->child;
-       for (; next; next = next->sibling)
-               if (of_node_get(next))
-                       break;
-       of_node_put(prev);
+       next = __of_get_next_child(node, prev);
        raw_spin_unlock_irqrestore(&devtree_lock, flags);
        return next;
 }
@@ -734,6 +749,9 @@ struct device_node *of_get_next_available_child(const struct device_node *node,
        struct device_node *next;
        unsigned long flags;
 
+       if (!node)
+               return NULL;
+
        raw_spin_lock_irqsave(&devtree_lock, flags);
        next = prev ? prev->sibling : node->child;
        for (; next; next = next->sibling) {
@@ -771,23 +789,78 @@ struct device_node *of_get_child_by_name(const struct device_node *node,
 }
 EXPORT_SYMBOL(of_get_child_by_name);
 
+static struct device_node *__of_find_node_by_path(struct device_node *parent,
+                                               const char *path)
+{
+       struct device_node *child;
+       int len = strchrnul(path, '/') - path;
+
+       if (!len)
+               return NULL;
+
+       __for_each_child_of_node(parent, child) {
+               const char *name = strrchr(child->full_name, '/');
+               if (WARN(!name, "malformed device_node %s\n", child->full_name))
+                       continue;
+               name++;
+               if (strncmp(path, name, len) == 0 && (strlen(name) == len))
+                       return child;
+       }
+       return NULL;
+}
+
 /**
  *     of_find_node_by_path - Find a node matching a full OF path
- *     @path:  The full path to match
+ *     @path: Either the full path to match, or if the path does not
+ *            start with '/', the name of a property of the /aliases
+ *            node (an alias).  In the case of an alias, the node
+ *            matching the alias' value will be returned.
+ *
+ *     Valid paths:
+ *             /foo/bar        Full path
+ *             foo             Valid alias
+ *             foo/bar         Valid alias + relative path
  *
  *     Returns a node pointer with refcount incremented, use
  *     of_node_put() on it when done.
  */
 struct device_node *of_find_node_by_path(const char *path)
 {
-       struct device_node *np = of_allnodes;
+       struct device_node *np = NULL;
+       struct property *pp;
        unsigned long flags;
 
+       if (strcmp(path, "/") == 0)
+               return of_node_get(of_allnodes);
+
+       /* The path could begin with an alias */
+       if (*path != '/') {
+               char *p = strchrnul(path, '/');
+               int len = p - path;
+
+               /* of_aliases must not be NULL */
+               if (!of_aliases)
+                       return NULL;
+
+               for_each_property_of_node(of_aliases, pp) {
+                       if (strlen(pp->name) == len && !strncmp(pp->name, path, len)) {
+                               np = of_find_node_by_path(pp->value);
+                               break;
+                       }
+               }
+               if (!np)
+                       return NULL;
+               path = p;
+       }
+
+       /* Step down the tree matching path components */
        raw_spin_lock_irqsave(&devtree_lock, flags);
-       for (; np; np = np->allnext) {
-               if (np->full_name && (of_node_cmp(np->full_name, path) == 0)
-                   && of_node_get(np))
-                       break;
+       if (!np)
+               np = of_node_get(of_allnodes);
+       while (np && *path == '/') {
+               path++; /* Increment past '/' delimiter */
+               np = __of_find_node_by_path(np, path);
+               path = strchrnul(path, '/');
        }
        raw_spin_unlock_irqrestore(&devtree_lock, flags);
        return np;
@@ -1800,7 +1873,7 @@ int of_update_property(struct device_node *np, struct property *newprop)
 {
        struct property **next, *oldprop;
        unsigned long flags;
-       int rc, found = 0;
+       int rc;
 
        rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop);
        if (rc)
@@ -1809,34 +1882,34 @@ int of_update_property(struct device_node *np, struct property *newprop)
        if (!newprop->name)
                return -EINVAL;
 
-       oldprop = of_find_property(np, newprop->name, NULL);
-       if (!oldprop)
-               return of_add_property(np, newprop);
-
        raw_spin_lock_irqsave(&devtree_lock, flags);
        next = &np->properties;
-       while (*next) {
+       oldprop = __of_find_property(np, newprop->name, NULL);
+       if (!oldprop) {
+               /* add the new node */
+               rc = __of_add_property(np, newprop);
+       } else while (*next) {
+               /* replace the node */
                if (*next == oldprop) {
-                       /* found the node */
                        newprop->next = oldprop->next;
                        *next = newprop;
                        oldprop->next = np->deadprops;
                        np->deadprops = oldprop;
-                       found = 1;
                        break;
                }
                next = &(*next)->next;
        }
        raw_spin_unlock_irqrestore(&devtree_lock, flags);
-       if (!found)
-               return -ENODEV;
+       if (rc)
+               return rc;
 
        /* At early boot, bail out and defer setup to of_init() */
        if (!of_kset)
-               return found ? 0 : -ENODEV;
+               return 0;
 
        /* Update the sysfs attribute */
-       sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
+       if (oldprop)
+               sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
        __of_add_property_sysfs(np, newprop);
 
        return 0;
@@ -2040,8 +2113,8 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
  * @np:                Pointer to the given device_node
  * @stem:      Alias stem of the given device_node
  *
- * The function travels the lookup table to get alias id for the given
- * device_node and alias stem.  It returns the alias id if find it.
+ * The function travels the lookup table to get the alias id for the given
+ * device_node and alias stem.  It returns the alias id if found.
  */
 int of_alias_get_id(struct device_node *np, const char *stem)
 {
index 7a2ef7b..c4cddf0 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/kernel.h>
 #include <linux/initrd.h>
 #include <linux/memblock.h>
-#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_fdt.h>
 #include <linux/of_reserved_mem.h>
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
+#include <linux/libfdt.h>
+#include <linux/debugfs.h>
+#include <linux/serial_core.h>
 
 #include <asm/setup.h>  /* for COMMAND_LINE_SIZE */
-#ifdef CONFIG_PPC
-#include <asm/machdep.h>
-#endif /* CONFIG_PPC */
-
 #include <asm/page.h>
 
-char *of_fdt_get_string(struct boot_param_header *blob, u32 offset)
-{
-       return ((char *)blob) +
-               be32_to_cpu(blob->off_dt_strings) + offset;
-}
-
-/**
- * of_fdt_get_property - Given a node in the given flat blob, return
- * the property ptr
- */
-void *of_fdt_get_property(struct boot_param_header *blob,
-                      unsigned long node, const char *name,
-                      unsigned long *size)
-{
-       unsigned long p = node;
-
-       do {
-               u32 tag = be32_to_cpup((__be32 *)p);
-               u32 sz, noff;
-               const char *nstr;
-
-               p += 4;
-               if (tag == OF_DT_NOP)
-                       continue;
-               if (tag != OF_DT_PROP)
-                       return NULL;
-
-               sz = be32_to_cpup((__be32 *)p);
-               noff = be32_to_cpup((__be32 *)(p + 4));
-               p += 8;
-               if (be32_to_cpu(blob->version) < 0x10)
-                       p = ALIGN(p, sz >= 8 ? 8 : 4);
-
-               nstr = of_fdt_get_string(blob, noff);
-               if (nstr == NULL) {
-                       pr_warning("Can't find property index name !\n");
-                       return NULL;
-               }
-               if (strcmp(name, nstr) == 0) {
-                       if (size)
-                               *size = sz;
-                       return (void *)p;
-               }
-               p += sz;
-               p = ALIGN(p, 4);
-       } while (1);
-}
-
 /**
  * of_fdt_is_compatible - Return true if given node from the given blob has
  * compat in its compatible list
@@ -86,13 +36,14 @@ void *of_fdt_get_property(struct boot_param_header *blob,
  * On match, returns a non-zero value with smaller values returned for more
  * specific compatible values.
  */
-int of_fdt_is_compatible(struct boot_param_header *blob,
+int of_fdt_is_compatible(const void *blob,
                      unsigned long node, const char *compat)
 {
        const char *cp;
-       unsigned long cplen, l, score = 0;
+       int cplen;
+       unsigned long l, score = 0;
 
-       cp = of_fdt_get_property(blob, node, "compatible", &cplen);
+       cp = fdt_getprop(blob, node, "compatible", &cplen);
        if (cp == NULL)
                return 0;
        while (cplen > 0) {
@@ -110,7 +61,7 @@ int of_fdt_is_compatible(struct boot_param_header *blob,
 /**
  * of_fdt_match - Return true if node matches a list of compatible values
  */
-int of_fdt_match(struct boot_param_header *blob, unsigned long node,
+int of_fdt_match(const void *blob, unsigned long node,
                  const char *const *compat)
 {
        unsigned int tmp, score = 0;
@@ -149,30 +100,29 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size,
  * @allnextpp: pointer to ->allnext from last allocated device_node
  * @fpsize: Size of the node path up at the current depth.
  */
-static void * unflatten_dt_node(struct boot_param_header *blob,
+static void * unflatten_dt_node(void *blob,
                                void *mem,
-                               void **p,
+                               int *poffset,
                                struct device_node *dad,
                                struct device_node ***allnextpp,
                                unsigned long fpsize)
 {
+       const __be32 *p;
        struct device_node *np;
        struct property *pp, **prev_pp = NULL;
-       char *pathp;
-       u32 tag;
+       const char *pathp;
        unsigned int l, allocl;
+       static int depth = 0;
+       int old_depth;
+       int offset;
        int has_name = 0;
        int new_format = 0;
 
-       tag = be32_to_cpup(*p);
-       if (tag != OF_DT_BEGIN_NODE) {
-               pr_err("Weird tag at start of node: %x\n", tag);
+       pathp = fdt_get_name(blob, *poffset, &l);
+       if (!pathp)
                return mem;
-       }
-       *p += 4;
-       pathp = *p;
-       l = allocl = strlen(pathp) + 1;
-       *p = PTR_ALIGN(*p + l, 4);
+
+       allocl = l++;
 
        /* version 0x10 has a more compact unit name here instead of the full
         * path. we accumulate the full path size using "fpsize", we'll rebuild
@@ -190,7 +140,7 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
                        fpsize = 1;
                        allocl = 2;
                        l = 1;
-                       *pathp = '\0';
+                       pathp = "";
                } else {
                        /* account for '/' and path size minus terminal 0
                         * already in 'l'
@@ -237,32 +187,23 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
                }
        }
        /* process properties */
-       while (1) {
-               u32 sz, noff;
-               char *pname;
-
-               tag = be32_to_cpup(*p);
-               if (tag == OF_DT_NOP) {
-                       *p += 4;
-                       continue;
-               }
-               if (tag != OF_DT_PROP)
+       for (offset = fdt_first_property_offset(blob, *poffset);
+            (offset >= 0);
+            (offset = fdt_next_property_offset(blob, offset))) {
+               const char *pname;
+               u32 sz;
+
+               if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) {
+                       offset = -FDT_ERR_INTERNAL;
                        break;
-               *p += 4;
-               sz = be32_to_cpup(*p);
-               noff = be32_to_cpup(*p + 4);
-               *p += 8;
-               if (be32_to_cpu(blob->version) < 0x10)
-                       *p = PTR_ALIGN(*p, sz >= 8 ? 8 : 4);
-
-               pname = of_fdt_get_string(blob, noff);
+               }
+
                if (pname == NULL) {
                        pr_info("Can't find property name in list !\n");
                        break;
                }
                if (strcmp(pname, "name") == 0)
                        has_name = 1;
-               l = strlen(pname) + 1;
                pp = unflatten_dt_alloc(&mem, sizeof(struct property),
                                        __alignof__(struct property));
                if (allnextpp) {
@@ -274,26 +215,25 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
                        if ((strcmp(pname, "phandle") == 0) ||
                            (strcmp(pname, "linux,phandle") == 0)) {
                                if (np->phandle == 0)
-                                       np->phandle = be32_to_cpup((__be32*)*p);
+                                       np->phandle = be32_to_cpup(p);
                        }
                        /* And we process the "ibm,phandle" property
                         * used in pSeries dynamic device tree
                         * stuff */
                        if (strcmp(pname, "ibm,phandle") == 0)
-                               np->phandle = be32_to_cpup((__be32 *)*p);
-                       pp->name = pname;
+                               np->phandle = be32_to_cpup(p);
+                       pp->name = (char *)pname;
                        pp->length = sz;
-                       pp->value = *p;
+                       pp->value = (__be32 *)p;
                        *prev_pp = pp;
                        prev_pp = &pp->next;
                }
-               *p = PTR_ALIGN((*p) + sz, 4);
        }
        /* with version 0x10 we may not have the name property, recreate
         * it here from the unit name if absent
         */
        if (!has_name) {
-               char *p1 = pathp, *ps = pathp, *pa = NULL;
+               const char *p1 = pathp, *ps = pathp, *pa = NULL;
                int sz;
 
                while (*p1) {
@@ -330,19 +270,18 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
                if (!np->type)
                        np->type = "<NULL>";
        }
-       while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) {
-               if (tag == OF_DT_NOP)
-                       *p += 4;
-               else
-                       mem = unflatten_dt_node(blob, mem, p, np, allnextpp,
-                                               fpsize);
-               tag = be32_to_cpup(*p);
-       }
-       if (tag != OF_DT_END_NODE) {
-               pr_err("Weird tag at end of node: %x\n", tag);
-               return mem;
-       }
-       *p += 4;
+
+       old_depth = depth;
+       *poffset = fdt_next_node(blob, *poffset, &depth);
+       if (depth < 0)
+               depth = 0;
+       while (*poffset > 0 && depth > old_depth)
+               mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp,
+                                       fpsize);
+
+       if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
+               pr_err("unflatten: error %d processing FDT\n", *poffset);
+
        return mem;
 }
 
@@ -358,12 +297,13 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
  * @dt_alloc: An allocator that provides a virtual address to memory
  * for the resulting tree
  */
-static void __unflatten_device_tree(struct boot_param_header *blob,
+static void __unflatten_device_tree(void *blob,
                             struct device_node **mynodes,
                             void * (*dt_alloc)(u64 size, u64 align))
 {
        unsigned long size;
-       void *start, *mem;
+       int start;
+       void *mem;
        struct device_node **allnextp = mynodes;
 
        pr_debug(" -> unflatten_device_tree()\n");
@@ -374,18 +314,18 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
        }
 
        pr_debug("Unflattening device tree:\n");
-       pr_debug("magic: %08x\n", be32_to_cpu(blob->magic));
-       pr_debug("size: %08x\n", be32_to_cpu(blob->totalsize));
-       pr_debug("version: %08x\n", be32_to_cpu(blob->version));
+       pr_debug("magic: %08x\n", fdt_magic(blob));
+       pr_debug("size: %08x\n", fdt_totalsize(blob));
+       pr_debug("version: %08x\n", fdt_version(blob));
 
-       if (be32_to_cpu(blob->magic) != OF_DT_HEADER) {
+       if (fdt_check_header(blob)) {
                pr_err("Invalid device tree blob header\n");
                return;
        }
 
        /* First pass, scan for size */
-       start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct);
-       size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
+       start = 0;
+       size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0);
        size = ALIGN(size, 4);
 
        pr_debug("  size is %lx, allocating...\n", size);
@@ -399,10 +339,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
        pr_debug("  unflattening %p...\n", mem);
 
        /* Second pass, do actual unflattening */
-       start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct);
+       start = 0;
        unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
-       if (be32_to_cpup(start) != OF_DT_END)
-               pr_warning("Weird tag at end of tree: %08x\n", be32_to_cpup(start));
        if (be32_to_cpup(mem + size) != 0xdeadbeef)
                pr_warning("End of tree marker overwritten: %08x\n",
                           be32_to_cpup(mem + size));
@@ -427,9 +365,7 @@ static void *kernel_tree_alloc(u64 size, u64 align)
 void of_fdt_unflatten_tree(unsigned long *blob,
                        struct device_node **mynodes)
 {
-       struct boot_param_header *device_tree =
-               (struct boot_param_header *)blob;
-       __unflatten_device_tree(device_tree, mynodes, &kernel_tree_alloc);
+       __unflatten_device_tree(blob, mynodes, &kernel_tree_alloc);
 }
 EXPORT_SYMBOL_GPL(of_fdt_unflatten_tree);
 
@@ -437,7 +373,7 @@ EXPORT_SYMBOL_GPL(of_fdt_unflatten_tree);
 int __initdata dt_root_addr_cells;
 int __initdata dt_root_size_cells;
 
-struct boot_param_header *initial_boot_params;
+void *initial_boot_params;
 
 #ifdef CONFIG_OF_EARLY_FLATTREE
 
@@ -449,8 +385,8 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
 {
        int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
        phys_addr_t base, size;
-       unsigned long len;
-       __be32 *prop;
+       int len;
+       const __be32 *prop;
        int nomap, first = 1;
 
        prop = of_get_flat_dt_prop(node, "reg", &len);
@@ -493,7 +429,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
  */
 static int __init __reserved_mem_check_root(unsigned long node)
 {
-       __be32 *prop;
+       const __be32 *prop;
 
        prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
        if (!prop || be32_to_cpup(prop) != dt_root_size_cells)
@@ -557,9 +493,25 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
  */
 void __init early_init_fdt_scan_reserved_mem(void)
 {
+       int n;
+       u64 base, size;
+
        if (!initial_boot_params)
                return;
 
+       /* Reserve the dtb region */
+       early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
+                                         fdt_totalsize(initial_boot_params),
+                                         0);
+
+       /* Process header /memreserve/ fields */
+       for (n = 0; ; n++) {
+               fdt_get_mem_rsv(initial_boot_params, n, &base, &size);
+               if (!size)
+                       break;
+               early_init_dt_reserve_memory_arch(base, size, 0);
+       }
+
        of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
        fdt_init_reserved_mem();
 }
@@ -578,47 +530,19 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
                                     void *data),
                           void *data)
 {
-       unsigned long p = ((unsigned long)initial_boot_params) +
-               be32_to_cpu(initial_boot_params->off_dt_struct);
-       int rc = 0;
-       int depth = -1;
-
-       do {
-               u32 tag = be32_to_cpup((__be32 *)p);
-               const char *pathp;
-
-               p += 4;
-               if (tag == OF_DT_END_NODE) {
-                       depth--;
-                       continue;
-               }
-               if (tag == OF_DT_NOP)
-                       continue;
-               if (tag == OF_DT_END)
-                       break;
-               if (tag == OF_DT_PROP) {
-                       u32 sz = be32_to_cpup((__be32 *)p);
-                       p += 8;
-                       if (be32_to_cpu(initial_boot_params->version) < 0x10)
-                               p = ALIGN(p, sz >= 8 ? 8 : 4);
-                       p += sz;
-                       p = ALIGN(p, 4);
-                       continue;
-               }
-               if (tag != OF_DT_BEGIN_NODE) {
-                       pr_err("Invalid tag %x in flat device tree!\n", tag);
-                       return -EINVAL;
-               }
-               depth++;
-               pathp = (char *)p;
-               p = ALIGN(p + strlen(pathp) + 1, 4);
+       const void *blob = initial_boot_params;
+       const char *pathp;
+       int offset, rc = 0, depth = -1;
+
+        for (offset = fdt_next_node(blob, -1, &depth);
+             offset >= 0 && depth >= 0 && !rc;
+             offset = fdt_next_node(blob, offset, &depth)) {
+
+               pathp = fdt_get_name(blob, offset, NULL);
                if (*pathp == '/')
                        pathp = kbasename(pathp);
-               rc = it(p, pathp, depth, data);
-               if (rc != 0)
-                       break;
-       } while (1);
-
+               rc = it(offset, pathp, depth, data);
+       }
        return rc;
 }
 
@@ -627,14 +551,15 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
  */
 unsigned long __init of_get_flat_dt_root(void)
 {
-       unsigned long p = ((unsigned long)initial_boot_params) +
-               be32_to_cpu(initial_boot_params->off_dt_struct);
-
-       while (be32_to_cpup((__be32 *)p) == OF_DT_NOP)
-               p += 4;
-       BUG_ON(be32_to_cpup((__be32 *)p) != OF_DT_BEGIN_NODE);
-       p += 4;
-       return ALIGN(p + strlen((char *)p) + 1, 4);
+       return 0;
+}
+
+/**
+ * of_get_flat_dt_size - Return the total size of the FDT
+ */
+int __init of_get_flat_dt_size(void)
+{
+       return fdt_totalsize(initial_boot_params);
 }
 
 /**
@@ -643,10 +568,10 @@ unsigned long __init of_get_flat_dt_root(void)
  * This function can be used within scan_flattened_dt callback to get
  * access to properties
  */
-void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
-                                unsigned long *size)
+const void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
+                                      int *size)
 {
-       return of_fdt_get_property(initial_boot_params, node, name, size);
+       return fdt_getprop(initial_boot_params, node, name, size);
 }
 
 /**
@@ -676,73 +601,6 @@ struct fdt_scan_status {
        void *data;
 };
 
-/**
- * fdt_scan_node_by_path - iterator for of_scan_flat_dt_by_path function
- */
-static int __init fdt_scan_node_by_path(unsigned long node, const char *uname,
-                                       int depth, void *data)
-{
-       struct fdt_scan_status *st = data;
-
-       /*
-        * if scan at the requested fdt node has been completed,
-        * return -ENXIO to abort further scanning
-        */
-       if (depth <= st->depth)
-               return -ENXIO;
-
-       /* requested fdt node has been found, so call iterator function */
-       if (st->found)
-               return st->iterator(node, uname, depth, st->data);
-
-       /* check if scanning automata is entering next level of fdt nodes */
-       if (depth == st->depth + 1 &&
-           strncmp(st->name, uname, st->namelen) == 0 &&
-           uname[st->namelen] == 0) {
-               st->depth += 1;
-               if (st->name[st->namelen] == 0) {
-                       st->found = 1;
-               } else {
-                       const char *next = st->name + st->namelen + 1;
-                       st->name = next;
-                       st->namelen = strcspn(next, "/");
-               }
-               return 0;
-       }
-
-       /* scan next fdt node */
-       return 0;
-}
-
-/**
- * of_scan_flat_dt_by_path - scan flattened tree blob and call callback on each
- *                          child of the given path.
- * @path: path to start searching for children
- * @it: callback function
- * @data: context data pointer
- *
- * This function is used to scan the flattened device-tree starting from the
- * node given by path. It is used to extract information (like reserved
- * memory), which is required on ealy boot before we can unflatten the tree.
- */
-int __init of_scan_flat_dt_by_path(const char *path,
-       int (*it)(unsigned long node, const char *name, int depth, void *data),
-       void *data)
-{
-       struct fdt_scan_status st = {path, 0, -1, 0, it, data};
-       int ret = 0;
-
-       if (initial_boot_params)
-                ret = of_scan_flat_dt(fdt_scan_node_by_path, &st);
-
-       if (!st.found)
-               return -ENOENT;
-       else if (ret == -ENXIO) /* scan has been completed */
-               return 0;
-       else
-               return ret;
-}
-
 const char * __init of_flat_dt_get_machine_name(void)
 {
        const char *name;
@@ -782,7 +640,7 @@ const void * __init of_flat_dt_match_machine(const void *default_match,
        }
        if (!best_data) {
                const char *prop;
-               long size;
+               int size;
 
                pr_err("\n unrecognized device tree list:\n[ ");
 
@@ -811,8 +669,8 @@ const void * __init of_flat_dt_match_machine(const void *default_match,
 static void __init early_init_dt_check_for_initrd(unsigned long node)
 {
        u64 start, end;
-       unsigned long len;
-       __be32 *prop;
+       int len;
+       const __be32 *prop;
 
        pr_debug("Looking for initrd properties... ");
 
@@ -839,13 +697,68 @@ static inline void early_init_dt_check_for_initrd(unsigned long node)
 }
 #endif /* CONFIG_BLK_DEV_INITRD */
 
+#ifdef CONFIG_SERIAL_EARLYCON
+extern struct of_device_id __earlycon_of_table[];
+
+int __init early_init_dt_scan_chosen_serial(void)
+{
+       int offset;
+       const char *p;
+       int l;
+       const struct of_device_id *match = __earlycon_of_table;
+       const void *fdt = initial_boot_params;
+
+       offset = fdt_path_offset(fdt, "/chosen");
+       if (offset < 0)
+               offset = fdt_path_offset(fdt, "/chosen@0");
+       if (offset < 0)
+               return -ENOENT;
+
+       p = fdt_getprop(fdt, offset, "stdout-path", &l);
+       if (!p)
+               p = fdt_getprop(fdt, offset, "linux,stdout-path", &l);
+       if (!p || !l)
+               return -ENOENT;
+
+       /* Get the node specified by stdout-path */
+       offset = fdt_path_offset(fdt, p);
+       if (offset < 0)
+               return -ENODEV;
+
+       while (match->compatible) {
+               unsigned long addr;
+               if (fdt_node_check_compatible(fdt, offset, match->compatible)) {
+                       match++;
+                       continue;
+               }
+
+               addr = fdt_translate_address(fdt, offset);
+               if (!addr)
+                       return -ENXIO;
+
+               of_setup_earlycon(addr, match->data);
+               return 0;
+       }
+       return -ENODEV;
+}
+
+static int __init setup_of_earlycon(char *buf)
+{
+       if (buf)
+               return 0;
+
+       return early_init_dt_scan_chosen_serial();
+}
+early_param("earlycon", setup_of_earlycon);
+#endif
+
 /**
  * early_init_dt_scan_root - fetch the top level address and size cells
  */
 int __init early_init_dt_scan_root(unsigned long node, const char *uname,
                                   int depth, void *data)
 {
-       __be32 *prop;
+       const __be32 *prop;
 
        if (depth != 0)
                return 0;
@@ -867,9 +780,9 @@ int __init early_init_dt_scan_root(unsigned long node, const char *uname,
        return 1;
 }
 
-u64 __init dt_mem_next_cell(int s, __be32 **cellp)
+u64 __init dt_mem_next_cell(int s, const __be32 **cellp)
 {
-       __be32 *p = *cellp;
+       const __be32 *p = *cellp;
 
        *cellp = p + s;
        return of_read_number(p, s);
@@ -881,9 +794,9 @@ u64 __init dt_mem_next_cell(int s, __be32 **cellp)
 int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
                                     int depth, void *data)
 {
-       char *type = of_get_flat_dt_prop(node, "device_type", NULL);
-       __be32 *reg, *endp;
-       unsigned long l;
+       const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+       const __be32 *reg, *endp;
+       int l;
 
        /* We are scanning "memory" nodes only */
        if (type == NULL) {
@@ -891,7 +804,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
                 * The longtrail doesn't have a device_type on the
                 * /memory node, so look for the node called /memory@0.
                 */
-               if (depth != 1 || strcmp(uname, "memory@0") != 0)
+               if (!IS_ENABLED(CONFIG_PPC32) || depth != 1 || strcmp(uname, "memory@0") != 0)
                        return 0;
        } else if (strcmp(type, "memory") != 0)
                return 0;
@@ -904,7 +817,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
 
        endp = reg + (l / sizeof(__be32));
 
-       pr_debug("memory scan node %s, reg size %ld, data: %x %x %x %x,\n",
+       pr_debug("memory scan node %s, reg size %d, data: %x %x %x %x,\n",
            uname, l, reg[0], reg[1], reg[2], reg[3]);
 
        while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
@@ -927,8 +840,8 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
 int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
                                     int depth, void *data)
 {
-       unsigned long l;
-       char *p;
+       int l;
+       const char *p;
 
        pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
 
@@ -1003,8 +916,8 @@ void * __init __weak early_init_dt_alloc_memory_arch(u64 size, u64 align)
 int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
                                        phys_addr_t size, bool nomap)
 {
-       pr_err("Reserved memory not supported, ignoring range 0x%llx - 0x%llx%s\n",
-                 base, size, nomap ? " (nomap)" : "");
+       pr_err("Reserved memory not supported, ignoring range 0x%pa - 0x%pa%s\n",
+                 &base, &size, nomap ? " (nomap)" : "");
        return -ENOSYS;
 }
 #endif
@@ -1018,7 +931,7 @@ bool __init early_init_dt_scan(void *params)
        initial_boot_params = params;
 
        /* check device tree validity */
-       if (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) {
+       if (fdt_check_header(params)) {
                initial_boot_params = NULL;
                return false;
        }
@@ -1073,9 +986,9 @@ void __init unflatten_and_copy_device_tree(void)
                return;
        }
 
-       size = __be32_to_cpu(initial_boot_params->totalsize);
+       size = fdt_totalsize(initial_boot_params);
        dt = early_init_dt_alloc_memory_arch(size,
-               __alignof__(struct boot_param_header));
+                                            roundup_pow_of_two(FDT_V17_SIZE));
 
        if (dt) {
                memcpy(dt, initial_boot_params, size);
@@ -1084,4 +997,27 @@ void __init unflatten_and_copy_device_tree(void)
        unflatten_device_tree();
 }
 
+#if defined(CONFIG_DEBUG_FS) && defined(DEBUG)
+static struct debugfs_blob_wrapper flat_dt_blob;
+
+static int __init of_flat_dt_debugfs_export_fdt(void)
+{
+       struct dentry *d = debugfs_create_dir("device-tree", NULL);
+
+       if (!d)
+               return -ENOENT;
+
+       flat_dt_blob.data = initial_boot_params;
+       flat_dt_blob.size = fdt_totalsize(initial_boot_params);
+
+       d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR,
+                               d, &flat_dt_blob);
+       if (!d)
+               return -ENOENT;
+
+       return 0;
+}
+module_init(of_flat_dt_debugfs_export_fdt);
+#endif
+
 #endif /* CONFIG_OF_EARLY_FLATTREE */
diff --git a/drivers/of/fdt_address.c b/drivers/of/fdt_address.c
new file mode 100644 (file)
index 0000000..8d3dc6f
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * FDT Address translation based on u-boot fdt_support.c which in turn was
+ * based on the kernel unflattened DT address translation code.
+ *
+ * (C) Copyright 2007
+ * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/libfdt.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/sizes.h>
+
+/* Max address size we deal with */
+#define OF_MAX_ADDR_CELLS      4
+#define OF_CHECK_COUNTS(na, ns)        ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
+                       (ns) > 0)
+
+/* Debug utility */
+#ifdef DEBUG
+static void __init of_dump_addr(const char *s, const __be32 *addr, int na)
+{
+       pr_debug("%s", s);
+       while(na--)
+               pr_cont(" %08x", *(addr++));
+       pr_debug("\n");
+}
+#else
+static void __init of_dump_addr(const char *s, const __be32 *addr, int na) { }
+#endif
+
+/* Callbacks for bus specific translators */
+struct of_bus {
+       void            (*count_cells)(const void *blob, int parentoffset,
+                               int *addrc, int *sizec);
+       u64             (*map)(__be32 *addr, const __be32 *range,
+                               int na, int ns, int pna);
+       int             (*translate)(__be32 *addr, u64 offset, int na);
+};
+
+/* Default translator (generic bus) */
+static void __init fdt_bus_default_count_cells(const void *blob, int parentoffset,
+                                              int *addrc, int *sizec)
+{
+       const __be32 *prop;
+
+       if (addrc) {
+               prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL);
+               if (prop)
+                       *addrc = be32_to_cpup(prop);
+               else
+                       *addrc = dt_root_addr_cells;
+       }
+
+       if (sizec) {
+               prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL);
+               if (prop)
+                       *sizec = be32_to_cpup(prop);
+               else
+                       *sizec = dt_root_size_cells;
+       }
+}
+
+static u64 __init fdt_bus_default_map(__be32 *addr, const __be32 *range,
+                                     int na, int ns, int pna)
+{
+       u64 cp, s, da;
+
+       cp = of_read_number(range, na);
+       s  = of_read_number(range + na + pna, ns);
+       da = of_read_number(addr, na);
+
+       pr_debug("FDT: default map, cp=%llx, s=%llx, da=%llx\n",
+           cp, s, da);
+
+       if (da < cp || da >= (cp + s))
+               return OF_BAD_ADDR;
+       return da - cp;
+}
+
+static int __init fdt_bus_default_translate(__be32 *addr, u64 offset, int na)
+{
+       u64 a = of_read_number(addr, na);
+       memset(addr, 0, na * 4);
+       a += offset;
+       if (na > 1)
+               addr[na - 2] = cpu_to_fdt32(a >> 32);
+       addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu);
+
+       return 0;
+}
+
+/* Array of bus specific translators */
+static const struct of_bus of_busses[] __initconst = {
+       /* Default */
+       {
+               .count_cells = fdt_bus_default_count_cells,
+               .map = fdt_bus_default_map,
+               .translate = fdt_bus_default_translate,
+       },
+};
+
+static int __init fdt_translate_one(const void *blob, int parent,
+                                   const struct of_bus *bus,
+                                   const struct of_bus *pbus, __be32 *addr,
+                                   int na, int ns, int pna, const char *rprop)
+{
+       const __be32 *ranges;
+       int rlen;
+       int rone;
+       u64 offset = OF_BAD_ADDR;
+
+       ranges = fdt_getprop(blob, parent, rprop, &rlen);
+       if (!ranges)
+               return 1;
+       if (rlen == 0) {
+               offset = of_read_number(addr, na);
+               memset(addr, 0, pna * 4);
+               pr_debug("FDT: empty ranges, 1:1 translation\n");
+               goto finish;
+       }
+
+       pr_debug("FDT: walking ranges...\n");
+
+       /* Now walk through the ranges */
+       rlen /= 4;
+       rone = na + pna + ns;
+       for (; rlen >= rone; rlen -= rone, ranges += rone) {
+               offset = bus->map(addr, ranges, na, ns, pna);
+               if (offset != OF_BAD_ADDR)
+                       break;
+       }
+       if (offset == OF_BAD_ADDR) {
+               pr_debug("FDT: not found !\n");
+               return 1;
+       }
+       memcpy(addr, ranges + na, 4 * pna);
+
+ finish:
+       of_dump_addr("FDT: parent translation for:", addr, pna);
+       pr_debug("FDT: with offset: %llx\n", offset);
+
+       /* Translate it into parent bus space */
+       return pbus->translate(addr, offset, pna);
+}
+
+/*
+ * Translate an address from the device-tree into a CPU physical address,
+ * this walks up the tree and applies the various bus mappings on the
+ * way.
+ *
+ * Note: We consider that crossing any level with #size-cells == 0 to mean
+ * that translation is impossible (that is we are not dealing with a value
+ * that can be mapped to a cpu physical address). This is not really specified
+ * that way, but this is traditionally the way IBM at least do things
+ */
+u64 __init fdt_translate_address(const void *blob, int node_offset)
+{
+       int parent, len;
+       const struct of_bus *bus, *pbus;
+       const __be32 *reg;
+       __be32 addr[OF_MAX_ADDR_CELLS];
+       int na, ns, pna, pns;
+       u64 result = OF_BAD_ADDR;
+
+       pr_debug("FDT: ** translation for device %s **\n",
+                fdt_get_name(blob, node_offset, NULL));
+
+       reg = fdt_getprop(blob, node_offset, "reg", &len);
+       if (!reg) {
+               pr_err("FDT: warning: device tree node '%s' has no address.\n",
+                       fdt_get_name(blob, node_offset, NULL));
+               goto bail;
+       }
+
+       /* Get parent & match bus type */
+       parent = fdt_parent_offset(blob, node_offset);
+       if (parent < 0)
+               goto bail;
+       bus = &of_busses[0];
+
+       /* Cound address cells & copy address locally */
+       bus->count_cells(blob, parent, &na, &ns);
+       if (!OF_CHECK_COUNTS(na, ns)) {
+               pr_err("FDT: Bad cell count for %s\n",
+                      fdt_get_name(blob, node_offset, NULL));
+               goto bail;
+       }
+       memcpy(addr, reg, na * 4);
+
+       pr_debug("FDT: bus (na=%d, ns=%d) on %s\n",
+                na, ns, fdt_get_name(blob, parent, NULL));
+       of_dump_addr("OF: translating address:", addr, na);
+
+       /* Translate */
+       for (;;) {
+               /* Switch to parent bus */
+               node_offset = parent;
+               parent = fdt_parent_offset(blob, node_offset);
+
+               /* If root, we have finished */
+               if (parent < 0) {
+                       pr_debug("FDT: reached root node\n");
+                       result = of_read_number(addr, na);
+                       break;
+               }
+
+               /* Get new parent bus and counts */
+               pbus = &of_busses[0];
+               pbus->count_cells(blob, parent, &pna, &pns);
+               if (!OF_CHECK_COUNTS(pna, pns)) {
+                       pr_err("FDT: Bad cell count for %s\n",
+                               fdt_get_name(blob, node_offset, NULL));
+                       break;
+               }
+
+               pr_debug("FDT: parent bus (na=%d, ns=%d) on %s\n",
+                        pna, pns, fdt_get_name(blob, parent, NULL));
+
+               /* Apply bus translation */
+               if (fdt_translate_one(blob, node_offset, bus, pbus,
+                                       addr, na, ns, pna, "ranges"))
+                       break;
+
+               /* Complete the move up one level */
+               na = pna;
+               ns = pns;
+               bus = pbus;
+
+               of_dump_addr("FDT: one level translation:", addr, na);
+       }
+ bail:
+       return result;
+}
index 5aeb894..3e06a69 100644 (file)
@@ -405,6 +405,28 @@ int of_irq_get(struct device_node *dev, int index)
        return irq_create_of_mapping(&oirq);
 }
 
+/**
+ * of_irq_get_byname - Decode a node's IRQ and return it as a Linux irq number
+ * @dev: pointer to device tree node
+ * @name: irq name
+ *
+ * Returns Linux irq number on success, or -EPROBE_DEFER if the irq domain
+ * is not yet created, or error code in case of any other failure.
+ */
+int of_irq_get_byname(struct device_node *dev, const char *name)
+{
+       int index;
+
+       if (unlikely(!name))
+               return -EINVAL;
+
+       index = of_property_match_string(dev, "interrupt-names", name);
+       if (index < 0)
+               return index;
+
+       return of_irq_get(dev, index);
+}
+
 /**
  * of_irq_count - Count the number of IRQs a node uses
  * @dev: pointer to device tree node
index 8736bc7..1710d9d 100644 (file)
@@ -18,8 +18,6 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
 {
        struct device_node *dn, *ppnode;
        struct pci_dev *ppdev;
-       u32 lspec;
-       __be32 lspec_be;
        __be32 laddr[3];
        u8 pin;
        int rc;
@@ -46,7 +44,6 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
                return -ENODEV;
 
        /* Now we walk up the PCI tree */
-       lspec = pin;
        for (;;) {
                /* Get the pci_dev of our parent */
                ppdev = pdev->bus->self;
@@ -80,14 +77,13 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
                /* We can only get here if we hit a P2P bridge with no node,
                 * let's do standard swizzling and try again
                 */
-               lspec = pci_swizzle_interrupt_pin(pdev, lspec);
+               pin = pci_swizzle_interrupt_pin(pdev, pin);
                pdev = ppdev;
        }
 
        out_irq->np = ppnode;
        out_irq->args_count = 1;
-       out_irq->args[0] = lspec;
-       lspec_be = cpu_to_be32(lspec);
+       out_irq->args[0] = pin;
        laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8));
        laddr[1] = laddr[2] = cpu_to_be32(0);
        return of_irq_parse_raw(laddr, out_irq);
index daaaf93..632aae8 100644 (file)
@@ -95,8 +95,8 @@ static int __init __reserved_mem_alloc_size(unsigned long node,
        int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
        phys_addr_t start = 0, end = 0;
        phys_addr_t base = 0, align = 0, size;
-       unsigned long len;
-       __be32 *prop;
+       int len;
+       const __be32 *prop;
        int nomap;
        int ret;
 
@@ -188,7 +188,7 @@ static int __init __reserved_mem_init_node(struct reserved_mem *rmem)
                if (!of_flat_dt_is_compatible(rmem->fdt_node, compat))
                        continue;
 
-               if (initfn(rmem, rmem->fdt_node, rmem->name) == 0) {
+               if (initfn(rmem) == 0) {
                        pr_info("Reserved memory: initialized node %s, compatible id %s\n",
                                rmem->name, compat);
                        return 0;
index e8376d6..92c060e 100644 (file)
@@ -51,10 +51,6 @@ struct platform_device *of_find_device_by_node(struct device_node *np)
 }
 EXPORT_SYMBOL(of_find_device_by_node);
 
-#if defined(CONFIG_PPC_DCR)
-#include <asm/dcr.h>
-#endif
-
 #ifdef CONFIG_OF_ADDRESS
 /*
  * The following routines scan a subtree and registers a device for
@@ -68,66 +64,35 @@ EXPORT_SYMBOL(of_find_device_by_node);
  * of_device_make_bus_id - Use the device node data to assign a unique name
  * @dev: pointer to device structure that is linked to a device tree node
  *
- * This routine will first try using either the dcr-reg or the reg property
- * value to derive a unique name.  As a last resort it will use the node
- * name followed by a unique number.
+ * This routine will first try using the translated bus address to
+ * derive a unique name. If it cannot, then it will prepend names from
+ * parent nodes until a unique name can be derived.
  */
 void of_device_make_bus_id(struct device *dev)
 {
-       static atomic_t bus_no_reg_magic;
        struct device_node *node = dev->of_node;
        const __be32 *reg;
        u64 addr;
-       const __be32 *addrp;
-       int magic;
 
-#ifdef CONFIG_PPC_DCR
-       /*
-        * If it's a DCR based device, use 'd' for native DCRs
-        * and 'D' for MMIO DCRs.
-        */
-       reg = of_get_property(node, "dcr-reg", NULL);
-       if (reg) {
-#ifdef CONFIG_PPC_DCR_NATIVE
-               dev_set_name(dev, "d%x.%s", *reg, node->name);
-#else /* CONFIG_PPC_DCR_NATIVE */
-               u64 addr = of_translate_dcr_address(node, *reg, NULL);
-               if (addr != OF_BAD_ADDR) {
-                       dev_set_name(dev, "D%llx.%s",
-                                    (unsigned long long)addr, node->name);
+       /* Construct the name, using parent nodes if necessary to ensure uniqueness */
+       while (node->parent) {
+               /*
+                * If the address can be translated, then that is as much
+                * uniqueness as we need. Make it the first component and return
+                */
+               reg = of_get_property(node, "reg", NULL);
+               if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
+                       dev_set_name(dev, dev_name(dev) ? "%llx.%s:%s" : "%llx.%s",
+                                    (unsigned long long)addr, node->name,
+                                    dev_name(dev));
                        return;
                }
-#endif /* !CONFIG_PPC_DCR_NATIVE */
-       }
-#endif /* CONFIG_PPC_DCR */
 
-       /*
-        * For MMIO, get the physical address
-        */
-       reg = of_get_property(node, "reg", NULL);
-       if (reg) {
-               if (of_can_translate_address(node)) {
-                       addr = of_translate_address(node, reg);
-               } else {
-                       addrp = of_get_address(node, 0, NULL, NULL);
-                       if (addrp)
-                               addr = of_read_number(addrp, 1);
-                       else
-                               addr = OF_BAD_ADDR;
-               }
-               if (addr != OF_BAD_ADDR) {
-                       dev_set_name(dev, "%llx.%s",
-                                    (unsigned long long)addr, node->name);
-                       return;
-               }
+               /* format arguments only used if dev_name() resolves to NULL */
+               dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
+                            strrchr(node->full_name, '/') + 1, dev_name(dev));
+               node = node->parent;
        }
-
-       /*
-        * No BusID, use the node name and add a globally incremented
-        * counter (and pray...)
-        */
-       magic = atomic_add_return(1, &bus_no_reg_magic);
-       dev_set_name(dev, "%s.%d", node->name, magic - 1);
 }
 
 /**
@@ -149,9 +114,8 @@ struct platform_device *of_device_alloc(struct device_node *np,
                return NULL;
 
        /* count the io and irq resources */
-       if (of_can_translate_address(np))
-               while (of_address_to_resource(np, num_reg, &temp_res) == 0)
-                       num_reg++;
+       while (of_address_to_resource(np, num_reg, &temp_res) == 0)
+               num_reg++;
        num_irq = of_irq_count(np);
 
        /* Populate the resource table */
index fe70b86..077314e 100644 (file)
@@ -31,6 +31,51 @@ static struct selftest_results {
        } \
 }
 
+static void __init of_selftest_find_node_by_name(void)
+{
+       struct device_node *np;
+
+       np = of_find_node_by_path("/testcase-data");
+       selftest(np && !strcmp("/testcase-data", np->full_name),
+               "find /testcase-data failed\n");
+       of_node_put(np);
+
+       /* Test if trailing '/' works */
+       np = of_find_node_by_path("/testcase-data/");
+       selftest(!np, "trailing '/' on /testcase-data/ should fail\n");
+
+       np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
+       selftest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name),
+               "find /testcase-data/phandle-tests/consumer-a failed\n");
+       of_node_put(np);
+
+       np = of_find_node_by_path("testcase-alias");
+       selftest(np && !strcmp("/testcase-data", np->full_name),
+               "find testcase-alias failed\n");
+       of_node_put(np);
+
+       /* Test if trailing '/' works on aliases */
+       np = of_find_node_by_path("testcase-alias/");
+       selftest(!np, "trailing '/' on testcase-alias/ should fail\n");
+
+       np = of_find_node_by_path("testcase-alias/phandle-tests/consumer-a");
+       selftest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name),
+               "find testcase-alias/phandle-tests/consumer-a failed\n");
+       of_node_put(np);
+
+       np = of_find_node_by_path("/testcase-data/missing-path");
+       selftest(!np, "non-existent path returned node %s\n", np->full_name);
+       of_node_put(np);
+
+       np = of_find_node_by_path("missing-alias");
+       selftest(!np, "non-existent alias returned node %s\n", np->full_name);
+       of_node_put(np);
+
+       np = of_find_node_by_path("testcase-alias/missing-path");
+       selftest(!np, "non-existent alias with relative path returned node %s\n", np->full_name);
+       of_node_put(np);
+}
+
 static void __init of_selftest_dynamic(void)
 {
        struct device_node *np;
@@ -431,8 +476,12 @@ static void __init of_selftest_match_node(void)
 static void __init of_selftest_platform_populate(void)
 {
        int irq;
-       struct device_node *np;
+       struct device_node *np, *child;
        struct platform_device *pdev;
+       struct of_device_id match[] = {
+               { .compatible = "test-device", },
+               {}
+       };
 
        np = of_find_node_by_path("/testcase-data");
        of_platform_populate(np, of_default_bus_match_table, NULL, NULL);
@@ -440,22 +489,32 @@ static void __init of_selftest_platform_populate(void)
        /* Test that a missing irq domain returns -EPROBE_DEFER */
        np = of_find_node_by_path("/testcase-data/testcase-device1");
        pdev = of_find_device_by_node(np);
-       if (!pdev)
-               selftest(0, "device 1 creation failed\n");
+       selftest(pdev, "device 1 creation failed\n");
+
        irq = platform_get_irq(pdev, 0);
-       if (irq != -EPROBE_DEFER)
-               selftest(0, "device deferred probe failed - %d\n", irq);
+       selftest(irq == -EPROBE_DEFER, "device deferred probe failed - %d\n", irq);
 
        /* Test that a parsing failure does not return -EPROBE_DEFER */
        np = of_find_node_by_path("/testcase-data/testcase-device2");
        pdev = of_find_device_by_node(np);
-       if (!pdev)
-               selftest(0, "device 2 creation failed\n");
+       selftest(pdev, "device 2 creation failed\n");
        irq = platform_get_irq(pdev, 0);
-       if (irq >= 0 || irq == -EPROBE_DEFER)
-               selftest(0, "device parsing error failed - %d\n", irq);
+       selftest(irq < 0 && irq != -EPROBE_DEFER, "device parsing error failed - %d\n", irq);
 
-       selftest(1, "passed");
+       np = of_find_node_by_path("/testcase-data/platform-tests");
+       if (!np) {
+               pr_err("No testcase data in device tree\n");
+               return;
+       }
+
+       for_each_child_of_node(np, child) {
+               struct device_node *grandchild;
+               of_platform_populate(child, match, NULL, NULL);
+               for_each_child_of_node(child, grandchild)
+                       selftest(of_find_device_by_node(grandchild),
+                                "Could not create device for node '%s'\n",
+                                grandchild->name);
+       }
 }
 
 static int __init of_selftest(void)
@@ -470,6 +529,7 @@ static int __init of_selftest(void)
        of_node_put(np);
 
        pr_info("start of selftest - you will see error messages\n");
+       of_selftest_find_node_by_name();
        of_selftest_dynamic();
        of_selftest_parse_phandle_with_args();
        of_selftest_property_match_string();
index 3a5b75a..6d8d980 100644 (file)
@@ -1,3 +1,4 @@
 #include "tests-phandle.dtsi"
 #include "tests-interrupts.dtsi"
 #include "tests-match.dtsi"
+#include "tests-platform.dtsi"
index 788a4c2..ce0fe08 100644 (file)
@@ -1,6 +1,10 @@
 
 / {
-       testcase-data {
+       aliases {
+               testcase-alias = &testcase;
+       };
+
+       testcase: testcase-data {
                security-password = "password";
                duplicate-name = "duplicate";
                duplicate-name { };
diff --git a/drivers/of/testcase-data/tests-platform.dtsi b/drivers/of/testcase-data/tests-platform.dtsi
new file mode 100644 (file)
index 0000000..eb20eeb
--- /dev/null
@@ -0,0 +1,35 @@
+
+/ {
+       testcase-data {
+               platform-tests {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       test-device@0 {
+                               compatible = "test-device";
+                               reg = <0x0>;
+
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               dev@100 {
+                                       compatible = "test-sub-device";
+                                       reg = <0x100>;
+                               };
+                       };
+
+                       test-device@1 {
+                               compatible = "test-device";
+                               reg = <0x1>;
+
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               dev@100 {
+                                       compatible = "test-sub-device";
+                                       reg = <0x100>;
+                               };
+                       };
+               };
+       };
+};
index 7ae7aa0..436a76a 100644 (file)
@@ -106,7 +106,7 @@ static bool pcie_ari_disabled;
  * Given a PCI bus, returns the highest PCI bus number present in the set
  * including the given PCI bus and its list of child PCI buses.
  */
-unsigned char pci_bus_max_busnr(struct pci_busbus)
+unsigned char pci_bus_max_busnr(struct pci_bus *bus)
 {
        struct pci_bus *tmp;
        unsigned char max, n;
@@ -1371,7 +1371,7 @@ static void pcim_release(struct device *gendev, void *res)
                pci_disable_device(dev);
 }
 
-static struct pci_devres * get_pci_dr(struct pci_dev *pdev)
+static struct pci_devres *get_pci_dr(struct pci_dev *pdev)
 {
        struct pci_devres *dr, *new_dr;
 
@@ -1385,7 +1385,7 @@ static struct pci_devres * get_pci_dr(struct pci_dev *pdev)
        return devres_get(&pdev->dev, new_dr, NULL, NULL);
 }
 
-static struct pci_devres * find_pci_dr(struct pci_dev *pdev)
+static struct pci_devres *find_pci_dr(struct pci_dev *pdev)
 {
        if (pci_is_managed(pdev))
                return devres_find(&pdev->dev, pcim_release, NULL, NULL);
index c91f69b..bbf78b2 100644 (file)
@@ -570,6 +570,14 @@ static const struct dmi_system_id video_vendor_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5750"),
                },
        },
+       {
+               .callback = video_set_backlight_video_vendor,
+               .ident = "Acer Aspire 5741",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5741"),
+               },
+       },
        {}
 };
 
@@ -2228,7 +2236,7 @@ static int __init acer_wmi_init(void)
                pr_info("Brightness must be controlled by acpi video driver\n");
        } else {
                pr_info("Disabling ACPI video driver\n");
-               acpi_video_unregister();
+               acpi_video_unregister_backlight();
        }
 
        if (wmi_has_guid(WMID_GUID3)) {
index c31aa07..b81448b 100644 (file)
 
 static int num;
 
-/* We need only to blacklist devices that have already an acpi driver that
- * can't use pnp layer. We don't need to blacklist device that are directly
- * used by the kernel (PCI root, ...), as it is harmless and there were
- * already present in pnpbios. But there is an exception for devices that
- * have irqs (PIC, Timer) because we call acpi_register_gsi.
- * Finally, only devices that have a CRS method need to be in this list.
- */
-static struct acpi_device_id excluded_id_list[] __initdata = {
-       {"PNP0C09", 0},         /* EC */
-       {"PNP0C0F", 0},         /* Link device */
-       {"PNP0000", 0},         /* PIC */
-       {"PNP0100", 0},         /* Timer */
-       {"", 0},
-};
-
-static inline int __init is_exclusive_device(struct acpi_device *dev)
-{
-       return (!acpi_match_device_ids(dev, excluded_id_list));
-}
-
 /*
  * Compatible Device IDs
  */
@@ -266,7 +246,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device)
        if (!pnpid)
                return 0;
 
-       if (is_exclusive_device(device) || !device->status.present)
+       if (!device->status.present)
                return 0;
 
        dev = pnp_alloc_dev(&pnpacpi_protocol, num, pnpid);
@@ -326,10 +306,10 @@ static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle,
 {
        struct acpi_device *device;
 
-       if (!acpi_bus_get_device(handle, &device))
-               pnpacpi_add_device(device);
-       else
+       if (acpi_bus_get_device(handle, &device))
                return AE_CTRL_DEPTH;
+       if (acpi_is_pnp_device(device))
+               pnpacpi_add_device(device);
        return AE_OK;
 }
 
index 01712cb..782e822 100644 (file)
@@ -360,7 +360,7 @@ int pnp_check_irq(struct pnp_dev *dev, struct resource *res)
                return 1;
 
        /* check if the resource is valid */
-       if (*irq < 0 || *irq > 15)
+       if (*irq > 15)
                return 0;
 
        /* check if the resource is reserved */
@@ -424,7 +424,7 @@ int pnp_check_dma(struct pnp_dev *dev, struct resource *res)
                return 1;
 
        /* check if the resource is valid */
-       if (*dma < 0 || *dma == 4 || *dma > 7)
+       if (*dma == 4 || *dma > 7)
                return 0;
 
        /* check if the resource is reserved */
index 2660664..5a5a24e 100644 (file)
@@ -537,7 +537,7 @@ static void psy_unregister_cooler(struct power_supply *psy)
 }
 #endif
 
-int power_supply_register(struct device *parent, struct power_supply *psy)
+int __power_supply_register(struct device *parent, struct power_supply *psy, bool ws)
 {
        struct device *dev;
        int rc;
@@ -568,7 +568,7 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
        }
 
        spin_lock_init(&psy->changed_lock);
-       rc = device_init_wakeup(dev, true);
+       rc = device_init_wakeup(dev, ws);
        if (rc)
                goto wakeup_init_failed;
 
@@ -606,8 +606,19 @@ dev_set_name_failed:
 success:
        return rc;
 }
+
+int power_supply_register(struct device *parent, struct power_supply *psy)
+{
+       return __power_supply_register(parent, psy, true);
+}
 EXPORT_SYMBOL_GPL(power_supply_register);
 
+int power_supply_register_no_ws(struct device *parent, struct power_supply *psy)
+{
+       return __power_supply_register(parent, psy, false);
+}
+EXPORT_SYMBOL_GPL(power_supply_register_no_ws);
+
 void power_supply_unregister(struct power_supply *psy)
 {
        cancel_work_sync(&psy->changed_work);
index d9a0770..b1cda6f 100644 (file)
@@ -951,7 +951,9 @@ static const struct x86_cpu_id rapl_ids[] = {
        { X86_VENDOR_INTEL, 6, 0x2d},/* Sandy Bridge EP */
        { X86_VENDOR_INTEL, 6, 0x37},/* Valleyview */
        { X86_VENDOR_INTEL, 6, 0x3a},/* Ivy Bridge */
-       { X86_VENDOR_INTEL, 6, 0x45},/* Haswell */
+       { X86_VENDOR_INTEL, 6, 0x3c},/* Haswell */
+       { X86_VENDOR_INTEL, 6, 0x3d},/* Broadwell */
+       { X86_VENDOR_INTEL, 6, 0x45},/* Haswell ULT */
        /* TODO: Add more CPU IDs after testing */
        {}
 };
@@ -1124,8 +1126,7 @@ err_cleanup_package:
 static int rapl_check_domain(int cpu, int domain)
 {
        unsigned msr;
-       u64 val1, val2 = 0;
-       int retry = 0;
+       u64 val = 0;
 
        switch (domain) {
        case RAPL_DOMAIN_PACKAGE:
@@ -1144,26 +1145,13 @@ static int rapl_check_domain(int cpu, int domain)
                pr_err("invalid domain id %d\n", domain);
                return -EINVAL;
        }
-       if (rdmsrl_safe_on_cpu(cpu, msr, &val1))
-               return -ENODEV;
-
-       /* PP1/uncore/graphics domain may not be active at the time of
-        * driver loading. So skip further checks.
+       /* make sure domain counters are available and contains non-zero
+        * values, otherwise skip it.
         */
-       if (domain == RAPL_DOMAIN_PP1)
-               return 0;
-       /* energy counters roll slowly on some domains */
-       while (++retry < 10) {
-               usleep_range(10000, 15000);
-               rdmsrl_safe_on_cpu(cpu, msr, &val2);
-               if ((val1 & ENERGY_STATUS_MASK) != (val2 & ENERGY_STATUS_MASK))
-                       return 0;
-       }
-       /* if energy counter does not change, report as bad domain */
-       pr_info("domain %s energy ctr %llu:%llu not working, skip\n",
-               rapl_domain_names[domain], val1, val2);
+       if (rdmsrl_safe_on_cpu(cpu, msr, &val) || !val)
+               return -ENODEV;
 
-       return -ENODEV;
+       return 0;
 }
 
 /* Detect active and valid domains for the given CPU, caller must
@@ -1180,6 +1168,9 @@ static int rapl_detect_domains(struct rapl_package *rp, int cpu)
                /* use physical package id to read counters */
                if (!rapl_check_domain(cpu, i))
                        rp->domain_map |= 1 << i;
+               else
+                       pr_warn("RAPL domain %s detection failed\n",
+                               rapl_domain_names[i]);
        }
        rp->nr_domains = bitmap_weight(&rp->domain_map, RAPL_DOMAIN_MAX);
        if (!rp->nr_domains) {
index 14196ea..1918d9d 100644 (file)
@@ -22,11 +22,14 @@ struct read_info_sccb {
        u8      rnsize;                 /* 10 */
        u8      _reserved0[16 - 11];    /* 11-15 */
        u16     ncpurl;                 /* 16-17 */
-       u8      _reserved7[24 - 18];    /* 18-23 */
+       u16     cpuoff;                 /* 18-19 */
+       u8      _reserved7[24 - 20];    /* 20-23 */
        u8      loadparm[8];            /* 24-31 */
        u8      _reserved1[48 - 32];    /* 32-47 */
        u64     facilities;             /* 48-55 */
-       u8      _reserved2[84 - 56];    /* 56-83 */
+       u8      _reserved2a[76 - 56];   /* 56-75 */
+       u32     ibc;                    /* 76-79 */
+       u8      _reserved2b[84 - 80];   /* 80-83 */
        u8      fac84;                  /* 84 */
        u8      fac85;                  /* 85 */
        u8      _reserved3[91 - 86];    /* 86-90 */
@@ -45,6 +48,8 @@ static unsigned int sclp_con_has_linemode __initdata;
 static unsigned long sclp_hsa_size;
 static unsigned int sclp_max_cpu;
 static struct sclp_ipl_info sclp_ipl_info;
+static unsigned char sclp_siif;
+static u32 sclp_ibc;
 
 u64 sclp_facilities;
 u8 sclp_fac84;
@@ -96,6 +101,9 @@ static int __init sclp_read_info_early(struct read_info_sccb *sccb)
 
 static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
 {
+       struct sclp_cpu_entry *cpue;
+       u16 boot_cpu_address, cpu;
+
        if (sclp_read_info_early(sccb))
                return;
 
@@ -106,6 +114,7 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
        sclp_rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
        sclp_rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
        sclp_rzm <<= 20;
+       sclp_ibc = sccb->ibc;
 
        if (!sccb->hcpua) {
                if (MACHINE_IS_VM)
@@ -116,6 +125,15 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
                sclp_max_cpu = sccb->hcpua + 1;
        }
 
+       boot_cpu_address = stap();
+       cpue = (void *)sccb + sccb->cpuoff;
+       for (cpu = 0; cpu < sccb->ncpurl; cpue++, cpu++) {
+               if (boot_cpu_address != cpue->address)
+                       continue;
+               sclp_siif = cpue->siif;
+               break;
+       }
+
        /* Save IPL information */
        sclp_ipl_info.is_valid = 1;
        if (sccb->flags & 0x2)
@@ -148,6 +166,18 @@ unsigned int sclp_get_max_cpu(void)
        return sclp_max_cpu;
 }
 
+int sclp_has_siif(void)
+{
+       return sclp_siif;
+}
+EXPORT_SYMBOL(sclp_has_siif);
+
+unsigned int sclp_get_ibc(void)
+{
+       return sclp_ibc;
+}
+EXPORT_SYMBOL(sclp_get_ibc);
+
 /*
  * This function will be called after sclp_facilities_detect(), which gets
  * called from early.c code. The sclp_facilities_detect() function retrieves
index 9b05942..113874c 100644 (file)
@@ -911,7 +911,7 @@ struct vpd_config {
        uint8_t  length;
        uint8_t  revision;
        uint8_t  device_flags;
-       uint8_t  termnation_menus[2];
+       uint8_t  termination_menus[2];
        uint8_t  fifo_threshold;
        uint8_t  end_tag;
        uint8_t  vpd_checksum;
index c0c6258..114ff0c 100644 (file)
@@ -144,16 +144,6 @@ static struct scsi_transport_template *ahc_linux_transport_template = NULL;
 #define AIC7XXX_RESET_DELAY 5000
 #endif
 
-/*
- * Control collection of SCSI transfer statistics for the /proc filesystem.
- *
- * NOTE: Do NOT enable this when running on kernels version 1.2.x and below.
- * NOTE: This does affect performance since it has to maintain statistics.
- */
-#ifdef CONFIG_AIC7XXX_PROC_STATS
-#define AIC7XXX_PROC_STATS
-#endif
-
 /*
  * To change the default number of tagged transactions allowed per-device,
  * add a line to the lilo.conf file like:
index 7472785..be56b22 100644 (file)
@@ -196,17 +196,11 @@ int clk_rate_table_find(struct clk *clk,
                        struct cpufreq_frequency_table *freq_table,
                        unsigned long rate)
 {
-       int i;
-
-       for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               unsigned long freq = freq_table[i].frequency;
+       struct cpufreq_frequency_table *pos;
 
-               if (freq == CPUFREQ_ENTRY_INVALID)
-                       continue;
-
-               if (freq == rate)
-                       return i;
-       }
+       cpufreq_for_each_valid_entry(pos, freq_table)
+               if (pos->frequency == rate)
+                       return pos - freq_table;
 
        return -ENOENT;
 }
@@ -575,11 +569,7 @@ long clk_round_parent(struct clk *clk, unsigned long target,
                return abs(target - *best_freq);
        }
 
-       for (freq = parent->freq_table; freq->frequency != CPUFREQ_TABLE_END;
-            freq++) {
-               if (freq->frequency == CPUFREQ_ENTRY_INVALID)
-                       continue;
-
+       cpufreq_for_each_valid_entry(freq, parent->freq_table) {
                if (unlikely(freq->frequency / target <= div_min - 1)) {
                        unsigned long freq_max;
 
index d87f96d..c4ddec2 100644 (file)
@@ -2706,20 +2706,15 @@ ll_file_flock(struct file *file, int cmd, struct file_lock *file_lock)
 
        ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_FLOCK, 1);
 
-       if (file_lock->fl_flags & FL_FLOCK) {
+       if (file_lock->fl_flags & FL_FLOCK)
                LASSERT((cmd == F_SETLKW) || (cmd == F_SETLK));
-               /* flocks are whole-file locks */
-               flock.l_flock.end = OFFSET_MAX;
-               /* For flocks owner is determined by the local file descriptor*/
-               flock.l_flock.owner = (unsigned long)file_lock->fl_file;
-       } else if (file_lock->fl_flags & FL_POSIX) {
-               flock.l_flock.owner = (unsigned long)file_lock->fl_owner;
-               flock.l_flock.start = file_lock->fl_start;
-               flock.l_flock.end = file_lock->fl_end;
-       } else {
+       else if (!(file_lock->fl_flags & FL_POSIX))
                return -EINVAL;
-       }
+
+       flock.l_flock.owner = (unsigned long)file_lock->fl_owner;
        flock.l_flock.pid = file_lock->fl_pid;
+       flock.l_flock.start = file_lock->fl_start;
+       flock.l_flock.end = file_lock->fl_end;
 
        /* Somewhat ugly workaround for svc lockd.
         * lockd installs custom fl_lmops->lm_compare_owner that checks
index e4a6945..e6f6278 100644 (file)
@@ -249,7 +249,7 @@ static void as102_free_usb_stream_buffer(struct as102_dev_t *dev)
 
 static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev)
 {
-       int i, ret = 0;
+       int i;
 
        dev->stream = usb_alloc_coherent(dev->bus_adap.usb_dev,
                                       MAX_STREAM_URB * AS102_USB_BUF_SIZE,
@@ -280,7 +280,7 @@ static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev)
 
                dev->stream_urb[i] = urb;
        }
-       return ret;
+       return 0;
 }
 
 static void as102_usb_stop_stream(struct as102_dev_t *dev)
@@ -458,7 +458,6 @@ exit:
 
 static int as102_release(struct inode *inode, struct file *file)
 {
-       int ret = 0;
        struct as102_dev_t *dev = NULL;
 
        dev = file->private_data;
@@ -467,7 +466,7 @@ static int as102_release(struct inode *inode, struct file *file)
                kref_put(&dev->kref, as102_usb_release);
        }
 
-       return ret;
+       return 0;
 }
 
 MODULE_DEVICE_TABLE(usb, as102_usb_id_table);
index b2cd3a8..bbf236e 100644 (file)
@@ -737,7 +737,7 @@ static int bcm2048_set_region(struct bcm2048_device *bdev, u8 region)
        int err;
        u32 new_frequency = 0;
 
-       if (region > ARRAY_SIZE(region_configs))
+       if (region >= ARRAY_SIZE(region_configs))
                return -EINVAL;
 
        mutex_lock(&bdev->mutex);
index 68f6fe4..2632a80 100644 (file)
@@ -87,8 +87,6 @@ struct vpfe_fh {
        struct vpfe_video_device *video;
        /* Indicates whether this file handle is doing IO */
        u8 io_allowed;
-       /* Used to keep track priority of this instance */
-       enum v4l2_priority prio;
 };
 
 void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus,
index acc8184..d95c427 100644 (file)
@@ -415,7 +415,6 @@ static int vpfe_open(struct file *file)
        video->usrs++;
        /* Set io_allowed member to false */
        handle->io_allowed = 0;
-       v4l2_prio_open(&video->prio, &handle->prio);
        handle->video = video;
        file->private_data = &handle->vfh;
        mutex_unlock(&video->lock);
@@ -532,8 +531,8 @@ static int vpfe_release(struct file *file)
        }
        /* Decrement device users counter */
        video->usrs--;
-       /* Close the priority */
-       v4l2_prio_close(&video->prio, fh->prio);
+       v4l2_fh_del(&fh->vfh);
+       v4l2_fh_exit(&fh->vfh);
        /* If this is the last file handle */
        if (!video->usrs)
                video->initialized = 0;
@@ -945,7 +944,7 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id)
                goto unlock_out;
        }
        ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
-                                        core, s_std, std_id);
+                                        video, s_std, std_id);
        if (ret < 0) {
                v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n");
                video->stdid = V4L2_STD_UNKNOWN;
@@ -987,8 +986,10 @@ vpfe_enum_dv_timings(struct file *file, void *fh,
        struct vpfe_device *vpfe_dev = video->vpfe_dev;
        struct v4l2_subdev *subdev = video->current_ext_subdev->subdev;
 
+       timings->pad = 0;
+
        v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_dv_timings\n");
-       return v4l2_subdev_call(subdev, video, enum_dv_timings, timings);
+       return v4l2_subdev_call(subdev, pad, enum_dv_timings, timings);
 }
 
 /*
@@ -1219,8 +1220,16 @@ static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count)
        video->state = VPFE_VIDEO_BUFFER_QUEUED;
 
        ret = vpfe_start_capture(video);
-       if (ret)
+       if (ret) {
+               struct vpfe_cap_buffer *buf, *tmp;
+
+               vb2_buffer_done(&video->cur_frm->vb, VB2_BUF_STATE_QUEUED);
+               list_for_each_entry_safe(buf, tmp, &video->dma_queue, list) {
+                       list_del(&buf->list);
+                       vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+               }
                goto unlock_out;
+       }
 
        mutex_unlock(&video->lock);
 
@@ -1242,7 +1251,7 @@ static int vpfe_buffer_init(struct vb2_buffer *vb)
 }
 
 /* abort streaming and wait for last buffer */
-static int vpfe_stop_streaming(struct vb2_queue *vq)
+static void vpfe_stop_streaming(struct vb2_queue *vq)
 {
        struct vpfe_fh *fh = vb2_get_drv_priv(vq);
        struct vpfe_video_device *video = fh->video;
@@ -1265,7 +1274,6 @@ static int vpfe_stop_streaming(struct vb2_queue *vq)
                list_del(&video->next_frm->list);
                vb2_buffer_done(&video->next_frm->vb, VB2_BUF_STATE_ERROR);
        }
-       return 0;
 }
 
 static void vpfe_buf_cleanup(struct vb2_buffer *vb)
@@ -1590,8 +1598,6 @@ int vpfe_video_init(struct vpfe_video_device *video, const char *name)
        snprintf(video->video_dev.name, sizeof(video->video_dev.name),
                 "DAVINCI VIDEO %s %s", name, direction);
 
-       /* Initialize prio member of device object */
-       v4l2_prio_init(&video->prio);
        spin_lock_init(&video->irqlock);
        spin_lock_init(&video->dma_queue_lock);
        mutex_init(&video->lock);
@@ -1600,6 +1606,7 @@ int vpfe_video_init(struct vpfe_video_device *video, const char *name)
        if (ret < 0)
                return ret;
 
+       set_bit(V4L2_FL_USE_FH_PRIO, &video->video_dev.flags);
        video_set_drvdata(&video->video_dev, video);
 
        return 0;
index ca9a702..1b1b6c4 100644 (file)
@@ -102,8 +102,6 @@ struct vpfe_video_device {
         * user has selected
         */
        enum v4l2_memory                        memory;
-       /* Used to keep track of state of the priority */
-       struct v4l2_prio_state                  prio;
        /* number of open instances of the channel */
        u32                                     usrs;
        /* flag to indicate whether decoder is initialized */
index afbc2e5..4058022 100644 (file)
@@ -262,7 +262,7 @@ dt3155_buf_prepare(struct vb2_buffer *vb)
        return 0;
 }
 
-static int
+static void
 dt3155_stop_streaming(struct vb2_queue *q)
 {
        struct dt3155_priv *pd = vb2_get_drv_priv(q);
@@ -276,7 +276,6 @@ dt3155_stop_streaming(struct vb2_queue *q)
        }
        spin_unlock_irq(&pd->lock);
        msleep(45); /* irq hendler will stop the hardware */
-       return 0;
 }
 
 static void
@@ -907,8 +906,10 @@ dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (!pd)
                return -ENOMEM;
        pd->vdev = video_device_alloc();
-       if (!pd->vdev)
+       if (!pd->vdev) {
+               err = -ENOMEM;
                goto err_video_device_alloc;
+       }
        *pd->vdev = dt3155_vdev;
        pci_set_drvdata(pdev, pd);    /* for use in dt3155_remove() */
        video_set_drvdata(pd->vdev, pd);  /* for use in video_fops */
index b397aa3..da7b549 100644 (file)
@@ -516,7 +516,7 @@ static int go7007_start_streaming(struct vb2_queue *q, unsigned int count)
        return ret;
 }
 
-static int go7007_stop_streaming(struct vb2_queue *q)
+static void go7007_stop_streaming(struct vb2_queue *q)
 {
        struct go7007 *go = vb2_get_drv_priv(q);
        unsigned long flags;
@@ -538,7 +538,6 @@ static int go7007_stop_streaming(struct vb2_queue *q)
        /* Turn on Capture LED */
        if (go->board_id == GO7007_BOARDID_ADS_USBAV_709)
                go7007_write_addr(go, 0x3c82, 0x000d);
-       return 0;
 }
 
 static struct vb2_ops go7007_video_qops = {
@@ -666,7 +665,7 @@ static int go7007_s_std(struct go7007 *go)
                go->sensor_framerate = 30000;
        }
 
-       call_all(&go->v4l2_dev, core, s_std, go->std);
+       call_all(&go->v4l2_dev, video, s_std, go->std);
        set_capture_size(go, NULL, 0);
        return 0;
 }
index dc0026c..c8e5eb0 100644 (file)
@@ -79,7 +79,6 @@ for custom-built kernels, the following options need to be enabled in the
 kernel as built-in or modules:
 
        CONFIG_MODULES           - Enable loadable module support
-       CONFIG_KMOD              - Automatic kernel module loading
        CONFIG_FW_LOADER         - Hotplug firmware loading support
        CONFIG_I2C               - I2C support
        CONFIG_VIDEO_DEV         - Video For Linux
index 696a807..eaa2b09 100644 (file)
@@ -474,7 +474,6 @@ static const struct v4l2_ctrl_ops s2250_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops s2250_core_ops = {
        .log_status = s2250_log_status,
-       .s_std = s2250_s_std,
 };
 
 static const struct v4l2_subdev_audio_ops s2250_audio_ops = {
@@ -482,6 +481,7 @@ static const struct v4l2_subdev_audio_ops s2250_audio_ops = {
 };
 
 static const struct v4l2_subdev_video_ops s2250_video_ops = {
+       .s_std = s2250_s_std,
        .s_routing = s2250_s_video_routing,
        .s_mbus_fmt = s2250_s_mbus_fmt,
 };
index 6e2ca33..e40f7fb 100644 (file)
@@ -434,11 +434,15 @@ static const struct v4l2_subdev_core_ops saa7134_go7007_core_ops = {
        .g_ctrl = saa7134_go7007_g_ctrl,
        .s_ctrl = saa7134_go7007_s_ctrl,
        .queryctrl = saa7134_go7007_queryctrl,
+};
+
+static const struct v4l2_subdev_video_ops saa7134_go7007_video_ops = {
        .s_std = saa7134_go7007_s_std,
 };
 
 static const struct v4l2_subdev_ops saa7134_go7007_sd_ops = {
        .core = &saa7134_go7007_core_ops,
+       .video = &saa7134_go7007_video_ops,
 };
 
 /* --------------------------------------------------------------------------*/
index 65d351f..08d0d09 100644 (file)
@@ -1074,14 +1074,13 @@ static int msi3101_start_streaming(struct vb2_queue *vq, unsigned int count)
        return ret;
 }
 
-static int msi3101_stop_streaming(struct vb2_queue *vq)
+static void msi3101_stop_streaming(struct vb2_queue *vq)
 {
        struct msi3101_state *s = vb2_get_drv_priv(vq);
-       int ret;
+
        dev_dbg(&s->udev->dev, "%s:\n", __func__);
 
-       if (mutex_lock_interruptible(&s->v4l2_lock))
-               return -ERESTARTSYS;
+       mutex_lock(&s->v4l2_lock);
 
        if (s->udev)
                msi3101_isoc_cleanup(s);
@@ -1090,22 +1089,15 @@ static int msi3101_stop_streaming(struct vb2_queue *vq)
 
        /* according to tests, at least 700us delay is required  */
        msleep(20);
-       ret = msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0);
-       if (ret)
-               goto err_sleep_tuner;
-
-       /* sleep USB IF / ADC */
-       ret = msi3101_ctrl_msg(s, CMD_WREG, 0x01000003);
-       if (ret)
-               goto err_sleep_tuner;
+       if (!msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0)) {
+               /* sleep USB IF / ADC */
+               msi3101_ctrl_msg(s, CMD_WREG, 0x01000003);
+       }
 
-err_sleep_tuner:
        /* sleep tuner */
-       ret = v4l2_subdev_call(s->v4l2_subdev, core, s_power, 0);
+       v4l2_subdev_call(s->v4l2_subdev, core, s_power, 0);
 
        mutex_unlock(&s->v4l2_lock);
-
-       return ret;
 }
 
 static struct vb2_ops msi3101_vb2_ops = {
index f4dd32d..3367ccd 100644 (file)
@@ -89,10 +89,10 @@ static const struct tcm825x_reg rgb565      =       { 0x02, TCM825X_PICFMT };
 
 /* Our own specific controls */
 #define V4L2_CID_ALC                           V4L2_CID_PRIVATE_BASE
-#define V4L2_CID_H_EDGE_EN                     V4L2_CID_PRIVATE_BASE + 1
-#define V4L2_CID_V_EDGE_EN                     V4L2_CID_PRIVATE_BASE + 2
-#define V4L2_CID_LENS                          V4L2_CID_PRIVATE_BASE + 3
-#define V4L2_CID_MAX_EXPOSURE_TIME             V4L2_CID_PRIVATE_BASE + 4
+#define V4L2_CID_H_EDGE_EN                     (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_V_EDGE_EN                     (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_LENS                          (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_MAX_EXPOSURE_TIME             (V4L2_CID_PRIVATE_BASE + 4)
 #define V4L2_CID_LAST_PRIV                     V4L2_CID_MAX_EXPOSURE_TIME
 
 /*  Video controls  */
@@ -914,8 +914,8 @@ static int __init tcm825x_init(void)
 
        rval = i2c_add_driver(&tcm825x_i2c_driver);
        if (rval)
-               printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n",
-                      __func__);
+               pr_info("%s: failed registering " TCM825X_NAME "\n",
+                       __func__);
 
        return rval;
 }
index 9970fb1..8a29636 100644 (file)
@@ -21,8 +21,8 @@
 
 #define TCM825X_NAME "tcm825x"
 
-#define TCM825X_MASK(x)  x & 0x00ff
-#define TCM825X_ADDR(x) (x & 0xff00) >> 8
+#define TCM825X_MASK(x) (x & 0x00ff)
+#define TCM825X_ADDR(x) ((x & 0xff00) >> 8)
 
 /* The TCM825X I2C sensor chip has a fixed slave address of 0x3d. */
 #define TCM825X_I2C_ADDR       0x3d
index b9fe753..78b0fba 100644 (file)
@@ -4,9 +4,3 @@ config VIDEO_OMAP4
        select VIDEOBUF2_DMA_CONTIG
        ---help---
          Driver for an OMAP 4 ISS controller.
-
-config VIDEO_OMAP4_DEBUG
-       bool "OMAP 4 Camera debug messages"
-       depends on VIDEO_OMAP4
-       ---help---
-         Enable debug messages on OMAP 4 ISS controller driver.
index 61fbfcd..2e422dd 100644 (file)
@@ -204,7 +204,7 @@ void omap4iss_configure_bridge(struct iss_device *iss,
        iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL, isp5ctrl_val);
 }
 
-#if defined(DEBUG) && defined(ISS_ISR_DEBUG)
+#ifdef ISS_ISR_DEBUG
 static void iss_isr_dbg(struct iss_device *iss, u32 irqstatus)
 {
        static const char * const name[] = {
@@ -347,14 +347,14 @@ static irqreturn_t iss_isr(int irq, void *_iss)
                        omap4iss_resizer_isr(&iss->resizer,
                                             isp_irqstatus & resizer_events);
 
-#if defined(DEBUG) && defined(ISS_ISR_DEBUG)
+#ifdef ISS_ISR_DEBUG
                iss_isp_isr_dbg(iss, isp_irqstatus);
 #endif
        }
 
        omap4iss_flush(iss);
 
-#if defined(DEBUG) && defined(ISS_ISR_DEBUG)
+#ifdef ISS_ISR_DEBUG
        iss_isr_dbg(iss, irqstatus);
 #endif
 
@@ -734,18 +734,17 @@ static int iss_pipeline_is_last(struct media_entity *me)
 
 static int iss_reset(struct iss_device *iss)
 {
-       unsigned long timeout = 0;
+       unsigned int timeout;
 
        iss_reg_set(iss, OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG,
                    ISS_HL_SYSCONFIG_SOFTRESET);
 
-       while (iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG) &
-              ISS_HL_SYSCONFIG_SOFTRESET) {
-               if (timeout++ > 100) {
-                       dev_alert(iss->dev, "cannot reset ISS\n");
-                       return -ETIMEDOUT;
-               }
-               usleep_range(10, 10);
+       timeout = iss_poll_condition_timeout(
+               !(iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG) &
+               ISS_HL_SYSCONFIG_SOFTRESET), 1000, 10, 100);
+       if (timeout) {
+               dev_err(iss->dev, "ISS reset timeout\n");
+               return -ETIMEDOUT;
        }
 
        iss->crashed = 0;
@@ -754,7 +753,7 @@ static int iss_reset(struct iss_device *iss)
 
 static int iss_isp_reset(struct iss_device *iss)
 {
-       unsigned long timeout = 0;
+       unsigned int timeout;
 
        /* Fist, ensure that the ISP is IDLE (no transactions happening) */
        iss_reg_update(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_SYSCONFIG,
@@ -763,29 +762,24 @@ static int iss_isp_reset(struct iss_device *iss)
 
        iss_reg_set(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL, ISP5_CTRL_MSTANDBY);
 
-       for (;;) {
-               if (iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL) &
-                   ISP5_CTRL_MSTANDBY_WAIT)
-                       break;
-               if (timeout++ > 1000) {
-                       dev_alert(iss->dev, "cannot set ISP5 to standby\n");
-                       return -ETIMEDOUT;
-               }
-               usleep_range(1000, 1500);
+       timeout = iss_poll_condition_timeout(
+               iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL) &
+               ISP5_CTRL_MSTANDBY_WAIT, 1000000, 1000, 1500);
+       if (timeout) {
+               dev_err(iss->dev, "ISP5 standby timeout\n");
+               return -ETIMEDOUT;
        }
 
        /* Now finally, do the reset */
        iss_reg_set(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_SYSCONFIG,
                    ISP5_SYSCONFIG_SOFTRESET);
 
-       timeout = 0;
-       while (iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_SYSCONFIG) &
-              ISP5_SYSCONFIG_SOFTRESET) {
-               if (timeout++ > 1000) {
-                       dev_alert(iss->dev, "cannot reset ISP5\n");
-                       return -ETIMEDOUT;
-               }
-               usleep_range(1000, 1500);
+       timeout = iss_poll_condition_timeout(
+               !(iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_SYSCONFIG) &
+               ISP5_SYSCONFIG_SOFTRESET), 1000000, 1000, 1500);
+       if (timeout) {
+               dev_err(iss->dev, "ISP5 reset timeout\n");
+               return -ETIMEDOUT;
        }
 
        return 0;
index 346db92..05cd9bf 100644 (file)
@@ -233,4 +233,18 @@ void iss_reg_update(struct iss_device *iss, enum iss_mem_resources res,
        iss_reg_write(iss, res, offset, (v & ~clr) | set);
 }
 
+#define iss_poll_condition_timeout(cond, timeout, min_ival, max_ival)  \
+({                                                                     \
+       unsigned long __timeout = jiffies + usecs_to_jiffies(timeout);  \
+       unsigned int __min_ival = (min_ival);                           \
+       unsigned int __max_ival = (max_ival);                           \
+       bool __cond;                                                    \
+       while (!(__cond = (cond))) {                                    \
+               if (time_after(jiffies, __timeout))                     \
+                       break;                                          \
+               usleep_range(__min_ival, __max_ival);                   \
+       }                                                               \
+       !__cond;                                                        \
+})
+
 #endif /* _OMAP4_ISS_H_ */
index 61fc350..bf8a657 100644 (file)
@@ -487,9 +487,7 @@ static void csi2_irq_status_set(struct iss_csi2_device *csi2, int enable)
  */
 int omap4iss_csi2_reset(struct iss_csi2_device *csi2)
 {
-       u8 soft_reset_retries = 0;
-       u32 reg;
-       int i;
+       unsigned int timeout;
 
        if (!csi2->available)
                return -ENODEV;
@@ -500,37 +498,22 @@ int omap4iss_csi2_reset(struct iss_csi2_device *csi2)
        iss_reg_set(csi2->iss, csi2->regs1, CSI2_SYSCONFIG,
                    CSI2_SYSCONFIG_SOFT_RESET);
 
-       do {
-               reg = iss_reg_read(csi2->iss, csi2->regs1, CSI2_SYSSTATUS)
-                   & CSI2_SYSSTATUS_RESET_DONE;
-               if (reg == CSI2_SYSSTATUS_RESET_DONE)
-                       break;
-               soft_reset_retries++;
-               if (soft_reset_retries < 5)
-                       usleep_range(100, 100);
-       } while (soft_reset_retries < 5);
-
-       if (soft_reset_retries == 5) {
-               dev_err(csi2->iss->dev,
-                       "CSI2: Soft reset try count exceeded!\n");
+       timeout = iss_poll_condition_timeout(
+               iss_reg_read(csi2->iss, csi2->regs1, CSI2_SYSSTATUS) &
+               CSI2_SYSSTATUS_RESET_DONE, 500, 100, 200);
+       if (timeout) {
+               dev_err(csi2->iss->dev, "CSI2: Soft reset timeout!\n");
                return -EBUSY;
        }
 
        iss_reg_set(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_CFG,
                    CSI2_COMPLEXIO_CFG_RESET_CTRL);
 
-       i = 100;
-       do {
-               reg = iss_reg_read(csi2->iss, csi2->phy->phy_regs, REGISTER1)
-                   & REGISTER1_RESET_DONE_CTRLCLK;
-               if (reg == REGISTER1_RESET_DONE_CTRLCLK)
-                       break;
-               usleep_range(100, 100);
-       } while (--i > 0);
-
-       if (i == 0) {
-               dev_err(csi2->iss->dev,
-                       "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n");
+       timeout = iss_poll_condition_timeout(
+               iss_reg_read(csi2->iss, csi2->phy->phy_regs, REGISTER1) &
+               REGISTER1_RESET_DONE_CTRLCLK, 10000, 100, 500);
+       if (timeout) {
+               dev_err(csi2->iss->dev, "CSI2: CSI2_96M_FCLK reset timeout!\n");
                return -EBUSY;
        }
 
index 878e4a3..9dccdb1 100644 (file)
@@ -140,7 +140,7 @@ enum iss_video_dmaqueue_flags {
  *             if there was no buffer previously queued.
  */
 struct iss_video_operations {
-       int(*queue)(struct iss_video *video, struct iss_buffer *buffer);
+       int (*queue)(struct iss_video *video, struct iss_buffer *buffer);
 };
 
 struct iss_video {
index 104ee8a..093df6b 100644 (file)
@@ -1032,13 +1032,12 @@ err:
        return ret;
 }
 
-static int rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
+static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
 {
        struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
        dev_dbg(&s->udev->dev, "%s:\n", __func__);
 
-       if (mutex_lock_interruptible(&s->v4l2_lock))
-               return -ERESTARTSYS;
+       mutex_lock(&s->v4l2_lock);
 
        rtl2832_sdr_kill_urbs(s);
        rtl2832_sdr_free_urbs(s);
@@ -1053,8 +1052,6 @@ static int rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
                s->d->props->power_ctrl(s->d, 0);
 
        mutex_unlock(&s->v4l2_lock);
-
-       return 0;
 }
 
 static struct vb2_ops rtl2832_sdr_vb2_ops = {
index 8a917f0..37ca722 100644 (file)
@@ -53,7 +53,7 @@ enum sn9c102_frame_state {
 };
 
 struct sn9c102_frame_t {
-       voidbufmem;
+       void *bufmem;
        struct v4l2_buffer buf;
        enum sn9c102_frame_state state;
        struct list_head frame;
@@ -99,17 +99,17 @@ static DEFINE_MUTEX(sn9c102_sysfs_lock);
 static DECLARE_RWSEM(sn9c102_dev_lock);
 
 struct sn9c102_device {
-       struct video_devicev4ldev;
+       struct video_device *v4ldev;
 
        struct v4l2_device v4l2_dev;
 
        enum sn9c102_bridge bridge;
        struct sn9c102_sensor sensor;
 
-       struct usb_deviceusbdev;
-       struct urburb[SN9C102_URBS];
-       voidtransfer_buffer[SN9C102_URBS];
-       u8control_buffer;
+       struct usb_device *usbdev;
+       struct urb *urb[SN9C102_URBS];
+       void *transfer_buffer[SN9C102_URBS];
+       u8 *control_buffer;
 
        struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES];
        struct list_head inqueue, outqueue;
@@ -139,28 +139,28 @@ struct sn9c102_device {
 /*****************************************************************************/
 
 struct sn9c102_device*
-sn9c102_match_id(struct sn9c102_devicecam, const struct usb_device_id *id)
+sn9c102_match_id(struct sn9c102_device *cam, const struct usb_device_id *id)
 {
        return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL;
 }
 
 
 void
-sn9c102_attach_sensor(struct sn9c102_devicecam,
-                     const struct sn9c102_sensorsensor)
+sn9c102_attach_sensor(struct sn9c102_device *cam,
+                     const struct sn9c102_sensor *sensor)
 {
        memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor));
 }
 
 
 enum sn9c102_bridge
-sn9c102_get_bridge(struct sn9c102_devicecam)
+sn9c102_get_bridge(struct sn9c102_device *cam)
 {
        return cam->bridge;
 }
 
 
-struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam)
+struct sn9c102_sensor *sn9c102_get_sensor(struct sn9c102_device *cam)
 {
        return &cam->sensor;
 }
@@ -198,9 +198,9 @@ do {                                                                          \
        }                                                                     \
 } while (0)
 #else
-#      define DBG(level, fmt, args...) do {;} while(0)
-#      define V4LDBG(level, name, cmd) do {;} while(0)
-#      define KDBG(level, fmt, args...) do {;} while(0)
+#      define DBG(level, fmt, args...) do { ; } while (0)
+#      define V4LDBG(level, name, cmd) do { ; } while (0)
+#      define KDBG(level, fmt, args...) do { ; } while (0)
 #endif
 
 #undef PDBG
@@ -209,6 +209,6 @@ dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__,   \
         __LINE__ , ## args)
 
 #undef PDBGG
-#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
+#define PDBGG(fmt, args...) do { ; } while (0) /* placeholder */
 
 #endif /* _SN9C102_H_ */
index 71f594f..98b3057 100644 (file)
@@ -139,15 +139,15 @@ static int (*sn9c102_sensor_table[])(struct sn9c102_device *) = {
 /*****************************************************************************/
 
 static u32
-sn9c102_request_buffers(struct sn9c102_devicecam, u32 count,
+sn9c102_request_buffers(struct sn9c102_device *cam, u32 count,
                        enum sn9c102_io_method io)
 {
-       struct v4l2_pix_formatp = &(cam->sensor.pix_format);
-       struct v4l2_rectr = &(cam->sensor.cropcap.bounds);
+       struct v4l2_pix_format *p = &(cam->sensor.pix_format);
+       struct v4l2_rect *r = &(cam->sensor.cropcap.bounds);
        size_t imagesize = cam->module_param.force_munmap || io == IO_READ ?
                           (p->width * p->height * p->priv) / 8 :
                           (r->width * r->height * p->priv) / 8;
-       voidbuff = NULL;
+       void *buff = NULL;
        u32 i;
 
        if (count > SN9C102_MAX_FRAMES)
@@ -180,7 +180,7 @@ sn9c102_request_buffers(struct sn9c102_device* cam, u32 count,
 }
 
 
-static void sn9c102_release_buffers(struct sn9c102_devicecam)
+static void sn9c102_release_buffers(struct sn9c102_device *cam)
 {
        if (cam->nbuffers) {
                vfree(cam->frame[0].bufmem);
@@ -190,7 +190,7 @@ static void sn9c102_release_buffers(struct sn9c102_device* cam)
 }
 
 
-static void sn9c102_empty_framequeues(struct sn9c102_devicecam)
+static void sn9c102_empty_framequeues(struct sn9c102_device *cam)
 {
        u32 i;
 
@@ -204,7 +204,7 @@ static void sn9c102_empty_framequeues(struct sn9c102_device* cam)
 }
 
 
-static void sn9c102_requeue_outqueue(struct sn9c102_devicecam)
+static void sn9c102_requeue_outqueue(struct sn9c102_device *cam)
 {
        struct sn9c102_frame_t *i;
 
@@ -217,7 +217,7 @@ static void sn9c102_requeue_outqueue(struct sn9c102_device* cam)
 }
 
 
-static void sn9c102_queue_unusedframes(struct sn9c102_devicecam)
+static void sn9c102_queue_unusedframes(struct sn9c102_device *cam)
 {
        unsigned long lock_flags;
        u32 i;
@@ -237,11 +237,11 @@ static void sn9c102_queue_unusedframes(struct sn9c102_device* cam)
    Write a sequence of count value/register pairs. Returns -1 after the first
    failed write, or 0 for no errors.
 */
-int sn9c102_write_regs(struct sn9c102_devicecam, const u8 valreg[][2],
+int sn9c102_write_regs(struct sn9c102_device *cam, const u8 valreg[][2],
                       int count)
 {
-       struct usb_deviceudev = cam->usbdev;
-       u8buff = cam->control_buffer;
+       struct usb_device *udev = cam->usbdev;
+       u8 *buff = cam->control_buffer;
        int i, res;
 
        for (i = 0; i < count; i++) {
@@ -273,10 +273,10 @@ int sn9c102_write_regs(struct sn9c102_device* cam, const u8 valreg[][2],
 }
 
 
-int sn9c102_write_reg(struct sn9c102_devicecam, u8 value, u16 index)
+int sn9c102_write_reg(struct sn9c102_device *cam, u8 value, u16 index)
 {
-       struct usb_deviceudev = cam->usbdev;
-       u8buff = cam->control_buffer;
+       struct usb_device *udev = cam->usbdev;
+       u8 *buff = cam->control_buffer;
        int res;
 
        if (index >= ARRAY_SIZE(cam->reg))
@@ -299,10 +299,10 @@ int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index)
 
 
 /* NOTE: with the SN9C10[123] reading some registers always returns 0 */
-int sn9c102_read_reg(struct sn9c102_devicecam, u16 index)
+int sn9c102_read_reg(struct sn9c102_device *cam, u16 index)
 {
-       struct usb_deviceudev = cam->usbdev;
-       u8buff = cam->control_buffer;
+       struct usb_device *udev = cam->usbdev;
+       u8 *buff = cam->control_buffer;
        int res;
 
        res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
@@ -315,7 +315,7 @@ int sn9c102_read_reg(struct sn9c102_device* cam, u16 index)
 }
 
 
-int sn9c102_pread_reg(struct sn9c102_devicecam, u16 index)
+int sn9c102_pread_reg(struct sn9c102_device *cam, u16 index)
 {
        if (index >= ARRAY_SIZE(cam->reg))
                return -1;
@@ -325,8 +325,8 @@ int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index)
 
 
 static int
-sn9c102_i2c_wait(struct sn9c102_devicecam,
-                const struct sn9c102_sensorsensor)
+sn9c102_i2c_wait(struct sn9c102_device *cam,
+                const struct sn9c102_sensor *sensor)
 {
        int i, r;
 
@@ -346,8 +346,8 @@ sn9c102_i2c_wait(struct sn9c102_device* cam,
 
 
 static int
-sn9c102_i2c_detect_read_error(struct sn9c102_devicecam,
-                             const struct sn9c102_sensorsensor)
+sn9c102_i2c_detect_read_error(struct sn9c102_device *cam,
+                             const struct sn9c102_sensor *sensor)
 {
        int r , err = 0;
 
@@ -368,22 +368,23 @@ sn9c102_i2c_detect_read_error(struct sn9c102_device* cam,
 
 
 static int
-sn9c102_i2c_detect_write_error(struct sn9c102_devicecam,
-                              const struct sn9c102_sensorsensor)
+sn9c102_i2c_detect_write_error(struct sn9c102_device *cam,
+                              const struct sn9c102_sensor *sensor)
 {
        int r;
+
        r = sn9c102_read_reg(cam, 0x08);
        return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0;
 }
 
 
 int
-sn9c102_i2c_try_raw_read(struct sn9c102_devicecam,
-                        const struct sn9c102_sensorsensor, u8 data0,
+sn9c102_i2c_try_raw_read(struct sn9c102_device *cam,
+                        const struct sn9c102_sensor *sensor, u8 data0,
                         u8 data1, u8 n, u8 buffer[])
 {
-       struct usb_deviceudev = cam->usbdev;
-       u8data = cam->control_buffer;
+       struct usb_device *udev = cam->usbdev;
+       u8 *data = cam->control_buffer;
        int i = 0, err = 0, res;
 
        /* Write cycle */
@@ -437,12 +438,12 @@ sn9c102_i2c_try_raw_read(struct sn9c102_device* cam,
 
 
 int
-sn9c102_i2c_try_raw_write(struct sn9c102_devicecam,
-                         const struct sn9c102_sensorsensor, u8 n, u8 data0,
+sn9c102_i2c_try_raw_write(struct sn9c102_device *cam,
+                         const struct sn9c102_sensor *sensor, u8 n, u8 data0,
                          u8 data1, u8 data2, u8 data3, u8 data4, u8 data5)
 {
-       struct usb_deviceudev = cam->usbdev;
-       u8data = cam->control_buffer;
+       struct usb_device *udev = cam->usbdev;
+       u8 *data = cam->control_buffer;
        int err = 0, res;
 
        /* Write cycle. It usually is address + value */
@@ -476,16 +477,16 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
 
 
 int
-sn9c102_i2c_try_read(struct sn9c102_devicecam,
-                    const struct sn9c102_sensorsensor, u8 address)
+sn9c102_i2c_try_read(struct sn9c102_device *cam,
+                    const struct sn9c102_sensor *sensor, u8 address)
 {
        return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id,
                                        address, 1, NULL);
 }
 
 
-static int sn9c102_i2c_try_write(struct sn9c102_devicecam,
-                                const struct sn9c102_sensorsensor,
+static int sn9c102_i2c_try_write(struct sn9c102_device *cam,
+                                const struct sn9c102_sensor *sensor,
                                 u8 address, u8 value)
 {
        return sn9c102_i2c_try_raw_write(cam, sensor, 3,
@@ -494,20 +495,20 @@ static int sn9c102_i2c_try_write(struct sn9c102_device* cam,
 }
 
 
-int sn9c102_i2c_read(struct sn9c102_devicecam, u8 address)
+int sn9c102_i2c_read(struct sn9c102_device *cam, u8 address)
 {
        return sn9c102_i2c_try_read(cam, &cam->sensor, address);
 }
 
 
-int sn9c102_i2c_write(struct sn9c102_devicecam, u8 address, u8 value)
+int sn9c102_i2c_write(struct sn9c102_device *cam, u8 address, u8 value)
 {
        return sn9c102_i2c_try_write(cam, &cam->sensor, address, value);
 }
 
 /*****************************************************************************/
 
-static size_t sn9c102_sof_length(struct sn9c102_devicecam)
+static size_t sn9c102_sof_length(struct sn9c102_device *cam)
 {
        switch (cam->bridge) {
        case BRIDGE_SN9C101:
@@ -525,7 +526,7 @@ static size_t sn9c102_sof_length(struct sn9c102_device* cam)
 
 
 static void*
-sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len)
+sn9c102_find_sof_header(struct sn9c102_device *cam, void *mem, size_t len)
 {
        static const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
        const char *m = mem;
@@ -547,7 +548,7 @@ sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len)
                }
 
                /* Search for the SOF marker (fixed part) in the header */
-               for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) {
+               for (j = 0, b = cam->sof.bytesread; j+b < sizeof(marker); j++) {
                        if (unlikely(i+j == len))
                                return NULL;
                        if (*(m+i+j) == marker[cam->sof.bytesread]) {
@@ -570,7 +571,7 @@ sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len)
 
 
 static void*
-sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len)
+sn9c102_find_eof_header(struct sn9c102_device *cam, void *mem, size_t len)
 {
        static const u8 eof_header[4][4] = {
                {0x00, 0x00, 0x00, 0x00},
@@ -600,7 +601,7 @@ sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len)
 
 
 static void
-sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f)
+sn9c102_write_jpegheader(struct sn9c102_device *cam, struct sn9c102_frame_t *f)
 {
        static const u8 jpeg_header[589] = {
                0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05,
@@ -687,8 +688,8 @@ sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f)
 
 static void sn9c102_urb_complete(struct urb *urb)
 {
-       struct sn9c102_devicecam = urb->context;
-       struct sn9c102_frame_t** f;
+       struct sn9c102_device *cam = urb->context;
+       struct sn9c102_frame_t **f;
        size_t imagesize, soflen;
        u8 i;
        int err = 0;
@@ -787,7 +788,7 @@ end_of_frame:
 
                                        b = (*f)->buf.bytesused;
                                        (*f)->state = F_DONE;
-                                       (*f)->buf.sequence= ++cam->frame_count;
+                                       (*f)->buf.sequence = ++cam->frame_count;
 
                                        spin_lock(&cam->queue_lock);
                                        list_move_tail(&(*f)->frame,
@@ -796,7 +797,7 @@ end_of_frame:
                                                (*f) = list_entry(
                                                        cam->inqueue.next,
                                                        struct sn9c102_frame_t,
-                                                       frame );
+                                                       frame);
                                        else
                                                (*f) = NULL;
                                        spin_unlock(&cam->queue_lock);
@@ -883,11 +884,11 @@ resubmit_urb:
 }
 
 
-static int sn9c102_start_transfer(struct sn9c102_devicecam)
+static int sn9c102_start_transfer(struct sn9c102_device *cam)
 {
        struct usb_device *udev = cam->usbdev;
-       struct urburb;
-       struct usb_host_interfacealtsetting = usb_altnum_to_altsetting(
+       struct urb *urb;
+       struct usb_host_interface *altsetting = usb_altnum_to_altsetting(
                                                    usb_ifnum_to_if(udev, 0),
                                                    SN9C102_ALTERNATE_SETTING);
        const unsigned int psz = le16_to_cpu(altsetting->
@@ -971,7 +972,7 @@ free_buffers:
 }
 
 
-static int sn9c102_stop_transfer(struct sn9c102_devicecam)
+static int sn9c102_stop_transfer(struct sn9c102_device *cam)
 {
        struct usb_device *udev = cam->usbdev;
        s8 i;
@@ -994,7 +995,7 @@ static int sn9c102_stop_transfer(struct sn9c102_device* cam)
 }
 
 
-static int sn9c102_stream_interrupt(struct sn9c102_devicecam)
+static int sn9c102_stream_interrupt(struct sn9c102_device *cam)
 {
        cam->stream = STREAM_INTERRUPT;
        wait_event_timeout(cam->wait_stream,
@@ -1017,10 +1018,10 @@ static int sn9c102_stream_interrupt(struct sn9c102_device* cam)
 /*****************************************************************************/
 
 #ifdef CONFIG_VIDEO_ADV_DEBUG
-static u16 sn9c102_strtou16(const char* buff, size_t len, ssize_t* count)
+static u16 sn9c102_strtou16(const char *buff, size_t len, ssize_t *count)
 {
        char str[7];
-       charendp;
+       char *endp;
        unsigned long val;
 
        if (len < 6) {
@@ -1048,10 +1049,10 @@ static u16 sn9c102_strtou16(const char* buff, size_t len, ssize_t* count)
    NOTE 2: buffers are PAGE_SIZE long
 */
 
-static ssize_t sn9c102_show_reg(struct devicecd,
-                               struct device_attribute *attr, charbuf)
+static ssize_t sn9c102_show_reg(struct device *cd,
+                               struct device_attribute *attr, char *buf)
 {
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
        ssize_t count;
 
        if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
@@ -1072,10 +1073,10 @@ static ssize_t sn9c102_show_reg(struct device* cd,
 
 
 static ssize_t
-sn9c102_store_reg(struct devicecd, struct device_attribute *attr,
-                 const charbuf, size_t len)
+sn9c102_store_reg(struct device *cd, struct device_attribute *attr,
+                 const char *buf, size_t len)
 {
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
        u16 index;
        ssize_t count;
 
@@ -1105,10 +1106,10 @@ sn9c102_store_reg(struct device* cd, struct device_attribute *attr,
 }
 
 
-static ssize_t sn9c102_show_val(struct devicecd,
-                               struct device_attribute *attr, charbuf)
+static ssize_t sn9c102_show_val(struct device *cd,
+                               struct device_attribute *attr, char *buf)
 {
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
        ssize_t count;
        int val;
 
@@ -1138,10 +1139,10 @@ static ssize_t sn9c102_show_val(struct device* cd,
 
 
 static ssize_t
-sn9c102_store_val(struct devicecd, struct device_attribute *attr,
-                 const charbuf, size_t len)
+sn9c102_store_val(struct device *cd, struct device_attribute *attr,
+                 const char *buf, size_t len)
 {
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
        u16 value;
        ssize_t count;
        int err;
@@ -1177,10 +1178,10 @@ sn9c102_store_val(struct device* cd, struct device_attribute *attr,
 }
 
 
-static ssize_t sn9c102_show_i2c_reg(struct devicecd,
-                                   struct device_attribute *attr, charbuf)
+static ssize_t sn9c102_show_i2c_reg(struct device *cd,
+                                   struct device_attribute *attr, char *buf)
 {
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
        ssize_t count;
 
        if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
@@ -1203,10 +1204,10 @@ static ssize_t sn9c102_show_i2c_reg(struct device* cd,
 
 
 static ssize_t
-sn9c102_store_i2c_reg(struct devicecd, struct device_attribute *attr,
-                     const charbuf, size_t len)
+sn9c102_store_i2c_reg(struct device *cd, struct device_attribute *attr,
+                     const char *buf, size_t len)
 {
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
        u16 index;
        ssize_t count;
 
@@ -1236,10 +1237,10 @@ sn9c102_store_i2c_reg(struct device* cd, struct device_attribute *attr,
 }
 
 
-static ssize_t sn9c102_show_i2c_val(struct devicecd,
-                                   struct device_attribute *attr, charbuf)
+static ssize_t sn9c102_show_i2c_val(struct device *cd,
+                                   struct device_attribute *attr, char *buf)
 {
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
        ssize_t count;
        int val;
 
@@ -1274,10 +1275,10 @@ static ssize_t sn9c102_show_i2c_val(struct device* cd,
 
 
 static ssize_t
-sn9c102_store_i2c_val(struct devicecd, struct device_attribute *attr,
-                     const charbuf, size_t len)
+sn9c102_store_i2c_val(struct device *cd, struct device_attribute *attr,
+                     const char *buf, size_t len)
 {
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
        u16 value;
        ssize_t count;
        int err;
@@ -1319,10 +1320,10 @@ sn9c102_store_i2c_val(struct device* cd, struct device_attribute *attr,
 
 
 static ssize_t
-sn9c102_store_green(struct devicecd, struct device_attribute *attr,
-                   const charbuf, size_t len)
+sn9c102_store_green(struct device *cd, struct device_attribute *attr,
+                   const char *buf, size_t len)
 {
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
        enum sn9c102_bridge bridge;
        ssize_t res = 0;
        u16 value;
@@ -1350,7 +1351,8 @@ sn9c102_store_green(struct device* cd, struct device_attribute *attr,
        case BRIDGE_SN9C102:
                if (value > 0x0f)
                        return -EINVAL;
-               if ((res = sn9c102_store_reg(cd, attr, "0x11", 4)) >= 0)
+               res = sn9c102_store_reg(cd, attr, "0x11", 4);
+               if (res >= 0)
                        res = sn9c102_store_val(cd, attr, buf, len);
                break;
        case BRIDGE_SN9C103:
@@ -1358,7 +1360,8 @@ sn9c102_store_green(struct device* cd, struct device_attribute *attr,
        case BRIDGE_SN9C120:
                if (value > 0x7f)
                        return -EINVAL;
-               if ((res = sn9c102_store_reg(cd, attr, "0x07", 4)) >= 0)
+               res = sn9c102_store_reg(cd, attr, "0x07", 4);
+               if (res >= 0)
                        res = sn9c102_store_val(cd, attr, buf, len);
                break;
        }
@@ -1368,8 +1371,8 @@ sn9c102_store_green(struct device* cd, struct device_attribute *attr,
 
 
 static ssize_t
-sn9c102_store_blue(struct devicecd, struct device_attribute *attr,
-                  const charbuf, size_t len)
+sn9c102_store_blue(struct device *cd, struct device_attribute *attr,
+                  const char *buf, size_t len)
 {
        ssize_t res = 0;
        u16 value;
@@ -1379,7 +1382,8 @@ sn9c102_store_blue(struct device* cd, struct device_attribute *attr,
        if (!count || value > 0x7f)
                return -EINVAL;
 
-       if ((res = sn9c102_store_reg(cd, attr, "0x06", 4)) >= 0)
+       res = sn9c102_store_reg(cd, attr, "0x06", 4);
+       if (res >= 0)
                res = sn9c102_store_val(cd, attr, buf, len);
 
        return res;
@@ -1387,8 +1391,8 @@ sn9c102_store_blue(struct device* cd, struct device_attribute *attr,
 
 
 static ssize_t
-sn9c102_store_red(struct devicecd, struct device_attribute *attr,
-                 const charbuf, size_t len)
+sn9c102_store_red(struct device *cd, struct device_attribute *attr,
+                 const char *buf, size_t len)
 {
        ssize_t res = 0;
        u16 value;
@@ -1397,19 +1401,19 @@ sn9c102_store_red(struct device* cd, struct device_attribute *attr,
        value = sn9c102_strtou16(buf, len, &count);
        if (!count || value > 0x7f)
                return -EINVAL;
-
-       if ((res = sn9c102_store_reg(cd, attr, "0x05", 4)) >= 0)
+       res = sn9c102_store_reg(cd, attr, "0x05", 4);
+       if (res >= 0)
                res = sn9c102_store_val(cd, attr, buf, len);
 
        return res;
 }
 
 
-static ssize_t sn9c102_show_frame_header(struct devicecd,
+static ssize_t sn9c102_show_frame_header(struct device *cd,
                                         struct device_attribute *attr,
-                                        charbuf)
+                                        char *buf)
 {
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
        ssize_t count;
 
        cam = video_get_drvdata(container_of(cd, struct video_device, dev));
@@ -1437,7 +1441,7 @@ static DEVICE_ATTR(red, S_IWUSR, NULL, sn9c102_store_red);
 static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL);
 
 
-static int sn9c102_create_sysfs(struct sn9c102_devicecam)
+static int sn9c102_create_sysfs(struct sn9c102_device *cam)
 {
        struct device *dev = &(cam->v4ldev->dev);
        int err = 0;
@@ -1498,7 +1502,7 @@ err_out:
 /*****************************************************************************/
 
 static int
-sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix)
+sn9c102_set_pix_format(struct sn9c102_device *cam, struct v4l2_pix_format *pix)
 {
        int err = 0;
 
@@ -1538,8 +1542,8 @@ sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix)
 
 
 static int
-sn9c102_set_compression(struct sn9c102_devicecam,
-                       struct v4l2_jpegcompressioncompression)
+sn9c102_set_compression(struct sn9c102_device *cam,
+                       struct v4l2_jpegcompression *compression)
 {
        int i, err = 0;
 
@@ -1586,7 +1590,7 @@ sn9c102_set_compression(struct sn9c102_device* cam,
 }
 
 
-static int sn9c102_set_scale(struct sn9c102_devicecam, u8 scale)
+static int sn9c102_set_scale(struct sn9c102_device *cam, u8 scale)
 {
        u8 r = 0;
        int err = 0;
@@ -1609,9 +1613,9 @@ static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale)
 }
 
 
-static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
+static int sn9c102_set_crop(struct sn9c102_device *cam, struct v4l2_rect *rect)
 {
-       struct sn9c102_sensors = &cam->sensor;
+       struct sn9c102_sensor *s = &cam->sensor;
        u8 h_start = (u8)(rect->left - s->cropcap.bounds.left),
           v_start = (u8)(rect->top - s->cropcap.bounds.top),
           h_size = (u8)(rect->width / 16),
@@ -1632,12 +1636,12 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
 }
 
 
-static int sn9c102_init(struct sn9c102_devicecam)
+static int sn9c102_init(struct sn9c102_device *cam)
 {
-       struct sn9c102_sensors = &cam->sensor;
+       struct sn9c102_sensor *s = &cam->sensor;
        struct v4l2_control ctrl;
        struct v4l2_queryctrl *qctrl;
-       struct v4l2_rectrect;
+       struct v4l2_rect *rect;
        u8 i = 0;
        int err = 0;
 
@@ -1669,7 +1673,7 @@ static int sn9c102_init(struct sn9c102_device* cam)
                    cam->bridge == BRIDGE_SN9C102 ||
                    cam->bridge == BRIDGE_SN9C103) {
                        if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG)
-                               s->pix_format.pixelformat= V4L2_PIX_FMT_SBGGR8;
+                               s->pix_format.pixelformat = V4L2_PIX_FMT_SBGGR8;
                        cam->compression.quality =  cam->reg[0x17] & 0x01 ?
                                                    0 : 1;
                } else {
@@ -1761,7 +1765,7 @@ static void sn9c102_release_resources(struct kref *kref)
 
 static int sn9c102_open(struct file *filp)
 {
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
        int err = 0;
 
        /*
@@ -1873,7 +1877,7 @@ out:
 
 static int sn9c102_release(struct file *filp)
 {
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
 
        down_write(&sn9c102_dev_lock);
 
@@ -1895,10 +1899,10 @@ static int sn9c102_release(struct file *filp)
 
 
 static ssize_t
-sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
+sn9c102_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
 {
        struct sn9c102_device *cam = video_drvdata(filp);
-       struct sn9c102_frame_t* f, * i;
+       struct sn9c102_frame_t *f, *i;
        unsigned long lock_flags;
        long timeout;
        int err = 0;
@@ -1927,7 +1931,7 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
        }
 
        if (cam->io == IO_NONE) {
-               if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) {
+               if (!sn9c102_request_buffers(cam, cam->nreadbuffers, IO_READ)) {
                        DBG(1, "read() failed, not enough memory");
                        mutex_unlock(&cam->fileop_mutex);
                        return -ENOMEM;
@@ -1954,17 +1958,17 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
                }
                if (!cam->module_param.frame_timeout) {
                        err = wait_event_interruptible
-                             ( cam->wait_frame,
+                             (cam->wait_frame,
                                (!list_empty(&cam->outqueue)) ||
                                (cam->state & DEV_DISCONNECTED) ||
-                               (cam->state & DEV_MISCONFIGURED) );
+                               (cam->state & DEV_MISCONFIGURED));
                        if (err) {
                                mutex_unlock(&cam->fileop_mutex);
                                return err;
                        }
                } else {
                        timeout = wait_event_interruptible_timeout
-                                 ( cam->wait_frame,
+                                 (cam->wait_frame,
                                    (!list_empty(&cam->outqueue)) ||
                                    (cam->state & DEV_DISCONNECTED) ||
                                    (cam->state & DEV_MISCONFIGURED),
@@ -2024,7 +2028,7 @@ exit:
 static unsigned int sn9c102_poll(struct file *filp, poll_table *wait)
 {
        struct sn9c102_device *cam = video_drvdata(filp);
-       struct sn9c102_frame_tf;
+       struct sn9c102_frame_t *f;
        unsigned long lock_flags;
        unsigned int mask = 0;
 
@@ -2076,17 +2080,17 @@ error:
 }
 
 
-static void sn9c102_vm_open(struct vm_area_structvma)
+static void sn9c102_vm_open(struct vm_area_struct *vma)
 {
-       struct sn9c102_frame_tf = vma->vm_private_data;
+       struct sn9c102_frame_t *f = vma->vm_private_data;
        f->vma_use_count++;
 }
 
 
-static void sn9c102_vm_close(struct vm_area_structvma)
+static void sn9c102_vm_close(struct vm_area_struct *vma)
 {
        /* NOTE: buffers are not freed here */
-       struct sn9c102_frame_tf = vma->vm_private_data;
+       struct sn9c102_frame_t *f = vma->vm_private_data;
        f->vma_use_count--;
 }
 
@@ -2097,7 +2101,7 @@ static const struct vm_operations_struct sn9c102_vm_ops = {
 };
 
 
-static int sn9c102_mmap(struct filefilp, struct vm_area_struct *vma)
+static int sn9c102_mmap(struct file *filp, struct vm_area_struct *vma)
 {
        struct sn9c102_device *cam = video_drvdata(filp);
        unsigned long size = vma->vm_end - vma->vm_start,
@@ -2166,7 +2170,7 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
 /*****************************************************************************/
 
 static int
-sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_querycap(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_capability cap = {
                .driver = "sn9c102",
@@ -2188,7 +2192,7 @@ sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_enuminput(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_input i;
 
@@ -2211,7 +2215,7 @@ sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_g_input(struct sn9c102_device *cam, void __user *arg)
 {
        int index = 0;
 
@@ -2223,7 +2227,7 @@ sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_s_input(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_s_input(struct sn9c102_device *cam, void __user *arg)
 {
        int index;
 
@@ -2238,9 +2242,9 @@ sn9c102_vidioc_s_input(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_query_ctrl(struct sn9c102_device *cam, void __user *arg)
 {
-       struct sn9c102_sensors = &cam->sensor;
+       struct sn9c102_sensor *s = &cam->sensor;
        struct v4l2_queryctrl qc;
        u8 i;
 
@@ -2260,9 +2264,9 @@ sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_g_ctrl(struct sn9c102_device *cam, void __user *arg)
 {
-       struct sn9c102_sensors = &cam->sensor;
+       struct sn9c102_sensor *s = &cam->sensor;
        struct v4l2_control ctrl;
        int err = 0;
        u8 i;
@@ -2295,9 +2299,9 @@ exit:
 
 
 static int
-sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_s_ctrl(struct sn9c102_device *cam, void __user *arg)
 {
-       struct sn9c102_sensors = &cam->sensor;
+       struct sn9c102_sensor *s = &cam->sensor;
        struct v4l2_control ctrl;
        u8 i;
        int err = 0;
@@ -2335,9 +2339,9 @@ sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_cropcap(struct sn9c102_device *cam, void __user *arg)
 {
-       struct v4l2_cropcapcc = &(cam->sensor.cropcap);
+       struct v4l2_cropcap *cc = &(cam->sensor.cropcap);
 
        cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        cc->pixelaspect.numerator = 1;
@@ -2351,9 +2355,9 @@ sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_g_crop(struct sn9c102_device *cam, void __user *arg)
 {
-       struct sn9c102_sensors = &cam->sensor;
+       struct sn9c102_sensor *s = &cam->sensor;
        struct v4l2_crop crop = {
                .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
        };
@@ -2368,13 +2372,13 @@ sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_s_crop(struct sn9c102_device *cam, void __user *arg)
 {
-       struct sn9c102_sensors = &cam->sensor;
+       struct sn9c102_sensor *s = &cam->sensor;
        struct v4l2_crop crop;
-       struct v4l2_rectrect;
-       struct v4l2_rectbounds = &(s->cropcap.bounds);
-       struct v4l2_pix_formatpix_format = &(s->pix_format);
+       struct v4l2_rect *rect;
+       struct v4l2_rect *bounds = &(s->cropcap.bounds);
+       struct v4l2_pix_format *pix_format = &(s->pix_format);
        u8 scale;
        const enum sn9c102_stream_state stream = cam->stream;
        const u32 nbuffers = cam->nbuffers;
@@ -2482,7 +2486,7 @@ sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_enum_framesizes(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_enum_framesizes(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_frmsizeenum frmsize;
 
@@ -2523,7 +2527,7 @@ sn9c102_vidioc_enum_framesizes(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_enum_fmt(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_fmtdesc fmtd;
 
@@ -2565,10 +2569,10 @@ sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_g_fmt(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_format format;
-       struct v4l2_pix_formatpfmt = &(cam->sensor.pix_format);
+       struct v4l2_pix_format *pfmt = &(cam->sensor.pix_format);
 
        if (copy_from_user(&format, arg, sizeof(format)))
                return -EFAULT;
@@ -2593,14 +2597,14 @@ sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_try_s_fmt(struct sn9c102_devicecam, unsigned int cmd,
-                        void __user * arg)
+sn9c102_vidioc_try_s_fmt(struct sn9c102_device *cam, unsigned int cmd,
+                        void __user *arg)
 {
-       struct sn9c102_sensors = &cam->sensor;
+       struct sn9c102_sensor *s = &cam->sensor;
        struct v4l2_format format;
-       struct v4l2_pix_formatpix;
-       struct v4l2_pix_formatpfmt = &(s->pix_format);
-       struct v4l2_rectbounds = &(s->cropcap.bounds);
+       struct v4l2_pix_format *pix;
+       struct v4l2_pix_format *pfmt = &(s->pix_format);
+       struct v4l2_rect *bounds = &(s->cropcap.bounds);
        struct v4l2_rect rect;
        u8 scale;
        const enum sn9c102_stream_state stream = cam->stream;
@@ -2742,7 +2746,7 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd,
 
 
 static int
-sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_g_jpegcomp(struct sn9c102_device *cam, void __user *arg)
 {
        if (copy_to_user(arg, &cam->compression, sizeof(cam->compression)))
                return -EFAULT;
@@ -2752,7 +2756,7 @@ sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_s_jpegcomp(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_jpegcompression jc;
        const enum sn9c102_stream_state stream = cam->stream;
@@ -2788,7 +2792,7 @@ sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_reqbufs(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_reqbufs(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_requestbuffers rb;
        u32 i;
@@ -2839,7 +2843,7 @@ sn9c102_vidioc_reqbufs(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_querybuf(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_buffer b;
 
@@ -2868,7 +2872,7 @@ sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_qbuf(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_qbuf(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_buffer b;
        unsigned long lock_flags;
@@ -2896,8 +2900,8 @@ sn9c102_vidioc_qbuf(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp,
-                    void __user * arg)
+sn9c102_vidioc_dqbuf(struct sn9c102_device *cam, struct file *filp,
+                    void __user *arg)
 {
        struct v4l2_buffer b;
        struct sn9c102_frame_t *f;
@@ -2918,20 +2922,20 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp,
                        return -EAGAIN;
                if (!cam->module_param.frame_timeout) {
                        err = wait_event_interruptible
-                             ( cam->wait_frame,
+                             (cam->wait_frame,
                                (!list_empty(&cam->outqueue)) ||
                                (cam->state & DEV_DISCONNECTED) ||
-                               (cam->state & DEV_MISCONFIGURED) );
+                               (cam->state & DEV_MISCONFIGURED));
                        if (err)
                                return err;
                } else {
                        timeout = wait_event_interruptible_timeout
-                                 ( cam->wait_frame,
+                                 (cam->wait_frame,
                                    (!list_empty(&cam->outqueue)) ||
                                    (cam->state & DEV_DISCONNECTED) ||
                                    (cam->state & DEV_MISCONFIGURED),
                                    cam->module_param.frame_timeout *
-                                   1000 * msecs_to_jiffies(1) );
+                                   1000 * msecs_to_jiffies(1));
                        if (timeout < 0)
                                return timeout;
                        else if (timeout == 0 &&
@@ -2967,7 +2971,7 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp,
 
 
 static int
-sn9c102_vidioc_streamon(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_streamon(struct sn9c102_device *cam, void __user *arg)
 {
        int type;
 
@@ -2986,7 +2990,7 @@ sn9c102_vidioc_streamon(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_streamoff(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_streamoff(struct sn9c102_device *cam, void __user *arg)
 {
        int type, err;
 
@@ -3011,7 +3015,7 @@ sn9c102_vidioc_streamoff(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_g_parm(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_g_parm(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_streamparm sp;
 
@@ -3032,7 +3036,7 @@ sn9c102_vidioc_g_parm(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_s_parm(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_s_parm(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_streamparm sp;
 
@@ -3060,7 +3064,7 @@ sn9c102_vidioc_s_parm(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_enumaudio(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_enumaudio(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_audio audio;
 
@@ -3085,7 +3089,7 @@ sn9c102_vidioc_enumaudio(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_g_audio(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_g_audio(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_audio audio;
 
@@ -3106,7 +3110,7 @@ sn9c102_vidioc_g_audio(struct sn9c102_device* cam, void __user * arg)
 
 
 static int
-sn9c102_vidioc_s_audio(struct sn9c102_device* cam, void __user * arg)
+sn9c102_vidioc_s_audio(struct sn9c102_device *cam, void __user *arg)
 {
        struct v4l2_audio audio;
 
@@ -3266,10 +3270,10 @@ static const struct v4l2_file_operations sn9c102_fops = {
 
 /* It exists a single interface only. We do not need to validate anything. */
 static int
-sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+sn9c102_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
        struct usb_device *udev = interface_to_usbdev(intf);
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
        static unsigned int dev_nr;
        unsigned int i;
        int err = 0, r;
@@ -3419,9 +3423,9 @@ fail:
 }
 
 
-static void sn9c102_usb_disconnect(struct usb_interfaceintf)
+static void sn9c102_usb_disconnect(struct usb_interface *intf)
 {
-       struct sn9c102_devicecam;
+       struct sn9c102_device *cam;
 
        down_write(&sn9c102_dev_lock);
 
index 4ba5692..b187a8a 100644 (file)
@@ -129,17 +129,17 @@ static const struct usb_device_id sn9c102_id_table[] = {
    initialization of the SN9C1XX chip.
    Functions must return 0 on success, the appropriate error otherwise.
 */
-extern int sn9c102_probe_hv7131d(struct sn9c102_devicecam);
-extern int sn9c102_probe_hv7131r(struct sn9c102_devicecam);
-extern int sn9c102_probe_mi0343(struct sn9c102_devicecam);
-extern int sn9c102_probe_mi0360(struct sn9c102_devicecam);
+extern int sn9c102_probe_hv7131d(struct sn9c102_device *cam);
+extern int sn9c102_probe_hv7131r(struct sn9c102_device *cam);
+extern int sn9c102_probe_mi0343(struct sn9c102_device *cam);
+extern int sn9c102_probe_mi0360(struct sn9c102_device *cam);
 extern int sn9c102_probe_mt9v111(struct sn9c102_device *cam);
-extern int sn9c102_probe_ov7630(struct sn9c102_devicecam);
-extern int sn9c102_probe_ov7660(struct sn9c102_devicecam);
-extern int sn9c102_probe_pas106b(struct sn9c102_devicecam);
-extern int sn9c102_probe_pas202bcb(struct sn9c102_devicecam);
-extern int sn9c102_probe_tas5110c1b(struct sn9c102_devicecam);
-extern int sn9c102_probe_tas5110d(struct sn9c102_devicecam);
-extern int sn9c102_probe_tas5130d1b(struct sn9c102_devicecam);
+extern int sn9c102_probe_ov7630(struct sn9c102_device *cam);
+extern int sn9c102_probe_ov7660(struct sn9c102_device *cam);
+extern int sn9c102_probe_pas106b(struct sn9c102_device *cam);
+extern int sn9c102_probe_pas202bcb(struct sn9c102_device *cam);
+extern int sn9c102_probe_tas5110c1b(struct sn9c102_device *cam);
+extern int sn9c102_probe_tas5110d(struct sn9c102_device *cam);
+extern int sn9c102_probe_tas5130d1b(struct sn9c102_device *cam);
 
 #endif /* _SN9C102_DEVTABLE_H_ */
index 4680721..f1d94f0 100644 (file)
@@ -23,7 +23,7 @@
 #include "sn9c102_devtable.h"
 
 
-static int hv7131d_init(struct sn9c102_devicecam)
+static int hv7131d_init(struct sn9c102_device *cam)
 {
        int err;
 
@@ -39,8 +39,8 @@ static int hv7131d_init(struct sn9c102_device* cam)
 }
 
 
-static int hv7131d_get_ctrl(struct sn9c102_devicecam,
-                           struct v4l2_controlctrl)
+static int hv7131d_get_ctrl(struct sn9c102_device *cam,
+                           struct v4l2_control *ctrl)
 {
        switch (ctrl->id) {
        case V4L2_CID_EXPOSURE:
@@ -88,8 +88,8 @@ static int hv7131d_get_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int hv7131d_set_ctrl(struct sn9c102_devicecam,
-                           const struct v4l2_controlctrl)
+static int hv7131d_set_ctrl(struct sn9c102_device *cam,
+                           const struct v4l2_control *ctrl)
 {
        int err = 0;
 
@@ -121,10 +121,10 @@ static int hv7131d_set_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int hv7131d_set_crop(struct sn9c102_devicecam,
-                           const struct v4l2_rectrect)
+static int hv7131d_set_crop(struct sn9c102_device *cam,
+                           const struct v4l2_rect *rect)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
        u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2,
           v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2;
@@ -136,8 +136,8 @@ static int hv7131d_set_crop(struct sn9c102_device* cam,
 }
 
 
-static int hv7131d_set_pix_format(struct sn9c102_devicecam,
-                                 const struct v4l2_pix_formatpix)
+static int hv7131d_set_pix_format(struct sn9c102_device *cam,
+                                 const struct v4l2_pix_format *pix)
 {
        int err = 0;
 
@@ -248,7 +248,7 @@ static const struct sn9c102_sensor hv7131d = {
 };
 
 
-int sn9c102_probe_hv7131d(struct sn9c102_devicecam)
+int sn9c102_probe_hv7131d(struct sn9c102_device *cam)
 {
        int r0 = 0, r1 = 0, err;
 
index 26a9111..51b24e0 100644 (file)
@@ -23,7 +23,7 @@
 #include "sn9c102_devtable.h"
 
 
-static int hv7131r_init(struct sn9c102_devicecam)
+static int hv7131r_init(struct sn9c102_device *cam)
 {
        int err = 0;
 
@@ -137,8 +137,8 @@ static int hv7131r_init(struct sn9c102_device* cam)
 }
 
 
-static int hv7131r_get_ctrl(struct sn9c102_devicecam,
-                           struct v4l2_controlctrl)
+static int hv7131r_get_ctrl(struct sn9c102_device *cam,
+                           struct v4l2_control *ctrl)
 {
        switch (ctrl->id) {
        case V4L2_CID_GAIN:
@@ -176,8 +176,8 @@ static int hv7131r_get_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int hv7131r_set_ctrl(struct sn9c102_devicecam,
-                           const struct v4l2_controlctrl)
+static int hv7131r_set_ctrl(struct sn9c102_device *cam,
+                           const struct v4l2_control *ctrl)
 {
        int err = 0;
 
@@ -197,6 +197,7 @@ static int hv7131r_set_ctrl(struct sn9c102_device* cam,
        case V4L2_CID_BLACK_LEVEL:
                {
                        int r = sn9c102_i2c_read(cam, 0x01);
+
                        if (r < 0)
                                return -EIO;
                        err += sn9c102_i2c_write(cam, 0x01,
@@ -211,10 +212,10 @@ static int hv7131r_set_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int hv7131r_set_crop(struct sn9c102_devicecam,
-                           const struct v4l2_rectrect)
+static int hv7131r_set_crop(struct sn9c102_device *cam,
+                           const struct v4l2_rect *rect)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
        u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1,
           v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
@@ -226,8 +227,8 @@ static int hv7131r_set_crop(struct sn9c102_device* cam,
 }
 
 
-static int hv7131r_set_pix_format(struct sn9c102_devicecam,
-                                 const struct v4l2_pix_formatpix)
+static int hv7131r_set_pix_format(struct sn9c102_device *cam,
+                                 const struct v4l2_pix_format *pix)
 {
        int err = 0;
 
@@ -347,7 +348,7 @@ static const struct sn9c102_sensor hv7131r = {
 };
 
 
-int sn9c102_probe_hv7131r(struct sn9c102_devicecam)
+int sn9c102_probe_hv7131r(struct sn9c102_device *cam)
 {
        int devid, err;
 
index 1f5b09b..b20fdb6 100644 (file)
@@ -23,9 +23,9 @@
 #include "sn9c102_devtable.h"
 
 
-static int mi0343_init(struct sn9c102_devicecam)
+static int mi0343_init(struct sn9c102_device *cam)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
 
        err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
@@ -52,10 +52,10 @@ static int mi0343_init(struct sn9c102_device* cam)
 }
 
 
-static int mi0343_get_ctrl(struct sn9c102_devicecam,
-                          struct v4l2_controlctrl)
+static int mi0343_get_ctrl(struct sn9c102_device *cam,
+                          struct v4l2_control *ctrl)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        u8 data[2];
 
        switch (ctrl->id) {
@@ -119,10 +119,10 @@ static int mi0343_get_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int mi0343_set_ctrl(struct sn9c102_devicecam,
-                          const struct v4l2_controlctrl)
+static int mi0343_set_ctrl(struct sn9c102_device *cam,
+                          const struct v4l2_control *ctrl)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        u16 reg = 0;
        int err = 0;
 
@@ -189,10 +189,10 @@ static int mi0343_set_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int mi0343_set_crop(struct sn9c102_devicecam,
-                           const struct v4l2_rectrect)
+static int mi0343_set_crop(struct sn9c102_device *cam,
+                           const struct v4l2_rect *rect)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
        u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0,
           v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2;
@@ -204,10 +204,10 @@ static int mi0343_set_crop(struct sn9c102_device* cam,
 }
 
 
-static int mi0343_set_pix_format(struct sn9c102_devicecam,
-                                const struct v4l2_pix_formatpix)
+static int mi0343_set_pix_format(struct sn9c102_device *cam,
+                                const struct v4l2_pix_format *pix)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
 
        if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) {
@@ -331,7 +331,7 @@ static const struct sn9c102_sensor mi0343 = {
 };
 
 
-int sn9c102_probe_mi0343(struct sn9c102_devicecam)
+int sn9c102_probe_mi0343(struct sn9c102_device *cam)
 {
        u8 data[2];
 
index d973fc1..5f21d1b 100644 (file)
@@ -23,9 +23,9 @@
 #include "sn9c102_devtable.h"
 
 
-static int mi0360_init(struct sn9c102_devicecam)
+static int mi0360_init(struct sn9c102_device *cam)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
 
        switch (sn9c102_get_bridge(cam)) {
@@ -147,10 +147,10 @@ static int mi0360_init(struct sn9c102_device* cam)
 }
 
 
-static int mi0360_get_ctrl(struct sn9c102_devicecam,
-                          struct v4l2_controlctrl)
+static int mi0360_get_ctrl(struct sn9c102_device *cam,
+                          struct v4l2_control *ctrl)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        u8 data[2];
 
        switch (ctrl->id) {
@@ -204,10 +204,10 @@ static int mi0360_get_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int mi0360_set_ctrl(struct sn9c102_devicecam,
-                          const struct v4l2_controlctrl)
+static int mi0360_set_ctrl(struct sn9c102_device *cam,
+                          const struct v4l2_control *ctrl)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
 
        switch (ctrl->id) {
@@ -259,10 +259,10 @@ static int mi0360_set_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int mi0360_set_crop(struct sn9c102_devicecam,
-                           const struct v4l2_rectrect)
+static int mi0360_set_crop(struct sn9c102_device *cam,
+                           const struct v4l2_rect *rect)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
        u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
 
@@ -285,10 +285,10 @@ static int mi0360_set_crop(struct sn9c102_device* cam,
 }
 
 
-static int mi0360_set_pix_format(struct sn9c102_devicecam,
-                                const struct v4l2_pix_formatpix)
+static int mi0360_set_pix_format(struct sn9c102_device *cam,
+                                const struct v4l2_pix_format *pix)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
 
        if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) {
@@ -418,7 +418,7 @@ static const struct sn9c102_sensor mi0360 = {
 };
 
 
-int sn9c102_probe_mi0360(struct sn9c102_devicecam)
+int sn9c102_probe_mi0360(struct sn9c102_device *cam)
 {
 
        u8 data[2];
index d3a1bd8..9ec304d 100644 (file)
@@ -23,7 +23,7 @@
 #include "sn9c102_devtable.h"
 
 
-static int ov7630_init(struct sn9c102_devicecam)
+static int ov7630_init(struct sn9c102_device *cam)
 {
        int err = 0;
 
@@ -252,8 +252,8 @@ static int ov7630_init(struct sn9c102_device* cam)
 }
 
 
-static int ov7630_get_ctrl(struct sn9c102_devicecam,
-                          struct v4l2_controlctrl)
+static int ov7630_get_ctrl(struct sn9c102_device *cam,
+                          struct v4l2_control *ctrl)
 {
        enum sn9c102_bridge bridge = sn9c102_get_bridge(cam);
        int err = 0;
@@ -330,8 +330,8 @@ static int ov7630_get_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int ov7630_set_ctrl(struct sn9c102_devicecam,
-                          const struct v4l2_controlctrl)
+static int ov7630_set_ctrl(struct sn9c102_device *cam,
+                          const struct v4l2_control *ctrl)
 {
        enum sn9c102_bridge bridge = sn9c102_get_bridge(cam);
        int err = 0;
@@ -385,10 +385,10 @@ static int ov7630_set_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int ov7630_set_crop(struct sn9c102_devicecam,
-                          const struct v4l2_rectrect)
+static int ov7630_set_crop(struct sn9c102_device *cam,
+                          const struct v4l2_rect *rect)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
        u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
 
@@ -413,8 +413,8 @@ static int ov7630_set_crop(struct sn9c102_device* cam,
 }
 
 
-static int ov7630_set_pix_format(struct sn9c102_devicecam,
-                                const struct v4l2_pix_formatpix)
+static int ov7630_set_pix_format(struct sn9c102_device *cam,
+                                const struct v4l2_pix_format *pix)
 {
        int err = 0;
 
@@ -594,7 +594,7 @@ static const struct sn9c102_sensor ov7630 = {
 };
 
 
-int sn9c102_probe_ov7630(struct sn9c102_devicecam)
+int sn9c102_probe_ov7630(struct sn9c102_device *cam)
 {
        int pid, ver, err = 0;
 
index 530157a..ac07805 100644 (file)
@@ -23,7 +23,7 @@
 #include "sn9c102_devtable.h"
 
 
-static int ov7660_init(struct sn9c102_devicecam)
+static int ov7660_init(struct sn9c102_device *cam)
 {
        int err = 0;
 
@@ -271,8 +271,8 @@ static int ov7660_init(struct sn9c102_device* cam)
 }
 
 
-static int ov7660_get_ctrl(struct sn9c102_devicecam,
-                          struct v4l2_controlctrl)
+static int ov7660_get_ctrl(struct sn9c102_device *cam,
+                          struct v4l2_control *ctrl)
 {
        int err = 0;
 
@@ -332,8 +332,8 @@ static int ov7660_get_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int ov7660_set_ctrl(struct sn9c102_devicecam,
-                          const struct v4l2_controlctrl)
+static int ov7660_set_ctrl(struct sn9c102_device *cam,
+                          const struct v4l2_control *ctrl)
 {
        int err = 0;
 
@@ -371,10 +371,10 @@ static int ov7660_set_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int ov7660_set_crop(struct sn9c102_devicecam,
-                          const struct v4l2_rectrect)
+static int ov7660_set_crop(struct sn9c102_device *cam,
+                          const struct v4l2_rect *rect)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
        u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1,
           v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
@@ -386,8 +386,8 @@ static int ov7660_set_crop(struct sn9c102_device* cam,
 }
 
 
-static int ov7660_set_pix_format(struct sn9c102_devicecam,
-                                const struct v4l2_pix_formatpix)
+static int ov7660_set_pix_format(struct sn9c102_device *cam,
+                                const struct v4l2_pix_format *pix)
 {
        int r0, err = 0;
 
@@ -525,7 +525,7 @@ static const struct sn9c102_sensor ov7660 = {
 };
 
 
-int sn9c102_probe_ov7660(struct sn9c102_devicecam)
+int sn9c102_probe_ov7660(struct sn9c102_device *cam)
 {
        int pid, ver, err;
 
index 47bd82d..895931e 100644 (file)
@@ -24,7 +24,7 @@
 #include "sn9c102_devtable.h"
 
 
-static int pas106b_init(struct sn9c102_devicecam)
+static int pas106b_init(struct sn9c102_device *cam)
 {
        int err = 0;
 
@@ -48,8 +48,8 @@ static int pas106b_init(struct sn9c102_device* cam)
 }
 
 
-static int pas106b_get_ctrl(struct sn9c102_devicecam,
-                           struct v4l2_controlctrl)
+static int pas106b_get_ctrl(struct sn9c102_device *cam,
+                           struct v4l2_control *ctrl)
 {
        switch (ctrl->id) {
        case V4L2_CID_EXPOSURE:
@@ -103,8 +103,8 @@ static int pas106b_get_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int pas106b_set_ctrl(struct sn9c102_devicecam,
-                           const struct v4l2_controlctrl)
+static int pas106b_set_ctrl(struct sn9c102_device *cam,
+                           const struct v4l2_control *ctrl)
 {
        int err = 0;
 
@@ -141,10 +141,10 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int pas106b_set_crop(struct sn9c102_devicecam,
-                           const struct v4l2_rectrect)
+static int pas106b_set_crop(struct sn9c102_device *cam,
+                           const struct v4l2_rect *rect)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
        u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4,
           v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3;
@@ -156,8 +156,8 @@ static int pas106b_set_crop(struct sn9c102_device* cam,
 }
 
 
-static int pas106b_set_pix_format(struct sn9c102_devicecam,
-                                 const struct v4l2_pix_formatpix)
+static int pas106b_set_pix_format(struct sn9c102_device *cam,
+                                 const struct v4l2_pix_format *pix)
 {
        int err = 0;
 
@@ -278,7 +278,7 @@ static const struct sn9c102_sensor pas106b = {
 };
 
 
-int sn9c102_probe_pas106b(struct sn9c102_devicecam)
+int sn9c102_probe_pas106b(struct sn9c102_device *cam)
 {
        int r0 = 0, r1 = 0;
        unsigned int pid = 0;
index cbfacc2..f9e31ae 100644 (file)
@@ -28,7 +28,7 @@
 #include "sn9c102_devtable.h"
 
 
-static int pas202bcb_init(struct sn9c102_devicecam)
+static int pas202bcb_init(struct sn9c102_device *cam)
 {
        int err = 0;
 
@@ -78,8 +78,8 @@ static int pas202bcb_init(struct sn9c102_device* cam)
 }
 
 
-static int pas202bcb_get_ctrl(struct sn9c102_devicecam,
-                             struct v4l2_controlctrl)
+static int pas202bcb_get_ctrl(struct sn9c102_device *cam,
+                             struct v4l2_control *ctrl)
 {
        switch (ctrl->id) {
        case V4L2_CID_EXPOSURE:
@@ -126,8 +126,8 @@ static int pas202bcb_get_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int pas202bcb_set_pix_format(struct sn9c102_devicecam,
-                                   const struct v4l2_pix_formatpix)
+static int pas202bcb_set_pix_format(struct sn9c102_device *cam,
+                                   const struct v4l2_pix_format *pix)
 {
        int err = 0;
 
@@ -140,8 +140,8 @@ static int pas202bcb_set_pix_format(struct sn9c102_device* cam,
 }
 
 
-static int pas202bcb_set_ctrl(struct sn9c102_devicecam,
-                             const struct v4l2_controlctrl)
+static int pas202bcb_set_ctrl(struct sn9c102_device *cam,
+                             const struct v4l2_control *ctrl)
 {
        int err = 0;
 
@@ -174,10 +174,10 @@ static int pas202bcb_set_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int pas202bcb_set_crop(struct sn9c102_devicecam,
-                             const struct v4l2_rectrect)
+static int pas202bcb_set_crop(struct sn9c102_device *cam,
+                             const struct v4l2_rect *rect)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
        u8 h_start = 0,
           v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3;
@@ -299,7 +299,7 @@ static const struct sn9c102_sensor pas202bcb = {
 };
 
 
-int sn9c102_probe_pas202bcb(struct sn9c102_devicecam)
+int sn9c102_probe_pas202bcb(struct sn9c102_device *cam)
 {
        int r0 = 0, r1 = 0, err = 0;
        unsigned int pid = 0;
index 3679970..9f59c81 100644 (file)
@@ -62,19 +62,19 @@ enum sn9c102_bridge {
 };
 
 /* Return the bridge name */
-enum sn9c102_bridge sn9c102_get_bridge(struct sn9c102_devicecam);
+enum sn9c102_bridge sn9c102_get_bridge(struct sn9c102_device *cam);
 
 /* Return a pointer the sensor struct attached to the camera */
-struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam);
+struct sn9c102_sensor *sn9c102_get_sensor(struct sn9c102_device *cam);
 
 /* Identify a device */
 extern struct sn9c102_device*
-sn9c102_match_id(struct sn9c102_devicecam, const struct usb_device_id *id);
+sn9c102_match_id(struct sn9c102_device *cam, const struct usb_device_id *id);
 
 /* Attach a probed sensor to the camera. */
 extern void
-sn9c102_attach_sensor(struct sn9c102_devicecam,
-                     const struct sn9c102_sensorsensor);
+sn9c102_attach_sensor(struct sn9c102_device *cam,
+                     const struct sn9c102_sensor *sensor);
 
 /*
    Read/write routines: they always return -1 on error, 0 or the read value
@@ -99,12 +99,12 @@ extern int sn9c102_i2c_try_read(struct sn9c102_device*,
    version returns 0 on success, while the read version returns the first read
    byte.
 */
-extern int sn9c102_i2c_try_raw_write(struct sn9c102_devicecam,
-                                    const struct sn9c102_sensorsensor, u8 n,
+extern int sn9c102_i2c_try_raw_write(struct sn9c102_device *cam,
+                                    const struct sn9c102_sensor *sensor, u8 n,
                                     u8 data0, u8 data1, u8 data2, u8 data3,
                                     u8 data4, u8 data5);
-extern int sn9c102_i2c_try_raw_read(struct sn9c102_devicecam,
-                                   const struct sn9c102_sensorsensor,
+extern int sn9c102_i2c_try_raw_read(struct sn9c102_device *cam,
+                                   const struct sn9c102_sensor *sensor,
                                    u8 data0, u8 data1, u8 n, u8 buffer[]);
 
 /* To be used after the sensor struct has been attached to the camera struct */
@@ -174,7 +174,7 @@ struct sn9c102_sensor {
                 they must return 0 on success, the proper error otherwise.
        */
 
-       int (*init)(struct sn9c102_devicecam);
+       int (*init)(struct sn9c102_device *cam);
        /*
           This function will be called after the sensor has been attached.
           It should be used to initialize the sensor only, but may also
@@ -195,9 +195,9 @@ struct sn9c102_sensor {
           V4L2 API. Menu type controls are not handled by this interface.
        */
 
-       int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl);
-       int (*set_ctrl)(struct sn9c102_devicecam,
-                       const struct v4l2_controlctrl);
+       int (*get_ctrl)(struct sn9c102_device *cam, struct v4l2_control *ctrl);
+       int (*set_ctrl)(struct sn9c102_device *cam,
+                       const struct v4l2_control *ctrl);
        /*
           You must implement at least the set_ctrl method if you have defined
           the list above. The returned value must follow the V4L2
@@ -240,8 +240,8 @@ struct sn9c102_sensor {
           will be ignored.
        */
 
-       int (*set_crop)(struct sn9c102_devicecam,
-                       const struct v4l2_rectrect);
+       int (*set_crop)(struct sn9c102_device *cam,
+                       const struct v4l2_rect *rect);
        /*
           To be called on VIDIOC_C_SETCROP. The core module always calls a
           default routine which configures the appropriate SN9C1XX regs (also
@@ -276,8 +276,8 @@ struct sn9c102_sensor {
                   matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR).
        */
 
-       int (*set_pix_format)(struct sn9c102_devicecam,
-                             const struct v4l2_pix_formatpix);
+       int (*set_pix_format)(struct sn9c102_device *cam,
+                             const struct v4l2_pix_format *pix);
        /*
           To be called on VIDIOC_S_FMT, when switching from the SBGGR8 to
           SN9C10X pixel format or viceversa. On error return the corresponding
index 04cdfdd..6a00b62 100644 (file)
@@ -23,7 +23,7 @@
 #include "sn9c102_devtable.h"
 
 
-static int tas5110c1b_init(struct sn9c102_devicecam)
+static int tas5110c1b_init(struct sn9c102_device *cam)
 {
        int err = 0;
 
@@ -38,8 +38,8 @@ static int tas5110c1b_init(struct sn9c102_device* cam)
 }
 
 
-static int tas5110c1b_set_ctrl(struct sn9c102_devicecam,
-                              const struct v4l2_controlctrl)
+static int tas5110c1b_set_ctrl(struct sn9c102_device *cam,
+                              const struct v4l2_control *ctrl)
 {
        int err = 0;
 
@@ -55,10 +55,10 @@ static int tas5110c1b_set_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int tas5110c1b_set_crop(struct sn9c102_devicecam,
-                              const struct v4l2_rectrect)
+static int tas5110c1b_set_crop(struct sn9c102_device *cam,
+                              const struct v4l2_rect *rect)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
        u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69,
           v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9;
@@ -75,8 +75,8 @@ static int tas5110c1b_set_crop(struct sn9c102_device* cam,
 }
 
 
-static int tas5110c1b_set_pix_format(struct sn9c102_devicecam,
-                                    const struct v4l2_pix_formatpix)
+static int tas5110c1b_set_pix_format(struct sn9c102_device *cam,
+                                    const struct v4l2_pix_format *pix)
 {
        int err = 0;
 
@@ -135,7 +135,7 @@ static const struct sn9c102_sensor tas5110c1b = {
 };
 
 
-int sn9c102_probe_tas5110c1b(struct sn9c102_devicecam)
+int sn9c102_probe_tas5110c1b(struct sn9c102_device *cam)
 {
        const struct usb_device_id tas5110c1b_id_table[] = {
                { USB_DEVICE(0x0c45, 0x6001), },
index 9372e6f..eefbf86 100644 (file)
@@ -23,7 +23,7 @@
 #include "sn9c102_devtable.h"
 
 
-static int tas5110d_init(struct sn9c102_devicecam)
+static int tas5110d_init(struct sn9c102_device *cam)
 {
        int err;
 
@@ -37,10 +37,10 @@ static int tas5110d_init(struct sn9c102_device* cam)
 }
 
 
-static int tas5110d_set_crop(struct sn9c102_devicecam,
-                            const struct v4l2_rectrect)
+static int tas5110d_set_crop(struct sn9c102_device *cam,
+                            const struct v4l2_rect *rect)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        int err = 0;
        u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69,
           v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9;
@@ -55,8 +55,8 @@ static int tas5110d_set_crop(struct sn9c102_device* cam,
 }
 
 
-static int tas5110d_set_pix_format(struct sn9c102_devicecam,
-                                    const struct v4l2_pix_formatpix)
+static int tas5110d_set_pix_format(struct sn9c102_device *cam,
+                                    const struct v4l2_pix_format *pix)
 {
        int err = 0;
 
@@ -103,7 +103,7 @@ static const struct sn9c102_sensor tas5110d = {
 };
 
 
-int sn9c102_probe_tas5110d(struct sn9c102_devicecam)
+int sn9c102_probe_tas5110d(struct sn9c102_device *cam)
 {
        const struct usb_device_id tas5110d_id_table[] = {
                { USB_DEVICE(0x0c45, 0x6007), },
index a30bbc4..725de85 100644 (file)
@@ -23,7 +23,7 @@
 #include "sn9c102_devtable.h"
 
 
-static int tas5130d1b_init(struct sn9c102_devicecam)
+static int tas5130d1b_init(struct sn9c102_device *cam)
 {
        int err;
 
@@ -36,8 +36,8 @@ static int tas5130d1b_init(struct sn9c102_device* cam)
 }
 
 
-static int tas5130d1b_set_ctrl(struct sn9c102_devicecam,
-                              const struct v4l2_controlctrl)
+static int tas5130d1b_set_ctrl(struct sn9c102_device *cam,
+                              const struct v4l2_control *ctrl)
 {
        int err = 0;
 
@@ -56,10 +56,10 @@ static int tas5130d1b_set_ctrl(struct sn9c102_device* cam,
 }
 
 
-static int tas5130d1b_set_crop(struct sn9c102_devicecam,
-                              const struct v4l2_rectrect)
+static int tas5130d1b_set_crop(struct sn9c102_device *cam,
+                              const struct v4l2_rect *rect)
 {
-       struct sn9c102_sensors = sn9c102_get_sensor(cam);
+       struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
        u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104,
           v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12;
        int err = 0;
@@ -76,8 +76,8 @@ static int tas5130d1b_set_crop(struct sn9c102_device* cam,
 }
 
 
-static int tas5130d1b_set_pix_format(struct sn9c102_devicecam,
-                                    const struct v4l2_pix_formatpix)
+static int tas5130d1b_set_pix_format(struct sn9c102_device *cam,
+                                    const struct v4l2_pix_format *pix)
 {
        int err = 0;
 
@@ -146,7 +146,7 @@ static const struct sn9c102_sensor tas5130d1b = {
 };
 
 
-int sn9c102_probe_tas5130d1b(struct sn9c102_devicecam)
+int sn9c102_probe_tas5130d1b(struct sn9c102_device *cam)
 {
        const struct usb_device_id tas5130d1b_id_table[] = {
                { USB_DEVICE(0x0c45, 0x6024), },
index 9a4296c..6a1906f 100644 (file)
@@ -1,5 +1,5 @@
 config SOLO6X10
-       tristate "Softlogic 6x10 MPEG codec cards"
+       tristate "Bluecherry / Softlogic 6x10 capture cards (MPEG-4/H.264)"
        depends on PCI && VIDEO_DEV && SND && I2C
        select FONT_SUPPORT
        select FONT_8x16
@@ -8,5 +8,11 @@ config SOLO6X10
        select SND_PCM
        select FONT_8x16
        ---help---
-         This driver supports the Softlogic based MPEG-4 and h.264 codec
-         cards.
+         This driver supports the Bluecherry H.264 and MPEG-4 hardware
+         compression capture cards and other Softlogic-based ones.
+
+         Following cards have been tested:
+         * Bluecherry BC-H16480A (PCIe, 16 port, H.264)
+         * Bluecherry BC-H04120A (PCIe, 4 port, H.264)
+         * Bluecherry BC-H04120A-MPCI (Mini-PCI, 4 port, H.264)
+         * Bluecherry BC-04120A (PCIe, 4 port, MPEG-4)
index 94d5735..2db53b6 100644 (file)
@@ -134,51 +134,48 @@ static void solo_capture_config(struct solo_dev *solo_dev)
        kfree(buf);
 }
 
+#define SOLO_OSD_WRITE_SIZE (16 * OSD_TEXT_MAX)
+
 /* Should be called with enable_lock held */
 int solo_osd_print(struct solo_enc_dev *solo_enc)
 {
        struct solo_dev *solo_dev = solo_enc->solo_dev;
        unsigned char *str = solo_enc->osd_text;
        u8 *buf = solo_enc->osd_buf;
-       u32 reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH);
+       u32 reg;
        const struct font_desc *vga = find_font("VGA8x16");
        const unsigned char *vga_data;
-       int len;
        int i, j;
 
        if (WARN_ON_ONCE(!vga))
                return -ENODEV;
 
-       len = strlen(str);
-
-       if (len == 0) {
+       reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH);
+       if (!*str) {
                /* Disable OSD on this channel */
                reg &= ~(1 << solo_enc->ch);
-               solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
-               return 0;
+               goto out;
        }
 
-       memset(buf, 0, SOLO_EOSD_EXT_SIZE_MAX);
+       memset(buf, 0, SOLO_OSD_WRITE_SIZE);
        vga_data = (const unsigned char *)vga->data;
 
-       for (i = 0; i < len; i++) {
-               unsigned char c = str[i];
-
+       for (i = 0; *str; i++, str++) {
                for (j = 0; j < 16; j++) {
-                       buf[(j * 2) + (i % 2) + (i / 2 * 32)] =
-                               bitrev8(vga_data[(c * 16) + j]);
+                       buf[(j << 1) | (i & 1) | ((i & ~1) << 4)] =
+                           bitrev8(vga_data[(*str << 4) | j]);
                }
        }
 
        solo_p2m_dma(solo_dev, 1, buf,
-                    SOLO_EOSD_EXT_ADDR +
-                    (solo_enc->ch * SOLO_EOSD_EXT_SIZE(solo_dev)),
-                    SOLO_EOSD_EXT_SIZE(solo_dev), 0, 0);
+                    SOLO_EOSD_EXT_ADDR_CHAN(solo_dev, solo_enc->ch),
+                    SOLO_OSD_WRITE_SIZE, 0, 0);
 
        /* Enable OSD on this channel */
        reg |= (1 << solo_enc->ch);
-       solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
 
+out:
+       solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg);
        return 0;
 }
 
index f005dca..13eeb44 100644 (file)
@@ -35,6 +35,8 @@
 #define SOLO_EOSD_EXT_SIZE_MAX                 0x20000
 #define SOLO_EOSD_EXT_AREA(__solo) \
        (SOLO_EOSD_EXT_SIZE(__solo) * 32)
+#define SOLO_EOSD_EXT_ADDR_CHAN(__solo, ch) \
+       (SOLO_EOSD_EXT_ADDR + SOLO_EOSD_EXT_SIZE(__solo) * (ch))
 
 #define SOLO_MOTION_EXT_ADDR(__solo) \
        (SOLO_EOSD_EXT_ADDR + SOLO_EOSD_EXT_AREA(__solo))
index 2cbe088..b8ff113 100644 (file)
@@ -745,14 +745,13 @@ static int solo_enc_start_streaming(struct vb2_queue *q, unsigned int count)
        return solo_ring_start(solo_enc->solo_dev);
 }
 
-static int solo_enc_stop_streaming(struct vb2_queue *q)
+static void solo_enc_stop_streaming(struct vb2_queue *q)
 {
        struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q);
 
        solo_enc_off(solo_enc);
        INIT_LIST_HEAD(&solo_enc->vidq_active);
        solo_ring_stop(solo_enc->solo_dev);
-       return 0;
 }
 
 static struct vb2_ops solo_enc_video_qops = {
index 1815f76..5d0100e 100644 (file)
@@ -336,13 +336,12 @@ static int solo_start_streaming(struct vb2_queue *q, unsigned int count)
        return solo_start_thread(solo_dev);
 }
 
-static int solo_stop_streaming(struct vb2_queue *q)
+static void solo_stop_streaming(struct vb2_queue *q)
 {
        struct solo_dev *solo_dev = vb2_get_drv_priv(q);
 
        solo_stop_thread(solo_dev);
        INIT_LIST_HEAD(&solo_dev->vidq_active);
-       return 0;
 }
 
 static void solo_buf_queue(struct vb2_buffer *vb)
index 4246262..84a75f8 100644 (file)
@@ -144,11 +144,11 @@ static int get_property(unsigned int cpu, unsigned long input,
                        unsigned int *output,
                        enum cpufreq_cooling_property property)
 {
-       int i, j;
+       int i;
        unsigned long max_level = 0, level = 0;
        unsigned int freq = CPUFREQ_ENTRY_INVALID;
        int descend = -1;
-       struct cpufreq_frequency_table *table =
+       struct cpufreq_frequency_table *pos, *table =
                                        cpufreq_frequency_get_table(cpu);
 
        if (!output)
@@ -157,20 +157,16 @@ static int get_property(unsigned int cpu, unsigned long input,
        if (!table)
                return -EINVAL;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               /* ignore invalid entries */
-               if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
-                       continue;
-
+       cpufreq_for_each_valid_entry(pos, table) {
                /* ignore duplicate entry */
-               if (freq == table[i].frequency)
+               if (freq == pos->frequency)
                        continue;
 
                /* get the frequency order */
                if (freq != CPUFREQ_ENTRY_INVALID && descend == -1)
-                       descend = !!(freq > table[i].frequency);
+                       descend = freq > pos->frequency;
 
-               freq = table[i].frequency;
+               freq = pos->frequency;
                max_level++;
        }
 
@@ -190,29 +186,26 @@ static int get_property(unsigned int cpu, unsigned long input,
        if (property == GET_FREQ)
                level = descend ? input : (max_level - input);
 
-       for (i = 0, j = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               /* ignore invalid entry */
-               if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
-                       continue;
-
+       i = 0;
+       cpufreq_for_each_valid_entry(pos, table) {
                /* ignore duplicate entry */
-               if (freq == table[i].frequency)
+               if (freq == pos->frequency)
                        continue;
 
                /* now we have a valid frequency entry */
-               freq = table[i].frequency;
+               freq = pos->frequency;
 
                if (property == GET_LEVEL && (unsigned int)input == freq) {
                        /* get level by frequency */
-                       *output = descend ? j : (max_level - j);
+                       *output = descend ? i : (max_level - i);
                        return 0;
                }
-               if (property == GET_FREQ && level == j) {
+               if (property == GET_FREQ && level == i) {
                        /* get frequency by level */
                        *output = freq;
                        return 0;
                }
-               j++;
+               i++;
        }
 
        return -EINVAL;
index ee3d803..908a6e3 100644 (file)
@@ -2072,6 +2072,7 @@ static int __init pl011_early_console_setup(struct earlycon_device *device,
        return 0;
 }
 EARLYCON_DECLARE(pl011, pl011_early_console_setup);
+OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup);
 
 #else
 #define AMBA_CONSOLE   NULL
index c92e830..5131b5e 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/serial_core.h>
+#include <linux/sizes.h>
+#include <linux/mod_devicetable.h>
 
 #ifdef CONFIG_FIX_EARLYCON_MEM
 #include <asm/fixmap.h>
@@ -32,6 +34,9 @@ static struct earlycon_device early_console_dev = {
        .con = &early_con,
 };
 
+static const struct of_device_id __earlycon_of_table_sentinel
+       __used __section(__earlycon_of_table_end);
+
 static void __iomem * __init earlycon_map(unsigned long paddr, size_t size)
 {
        void __iomem *base;
@@ -142,3 +147,26 @@ int __init setup_earlycon(char *buf, const char *match,
        register_console(early_console_dev.con);
        return 0;
 }
+
+int __init of_setup_earlycon(unsigned long addr,
+                            int (*setup)(struct earlycon_device *, const char *))
+{
+       int err;
+       struct uart_port *port = &early_console_dev.port;
+
+       port->iotype = UPIO_MEM;
+       port->mapbase = addr;
+       port->uartclk = BASE_BAUD * 16;
+       port->membase = earlycon_map(addr, SZ_4K);
+
+       early_console_dev.con->data = &early_console_dev;
+       err = setup(&early_console_dev, NULL);
+       if (err < 0)
+               return err;
+       if (!early_console_dev.con->write)
+               return -ENODEV;
+
+
+       register_console(early_console_dev.con);
+       return 0;
+}
index bd2172c..4280890 100644 (file)
@@ -23,6 +23,7 @@
 
 static struct list_head backlight_dev_list;
 static struct mutex backlight_dev_list_mutex;
+static struct blocking_notifier_head backlight_notifier;
 
 static const char *const backlight_types[] = {
        [BACKLIGHT_RAW] = "raw",
@@ -370,6 +371,9 @@ struct backlight_device *backlight_device_register(const char *name,
        list_add(&new_bd->entry, &backlight_dev_list);
        mutex_unlock(&backlight_dev_list_mutex);
 
+       blocking_notifier_call_chain(&backlight_notifier,
+                                    BACKLIGHT_REGISTERED, new_bd);
+
        return new_bd;
 }
 EXPORT_SYMBOL(backlight_device_register);
@@ -413,6 +417,10 @@ void backlight_device_unregister(struct backlight_device *bd)
                pmac_backlight = NULL;
        mutex_unlock(&pmac_backlight_mutex);
 #endif
+
+       blocking_notifier_call_chain(&backlight_notifier,
+                                    BACKLIGHT_UNREGISTERED, bd);
+
        mutex_lock(&bd->ops_lock);
        bd->ops = NULL;
        mutex_unlock(&bd->ops_lock);
@@ -437,6 +445,36 @@ static int devm_backlight_device_match(struct device *dev, void *res,
        return *r == data;
 }
 
+/**
+ * backlight_register_notifier - get notified of backlight (un)registration
+ * @nb: notifier block with the notifier to call on backlight (un)registration
+ *
+ * @return 0 on success, otherwise a negative error code
+ *
+ * Register a notifier to get notified when backlight devices get registered
+ * or unregistered.
+ */
+int backlight_register_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&backlight_notifier, nb);
+}
+EXPORT_SYMBOL(backlight_register_notifier);
+
+/**
+ * backlight_unregister_notifier - unregister a backlight notifier
+ * @nb: notifier block to unregister
+ *
+ * @return 0 on success, otherwise a negative error code
+ *
+ * Register a notifier to get notified when backlight devices get registered
+ * or unregistered.
+ */
+int backlight_unregister_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_unregister(&backlight_notifier, nb);
+}
+EXPORT_SYMBOL(backlight_unregister_notifier);
+
 /**
  * devm_backlight_device_register - resource managed backlight_device_register()
  * @dev: the device to register
@@ -544,6 +582,8 @@ static int __init backlight_class_init(void)
        backlight_class->pm = &backlight_class_dev_pm_ops;
        INIT_LIST_HEAD(&backlight_dev_list);
        mutex_init(&backlight_dev_list_mutex);
+       BLOCKING_INIT_NOTIFIER_HEAD(&backlight_notifier);
+
        return 0;
 }
 
index f447734..57b1d44 100644 (file)
@@ -748,6 +748,7 @@ static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,
                fbcon_del_cursor_timer(oldinfo);
                kfree(ops->cursor_state.mask);
                kfree(ops->cursor_data);
+               kfree(ops->cursor_src);
                kfree(ops->fontbuffer);
                kfree(oldinfo->fbcon_par);
                oldinfo->fbcon_par = NULL;
index e1f4727..59c98bf 100644 (file)
@@ -360,6 +360,7 @@ config FB_CYBER2000_DDC
 config FB_CYBER2000_I2C
        bool "CyberPro 2000/2010/5000 I2C support"
        depends on FB_CYBER2000 && I2C && ARCH_NETWINDER
+       depends on I2C=y || FB_CYBER2000=m
        select I2C_ALGOBIT
        help
          Enable support for the I2C video decoder interface on the
@@ -966,6 +967,7 @@ config FB_S1D13XXX
 config FB_ATMEL
        tristate "AT91/AT32 LCD Controller support"
        depends on FB && HAVE_FB_ATMEL
+       select FB_BACKLIGHT
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
@@ -1971,6 +1973,7 @@ config FB_W100
 config FB_SH_MOBILE_LCDC
        tristate "SuperH Mobile LCDC framebuffer support"
        depends on FB && (SUPERH || ARCH_SHMOBILE) && HAVE_CLK
+       depends on FB_SH_MOBILE_MERAM || !FB_SH_MOBILE_MERAM
        select FB_SYS_FILLRECT
        select FB_SYS_COPYAREA
        select FB_SYS_IMAGEBLIT
@@ -1993,7 +1996,7 @@ config FB_SH_MOBILE_HDMI
 
 config FB_TMIO
        tristate "Toshiba Mobile IO FrameBuffer support"
-       depends on FB && MFD_CORE
+       depends on FB && (MFD_TMIO || COMPILE_TEST)
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
@@ -2062,7 +2065,7 @@ config FB_S3C2410_DEBUG
          through sysfs
 
 config FB_NUC900
-        bool "NUC900 LCD framebuffer support"
+        tristate "NUC900 LCD framebuffer support"
         depends on FB && ARCH_W90X900
         select FB_CFB_FILLRECT
         select FB_CFB_COPYAREA
@@ -2169,7 +2172,7 @@ config FB_XILINX
 
 config FB_GOLDFISH
        tristate "Goldfish Framebuffer"
-       depends on FB && HAS_DMA
+       depends on FB && HAS_DMA && (GOLDFISH || COMPILE_TEST)
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
@@ -2295,6 +2298,7 @@ endchoice
 config FB_MB862XX_I2C
        bool "Support I2C bus on MB862XX GDC"
        depends on FB_MB862XX && I2C
+       depends on FB_MB862XX=m || I2C=y
        default y
        help
          Selecting this option adds Coral-P(A)/Lime GDC I2C bus adapter
@@ -2332,6 +2336,7 @@ config FB_MX3
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
+       select BACKLIGHT_CLASS_DEVICE
        default y
        help
          This is a framebuffer device for the i.MX31 LCD Controller. So
index e2c42ad..adbef54 100644 (file)
@@ -717,8 +717,6 @@ static int bfin_bf54x_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM
 static int bfin_bf54x_suspend(struct platform_device *pdev, pm_message_t state)
 {
-       struct fb_info *fbinfo = platform_get_drvdata(pdev);
-
        bfin_write_EPPI0_CONTROL(bfin_read_EPPI0_CONTROL() & ~EPPI_EN);
        disable_dma(CH_EPPI0);
        bfin_write_EPPI0_STATUS(0xFFFF);
index b6d5008..b5e85f6 100644 (file)
@@ -433,7 +433,7 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
                        image->dx += image->width + 8;
                }
        } else if (rotate == FB_ROTATE_UD) {
-               for (x = 0; x < num && image->dx >= 0; x++) {
+               for (x = 0; x < num; x++) {
                        info->fbops->fb_imageblit(info, image);
                        image->dx -= image->width + 8;
                }
@@ -445,7 +445,7 @@ static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
                        image->dy += image->height + 8;
                }
        } else if (rotate == FB_ROTATE_CCW) {
-               for (x = 0; x < num && image->dy >= 0; x++) {
+               for (x = 0; x < num; x++) {
                        info->fbops->fb_imageblit(info, image);
                        image->dy -= image->height + 8;
                }
@@ -674,6 +674,7 @@ int fb_show_logo(struct fb_info *info, int rotate)
 int fb_prepare_logo(struct fb_info *info, int rotate) { return 0; }
 int fb_show_logo(struct fb_info *info, int rotate) { return 0; }
 #endif /* CONFIG_LOGO */
+EXPORT_SYMBOL(fb_prepare_logo);
 EXPORT_SYMBOL(fb_show_logo);
 
 static void *fb_seq_start(struct seq_file *m, loff_t *pos)
@@ -1179,7 +1180,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
                        return -EFAULT;
                if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
                        return -EINVAL;
-               if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
+               if (con2fb.framebuffer >= FB_MAX)
                        return -EINVAL;
                if (!registered_fb[con2fb.framebuffer])
                        request_module("fb%d", con2fb.framebuffer);
index c204ebe..5b0e313 100644 (file)
@@ -1012,13 +1012,20 @@ void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs)
        while (pos < edid[2]) {
                u8 len = edid[pos] & 0x1f, type = (edid[pos] >> 5) & 7;
                pr_debug("Data block %u of %u bytes\n", type, len);
-               if (type == 2)
+               if (type == 2) {
                        for (i = pos; i < pos + len; i++) {
                                u8 idx = edid[pos + i] & 0x7f;
                                svd[svd_n++] = idx;
                                pr_debug("N%sative mode #%d\n",
                                         edid[pos + i] & 0x80 ? "" : "on-n", idx);
                        }
+               } else if (type == 3 && len >= 3) {
+                       /* Check Vendor Specific Data Block.  For HDMI,
+                          it is always 00-0C-03 for HDMI Licensing, LLC. */
+                       if (edid[pos + 1] == 3 && edid[pos + 2] == 0xc &&
+                           edid[pos + 3] == 0)
+                               specs->misc |= FB_MISC_HDMI;
+               }
                pos += len + 1;
        }
 
index 6b23508..a8484f7 100644 (file)
@@ -242,6 +242,20 @@ static struct fb_videomode known_lcd_panels[] = {
                .sync           = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
                .flag           = 0,
        },
+       [3] = {
+               /* Densitron 84-0023-001T */
+               .name           = "Densitron_84-0023-001T",
+               .xres           = 320,
+               .yres           = 240,
+               .pixclock       = KHZ2PICOS(6400),
+               .left_margin    = 0,
+               .right_margin   = 0,
+               .upper_margin   = 0,
+               .lower_margin   = 0,
+               .hsync_len      = 30,
+               .vsync_len      = 3,
+               .sync           = 0,
+       },
 };
 
 static bool da8xx_fb_is_raster_enabled(void)
index fcf2d48..1f16b46 100644 (file)
@@ -4,6 +4,7 @@
 
 menuconfig EXYNOS_VIDEO
        bool "Exynos Video driver support"
+       depends on ARCH_S5PV210 || ARCH_EXYNOS
        help
          This enables support for EXYNOS Video device.
 
@@ -15,7 +16,6 @@ if EXYNOS_VIDEO
 
 config EXYNOS_MIPI_DSI
        bool "EXYNOS MIPI DSI driver support."
-       depends on ARCH_S5PV210 || ARCH_EXYNOS
        select GENERIC_PHY
        help
          This enables support for MIPI-DSI device.
index 6db9ebd..88fa2e7 100644 (file)
 #include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/console.h>
+#include <linux/mm.h>
 
 #include <asm/sizes.h>
+#include <asm/pgtable.h>
 #include <mach/hardware.h>
 
 /* Platform_data reserved for unifb registers. */
index 3ec65a8..4aa56ba 100644 (file)
@@ -1068,7 +1068,7 @@ static struct fb_ops gbefb_ops = {
 
 static ssize_t gbefb_show_memsize(struct device *dev, struct device_attribute *attr, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "%d\n", gbe_mem_size);
+       return snprintf(buf, PAGE_SIZE, "%u\n", gbe_mem_size);
 }
 
 static DEVICE_ATTR(size, S_IRUGO, gbefb_show_memsize, NULL);
index c078701..2db5bb1 100644 (file)
@@ -514,9 +514,10 @@ free_fb:
 static int grvga_remove(struct platform_device *device)
 {
        struct fb_info *info = dev_get_drvdata(&device->dev);
-       struct grvga_par *par = info->par;
+       struct grvga_par *par;
 
        if (info) {
+               par = info->par;
                unregister_framebuffer(info);
                fb_dealloc_cmap(&info->cmap);
 
index 556d96c..89a8a89 100644 (file)
@@ -698,7 +698,7 @@ void matroxfb_unregister_driver(struct matroxfb_driver* drv);
 
 #define mga_fifo(n)    do {} while ((mga_inl(M_FIFOSTATUS) & 0xFF) < (n))
 
-#define WaitTillIdle() do {} while (mga_inl(M_STATUS) & 0x10000)
+#define WaitTillIdle() do { mga_inl(M_STATUS); do {} while (mga_inl(M_STATUS) & 0x10000); } while (0)
 
 /* code speedup */
 #ifdef CONFIG_FB_MATROX_MILLENIUM
index 16c1165..d7ae5a9 100644 (file)
@@ -1,4 +1,3 @@
 # Makefile for the 2700G controller driver.
 
-obj-$(CONFIG_FB_MBX)      += mbxfb.o
-obj-$(CONFIG_FB_MBX_DEBUG) += mbxfbdebugfs.o
+obj-y                  += mbxfb.o
index 4449f24..e3bc00a 100644 (file)
@@ -17,7 +17,7 @@ struct mbxfb_debugfs_data {
 
 static int open_file_generic(struct inode *inode, struct file *file)
 {
-       file->private_data = inode->u.generic_ip;
+       file->private_data = inode->i_private;
        return 0;
 }
 
index f0a5392..2bd52ed 100644 (file)
@@ -877,6 +877,8 @@ static int mbxfb_resume(struct platform_device *dev)
 #ifndef CONFIG_FB_MBX_DEBUG
 #define mbxfb_debugfs_init(x)  do {} while(0)
 #define mbxfb_debugfs_remove(x)        do {} while(0)
+#else
+#include "mbxdebugfs.c"
 #endif
 
 #define res_size(_r) (((_r)->end - (_r)->start) + 1)
index d4a4ffc..f56a7e2 100644 (file)
@@ -1,6 +1,6 @@
 menuconfig MMP_DISP
         tristate "Marvell MMP Display Subsystem support"
-        depends on CPU_PXA910 || CPU_MMP2 || CPU_MMP3 || CPU_PXA988
+        depends on CPU_PXA910 || CPU_MMP2
         help
          Marvell Display Subsystem support.
 
index 7ab31eb..910fcc6 100644 (file)
@@ -554,8 +554,8 @@ static void fb_info_clear(struct fb_info *info)
 static int mmpfb_probe(struct platform_device *pdev)
 {
        struct mmp_buffer_driver_mach_info *mi;
-       struct fb_info *info = 0;
-       struct mmpfb_info *fbi = 0;
+       struct fb_info *info;
+       struct mmpfb_info *fbi;
        int ret, modes_num;
 
        mi = pdev->dev.platform_data;
@@ -569,10 +569,6 @@ static int mmpfb_probe(struct platform_device *pdev)
        if (info == NULL)
                return -ENOMEM;
        fbi = info->par;
-       if (!fbi) {
-               ret = -EINVAL;
-               goto failed;
-       }
 
        /* init fb */
        fbi->fb_info = info;
@@ -667,7 +663,6 @@ failed_free_buff:
                fbi->fb_start_dma);
 failed_destroy_mutex:
        mutex_destroy(&fbi->access_ok);
-failed:
        dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
 
        framebuffer_release(info);
index 02f109a..c735d13 100644 (file)
@@ -2,12 +2,12 @@ if MMP_DISP
 
 config MMP_DISP_CONTROLLER
        bool "mmp display controller hw support"
-       depends on CPU_PXA910 || CPU_MMP2 || CPU_MMP3 || CPU_PXA988
+       depends on CPU_PXA910 || CPU_MMP2
        default n
        help
                Marvell MMP display hw controller support
-               this controller is used on Marvell PXA910,
-               MMP2, MMP3, PXA988 chips
+               this controller is used on Marvell PXA910 and
+               MMP2 chips
 
 config MMP_DISP_SPI
        bool "mmp display controller spi port"
index 53301cf..56fdeab 100644 (file)
@@ -167,11 +167,7 @@ struct lcd_regs {
                                PN2_IOPAD_CONTROL) : LCD_TOP_CTRL)
 
 /* dither configure */
-#ifdef CONFIG_CPU_PXA988
-#define LCD_DITHER_CTRL                                (0x01EC)
-#else
 #define LCD_DITHER_CTRL                                (0x00A0)
-#endif
 
 #define DITHER_TBL_INDEX_SEL(s)                ((s) << 16)
 #define DITHER_MODE2(m)                                ((m) << 12)
@@ -186,15 +182,6 @@ struct lcd_regs {
 #define DITHER_EN1                                     (1)
 
 /* dither table data was fixed by video bpp of input and output*/
-#ifdef CONFIG_CPU_PXA988
-#define DITHER_TB_4X4_INDEX0           (0x6e4ca280)
-#define DITHER_TB_4X4_INDEX1           (0x5d7f91b3)
-#define DITHER_TB_4X8_INDEX0           (0xb391a280)
-#define DITHER_TB_4X8_INDEX1           (0x7f5d6e4c)
-#define DITHER_TB_4X8_INDEX2           (0x80a291b3)
-#define DITHER_TB_4X8_INDEX3           (0x4c6e5d7f)
-#define LCD_DITHER_TBL_DATA            (0x01F0)
-#else
 #define DITHER_TB_4X4_INDEX0           (0x3b19f7d5)
 #define DITHER_TB_4X4_INDEX1           (0x082ac4e6)
 #define DITHER_TB_4X8_INDEX0           (0xf7d508e6)
@@ -202,7 +189,6 @@ struct lcd_regs {
 #define DITHER_TB_4X8_INDEX2           (0xc4e6d5f7)
 #define DITHER_TB_4X8_INDEX3           (0x082a193b)
 #define LCD_DITHER_TBL_DATA            (0x00A4)
-#endif
 
 /* Video Frame 0&1 start address registers */
 #define        LCD_SPU_DMA_START_ADDR_Y0       0x00C0
@@ -933,16 +919,9 @@ struct lcd_regs {
 #define LCD_PN2_SQULN2_CTRL                    (0x02F0)
 #define ALL_LAYER_ALPHA_SEL                    (0x02F4)
 
-/* pxa988 has different MASTER_CTRL from MMP3/MMP2 */
-#ifdef CONFIG_CPU_PXA988
-#define TIMING_MASTER_CONTROL                  (0x01F4)
-#define MASTER_ENH(id)                         (1 << ((id) + 5))
-#define MASTER_ENV(id)                         (1 << ((id) + 6))
-#else
 #define TIMING_MASTER_CONTROL                  (0x02F8)
 #define MASTER_ENH(id)                         (1 << (id))
 #define MASTER_ENV(id)                         (1 << ((id) + 4))
-#endif
 
 #define DSI_START_SEL_SHIFT(id)                (((id) << 1) + 8)
 #define timing_master_config(path, dsi_id, lcd_id) \
@@ -1312,19 +1291,8 @@ struct dsi_regs {
 #define        DSI_PHY_TIME_3_CFG_CSR_TIME_REQRDY_MASK         (0xff)
 #define        DSI_PHY_TIME_3_CFG_CSR_TIME_REQRDY_SHIFT        0
 
-/*
- * DSI timings
- * PXA988 has diffrent ESC CLK with MMP2/MMP3
- * it will be used in dsi_set_dphy() in pxa688_phy.c
- * as low power mode clock.
- */
-#ifdef CONFIG_CPU_PXA988
-#define DSI_ESC_CLK                            52  /* Unit: Mhz */
-#define DSI_ESC_CLK_T                          19  /* Unit: ns */
-#else
 #define DSI_ESC_CLK                            66  /* Unit: Mhz */
 #define DSI_ESC_CLK_T                          15  /* Unit: ns */
-#endif
 
 /* LVDS */
 /* LVDS_PHY_CTRL */
index 142e860..c645a0a 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/clk.h>
 #include <linux/mutex.h>
 #include <linux/dma/ipu-dma.h>
+#include <linux/backlight.h>
 
 #include <linux/platform_data/dma-imx.h>
 #include <linux/platform_data/video-mx3fb.h>
@@ -241,6 +242,7 @@ struct mx3fb_data {
        void __iomem            *reg_base;
        spinlock_t              lock;
        struct device           *dev;
+       struct backlight_device *bl;
 
        uint32_t                h_start_width;
        uint32_t                v_start_width;
@@ -271,6 +273,71 @@ struct mx3fb_info {
        struct fb_var_screeninfo        cur_var; /* current var info */
 };
 
+static void sdc_set_brightness(struct mx3fb_data *mx3fb, uint8_t value);
+static u32 sdc_get_brightness(struct mx3fb_data *mx3fb);
+
+static int mx3fb_bl_get_brightness(struct backlight_device *bl)
+{
+       struct mx3fb_data *fbd = bl_get_data(bl);
+
+       return sdc_get_brightness(fbd);
+}
+
+static int mx3fb_bl_update_status(struct backlight_device *bl)
+{
+       struct mx3fb_data *fbd = bl_get_data(bl);
+       int brightness = bl->props.brightness;
+
+       if (bl->props.power != FB_BLANK_UNBLANK)
+               brightness = 0;
+       if (bl->props.fb_blank != FB_BLANK_UNBLANK)
+               brightness = 0;
+
+       fbd->backlight_level = (fbd->backlight_level & ~0xFF) | brightness;
+
+       sdc_set_brightness(fbd, fbd->backlight_level);
+
+       return 0;
+}
+
+static const struct backlight_ops mx3fb_lcdc_bl_ops = {
+       .update_status = mx3fb_bl_update_status,
+       .get_brightness = mx3fb_bl_get_brightness,
+};
+
+static void mx3fb_init_backlight(struct mx3fb_data *fbd)
+{
+       struct backlight_properties props;
+       struct backlight_device *bl;
+
+       if (fbd->bl)
+               return;
+
+       memset(&props, 0, sizeof(struct backlight_properties));
+       props.max_brightness = 0xff;
+       props.type = BACKLIGHT_RAW;
+       sdc_set_brightness(fbd, fbd->backlight_level);
+
+       bl = backlight_device_register("mx3fb-bl", fbd->dev, fbd,
+                                      &mx3fb_lcdc_bl_ops, &props);
+       if (IS_ERR(bl)) {
+               dev_err(fbd->dev, "error %ld on backlight register\n",
+                               PTR_ERR(bl));
+               return;
+       }
+
+       fbd->bl = bl;
+       bl->props.power = FB_BLANK_UNBLANK;
+       bl->props.fb_blank = FB_BLANK_UNBLANK;
+       bl->props.brightness = mx3fb_bl_get_brightness(bl);
+}
+
+static void mx3fb_exit_backlight(struct mx3fb_data *fbd)
+{
+       if (fbd->bl)
+               backlight_device_unregister(fbd->bl);
+}
+
 static void mx3fb_dma_done(void *);
 
 /* Used fb-mode and bpp. Can be set on kernel command line, therefore file-static. */
@@ -628,6 +695,16 @@ static int sdc_set_global_alpha(struct mx3fb_data *mx3fb, bool enable, uint8_t a
        return 0;
 }
 
+static u32 sdc_get_brightness(struct mx3fb_data *mx3fb)
+{
+       u32 brightness;
+
+       brightness = mx3fb_read_reg(mx3fb, SDC_PWM_CTRL);
+       brightness = (brightness >> 16) & 0xFF;
+
+       return brightness;
+}
+
 static void sdc_set_brightness(struct mx3fb_data *mx3fb, uint8_t value)
 {
        dev_dbg(mx3fb->dev, "%s: value = %d\n", __func__, value);
@@ -1496,7 +1573,7 @@ static int mx3fb_probe(struct platform_device *pdev)
        if (!sdc_reg)
                return -EINVAL;
 
-       mx3fb = kzalloc(sizeof(*mx3fb), GFP_KERNEL);
+       mx3fb = devm_kzalloc(&pdev->dev, sizeof(*mx3fb), GFP_KERNEL);
        if (!mx3fb)
                return -ENOMEM;
 
@@ -1534,6 +1611,8 @@ static int mx3fb_probe(struct platform_device *pdev)
        if (ret < 0)
                goto eisdc0;
 
+       mx3fb_init_backlight(mx3fb);
+
        return 0;
 
 eisdc0:
@@ -1542,7 +1621,6 @@ ersdc0:
        dmaengine_put();
        iounmap(mx3fb->reg_base);
 eremap:
-       kfree(mx3fb);
        dev_err(dev, "mx3fb: failed to register fb\n");
        return ret;
 }
@@ -1557,11 +1635,12 @@ static int mx3fb_remove(struct platform_device *dev)
        chan = &mx3_fbi->idmac_channel->dma_chan;
        release_fbi(fbi);
 
+       mx3fb_exit_backlight(mx3fb);
+
        dma_release_channel(chan);
        dmaengine_put();
 
        iounmap(mx3fb->reg_base);
-       kfree(mx3fb);
        return 0;
 }
 
index 0bc3a93..18c4cb0 100644 (file)
@@ -39,6 +39,15 @@ config FB_OMAP_LCD_MIPID
          the Mobile Industry Processor Interface DBI-C/DCS
          specification. (Supported LCDs: Philips LPH8923, Sharp LS041Y3)
 
+config FB_OMAP_LCD_H3
+       bool "TPS65010 LCD controller on OMAP-H3"
+       depends on MACH_OMAP_H3
+       depends on TPS65010
+       default y
+       help
+         Say Y here if you want to have support for the LCD on the
+         H3 board.
+
 config FB_OMAP_DMA_TUNE
         bool "Set DMA SDRAM access priority high"
         depends on FB_OMAP
index 1927faf..732e071 100644 (file)
@@ -10,17 +10,18 @@ objs-y$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o
 
 objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o
 
-objs-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o
-objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o
-objs-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o
-objs-y$(CONFIG_MACH_OMAP_PALMTT) += lcd_palmtt.o
-objs-y$(CONFIG_MACH_OMAP_PALMZ71) += lcd_palmz71.o
-objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o
-objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o
-objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o
-
-objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o
-objs-y$(CONFIG_MACH_HERALD) += lcd_htcherald.o
+lcds-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o
+lcds-y$(CONFIG_FB_OMAP_LCD_H3) += lcd_h3.o
+lcds-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o
+lcds-y$(CONFIG_MACH_OMAP_PALMTT) += lcd_palmtt.o
+lcds-y$(CONFIG_MACH_OMAP_PALMZ71) += lcd_palmz71.o
+lcds-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o
+lcds-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o
+lcds-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o
+
+lcds-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o
+lcds-y$(CONFIG_MACH_HERALD) += lcd_htcherald.o
 
 omapfb-objs := $(objs-yy)
 
+obj-$(CONFIG_FB_OMAP) += $(lcds-yy)
index b52f625..6efa259 100644 (file)
@@ -74,7 +74,6 @@ static struct omap_lcd_controller {
        void                    (*dma_callback)(void *data);
        void                    *dma_callback_data;
 
-       int                     fbmem_allocated;
        dma_addr_t              vram_phys;
        void                    *vram_virt;
        unsigned long           vram_size;
@@ -611,42 +610,6 @@ static void lcdc_dma_handler(u16 status, void *data)
                lcdc.dma_callback(lcdc.dma_callback_data);
 }
 
-static int mmap_kern(void)
-{
-       struct vm_struct        *kvma;
-       struct vm_area_struct   vma;
-       pgprot_t                pgprot;
-       unsigned long           vaddr;
-
-       kvma = get_vm_area(lcdc.vram_size, VM_IOREMAP);
-       if (kvma == NULL) {
-               dev_err(lcdc.fbdev->dev, "can't get kernel vm area\n");
-               return -ENOMEM;
-       }
-       vma.vm_mm = &init_mm;
-
-       vaddr = (unsigned long)kvma->addr;
-       vma.vm_start = vaddr;
-       vma.vm_end = vaddr + lcdc.vram_size;
-
-       pgprot = pgprot_writecombine(pgprot_kernel);
-       if (io_remap_pfn_range(&vma, vaddr,
-                          lcdc.vram_phys >> PAGE_SHIFT,
-                          lcdc.vram_size, pgprot) < 0) {
-               dev_err(lcdc.fbdev->dev, "kernel mmap for FB memory failed\n");
-               return -EAGAIN;
-       }
-
-       lcdc.vram_virt = (void *)vaddr;
-
-       return 0;
-}
-
-static void unmap_kern(void)
-{
-       vunmap(lcdc.vram_virt);
-}
-
 static int alloc_palette_ram(void)
 {
        lcdc.palette_virt = dma_alloc_writecombine(lcdc.fbdev->dev,
@@ -703,8 +666,6 @@ static void free_fbmem(void)
 
 static int setup_fbmem(struct omapfb_mem_desc *req_md)
 {
-       int r;
-
        if (!req_md->region_cnt) {
                dev_err(lcdc.fbdev->dev, "no memory regions defined\n");
                return -EINVAL;
@@ -715,31 +676,7 @@ static int setup_fbmem(struct omapfb_mem_desc *req_md)
                req_md->region_cnt = 1;
        }
 
-       if (req_md->region[0].paddr == 0) {
-               lcdc.fbmem_allocated = 1;
-               if ((r = alloc_fbmem(&req_md->region[0])) < 0)
-                       return r;
-               return 0;
-       }
-
-       lcdc.vram_phys = req_md->region[0].paddr;
-       lcdc.vram_size = req_md->region[0].size;
-
-       if ((r = mmap_kern()) < 0)
-               return r;
-
-       dev_dbg(lcdc.fbdev->dev, "vram at %08x size %08lx mapped to 0x%p\n",
-                lcdc.vram_phys, lcdc.vram_size, lcdc.vram_virt);
-
-       return 0;
-}
-
-static void cleanup_fbmem(void)
-{
-       if (lcdc.fbmem_allocated)
-               free_fbmem();
-       else
-               unmap_kern();
+       return alloc_fbmem(&req_md->region[0]);
 }
 
 static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode,
@@ -833,7 +770,7 @@ static void omap_lcdc_cleanup(void)
 {
        if (!lcdc.ext_mode)
                free_palette_ram();
-       cleanup_fbmem();
+       free_fbmem();
        omap_free_lcd_dma();
        free_irq(OMAP_LCDC_IRQ, lcdc.fbdev);
        clk_disable(lcdc.lcd_ck);
index e4fc6d9..d8d028d 100644 (file)
@@ -1823,6 +1823,7 @@ void omapfb_register_panel(struct lcd_panel *panel)
        if (fbdev_pdev != NULL)
                omapfb_do_probe(fbdev_pdev, fbdev_panel);
 }
+EXPORT_SYMBOL_GPL(omapfb_register_panel);
 
 /* Called when the device is being detached from the driver */
 static int omapfb_remove(struct platform_device *pdev)
index bf8127d..f8745ec 100644 (file)
@@ -1,5 +1,5 @@
 obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
 
-obj-$(CONFIG_OMAP2_DSS) += dss/
+obj-y += dss/
 obj-y += displays-new/
 obj-$(CONFIG_FB_OMAP2) += omapfb/
index 29ed21b..4420ccb 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/of.h>
+#include <linux/of_gpio.h>
 
 #include <drm/drm_edid.h>
 
@@ -43,6 +44,8 @@ struct panel_drv_data {
        struct device *dev;
 
        struct omap_video_timings timings;
+
+       int hpd_gpio;
 };
 
 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
@@ -161,7 +164,10 @@ static bool hdmic_detect(struct omap_dss_device *dssdev)
        struct panel_drv_data *ddata = to_panel_data(dssdev);
        struct omap_dss_device *in = ddata->in;
 
-       return in->ops.hdmi->detect(in);
+       if (gpio_is_valid(ddata->hpd_gpio))
+               return gpio_get_value_cansleep(ddata->hpd_gpio);
+       else
+               return in->ops.hdmi->detect(in);
 }
 
 static int hdmic_audio_enable(struct omap_dss_device *dssdev)
@@ -288,6 +294,8 @@ static int hdmic_probe_pdata(struct platform_device *pdev)
 
        pdata = dev_get_platdata(&pdev->dev);
 
+       ddata->hpd_gpio = -ENODEV;
+
        in = omap_dss_find_output(pdata->source);
        if (in == NULL) {
                dev_err(&pdev->dev, "Failed to find video source\n");
@@ -307,6 +315,14 @@ static int hdmic_probe_of(struct platform_device *pdev)
        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
        struct device_node *node = pdev->dev.of_node;
        struct omap_dss_device *in;
+       int gpio;
+
+       /* HPD GPIO */
+       gpio = of_get_named_gpio(node, "hpd-gpios", 0);
+       if (gpio_is_valid(gpio))
+               ddata->hpd_gpio = gpio;
+       else
+               ddata->hpd_gpio = -ENODEV;
 
        in = omapdss_of_find_source_for_first_ep(node);
        if (IS_ERR(in)) {
@@ -344,6 +360,13 @@ static int hdmic_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
+       if (gpio_is_valid(ddata->hpd_gpio)) {
+               r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
+                               GPIOF_DIR_IN, "hdmi_hpd");
+               if (r)
+                       goto err_reg;
+       }
+
        ddata->timings = hdmic_default_timings;
 
        dssdev = &ddata->dssdev;
index 5f8f7e7..3636b61 100644 (file)
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
 
 #include <video/omapdss.h>
 #include <video/omap-panel-data.h>
+#include <video/of_display_timing.h>
 
 struct panel_drv_data {
        struct omap_dss_device dssdev;
@@ -25,8 +28,10 @@ struct panel_drv_data {
 
        struct omap_video_timings videomode;
 
+       /* used for non-DT boot, to be removed */
        int backlight_gpio;
-       int enable_gpio;
+
+       struct gpio_desc *enable_gpio;
 };
 
 #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
@@ -70,15 +75,16 @@ static int panel_dpi_enable(struct omap_dss_device *dssdev)
        if (omapdss_device_is_enabled(dssdev))
                return 0;
 
-       in->ops.dpi->set_data_lines(in, ddata->data_lines);
+       if (ddata->data_lines)
+               in->ops.dpi->set_data_lines(in, ddata->data_lines);
        in->ops.dpi->set_timings(in, &ddata->videomode);
 
        r = in->ops.dpi->enable(in);
        if (r)
                return r;
 
-       if (gpio_is_valid(ddata->enable_gpio))
-               gpio_set_value_cansleep(ddata->enable_gpio, 1);
+       if (ddata->enable_gpio)
+               gpiod_set_value_cansleep(ddata->enable_gpio, 1);
 
        if (gpio_is_valid(ddata->backlight_gpio))
                gpio_set_value_cansleep(ddata->backlight_gpio, 1);
@@ -96,8 +102,8 @@ static void panel_dpi_disable(struct omap_dss_device *dssdev)
        if (!omapdss_device_is_enabled(dssdev))
                return;
 
-       if (gpio_is_valid(ddata->enable_gpio))
-               gpio_set_value_cansleep(ddata->enable_gpio, 0);
+       if (ddata->enable_gpio)
+               gpiod_set_value_cansleep(ddata->enable_gpio, 0);
 
        if (gpio_is_valid(ddata->backlight_gpio))
                gpio_set_value_cansleep(ddata->backlight_gpio, 0);
@@ -156,6 +162,7 @@ static int panel_dpi_probe_pdata(struct platform_device *pdev)
        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
        struct omap_dss_device *dssdev, *in;
        struct videomode vm;
+       int r;
 
        pdata = dev_get_platdata(&pdev->dev);
 
@@ -176,9 +183,64 @@ static int panel_dpi_probe_pdata(struct platform_device *pdev)
        dssdev = &ddata->dssdev;
        dssdev->name = pdata->name;
 
-       ddata->enable_gpio = pdata->enable_gpio;
+       r = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio,
+                                       GPIOF_OUT_INIT_LOW, "panel enable");
+       if (r)
+               goto err_gpio;
+
+       ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio);
+
        ddata->backlight_gpio = pdata->backlight_gpio;
 
+       return 0;
+
+err_gpio:
+       omap_dss_put_device(ddata->in);
+       return r;
+}
+
+static int panel_dpi_probe_of(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct device_node *node = pdev->dev.of_node;
+       struct omap_dss_device *in;
+       int r;
+       struct display_timing timing;
+       struct videomode vm;
+       struct gpio_desc *gpio;
+
+       gpio = devm_gpiod_get(&pdev->dev, "enable");
+
+       if (IS_ERR(gpio)) {
+               if (PTR_ERR(gpio) != -ENOENT)
+                       return PTR_ERR(gpio);
+               else
+                       gpio = NULL;
+       } else {
+               gpiod_direction_output(gpio, 0);
+       }
+
+       ddata->enable_gpio = gpio;
+
+       ddata->backlight_gpio = -ENOENT;
+
+       r = of_get_display_timing(node, "panel-timing", &timing);
+       if (r) {
+               dev_err(&pdev->dev, "failed to get video timing\n");
+               return r;
+       }
+
+       videomode_from_timing(&timing, &vm);
+       videomode_to_omap_video_timings(&vm, &ddata->videomode);
+
+       in = omapdss_of_find_source_for_first_ep(node);
+       if (IS_ERR(in)) {
+               dev_err(&pdev->dev, "failed to find video source\n");
+               return PTR_ERR(in);
+       }
+
+       ddata->in = in;
+
        return 0;
 }
 
@@ -198,17 +260,14 @@ static int panel_dpi_probe(struct platform_device *pdev)
                r = panel_dpi_probe_pdata(pdev);
                if (r)
                        return r;
+       } else if (pdev->dev.of_node) {
+               r = panel_dpi_probe_of(pdev);
+               if (r)
+                       return r;
        } else {
                return -ENODEV;
        }
 
-       if (gpio_is_valid(ddata->enable_gpio)) {
-               r = devm_gpio_request_one(&pdev->dev, ddata->enable_gpio,
-                               GPIOF_OUT_INIT_LOW, "panel enable");
-               if (r)
-                       goto err_gpio;
-       }
-
        if (gpio_is_valid(ddata->backlight_gpio)) {
                r = devm_gpio_request_one(&pdev->dev, ddata->backlight_gpio,
                                GPIOF_OUT_INIT_LOW, "panel backlight");
@@ -254,12 +313,20 @@ static int __exit panel_dpi_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id panel_dpi_of_match[] = {
+       { .compatible = "omapdss,panel-dpi", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
+
 static struct platform_driver panel_dpi_driver = {
        .probe = panel_dpi_probe,
        .remove = __exit_p(panel_dpi_remove),
        .driver = {
                .name = "panel-dpi",
                .owner = THIS_MODULE,
+               .of_match_table = panel_dpi_of_match,
        },
 };
 
index 2e6b513..cc5b512 100644 (file)
@@ -50,9 +50,10 @@ struct panel_drv_data {
 
        struct omap_video_timings videomode;
 
-       int reset_gpio;
+       /* used for non-DT boot, to be removed */
        int backlight_gpio;
-       int enable_gpio;
+
+       struct gpio_desc *enable_gpio;
 };
 
 #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
@@ -158,15 +159,16 @@ static int lb035q02_enable(struct omap_dss_device *dssdev)
        if (omapdss_device_is_enabled(dssdev))
                return 0;
 
-       in->ops.dpi->set_data_lines(in, ddata->data_lines);
+       if (ddata->data_lines)
+               in->ops.dpi->set_data_lines(in, ddata->data_lines);
        in->ops.dpi->set_timings(in, &ddata->videomode);
 
        r = in->ops.dpi->enable(in);
        if (r)
                return r;
 
-       if (gpio_is_valid(ddata->enable_gpio))
-               gpio_set_value_cansleep(ddata->enable_gpio, 1);
+       if (ddata->enable_gpio)
+               gpiod_set_value_cansleep(ddata->enable_gpio, 1);
 
        if (gpio_is_valid(ddata->backlight_gpio))
                gpio_set_value_cansleep(ddata->backlight_gpio, 1);
@@ -184,8 +186,8 @@ static void lb035q02_disable(struct omap_dss_device *dssdev)
        if (!omapdss_device_is_enabled(dssdev))
                return;
 
-       if (gpio_is_valid(ddata->enable_gpio))
-               gpio_set_value_cansleep(ddata->enable_gpio, 0);
+       if (ddata->enable_gpio)
+               gpiod_set_value_cansleep(ddata->enable_gpio, 0);
 
        if (gpio_is_valid(ddata->backlight_gpio))
                gpio_set_value_cansleep(ddata->backlight_gpio, 0);
@@ -243,6 +245,7 @@ static int lb035q02_probe_pdata(struct spi_device *spi)
        const struct panel_lb035q02_platform_data *pdata;
        struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
        struct omap_dss_device *dssdev, *in;
+       int r;
 
        pdata = dev_get_platdata(&spi->dev);
 
@@ -260,9 +263,47 @@ static int lb035q02_probe_pdata(struct spi_device *spi)
        dssdev = &ddata->dssdev;
        dssdev->name = pdata->name;
 
-       ddata->enable_gpio = pdata->enable_gpio;
+       r = devm_gpio_request_one(&spi->dev, pdata->enable_gpio,
+                                       GPIOF_OUT_INIT_LOW, "panel enable");
+       if (r)
+               goto err_gpio;
+
+       ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio);
+
        ddata->backlight_gpio = pdata->backlight_gpio;
 
+       return 0;
+err_gpio:
+       omap_dss_put_device(ddata->in);
+       return r;
+}
+
+static int lb035q02_probe_of(struct spi_device *spi)
+{
+       struct device_node *node = spi->dev.of_node;
+       struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+       struct omap_dss_device *in;
+       struct gpio_desc *gpio;
+
+       gpio = devm_gpiod_get(&spi->dev, "enable");
+       if (IS_ERR(gpio)) {
+               dev_err(&spi->dev, "failed to parse enable gpio\n");
+               return PTR_ERR(gpio);
+       } else {
+               gpiod_direction_output(gpio, 0);
+               ddata->enable_gpio = gpio;
+       }
+
+       ddata->backlight_gpio = -ENOENT;
+
+       in = omapdss_of_find_source_for_first_ep(node);
+       if (IS_ERR(in)) {
+               dev_err(&spi->dev, "failed to find video source\n");
+               return PTR_ERR(in);
+       }
+
+       ddata->in = in;
+
        return 0;
 }
 
@@ -284,17 +325,14 @@ static int lb035q02_panel_spi_probe(struct spi_device *spi)
                r = lb035q02_probe_pdata(spi);
                if (r)
                        return r;
+       } else if (spi->dev.of_node) {
+               r = lb035q02_probe_of(spi);
+               if (r)
+                       return r;
        } else {
                return -ENODEV;
        }
 
-       if (gpio_is_valid(ddata->enable_gpio)) {
-               r = devm_gpio_request_one(&spi->dev, ddata->enable_gpio,
-                               GPIOF_OUT_INIT_LOW, "panel enable");
-               if (r)
-                       goto err_gpio;
-       }
-
        if (gpio_is_valid(ddata->backlight_gpio)) {
                r = devm_gpio_request_one(&spi->dev, ddata->backlight_gpio,
                                GPIOF_OUT_INIT_LOW, "panel backlight");
@@ -342,17 +380,26 @@ static int lb035q02_panel_spi_remove(struct spi_device *spi)
        return 0;
 }
 
+static const struct of_device_id lb035q02_of_match[] = {
+       { .compatible = "omapdss,lgphilips,lb035q02", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, lb035q02_of_match);
+
 static struct spi_driver lb035q02_spi_driver = {
        .probe          = lb035q02_panel_spi_probe,
        .remove         = lb035q02_panel_spi_remove,
        .driver         = {
                .name   = "panel_lgphilips_lb035q02",
                .owner  = THIS_MODULE,
+               .of_match_table = lb035q02_of_match,
        },
 };
 
 module_spi_driver(lb035q02_spi_driver);
 
+MODULE_ALIAS("spi:lgphilips,lb035q02");
 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
 MODULE_LICENSE("GPL");
index 996fa00..3595f11 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/spi/spi.h>
 #include <linux/fb.h>
 #include <linux/gpio.h>
+#include <linux/of_gpio.h>
 
 #include <video/omapdss.h>
 #include <video/omap-panel-data.h>
@@ -156,7 +157,8 @@ static int nec_8048_enable(struct omap_dss_device *dssdev)
        if (omapdss_device_is_enabled(dssdev))
                return 0;
 
-       in->ops.dpi->set_data_lines(in, ddata->data_lines);
+       if (ddata->data_lines)
+               in->ops.dpi->set_data_lines(in, ddata->data_lines);
        in->ops.dpi->set_timings(in, &ddata->videomode);
 
        r = in->ops.dpi->enable(in);
@@ -258,6 +260,34 @@ static int nec_8048_probe_pdata(struct spi_device *spi)
        return 0;
 }
 
+static int nec_8048_probe_of(struct spi_device *spi)
+{
+       struct device_node *node = spi->dev.of_node;
+       struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+       struct omap_dss_device *in;
+       int gpio;
+
+       gpio = of_get_named_gpio(node, "reset-gpios", 0);
+       if (!gpio_is_valid(gpio)) {
+               dev_err(&spi->dev, "failed to parse enable gpio\n");
+               return gpio;
+       }
+       ddata->res_gpio = gpio;
+
+       /* XXX the panel spec doesn't mention any QVGA pin?? */
+       ddata->qvga_gpio = -ENOENT;
+
+       in = omapdss_of_find_source_for_first_ep(node);
+       if (IS_ERR(in)) {
+               dev_err(&spi->dev, "failed to find video source\n");
+               return PTR_ERR(in);
+       }
+
+       ddata->in = in;
+
+       return 0;
+}
+
 static int nec_8048_probe(struct spi_device *spi)
 {
        struct panel_drv_data *ddata;
@@ -289,6 +319,10 @@ static int nec_8048_probe(struct spi_device *spi)
                r = nec_8048_probe_pdata(spi);
                if (r)
                        return r;
+       } else if (spi->dev.of_node) {
+               r = nec_8048_probe_of(spi);
+               if (r)
+                       return r;
        } else {
                return -ENODEV;
        }
@@ -377,11 +411,19 @@ static SIMPLE_DEV_PM_OPS(nec_8048_pm_ops, nec_8048_suspend,
 #define NEC_8048_PM_OPS NULL
 #endif
 
+static const struct of_device_id nec_8048_of_match[] = {
+       { .compatible = "omapdss,nec,nl8048hl11", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, nec_8048_of_match);
+
 static struct spi_driver nec_8048_driver = {
        .driver = {
                .name   = "panel-nec-nl8048hl11",
                .owner  = THIS_MODULE,
                .pm     = NEC_8048_PM_OPS,
+               .of_match_table = nec_8048_of_match,
        },
        .probe  = nec_8048_probe,
        .remove = nec_8048_remove,
@@ -389,6 +431,7 @@ static struct spi_driver nec_8048_driver = {
 
 module_spi_driver(nec_8048_driver);
 
+MODULE_ALIAS("spi:nec,nl8048hl11");
 MODULE_AUTHOR("Erik Gilling <konkers@android.com>");
 MODULE_DESCRIPTION("NEC-NL8048HL11 Driver");
 MODULE_LICENSE("GPL");
index b2f710b..f1f72ce 100644 (file)
 #include <linux/delay.h>
 #include <linux/gpio.h>
 #include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
-
+#include <linux/regulator/consumer.h>
 #include <video/omapdss.h>
 #include <video/omap-panel-data.h>
 
 struct panel_drv_data {
        struct omap_dss_device dssdev;
        struct omap_dss_device *in;
+       struct regulator *vcc;
 
        int data_lines;
 
        struct omap_video_timings videomode;
 
-       int resb_gpio;
-       int ini_gpio;
-       int mo_gpio;
-       int lr_gpio;
-       int ud_gpio;
+       struct gpio_desc *resb_gpio;    /* low = reset active min 20 us */
+       struct gpio_desc *ini_gpio;     /* high = power on */
+       struct gpio_desc *mo_gpio;      /* low = 480x640, high = 240x320 */
+       struct gpio_desc *lr_gpio;      /* high = conventional horizontal scanning */
+       struct gpio_desc *ud_gpio;      /* high = conventional vertical scanning */
 };
 
 static const struct omap_video_timings sharp_ls_timings = {
@@ -95,21 +98,30 @@ static int sharp_ls_enable(struct omap_dss_device *dssdev)
        if (omapdss_device_is_enabled(dssdev))
                return 0;
 
-       in->ops.dpi->set_data_lines(in, ddata->data_lines);
+       if (ddata->data_lines)
+               in->ops.dpi->set_data_lines(in, ddata->data_lines);
        in->ops.dpi->set_timings(in, &ddata->videomode);
 
+       if (ddata->vcc) {
+               r = regulator_enable(ddata->vcc);
+               if (r != 0)
+                       return r;
+       }
+
        r = in->ops.dpi->enable(in);
-       if (r)
+       if (r) {
+               regulator_disable(ddata->vcc);
                return r;
+       }
 
        /* wait couple of vsyncs until enabling the LCD */
        msleep(50);
 
-       if (gpio_is_valid(ddata->resb_gpio))
-               gpio_set_value_cansleep(ddata->resb_gpio, 1);
+       if (ddata->resb_gpio)
+               gpiod_set_value_cansleep(ddata->resb_gpio, 1);
 
-       if (gpio_is_valid(ddata->ini_gpio))
-               gpio_set_value_cansleep(ddata->ini_gpio, 1);
+       if (ddata->ini_gpio)
+               gpiod_set_value_cansleep(ddata->ini_gpio, 1);
 
        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 
@@ -124,11 +136,11 @@ static void sharp_ls_disable(struct omap_dss_device *dssdev)
        if (!omapdss_device_is_enabled(dssdev))
                return;
 
-       if (gpio_is_valid(ddata->ini_gpio))
-               gpio_set_value_cansleep(ddata->ini_gpio, 0);
+       if (ddata->ini_gpio)
+               gpiod_set_value_cansleep(ddata->ini_gpio, 0);
 
-       if (gpio_is_valid(ddata->resb_gpio))
-               gpio_set_value_cansleep(ddata->resb_gpio, 0);
+       if (ddata->resb_gpio)
+               gpiod_set_value_cansleep(ddata->resb_gpio, 0);
 
        /* wait at least 5 vsyncs after disabling the LCD */
 
@@ -136,6 +148,9 @@ static void sharp_ls_disable(struct omap_dss_device *dssdev)
 
        in->ops.dpi->disable(in);
 
+       if (ddata->vcc)
+               regulator_disable(ddata->vcc);
+
        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 }
 
@@ -182,11 +197,32 @@ static struct omap_dss_driver sharp_ls_ops = {
        .get_resolution = omapdss_default_get_resolution,
 };
 
+static int sharp_ls_get_gpio(struct device *dev, int gpio, unsigned long flags,
+                 char *desc, struct gpio_desc **gpiod)
+{
+       struct gpio_desc *gd;
+       int r;
+
+       *gpiod = NULL;
+
+       r = devm_gpio_request_one(dev, gpio, flags, desc);
+       if (r)
+               return r == -ENOENT ? 0 : r;
+
+       gd = gpio_to_desc(gpio);
+       if (IS_ERR(gd))
+               return PTR_ERR(gd) == -ENOENT ? 0 : PTR_ERR(gd);
+
+       *gpiod = gd;
+       return 0;
+}
+
 static int sharp_ls_probe_pdata(struct platform_device *pdev)
 {
        const struct panel_sharp_ls037v7dw01_platform_data *pdata;
        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
        struct omap_dss_device *dssdev, *in;
+       int r;
 
        pdata = dev_get_platdata(&pdev->dev);
 
@@ -204,11 +240,95 @@ static int sharp_ls_probe_pdata(struct platform_device *pdev)
        dssdev = &ddata->dssdev;
        dssdev->name = pdata->name;
 
-       ddata->resb_gpio = pdata->resb_gpio;
-       ddata->ini_gpio = pdata->ini_gpio;
-       ddata->mo_gpio = pdata->mo_gpio;
-       ddata->lr_gpio = pdata->lr_gpio;
-       ddata->ud_gpio = pdata->ud_gpio;
+       r = sharp_ls_get_gpio(&pdev->dev, pdata->mo_gpio, GPIOF_OUT_INIT_LOW,
+               "lcd MO", &ddata->mo_gpio);
+       if (r)
+               return r;
+       r = sharp_ls_get_gpio(&pdev->dev, pdata->lr_gpio, GPIOF_OUT_INIT_HIGH,
+               "lcd LR", &ddata->lr_gpio);
+       if (r)
+               return r;
+       r = sharp_ls_get_gpio(&pdev->dev, pdata->ud_gpio, GPIOF_OUT_INIT_HIGH,
+               "lcd UD", &ddata->ud_gpio);
+       if (r)
+               return r;
+       r = sharp_ls_get_gpio(&pdev->dev, pdata->resb_gpio, GPIOF_OUT_INIT_LOW,
+               "lcd RESB", &ddata->resb_gpio);
+       if (r)
+               return r;
+       r = sharp_ls_get_gpio(&pdev->dev, pdata->ini_gpio, GPIOF_OUT_INIT_LOW,
+               "lcd INI", &ddata->ini_gpio);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static  int sharp_ls_get_gpio_of(struct device *dev, int index, int val,
+       const char *desc, struct gpio_desc **gpiod)
+{
+       struct gpio_desc *gd;
+       int r;
+
+       *gpiod = NULL;
+
+       gd = devm_gpiod_get_index(dev, desc, index);
+       if (IS_ERR(gd))
+               return PTR_ERR(gd) == -ENOENT ? 0 : PTR_ERR(gd);
+
+       r = gpiod_direction_output(gd, val);
+       if (r)
+               return r;
+
+       *gpiod = gd;
+       return 0;
+}
+
+static int sharp_ls_probe_of(struct platform_device *pdev)
+{
+       struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+       struct device_node *node = pdev->dev.of_node;
+       struct omap_dss_device *in;
+       int r;
+
+       ddata->vcc = devm_regulator_get(&pdev->dev, "envdd");
+       if (IS_ERR(ddata->vcc)) {
+               dev_err(&pdev->dev, "failed to get regulator\n");
+               return PTR_ERR(ddata->vcc);
+       }
+
+       /* lcd INI */
+       r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "enable", &ddata->ini_gpio);
+       if (r)
+               return r;
+
+       /* lcd RESB */
+       r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "reset", &ddata->resb_gpio);
+       if (r)
+               return r;
+
+       /* lcd MO */
+       r = sharp_ls_get_gpio_of(&pdev->dev, 0, 0, "mode", &ddata->mo_gpio);
+       if (r)
+               return r;
+
+       /* lcd LR */
+       r = sharp_ls_get_gpio_of(&pdev->dev, 1, 1, "mode", &ddata->lr_gpio);
+       if (r)
+               return r;
+
+       /* lcd UD */
+       r = sharp_ls_get_gpio_of(&pdev->dev, 2, 1, "mode", &ddata->ud_gpio);
+       if (r)
+               return r;
+
+       in = omapdss_of_find_source_for_first_ep(node);
+       if (IS_ERR(in)) {
+               dev_err(&pdev->dev, "failed to find video source\n");
+               return PTR_ERR(in);
+       }
+
+       ddata->in = in;
 
        return 0;
 }
@@ -229,45 +349,14 @@ static int sharp_ls_probe(struct platform_device *pdev)
                r = sharp_ls_probe_pdata(pdev);
                if (r)
                        return r;
+       } else if (pdev->dev.of_node) {
+               r = sharp_ls_probe_of(pdev);
+               if (r)
+                       return r;
        } else {
                return -ENODEV;
        }
 
-       if (gpio_is_valid(ddata->mo_gpio)) {
-               r = devm_gpio_request_one(&pdev->dev, ddata->mo_gpio,
-                               GPIOF_OUT_INIT_LOW, "lcd MO");
-               if (r)
-                       goto err_gpio;
-       }
-
-       if (gpio_is_valid(ddata->lr_gpio)) {
-               r = devm_gpio_request_one(&pdev->dev, ddata->lr_gpio,
-                               GPIOF_OUT_INIT_HIGH, "lcd LR");
-               if (r)
-                       goto err_gpio;
-       }
-
-       if (gpio_is_valid(ddata->ud_gpio)) {
-               r = devm_gpio_request_one(&pdev->dev, ddata->ud_gpio,
-                               GPIOF_OUT_INIT_HIGH, "lcd UD");
-               if (r)
-                       goto err_gpio;
-       }
-
-       if (gpio_is_valid(ddata->resb_gpio)) {
-               r = devm_gpio_request_one(&pdev->dev, ddata->resb_gpio,
-                               GPIOF_OUT_INIT_LOW, "lcd RESB");
-               if (r)
-                       goto err_gpio;
-       }
-
-       if (gpio_is_valid(ddata->ini_gpio)) {
-               r = devm_gpio_request_one(&pdev->dev, ddata->ini_gpio,
-                               GPIOF_OUT_INIT_LOW, "lcd INI");
-               if (r)
-                       goto err_gpio;
-       }
-
        ddata->videomode = sharp_ls_timings;
 
        dssdev = &ddata->dssdev;
@@ -287,7 +376,6 @@ static int sharp_ls_probe(struct platform_device *pdev)
        return 0;
 
 err_reg:
-err_gpio:
        omap_dss_put_device(ddata->in);
        return r;
 }
@@ -308,12 +396,20 @@ static int __exit sharp_ls_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id sharp_ls_of_match[] = {
+       { .compatible = "omapdss,sharp,ls037v7dw01", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, sharp_ls_of_match);
+
 static struct platform_driver sharp_ls_driver = {
        .probe = sharp_ls_probe,
        .remove = __exit_p(sharp_ls_remove),
        .driver = {
                .name = "panel-sharp-ls037v7dw01",
                .owner = THIS_MODULE,
+               .of_match_table = sharp_ls_of_match,
        },
 };
 
index fae6adc..728808b 100644 (file)
@@ -206,7 +206,8 @@ static int td028ttec1_panel_enable(struct omap_dss_device *dssdev)
        if (omapdss_device_is_enabled(dssdev))
                return 0;
 
-       in->ops.dpi->set_data_lines(in, ddata->data_lines);
+       if (ddata->data_lines)
+               in->ops.dpi->set_data_lines(in, ddata->data_lines);
        in->ops.dpi->set_timings(in, &ddata->videomode);
 
        r = in->ops.dpi->enable(in);
@@ -389,6 +390,23 @@ static int td028ttec1_panel_probe_pdata(struct spi_device *spi)
        return 0;
 }
 
+static int td028ttec1_probe_of(struct spi_device *spi)
+{
+       struct device_node *node = spi->dev.of_node;
+       struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+       struct omap_dss_device *in;
+
+       in = omapdss_of_find_source_for_first_ep(node);
+       if (IS_ERR(in)) {
+               dev_err(&spi->dev, "failed to find video source\n");
+               return PTR_ERR(in);
+       }
+
+       ddata->in = in;
+
+       return 0;
+}
+
 static int td028ttec1_panel_probe(struct spi_device *spi)
 {
        struct panel_drv_data *ddata;
@@ -418,6 +436,10 @@ static int td028ttec1_panel_probe(struct spi_device *spi)
                r = td028ttec1_panel_probe_pdata(spi);
                if (r)
                        return r;
+       } else if (spi->dev.of_node) {
+               r = td028ttec1_probe_of(spi);
+               if (r)
+                       return r;
        } else {
                return -ENODEV;
        }
@@ -463,6 +485,13 @@ static int td028ttec1_panel_remove(struct spi_device *spi)
        return 0;
 }
 
+static const struct of_device_id td028ttec1_of_match[] = {
+       { .compatible = "omapdss,toppoly,td028ttec1", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, td028ttec1_of_match);
+
 static struct spi_driver td028ttec1_spi_driver = {
        .probe          = td028ttec1_panel_probe,
        .remove         = td028ttec1_panel_remove,
@@ -470,11 +499,13 @@ static struct spi_driver td028ttec1_spi_driver = {
        .driver         = {
                .name   = "panel-tpo-td028ttec1",
                .owner  = THIS_MODULE,
+               .of_match_table = td028ttec1_of_match,
        },
 };
 
 module_spi_driver(td028ttec1_spi_driver);
 
+MODULE_ALIAS("spi:toppoly,td028ttec1");
 MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
 MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver");
 MODULE_LICENSE("GPL");
index 875b402..de78ab0 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/gpio.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/of_gpio.h>
 
 #include <video/omapdss.h>
 #include <video/omap-panel-data.h>
@@ -376,7 +377,8 @@ static int tpo_td043_enable(struct omap_dss_device *dssdev)
        if (omapdss_device_is_enabled(dssdev))
                return 0;
 
-       in->ops.dpi->set_data_lines(in, ddata->data_lines);
+       if (ddata->data_lines)
+               in->ops.dpi->set_data_lines(in, ddata->data_lines);
        in->ops.dpi->set_timings(in, &ddata->videomode);
 
        r = in->ops.dpi->enable(in);
@@ -489,6 +491,31 @@ static int tpo_td043_probe_pdata(struct spi_device *spi)
        return 0;
 }
 
+static int tpo_td043_probe_of(struct spi_device *spi)
+{
+       struct device_node *node = spi->dev.of_node;
+       struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev);
+       struct omap_dss_device *in;
+       int gpio;
+
+       gpio = of_get_named_gpio(node, "reset-gpios", 0);
+       if (!gpio_is_valid(gpio)) {
+               dev_err(&spi->dev, "failed to parse enable gpio\n");
+               return gpio;
+       }
+       ddata->nreset_gpio = gpio;
+
+       in = omapdss_of_find_source_for_first_ep(node);
+       if (IS_ERR(in)) {
+               dev_err(&spi->dev, "failed to find video source\n");
+               return PTR_ERR(in);
+       }
+
+       ddata->in = in;
+
+       return 0;
+}
+
 static int tpo_td043_probe(struct spi_device *spi)
 {
        struct panel_drv_data *ddata;
@@ -518,6 +545,10 @@ static int tpo_td043_probe(struct spi_device *spi)
                r = tpo_td043_probe_pdata(spi);
                if (r)
                        return r;
+       } else if (spi->dev.of_node) {
+               r = tpo_td043_probe_of(spi);
+               if (r)
+                       return r;
        } else {
                return -ENODEV;
        }
@@ -629,11 +660,19 @@ static int tpo_td043_spi_resume(struct device *dev)
 static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm,
        tpo_td043_spi_suspend, tpo_td043_spi_resume);
 
+static const struct of_device_id tpo_td043_of_match[] = {
+       { .compatible = "omapdss,tpo,td043mtea1", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, tpo_td043_of_match);
+
 static struct spi_driver tpo_td043_spi_driver = {
        .driver = {
                .name   = "panel-tpo-td043mtea1",
                .owner  = THIS_MODULE,
                .pm     = &tpo_td043_spi_pm,
+               .of_match_table = tpo_td043_of_match,
        },
        .probe  = tpo_td043_probe,
        .remove = tpo_td043_remove,
@@ -641,6 +680,7 @@ static struct spi_driver tpo_td043_spi_driver = {
 
 module_spi_driver(tpo_td043_spi_driver);
 
+MODULE_ALIAS("spi:tpo,td043mtea1");
 MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
 MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver");
 MODULE_LICENSE("GPL");
index dde4281..285bcd1 100644 (file)
@@ -1,6 +1,10 @@
+config OMAP2_DSS_INIT
+       bool
+
 menuconfig OMAP2_DSS
         tristate "OMAP2+ Display Subsystem support"
        select VIDEOMODE_HELPERS
+       select OMAP2_DSS_INIT
         help
          OMAP2+ Display Subsystem support.
 
@@ -59,16 +63,32 @@ config OMAP2_DSS_VENC
        help
          OMAP Video Encoder support for S-Video and composite TV-out.
 
+config OMAP2_DSS_HDMI_COMMON
+       bool
+
 config OMAP4_DSS_HDMI
-       bool "HDMI support"
+       bool "HDMI support for OMAP4"
         default y
+       select OMAP2_DSS_HDMI_COMMON
        help
-         HDMI Interface. This adds the High Definition Multimedia Interface.
-         See http://www.hdmi.org/ for HDMI specification.
+         HDMI support for OMAP4 based SoCs.
 
 config OMAP4_DSS_HDMI_AUDIO
        bool
 
+config OMAP5_DSS_HDMI
+       bool "HDMI support for OMAP5"
+       default n
+       select OMAP2_DSS_HDMI_COMMON
+       help
+         HDMI Interface for OMAP5 and similar cores. This adds the High
+         Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI
+         specification.
+
+config OMAP5_DSS_HDMI_AUDIO
+       depends on OMAP5_DSS_HDMI
+       bool
+
 config OMAP2_DSS_SDI
        bool "SDI support"
         default n
index 8aec8bd..245f933 100644 (file)
@@ -1,3 +1,4 @@
+obj-$(CONFIG_OMAP2_DSS_INIT) += omapdss-boot-init.o
 obj-$(CONFIG_OMAP2_DSS) += omapdss.o
 # Core DSS files
 omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \
@@ -10,6 +11,8 @@ omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
 omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
 omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
 omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
-omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi4.o hdmi_common.o hdmi_wp.o hdmi_pll.o \
-       hdmi_phy.o hdmi4_core.o
+omapdss-$(CONFIG_OMAP2_DSS_HDMI_COMMON) += hdmi_common.o hdmi_wp.o hdmi_pll.o \
+       hdmi_phy.o
+omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi4.o hdmi4_core.o
+omapdss-$(CONFIG_OMAP5_DSS_HDMI) += hdmi5.o hdmi5_core.o
 ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG
index ffa45c8..6b74f73 100644 (file)
@@ -268,6 +268,9 @@ static int (*dss_output_drv_reg_funcs[])(void) __initdata = {
 #ifdef CONFIG_OMAP4_DSS_HDMI
        hdmi4_init_platform_driver,
 #endif
+#ifdef CONFIG_OMAP5_DSS_HDMI
+       hdmi5_init_platform_driver,
+#endif
 };
 
 static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = {
@@ -289,6 +292,9 @@ static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = {
 #ifdef CONFIG_OMAP4_DSS_HDMI
        hdmi4_uninit_platform_driver,
 #endif
+#ifdef CONFIG_OMAP5_DSS_HDMI
+       hdmi5_uninit_platform_driver,
+#endif
 };
 
 static bool dss_output_drv_loaded[ARRAY_SIZE(dss_output_drv_reg_funcs)];
index f18397c..7aa33b0 100644 (file)
@@ -2577,9 +2577,9 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
 
        channel = dispc_ovl_get_channel_out(plane);
 
-       DSSDBG("dispc_ovl_setup %d, pa %x, pa_uv %x, sw %d, %d,%d, %dx%d -> "
-               "%dx%d, cmode %x, rot %d, mir %d, chan %d repl %d\n",
-               plane, oi->paddr, oi->p_uv_addr, oi->screen_width, oi->pos_x,
+       DSSDBG("dispc_ovl_setup %d, pa %pad, pa_uv %pad, sw %d, %d,%d, %dx%d ->"
+               " %dx%d, cmode %x, rot %d, mir %d, chan %d repl %d\n",
+               plane, &oi->paddr, &oi->p_uv_addr, oi->screen_width, oi->pos_x,
                oi->pos_y, oi->width, oi->height, oi->out_width, oi->out_height,
                oi->color_mode, oi->rotation, oi->mirror, channel, replication);
 
@@ -2945,13 +2945,13 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
                BUG();
        }
 
-       l = dispc_read_reg(DISPC_POL_FREQ(channel));
-       l |= FLD_VAL(onoff, 17, 17);
-       l |= FLD_VAL(rf, 16, 16);
-       l |= FLD_VAL(de_level, 15, 15);
-       l |= FLD_VAL(ipc, 14, 14);
-       l |= FLD_VAL(hsync_level, 13, 13);
-       l |= FLD_VAL(vsync_level, 12, 12);
+       l = FLD_VAL(onoff, 17, 17) |
+               FLD_VAL(rf, 16, 16) |
+               FLD_VAL(de_level, 15, 15) |
+               FLD_VAL(ipc, 14, 14) |
+               FLD_VAL(hsync_level, 13, 13) |
+               FLD_VAL(vsync_level, 12, 12);
+
        dispc_write_reg(DISPC_POL_FREQ(channel), l);
 }
 
@@ -3656,6 +3656,7 @@ static int __init dispc_init_features(struct platform_device *pdev)
        case OMAPDSS_VER_OMAP34xx_ES3:
        case OMAPDSS_VER_OMAP3630:
        case OMAPDSS_VER_AM35xx:
+       case OMAPDSS_VER_AM43xx:
                src = &omap34xx_rev3_0_dispc_feats;
                break;
 
@@ -3829,6 +3830,7 @@ static const struct of_device_id dispc_of_match[] = {
        { .compatible = "ti,omap2-dispc", },
        { .compatible = "ti,omap3-dispc", },
        { .compatible = "ti,omap4-dispc", },
+       { .compatible = "ti,omap5-dispc", },
        {},
 };
 
index 157921d..9368972 100644 (file)
@@ -67,6 +67,7 @@ static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
        case OMAPDSS_VER_OMAP34xx_ES3:
        case OMAPDSS_VER_OMAP3630:
        case OMAPDSS_VER_AM35xx:
+       case OMAPDSS_VER_AM43xx:
                return NULL;
 
        case OMAPDSS_VER_OMAP4430_ES1:
@@ -103,6 +104,8 @@ static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
                return OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC;
        case OMAP_DSS_CHANNEL_LCD2:
                return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
+       case OMAP_DSS_CHANNEL_LCD3:
+               return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
        default:
                /* this shouldn't happen */
                WARN_ON(1);
@@ -595,6 +598,7 @@ static enum omap_channel dpi_get_channel(void)
        case OMAPDSS_VER_OMAP34xx_ES3:
        case OMAPDSS_VER_OMAP3630:
        case OMAPDSS_VER_AM35xx:
+       case OMAPDSS_VER_AM43xx:
                return OMAP_DSS_CHANNEL_LCD;
 
        case OMAPDSS_VER_OMAP4430_ES1:
index 8be9b04..4755a34 100644 (file)
@@ -1161,6 +1161,7 @@ static int dsi_regulator_init(struct platform_device *dsidev)
 {
        struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
        struct regulator *vdds_dsi;
+       int r;
 
        if (dsi->vdds_dsi_reg != NULL)
                return 0;
@@ -1173,6 +1174,15 @@ static int dsi_regulator_init(struct platform_device *dsidev)
                return PTR_ERR(vdds_dsi);
        }
 
+       if (regulator_can_change_voltage(vdds_dsi)) {
+               r = regulator_set_voltage(vdds_dsi, 1800000, 1800000);
+               if (r) {
+                       devm_regulator_put(vdds_dsi);
+                       DSSERR("can't set the DSI regulator voltage\n");
+                       return r;
+               }
+       }
+
        dsi->vdds_dsi_reg = vdds_dsi;
 
        return 0;
@@ -5122,6 +5132,7 @@ static enum omap_channel dsi_get_channel(int module_id)
 {
        switch (omapdss_get_version()) {
        case OMAPDSS_VER_OMAP24xx:
+       case OMAPDSS_VER_AM43xx:
                DSSWARN("DSI not supported\n");
                return OMAP_DSS_CHANNEL_LCD;
 
@@ -5723,9 +5734,16 @@ static const struct dsi_module_id_data dsi_of_data_omap4[] = {
        { },
 };
 
+static const struct dsi_module_id_data dsi_of_data_omap5[] = {
+       { .address = 0x58004000, .id = 0, },
+       { .address = 0x58009000, .id = 1, },
+       { },
+};
+
 static const struct of_device_id dsi_of_match[] = {
        { .compatible = "ti,omap3-dsi", .data = dsi_of_data_omap3, },
        { .compatible = "ti,omap4-dsi", .data = dsi_of_data_omap4, },
+       { .compatible = "ti,omap5-dsi", .data = dsi_of_data_omap5, },
        {},
 };
 
index d55266c..6daeb7e 100644 (file)
@@ -728,6 +728,13 @@ static const struct dss_features omap54xx_dss_feats __initconst = {
        .dpi_select_source      =       &dss_dpi_select_source_omap5,
 };
 
+static const struct dss_features am43xx_dss_feats __initconst = {
+       .fck_div_max            =       0,
+       .dss_fck_multiplier     =       0,
+       .parent_clk_name        =       NULL,
+       .dpi_select_source      =       &dss_dpi_select_source_omap2_omap3,
+};
+
 static int __init dss_init_features(struct platform_device *pdev)
 {
        const struct dss_features *src;
@@ -764,6 +771,10 @@ static int __init dss_init_features(struct platform_device *pdev)
                src = &omap54xx_dss_feats;
                break;
 
+       case OMAPDSS_VER_AM43xx:
+               src = &am43xx_dss_feats;
+               break;
+
        default:
                return -ENODEV;
        }
@@ -784,12 +795,8 @@ static int __init dss_init_ports(struct platform_device *pdev)
                return 0;
 
        port = omapdss_of_get_next_port(parent, NULL);
-       if (!port) {
-#ifdef CONFIG_OMAP2_DSS_DPI
-               dpi_init_port(pdev, parent);
-#endif
+       if (!port)
                return 0;
-       }
 
        do {
                u32 reg;
@@ -813,7 +820,7 @@ static int __init dss_init_ports(struct platform_device *pdev)
        return 0;
 }
 
-static void dss_uninit_ports(void)
+static void __exit dss_uninit_ports(void)
 {
 #ifdef CONFIG_OMAP2_DSS_DPI
        dpi_uninit_port();
@@ -946,6 +953,7 @@ static const struct of_device_id dss_of_match[] = {
        { .compatible = "ti,omap2-dss", },
        { .compatible = "ti,omap3-dss", },
        { .compatible = "ti,omap4-dss", },
+       { .compatible = "ti,omap5-dss", },
        {},
 };
 
index 560078f..8ff22c1 100644 (file)
@@ -419,6 +419,9 @@ void venc_uninit_platform_driver(void) __exit;
 int hdmi4_init_platform_driver(void) __init;
 void hdmi4_uninit_platform_driver(void) __exit;
 
+int hdmi5_init_platform_driver(void) __init;
+void hdmi5_uninit_platform_driver(void) __exit;
+
 /* RFBI */
 int rfbi_init_platform_driver(void) __init;
 void rfbi_uninit_platform_driver(void) __exit;
index 7f89691..15088df 100644 (file)
@@ -93,6 +93,17 @@ static const struct dss_reg_field omap3_dss_reg_fields[] = {
        [FEAT_REG_DSIPLL_REGM_DSI]              = { 26, 23 },
 };
 
+static const struct dss_reg_field am43xx_dss_reg_fields[] = {
+       [FEAT_REG_FIRHINC]                      = { 12, 0 },
+       [FEAT_REG_FIRVINC]                      = { 28, 16 },
+       [FEAT_REG_FIFOLOWTHRESHOLD]     = { 11, 0 },
+       [FEAT_REG_FIFOHIGHTHRESHOLD]            = { 27, 16 },
+       [FEAT_REG_FIFOSIZE]             = { 10, 0 },
+       [FEAT_REG_HORIZONTALACCU]               = { 9, 0 },
+       [FEAT_REG_VERTICALACCU]                 = { 25, 16 },
+       [FEAT_REG_DISPC_CLK_SWITCH]             = { 0, 0 },
+};
+
 static const struct dss_reg_field omap4_dss_reg_fields[] = {
        [FEAT_REG_FIRHINC]                      = { 12, 0 },
        [FEAT_REG_FIRVINC]                      = { 28, 16 },
@@ -149,6 +160,11 @@ static const enum omap_display_type omap3630_dss_supported_displays[] = {
        OMAP_DISPLAY_TYPE_VENC,
 };
 
+static const enum omap_display_type am43xx_dss_supported_displays[] = {
+       /* OMAP_DSS_CHANNEL_LCD */
+       OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI,
+};
+
 static const enum omap_display_type omap4_dss_supported_displays[] = {
        /* OMAP_DSS_CHANNEL_LCD */
        OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI,
@@ -200,6 +216,11 @@ static const enum omap_dss_output_id omap3630_dss_supported_outputs[] = {
        OMAP_DSS_OUTPUT_VENC,
 };
 
+static const enum omap_dss_output_id am43xx_dss_supported_outputs[] = {
+       /* OMAP_DSS_CHANNEL_LCD */
+       OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI,
+};
+
 static const enum omap_dss_output_id omap4_dss_supported_outputs[] = {
        /* OMAP_DSS_CHANNEL_LCD */
        OMAP_DSS_OUTPUT_DBI | OMAP_DSS_OUTPUT_DSI1,
@@ -444,6 +465,13 @@ static const struct dss_param_range omap3_dss_param_range[] = {
        [FEAT_PARAM_LINEWIDTH]                  = { 1, 1024 },
 };
 
+static const struct dss_param_range am43xx_dss_param_range[] = {
+       [FEAT_PARAM_DSS_FCK]                    = { 0, 200000000 },
+       [FEAT_PARAM_DSS_PCD]                    = { 2, 255 },
+       [FEAT_PARAM_DOWNSCALE]                  = { 1, 4 },
+       [FEAT_PARAM_LINEWIDTH]                  = { 1, 1024 },
+};
+
 static const struct dss_param_range omap4_dss_param_range[] = {
        [FEAT_PARAM_DSS_FCK]                    = { 0, 186000000 },
        [FEAT_PARAM_DSS_PCD]                    = { 1, 255 },
@@ -520,6 +548,21 @@ static const enum dss_feat_id am35xx_dss_feat_list[] = {
        FEAT_OMAP3_DSI_FIFO_BUG,
 };
 
+static const enum dss_feat_id am43xx_dss_feat_list[] = {
+       FEAT_LCDENABLEPOL,
+       FEAT_LCDENABLESIGNAL,
+       FEAT_PCKFREEENABLE,
+       FEAT_FUNCGATED,
+       FEAT_LINEBUFFERSPLIT,
+       FEAT_ROWREPEATENABLE,
+       FEAT_RESIZECONF,
+       FEAT_CPR,
+       FEAT_PRELOAD,
+       FEAT_FIR_COEF_V,
+       FEAT_ALPHA_FIXED_ZORDER,
+       FEAT_FIFO_MERGE,
+};
+
 static const enum dss_feat_id omap3630_dss_feat_list[] = {
        FEAT_LCDENABLEPOL,
        FEAT_LCDENABLESIGNAL,
@@ -595,6 +638,7 @@ static const enum dss_feat_id omap4_dss_feat_list[] = {
 
 static const enum dss_feat_id omap5_dss_feat_list[] = {
        FEAT_MGR_LCD2,
+       FEAT_MGR_LCD3,
        FEAT_CORE_CLK_DIV,
        FEAT_LCD_CLK_SRC,
        FEAT_DSI_DCS_CMD_CONFIG_VC,
@@ -682,6 +726,26 @@ static const struct omap_dss_features am35xx_dss_features = {
        .burst_size_unit = 8,
 };
 
+static const struct omap_dss_features am43xx_dss_features = {
+       .reg_fields = am43xx_dss_reg_fields,
+       .num_reg_fields = ARRAY_SIZE(am43xx_dss_reg_fields),
+
+       .features = am43xx_dss_feat_list,
+       .num_features = ARRAY_SIZE(am43xx_dss_feat_list),
+
+       .num_mgrs = 1,
+       .num_ovls = 3,
+       .supported_displays = am43xx_dss_supported_displays,
+       .supported_outputs = am43xx_dss_supported_outputs,
+       .supported_color_modes = omap3_dss_supported_color_modes,
+       .overlay_caps = omap3430_dss_overlay_caps,
+       .clksrc_names = omap2_dss_clk_source_names,
+       .dss_params = am43xx_dss_param_range,
+       .supported_rotation_types = OMAP_DSS_ROT_DMA,
+       .buffer_size_unit = 1,
+       .burst_size_unit = 8,
+};
+
 static const struct omap_dss_features omap3630_dss_features = {
        .reg_fields = omap3_dss_reg_fields,
        .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields),
@@ -777,7 +841,7 @@ static const struct omap_dss_features omap5_dss_features = {
        .features = omap5_dss_feat_list,
        .num_features = ARRAY_SIZE(omap5_dss_feat_list),
 
-       .num_mgrs = 3,
+       .num_mgrs = 4,
        .num_ovls = 4,
        .supported_displays = omap5_dss_supported_displays,
        .supported_outputs = omap5_dss_supported_outputs,
@@ -928,6 +992,10 @@ void dss_features_init(enum omapdss_version version)
                omap_current_dss_features = &am35xx_dss_features;
                break;
 
+       case OMAPDSS_VER_AM43xx:
+               omap_current_dss_features = &am43xx_dss_features;
+               break;
+
        default:
                DSSWARN("Unsupported OMAP version");
                break;
index e25681f..fbee078 100644 (file)
@@ -80,6 +80,7 @@
 #define HDMI_TXPHY_DIGITAL_CTRL                        0x4
 #define HDMI_TXPHY_POWER_CTRL                  0x8
 #define HDMI_TXPHY_PAD_CFG_CTRL                        0xC
+#define HDMI_TXPHY_BIST_CONTROL                        0x1C
 
 enum hdmi_pll_pwr {
        HDMI_PLLPWRCMD_ALLOFF = 0,
@@ -351,7 +352,8 @@ struct hdmi_pll_data {
 struct hdmi_phy_data {
        void __iomem *base;
 
-       int irq;
+       u8 lane_function[4];
+       u8 lane_polarity[4];
 };
 
 struct hdmi_core_data {
@@ -360,13 +362,13 @@ struct hdmi_core_data {
        struct hdmi_core_infoframe_avi avi_cfg;
 };
 
-static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx,
+static inline void hdmi_write_reg(void __iomem *base_addr, const u32 idx,
                u32 val)
 {
        __raw_writel(val, base_addr + idx);
 }
 
-static inline u32 hdmi_read_reg(void __iomem *base_addr, const u16 idx)
+static inline u32 hdmi_read_reg(void __iomem *base_addr, const u32 idx)
 {
        return __raw_readl(base_addr + idx);
 }
@@ -417,18 +419,19 @@ void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy);
 int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll);
 
 /* HDMI PHY funcs */
-int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp,
-               struct hdmi_config *cfg);
-void hdmi_phy_disable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp);
+int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg);
 void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s);
 int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy);
+int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes);
 
 /* HDMI common funcs */
 const struct hdmi_config *hdmi_default_timing(void);
 const struct hdmi_config *hdmi_get_timings(int mode, int code);
 struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing);
+int hdmi_parse_lanes_of(struct platform_device *pdev, struct device_node *ep,
+       struct hdmi_phy_data *phy);
 
-#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
 int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts);
 int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable);
 int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable);
index f5f7944..626aad2 100644 (file)
@@ -81,8 +81,40 @@ static void hdmi_runtime_put(void)
        WARN_ON(r < 0 && r != -ENOSYS);
 }
 
+static irqreturn_t hdmi_irq_handler(int irq, void *data)
+{
+       struct hdmi_wp_data *wp = data;
+       u32 irqstatus;
+
+       irqstatus = hdmi_wp_get_irqstatus(wp);
+       hdmi_wp_set_irqstatus(wp, irqstatus);
+
+       if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
+                       irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
+               /*
+                * If we get both connect and disconnect interrupts at the same
+                * time, turn off the PHY, clear interrupts, and restart, which
+                * raises connect interrupt if a cable is connected, or nothing
+                * if cable is not connected.
+                */
+               hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
+
+               hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
+                               HDMI_IRQ_LINK_DISCONNECT);
+
+               hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
+       } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
+               hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
+       } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
+               hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
+       }
+
+       return IRQ_HANDLED;
+}
+
 static int hdmi_init_regulator(void)
 {
+       int r;
        struct regulator *reg;
 
        if (hdmi.vdda_hdmi_dac_reg != NULL)
@@ -96,6 +128,15 @@ static int hdmi_init_regulator(void)
                return PTR_ERR(reg);
        }
 
+       if (regulator_can_change_voltage(reg)) {
+               r = regulator_set_voltage(reg, 1800000, 1800000);
+               if (r) {
+                       devm_regulator_put(reg);
+                       DSSWARN("can't set the regulator voltage\n");
+                       return r;
+               }
+       }
+
        hdmi.vdda_hdmi_dac_reg = reg;
 
        return 0;
@@ -140,11 +181,16 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
        struct omap_video_timings *p;
        struct omap_overlay_manager *mgr = hdmi.output.manager;
        unsigned long phy;
+       struct hdmi_wp_data *wp = &hdmi.wp;
 
        r = hdmi_power_on_core(dssdev);
        if (r)
                return r;
 
+       /* disable and clear irqs */
+       hdmi_wp_clear_irqenable(wp, 0xffffffff);
+       hdmi_wp_set_irqstatus(wp, 0xffffffff);
+
        p = &hdmi.cfg.timings;
 
        DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
@@ -161,12 +207,16 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
                goto err_pll_enable;
        }
 
-       r = hdmi_phy_enable(&hdmi.phy, &hdmi.wp, &hdmi.cfg);
+       r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg);
        if (r) {
-               DSSDBG("Failed to start PHY\n");
-               goto err_phy_enable;
+               DSSDBG("Failed to configure PHY\n");
+               goto err_phy_cfg;
        }
 
+       r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
+       if (r)
+               goto err_phy_pwr;
+
        hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
 
        /* bypass TV gamma table */
@@ -183,13 +233,17 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
        if (r)
                goto err_mgr_enable;
 
+       hdmi_wp_set_irqenable(wp,
+               HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
+
        return 0;
 
 err_mgr_enable:
        hdmi_wp_video_stop(&hdmi.wp);
 err_vid_enable:
-       hdmi_phy_disable(&hdmi.phy, &hdmi.wp);
-err_phy_enable:
+err_phy_cfg:
+       hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
+err_phy_pwr:
        hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
 err_pll_enable:
        hdmi_power_off_core(dssdev);
@@ -200,10 +254,14 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev)
 {
        struct omap_overlay_manager *mgr = hdmi.output.manager;
 
+       hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
+
        dss_mgr_disable(mgr);
 
        hdmi_wp_video_stop(&hdmi.wp);
-       hdmi_phy_disable(&hdmi.phy, &hdmi.wp);
+
+       hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
+
        hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
 
        hdmi_power_off_core(dssdev);
@@ -600,15 +658,44 @@ static void __exit hdmi_uninit_output(struct platform_device *pdev)
        omapdss_unregister_output(out);
 }
 
+static int hdmi_probe_of(struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       struct device_node *ep;
+       int r;
+
+       ep = omapdss_of_get_first_endpoint(node);
+       if (!ep)
+               return 0;
+
+       r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy);
+       if (r)
+               goto err;
+
+       of_node_put(ep);
+       return 0;
+
+err:
+       of_node_put(ep);
+       return r;
+}
+
 /* HDMI HW IP initialisation */
 static int omapdss_hdmihw_probe(struct platform_device *pdev)
 {
        int r;
+       int irq;
 
        hdmi.pdev = pdev;
 
        mutex_init(&hdmi.lock);
 
+       if (pdev->dev.of_node) {
+               r = hdmi_probe_of(pdev);
+               if (r)
+                       return r;
+       }
+
        r = hdmi_wp_init(pdev, &hdmi.wp);
        if (r)
                return r;
@@ -631,6 +718,20 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
                return r;
        }
 
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               DSSERR("platform_get_irq failed\n");
+               return -ENODEV;
+       }
+
+       r = devm_request_threaded_irq(&pdev->dev, irq,
+                       NULL, hdmi_irq_handler,
+                       IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp);
+       if (r) {
+               DSSERR("HDMI IRQ request failed\n");
+               return r;
+       }
+
        pm_runtime_enable(&pdev->dev);
 
        hdmi_init_output(pdev);
index 2eb04dc..8bde7b7 100644 (file)
@@ -998,38 +998,20 @@ int hdmi4_audio_get_dma_port(u32 *offset, u32 *size)
 
 #endif
 
-#define CORE_OFFSET            0x400
-#define CORE_SIZE              0xc00
-
 int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core)
 {
        struct resource *res;
-       struct resource temp_res;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
        if (!res) {
-               DSSDBG("can't get CORE mem resource by name\n");
-               /*
-                * if hwmod/DT doesn't have the memory resource information
-                * split into HDMI sub blocks by name, we try again by getting
-                * the platform's first resource. this code will be removed when
-                * the driver can get the mem resources by name
-                */
-               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-               if (!res) {
-                       DSSERR("can't get CORE mem resource\n");
-                       return -EINVAL;
-               }
-
-               temp_res.start = res->start + CORE_OFFSET;
-               temp_res.end = temp_res.start + CORE_SIZE - 1;
-               res = &temp_res;
+               DSSERR("can't get CORE mem resource\n");
+               return -EINVAL;
        }
 
-       core->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-       if (!core->base) {
+       core->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(core->base)) {
                DSSERR("can't ioremap CORE\n");
-               return -ENOMEM;
+               return PTR_ERR(core->base);
        }
 
        return 0;
diff --git a/drivers/video/fbdev/omap2/dss/hdmi5.c b/drivers/video/fbdev/omap2/dss/hdmi5.c
new file mode 100644 (file)
index 0000000..c468b9e
--- /dev/null
@@ -0,0 +1,829 @@
+/*
+ * HDMI driver for OMAP5
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated
+ *
+ * Authors:
+ *     Yong Zhi
+ *     Mythri pk
+ *     Archit Taneja <archit@ti.com>
+ *     Tomi Valkeinen <tomi.valkeinen@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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "HDMI"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <video/omapdss.h>
+
+#include "hdmi5_core.h"
+#include "dss.h"
+#include "dss_features.h"
+
+static struct {
+       struct mutex lock;
+       struct platform_device *pdev;
+
+       struct hdmi_wp_data     wp;
+       struct hdmi_pll_data    pll;
+       struct hdmi_phy_data    phy;
+       struct hdmi_core_data   core;
+
+       struct hdmi_config cfg;
+
+       struct clk *sys_clk;
+       struct regulator *vdda_reg;
+
+       bool core_enabled;
+
+       struct omap_dss_device output;
+} hdmi;
+
+static int hdmi_runtime_get(void)
+{
+       int r;
+
+       DSSDBG("hdmi_runtime_get\n");
+
+       r = pm_runtime_get_sync(&hdmi.pdev->dev);
+       WARN_ON(r < 0);
+       if (r < 0)
+               return r;
+
+       return 0;
+}
+
+static void hdmi_runtime_put(void)
+{
+       int r;
+
+       DSSDBG("hdmi_runtime_put\n");
+
+       r = pm_runtime_put_sync(&hdmi.pdev->dev);
+       WARN_ON(r < 0 && r != -ENOSYS);
+}
+
+static irqreturn_t hdmi_irq_handler(int irq, void *data)
+{
+       struct hdmi_wp_data *wp = data;
+       u32 irqstatus;
+
+       irqstatus = hdmi_wp_get_irqstatus(wp);
+       hdmi_wp_set_irqstatus(wp, irqstatus);
+
+       if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
+                       irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
+               u32 v;
+               /*
+                * If we get both connect and disconnect interrupts at the same
+                * time, turn off the PHY, clear interrupts, and restart, which
+                * raises connect interrupt if a cable is connected, or nothing
+                * if cable is not connected.
+                */
+
+               hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
+
+               /*
+                * We always get bogus CONNECT & DISCONNECT interrupts when
+                * setting the PHY to LDOON. To ignore those, we force the RXDET
+                * line to 0 until the PHY power state has been changed.
+                */
+               v = hdmi_read_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL);
+               v = FLD_MOD(v, 1, 15, 15); /* FORCE_RXDET_HIGH */
+               v = FLD_MOD(v, 0, 14, 7); /* RXDET_LINE */
+               hdmi_write_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, v);
+
+               hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
+                               HDMI_IRQ_LINK_DISCONNECT);
+
+               hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
+
+               REG_FLD_MOD(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, 0, 15, 15);
+
+       } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
+               hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
+       } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
+               hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int hdmi_init_regulator(void)
+{
+       int r;
+       struct regulator *reg;
+
+       if (hdmi.vdda_reg != NULL)
+               return 0;
+
+       reg = devm_regulator_get(&hdmi.pdev->dev, "vdda");
+       if (IS_ERR(reg)) {
+               DSSERR("can't get VDDA regulator\n");
+               return PTR_ERR(reg);
+       }
+
+       if (regulator_can_change_voltage(reg)) {
+               r = regulator_set_voltage(reg, 1800000, 1800000);
+               if (r) {
+                       devm_regulator_put(reg);
+                       DSSWARN("can't set the regulator voltage\n");
+                       return r;
+               }
+       }
+
+       hdmi.vdda_reg = reg;
+
+       return 0;
+}
+
+static int hdmi_power_on_core(struct omap_dss_device *dssdev)
+{
+       int r;
+
+       r = regulator_enable(hdmi.vdda_reg);
+       if (r)
+               return r;
+
+       r = hdmi_runtime_get();
+       if (r)
+               goto err_runtime_get;
+
+       /* Make selection of HDMI in DSS */
+       dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
+
+       hdmi.core_enabled = true;
+
+       return 0;
+
+err_runtime_get:
+       regulator_disable(hdmi.vdda_reg);
+
+       return r;
+}
+
+static void hdmi_power_off_core(struct omap_dss_device *dssdev)
+{
+       hdmi.core_enabled = false;
+
+       hdmi_runtime_put();
+       regulator_disable(hdmi.vdda_reg);
+}
+
+static int hdmi_power_on_full(struct omap_dss_device *dssdev)
+{
+       int r;
+       struct omap_video_timings *p;
+       struct omap_overlay_manager *mgr = hdmi.output.manager;
+       unsigned long phy;
+
+       r = hdmi_power_on_core(dssdev);
+       if (r)
+               return r;
+
+       p = &hdmi.cfg.timings;
+
+       DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
+
+       /* the functions below use kHz pixel clock. TODO: change to Hz */
+       phy = p->pixelclock / 1000;
+
+       hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy);
+
+       /* disable and clear irqs */
+       hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
+       hdmi_wp_set_irqstatus(&hdmi.wp,
+                       hdmi_wp_get_irqstatus(&hdmi.wp));
+
+       /* config the PLL and PHY hdmi_set_pll_pwrfirst */
+       r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp);
+       if (r) {
+               DSSDBG("Failed to lock PLL\n");
+               goto err_pll_enable;
+       }
+
+       r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg);
+       if (r) {
+               DSSDBG("Failed to start PHY\n");
+               goto err_phy_cfg;
+       }
+
+       r = hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_LDOON);
+       if (r)
+               goto err_phy_pwr;
+
+       hdmi5_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
+
+       /* bypass TV gamma table */
+       dispc_enable_gamma_table(0);
+
+       /* tv size */
+       dss_mgr_set_timings(mgr, p);
+
+       r = hdmi_wp_video_start(&hdmi.wp);
+       if (r)
+               goto err_vid_enable;
+
+       r = dss_mgr_enable(mgr);
+       if (r)
+               goto err_mgr_enable;
+
+       hdmi_wp_set_irqenable(&hdmi.wp,
+                       HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
+
+       return 0;
+
+err_mgr_enable:
+       hdmi_wp_video_stop(&hdmi.wp);
+err_vid_enable:
+       hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
+err_phy_pwr:
+err_phy_cfg:
+       hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
+err_pll_enable:
+       hdmi_power_off_core(dssdev);
+       return -EIO;
+}
+
+static void hdmi_power_off_full(struct omap_dss_device *dssdev)
+{
+       struct omap_overlay_manager *mgr = hdmi.output.manager;
+
+       hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
+
+       dss_mgr_disable(mgr);
+
+       hdmi_wp_video_stop(&hdmi.wp);
+
+       hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
+
+       hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
+
+       hdmi_power_off_core(dssdev);
+}
+
+static int hdmi_display_check_timing(struct omap_dss_device *dssdev,
+                                       struct omap_video_timings *timings)
+{
+       struct omap_dss_device *out = &hdmi.output;
+
+       if (!dispc_mgr_timings_ok(out->dispc_channel, timings))
+               return -EINVAL;
+
+       return 0;
+}
+
+static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       struct hdmi_cm cm;
+       const struct hdmi_config *t;
+
+       mutex_lock(&hdmi.lock);
+
+       cm = hdmi_get_code(timings);
+       hdmi.cfg.cm = cm;
+
+       t = hdmi_get_timings(cm.mode, cm.code);
+       if (t != NULL) {
+               hdmi.cfg = *t;
+
+               dispc_set_tv_pclk(t->timings.pixelclock);
+       } else {
+               hdmi.cfg.timings = *timings;
+               hdmi.cfg.cm.code = 0;
+               hdmi.cfg.cm.mode = HDMI_DVI;
+
+               dispc_set_tv_pclk(timings->pixelclock);
+       }
+
+       DSSDBG("using mode: %s, code %d\n", hdmi.cfg.cm.mode == HDMI_DVI ?
+                       "DVI" : "HDMI", hdmi.cfg.cm.code);
+
+       mutex_unlock(&hdmi.lock);
+}
+
+static void hdmi_display_get_timings(struct omap_dss_device *dssdev,
+               struct omap_video_timings *timings)
+{
+       const struct hdmi_config *cfg;
+       struct hdmi_cm cm = hdmi.cfg.cm;
+
+       cfg = hdmi_get_timings(cm.mode, cm.code);
+       if (cfg == NULL)
+               cfg = hdmi_default_timing();
+
+       memcpy(timings, &cfg->timings, sizeof(cfg->timings));
+}
+
+static void hdmi_dump_regs(struct seq_file *s)
+{
+       mutex_lock(&hdmi.lock);
+
+       if (hdmi_runtime_get()) {
+               mutex_unlock(&hdmi.lock);
+               return;
+       }
+
+       hdmi_wp_dump(&hdmi.wp, s);
+       hdmi_pll_dump(&hdmi.pll, s);
+       hdmi_phy_dump(&hdmi.phy, s);
+       hdmi5_core_dump(&hdmi.core, s);
+
+       hdmi_runtime_put();
+       mutex_unlock(&hdmi.lock);
+}
+
+static int read_edid(u8 *buf, int len)
+{
+       int r;
+       int idlemode;
+
+       mutex_lock(&hdmi.lock);
+
+       r = hdmi_runtime_get();
+       BUG_ON(r);
+
+       idlemode = REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2);
+       /* No-idle mode */
+       REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
+
+       r = hdmi5_read_edid(&hdmi.core,  buf, len);
+
+       REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2);
+
+       hdmi_runtime_put();
+       mutex_unlock(&hdmi.lock);
+
+       return r;
+}
+
+static int hdmi_display_enable(struct omap_dss_device *dssdev)
+{
+       struct omap_dss_device *out = &hdmi.output;
+       int r = 0;
+
+       DSSDBG("ENTER hdmi_display_enable\n");
+
+       mutex_lock(&hdmi.lock);
+
+       if (out == NULL || out->manager == NULL) {
+               DSSERR("failed to enable display: no output/manager\n");
+               r = -ENODEV;
+               goto err0;
+       }
+
+       r = hdmi_power_on_full(dssdev);
+       if (r) {
+               DSSERR("failed to power on device\n");
+               goto err0;
+       }
+
+       mutex_unlock(&hdmi.lock);
+       return 0;
+
+err0:
+       mutex_unlock(&hdmi.lock);
+       return r;
+}
+
+static void hdmi_display_disable(struct omap_dss_device *dssdev)
+{
+       DSSDBG("Enter hdmi_display_disable\n");
+
+       mutex_lock(&hdmi.lock);
+
+       hdmi_power_off_full(dssdev);
+
+       mutex_unlock(&hdmi.lock);
+}
+
+static int hdmi_core_enable(struct omap_dss_device *dssdev)
+{
+       int r = 0;
+
+       DSSDBG("ENTER omapdss_hdmi_core_enable\n");
+
+       mutex_lock(&hdmi.lock);
+
+       r = hdmi_power_on_core(dssdev);
+       if (r) {
+               DSSERR("failed to power on device\n");
+               goto err0;
+       }
+
+       mutex_unlock(&hdmi.lock);
+       return 0;
+
+err0:
+       mutex_unlock(&hdmi.lock);
+       return r;
+}
+
+static void hdmi_core_disable(struct omap_dss_device *dssdev)
+{
+       DSSDBG("Enter omapdss_hdmi_core_disable\n");
+
+       mutex_lock(&hdmi.lock);
+
+       hdmi_power_off_core(dssdev);
+
+       mutex_unlock(&hdmi.lock);
+}
+
+static int hdmi_get_clocks(struct platform_device *pdev)
+{
+       struct clk *clk;
+
+       clk = devm_clk_get(&pdev->dev, "sys_clk");
+       if (IS_ERR(clk)) {
+               DSSERR("can't get sys_clk\n");
+               return PTR_ERR(clk);
+       }
+
+       hdmi.sys_clk = clk;
+
+       return 0;
+}
+
+static int hdmi_connect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       struct omap_overlay_manager *mgr;
+       int r;
+
+       r = hdmi_init_regulator();
+       if (r)
+               return r;
+
+       mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
+       if (!mgr)
+               return -ENODEV;
+
+       r = dss_mgr_connect(mgr, dssdev);
+       if (r)
+               return r;
+
+       r = omapdss_output_set_device(dssdev, dst);
+       if (r) {
+               DSSERR("failed to connect output to new device: %s\n",
+                               dst->name);
+               dss_mgr_disconnect(mgr, dssdev);
+               return r;
+       }
+
+       return 0;
+}
+
+static void hdmi_disconnect(struct omap_dss_device *dssdev,
+               struct omap_dss_device *dst)
+{
+       WARN_ON(dst != dssdev->dst);
+
+       if (dst != dssdev->dst)
+               return;
+
+       omapdss_output_unset_device(dssdev);
+
+       if (dssdev->manager)
+               dss_mgr_disconnect(dssdev->manager, dssdev);
+}
+
+static int hdmi_read_edid(struct omap_dss_device *dssdev,
+               u8 *edid, int len)
+{
+       bool need_enable;
+       int r;
+
+       need_enable = hdmi.core_enabled == false;
+
+       if (need_enable) {
+               r = hdmi_core_enable(dssdev);
+               if (r)
+                       return r;
+       }
+
+       r = read_edid(edid, len);
+
+       if (need_enable)
+               hdmi_core_disable(dssdev);
+
+       return r;
+}
+
+#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+static int hdmi_audio_enable(struct omap_dss_device *dssdev)
+{
+       int r;
+
+       mutex_lock(&hdmi.lock);
+
+       if (!hdmi_mode_has_audio(hdmi.cfg.cm.mode)) {
+               r = -EPERM;
+               goto err;
+       }
+
+       r = hdmi_wp_audio_enable(&hdmi.wp, true);
+       if (r)
+               goto err;
+
+       mutex_unlock(&hdmi.lock);
+       return 0;
+
+err:
+       mutex_unlock(&hdmi.lock);
+       return r;
+}
+
+static void hdmi_audio_disable(struct omap_dss_device *dssdev)
+{
+       hdmi_wp_audio_enable(&hdmi.wp, false);
+}
+
+static int hdmi_audio_start(struct omap_dss_device *dssdev)
+{
+       return hdmi_wp_audio_core_req_enable(&hdmi.wp, true);
+}
+
+static void hdmi_audio_stop(struct omap_dss_device *dssdev)
+{
+       hdmi_wp_audio_core_req_enable(&hdmi.wp, false);
+}
+
+static bool hdmi_audio_supported(struct omap_dss_device *dssdev)
+{
+       bool r;
+
+       mutex_lock(&hdmi.lock);
+
+       r = hdmi_mode_has_audio(hdmi.cfg.cm.mode);
+
+       mutex_unlock(&hdmi.lock);
+       return r;
+}
+
+static int hdmi_audio_config(struct omap_dss_device *dssdev,
+               struct omap_dss_audio *audio)
+{
+       int r;
+       u32 pclk = hdmi.cfg.timings.pixelclock;
+
+       mutex_lock(&hdmi.lock);
+
+       if (!hdmi_mode_has_audio(hdmi.cfg.cm.mode)) {
+               r = -EPERM;
+               goto err;
+       }
+
+       r = hdmi5_audio_config(&hdmi.core, &hdmi.wp, audio, pclk);
+       if (r)
+               goto err;
+
+       mutex_unlock(&hdmi.lock);
+       return 0;
+
+err:
+       mutex_unlock(&hdmi.lock);
+       return r;
+}
+#else
+static int hdmi_audio_enable(struct omap_dss_device *dssdev)
+{
+       return -EPERM;
+}
+
+static void hdmi_audio_disable(struct omap_dss_device *dssdev)
+{
+}
+
+static int hdmi_audio_start(struct omap_dss_device *dssdev)
+{
+       return -EPERM;
+}
+
+static void hdmi_audio_stop(struct omap_dss_device *dssdev)
+{
+}
+
+static bool hdmi_audio_supported(struct omap_dss_device *dssdev)
+{
+       return false;
+}
+
+static int hdmi_audio_config(struct omap_dss_device *dssdev,
+               struct omap_dss_audio *audio)
+{
+       return -EPERM;
+}
+#endif
+
+static const struct omapdss_hdmi_ops hdmi_ops = {
+       .connect                = hdmi_connect,
+       .disconnect             = hdmi_disconnect,
+
+       .enable                 = hdmi_display_enable,
+       .disable                = hdmi_display_disable,
+
+       .check_timings          = hdmi_display_check_timing,
+       .set_timings            = hdmi_display_set_timing,
+       .get_timings            = hdmi_display_get_timings,
+
+       .read_edid              = hdmi_read_edid,
+
+       .audio_enable           = hdmi_audio_enable,
+       .audio_disable          = hdmi_audio_disable,
+       .audio_start            = hdmi_audio_start,
+       .audio_stop             = hdmi_audio_stop,
+       .audio_supported        = hdmi_audio_supported,
+       .audio_config           = hdmi_audio_config,
+};
+
+static void hdmi_init_output(struct platform_device *pdev)
+{
+       struct omap_dss_device *out = &hdmi.output;
+
+       out->dev = &pdev->dev;
+       out->id = OMAP_DSS_OUTPUT_HDMI;
+       out->output_type = OMAP_DISPLAY_TYPE_HDMI;
+       out->name = "hdmi.0";
+       out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
+       out->ops.hdmi = &hdmi_ops;
+       out->owner = THIS_MODULE;
+
+       omapdss_register_output(out);
+}
+
+static void __exit hdmi_uninit_output(struct platform_device *pdev)
+{
+       struct omap_dss_device *out = &hdmi.output;
+
+       omapdss_unregister_output(out);
+}
+
+static int hdmi_probe_of(struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       struct device_node *ep;
+       int r;
+
+       ep = omapdss_of_get_first_endpoint(node);
+       if (!ep)
+               return 0;
+
+       r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy);
+       if (r)
+               goto err;
+
+       of_node_put(ep);
+       return 0;
+
+err:
+       of_node_put(ep);
+       return r;
+}
+
+/* HDMI HW IP initialisation */
+static int omapdss_hdmihw_probe(struct platform_device *pdev)
+{
+       int r;
+       int irq;
+
+       hdmi.pdev = pdev;
+
+       mutex_init(&hdmi.lock);
+
+       if (pdev->dev.of_node) {
+               r = hdmi_probe_of(pdev);
+               if (r)
+                       return r;
+       }
+
+       r = hdmi_wp_init(pdev, &hdmi.wp);
+       if (r)
+               return r;
+
+       r = hdmi_pll_init(pdev, &hdmi.pll);
+       if (r)
+               return r;
+
+       r = hdmi_phy_init(pdev, &hdmi.phy);
+       if (r)
+               return r;
+
+       r = hdmi5_core_init(pdev, &hdmi.core);
+       if (r)
+               return r;
+
+       r = hdmi_get_clocks(pdev);
+       if (r) {
+               DSSERR("can't get clocks\n");
+               return r;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               DSSERR("platform_get_irq failed\n");
+               return -ENODEV;
+       }
+
+       r = devm_request_threaded_irq(&pdev->dev, irq,
+                       NULL, hdmi_irq_handler,
+                       IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp);
+       if (r) {
+               DSSERR("HDMI IRQ request failed\n");
+               return r;
+       }
+
+       pm_runtime_enable(&pdev->dev);
+
+       hdmi_init_output(pdev);
+
+       dss_debugfs_create_file("hdmi", hdmi_dump_regs);
+
+       return 0;
+}
+
+static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
+{
+       hdmi_uninit_output(pdev);
+
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static int hdmi_runtime_suspend(struct device *dev)
+{
+       clk_disable_unprepare(hdmi.sys_clk);
+
+       dispc_runtime_put();
+
+       return 0;
+}
+
+static int hdmi_runtime_resume(struct device *dev)
+{
+       int r;
+
+       r = dispc_runtime_get();
+       if (r < 0)
+               return r;
+
+       clk_prepare_enable(hdmi.sys_clk);
+
+       return 0;
+}
+
+static const struct dev_pm_ops hdmi_pm_ops = {
+       .runtime_suspend = hdmi_runtime_suspend,
+       .runtime_resume = hdmi_runtime_resume,
+};
+
+static const struct of_device_id hdmi_of_match[] = {
+       { .compatible = "ti,omap5-hdmi", },
+       {},
+};
+
+static struct platform_driver omapdss_hdmihw_driver = {
+       .probe          = omapdss_hdmihw_probe,
+       .remove         = __exit_p(omapdss_hdmihw_remove),
+       .driver         = {
+               .name   = "omapdss_hdmi5",
+               .owner  = THIS_MODULE,
+               .pm     = &hdmi_pm_ops,
+               .of_match_table = hdmi_of_match,
+       },
+};
+
+int __init hdmi5_init_platform_driver(void)
+{
+       return platform_driver_register(&omapdss_hdmihw_driver);
+}
+
+void __exit hdmi5_uninit_platform_driver(void)
+{
+       platform_driver_unregister(&omapdss_hdmihw_driver);
+}
diff --git a/drivers/video/fbdev/omap2/dss/hdmi5_core.c b/drivers/video/fbdev/omap2/dss/hdmi5_core.c
new file mode 100644 (file)
index 0000000..7528c7a
--- /dev/null
@@ -0,0 +1,922 @@
+/*
+ * OMAP5 HDMI CORE IP driver library
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated
+ *
+ * Authors:
+ *     Yong Zhi
+ *     Mythri pk
+ *     Archit Taneja <archit@ti.com>
+ *     Tomi Valkeinen <tomi.valkeinen@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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/seq_file.h>
+#include <drm/drm_edid.h>
+#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+#include <sound/asound.h>
+#include <sound/asoundef.h>
+#endif
+
+#include "hdmi5_core.h"
+
+/* only 24 bit color depth used for now */
+static const struct csc_table csc_table_deepcolor[] = {
+       /* HDMI_DEEP_COLOR_24BIT */
+       [0] = { 7036, 0, 0, 32, 0, 7036, 0, 32, 0, 0, 7036, 32, },
+       /* HDMI_DEEP_COLOR_30BIT */
+       [1] = { 7015, 0, 0, 128, 0, 7015, 0, 128, 0, 0, 7015, 128, },
+       /* HDMI_DEEP_COLOR_36BIT */
+       [2] = { 7010, 0, 0, 512, 0, 7010, 0, 512, 0, 0, 7010, 512, },
+       /* FULL RANGE */
+       [3] = { 8192, 0, 0, 0, 0, 8192, 0, 0, 0, 0, 8192, 0, },
+};
+
+static void hdmi_core_ddc_init(struct hdmi_core_data *core)
+{
+       void __iomem *base = core->base;
+       const unsigned long long iclk = 266000000;      /* DSS L3 ICLK */
+       const unsigned ss_scl_high = 4000;              /* ns */
+       const unsigned ss_scl_low = 4700;               /* ns */
+       const unsigned fs_scl_high = 600;               /* ns */
+       const unsigned fs_scl_low = 1300;               /* ns */
+       const unsigned sda_hold = 300;                  /* ns */
+       const unsigned sfr_div = 10;
+       unsigned long long sfr;
+       unsigned v;
+
+       sfr = iclk / sfr_div;   /* SFR_DIV */
+       sfr /= 1000;            /* SFR clock in kHz */
+
+       /* Reset */
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_SOFTRSTZ, 0, 0, 0);
+       if (hdmi_wait_for_bit_change(base, HDMI_CORE_I2CM_SOFTRSTZ,
+                               0, 0, 1) != 1)
+               DSSERR("HDMI I2CM reset failed\n");
+
+       /* Standard (0) or Fast (1) Mode */
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_DIV, 0, 3, 3);
+
+       /* Standard Mode SCL High counter */
+       v = DIV_ROUND_UP_ULL(ss_scl_high * sfr, 1000000);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR,
+                       (v >> 8) & 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR,
+                       v & 0xff, 7, 0);
+
+       /* Standard Mode SCL Low counter */
+       v = DIV_ROUND_UP_ULL(ss_scl_low * sfr, 1000000);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR,
+                       (v >> 8) & 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR,
+                       v & 0xff, 7, 0);
+
+       /* Fast Mode SCL High Counter */
+       v = DIV_ROUND_UP_ULL(fs_scl_high * sfr, 1000000);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR,
+                       (v >> 8) & 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR,
+                       v & 0xff, 7, 0);
+
+       /* Fast Mode SCL Low Counter */
+       v = DIV_ROUND_UP_ULL(fs_scl_low * sfr, 1000000);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR,
+                       (v >> 8) & 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR,
+                       v & 0xff, 7, 0);
+
+       /* SDA Hold Time */
+       v = DIV_ROUND_UP_ULL(sda_hold * sfr, 1000000);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_SDA_HOLD_ADDR, v & 0xff, 7, 0);
+
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_SLAVE, 0x50, 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_SEGADDR, 0x30, 6, 0);
+
+       /* NACK_POL to high */
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 7, 7);
+
+       /* NACK_MASK to unmasked */
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x0, 6, 6);
+
+       /* ARBITRATION_POL to high */
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 3, 3);
+
+       /* ARBITRATION_MASK to unmasked */
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x0, 2, 2);
+
+       /* DONE_POL to high */
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 3, 3);
+
+       /* DONE_MASK to unmasked */
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x0, 2, 2);
+}
+
+static void hdmi_core_ddc_uninit(struct hdmi_core_data *core)
+{
+       void __iomem *base = core->base;
+
+       /* Mask I2C interrupts */
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 6, 6);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 2, 2);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 2, 2);
+}
+
+static int hdmi_core_ddc_edid(struct hdmi_core_data *core, u8 *pedid, u8 ext)
+{
+       void __iomem *base = core->base;
+       u8 cur_addr;
+       char checksum = 0;
+       const int retries = 1000;
+       u8 seg_ptr = ext / 2;
+       u8 edidbase = ((ext % 2) * 0x80);
+
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_SEGPTR, seg_ptr, 7, 0);
+
+       /*
+        * TODO: We use polling here, although we probably should use proper
+        * interrupts.
+        */
+       for (cur_addr = 0; cur_addr < 128; ++cur_addr) {
+               int i;
+
+               /* clear ERROR and DONE */
+               REG_FLD_MOD(base, HDMI_CORE_IH_I2CM_STAT0, 0x3, 1, 0);
+
+               REG_FLD_MOD(base, HDMI_CORE_I2CM_ADDRESS,
+                               edidbase + cur_addr, 7, 0);
+
+               if (seg_ptr)
+                       REG_FLD_MOD(base, HDMI_CORE_I2CM_OPERATION, 1, 1, 1);
+               else
+                       REG_FLD_MOD(base, HDMI_CORE_I2CM_OPERATION, 1, 0, 0);
+
+               for (i = 0; i < retries; ++i) {
+                       u32 stat;
+
+                       stat = REG_GET(base, HDMI_CORE_IH_I2CM_STAT0, 1, 0);
+
+                       /* I2CM_ERROR */
+                       if (stat & 1) {
+                               DSSERR("HDMI I2C Master Error\n");
+                               return -EIO;
+                       }
+
+                       /* I2CM_DONE */
+                       if (stat & (1 << 1))
+                               break;
+
+                       usleep_range(250, 1000);
+               }
+
+               if (i == retries) {
+                       DSSERR("HDMI I2C timeout reading EDID\n");
+                       return -EIO;
+               }
+
+               pedid[cur_addr] = REG_GET(base, HDMI_CORE_I2CM_DATAI, 7, 0);
+               checksum += pedid[cur_addr];
+       }
+
+       return 0;
+
+}
+
+int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len)
+{
+       int r, n, i;
+       int max_ext_blocks = (len / 128) - 1;
+
+       if (len < 128)
+               return -EINVAL;
+
+       hdmi_core_ddc_init(core);
+
+       r = hdmi_core_ddc_edid(core, edid, 0);
+       if (r)
+               goto out;
+
+       n = edid[0x7e];
+
+       if (n > max_ext_blocks)
+               n = max_ext_blocks;
+
+       for (i = 1; i <= n; i++) {
+               r = hdmi_core_ddc_edid(core, edid + i * EDID_LENGTH, i);
+               if (r)
+                       goto out;
+       }
+
+out:
+       hdmi_core_ddc_uninit(core);
+
+       return r ? r : len;
+}
+
+void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s)
+{
+
+#define DUMPCORE(r) seq_printf(s, "%-35s %08x\n", #r,\
+               hdmi_read_reg(core->base, r))
+
+       DUMPCORE(HDMI_CORE_FC_INVIDCONF);
+       DUMPCORE(HDMI_CORE_FC_INHACTIV0);
+       DUMPCORE(HDMI_CORE_FC_INHACTIV1);
+       DUMPCORE(HDMI_CORE_FC_INHBLANK0);
+       DUMPCORE(HDMI_CORE_FC_INHBLANK1);
+       DUMPCORE(HDMI_CORE_FC_INVACTIV0);
+       DUMPCORE(HDMI_CORE_FC_INVACTIV1);
+       DUMPCORE(HDMI_CORE_FC_INVBLANK);
+       DUMPCORE(HDMI_CORE_FC_HSYNCINDELAY0);
+       DUMPCORE(HDMI_CORE_FC_HSYNCINDELAY1);
+       DUMPCORE(HDMI_CORE_FC_HSYNCINWIDTH0);
+       DUMPCORE(HDMI_CORE_FC_HSYNCINWIDTH1);
+       DUMPCORE(HDMI_CORE_FC_VSYNCINDELAY);
+       DUMPCORE(HDMI_CORE_FC_VSYNCINWIDTH);
+       DUMPCORE(HDMI_CORE_FC_CTRLDUR);
+       DUMPCORE(HDMI_CORE_FC_EXCTRLDUR);
+       DUMPCORE(HDMI_CORE_FC_EXCTRLSPAC);
+       DUMPCORE(HDMI_CORE_FC_CH0PREAM);
+       DUMPCORE(HDMI_CORE_FC_CH1PREAM);
+       DUMPCORE(HDMI_CORE_FC_CH2PREAM);
+       DUMPCORE(HDMI_CORE_FC_AVICONF0);
+       DUMPCORE(HDMI_CORE_FC_AVICONF1);
+       DUMPCORE(HDMI_CORE_FC_AVICONF2);
+       DUMPCORE(HDMI_CORE_FC_AVIVID);
+       DUMPCORE(HDMI_CORE_FC_PRCONF);
+
+       DUMPCORE(HDMI_CORE_MC_CLKDIS);
+       DUMPCORE(HDMI_CORE_MC_SWRSTZREQ);
+       DUMPCORE(HDMI_CORE_MC_FLOWCTRL);
+       DUMPCORE(HDMI_CORE_MC_PHYRSTZ);
+       DUMPCORE(HDMI_CORE_MC_LOCKONCLOCK);
+
+       DUMPCORE(HDMI_CORE_I2CM_SLAVE);
+       DUMPCORE(HDMI_CORE_I2CM_ADDRESS);
+       DUMPCORE(HDMI_CORE_I2CM_DATAO);
+       DUMPCORE(HDMI_CORE_I2CM_DATAI);
+       DUMPCORE(HDMI_CORE_I2CM_OPERATION);
+       DUMPCORE(HDMI_CORE_I2CM_INT);
+       DUMPCORE(HDMI_CORE_I2CM_CTLINT);
+       DUMPCORE(HDMI_CORE_I2CM_DIV);
+       DUMPCORE(HDMI_CORE_I2CM_SEGADDR);
+       DUMPCORE(HDMI_CORE_I2CM_SOFTRSTZ);
+       DUMPCORE(HDMI_CORE_I2CM_SEGPTR);
+       DUMPCORE(HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR);
+       DUMPCORE(HDMI_CORE_I2CM_SDA_HOLD_ADDR);
+}
+
+static void hdmi_core_init(struct hdmi_core_vid_config *video_cfg,
+                       struct hdmi_core_infoframe_avi *avi_cfg,
+                       struct hdmi_config *cfg)
+{
+       DSSDBG("hdmi_core_init\n");
+
+       /* video core */
+       video_cfg->data_enable_pol = 1; /* It is always 1*/
+       video_cfg->v_fc_config.timings.hsync_level = cfg->timings.hsync_level;
+       video_cfg->v_fc_config.timings.x_res = cfg->timings.x_res;
+       video_cfg->v_fc_config.timings.hsw = cfg->timings.hsw - 1;
+       video_cfg->v_fc_config.timings.hbp = cfg->timings.hbp;
+       video_cfg->v_fc_config.timings.hfp = cfg->timings.hfp;
+       video_cfg->hblank = cfg->timings.hfp +
+                               cfg->timings.hbp + cfg->timings.hsw - 1;
+       video_cfg->v_fc_config.timings.vsync_level = cfg->timings.vsync_level;
+       video_cfg->v_fc_config.timings.y_res = cfg->timings.y_res;
+       video_cfg->v_fc_config.timings.vsw = cfg->timings.vsw;
+       video_cfg->v_fc_config.timings.vfp = cfg->timings.vfp;
+       video_cfg->v_fc_config.timings.vbp = cfg->timings.vbp;
+       video_cfg->vblank_osc = 0; /* Always 0 - need to confirm */
+       video_cfg->vblank = cfg->timings.vsw +
+                               cfg->timings.vfp + cfg->timings.vbp;
+       video_cfg->v_fc_config.cm.mode = cfg->cm.mode;
+       video_cfg->v_fc_config.timings.interlace = cfg->timings.interlace;
+
+       /* info frame */
+       avi_cfg->db1_format = 0;
+       avi_cfg->db1_active_info = 0;
+       avi_cfg->db1_bar_info_dv = 0;
+       avi_cfg->db1_scan_info = 0;
+       avi_cfg->db2_colorimetry = 0;
+       avi_cfg->db2_aspect_ratio = 0;
+       avi_cfg->db2_active_fmt_ar = 0;
+       avi_cfg->db3_itc = 0;
+       avi_cfg->db3_ec = 0;
+       avi_cfg->db3_q_range = 0;
+       avi_cfg->db3_nup_scaling = 0;
+       avi_cfg->db4_videocode = 0;
+       avi_cfg->db5_pixel_repeat = 0;
+       avi_cfg->db6_7_line_eoftop = 0;
+       avi_cfg->db8_9_line_sofbottom = 0;
+       avi_cfg->db10_11_pixel_eofleft = 0;
+       avi_cfg->db12_13_pixel_sofright = 0;
+}
+
+/* DSS_HDMI_CORE_VIDEO_CONFIG */
+static void hdmi_core_video_config(struct hdmi_core_data *core,
+                       struct hdmi_core_vid_config *cfg)
+{
+       void __iomem *base = core->base;
+       unsigned char r = 0;
+       bool vsync_pol, hsync_pol;
+
+       vsync_pol =
+               cfg->v_fc_config.timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
+       hsync_pol =
+               cfg->v_fc_config.timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
+
+       /* Set hsync, vsync and data-enable polarity  */
+       r = hdmi_read_reg(base, HDMI_CORE_FC_INVIDCONF);
+       r = FLD_MOD(r, vsync_pol, 6, 6);
+       r = FLD_MOD(r, hsync_pol, 5, 5);
+       r = FLD_MOD(r, cfg->data_enable_pol, 4, 4);
+       r = FLD_MOD(r, cfg->vblank_osc, 1, 1);
+       r = FLD_MOD(r, cfg->v_fc_config.timings.interlace, 0, 0);
+       hdmi_write_reg(base, HDMI_CORE_FC_INVIDCONF, r);
+
+       /* set x resolution */
+       REG_FLD_MOD(base, HDMI_CORE_FC_INHACTIV1,
+                       cfg->v_fc_config.timings.x_res >> 8, 4, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_INHACTIV0,
+                       cfg->v_fc_config.timings.x_res & 0xFF, 7, 0);
+
+       /* set y resolution */
+       REG_FLD_MOD(base, HDMI_CORE_FC_INVACTIV1,
+                       cfg->v_fc_config.timings.y_res >> 8, 4, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_INVACTIV0,
+                       cfg->v_fc_config.timings.y_res & 0xFF, 7, 0);
+
+       /* set horizontal blanking pixels */
+       REG_FLD_MOD(base, HDMI_CORE_FC_INHBLANK1, cfg->hblank >> 8, 4, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_INHBLANK0, cfg->hblank & 0xFF, 7, 0);
+
+       /* set vertial blanking pixels */
+       REG_FLD_MOD(base, HDMI_CORE_FC_INVBLANK, cfg->vblank, 7, 0);
+
+       /* set horizontal sync offset */
+       REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINDELAY1,
+                       cfg->v_fc_config.timings.hfp >> 8, 4, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINDELAY0,
+                       cfg->v_fc_config.timings.hfp & 0xFF, 7, 0);
+
+       /* set vertical sync offset */
+       REG_FLD_MOD(base, HDMI_CORE_FC_VSYNCINDELAY,
+                       cfg->v_fc_config.timings.vfp, 7, 0);
+
+       /* set horizontal sync pulse width */
+       REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINWIDTH1,
+                       (cfg->v_fc_config.timings.hsw >> 8), 1, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINWIDTH0,
+                       cfg->v_fc_config.timings.hsw & 0xFF, 7, 0);
+
+       /*  set vertical sync pulse width */
+       REG_FLD_MOD(base, HDMI_CORE_FC_VSYNCINWIDTH,
+                       cfg->v_fc_config.timings.vsw, 5, 0);
+
+       /* select DVI mode */
+       REG_FLD_MOD(base, HDMI_CORE_FC_INVIDCONF,
+                       cfg->v_fc_config.cm.mode, 3, 3);
+}
+
+static void hdmi_core_config_video_packetizer(struct hdmi_core_data *core)
+{
+       void __iomem *base = core->base;
+       int clr_depth = 0;      /* 24 bit color depth */
+
+       /* COLOR_DEPTH */
+       REG_FLD_MOD(base, HDMI_CORE_VP_PR_CD, clr_depth, 7, 4);
+       /* BYPASS_EN */
+       REG_FLD_MOD(base, HDMI_CORE_VP_CONF, clr_depth ? 0 : 1, 6, 6);
+       /* PP_EN */
+       REG_FLD_MOD(base, HDMI_CORE_VP_CONF, clr_depth ? 1 : 0, 5, 5);
+       /* YCC422_EN */
+       REG_FLD_MOD(base, HDMI_CORE_VP_CONF, 0, 3, 3);
+       /* PP_STUFFING */
+       REG_FLD_MOD(base, HDMI_CORE_VP_STUFF, clr_depth ? 1 : 0, 1, 1);
+       /* YCC422_STUFFING */
+       REG_FLD_MOD(base, HDMI_CORE_VP_STUFF, 1, 2, 2);
+       /* OUTPUT_SELECTOR */
+       REG_FLD_MOD(base, HDMI_CORE_VP_CONF, clr_depth ? 0 : 2, 1, 0);
+}
+
+static void hdmi_core_config_csc(struct hdmi_core_data *core)
+{
+       int clr_depth = 0;      /* 24 bit color depth */
+
+       /* CSC_COLORDEPTH */
+       REG_FLD_MOD(core->base, HDMI_CORE_CSC_SCALE, clr_depth, 7, 4);
+}
+
+static void hdmi_core_config_video_sampler(struct hdmi_core_data *core)
+{
+       int video_mapping = 1;  /* for 24 bit color depth */
+
+       /* VIDEO_MAPPING */
+       REG_FLD_MOD(core->base, HDMI_CORE_TX_INVID0, video_mapping, 4, 0);
+}
+
+static void hdmi_core_aux_infoframe_avi_config(struct hdmi_core_data *core)
+{
+       void __iomem *base = core->base;
+       struct hdmi_core_infoframe_avi avi = core->avi_cfg;
+
+       REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF0, avi.db1_format, 1, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF0, avi.db1_active_info, 6, 6);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF0, avi.db1_bar_info_dv, 3, 2);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF0, avi.db1_scan_info, 5, 4);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF1, avi.db2_colorimetry, 7, 6);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF1, avi.db2_aspect_ratio, 5, 4);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF1, avi.db2_active_fmt_ar, 3, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF2, avi.db3_itc, 7, 7);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF2, avi.db3_ec, 6, 4);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF2, avi.db3_q_range, 3, 2);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AVICONF2, avi.db3_nup_scaling, 1, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AVIVID, avi.db4_videocode, 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_PRCONF, avi.db5_pixel_repeat, 3, 0);
+}
+
+static void hdmi_core_csc_config(struct hdmi_core_data *core,
+               struct csc_table csc_coeff)
+{
+       void __iomem *base = core->base;
+
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A1_MSB, csc_coeff.a1 >> 8 , 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A1_LSB, csc_coeff.a1, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A2_MSB, csc_coeff.a2 >> 8, 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A2_LSB, csc_coeff.a2, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A3_MSB, csc_coeff.a3 >> 8, 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A3_LSB, csc_coeff.a3, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A4_MSB, csc_coeff.a4 >> 8, 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A4_LSB, csc_coeff.a4, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B1_MSB, csc_coeff.b1 >> 8, 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B1_LSB, csc_coeff.b1, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B2_MSB, csc_coeff.b2 >> 8, 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B2_LSB, csc_coeff.b2, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B3_MSB, csc_coeff.b3 >> 8, 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B3_LSB, csc_coeff.b3, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B4_MSB, csc_coeff.b4 >> 8, 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B4_LSB, csc_coeff.b4, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C1_MSB, csc_coeff.c1 >> 8, 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C1_LSB, csc_coeff.c1, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C2_MSB, csc_coeff.c2 >> 8, 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C2_LSB, csc_coeff.c2, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C3_MSB, csc_coeff.c3 >> 8, 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C3_LSB, csc_coeff.c3, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C4_MSB, csc_coeff.c4 >> 8, 6, 0);
+       REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C4_LSB, csc_coeff.c4, 7, 0);
+
+       REG_FLD_MOD(base, HDMI_CORE_MC_FLOWCTRL, 0x1, 0, 0);
+}
+
+static void hdmi_core_configure_range(struct hdmi_core_data *core)
+{
+       struct csc_table csc_coeff = { 0 };
+
+       /* support limited range with 24 bit color depth for now */
+       csc_coeff = csc_table_deepcolor[0];
+       core->avi_cfg.db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_LR;
+
+       hdmi_core_csc_config(core, csc_coeff);
+       hdmi_core_aux_infoframe_avi_config(core);
+}
+
+static void hdmi_core_enable_video_path(struct hdmi_core_data *core)
+{
+       void __iomem *base = core->base;
+
+       DSSDBG("hdmi_core_enable_video_path\n");
+
+       REG_FLD_MOD(base, HDMI_CORE_FC_CTRLDUR, 0x0C, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_EXCTRLDUR, 0x20, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_EXCTRLSPAC, 0x01, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_CH0PREAM, 0x0B, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_CH1PREAM, 0x16, 5, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_CH2PREAM, 0x21, 5, 0);
+       REG_FLD_MOD(base, HDMI_CORE_MC_CLKDIS, 0x00, 0, 0);
+       REG_FLD_MOD(base, HDMI_CORE_MC_CLKDIS, 0x00, 1, 1);
+}
+
+static void hdmi_core_mask_interrupts(struct hdmi_core_data *core)
+{
+       void __iomem *base = core->base;
+
+       /* Master IRQ mask */
+       REG_FLD_MOD(base, HDMI_CORE_IH_MUTE, 0x3, 1, 0);
+
+       /* Mask all the interrupts in HDMI core */
+
+       REG_FLD_MOD(base, HDMI_CORE_VP_MASK, 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_MASK0, 0xe7, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_MASK1, 0xfb, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_MASK2, 0x3, 1, 0);
+
+       REG_FLD_MOD(base, HDMI_CORE_AUD_INT, 0x3, 3, 2);
+       REG_FLD_MOD(base, HDMI_CORE_AUD_GP_MASK, 0x3, 1, 0);
+
+       REG_FLD_MOD(base, HDMI_CORE_CEC_MASK, 0x7f, 6, 0);
+
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 6, 6);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 2, 2);
+       REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 2, 2);
+
+       REG_FLD_MOD(base, HDMI_CORE_PHY_MASK0, 0xf3, 7, 0);
+
+       REG_FLD_MOD(base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0);
+
+       /* Clear all the current interrupt bits */
+
+       REG_FLD_MOD(base, HDMI_CORE_IH_VP_STAT0, 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT0, 0xe7, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT1, 0xfb, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT2, 0x3, 1, 0);
+
+       REG_FLD_MOD(base, HDMI_CORE_IH_AS_STAT0, 0x7, 2, 0);
+
+       REG_FLD_MOD(base, HDMI_CORE_IH_CEC_STAT0, 0x7f, 6, 0);
+
+       REG_FLD_MOD(base, HDMI_CORE_IH_I2CM_STAT0, 0x3, 1, 0);
+
+       REG_FLD_MOD(base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0);
+}
+
+static void hdmi_core_enable_interrupts(struct hdmi_core_data *core)
+{
+       /* Unmute interrupts */
+       REG_FLD_MOD(core->base, HDMI_CORE_IH_MUTE, 0x0, 1, 0);
+}
+
+int hdmi5_core_handle_irqs(struct hdmi_core_data *core)
+{
+       void __iomem *base = core->base;
+
+       REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT0, 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT1, 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT2, 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_IH_AS_STAT0, 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_IH_I2CM_STAT0, 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_IH_CEC_STAT0, 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_IH_VP_STAT0, 0xff, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_IH_I2CMPHY_STAT0, 0xff, 7, 0);
+
+       return 0;
+}
+
+void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
+               struct hdmi_config *cfg)
+{
+       struct omap_video_timings video_timing;
+       struct hdmi_video_format video_format;
+       struct hdmi_core_vid_config v_core_cfg;
+       struct hdmi_core_infoframe_avi *avi_cfg = &core->avi_cfg;
+
+       hdmi_core_mask_interrupts(core);
+
+       hdmi_core_init(&v_core_cfg, avi_cfg, cfg);
+
+       hdmi_wp_init_vid_fmt_timings(&video_format, &video_timing, cfg);
+
+       hdmi_wp_video_config_timing(wp, &video_timing);
+
+       /* video config */
+       video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422;
+
+       hdmi_wp_video_config_format(wp, &video_format);
+
+       hdmi_wp_video_config_interface(wp, &video_timing);
+
+       hdmi_core_configure_range(core);
+
+       /*
+        * configure core video part, set software reset in the core
+        */
+       v_core_cfg.packet_mode = HDMI_PACKETMODE24BITPERPIXEL;
+
+       hdmi_core_video_config(core, &v_core_cfg);
+
+       hdmi_core_config_video_packetizer(core);
+       hdmi_core_config_csc(core);
+       hdmi_core_config_video_sampler(core);
+
+       /*
+        * configure packet info frame video see doc CEA861-D page 65
+        */
+       avi_cfg->db1_format = HDMI_INFOFRAME_AVI_DB1Y_RGB;
+       avi_cfg->db1_active_info =
+                       HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF;
+       avi_cfg->db1_bar_info_dv = HDMI_INFOFRAME_AVI_DB1B_NO;
+       avi_cfg->db1_scan_info = HDMI_INFOFRAME_AVI_DB1S_0;
+       avi_cfg->db2_colorimetry = HDMI_INFOFRAME_AVI_DB2C_NO;
+       avi_cfg->db2_aspect_ratio = HDMI_INFOFRAME_AVI_DB2M_NO;
+       avi_cfg->db2_active_fmt_ar = HDMI_INFOFRAME_AVI_DB2R_SAME;
+       avi_cfg->db3_itc = HDMI_INFOFRAME_AVI_DB3ITC_NO;
+       avi_cfg->db3_ec = HDMI_INFOFRAME_AVI_DB3EC_XVYUV601;
+       avi_cfg->db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_DEFAULT;
+       avi_cfg->db3_nup_scaling = HDMI_INFOFRAME_AVI_DB3SC_NO;
+       avi_cfg->db4_videocode = cfg->cm.code;
+       avi_cfg->db5_pixel_repeat = HDMI_INFOFRAME_AVI_DB5PR_NO;
+       avi_cfg->db6_7_line_eoftop = 0;
+       avi_cfg->db8_9_line_sofbottom = 0;
+       avi_cfg->db10_11_pixel_eofleft = 0;
+       avi_cfg->db12_13_pixel_sofright = 0;
+
+       hdmi_core_aux_infoframe_avi_config(core);
+
+       hdmi_core_enable_video_path(core);
+
+       hdmi_core_enable_interrupts(core);
+}
+
+
+#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+
+static void hdmi5_core_audio_config(struct hdmi_core_data *core,
+                       struct hdmi_core_audio_config *cfg)
+{
+       void __iomem *base = core->base;
+       u8 val;
+
+       /* Mute audio before configuring */
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 0xf, 7, 4);
+
+       /* Set the N parameter */
+       REG_FLD_MOD(base, HDMI_CORE_AUD_N1, cfg->n, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_AUD_N2, cfg->n >> 8, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_AUD_N3, cfg->n >> 16, 3, 0);
+
+       /*
+        * CTS manual mode. Automatic mode is not supported when using audio
+        * parallel interface.
+        */
+       REG_FLD_MOD(base, HDMI_CORE_AUD_CTS3, 1, 4, 4);
+       REG_FLD_MOD(base, HDMI_CORE_AUD_CTS1, cfg->cts, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_AUD_CTS2, cfg->cts >> 8, 7, 0);
+       REG_FLD_MOD(base, HDMI_CORE_AUD_CTS3, cfg->cts >> 16, 3, 0);
+
+       /* Layout of Audio Sample Packets: 2-channel or multichannels */
+       if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH)
+               REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 0, 0, 0);
+       else
+               REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 1, 0, 0);
+
+       /* Configure IEC-609580 Validity bits */
+       /* Channel 0 is valid */
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, 0, 0, 0);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, 0, 4, 4);
+
+       if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH)
+               val = 1;
+       else
+               val = 0;
+
+       /* Channels 1, 2 setting */
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 1, 1);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 5, 5);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 2, 2);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 6, 6);
+       /* Channel 3 setting */
+       if (cfg->layout == HDMI_AUDIO_LAYOUT_6CH)
+               val = 1;
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 3, 3);
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 7, 7);
+
+       /* Configure IEC-60958 User bits */
+       /* TODO: should be set by user. */
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSU, 0, 7, 0);
+
+       /* Configure IEC-60958 Channel Status word */
+       /* CGMSA */
+       val = cfg->iec60958_cfg->status[5] & IEC958_AES5_CON_CGMSA;
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(0), val, 5, 4);
+
+       /* Copyright */
+       val = (cfg->iec60958_cfg->status[0] &
+                       IEC958_AES0_CON_NOT_COPYRIGHT) >> 2;
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(0), val, 0, 0);
+
+       /* Category */
+       hdmi_write_reg(base, HDMI_CORE_FC_AUDSCHNLS(1),
+               cfg->iec60958_cfg->status[1]);
+
+       /* PCM audio mode */
+       val = (cfg->iec60958_cfg->status[0] & IEC958_AES0_CON_MODE) >> 6;
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(2), val, 6, 4);
+
+       /* Source number */
+       val = cfg->iec60958_cfg->status[2] & IEC958_AES2_CON_SOURCE;
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(2), val, 3, 4);
+
+       /* Channel number right 0  */
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(3), 2, 3, 0);
+       /* Channel number right 1*/
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(3), 4, 7, 4);
+       /* Channel number right 2  */
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(4), 6, 3, 0);
+       /* Channel number right 3*/
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(4), 8, 7, 4);
+       /* Channel number left 0  */
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(5), 1, 3, 0);
+       /* Channel number left 1*/
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(5), 3, 7, 4);
+       /* Channel number left 2  */
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(6), 5, 3, 0);
+       /* Channel number left 3*/
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(6), 7, 7, 4);
+
+       /* Clock accuracy and sample rate */
+       hdmi_write_reg(base, HDMI_CORE_FC_AUDSCHNLS(7),
+               cfg->iec60958_cfg->status[3]);
+
+       /* Original sample rate and word length */
+       hdmi_write_reg(base, HDMI_CORE_FC_AUDSCHNLS(8),
+               cfg->iec60958_cfg->status[4]);
+
+       /* Enable FIFO empty and full interrupts */
+       REG_FLD_MOD(base, HDMI_CORE_AUD_INT, 3, 3, 2);
+
+       /* Configure GPA */
+       /* select HBR/SPDIF interfaces */
+       if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH) {
+               /* select HBR/SPDIF interfaces */
+               REG_FLD_MOD(base, HDMI_CORE_AUD_CONF0, 0, 5, 5);
+               /* enable two channels in GPA */
+               REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF1, 3, 7, 0);
+       } else if (cfg->layout == HDMI_AUDIO_LAYOUT_6CH) {
+               /* select HBR/SPDIF interfaces */
+               REG_FLD_MOD(base, HDMI_CORE_AUD_CONF0, 0, 5, 5);
+               /* enable six channels in GPA */
+               REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF1, 0x3F, 7, 0);
+       } else {
+               /* select HBR/SPDIF interfaces */
+               REG_FLD_MOD(base, HDMI_CORE_AUD_CONF0, 0, 5, 5);
+               /* enable eight channels in GPA */
+               REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF1, 0xFF, 7, 0);
+       }
+
+       /* disable HBR */
+       REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF2, 0, 0, 0);
+       /* enable PCUV */
+       REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF2, 1, 1, 1);
+       /* enable GPA FIFO full and empty mask */
+       REG_FLD_MOD(base, HDMI_CORE_AUD_GP_MASK, 3, 1, 0);
+       /* set polarity of GPA FIFO empty interrupts */
+       REG_FLD_MOD(base, HDMI_CORE_AUD_GP_POL, 1, 0, 0);
+
+       /* unmute audio */
+       REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 0, 7, 4);
+}
+
+static void hdmi5_core_audio_infoframe_cfg(struct hdmi_core_data *core,
+        struct snd_cea_861_aud_if *info_aud)
+{
+       void __iomem *base = core->base;
+
+       /* channel count and coding type fields in AUDICONF0 are swapped */
+       hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF0,
+               (info_aud->db1_ct_cc & CEA861_AUDIO_INFOFRAME_DB1CC) << 4 |
+               (info_aud->db1_ct_cc & CEA861_AUDIO_INFOFRAME_DB1CT) >> 4);
+
+       hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF1, info_aud->db2_sf_ss);
+       hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF2, info_aud->db4_ca);
+       hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF3, info_aud->db5_dminh_lsv);
+}
+
+int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
+                       struct omap_dss_audio *audio, u32 pclk)
+{
+       struct hdmi_audio_format audio_format;
+       struct hdmi_audio_dma audio_dma;
+       struct hdmi_core_audio_config core_cfg;
+       int err, n, cts, channel_count;
+       unsigned int fs_nr;
+       bool word_length_16b = false;
+
+       if (!audio || !audio->iec || !audio->cea || !core)
+               return -EINVAL;
+
+       core_cfg.iec60958_cfg = audio->iec;
+
+       if (!(audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24) &&
+               (audio->iec->status[4] & IEC958_AES4_CON_WORDLEN_20_16))
+                       word_length_16b = true;
+
+       /* only 16-bit word length supported atm */
+       if (!word_length_16b)
+               return -EINVAL;
+
+       switch (audio->iec->status[3] & IEC958_AES3_CON_FS) {
+       case IEC958_AES3_CON_FS_32000:
+               fs_nr = 32000;
+               break;
+       case IEC958_AES3_CON_FS_44100:
+               fs_nr = 44100;
+               break;
+       case IEC958_AES3_CON_FS_48000:
+               fs_nr = 48000;
+               break;
+       case IEC958_AES3_CON_FS_88200:
+               fs_nr = 88200;
+               break;
+       case IEC958_AES3_CON_FS_96000:
+               fs_nr = 96000;
+               break;
+       case IEC958_AES3_CON_FS_176400:
+               fs_nr = 176400;
+               break;
+       case IEC958_AES3_CON_FS_192000:
+               fs_nr = 192000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       err = hdmi_compute_acr(pclk, fs_nr, &n, &cts);
+       core_cfg.n = n;
+       core_cfg.cts = cts;
+
+       /* Audio channels settings */
+       channel_count = (audio->cea->db1_ct_cc & CEA861_AUDIO_INFOFRAME_DB1CC)
+                               + 1;
+
+       if (channel_count == 2)
+               core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH;
+       else if (channel_count == 6)
+               core_cfg.layout = HDMI_AUDIO_LAYOUT_6CH;
+       else
+               core_cfg.layout = HDMI_AUDIO_LAYOUT_8CH;
+
+       /* DMA settings */
+       if (word_length_16b)
+               audio_dma.transfer_size = 0x10;
+       else
+               audio_dma.transfer_size = 0x20;
+       audio_dma.block_size = 0xC0;
+       audio_dma.mode = HDMI_AUDIO_TRANSF_DMA;
+       audio_dma.fifo_threshold = 0x20; /* in number of samples */
+
+       /* audio FIFO format settings for 16-bit samples*/
+       audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES;
+       audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS;
+       audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT;
+
+       /* only LPCM atm */
+       audio_format.type = HDMI_AUDIO_TYPE_LPCM;
+
+       /* disable start/stop signals of IEC 60958 blocks */
+       audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON;
+
+       /* configure DMA and audio FIFO format*/
+       hdmi_wp_audio_config_dma(wp, &audio_dma);
+       hdmi_wp_audio_config_format(wp, &audio_format);
+
+       /* configure the core */
+       hdmi5_core_audio_config(core, &core_cfg);
+
+       /* configure CEA 861 audio infoframe */
+       hdmi5_core_audio_infoframe_cfg(core, audio->cea);
+
+       return 0;
+}
+#endif
+
+int hdmi5_core_init(struct platform_device *pdev, struct hdmi_core_data *core)
+{
+       struct resource *res;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
+       if (!res) {
+               DSSERR("can't get CORE IORESOURCE_MEM HDMI\n");
+               return -EINVAL;
+       }
+
+       core->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(core->base)) {
+               DSSERR("can't ioremap HDMI core\n");
+               return PTR_ERR(core->base);
+       }
+
+       return 0;
+}
diff --git a/drivers/video/fbdev/omap2/dss/hdmi5_core.h b/drivers/video/fbdev/omap2/dss/hdmi5_core.h
new file mode 100644 (file)
index 0000000..ce7e9f3
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * HDMI driver definition for TI OMAP5 processors.
+ *
+ * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _HDMI5_CORE_H_
+#define _HDMI5_CORE_H_
+
+#include "hdmi.h"
+
+/* HDMI IP Core System */
+
+/* HDMI Identification */
+#define HDMI_CORE_DESIGN_ID                    0x00000
+#define HDMI_CORE_REVISION_ID                  0x00004
+#define HDMI_CORE_PRODUCT_ID0                  0x00008
+#define HDMI_CORE_PRODUCT_ID1                  0x0000C
+#define HDMI_CORE_CONFIG0_ID                   0x00010
+#define HDMI_CORE_CONFIG1_ID                   0x00014
+#define HDMI_CORE_CONFIG2_ID                   0x00018
+#define HDMI_CORE_CONFIG3_ID                   0x0001C
+
+/* HDMI Interrupt */
+#define HDMI_CORE_IH_FC_STAT0                  0x00400
+#define HDMI_CORE_IH_FC_STAT1                  0x00404
+#define HDMI_CORE_IH_FC_STAT2                  0x00408
+#define HDMI_CORE_IH_AS_STAT0                  0x0040C
+#define HDMI_CORE_IH_PHY_STAT0                 0x00410
+#define HDMI_CORE_IH_I2CM_STAT0                        0x00414
+#define HDMI_CORE_IH_CEC_STAT0                 0x00418
+#define HDMI_CORE_IH_VP_STAT0                  0x0041C
+#define HDMI_CORE_IH_I2CMPHY_STAT0             0x00420
+#define HDMI_CORE_IH_MUTE                      0x007FC
+
+/* HDMI Video Sampler */
+#define HDMI_CORE_TX_INVID0                    0x00800
+#define HDMI_CORE_TX_INSTUFFING                        0x00804
+#define HDMI_CORE_TX_RGYDATA0                  0x00808
+#define HDMI_CORE_TX_RGYDATA1                  0x0080C
+#define HDMI_CORE_TX_RCRDATA0                  0x00810
+#define HDMI_CORE_TX_RCRDATA1                  0x00814
+#define HDMI_CORE_TX_BCBDATA0                  0x00818
+#define HDMI_CORE_TX_BCBDATA1                  0x0081C
+
+/* HDMI Video Packetizer */
+#define HDMI_CORE_VP_STATUS                    0x02000
+#define HDMI_CORE_VP_PR_CD                     0x02004
+#define HDMI_CORE_VP_STUFF                     0x02008
+#define HDMI_CORE_VP_REMAP                     0x0200C
+#define HDMI_CORE_VP_CONF                      0x02010
+#define HDMI_CORE_VP_STAT                      0x02014
+#define HDMI_CORE_VP_INT                       0x02018
+#define HDMI_CORE_VP_MASK                      0x0201C
+#define HDMI_CORE_VP_POL                       0x02020
+
+/* Frame Composer */
+#define HDMI_CORE_FC_INVIDCONF                 0x04000
+#define HDMI_CORE_FC_INHACTIV0                 0x04004
+#define HDMI_CORE_FC_INHACTIV1                 0x04008
+#define HDMI_CORE_FC_INHBLANK0                 0x0400C
+#define HDMI_CORE_FC_INHBLANK1                 0x04010
+#define HDMI_CORE_FC_INVACTIV0                 0x04014
+#define HDMI_CORE_FC_INVACTIV1                 0x04018
+#define HDMI_CORE_FC_INVBLANK                  0x0401C
+#define HDMI_CORE_FC_HSYNCINDELAY0             0x04020
+#define HDMI_CORE_FC_HSYNCINDELAY1             0x04024
+#define HDMI_CORE_FC_HSYNCINWIDTH0             0x04028
+#define HDMI_CORE_FC_HSYNCINWIDTH1             0x0402C
+#define HDMI_CORE_FC_VSYNCINDELAY              0x04030
+#define HDMI_CORE_FC_VSYNCINWIDTH              0x04034
+#define HDMI_CORE_FC_INFREQ0                   0x04038
+#define HDMI_CORE_FC_INFREQ1                   0x0403C
+#define HDMI_CORE_FC_INFREQ2                   0x04040
+#define HDMI_CORE_FC_CTRLDUR                   0x04044
+#define HDMI_CORE_FC_EXCTRLDUR                 0x04048
+#define HDMI_CORE_FC_EXCTRLSPAC                        0x0404C
+#define HDMI_CORE_FC_CH0PREAM                  0x04050
+#define HDMI_CORE_FC_CH1PREAM                  0x04054
+#define HDMI_CORE_FC_CH2PREAM                  0x04058
+#define HDMI_CORE_FC_AVICONF3                  0x0405C
+#define HDMI_CORE_FC_GCP                       0x04060
+#define HDMI_CORE_FC_AVICONF0                  0x04064
+#define HDMI_CORE_FC_AVICONF1                  0x04068
+#define HDMI_CORE_FC_AVICONF2                  0x0406C
+#define HDMI_CORE_FC_AVIVID                    0x04070
+#define HDMI_CORE_FC_AVIETB0                   0x04074
+#define HDMI_CORE_FC_AVIETB1                   0x04078
+#define HDMI_CORE_FC_AVISBB0                   0x0407C
+#define HDMI_CORE_FC_AVISBB1                   0x04080
+#define HDMI_CORE_FC_AVIELB0                   0x04084
+#define HDMI_CORE_FC_AVIELB1                   0x04088
+#define HDMI_CORE_FC_AVISRB0                   0x0408C
+#define HDMI_CORE_FC_AVISRB1                   0x04090
+#define HDMI_CORE_FC_AUDICONF0                 0x04094
+#define HDMI_CORE_FC_AUDICONF1                 0x04098
+#define HDMI_CORE_FC_AUDICONF2                 0x0409C
+#define HDMI_CORE_FC_AUDICONF3                 0x040A0
+#define HDMI_CORE_FC_VSDIEEEID0                        0x040A4
+#define HDMI_CORE_FC_VSDSIZE                   0x040A8
+#define HDMI_CORE_FC_VSDIEEEID1                        0x040C0
+#define HDMI_CORE_FC_VSDIEEEID2                        0x040C4
+#define HDMI_CORE_FC_VSDPAYLOAD(n)             (n * 4 + 0x040C8)
+#define HDMI_CORE_FC_SPDVENDORNAME(n)          (n * 4 + 0x04128)
+#define HDMI_CORE_FC_SPDPRODUCTNAME(n)         (n * 4 + 0x04148)
+#define HDMI_CORE_FC_SPDDEVICEINF              0x04188
+#define HDMI_CORE_FC_AUDSCONF                  0x0418C
+#define HDMI_CORE_FC_AUDSSTAT                  0x04190
+#define HDMI_CORE_FC_AUDSV                     0x04194
+#define HDMI_CORE_FC_AUDSU                     0x04198
+#define HDMI_CORE_FC_AUDSCHNLS(n)              (n * 4 + 0x0419C)
+#define HDMI_CORE_FC_CTRLQHIGH                 0x041CC
+#define HDMI_CORE_FC_CTRLQLOW                  0x041D0
+#define HDMI_CORE_FC_ACP0                      0x041D4
+#define HDMI_CORE_FC_ACP(n)                    ((16-n) * 4 + 0x04208)
+#define HDMI_CORE_FC_ISCR1_0                   0x04248
+#define HDMI_CORE_FC_ISCR1(n)                  ((16-n) * 4 + 0x0424C)
+#define HDMI_CORE_FC_ISCR2(n)                  ((15-n) * 4 + 0x0428C)
+#define HDMI_CORE_FC_DATAUTO0                  0x042CC
+#define HDMI_CORE_FC_DATAUTO1                  0x042D0
+#define HDMI_CORE_FC_DATAUTO2                  0x042D4
+#define HDMI_CORE_FC_DATMAN                    0x042D8
+#define HDMI_CORE_FC_DATAUTO3                  0x042DC
+#define HDMI_CORE_FC_RDRB(n)                   (n * 4 + 0x042E0)
+#define HDMI_CORE_FC_STAT0                     0x04340
+#define HDMI_CORE_FC_INT0                      0x04344
+#define HDMI_CORE_FC_MASK0                     0x04348
+#define HDMI_CORE_FC_POL0                      0x0434C
+#define HDMI_CORE_FC_STAT1                     0x04350
+#define HDMI_CORE_FC_INT1                      0x04354
+#define HDMI_CORE_FC_MASK1                     0x04358
+#define HDMI_CORE_FC_POL1                      0x0435C
+#define HDMI_CORE_FC_STAT2                     0x04360
+#define HDMI_CORE_FC_INT2                      0x04364
+#define HDMI_CORE_FC_MASK2                     0x04368
+#define HDMI_CORE_FC_POL2                      0x0436C
+#define HDMI_CORE_FC_PRCONF                    0x04380
+#define HDMI_CORE_FC_GMD_STAT                  0x04400
+#define HDMI_CORE_FC_GMD_EN                    0x04404
+#define HDMI_CORE_FC_GMD_UP                    0x04408
+#define HDMI_CORE_FC_GMD_CONF                  0x0440C
+#define HDMI_CORE_FC_GMD_HB                    0x04410
+#define HDMI_CORE_FC_GMD_PB(n)                 (n * 4 + 0x04414)
+#define HDMI_CORE_FC_DBGFORCE                  0x04800
+#define HDMI_CORE_FC_DBGAUD0CH0                        0x04804
+#define HDMI_CORE_FC_DBGAUD1CH0                        0x04808
+#define HDMI_CORE_FC_DBGAUD2CH0                        0x0480C
+#define HDMI_CORE_FC_DBGAUD0CH1                        0x04810
+#define HDMI_CORE_FC_DBGAUD1CH1                        0x04814
+#define HDMI_CORE_FC_DBGAUD2CH1                        0x04818
+#define HDMI_CORE_FC_DBGAUD0CH2                        0x0481C
+#define HDMI_CORE_FC_DBGAUD1CH2                        0x04820
+#define HDMI_CORE_FC_DBGAUD2CH2                        0x04824
+#define HDMI_CORE_FC_DBGAUD0CH3                        0x04828
+#define HDMI_CORE_FC_DBGAUD1CH3                        0x0482C
+#define HDMI_CORE_FC_DBGAUD2CH3                        0x04830
+#define HDMI_CORE_FC_DBGAUD0CH4                        0x04834
+#define HDMI_CORE_FC_DBGAUD1CH4                        0x04838
+#define HDMI_CORE_FC_DBGAUD2CH4                        0x0483C
+#define HDMI_CORE_FC_DBGAUD0CH5                        0x04840
+#define HDMI_CORE_FC_DBGAUD1CH5                        0x04844
+#define HDMI_CORE_FC_DBGAUD2CH5                        0x04848
+#define HDMI_CORE_FC_DBGAUD0CH6                        0x0484C
+#define HDMI_CORE_FC_DBGAUD1CH6                        0x04850
+#define HDMI_CORE_FC_DBGAUD2CH6                        0x04854
+#define HDMI_CORE_FC_DBGAUD0CH7                        0x04858
+#define HDMI_CORE_FC_DBGAUD1CH7                        0x0485C
+#define HDMI_CORE_FC_DBGAUD2CH7                        0x04860
+#define HDMI_CORE_FC_DBGTMDS0                  0x04864
+#define HDMI_CORE_FC_DBGTMDS1                  0x04868
+#define HDMI_CORE_FC_DBGTMDS2                  0x0486C
+#define HDMI_CORE_PHY_MASK0                    0x0C018
+#define HDMI_CORE_PHY_I2CM_INT_ADDR            0x0C09C
+#define HDMI_CORE_PHY_I2CM_CTLINT_ADDR         0x0C0A0
+
+/* HDMI Audio */
+#define HDMI_CORE_AUD_CONF0                    0x0C400
+#define HDMI_CORE_AUD_CONF1                    0x0C404
+#define HDMI_CORE_AUD_INT                      0x0C408
+#define HDMI_CORE_AUD_N1                       0x0C800
+#define HDMI_CORE_AUD_N2                       0x0C804
+#define HDMI_CORE_AUD_N3                       0x0C808
+#define HDMI_CORE_AUD_CTS1                     0x0C80C
+#define HDMI_CORE_AUD_CTS2                     0x0C810
+#define HDMI_CORE_AUD_CTS3                     0x0C814
+#define HDMI_CORE_AUD_INCLKFS                  0x0C818
+#define HDMI_CORE_AUD_CC08                     0x0CC08
+#define HDMI_CORE_AUD_GP_CONF0                 0x0D400
+#define HDMI_CORE_AUD_GP_CONF1                 0x0D404
+#define HDMI_CORE_AUD_GP_CONF2                 0x0D408
+#define HDMI_CORE_AUD_D010                     0x0D010
+#define HDMI_CORE_AUD_GP_STAT                  0x0D40C
+#define HDMI_CORE_AUD_GP_INT                   0x0D410
+#define HDMI_CORE_AUD_GP_POL                   0x0D414
+#define HDMI_CORE_AUD_GP_MASK                  0x0D418
+
+/* HDMI Main Controller */
+#define HDMI_CORE_MC_CLKDIS                    0x10004
+#define HDMI_CORE_MC_SWRSTZREQ                 0x10008
+#define HDMI_CORE_MC_FLOWCTRL                  0x10010
+#define HDMI_CORE_MC_PHYRSTZ                   0x10014
+#define HDMI_CORE_MC_LOCKONCLOCK               0x10018
+
+/* HDMI COLOR SPACE CONVERTER */
+#define HDMI_CORE_CSC_CFG                      0x10400
+#define HDMI_CORE_CSC_SCALE                    0x10404
+#define HDMI_CORE_CSC_COEF_A1_MSB              0x10408
+#define HDMI_CORE_CSC_COEF_A1_LSB              0x1040C
+#define HDMI_CORE_CSC_COEF_A2_MSB              0x10410
+#define HDMI_CORE_CSC_COEF_A2_LSB              0x10414
+#define HDMI_CORE_CSC_COEF_A3_MSB              0x10418
+#define HDMI_CORE_CSC_COEF_A3_LSB              0x1041C
+#define HDMI_CORE_CSC_COEF_A4_MSB              0x10420
+#define HDMI_CORE_CSC_COEF_A4_LSB              0x10424
+#define HDMI_CORE_CSC_COEF_B1_MSB              0x10428
+#define HDMI_CORE_CSC_COEF_B1_LSB              0x1042C
+#define HDMI_CORE_CSC_COEF_B2_MSB              0x10430
+#define HDMI_CORE_CSC_COEF_B2_LSB              0x10434
+#define HDMI_CORE_CSC_COEF_B3_MSB              0x10438
+#define HDMI_CORE_CSC_COEF_B3_LSB              0x1043C
+#define HDMI_CORE_CSC_COEF_B4_MSB              0x10440
+#define HDMI_CORE_CSC_COEF_B4_LSB              0x10444
+#define HDMI_CORE_CSC_COEF_C1_MSB              0x10448
+#define HDMI_CORE_CSC_COEF_C1_LSB              0x1044C
+#define HDMI_CORE_CSC_COEF_C2_MSB              0x10450
+#define HDMI_CORE_CSC_COEF_C2_LSB              0x10454
+#define HDMI_CORE_CSC_COEF_C3_MSB              0x10458
+#define HDMI_CORE_CSC_COEF_C3_LSB              0x1045C
+#define HDMI_CORE_CSC_COEF_C4_MSB              0x10460
+#define HDMI_CORE_CSC_COEF_C4_LSB              0x10464
+
+/* HDMI HDCP */
+#define HDMI_CORE_HDCP_MASK                    0x14020
+
+/* HDMI CEC */
+#define HDMI_CORE_CEC_MASK                     0x17408
+
+/* HDMI I2C Master */
+#define HDMI_CORE_I2CM_SLAVE                   0x157C8
+#define HDMI_CORE_I2CM_ADDRESS                 0x157CC
+#define HDMI_CORE_I2CM_DATAO                   0x157D0
+#define HDMI_CORE_I2CM_DATAI                   0X157D4
+#define HDMI_CORE_I2CM_OPERATION               0x157D8
+#define HDMI_CORE_I2CM_INT                     0x157DC
+#define HDMI_CORE_I2CM_CTLINT                  0x157E0
+#define HDMI_CORE_I2CM_DIV                     0x157E4
+#define HDMI_CORE_I2CM_SEGADDR                 0x157E8
+#define HDMI_CORE_I2CM_SOFTRSTZ                        0x157EC
+#define HDMI_CORE_I2CM_SEGPTR                  0x157F0
+#define HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR      0x157F4
+#define HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR      0x157F8
+#define HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR      0x157FC
+#define HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR      0x15800
+#define HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR      0x15804
+#define HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR      0x15808
+#define HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR      0x1580C
+#define HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR      0x15810
+#define HDMI_CORE_I2CM_SDA_HOLD_ADDR           0x15814
+
+enum hdmi_core_packet_mode {
+       HDMI_PACKETMODERESERVEDVALUE = 0,
+       HDMI_PACKETMODE24BITPERPIXEL = 4,
+       HDMI_PACKETMODE30BITPERPIXEL = 5,
+       HDMI_PACKETMODE36BITPERPIXEL = 6,
+       HDMI_PACKETMODE48BITPERPIXEL = 7,
+};
+
+struct hdmi_core_vid_config {
+       struct hdmi_config v_fc_config;
+       enum hdmi_core_packet_mode packet_mode;
+       int data_enable_pol;
+       int vblank_osc;
+       int hblank;
+       int vblank;
+};
+
+struct csc_table {
+       u16 a1, a2, a3, a4;
+       u16 b1, b2, b3, b4;
+       u16 c1, c2, c3, c4;
+};
+
+int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len);
+void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s);
+int hdmi5_core_handle_irqs(struct hdmi_core_data *core);
+void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
+                       struct hdmi_config *cfg);
+int hdmi5_core_init(struct platform_device *pdev, struct hdmi_core_data *core);
+
+#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
+int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
+                       struct omap_dss_audio *audio, u32 pclk);
+#endif
+#endif
index 0b12a3f..9a2c39c 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <linux/kernel.h>
 #include <linux/err.h>
+#include <linux/of.h>
 #include <video/omapdss.h>
 
 #include "hdmi.h"
@@ -323,6 +324,46 @@ end:
        return cm;
 }
 
+int hdmi_parse_lanes_of(struct platform_device *pdev, struct device_node *ep,
+       struct hdmi_phy_data *phy)
+{
+       struct property *prop;
+       int r, len;
+
+       prop = of_find_property(ep, "lanes", &len);
+       if (prop) {
+               u32 lanes[8];
+
+               if (len / sizeof(u32) != ARRAY_SIZE(lanes)) {
+                       dev_err(&pdev->dev, "bad number of lanes\n");
+                       return -EINVAL;
+               }
+
+               r = of_property_read_u32_array(ep, "lanes", lanes,
+                       ARRAY_SIZE(lanes));
+               if (r) {
+                       dev_err(&pdev->dev, "failed to read lane data\n");
+                       return r;
+               }
+
+               r = hdmi_phy_parse_lanes(phy, lanes);
+               if (r) {
+                       dev_err(&pdev->dev, "failed to parse lane data\n");
+                       return r;
+               }
+       } else {
+               static const u32 default_lanes[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+
+               r = hdmi_phy_parse_lanes(phy, default_lanes);
+               if (WARN_ON(r)) {
+                       dev_err(&pdev->dev, "failed to parse lane data\n");
+                       return r;
+               }
+       }
+
+       return 0;
+}
+
 #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
 int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts)
 {
index dd376ce..e007ac8 100644 (file)
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
+#include <linux/slab.h>
 #include <video/omapdss.h>
 
 #include "dss.h"
 #include "hdmi.h"
 
+struct hdmi_phy_features {
+       bool bist_ctrl;
+       bool calc_freqout;
+       bool ldo_voltage;
+       unsigned long dcofreq_min;
+       unsigned long max_phy;
+};
+
+static const struct hdmi_phy_features *phy_feat;
+
 void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s)
 {
 #define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\
@@ -26,53 +37,104 @@ void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s)
        DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL);
        DUMPPHY(HDMI_TXPHY_POWER_CTRL);
        DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL);
+       if (phy_feat->bist_ctrl)
+               DUMPPHY(HDMI_TXPHY_BIST_CONTROL);
 }
 
-static irqreturn_t hdmi_irq_handler(int irq, void *data)
+int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes)
 {
-       struct hdmi_wp_data *wp = data;
-       u32 irqstatus;
-
-       irqstatus = hdmi_wp_get_irqstatus(wp);
-       hdmi_wp_set_irqstatus(wp, irqstatus);
-
-       if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
-                       irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
-               /*
-                * If we get both connect and disconnect interrupts at the same
-                * time, turn off the PHY, clear interrupts, and restart, which
-                * raises connect interrupt if a cable is connected, or nothing
-                * if cable is not connected.
-                */
-               hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
-
-               hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
-                               HDMI_IRQ_LINK_DISCONNECT);
-
-               hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
-       } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
-               hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
-       } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
-               hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
+       int i;
+
+       for (i = 0; i < 8; i += 2) {
+               u8 lane, pol;
+               int dx, dy;
+
+               dx = lanes[i];
+               dy = lanes[i + 1];
+
+               if (dx < 0 || dx >= 8)
+                       return -EINVAL;
+
+               if (dy < 0 || dy >= 8)
+                       return -EINVAL;
+
+               if (dx & 1) {
+                       if (dy != dx - 1)
+                               return -EINVAL;
+                       pol = 1;
+               } else {
+                       if (dy != dx + 1)
+                               return -EINVAL;
+                       pol = 0;
+               }
+
+               lane = dx / 2;
+
+               phy->lane_function[lane] = i / 2;
+               phy->lane_polarity[lane] = pol;
        }
 
-       return IRQ_HANDLED;
+       return 0;
 }
 
-int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp,
-                       struct hdmi_config *cfg)
+static void hdmi_phy_configure_lanes(struct hdmi_phy_data *phy)
 {
-       u16 r = 0;
-       u32 irqstatus;
-
-       hdmi_wp_clear_irqenable(wp, 0xffffffff);
-
-       irqstatus = hdmi_wp_get_irqstatus(wp);
-       hdmi_wp_set_irqstatus(wp, irqstatus);
+       static const u16 pad_cfg_list[] = {
+               0x0123,
+               0x0132,
+               0x0312,
+               0x0321,
+               0x0231,
+               0x0213,
+               0x1023,
+               0x1032,
+               0x3012,
+               0x3021,
+               0x2031,
+               0x2013,
+               0x1203,
+               0x1302,
+               0x3102,
+               0x3201,
+               0x2301,
+               0x2103,
+               0x1230,
+               0x1320,
+               0x3120,
+               0x3210,
+               0x2310,
+               0x2130,
+       };
+
+       u16 lane_cfg = 0;
+       int i;
+       unsigned lane_cfg_val;
+       u16 pol_val = 0;
+
+       for (i = 0; i < 4; ++i)
+               lane_cfg |= phy->lane_function[i] << ((3 - i) * 4);
+
+       pol_val |= phy->lane_polarity[0] << 0;
+       pol_val |= phy->lane_polarity[1] << 3;
+       pol_val |= phy->lane_polarity[2] << 2;
+       pol_val |= phy->lane_polarity[3] << 1;
+
+       for (i = 0; i < ARRAY_SIZE(pad_cfg_list); ++i)
+               if (pad_cfg_list[i] == lane_cfg)
+                       break;
+
+       if (WARN_ON(i == ARRAY_SIZE(pad_cfg_list)))
+               i = 0;
+
+       lane_cfg_val = i;
+
+       REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, lane_cfg_val, 26, 22);
+       REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, pol_val, 30, 27);
+}
 
-       r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
-       if (r)
-               return r;
+int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg)
+{
+       u8 freqout;
 
        /*
         * Read address 0 in order to get the SCP reset done completed
@@ -80,80 +142,113 @@ int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp,
         */
        hdmi_read_reg(phy->base, HDMI_TXPHY_TX_CTRL);
 
+       /*
+        * In OMAP5+, the HFBITCLK must be divided by 2 before issuing the
+        * HDMI_PHYPWRCMD_LDOON command.
+       */
+       if (phy_feat->bist_ctrl)
+               REG_FLD_MOD(phy->base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11);
+
+       if (phy_feat->calc_freqout) {
+               /* DCOCLK/10 is pixel clock, compare pclk with DCOCLK_MIN/10 */
+               u32 dco_min = phy_feat->dcofreq_min / 10;
+               u32 pclk = cfg->timings.pixelclock;
+
+               if (pclk < dco_min)
+                       freqout = 0;
+               else if ((pclk >= dco_min) && (pclk < phy_feat->max_phy))
+                       freqout = 1;
+               else
+                       freqout = 2;
+       } else {
+               freqout = 1;
+       }
+
        /*
         * Write to phy address 0 to configure the clock
         * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field
         */
-       REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30);
+       REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, freqout, 31, 30);
 
        /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */
        hdmi_write_reg(phy->base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000);
 
        /* Setup max LDO voltage */
-       REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0);
+       if (phy_feat->ldo_voltage)
+               REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0);
 
-       /* Write to phy address 3 to change the polarity control */
-       REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27);
-
-       r = request_threaded_irq(phy->irq, NULL, hdmi_irq_handler,
-                               IRQF_ONESHOT, "OMAP HDMI", wp);
-       if (r) {
-               DSSERR("HDMI IRQ request failed\n");
-               hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
-               return r;
-       }
-
-       hdmi_wp_set_irqenable(wp,
-               HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
+       hdmi_phy_configure_lanes(phy);
 
        return 0;
 }
 
-void hdmi_phy_disable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp)
+static const struct hdmi_phy_features omap44xx_phy_feats = {
+       .bist_ctrl      =       false,
+       .calc_freqout   =       false,
+       .ldo_voltage    =       true,
+       .dcofreq_min    =       500000000,
+       .max_phy        =       185675000,
+};
+
+static const struct hdmi_phy_features omap54xx_phy_feats = {
+       .bist_ctrl      =       true,
+       .calc_freqout   =       true,
+       .ldo_voltage    =       false,
+       .dcofreq_min    =       750000000,
+       .max_phy        =       186000000,
+};
+
+static int hdmi_phy_init_features(struct platform_device *pdev)
 {
-       free_irq(phy->irq, wp);
+       struct hdmi_phy_features *dst;
+       const struct hdmi_phy_features *src;
 
-       hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
-}
+       dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL);
+       if (!dst) {
+               dev_err(&pdev->dev, "Failed to allocate HDMI PHY Features\n");
+               return -ENOMEM;
+       }
+
+       switch (omapdss_get_version()) {
+       case OMAPDSS_VER_OMAP4430_ES1:
+       case OMAPDSS_VER_OMAP4430_ES2:
+       case OMAPDSS_VER_OMAP4:
+               src = &omap44xx_phy_feats;
+               break;
 
-#define PHY_OFFSET     0x300
-#define PHY_SIZE       0x100
+       case OMAPDSS_VER_OMAP5:
+               src = &omap54xx_phy_feats;
+               break;
+
+       default:
+               return -ENODEV;
+       }
+
+       memcpy(dst, src, sizeof(*dst));
+       phy_feat = dst;
+
+       return 0;
+}
 
 int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy)
 {
+       int r;
        struct resource *res;
-       struct resource temp_res;
+
+       r = hdmi_phy_init_features(pdev);
+       if (r)
+               return r;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
        if (!res) {
-               DSSDBG("can't get PHY mem resource by name\n");
-               /*
-                * if hwmod/DT doesn't have the memory resource information
-                * split into HDMI sub blocks by name, we try again by getting
-                * the platform's first resource. this code will be removed when
-                * the driver can get the mem resources by name
-                */
-               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-               if (!res) {
-                       DSSERR("can't get PHY mem resource\n");
-                       return -EINVAL;
-               }
-
-               temp_res.start = res->start + PHY_OFFSET;
-               temp_res.end = temp_res.start + PHY_SIZE - 1;
-               res = &temp_res;
+               DSSERR("can't get PHY mem resource\n");
+               return -EINVAL;
        }
 
-       phy->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-       if (!phy->base) {
+       phy->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(phy->base)) {
                DSSERR("can't ioremap TX PHY\n");
-               return -ENOMEM;
-       }
-
-       phy->irq = platform_get_irq(pdev, 0);
-       if (phy->irq < 0) {
-               DSSERR("platform_get_irq failed\n");
-               return -ENODEV;
+               return PTR_ERR(phy->base);
        }
 
        return 0;
index 5fc7121..54df12a 100644 (file)
 #define HDMI_DEFAULT_REGN 16
 #define HDMI_DEFAULT_REGM2 1
 
+struct hdmi_pll_features {
+       bool sys_reset;
+       /* this is a hack, need to replace it with a better computation of M2 */
+       bool bound_dcofreq;
+       unsigned long fint_min, fint_max;
+       u16 regm_max;
+       unsigned long dcofreq_low_min, dcofreq_low_max;
+       unsigned long dcofreq_high_min, dcofreq_high_max;
+};
+
+static const struct hdmi_pll_features *pll_feat;
+
 void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s)
 {
 #define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\
@@ -57,7 +69,11 @@ void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy)
 
        refclk = clkin / pi->regn;
 
-       pi->regm2 = HDMI_DEFAULT_REGM2;
+       /* temorary hack to make sure DCO freq isn't calculated too low */
+       if (pll_feat->bound_dcofreq && phy <= 65000)
+               pi->regm2 = 3;
+       else
+               pi->regm2 = HDMI_DEFAULT_REGM2;
 
        /*
         * multiplier is pixel_clk/ref_clk
@@ -154,7 +170,7 @@ static int hdmi_pll_config(struct hdmi_pll_data *pll)
 static int hdmi_pll_reset(struct hdmi_pll_data *pll)
 {
        /* SYSRESET  controlled by power FSM */
-       REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 3, 3);
+       REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, pll_feat->sys_reset, 3, 3);
 
        /* READ 0x0 reset is in progress */
        if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_STATUS, 0, 0, 1)
@@ -194,38 +210,81 @@ void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp)
        hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF);
 }
 
-#define PLL_OFFSET     0x200
-#define PLL_SIZE       0x100
+static const struct hdmi_pll_features omap44xx_pll_feats = {
+       .sys_reset              =       false,
+       .bound_dcofreq          =       false,
+       .fint_min               =       500000,
+       .fint_max               =       2500000,
+       .regm_max               =       4095,
+       .dcofreq_low_min        =       500000000,
+       .dcofreq_low_max        =       1000000000,
+       .dcofreq_high_min       =       1000000000,
+       .dcofreq_high_max       =       2000000000,
+};
+
+static const struct hdmi_pll_features omap54xx_pll_feats = {
+       .sys_reset              =       true,
+       .bound_dcofreq          =       true,
+       .fint_min               =       620000,
+       .fint_max               =       2500000,
+       .regm_max               =       2046,
+       .dcofreq_low_min        =       750000000,
+       .dcofreq_low_max        =       1500000000,
+       .dcofreq_high_min       =       1250000000,
+       .dcofreq_high_max       =       2500000000UL,
+};
+
+static int hdmi_pll_init_features(struct platform_device *pdev)
+{
+       struct hdmi_pll_features *dst;
+       const struct hdmi_pll_features *src;
+
+       dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL);
+       if (!dst) {
+               dev_err(&pdev->dev, "Failed to allocate HDMI PHY Features\n");
+               return -ENOMEM;
+       }
+
+       switch (omapdss_get_version()) {
+       case OMAPDSS_VER_OMAP4430_ES1:
+       case OMAPDSS_VER_OMAP4430_ES2:
+       case OMAPDSS_VER_OMAP4:
+               src = &omap44xx_pll_feats;
+               break;
+
+       case OMAPDSS_VER_OMAP5:
+               src = &omap54xx_pll_feats;
+               break;
+
+       default:
+               return -ENODEV;
+       }
+
+       memcpy(dst, src, sizeof(*dst));
+       pll_feat = dst;
+
+       return 0;
+}
 
 int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll)
 {
+       int r;
        struct resource *res;
-       struct resource temp_res;
+
+       r = hdmi_pll_init_features(pdev);
+       if (r)
+               return r;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll");
        if (!res) {
-               DSSDBG("can't get PLL mem resource by name\n");
-               /*
-                * if hwmod/DT doesn't have the memory resource information
-                * split into HDMI sub blocks by name, we try again by getting
-                * the platform's first resource. this code will be removed when
-                * the driver can get the mem resources by name
-                */
-               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-               if (!res) {
-                       DSSERR("can't get PLL mem resource\n");
-                       return -EINVAL;
-               }
-
-               temp_res.start = res->start + PLL_OFFSET;
-               temp_res.end = temp_res.start + PLL_SIZE - 1;
-               res = &temp_res;
+               DSSERR("can't get PLL mem resource\n");
+               return -EINVAL;
        }
 
-       pll->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-       if (!pll->base) {
+       pll->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(pll->base)) {
                DSSERR("can't ioremap PLLCTRL\n");
-               return -ENOMEM;
+               return PTR_ERR(pll->base);
        }
 
        return 0;
index f5f4ccf..496327e 100644 (file)
@@ -185,7 +185,7 @@ void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
        timings->interlace = param->timings.interlace;
 }
 
-#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || defined(CONFIG_OMAP5_DSS_HDMI_AUDIO)
 void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp,
                struct hdmi_audio_format *aud_fmt)
 {
@@ -238,37 +238,20 @@ int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable)
 }
 #endif
 
-#define WP_SIZE        0x200
-
 int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp)
 {
        struct resource *res;
-       struct resource temp_res;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wp");
        if (!res) {
-               DSSDBG("can't get WP mem resource by name\n");
-               /*
-                * if hwmod/DT doesn't have the memory resource information
-                * split into HDMI sub blocks by name, we try again by getting
-                * the platform's first resource. this code will be removed when
-                * the driver can get the mem resources by name
-                */
-               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-               if (!res) {
-                       DSSERR("can't get WP mem resource\n");
-                       return -EINVAL;
-               }
-
-               temp_res.start = res->start;
-               temp_res.end = temp_res.start + WP_SIZE - 1;
-               res = &temp_res;
+               DSSERR("can't get WP mem resource\n");
+               return -EINVAL;
        }
 
-       wp->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-       if (!wp->base) {
+       wp->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(wp->base)) {
                DSSERR("can't ioremap HDMI WP\n");
-               return -ENOMEM;
+               return PTR_ERR(wp->base);
        }
 
        return 0;
diff --git a/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c b/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c
new file mode 100644 (file)
index 0000000..99af9e8
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2014 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * As omapdss panel drivers are omapdss specific, but we want to define the
+ * DT-data in generic manner, we convert the compatible strings of the panel and
+ * encoder nodes from "panel-foo" to "omapdss,panel-foo". This way we can have
+ * both correct DT data and omapdss specific drivers.
+ *
+ * When we get generic panel drivers to the kernel, this file will be removed.
+ */
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+static struct list_head dss_conv_list __initdata;
+
+static const char prefix[] __initconst = "omapdss,";
+
+struct dss_conv_node {
+       struct list_head list;
+       struct device_node *node;
+       bool root;
+};
+
+static int __init omapdss_count_strings(const struct property *prop)
+{
+       const char *p = prop->value;
+       int l = 0, total = 0;
+       int i;
+
+       for (i = 0; total < prop->length; total += l, p += l, i++)
+               l = strlen(p) + 1;
+
+       return i;
+}
+
+static void __init omapdss_update_prop(struct device_node *node, char *compat,
+       int len)
+{
+       struct property *prop;
+
+       prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+       if (!prop)
+               return;
+
+       prop->name = "compatible";
+       prop->value = compat;
+       prop->length = len;
+
+       of_update_property(node, prop);
+}
+
+static void __init omapdss_prefix_strcpy(char *dst, int dst_len,
+       const char *src, int src_len)
+{
+       size_t total = 0;
+
+       while (total < src_len) {
+               size_t l = strlen(src) + 1;
+
+               strcpy(dst, prefix);
+               dst += strlen(prefix);
+
+               strcpy(dst, src);
+               dst += l;
+
+               src += l;
+               total += l;
+       }
+}
+
+/* prepend compatible property strings with "omapdss," */
+static void __init omapdss_omapify_node(struct device_node *node)
+{
+       struct property *prop;
+       char *new_compat;
+       int num_strs;
+       int new_len;
+
+       prop = of_find_property(node, "compatible", NULL);
+
+       if (!prop || !prop->value)
+               return;
+
+       if (strnlen(prop->value, prop->length) >= prop->length)
+               return;
+
+       /* is it already prefixed? */
+       if (strncmp(prefix, prop->value, strlen(prefix)) == 0)
+               return;
+
+       num_strs = omapdss_count_strings(prop);
+
+       new_len = prop->length + strlen(prefix) * num_strs;
+       new_compat = kmalloc(new_len, GFP_KERNEL);
+
+       omapdss_prefix_strcpy(new_compat, new_len, prop->value, prop->length);
+
+       omapdss_update_prop(node, new_compat, new_len);
+}
+
+static void __init omapdss_add_to_list(struct device_node *node, bool root)
+{
+       struct dss_conv_node *n = kmalloc(sizeof(struct dss_conv_node),
+               GFP_KERNEL);
+       n->node = node;
+       n->root = root;
+       list_add(&n->list, &dss_conv_list);
+}
+
+static bool __init omapdss_list_contains(const struct device_node *node)
+{
+       struct dss_conv_node *n;
+
+       list_for_each_entry(n, &dss_conv_list, list) {
+               if (n->node == node)
+                       return true;
+       }
+
+       return false;
+}
+
+static void __init omapdss_walk_device(struct device_node *node, bool root)
+{
+       struct device_node *n;
+
+       omapdss_add_to_list(node, root);
+
+       /*
+        * of_graph_get_remote_port_parent() prints an error if there is no
+        * port/ports node. To avoid that, check first that there's the node.
+        */
+       n = of_get_child_by_name(node, "ports");
+       if (!n)
+               n = of_get_child_by_name(node, "port");
+       if (!n)
+               return;
+
+       of_node_put(n);
+
+       n = NULL;
+       while ((n = of_graph_get_next_endpoint(node, n)) != NULL) {
+               struct device_node *pn;
+
+               pn = of_graph_get_remote_port_parent(n);
+
+               if (!pn) {
+                       of_node_put(n);
+                       continue;
+               }
+
+               if (!of_device_is_available(pn) || omapdss_list_contains(pn)) {
+                       of_node_put(pn);
+                       of_node_put(n);
+                       continue;
+               }
+
+               omapdss_walk_device(pn, false);
+
+               of_node_put(n);
+       }
+}
+
+static const struct of_device_id omapdss_of_match[] __initconst = {
+       { .compatible = "ti,omap2-dss", },
+       { .compatible = "ti,omap3-dss", },
+       { .compatible = "ti,omap4-dss", },
+       { .compatible = "ti,omap5-dss", },
+       {},
+};
+
+static int __init omapdss_boot_init(void)
+{
+       struct device_node *dss, *child;
+
+       INIT_LIST_HEAD(&dss_conv_list);
+
+       dss = of_find_matching_node(NULL, omapdss_of_match);
+
+       if (dss == NULL || !of_device_is_available(dss))
+               return 0;
+
+       omapdss_walk_device(dss, true);
+
+       for_each_available_child_of_node(dss, child) {
+               if (!of_find_property(child, "compatible", NULL)) {
+                       of_node_put(child);
+                       continue;
+               }
+
+               omapdss_walk_device(child, true);
+       }
+
+       while (!list_empty(&dss_conv_list)) {
+               struct dss_conv_node *n;
+
+               n = list_first_entry(&dss_conv_list, struct dss_conv_node,
+                       list);
+
+               if (!n->root)
+                       omapdss_omapify_node(n->node);
+
+               list_del(&n->list);
+               of_node_put(n->node);
+               kfree(n);
+       }
+
+       return 0;
+}
+
+subsys_initcall(omapdss_boot_init);
diff --git a/drivers/video/fbdev/omap2/dss/venc_panel.c b/drivers/video/fbdev/omap2/dss/venc_panel.c
deleted file mode 100644 (file)
index af68cd4..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2009 Nokia Corporation
- * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
- *
- * VENC panel 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.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/mutex.h>
-#include <linux/module.h>
-
-#include <video/omapdss.h>
-
-#include "dss.h"
-
-static struct {
-       struct mutex lock;
-} venc_panel;
-
-static ssize_t display_output_type_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct omap_dss_device *dssdev = to_dss_device(dev);
-       const char *ret;
-
-       switch (dssdev->phy.venc.type) {
-       case OMAP_DSS_VENC_TYPE_COMPOSITE:
-               ret = "composite";
-               break;
-       case OMAP_DSS_VENC_TYPE_SVIDEO:
-               ret = "svideo";
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return snprintf(buf, PAGE_SIZE, "%s\n", ret);
-}
-
-static ssize_t display_output_type_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct omap_dss_device *dssdev = to_dss_device(dev);
-       enum omap_dss_venc_type new_type;
-
-       if (sysfs_streq("composite", buf))
-               new_type = OMAP_DSS_VENC_TYPE_COMPOSITE;
-       else if (sysfs_streq("svideo", buf))
-               new_type = OMAP_DSS_VENC_TYPE_SVIDEO;
-       else
-               return -EINVAL;
-
-       mutex_lock(&venc_panel.lock);
-
-       if (dssdev->phy.venc.type != new_type) {
-               dssdev->phy.venc.type = new_type;
-               omapdss_venc_set_type(dssdev, new_type);
-               if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
-                       omapdss_venc_display_disable(dssdev);
-                       omapdss_venc_display_enable(dssdev);
-               }
-       }
-
-       mutex_unlock(&venc_panel.lock);
-
-       return size;
-}
-
-static DEVICE_ATTR(output_type, S_IRUGO | S_IWUSR,
-               display_output_type_show, display_output_type_store);
-
-static int venc_panel_probe(struct omap_dss_device *dssdev)
-{
-       /* set default timings to PAL */
-       const struct omap_video_timings default_timings = {
-               .x_res          = 720,
-               .y_res          = 574,
-               .pixelclock     = 13500000,
-               .hsw            = 64,
-               .hfp            = 12,
-               .hbp            = 68,
-               .vsw            = 5,
-               .vfp            = 5,
-               .vbp            = 41,
-
-               .vsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
-               .hsync_level    = OMAPDSS_SIG_ACTIVE_HIGH,
-
-               .interlace      = true,
-       };
-
-       mutex_init(&venc_panel.lock);
-
-       dssdev->panel.timings = default_timings;
-
-       return device_create_file(dssdev->dev, &dev_attr_output_type);
-}
-
-static void venc_panel_remove(struct omap_dss_device *dssdev)
-{
-       device_remove_file(dssdev->dev, &dev_attr_output_type);
-}
-
-static int venc_panel_enable(struct omap_dss_device *dssdev)
-{
-       int r;
-
-       dev_dbg(dssdev->dev, "venc_panel_enable\n");
-
-       mutex_lock(&venc_panel.lock);
-
-       if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
-               r = -EINVAL;
-               goto err;
-       }
-
-       omapdss_venc_set_timings(dssdev, &dssdev->panel.timings);
-       omapdss_venc_set_type(dssdev, dssdev->phy.venc.type);
-       omapdss_venc_invert_vid_out_polarity(dssdev,
-               dssdev->phy.venc.invert_polarity);
-
-       r = omapdss_venc_display_enable(dssdev);
-       if (r)
-               goto err;
-
-       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-       mutex_unlock(&venc_panel.lock);
-
-       return 0;
-err:
-       mutex_unlock(&venc_panel.lock);
-
-       return r;
-}
-
-static void venc_panel_disable(struct omap_dss_device *dssdev)
-{
-       dev_dbg(dssdev->dev, "venc_panel_disable\n");
-
-       mutex_lock(&venc_panel.lock);
-
-       if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)
-               goto end;
-
-       omapdss_venc_display_disable(dssdev);
-
-       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
-end:
-       mutex_unlock(&venc_panel.lock);
-}
-
-static void venc_panel_set_timings(struct omap_dss_device *dssdev,
-               struct omap_video_timings *timings)
-{
-       dev_dbg(dssdev->dev, "venc_panel_set_timings\n");
-
-       mutex_lock(&venc_panel.lock);
-
-       omapdss_venc_set_timings(dssdev, timings);
-       dssdev->panel.timings = *timings;
-
-       mutex_unlock(&venc_panel.lock);
-}
-
-static int venc_panel_check_timings(struct omap_dss_device *dssdev,
-               struct omap_video_timings *timings)
-{
-       dev_dbg(dssdev->dev, "venc_panel_check_timings\n");
-
-       return omapdss_venc_check_timings(dssdev, timings);
-}
-
-static u32 venc_panel_get_wss(struct omap_dss_device *dssdev)
-{
-       dev_dbg(dssdev->dev, "venc_panel_get_wss\n");
-
-       return omapdss_venc_get_wss(dssdev);
-}
-
-static int venc_panel_set_wss(struct omap_dss_device *dssdev, u32 wss)
-{
-       dev_dbg(dssdev->dev, "venc_panel_set_wss\n");
-
-       return omapdss_venc_set_wss(dssdev, wss);
-}
-
-static struct omap_dss_driver venc_driver = {
-       .probe          = venc_panel_probe,
-       .remove         = venc_panel_remove,
-
-       .enable         = venc_panel_enable,
-       .disable        = venc_panel_disable,
-
-       .get_resolution = omapdss_default_get_resolution,
-       .get_recommended_bpp = omapdss_default_get_recommended_bpp,
-
-       .set_timings    = venc_panel_set_timings,
-       .check_timings  = venc_panel_check_timings,
-
-       .get_wss        = venc_panel_get_wss,
-       .set_wss        = venc_panel_set_wss,
-
-       .driver         = {
-               .name   = "venc",
-               .owner  = THIS_MODULE,
-       },
-};
-
-int venc_panel_init(void)
-{
-       return omap_dss_register_driver(&venc_driver);
-}
-
-void venc_panel_exit(void)
-{
-       omap_dss_unregister_driver(&venc_driver);
-}
index 417f9a2..4df3657 100644 (file)
@@ -612,11 +612,9 @@ static int pxa3xx_gcu_probe(struct platform_device *pdev)
 
        /* handle IO resources */
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       priv->mmio_base = devm_request_and_ioremap(dev, r);
-       if (IS_ERR(priv->mmio_base)) {
-               dev_err(dev, "failed to map I/O memory\n");
+       priv->mmio_base = devm_ioremap_resource(dev, r);
+       if (IS_ERR(priv->mmio_base))
                return PTR_ERR(priv->mmio_base);
-       }
 
        /* enable the clock */
        priv->clk = devm_clk_get(dev, NULL);
index 9a3f8f1..c43b969 100644 (file)
@@ -1401,9 +1401,10 @@ err_enable_device:
 static void s3_pci_remove(struct pci_dev *dev)
 {
        struct fb_info *info = pci_get_drvdata(dev);
-       struct s3fb_info __maybe_unused *par = info->par;
+       struct s3fb_info __maybe_unused *par;
 
        if (info) {
+               par = info->par;
 
 #ifdef CONFIG_MTRR
                if (par->mtrr_reg >= 0) {
index 537d199..d2fafbb 100644 (file)
@@ -162,7 +162,7 @@ static ssize_t contrast_show(struct device *dev,
        struct fb_info *info = dev_get_drvdata(dev);
        struct wm8505fb_info *fbi = to_wm8505fb_info(info);
 
-       return sprintf(buf, "%d\n", fbi->contrast);
+       return sprintf(buf, "%u\n", fbi->contrast);
 }
 
 static ssize_t contrast_store(struct device *dev,
index ba5b40f..987edf1 100644 (file)
@@ -115,10 +115,8 @@ int of_get_display_timing(struct device_node *np, const char *name,
 {
        struct device_node *timing_np;
 
-       if (!np) {
-               pr_err("%s: no devicenode given\n", of_node_full_name(np));
+       if (!np)
                return -EINVAL;
-       }
 
        timing_np = of_get_child_by_name(np, name);
        if (!timing_np) {
@@ -142,10 +140,8 @@ struct display_timings *of_get_display_timings(struct device_node *np)
        struct device_node *native_mode;
        struct display_timings *disp;
 
-       if (!np) {
-               pr_err("%s: no device node given\n", of_node_full_name(np));
+       if (!np)
                return NULL;
-       }
 
        timings_np = of_get_child_by_name(np, "display-timings");
        if (!timings_np) {
@@ -164,7 +160,7 @@ struct display_timings *of_get_display_timings(struct device_node *np)
        entry = of_parse_phandle(timings_np, "native-mode", 0);
        /* assume first child as native mode if none provided */
        if (!entry)
-               entry = of_get_next_child(np, NULL);
+               entry = of_get_next_child(timings_np, NULL);
        /* if there is no child, it is useless to go on */
        if (!entry) {
                pr_err("%s: no timing specifications given\n",
index d822320..59e3fe3 100644 (file)
@@ -352,9 +352,6 @@ static int v9fs_file_flock_dotl(struct file *filp, int cmd,
                invalidate_mapping_pages(&inode->i_data, 0, -1);
        }
        /* Convert flock to posix lock */
-       fl->fl_owner = (fl_owner_t)filp;
-       fl->fl_start = 0;
-       fl->fl_end = OFFSET_MAX;
        fl->fl_flags |= FL_POSIX;
        fl->fl_flags ^= FL_FLOCK;
 
index a8cf2cf..4baf1d2 100644 (file)
@@ -555,10 +555,6 @@ int afs_flock(struct file *file, int cmd, struct file_lock *fl)
                return -ENOLCK;
 
        /* we're simulating flock() locks using posix locks on the server */
-       fl->fl_owner = (fl_owner_t) file;
-       fl->fl_start = 0;
-       fl->fl_end = OFFSET_MAX;
-
        if (fl->fl_type == F_UNLCK)
                return afs_do_unlk(file, fl);
        return afs_do_setlk(file, fl);
index 1913988..fbc39c4 100644 (file)
@@ -53,10 +53,7 @@ static int ceph_lock_message(u8 lock_type, u16 operation, struct file *file,
        else
                length = fl->fl_end - fl->fl_start + 1;
 
-       if (lock_type == CEPH_LOCK_FCNTL)
-               owner = secure_addr(fl->fl_owner);
-       else
-               owner = secure_addr(fl->fl_file);
+       owner = secure_addr(fl->fl_owner);
 
        dout("ceph_lock_message: rule: %d, op: %d, owner: %llx, pid: %llu, "
             "start: %llu, length: %llu, wait: %d, type: %d", (int)lock_type,
@@ -314,10 +311,7 @@ int lock_to_ceph_filelock(struct file_lock *lock,
        cephlock->length = cpu_to_le64(lock->fl_end - lock->fl_start + 1);
        cephlock->client = cpu_to_le64(0);
        cephlock->pid = cpu_to_le64((u64)lock->fl_pid);
-       if (lock->fl_flags & FL_POSIX)
-               cephlock->owner = cpu_to_le64(secure_addr(lock->fl_owner));
-       else
-               cephlock->owner = cpu_to_le64(secure_addr(lock->fl_file));
+       cephlock->owner = cpu_to_le64(secure_addr(lock->fl_owner));
 
        switch (lock->fl_type) {
        case F_RDLCK:
index 96d513e..f680d2c 100644 (file)
@@ -2304,7 +2304,6 @@ static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl)
                struct fuse_file *ff = file->private_data;
 
                /* emulate flock with POSIX locks */
-               fl->fl_owner = (fl_owner_t) file;
                ff->flock = true;
                err = fuse_setlk(file, fl, 1);
        }
index ce62dca..5a49b03 100644 (file)
@@ -431,7 +431,7 @@ static int gfs2_jdata_writepages(struct address_space *mapping,
 
        ret = gfs2_write_cache_jdata(mapping, wbc);
        if (ret == 0 && wbc->sync_mode == WB_SYNC_ALL) {
-               gfs2_log_flush(sdp, ip->i_gl);
+               gfs2_log_flush(sdp, ip->i_gl, NORMAL_FLUSH);
                ret = gfs2_write_cache_jdata(mapping, wbc);
        }
        return ret;
index c62d4b9..e6ee5b6 100644 (file)
@@ -707,7 +707,7 @@ int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsi
  * @top: The first pointer in the buffer
  * @bottom: One more than the last pointer
  * @height: the height this buffer is at
- * @data: a pointer to a struct strip_mine
+ * @sm: a pointer to a struct strip_mine
  *
  * Returns: errno
  */
@@ -992,6 +992,8 @@ unlock:
        return err;
 }
 
+#define GFS2_JTRUNC_REVOKES 8192
+
 /**
  * gfs2_journaled_truncate - Wrapper for truncate_pagecache for jdata files
  * @inode: The inode being truncated
@@ -1003,8 +1005,6 @@ unlock:
  * if the number of pages being truncated gets too large.
  */
 
-#define GFS2_JTRUNC_REVOKES 8192
-
 static int gfs2_journaled_truncate(struct inode *inode, u64 oldsize, u64 newsize)
 {
        struct gfs2_sbd *sdp = GFS2_SB(inode);
@@ -1348,7 +1348,7 @@ void gfs2_free_journal_extents(struct gfs2_jdesc *jd)
  * gfs2_add_jextent - Add or merge a new extent to extent cache
  * @jd: The journal descriptor
  * @lblock: The logical block at start of new extent
- * @pblock: The physical block at start of new extent
+ * @dblock: The physical block at start of new extent
  * @blocks: Size of extent in fs blocks
  *
  * Returns: 0 on success or -ENOMEM
index 80d6725..6ab0cfb 100644 (file)
@@ -203,9 +203,9 @@ void gfs2_set_inode_flags(struct inode *inode)
                             GFS2_DIF_INHERIT_JDATA)
 
 /**
- * gfs2_set_flags - set flags on an inode
- * @inode: The inode
- * @flags: The flags to set
+ * do_gfs2_set_flags - set flags on an inode
+ * @filp: file pointer
+ * @reqflags: The flags to set
  * @mask: Indicates which flags are valid
  *
  */
@@ -256,7 +256,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
        }
        if ((flags ^ new_flags) & GFS2_DIF_JDATA) {
                if (flags & GFS2_DIF_JDATA)
-                       gfs2_log_flush(sdp, ip->i_gl);
+                       gfs2_log_flush(sdp, ip->i_gl, NORMAL_FLUSH);
                error = filemap_fdatawrite(inode->i_mapping);
                if (error)
                        goto out;
@@ -318,7 +318,7 @@ static long gfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 
 /**
  * gfs2_size_hint - Give a hint to the size of a write request
- * @file: The struct file
+ * @filep: The struct file
  * @offset: The file offset of the write
  * @size: The length of the write
  *
@@ -371,7 +371,7 @@ static int gfs2_allocate_page_backing(struct page *page)
 /**
  * gfs2_page_mkwrite - Make a shared, mmap()ed, page writable
  * @vma: The virtual memory area
- * @page: The page which is about to become writable
+ * @vmf: The virtual memory fault containing the page to become writable
  *
  * When the page becomes writable, we need to ensure that we have
  * blocks allocated on disk to back that page.
index 74d9a3d..fc11007 100644 (file)
@@ -89,18 +89,23 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
        if (!tr.tr_revokes)
                return;
 
-       /* A shortened, inline version of gfs2_trans_begin() */
+       /* A shortened, inline version of gfs2_trans_begin()
+         * tr->alloced is not set since the transaction structure is
+         * on the stack */
        tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes, sizeof(u64));
        tr.tr_ip = (unsigned long)__builtin_return_address(0);
        sb_start_intwrite(sdp->sd_vfs);
-       gfs2_log_reserve(sdp, tr.tr_reserved);
+       if (gfs2_log_reserve(sdp, tr.tr_reserved) < 0) {
+               sb_end_intwrite(sdp->sd_vfs);
+               return;
+       }
        WARN_ON_ONCE(current->journal_info);
        current->journal_info = &tr;
 
        __gfs2_ail_flush(gl, 0, tr.tr_revokes);
 
        gfs2_trans_end(sdp);
-       gfs2_log_flush(sdp, NULL);
+       gfs2_log_flush(sdp, NULL, NORMAL_FLUSH);
 }
 
 void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync)
@@ -121,7 +126,7 @@ void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync)
                return;
        __gfs2_ail_flush(gl, fsync, max_revokes);
        gfs2_trans_end(sdp);
-       gfs2_log_flush(sdp, NULL);
+       gfs2_log_flush(sdp, NULL, NORMAL_FLUSH);
 }
 
 /**
@@ -144,7 +149,7 @@ static void rgrp_go_sync(struct gfs2_glock *gl)
                return;
        GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE);
 
-       gfs2_log_flush(sdp, gl);
+       gfs2_log_flush(sdp, gl, NORMAL_FLUSH);
        filemap_fdatawrite_range(mapping, gl->gl_vm.start, gl->gl_vm.end);
        error = filemap_fdatawait_range(mapping, gl->gl_vm.start, gl->gl_vm.end);
        mapping_set_error(mapping, error);
@@ -206,7 +211,7 @@ static void inode_go_sync(struct gfs2_glock *gl)
 
        GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE);
 
-       gfs2_log_flush(gl->gl_sbd, gl);
+       gfs2_log_flush(gl->gl_sbd, gl, NORMAL_FLUSH);
        filemap_fdatawrite(metamapping);
        if (ip) {
                struct address_space *mapping = ip->i_inode.i_mapping;
@@ -253,7 +258,7 @@ static void inode_go_inval(struct gfs2_glock *gl, int flags)
        }
 
        if (ip == GFS2_I(gl->gl_sbd->sd_rindex)) {
-               gfs2_log_flush(gl->gl_sbd, NULL);
+               gfs2_log_flush(gl->gl_sbd, NULL, NORMAL_FLUSH);
                gl->gl_sbd->sd_rindex_uptodate = 0;
        }
        if (ip && S_ISREG(ip->i_inode.i_mode))
@@ -455,31 +460,39 @@ static void inode_go_dump(struct seq_file *seq, const struct gfs2_glock *gl)
 }
 
 /**
- * trans_go_sync - promote/demote the transaction glock
+ * freeze_go_sync - promote/demote the freeze glock
  * @gl: the glock
  * @state: the requested state
  * @flags:
  *
  */
 
-static void trans_go_sync(struct gfs2_glock *gl)
+static void freeze_go_sync(struct gfs2_glock *gl)
 {
        struct gfs2_sbd *sdp = gl->gl_sbd;
+       DEFINE_WAIT(wait);
 
-       if (gl->gl_state != LM_ST_UNLOCKED &&
+       if (gl->gl_state == LM_ST_SHARED &&
            test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) {
-               gfs2_meta_syncfs(sdp);
-               gfs2_log_shutdown(sdp);
+               atomic_set(&sdp->sd_log_freeze, 1);
+               wake_up(&sdp->sd_logd_waitq);
+               do {
+                       prepare_to_wait(&sdp->sd_log_frozen_wait, &wait,
+                                       TASK_UNINTERRUPTIBLE);
+                       if (atomic_read(&sdp->sd_log_freeze))
+                               io_schedule();
+               } while(atomic_read(&sdp->sd_log_freeze));
+               finish_wait(&sdp->sd_log_frozen_wait, &wait);
        }
 }
 
 /**
- * trans_go_xmote_bh - After promoting/demoting the transaction glock
+ * freeze_go_xmote_bh - After promoting/demoting the freeze glock
  * @gl: the glock
  *
  */
 
-static int trans_go_xmote_bh(struct gfs2_glock *gl, struct gfs2_holder *gh)
+static int freeze_go_xmote_bh(struct gfs2_glock *gl, struct gfs2_holder *gh)
 {
        struct gfs2_sbd *sdp = gl->gl_sbd;
        struct gfs2_inode *ip = GFS2_I(sdp->sd_jdesc->jd_inode);
@@ -512,7 +525,7 @@ static int trans_go_xmote_bh(struct gfs2_glock *gl, struct gfs2_holder *gh)
  * Always returns 0
  */
 
-static int trans_go_demote_ok(const struct gfs2_glock *gl)
+static int freeze_go_demote_ok(const struct gfs2_glock *gl)
 {
        return 0;
 }
@@ -563,10 +576,10 @@ const struct gfs2_glock_operations gfs2_rgrp_glops = {
        .go_flags = GLOF_LVB,
 };
 
-const struct gfs2_glock_operations gfs2_trans_glops = {
-       .go_sync = trans_go_sync,
-       .go_xmote_bh = trans_go_xmote_bh,
-       .go_demote_ok = trans_go_demote_ok,
+const struct gfs2_glock_operations gfs2_freeze_glops = {
+       .go_sync = freeze_go_sync,
+       .go_xmote_bh = freeze_go_xmote_bh,
+       .go_demote_ok = freeze_go_demote_ok,
        .go_type = LM_TYPE_NONDISK,
 };
 
index bf95a2d..7455d26 100644 (file)
@@ -15,7 +15,7 @@
 extern const struct gfs2_glock_operations gfs2_meta_glops;
 extern const struct gfs2_glock_operations gfs2_inode_glops;
 extern const struct gfs2_glock_operations gfs2_rgrp_glops;
-extern const struct gfs2_glock_operations gfs2_trans_glops;
+extern const struct gfs2_glock_operations gfs2_freeze_glops;
 extern const struct gfs2_glock_operations gfs2_iopen_glops;
 extern const struct gfs2_glock_operations gfs2_flock_glops;
 extern const struct gfs2_glock_operations gfs2_nondisk_glops;
index bdf70c1..67d310c 100644 (file)
@@ -465,9 +465,7 @@ struct gfs2_trans {
        unsigned int tr_reserved;
        unsigned int tr_touched:1;
        unsigned int tr_attached:1;
-
-       struct gfs2_holder tr_t_gh;
-
+       unsigned int tr_alloced:1;
 
        unsigned int tr_num_buf_new;
        unsigned int tr_num_databuf_new;
@@ -682,7 +680,7 @@ struct gfs2_sbd {
        struct lm_lockstruct sd_lockstruct;
        struct gfs2_holder sd_live_gh;
        struct gfs2_glock *sd_rename_gl;
-       struct gfs2_glock *sd_trans_gl;
+       struct gfs2_glock *sd_freeze_gl;
        wait_queue_head_t sd_glock_wait;
        atomic_t sd_glock_disposal;
        struct completion sd_locking_init;
@@ -730,6 +728,8 @@ struct gfs2_sbd {
        struct gfs2_holder sd_sc_gh;
        struct gfs2_holder sd_qc_gh;
 
+       struct completion sd_journal_ready;
+
        /* Daemon stuff */
 
        struct task_struct *sd_logd_process;
@@ -794,6 +794,12 @@ struct gfs2_sbd {
 
        /* For quiescing the filesystem */
        struct gfs2_holder sd_freeze_gh;
+       struct gfs2_holder sd_freeze_root_gh;
+       struct gfs2_holder sd_thaw_gh;
+       atomic_t sd_log_freeze;
+       atomic_t sd_frozen_root;
+       wait_queue_head_t sd_frozen_root_wait;
+       wait_queue_head_t sd_log_frozen_wait;
 
        char sd_fsname[GFS2_FSNAME_LEN];
        char sd_table_name[GFS2_FSNAME_LEN];
index 28cc7bf..e62e594 100644 (file)
@@ -1613,18 +1613,26 @@ int gfs2_permission(struct inode *inode, int mask)
 {
        struct gfs2_inode *ip;
        struct gfs2_holder i_gh;
+       struct gfs2_sbd *sdp = GFS2_SB(inode);
        int error;
        int unlock = 0;
+       int frozen_root = 0;
 
 
        ip = GFS2_I(inode);
        if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
-               if (mask & MAY_NOT_BLOCK)
-                       return -ECHILD;
-               error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
-               if (error)
-                       return error;
-               unlock = 1;
+               if (unlikely(gfs2_glock_is_held_excl(sdp->sd_freeze_gl) &&
+                            inode == sdp->sd_root_dir->d_inode &&
+                            atomic_inc_not_zero(&sdp->sd_frozen_root)))
+                       frozen_root = 1;
+               else {
+                       if (mask & MAY_NOT_BLOCK)
+                               return -ECHILD;
+                       error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
+                       if (error)
+                               return error;
+                       unlock = 1;
+               }
        }
 
        if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
@@ -1633,6 +1641,8 @@ int gfs2_permission(struct inode *inode, int mask)
                error = generic_permission(inode, mask);
        if (unlock)
                gfs2_glock_dq_uninit(&i_gh);
+       else if (frozen_root && atomic_dec_and_test(&sdp->sd_frozen_root))
+               wake_up(&sdp->sd_frozen_root_wait);
 
        return error;
 }
@@ -1805,19 +1815,29 @@ static int gfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
        struct inode *inode = dentry->d_inode;
        struct gfs2_inode *ip = GFS2_I(inode);
        struct gfs2_holder gh;
+       struct gfs2_sbd *sdp = GFS2_SB(inode);
        int error;
        int unlock = 0;
+       int frozen_root = 0;
 
        if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
-               error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
-               if (error)
-                       return error;
-               unlock = 1;
+               if (unlikely(gfs2_glock_is_held_excl(sdp->sd_freeze_gl) &&
+                            inode == sdp->sd_root_dir->d_inode &&
+                            atomic_inc_not_zero(&sdp->sd_frozen_root)))
+                       frozen_root = 1;
+               else {
+                       error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
+                       if (error)
+                               return error;
+                       unlock = 1;
+               }
        }
 
        generic_fillattr(inode, stat);
        if (unlock)
                gfs2_glock_dq_uninit(&gh);
+       else if (frozen_root && atomic_dec_and_test(&sdp->sd_frozen_root))
+               wake_up(&sdp->sd_frozen_root_wait);
 
        return 0;
 }
index 4a14d50..3966fad 100644 (file)
@@ -300,6 +300,23 @@ static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail)
        spin_unlock(&sdp->sd_ail_lock);
 }
 
+/**
+ * gfs2_log_release - Release a given number of log blocks
+ * @sdp: The GFS2 superblock
+ * @blks: The number of blocks
+ *
+ */
+
+void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks)
+{
+
+       atomic_add(blks, &sdp->sd_log_blks_free);
+       trace_gfs2_log_blocks(sdp, blks);
+       gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <=
+                                 sdp->sd_jdesc->jd_blocks);
+       up_read(&sdp->sd_log_flush_lock);
+}
+
 /**
  * gfs2_log_reserve - Make a log reservation
  * @sdp: The GFS2 superblock
@@ -358,7 +375,10 @@ retry:
                wake_up(&sdp->sd_log_waitq);
 
        down_read(&sdp->sd_log_flush_lock);
-
+       if (unlikely(!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))) {
+               gfs2_log_release(sdp, blks);
+               return -EROFS;
+       }
        return 0;
 }
 
@@ -671,7 +691,8 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags)
  *
  */
 
-void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
+void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl,
+                   enum gfs2_flush_type type)
 {
        struct gfs2_trans *tr;
 
@@ -723,6 +744,42 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
        }
        spin_unlock(&sdp->sd_ail_lock);
        gfs2_log_unlock(sdp);
+
+       if (atomic_read(&sdp->sd_log_freeze))
+               type = FREEZE_FLUSH;
+       if (type != NORMAL_FLUSH) {
+               if (!sdp->sd_log_idle) {
+                       for (;;) {
+                               gfs2_ail1_start(sdp);
+                               gfs2_ail1_wait(sdp);
+                               if (gfs2_ail1_empty(sdp))
+                                       break;
+                       }
+                       atomic_dec(&sdp->sd_log_blks_free); /* Adjust for unreserved buffer */
+                       trace_gfs2_log_blocks(sdp, -1);
+                       sdp->sd_log_flush_wrapped = 0;
+                       log_write_header(sdp, 0);
+                       sdp->sd_log_head = sdp->sd_log_flush_head;
+               }
+               if (type == SHUTDOWN_FLUSH || type == FREEZE_FLUSH)
+                       gfs2_log_shutdown(sdp);
+               if (type == FREEZE_FLUSH) {
+                       int error;
+
+                       atomic_set(&sdp->sd_log_freeze, 0);
+                       wake_up(&sdp->sd_log_frozen_wait);
+                       error = gfs2_glock_nq_init(sdp->sd_freeze_gl,
+                                                  LM_ST_SHARED, 0,
+                                                  &sdp->sd_thaw_gh);
+                       if (error) {
+                               printk(KERN_INFO "GFS2: couln't get freeze lock : %d\n", error);
+                               gfs2_assert_withdraw(sdp, 0);
+                       }
+                       else
+                               gfs2_glock_dq_uninit(&sdp->sd_thaw_gh);
+               }
+       }
+
        trace_gfs2_log_flush(sdp, 0);
        up_write(&sdp->sd_log_flush_lock);
 
@@ -761,7 +818,7 @@ static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
        if (sdp->sd_log_tr) {
                gfs2_merge_trans(sdp->sd_log_tr, tr);
        } else if (tr->tr_num_buf_new || tr->tr_num_databuf_new) {
-               gfs2_assert_withdraw(sdp, tr->tr_t_gh.gh_gl);
+               gfs2_assert_withdraw(sdp, tr->tr_alloced);
                sdp->sd_log_tr = tr;
                tr->tr_attached = 1;
        }
@@ -813,8 +870,6 @@ void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
 
 void gfs2_log_shutdown(struct gfs2_sbd *sdp)
 {
-       down_write(&sdp->sd_log_flush_lock);
-
        gfs2_assert_withdraw(sdp, !sdp->sd_log_blks_reserved);
        gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke);
        gfs2_assert_withdraw(sdp, list_empty(&sdp->sd_ail1_list));
@@ -824,38 +879,16 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp)
 
        log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT);
 
-       gfs2_assert_warn(sdp, atomic_read(&sdp->sd_log_blks_free) == sdp->sd_jdesc->jd_blocks);
        gfs2_assert_warn(sdp, sdp->sd_log_head == sdp->sd_log_tail);
        gfs2_assert_warn(sdp, list_empty(&sdp->sd_ail2_list));
 
        sdp->sd_log_head = sdp->sd_log_flush_head;
        sdp->sd_log_tail = sdp->sd_log_head;
-
-       up_write(&sdp->sd_log_flush_lock);
-}
-
-
-/**
- * gfs2_meta_syncfs - sync all the buffers in a filesystem
- * @sdp: the filesystem
- *
- */
-
-void gfs2_meta_syncfs(struct gfs2_sbd *sdp)
-{
-       gfs2_log_flush(sdp, NULL);
-       for (;;) {
-               gfs2_ail1_start(sdp);
-               gfs2_ail1_wait(sdp);
-               if (gfs2_ail1_empty(sdp))
-                       break;
-       }
-       gfs2_log_flush(sdp, NULL);
 }
 
 static inline int gfs2_jrnl_flush_reqd(struct gfs2_sbd *sdp)
 {
-       return (atomic_read(&sdp->sd_log_pinned) >= atomic_read(&sdp->sd_log_thresh1));
+       return (atomic_read(&sdp->sd_log_pinned) >= atomic_read(&sdp->sd_log_thresh1) || atomic_read(&sdp->sd_log_freeze));
 }
 
 static inline int gfs2_ail_flush_reqd(struct gfs2_sbd *sdp)
@@ -882,14 +915,14 @@ int gfs2_logd(void *data)
 
                if (gfs2_jrnl_flush_reqd(sdp) || t == 0) {
                        gfs2_ail1_empty(sdp);
-                       gfs2_log_flush(sdp, NULL);
+                       gfs2_log_flush(sdp, NULL, NORMAL_FLUSH);
                }
 
                if (gfs2_ail_flush_reqd(sdp)) {
                        gfs2_ail1_start(sdp);
                        gfs2_ail1_wait(sdp);
                        gfs2_ail1_empty(sdp);
-                       gfs2_log_flush(sdp, NULL);
+                       gfs2_log_flush(sdp, NULL, NORMAL_FLUSH);
                }
 
                if (!gfs2_ail_flush_reqd(sdp))
index 3721663..9499a60 100644 (file)
@@ -63,14 +63,21 @@ extern void gfs2_ordered_del_inode(struct gfs2_inode *ip);
 extern unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct,
                            unsigned int ssize);
 
+extern void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks);
 extern int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks);
-extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl);
+enum gfs2_flush_type {
+       NORMAL_FLUSH = 0,
+       SYNC_FLUSH,
+       SHUTDOWN_FLUSH,
+       FREEZE_FLUSH
+};
+extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl,
+                          enum gfs2_flush_type type);
 extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans);
 extern void gfs2_remove_from_ail(struct gfs2_bufdata *bd);
 extern void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc);
 
 extern void gfs2_log_shutdown(struct gfs2_sbd *sdp);
-extern void gfs2_meta_syncfs(struct gfs2_sbd *sdp);
 extern int gfs2_logd(void *data);
 extern void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd);
 extern void gfs2_write_revokes(struct gfs2_sbd *sdp);
index a294d8d..2c1ae86 100644 (file)
@@ -75,7 +75,7 @@ static void maybe_release_space(struct gfs2_bufdata *bd)
        unsigned int index = bd->bd_bh->b_blocknr - gl->gl_name.ln_number;
        struct gfs2_bitmap *bi = rgd->rd_bits + index;
 
-       if (bi->bi_clone == 0)
+       if (bi->bi_clone == NULL)
                return;
        if (sdp->sd_args.ar_discard)
                gfs2_rgrp_send_discards(sdp, rgd->rd_data0, bd->bd_bh, bi, 1, NULL);
index 22f9540..bc564c0 100644 (file)
@@ -94,6 +94,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
        INIT_LIST_HEAD(&sdp->sd_jindex_list);
        spin_lock_init(&sdp->sd_jindex_spin);
        mutex_init(&sdp->sd_jindex_mutex);
+       init_completion(&sdp->sd_journal_ready);
 
        INIT_LIST_HEAD(&sdp->sd_quota_list);
        mutex_init(&sdp->sd_quota_mutex);
@@ -129,6 +130,10 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
        init_rwsem(&sdp->sd_log_flush_lock);
        atomic_set(&sdp->sd_log_in_flight, 0);
        init_waitqueue_head(&sdp->sd_log_flush_wait);
+       init_waitqueue_head(&sdp->sd_log_frozen_wait);
+       atomic_set(&sdp->sd_log_freeze, 0);
+       atomic_set(&sdp->sd_frozen_root, 0);
+       init_waitqueue_head(&sdp->sd_frozen_root_wait);
 
        return sdp;
 }
@@ -419,8 +424,8 @@ static int init_locking(struct gfs2_sbd *sdp, struct gfs2_holder *mount_gh,
                goto fail_live;
        }
 
-       error = gfs2_glock_get(sdp, GFS2_TRANS_LOCK, &gfs2_trans_glops,
-                              CREATE, &sdp->sd_trans_gl);
+       error = gfs2_glock_get(sdp, GFS2_FREEZE_LOCK, &gfs2_freeze_glops,
+                              CREATE, &sdp->sd_freeze_gl);
        if (error) {
                fs_err(sdp, "can't create transaction glock: %d\n", error);
                goto fail_rename;
@@ -429,7 +434,7 @@ static int init_locking(struct gfs2_sbd *sdp, struct gfs2_holder *mount_gh,
        return 0;
 
 fail_trans:
-       gfs2_glock_put(sdp->sd_trans_gl);
+       gfs2_glock_put(sdp->sd_freeze_gl);
 fail_rename:
        gfs2_glock_put(sdp->sd_rename_gl);
 fail_live:
@@ -755,7 +760,15 @@ static int init_journal(struct gfs2_sbd *sdp, int undo)
        set_bit(SDF_JOURNAL_CHECKED, &sdp->sd_flags);
        gfs2_glock_dq_uninit(&ji_gh);
        jindex = 0;
-
+       if (!sdp->sd_args.ar_spectator) {
+               error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED, 0,
+                                          &sdp->sd_thaw_gh);
+               if (error) {
+                       fs_err(sdp, "can't acquire freeze glock: %d\n", error);
+                       goto fail_jinode_gh;
+               }
+       }
+       gfs2_glock_dq_uninit(&sdp->sd_thaw_gh);
        return 0;
 
 fail_jinode_gh:
@@ -784,6 +797,7 @@ static int init_inodes(struct gfs2_sbd *sdp, int undo)
                goto fail_qinode;
 
        error = init_journal(sdp, undo);
+       complete_all(&sdp->sd_journal_ready);
        if (error)
                goto fail;
 
@@ -1200,6 +1214,7 @@ fail_sb:
 fail_locking:
        init_locking(sdp, &mount_gh, UNDO);
 fail_lm:
+       complete_all(&sdp->sd_journal_ready);
        gfs2_gl_hash_clear(sdp);
        gfs2_lm_unmount(sdp);
 fail_debug:
@@ -1380,7 +1395,7 @@ static void gfs2_kill_sb(struct super_block *sb)
                return;
        }
 
-       gfs2_meta_syncfs(sdp);
+       gfs2_log_flush(sdp, NULL, SYNC_FLUSH);
        dput(sdp->sd_root_dir);
        dput(sdp->sd_master_dir);
        sdp->sd_root_dir = NULL;
index c4effff..64b29f7 100644 (file)
@@ -778,6 +778,7 @@ get_a_page:
                i_size_write(inode, size);
        inode->i_mtime = inode->i_atime = CURRENT_TIME;
        mark_inode_dirty(inode);
+       set_bit(QDF_REFRESH, &qd->qd_flags);
        return 0;
 
 unlock_out:
@@ -879,7 +880,7 @@ out:
                gfs2_glock_dq_uninit(&ghs[qx]);
        mutex_unlock(&ip->i_inode.i_mutex);
        kfree(ghs);
-       gfs2_log_flush(ip->i_gl->gl_sbd, ip->i_gl);
+       gfs2_log_flush(ip->i_gl->gl_sbd, ip->i_gl, NORMAL_FLUSH);
        return error;
 }
 
index fe7a56f..94555d4 100644 (file)
@@ -454,7 +454,7 @@ void gfs2_recover_func(struct work_struct *work)
        struct gfs2_inode *ip = GFS2_I(jd->jd_inode);
        struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
        struct gfs2_log_header_host head;
-       struct gfs2_holder j_gh, ji_gh, t_gh;
+       struct gfs2_holder j_gh, ji_gh, thaw_gh;
        unsigned long t;
        int ro = 0;
        unsigned int pass;
@@ -508,11 +508,11 @@ void gfs2_recover_func(struct work_struct *work)
 
                t = jiffies;
 
-               /* Acquire a shared hold on the transaction lock */
+               /* Acquire a shared hold on the freeze lock */
 
-               error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED,
-                                          LM_FLAG_NOEXP | LM_FLAG_PRIORITY |
-                                          GL_NOCACHE, &t_gh);
+               error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED,
+                                          LM_FLAG_NOEXP | LM_FLAG_PRIORITY,
+                                          &thaw_gh);
                if (error)
                        goto fail_gunlock_ji;
 
@@ -538,7 +538,7 @@ void gfs2_recover_func(struct work_struct *work)
                        fs_warn(sdp, "jid=%u: Can't replay: read-only block "
                                "device\n", jd->jd_jid);
                        error = -EROFS;
-                       goto fail_gunlock_tr;
+                       goto fail_gunlock_thaw;
                }
 
                fs_info(sdp, "jid=%u: Replaying journal...\n", jd->jd_jid);
@@ -549,14 +549,14 @@ void gfs2_recover_func(struct work_struct *work)
                                                   head.lh_blkno, pass);
                        lops_after_scan(jd, error, pass);
                        if (error)
-                               goto fail_gunlock_tr;
+                               goto fail_gunlock_thaw;
                }
 
                error = clean_journal(jd, &head);
                if (error)
-                       goto fail_gunlock_tr;
+                       goto fail_gunlock_thaw;
 
-               gfs2_glock_dq_uninit(&t_gh);
+               gfs2_glock_dq_uninit(&thaw_gh);
                t = DIV_ROUND_UP(jiffies - t, HZ);
                fs_info(sdp, "jid=%u: Journal replayed in %lus\n",
                        jd->jd_jid, t);
@@ -572,8 +572,8 @@ void gfs2_recover_func(struct work_struct *work)
        fs_info(sdp, "jid=%u: Done\n", jd->jd_jid);
        goto done;
 
-fail_gunlock_tr:
-       gfs2_glock_dq_uninit(&t_gh);
+fail_gunlock_thaw:
+       gfs2_glock_dq_uninit(&thaw_gh);
 fail_gunlock_ji:
        if (jlocked) {
                gfs2_glock_dq_uninit(&ji_gh);
index 281a771..db629d1 100644 (file)
@@ -2001,7 +2001,7 @@ next_rgrp:
                }
                /* Flushing the log may release space */
                if (loops == 2)
-                       gfs2_log_flush(sdp, NULL);
+                       gfs2_log_flush(sdp, NULL, NORMAL_FLUSH);
        }
 
        return -ENOSPC;
index de8afad..1319b5c 100644 (file)
@@ -399,7 +399,7 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
 {
        struct gfs2_inode *ip = GFS2_I(sdp->sd_jdesc->jd_inode);
        struct gfs2_glock *j_gl = ip->i_gl;
-       struct gfs2_holder t_gh;
+       struct gfs2_holder thaw_gh;
        struct gfs2_log_header_host head;
        int error;
 
@@ -407,7 +407,8 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
        if (error)
                return error;
 
-       error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED, 0, &t_gh);
+       error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED, 0,
+                                  &thaw_gh);
        if (error)
                goto fail_threads;
 
@@ -433,13 +434,13 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
 
        set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
 
-       gfs2_glock_dq_uninit(&t_gh);
+       gfs2_glock_dq_uninit(&thaw_gh);
 
        return 0;
 
 fail:
-       t_gh.gh_flags |= GL_NOCACHE;
-       gfs2_glock_dq_uninit(&t_gh);
+       thaw_gh.gh_flags |= GL_NOCACHE;
+       gfs2_glock_dq_uninit(&thaw_gh);
 fail_threads:
        kthread_stop(sdp->sd_quotad_process);
        kthread_stop(sdp->sd_logd_process);
@@ -635,15 +636,21 @@ struct lfcc {
  */
 
 static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp,
-                                   struct gfs2_holder *t_gh)
+                                   struct gfs2_holder *freeze_gh)
 {
        struct gfs2_inode *ip;
        struct gfs2_jdesc *jd;
        struct lfcc *lfcc;
        LIST_HEAD(list);
        struct gfs2_log_header_host lh;
+       struct gfs2_inode *dip = GFS2_I(sdp->sd_root_dir->d_inode);
        int error;
 
+       error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0,
+                                  &sdp->sd_freeze_root_gh);
+       if (error)
+               return error;
+       atomic_set(&sdp->sd_frozen_root, 1);
        list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) {
                lfcc = kmalloc(sizeof(struct lfcc), GFP_KERNEL);
                if (!lfcc) {
@@ -659,8 +666,8 @@ static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp,
                list_add(&lfcc->list, &list);
        }
 
-       error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_DEFERRED,
-                                  GL_NOCACHE, t_gh);
+       error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_EXCLUSIVE,
+                                  GL_NOCACHE, freeze_gh);
 
        list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) {
                error = gfs2_jdesc_check(jd);
@@ -676,7 +683,7 @@ static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp,
        }
 
        if (error)
-               gfs2_glock_dq_uninit(t_gh);
+               gfs2_glock_dq_uninit(freeze_gh);
 
 out:
        while (!list_empty(&list)) {
@@ -685,6 +692,11 @@ out:
                gfs2_glock_dq_uninit(&lfcc->gh);
                kfree(lfcc);
        }
+       if (error) {
+               atomic_dec(&sdp->sd_frozen_root);
+               wait_event(sdp->sd_frozen_root_wait, atomic_read(&sdp->sd_frozen_root) == 0);
+               gfs2_glock_dq_uninit(&sdp->sd_freeze_root_gh);
+       }
        return error;
 }
 
@@ -742,7 +754,7 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc)
        int ret = 0;
 
        if (wbc->sync_mode == WB_SYNC_ALL)
-               gfs2_log_flush(GFS2_SB(inode), ip->i_gl);
+               gfs2_log_flush(GFS2_SB(inode), ip->i_gl, NORMAL_FLUSH);
        if (bdi->dirty_exceeded)
                gfs2_ail1_flush(sdp, wbc);
        else
@@ -822,9 +834,18 @@ out:
 
 static int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
 {
-       struct gfs2_holder t_gh;
+       struct gfs2_holder thaw_gh;
        int error;
 
+       error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED, GL_NOCACHE,
+                                  &thaw_gh);
+       if (error && !test_bit(SDF_SHUTDOWN, &sdp->sd_flags))
+               return error;
+
+       down_write(&sdp->sd_log_flush_lock);
+       clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
+       up_write(&sdp->sd_log_flush_lock);
+
        kthread_stop(sdp->sd_quotad_process);
        kthread_stop(sdp->sd_logd_process);
 
@@ -832,18 +853,11 @@ static int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
        gfs2_quota_sync(sdp->sd_vfs, 0);
        gfs2_statfs_sync(sdp->sd_vfs, 0);
 
-       error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED, GL_NOCACHE,
-                                  &t_gh);
-       if (error && !test_bit(SDF_SHUTDOWN, &sdp->sd_flags))
-               return error;
-
-       gfs2_meta_syncfs(sdp);
-       gfs2_log_shutdown(sdp);
-
-       clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
+       gfs2_log_flush(sdp, NULL, SHUTDOWN_FLUSH);
+       gfs2_assert_warn(sdp, atomic_read(&sdp->sd_log_blks_free) == sdp->sd_jdesc->jd_blocks);
 
-       if (t_gh.gh_gl)
-               gfs2_glock_dq_uninit(&t_gh);
+       if (thaw_gh.gh_gl)
+               gfs2_glock_dq_uninit(&thaw_gh);
 
        gfs2_quota_cleanup(sdp);
 
@@ -900,7 +914,7 @@ restart:
        iput(sdp->sd_quota_inode);
 
        gfs2_glock_put(sdp->sd_rename_gl);
-       gfs2_glock_put(sdp->sd_trans_gl);
+       gfs2_glock_put(sdp->sd_freeze_gl);
 
        if (!sdp->sd_args.ar_spectator) {
                gfs2_glock_dq_uninit(&sdp->sd_journal_gh);
@@ -935,8 +949,8 @@ static int gfs2_sync_fs(struct super_block *sb, int wait)
        struct gfs2_sbd *sdp = sb->s_fs_info;
 
        gfs2_quota_sync(sb, -1);
-       if (wait && sdp)
-               gfs2_log_flush(sdp, NULL);
+       if (wait && sdp && !atomic_read(&sdp->sd_log_freeze))
+               gfs2_log_flush(sdp, NULL, NORMAL_FLUSH);
        return 0;
 }
 
@@ -986,6 +1000,9 @@ static int gfs2_unfreeze(struct super_block *sb)
        struct gfs2_sbd *sdp = sb->s_fs_info;
 
        gfs2_glock_dq_uninit(&sdp->sd_freeze_gh);
+       atomic_dec(&sdp->sd_frozen_root);
+       wait_event(sdp->sd_frozen_root_wait, atomic_read(&sdp->sd_frozen_root) == 0);
+       gfs2_glock_dq_uninit(&sdp->sd_freeze_root_gh);
        return 0;
 }
 
@@ -1525,7 +1542,7 @@ static void gfs2_evict_inode(struct inode *inode)
        goto out_unlock;
 
 out_truncate:
-       gfs2_log_flush(sdp, ip->i_gl);
+       gfs2_log_flush(sdp, ip->i_gl, NORMAL_FLUSH);
        if (test_bit(GLF_DIRTY, &ip->i_gl->gl_flags)) {
                struct address_space *metamapping = gfs2_glock2aspace(ip->i_gl);
                filemap_fdatawrite(metamapping);
index 529d9a9..3ab566b 100644 (file)
@@ -240,8 +240,8 @@ static ssize_t demote_rq_store(struct gfs2_sbd *sdp, const char *buf, size_t len
 
        if (gltype > LM_TYPE_JOURNAL)
                return -EINVAL;
-       if (gltype == LM_TYPE_NONDISK && glnum == GFS2_TRANS_LOCK)
-               glops = &gfs2_trans_glops;
+       if (gltype == LM_TYPE_NONDISK && glnum == GFS2_FREEZE_LOCK)
+               glops = &gfs2_freeze_glops;
        else
                glops = gfs2_glops_list[gltype];
        if (glops == NULL)
@@ -407,6 +407,9 @@ int gfs2_recover_set(struct gfs2_sbd *sdp, unsigned jid)
        struct gfs2_jdesc *jd;
        int rv;
 
+       /* Wait for our primary journal to be initialized */
+       wait_for_completion(&sdp->sd_journal_ready);
+
        spin_lock(&sdp->sd_jindex_spin);
        rv = -EBUSY;
        if (sdp->sd_jdesc->jd_jid == jid)
index bead90d..0546ab4 100644 (file)
@@ -48,6 +48,7 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
        tr->tr_blocks = blocks;
        tr->tr_revokes = revokes;
        tr->tr_reserved = 1;
+       tr->tr_alloced = 1;
        if (blocks)
                tr->tr_reserved += 6 + blocks;
        if (revokes)
@@ -57,48 +58,22 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
        INIT_LIST_HEAD(&tr->tr_buf);
 
        sb_start_intwrite(sdp->sd_vfs);
-       gfs2_holder_init(sdp->sd_trans_gl, LM_ST_SHARED, 0, &tr->tr_t_gh);
-
-       error = gfs2_glock_nq(&tr->tr_t_gh);
-       if (error)
-               goto fail_holder_uninit;
 
        error = gfs2_log_reserve(sdp, tr->tr_reserved);
        if (error)
-               goto fail_gunlock;
+               goto fail;
 
        current->journal_info = tr;
 
        return 0;
 
-fail_gunlock:
-       gfs2_glock_dq(&tr->tr_t_gh);
-
-fail_holder_uninit:
+fail:
        sb_end_intwrite(sdp->sd_vfs);
-       gfs2_holder_uninit(&tr->tr_t_gh);
        kfree(tr);
 
        return error;
 }
 
-/**
- * gfs2_log_release - Release a given number of log blocks
- * @sdp: The GFS2 superblock
- * @blks: The number of blocks
- *
- */
-
-static void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks)
-{
-
-       atomic_add(blks, &sdp->sd_log_blks_free);
-       trace_gfs2_log_blocks(sdp, blks);
-       gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <=
-                                 sdp->sd_jdesc->jd_blocks);
-       up_read(&sdp->sd_log_flush_lock);
-}
-
 static void gfs2_print_trans(const struct gfs2_trans *tr)
 {
        pr_warn("Transaction created at: %pSR\n", (void *)tr->tr_ip);
@@ -119,11 +94,8 @@ void gfs2_trans_end(struct gfs2_sbd *sdp)
 
        if (!tr->tr_touched) {
                gfs2_log_release(sdp, tr->tr_reserved);
-               if (tr->tr_t_gh.gh_gl) {
-                       gfs2_glock_dq(&tr->tr_t_gh);
-                       gfs2_holder_uninit(&tr->tr_t_gh);
+               if (tr->tr_alloced)
                        kfree(tr);
-               }
                sb_end_intwrite(sdp->sd_vfs);
                return;
        }
@@ -137,16 +109,12 @@ void gfs2_trans_end(struct gfs2_sbd *sdp)
                gfs2_print_trans(tr);
 
        gfs2_log_commit(sdp, tr);
-       if (tr->tr_t_gh.gh_gl) {
-               gfs2_glock_dq(&tr->tr_t_gh);
-               gfs2_holder_uninit(&tr->tr_t_gh);
-               if (!tr->tr_attached)
+       if (tr->tr_alloced && !tr->tr_attached)
                        kfree(tr);
-       }
        up_read(&sdp->sd_log_flush_lock);
 
        if (sdp->sd_vfs->s_flags & MS_SYNCHRONOUS)
-               gfs2_log_flush(sdp, NULL);
+               gfs2_log_flush(sdp, NULL, NORMAL_FLUSH);
        sb_end_intwrite(sdp->sd_vfs);
 }
 
index 5a8ea16..0c8ca83 100644 (file)
@@ -83,13 +83,15 @@ static int __jfs_set_acl(tid_t tid, struct inode *inode, int type,
        switch (type) {
        case ACL_TYPE_ACCESS:
                ea_name = POSIX_ACL_XATTR_ACCESS;
-               rc = posix_acl_equiv_mode(acl, &inode->i_mode);
-               if (rc < 0)
-                       return rc;
-               inode->i_ctime = CURRENT_TIME;
-               mark_inode_dirty(inode);
-               if (rc == 0)
-                       acl = NULL;
+               if (acl) {
+                       rc = posix_acl_equiv_mode(acl, &inode->i_mode);
+                       if (rc < 0)
+                               return rc;
+                       inode->i_ctime = CURRENT_TIME;
+                       mark_inode_dirty(inode);
+                       if (rc == 0)
+                               acl = NULL;
+               }
                break;
        case ACL_TYPE_DEFAULT:
                ea_name = POSIX_ACL_XATTR_DEFAULT;
index 370d7b6..2d514c7 100644 (file)
@@ -1208,7 +1208,7 @@ static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
                                 * by this leaf.
                                 */
                                l2size =
-                                   min((int)leaf[word], NLSTOL2BSZ(nwords));
+                                   min_t(int, leaf[word], NLSTOL2BSZ(nwords));
 
                                /* determine how many words were handled.
                                 */
@@ -1902,7 +1902,7 @@ dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno, s64 * results)
 
                /* determine how many blocks to allocate from this dmap.
                 */
-               nb = min(n, (s64)BPERDMAP);
+               nb = min_t(s64, n, BPERDMAP);
 
                /* allocate the blocks from the dmap.
                 */
@@ -2260,7 +2260,8 @@ static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
                                 * of bits being allocated and the l2 number
                                 * of bits currently described by this leaf.
                                 */
-                               size = min((int)leaf[word], NLSTOL2BSZ(nwords));
+                               size = min_t(int, leaf[word],
+                                            NLSTOL2BSZ(nwords));
 
                                /* update the leaf to reflect the allocation.
                                 * in addition to setting the leaf value to
@@ -3563,7 +3564,7 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno,   s64 nblocks)
                                        if (mp == NULL)
                                                goto errout;
 
-                                       n = min(nblocks, (s64)BPERDMAP);
+                                       n = min_t(s64, nblocks, BPERDMAP);
                                }
 
                                dp = (struct dmap *) mp->data;
index 7f464c5..6b0f816 100644 (file)
 void jfs_set_inode_flags(struct inode *inode)
 {
        unsigned int flags = JFS_IP(inode)->mode2;
-
-       inode->i_flags &= ~(S_IMMUTABLE | S_APPEND |
-               S_NOATIME | S_DIRSYNC | S_SYNC);
+       unsigned int new_fl = 0;
 
        if (flags & JFS_IMMUTABLE_FL)
-               inode->i_flags |= S_IMMUTABLE;
+               new_fl |= S_IMMUTABLE;
        if (flags & JFS_APPEND_FL)
-               inode->i_flags |= S_APPEND;
+               new_fl |= S_APPEND;
        if (flags & JFS_NOATIME_FL)
-               inode->i_flags |= S_NOATIME;
+               new_fl |= S_NOATIME;
        if (flags & JFS_DIRSYNC_FL)
-               inode->i_flags |= S_DIRSYNC;
+               new_fl |= S_DIRSYNC;
        if (flags & JFS_SYNC_FL)
-               inode->i_flags |= S_SYNC;
+               new_fl |= S_SYNC;
+       inode_set_flags(inode, new_fl, S_IMMUTABLE | S_APPEND | S_NOATIME |
+                       S_DIRSYNC | S_SYNC);
 }
 
 void jfs_get_inode_flags(struct jfs_inode_info *jfs_ip)
index 8d811e0..0acddf6 100644 (file)
@@ -167,7 +167,7 @@ do {                                                \
  * Global list of active external journals
  */
 static LIST_HEAD(jfs_external_logs);
-static struct jfs_log *dummy_log = NULL;
+static struct jfs_log *dummy_log;
 static DEFINE_MUTEX(jfs_log_mutex);
 
 /*
index 97f7fda..adf8cb0 100644 (file)
@@ -50,14 +50,14 @@ MODULE_DESCRIPTION("The Journaled Filesystem (JFS)");
 MODULE_AUTHOR("Steve Best/Dave Kleikamp/Barry Arndt, IBM");
 MODULE_LICENSE("GPL");
 
-static struct kmem_cache * jfs_inode_cachep;
+static struct kmem_cache *jfs_inode_cachep;
 
 static const struct super_operations jfs_super_operations;
 static const struct export_operations jfs_export_operations;
 static struct file_system_type jfs_fs_type;
 
 #define MAX_COMMIT_THREADS 64
-static int commit_threads = 0;
+static int commit_threads;
 module_param(commit_threads, int, 0);
 MODULE_PARM_DESC(commit_threads, "Number of commit threads");
 
@@ -84,8 +84,7 @@ static void jfs_handle_error(struct super_block *sb)
                panic("JFS (device %s): panic forced after error\n",
                        sb->s_id);
        else if (sbi->flag & JFS_ERR_REMOUNT_RO) {
-               jfs_err("ERROR: (device %s): remounting filesystem "
-                       "as read-only\n",
+               jfs_err("ERROR: (device %s): remounting filesystem as read-only\n",
                        sb->s_id);
                sb->s_flags |= MS_RDONLY;
        }
@@ -273,7 +272,10 @@ static int parse_options(char *options, struct super_block *sb, s64 *newLVSize,
                case Opt_resize:
                {
                        char *resize = args[0].from;
-                       *newLVSize = simple_strtoull(resize, &resize, 0);
+                       int rc = kstrtoll(resize, 0, newLVSize);
+
+                       if (rc)
+                               goto cleanup;
                        break;
                }
                case Opt_resize_nosize:
@@ -327,7 +329,11 @@ static int parse_options(char *options, struct super_block *sb, s64 *newLVSize,
                case Opt_uid:
                {
                        char *uid = args[0].from;
-                       uid_t val = simple_strtoul(uid, &uid, 0);
+                       uid_t val;
+                       int rc = kstrtouint(uid, 0, &val);
+
+                       if (rc)
+                               goto cleanup;
                        sbi->uid = make_kuid(current_user_ns(), val);
                        if (!uid_valid(sbi->uid))
                                goto cleanup;
@@ -337,7 +343,11 @@ static int parse_options(char *options, struct super_block *sb, s64 *newLVSize,
                case Opt_gid:
                {
                        char *gid = args[0].from;
-                       gid_t val = simple_strtoul(gid, &gid, 0);
+                       gid_t val;
+                       int rc = kstrtouint(gid, 0, &val);
+
+                       if (rc)
+                               goto cleanup;
                        sbi->gid = make_kgid(current_user_ns(), val);
                        if (!gid_valid(sbi->gid))
                                goto cleanup;
@@ -347,7 +357,10 @@ static int parse_options(char *options, struct super_block *sb, s64 *newLVSize,
                case Opt_umask:
                {
                        char *umask = args[0].from;
-                       sbi->umask = simple_strtoul(umask, &umask, 8);
+                       int rc = kstrtouint(umask, 8, &sbi->umask);
+
+                       if (rc)
+                               goto cleanup;
                        if (sbi->umask & ~0777) {
                                pr_err("JFS: Invalid value of umask\n");
                                goto cleanup;
@@ -363,12 +376,10 @@ static int parse_options(char *options, struct super_block *sb, s64 *newLVSize,
                         * -> user has more control over the online trimming
                         */
                        sbi->minblks_trim = 64;
-                       if (blk_queue_discard(q)) {
+                       if (blk_queue_discard(q))
                                *flag |= JFS_DISCARD;
-                       } else {
-                               pr_err("JFS: discard option " \
-                                       "not supported on device\n");
-                       }
+                       else
+                               pr_err("JFS: discard option not supported on device\n");
                        break;
                }
 
@@ -380,20 +391,21 @@ static int parse_options(char *options, struct super_block *sb, s64 *newLVSize,
                {
                        struct request_queue *q = bdev_get_queue(sb->s_bdev);
                        char *minblks_trim = args[0].from;
+                       int rc;
                        if (blk_queue_discard(q)) {
                                *flag |= JFS_DISCARD;
-                               sbi->minblks_trim = simple_strtoull(
-                                       minblks_trim, &minblks_trim, 0);
-                       } else {
-                               pr_err("JFS: discard option " \
-                                       "not supported on device\n");
-                       }
+                               rc = kstrtouint(minblks_trim, 0,
+                                               &sbi->minblks_trim);
+                               if (rc)
+                                       goto cleanup;
+                       } else
+                               pr_err("JFS: discard option not supported on device\n");
                        break;
                }
 
                default:
-                       printk("jfs: Unrecognized mount option \"%s\" "
-                                       " or missing value\n", p);
+                       printk("jfs: Unrecognized mount option \"%s\" or missing value\n",
+                              p);
                        goto cleanup;
                }
        }
@@ -419,14 +431,12 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data)
        int ret;
 
        sync_filesystem(sb);
-       if (!parse_options(data, sb, &newLVSize, &flag)) {
+       if (!parse_options(data, sb, &newLVSize, &flag))
                return -EINVAL;
-       }
 
        if (newLVSize) {
                if (sb->s_flags & MS_RDONLY) {
-                       pr_err("JFS: resize requires volume" \
-                               " to be mounted read-write\n");
+                       pr_err("JFS: resize requires volume to be mounted read-write\n");
                        return -EROFS;
                }
                rc = jfs_extendfs(sb, newLVSize, 0);
@@ -452,9 +462,8 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data)
        }
        if ((!(sb->s_flags & MS_RDONLY)) && (*flags & MS_RDONLY)) {
                rc = dquot_suspend(sb, -1);
-               if (rc < 0) {
+               if (rc < 0)
                        return rc;
-               }
                rc = jfs_umount_rw(sb);
                JFS_SBI(sb)->flag = flag;
                return rc;
@@ -487,7 +496,7 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
        if (!new_valid_dev(sb->s_bdev->bd_dev))
                return -EOVERFLOW;
 
-       sbi = kzalloc(sizeof (struct jfs_sb_info), GFP_KERNEL);
+       sbi = kzalloc(sizeof(struct jfs_sb_info), GFP_KERNEL);
        if (!sbi)
                return -ENOMEM;
 
@@ -548,9 +557,8 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
 
        rc = jfs_mount(sb);
        if (rc) {
-               if (!silent) {
+               if (!silent)
                        jfs_err("jfs_mount failed w/return code = %d", rc);
-               }
                goto out_mount_failed;
        }
        if (sb->s_flags & MS_RDONLY)
@@ -587,7 +595,8 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
         * Page cache is indexed by long.
         * I would use MAX_LFS_FILESIZE, but it's only half as big
         */
-       sb->s_maxbytes = min(((u64) PAGE_CACHE_SIZE << 32) - 1, (u64)sb->s_maxbytes);
+       sb->s_maxbytes = min(((u64) PAGE_CACHE_SIZE << 32) - 1,
+                            (u64)sb->s_maxbytes);
 #endif
        sb->s_time_gran = 1;
        return 0;
@@ -597,9 +606,8 @@ out_no_root:
 
 out_no_rw:
        rc = jfs_umount(sb);
-       if (rc) {
+       if (rc)
                jfs_err("jfs_umount failed with return code %d", rc);
-       }
 out_mount_failed:
        filemap_write_and_wait(sbi->direct_inode->i_mapping);
        truncate_inode_pages(sbi->direct_inode->i_mapping, 0);
@@ -924,7 +932,8 @@ static int __init init_jfs_fs(void)
                commit_threads = MAX_COMMIT_THREADS;
 
        for (i = 0; i < commit_threads; i++) {
-               jfsCommitThread[i] = kthread_run(jfs_lazycommit, NULL, "jfsCommit");
+               jfsCommitThread[i] = kthread_run(jfs_lazycommit, NULL,
+                                                "jfsCommit");
                if (IS_ERR(jfsCommitThread[i])) {
                        rc = PTR_ERR(jfsCommitThread[i]);
                        jfs_err("init_jfs_fs: fork failed w/rc = %d", rc);
index e390bd9..da57c9b 100644 (file)
 #include <linux/percpu.h>
 #include <linux/lglock.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/filelock.h>
+
 #include <asm/uaccess.h>
 
 #define IS_POSIX(fl)   (fl->fl_flags & FL_POSIX)
@@ -322,6 +325,7 @@ static int flock_make_lock(struct file *filp, struct file_lock **lock,
                return -ENOMEM;
 
        fl->fl_file = filp;
+       fl->fl_owner = (fl_owner_t)filp;
        fl->fl_pid = current->tgid;
        fl->fl_flags = FL_FLOCK;
        fl->fl_type = type;
@@ -427,7 +431,7 @@ static int lease_init(struct file *filp, long type, struct file_lock *fl)
        if (assign_type(fl, type) != 0)
                return -EINVAL;
 
-       fl->fl_owner = current->files;
+       fl->fl_owner = (fl_owner_t)filp;
        fl->fl_pid = current->tgid;
 
        fl->fl_file = filp;
@@ -1286,6 +1290,7 @@ static void time_out_leases(struct inode *inode)
 
        before = &inode->i_flock;
        while ((fl = *before) && IS_LEASE(fl) && lease_breaking(fl)) {
+               trace_time_out_leases(inode, fl);
                if (past_time(fl->fl_downgrade_time))
                        lease_modify(before, F_RDLCK);
                if (past_time(fl->fl_break_time))
@@ -1373,6 +1378,7 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
        }
 
        if (i_have_this_lease || (mode & O_NONBLOCK)) {
+               trace_break_lease_noblock(inode, new_fl);
                error = -EWOULDBLOCK;
                goto out;
        }
@@ -1384,10 +1390,12 @@ restart:
        if (break_time == 0)
                break_time++;
        locks_insert_block(flock, new_fl);
+       trace_break_lease_block(inode, new_fl);
        spin_unlock(&inode->i_lock);
        error = wait_event_interruptible_timeout(new_fl->fl_wait,
                                                !new_fl->fl_next, break_time);
        spin_lock(&inode->i_lock);
+       trace_break_lease_unblock(inode, new_fl);
        locks_delete_block(new_fl);
        if (error >= 0) {
                if (error == 0)
@@ -1509,6 +1517,8 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
        int error;
 
        lease = *flp;
+       trace_generic_add_lease(inode, lease);
+
        /*
         * In the delegation case we need mutual exclusion with
         * a number of operations that take the i_mutex.  We trylock
@@ -1598,6 +1608,8 @@ static int generic_delete_lease(struct file *filp, struct file_lock **flp)
        struct dentry *dentry = filp->f_path.dentry;
        struct inode *inode = dentry->d_inode;
 
+       trace_generic_delete_lease(inode, *flp);
+
        for (before = &inode->i_flock;
                        ((fl = *before) != NULL) && IS_LEASE(fl);
                        before = &fl->fl_next) {
@@ -2316,6 +2328,7 @@ void locks_remove_file(struct file *filp)
 
        if (filp->f_op->flock) {
                struct file_lock fl = {
+                       .fl_owner = (fl_owner_t)filp,
                        .fl_pid = current->tgid,
                        .fl_file = filp,
                        .fl_flags = FL_FLOCK,
@@ -2423,31 +2436,31 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
        seq_printf(f, "%lld:%s ", id, pfx);
        if (IS_POSIX(fl)) {
                if (fl->fl_flags & FL_ACCESS)
-                       seq_printf(f, "ACCESS");
+                       seq_puts(f, "ACCESS");
                else if (IS_OFDLCK(fl))
-                       seq_printf(f, "OFDLCK");
+                       seq_puts(f, "OFDLCK");
                else
-                       seq_printf(f, "POSIX ");
+                       seq_puts(f, "POSIX ");
 
                seq_printf(f, " %s ",
                             (inode == NULL) ? "*NOINODE*" :
                             mandatory_lock(inode) ? "MANDATORY" : "ADVISORY ");
        } else if (IS_FLOCK(fl)) {
                if (fl->fl_type & LOCK_MAND) {
-                       seq_printf(f, "FLOCK  MSNFS     ");
+                       seq_puts(f, "FLOCK  MSNFS     ");
                } else {
-                       seq_printf(f, "FLOCK  ADVISORY  ");
+                       seq_puts(f, "FLOCK  ADVISORY  ");
                }
        } else if (IS_LEASE(fl)) {
-               seq_printf(f, "LEASE  ");
+               seq_puts(f, "LEASE  ");
                if (lease_breaking(fl))
-                       seq_printf(f, "BREAKING  ");
+                       seq_puts(f, "BREAKING  ");
                else if (fl->fl_file)
-                       seq_printf(f, "ACTIVE    ");
+                       seq_puts(f, "ACTIVE    ");
                else
-                       seq_printf(f, "BREAKER   ");
+                       seq_puts(f, "BREAKER   ");
        } else {
-               seq_printf(f, "UNKNOWN UNKNOWN  ");
+               seq_puts(f, "UNKNOWN UNKNOWN  ");
        }
        if (fl->fl_type & LOCK_MAND) {
                seq_printf(f, "%s ",
@@ -2479,7 +2492,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
                else
                        seq_printf(f, "%Ld %Ld\n", fl->fl_start, fl->fl_end);
        } else {
-               seq_printf(f, "0 EOF\n");
+               seq_puts(f, "0 EOF\n");
        }
 }
 
index 284ca90..c1edf73 100644 (file)
@@ -916,10 +916,6 @@ int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
                is_local = 1;
 
        /* We're simulating flock() locks using posix locks on the server */
-       fl->fl_owner = (fl_owner_t)filp;
-       fl->fl_start = 0;
-       fl->fl_end = OFFSET_MAX;
-
        if (fl->fl_type == F_UNLCK)
                return do_unlk(filp, cmd, fl, is_local);
        return do_setlk(filp, cmd, fl, is_local);
index ca0cb60..a08e55a 100644 (file)
@@ -62,8 +62,6 @@
 #include <acpi/acrestyp.h>             /* Resource Descriptor structs */
 #include <acpi/acpiosxf.h>             /* OSL interfaces (ACPICA-to-OS) */
 #include <acpi/acpixf.h>               /* ACPI core subsystem external interfaces */
-#ifdef ACPI_NATIVE_INTERFACE_HEADER
-#include ACPI_NATIVE_INTERFACE_HEADER
-#endif
+#include <acpi/platform/acenvex.h>     /* Extra environment-specific items */
 
 #endif                         /* __ACPI_H__ */
index 84a2e29..b571458 100644 (file)
@@ -131,6 +131,7 @@ static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile(
 struct acpi_scan_handler {
        const struct acpi_device_id *ids;
        struct list_head list_node;
+       bool (*match)(char *idstr, const struct acpi_device_id **matchid);
        int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id);
        void (*detach)(struct acpi_device *dev);
        void (*bind)(struct device *phys_dev);
@@ -232,7 +233,8 @@ struct acpi_hardware_id {
 struct acpi_pnp_type {
        u32 hardware_id:1;
        u32 bus_address:1;
-       u32 reserved:30;
+       u32 platform_id:1;
+       u32 reserved:29;
 };
 
 struct acpi_device_pnp {
@@ -261,7 +263,8 @@ struct acpi_device_power_flags {
        u32 inrush_current:1;   /* Serialize Dx->D0 */
        u32 power_removed:1;    /* Optimize Dx->D0 */
        u32 ignore_parent:1;    /* Power is independent of parent power state */
-       u32 reserved:27;
+       u32 dsw_present:1;      /* _DSW present? */
+       u32 reserved:26;
 };
 
 struct acpi_device_power_state {
@@ -406,6 +409,8 @@ extern struct kobject *acpi_kobj;
 extern int acpi_bus_generate_netlink_event(const char*, const char*, u8, int);
 void acpi_bus_private_data_handler(acpi_handle, void *);
 int acpi_bus_get_private_data(acpi_handle, void **);
+int acpi_bus_attach_private_data(acpi_handle, void *);
+void acpi_bus_detach_private_data(acpi_handle);
 void acpi_bus_no_hotplug(acpi_handle handle);
 extern int acpi_notifier_call_chain(struct acpi_device *, u32, u32);
 extern int register_acpi_notifier(struct notifier_block *);
index d504613..ea6428b 100644 (file)
@@ -96,7 +96,12 @@ struct pci_dev *acpi_get_pci_dev(acpi_handle);
 /* Arch-defined function to add a bus to the system */
 
 struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root);
+
+#ifdef CONFIG_X86
 void pci_acpi_crs_quirks(void);
+#else
+static inline void pci_acpi_crs_quirks(void) { }
+#endif
 
 /* --------------------------------------------------------------------------
                                     Processor
index 2be8580..444671e 100644 (file)
@@ -9,6 +9,9 @@ static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys,
        return ioremap_cache(phys, size);
 }
 
+void __iomem *__init_refok
+acpi_os_map_iomem(acpi_physical_address phys, acpi_size size);
+void __ref acpi_os_unmap_iomem(void __iomem *virt, acpi_size size);
 void __iomem *acpi_os_get_iomem(acpi_physical_address phys, unsigned int size);
 
 int acpi_os_map_generic_address(struct acpi_generic_address *addr);
index 44f5e97..35b525c 100644 (file)
@@ -46,7 +46,7 @@
 
 /* Current ACPICA subsystem version in YYYYMMDD format */
 
-#define ACPI_CA_VERSION                 0x20140214
+#define ACPI_CA_VERSION                 0x20140424
 
 #include <acpi/acconfig.h>
 #include <acpi/actypes.h>
 
 extern u8 acpi_gbl_permanent_mmap;
 
+/*****************************************************************************
+ *
+ * Macros used for ACPICA globals and configuration
+ *
+ ****************************************************************************/
+
 /*
- * Globals that are publically available
+ * Ensure that global variables are defined and initialized only once.
+ *
+ * The use of these macros allows for a single list of globals (here)
+ * in order to simplify maintenance of the code.
  */
-extern u32 acpi_current_gpe_count;
-extern struct acpi_table_fadt acpi_gbl_FADT;
-extern u8 acpi_gbl_system_awake_and_running;
-extern u8 acpi_gbl_reduced_hardware;   /* ACPI 5.0 */
-extern u8 acpi_gbl_osi_data;
+#ifdef DEFINE_ACPI_GLOBALS
+#define ACPI_GLOBAL(type,name) \
+       extern type name; \
+       type name
 
-/* Runtime configuration of debug print levels */
+#define ACPI_INIT_GLOBAL(type,name,value) \
+       type name=value
 
-extern u32 acpi_dbg_level;
-extern u32 acpi_dbg_layer;
-
-/* ACPICA runtime options */
+#else
+#ifndef ACPI_GLOBAL
+#define ACPI_GLOBAL(type,name) \
+       extern type name
+#endif
 
-extern u8 acpi_gbl_auto_serialize_methods;
-extern u8 acpi_gbl_copy_dsdt_locally;
-extern u8 acpi_gbl_create_osi_method;
-extern u8 acpi_gbl_disable_auto_repair;
-extern u8 acpi_gbl_disable_ssdt_table_load;
-extern u8 acpi_gbl_do_not_use_xsdt;
-extern u8 acpi_gbl_enable_aml_debug_object;
-extern u8 acpi_gbl_enable_interpreter_slack;
-extern u32 acpi_gbl_trace_flags;
-extern acpi_name acpi_gbl_trace_method_name;
-extern u8 acpi_gbl_truncate_io_addresses;
-extern u8 acpi_gbl_use32_bit_fadt_addresses;
-extern u8 acpi_gbl_use_default_register_widths;
+#ifndef ACPI_INIT_GLOBAL
+#define ACPI_INIT_GLOBAL(type,name,value) \
+       extern type name
+#endif
+#endif
 
 /*
- * Hardware-reduced prototypes. All interfaces that use these macros will
- * be configured out of the ACPICA build if the ACPI_REDUCED_HARDWARE flag
- * is set to TRUE.
+ * These macros configure the various ACPICA interfaces. They are
+ * useful for generating stub inline functions for features that are
+ * configured out of the current kernel or ACPICA application.
  */
-#if (!ACPI_REDUCED_HARDWARE)
-#define ACPI_HW_DEPENDENT_RETURN_STATUS(prototype) \
+#ifndef ACPI_EXTERNAL_RETURN_STATUS
+#define ACPI_EXTERNAL_RETURN_STATUS(prototype) \
        prototype;
+#endif
 
-#define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \
+#ifndef ACPI_EXTERNAL_RETURN_OK
+#define ACPI_EXTERNAL_RETURN_OK(prototype) \
        prototype;
+#endif
 
-#define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \
+#ifndef ACPI_EXTERNAL_RETURN_VOID
+#define ACPI_EXTERNAL_RETURN_VOID(prototype) \
        prototype;
+#endif
 
-#else
-#define ACPI_HW_DEPENDENT_RETURN_STATUS(prototype) \
-       static ACPI_INLINE prototype {return(AE_NOT_CONFIGURED);}
-
-#define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \
-       static ACPI_INLINE prototype {return(AE_OK);}
+#ifndef ACPI_EXTERNAL_RETURN_UINT32
+#define ACPI_EXTERNAL_RETURN_UINT32(prototype) \
+       prototype;
+#endif
 
-#define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \
-       static ACPI_INLINE prototype {return;}
+#ifndef ACPI_EXTERNAL_RETURN_PTR
+#define ACPI_EXTERNAL_RETURN_PTR(prototype) \
+       prototype;
+#endif
 
-#endif                         /* !ACPI_REDUCED_HARDWARE */
+/*****************************************************************************
+ *
+ * Public globals and runtime configuration options
+ *
+ ****************************************************************************/
 
 /*
- * Initialization
+ * Enable "slack mode" of the AML interpreter?  Default is FALSE, and the
+ * interpreter strictly follows the ACPI specification. Setting to TRUE
+ * allows the interpreter to ignore certain errors and/or bad AML constructs.
+ *
+ * Currently, these features are enabled by this flag:
+ *
+ * 1) Allow "implicit return" of last value in a control method
+ * 2) Allow access beyond the end of an operation region
+ * 3) Allow access to uninitialized locals/args (auto-init to integer 0)
+ * 4) Allow ANY object type to be a source operand for the Store() operator
+ * 5) Allow unresolved references (invalid target name) in package objects
+ * 6) Enable warning messages for behavior that is not ACPI spec compliant
  */
-acpi_status __init
-acpi_initialize_tables(struct acpi_table_desc *initial_storage,
-                      u32 initial_table_count, u8 allow_resize);
-
-acpi_status __init acpi_initialize_subsystem(void);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_enable_interpreter_slack, FALSE);
 
-acpi_status __init acpi_enable_subsystem(u32 flags);
-
-acpi_status __init acpi_initialize_objects(u32 flags);
+/*
+ * Automatically serialize all methods that create named objects? Default
+ * is TRUE, meaning that all non_serialized methods are scanned once at
+ * table load time to determine those that create named objects. Methods
+ * that create named objects are marked Serialized in order to prevent
+ * possible run-time problems if they are entered by more than one thread.
+ */
+ACPI_INIT_GLOBAL(u8, acpi_gbl_auto_serialize_methods, TRUE);
 
-acpi_status __init acpi_terminate(void);
+/*
+ * Create the predefined _OSI method in the namespace? Default is TRUE
+ * because ACPICA is fully compatible with other ACPI implementations.
+ * Changing this will revert ACPICA (and machine ASL) to pre-OSI behavior.
+ */
+ACPI_INIT_GLOBAL(u8, acpi_gbl_create_osi_method, TRUE);
 
 /*
- * Miscellaneous global interfaces
+ * Optionally use default values for the ACPI register widths. Set this to
+ * TRUE to use the defaults, if an FADT contains incorrect widths/lengths.
  */
-ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable(void))
-ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable(void))
-#ifdef ACPI_FUTURE_USAGE
-acpi_status acpi_subsystem_status(void);
-#endif
+ACPI_INIT_GLOBAL(u8, acpi_gbl_use_default_register_widths, TRUE);
 
-#ifdef ACPI_FUTURE_USAGE
-acpi_status acpi_get_system_info(struct acpi_buffer *ret_buffer);
-#endif
+/*
+ * Whether or not to verify the table checksum before installation. Set
+ * this to TRUE to verify the table checksum before install it to the table
+ * manager. Note that enabling this option causes errors to happen in some
+ * OSPMs during early initialization stages. Default behavior is to do such
+ * verification.
+ */
+ACPI_INIT_GLOBAL(u8, acpi_gbl_verify_table_checksum, TRUE);
 
-acpi_status acpi_get_statistics(struct acpi_statistics *stats);
+/*
+ * Optionally enable output from the AML Debug Object.
+ */
+ACPI_INIT_GLOBAL(u8, acpi_gbl_enable_aml_debug_object, FALSE);
 
-const char *acpi_format_exception(acpi_status exception);
+/*
+ * Optionally copy the entire DSDT to local memory (instead of simply
+ * mapping it.) There are some BIOSs that corrupt or replace the original
+ * DSDT, creating the need for this option. Default is FALSE, do not copy
+ * the DSDT.
+ */
+ACPI_INIT_GLOBAL(u8, acpi_gbl_copy_dsdt_locally, FALSE);
 
-acpi_status acpi_purge_cached_objects(void);
+/*
+ * Optionally ignore an XSDT if present and use the RSDT instead.
+ * Although the ACPI specification requires that an XSDT be used instead
+ * of the RSDT, the XSDT has been found to be corrupt or ill-formed on
+ * some machines. Default behavior is to use the XSDT if present.
+ */
+ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE);
 
-acpi_status acpi_install_interface(acpi_string interface_name);
+/*
+ * Optionally use 32-bit FADT addresses if and when there is a conflict
+ * (address mismatch) between the 32-bit and 64-bit versions of the
+ * address. Although ACPICA adheres to the ACPI specification which
+ * requires the use of the corresponding 64-bit address if it is non-zero,
+ * some machines have been found to have a corrupted non-zero 64-bit
+ * address. Default is TRUE, favor the 32-bit addresses.
+ */
+ACPI_INIT_GLOBAL(u8, acpi_gbl_use32_bit_fadt_addresses, TRUE);
 
-acpi_status acpi_remove_interface(acpi_string interface_name);
+/*
+ * Optionally truncate I/O addresses to 16 bits. Provides compatibility
+ * with other ACPI implementations. NOTE: During ACPICA initialization,
+ * this value is set to TRUE if any Windows OSI strings have been
+ * requested by the BIOS.
+ */
+ACPI_INIT_GLOBAL(u8, acpi_gbl_truncate_io_addresses, FALSE);
 
-acpi_status acpi_update_interfaces(u8 action);
+/*
+ * Disable runtime checking and repair of values returned by control methods.
+ * Use only if the repair is causing a problem on a particular machine.
+ */
+ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_auto_repair, FALSE);
 
-u32
-acpi_check_address_range(acpi_adr_space_type space_id,
-                        acpi_physical_address address,
-                        acpi_size length, u8 warn);
+/*
+ * Optionally do not install any SSDTs from the RSDT/XSDT during initialization.
+ * This can be useful for debugging ACPI problems on some machines.
+ */
+ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_ssdt_table_install, FALSE);
 
-acpi_status
-acpi_decode_pld_buffer(u8 *in_buffer,
-                      acpi_size length, struct acpi_pld_info **return_buffer);
+/*
+ * We keep track of the latest version of Windows that has been requested by
+ * the BIOS. ACPI 5.0.
+ */
+ACPI_INIT_GLOBAL(u8, acpi_gbl_osi_data, 0);
 
 /*
- * ACPI table load/unload interfaces
+ * ACPI 5.0 introduces the concept of a "reduced hardware platform", meaning
+ * that the ACPI hardware is no longer required. A flag in the FADT indicates
+ * a reduced HW machine, and that flag is duplicated here for convenience.
  */
-acpi_status acpi_load_table(struct acpi_table_header *table);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_reduced_hardware, FALSE);
 
-acpi_status acpi_unload_parent_table(acpi_handle object);
+/*
+ * This mechanism is used to trace a specified AML method. The method is
+ * traced each time it is executed.
+ */
+ACPI_INIT_GLOBAL(u32, acpi_gbl_trace_flags, 0);
+ACPI_INIT_GLOBAL(acpi_name, acpi_gbl_trace_method_name, 0);
 
-acpi_status __init acpi_load_tables(void);
+/*
+ * Runtime configuration of debug output control masks. We want the debug
+ * switches statically initialized so they are already set when the debugger
+ * is entered.
+ */
+ACPI_INIT_GLOBAL(u32, acpi_dbg_level, ACPI_DEBUG_DEFAULT);
+ACPI_INIT_GLOBAL(u32, acpi_dbg_layer, 0);
 
 /*
- * ACPI table manipulation interfaces
+ * Other miscellaneous globals
  */
-acpi_status __init acpi_reallocate_root_table(void);
+ACPI_GLOBAL(struct acpi_table_fadt, acpi_gbl_FADT);
+ACPI_GLOBAL(u32, acpi_current_gpe_count);
+ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running);
 
-acpi_status __init acpi_find_root_pointer(acpi_size *rsdp_address);
+/*****************************************************************************
+ *
+ * ACPICA public interface configuration.
+ *
+ * Interfaces that are configured out of the ACPICA build are replaced
+ * by inlined stubs by default.
+ *
+ ****************************************************************************/
 
-acpi_status acpi_unload_table_id(acpi_owner_id id);
+/*
+ * Hardware-reduced prototypes (default: Not hardware reduced).
+ *
+ * All ACPICA hardware-related interfaces that use these macros will be
+ * configured out of the ACPICA build if the ACPI_REDUCED_HARDWARE flag
+ * is set to TRUE.
+ *
+ * Note: This static build option for reduced hardware is intended to
+ * reduce ACPICA code size if desired or necessary. However, even if this
+ * option is not specified, the runtime behavior of ACPICA is dependent
+ * on the actual FADT reduced hardware flag (HW_REDUCED_ACPI). If set,
+ * the flag will enable similar behavior -- ACPICA will not attempt
+ * to access any ACPI-relate hardware (SCI, GPEs, Fixed Events, etc.)
+ */
+#if (!ACPI_REDUCED_HARDWARE)
+#define ACPI_HW_DEPENDENT_RETURN_STATUS(prototype) \
+       ACPI_EXTERNAL_RETURN_STATUS(prototype)
 
-acpi_status
-acpi_get_table_header(acpi_string signature,
-                     u32 instance, struct acpi_table_header *out_table_header);
+#define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \
+       ACPI_EXTERNAL_RETURN_OK(prototype)
 
-acpi_status
-acpi_get_table_with_size(acpi_string signature,
-              u32 instance, struct acpi_table_header **out_table,
-              acpi_size *tbl_size);
+#define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \
+       ACPI_EXTERNAL_RETURN_VOID(prototype)
 
-acpi_status
-acpi_get_table(acpi_string signature,
-              u32 instance, struct acpi_table_header **out_table);
+#else
+#define ACPI_HW_DEPENDENT_RETURN_STATUS(prototype) \
+       static ACPI_INLINE prototype {return(AE_NOT_CONFIGURED);}
 
-acpi_status
-acpi_get_table_by_index(u32 table_index, struct acpi_table_header **out_table);
+#define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \
+       static ACPI_INLINE prototype {return(AE_OK);}
 
-acpi_status
-acpi_install_table_handler(acpi_table_handler handler, void *context);
+#define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \
+       static ACPI_INLINE prototype {return;}
 
-acpi_status acpi_remove_table_handler(acpi_table_handler handler);
+#endif                         /* !ACPI_REDUCED_HARDWARE */
 
 /*
- * Namespace and name interfaces
+ * Error message prototypes (default: error messages enabled).
+ *
+ * All interfaces related to error and warning messages
+ * will be configured out of the ACPICA build if the
+ * ACPI_NO_ERROR_MESSAGE flag is defined.
  */
-acpi_status
-acpi_walk_namespace(acpi_object_type type,
-                   acpi_handle start_object,
-                   u32 max_depth,
-                   acpi_walk_callback descending_callback,
-                   acpi_walk_callback ascending_callback,
-                   void *context, void **return_value);
+#ifndef ACPI_NO_ERROR_MESSAGES
+#define ACPI_MSG_DEPENDENT_RETURN_VOID(prototype) \
+       prototype;
 
-acpi_status
-acpi_get_devices(const char *HID,
-                acpi_walk_callback user_function,
-                void *context, void **return_value);
+#else
+#define ACPI_MSG_DEPENDENT_RETURN_VOID(prototype) \
+       static ACPI_INLINE prototype {return;}
 
-acpi_status
-acpi_get_name(acpi_handle object,
-             u32 name_type, struct acpi_buffer *ret_path_ptr);
+#endif                         /* ACPI_NO_ERROR_MESSAGES */
 
-acpi_status
-acpi_get_handle(acpi_handle parent,
-               acpi_string pathname, acpi_handle * ret_handle);
+/*
+ * Debugging output prototypes (default: no debug output).
+ *
+ * All interfaces related to debug output messages
+ * will be configured out of the ACPICA build unless the
+ * ACPI_DEBUG_OUTPUT flag is defined.
+ */
+#ifdef ACPI_DEBUG_OUTPUT
+#define ACPI_DBG_DEPENDENT_RETURN_VOID(prototype) \
+       prototype;
 
-acpi_status
-acpi_attach_data(acpi_handle object, acpi_object_handler handler, void *data);
+#else
+#define ACPI_DBG_DEPENDENT_RETURN_VOID(prototype) \
+       static ACPI_INLINE prototype {return;}
 
-acpi_status acpi_detach_data(acpi_handle object, acpi_object_handler handler);
+#endif                         /* ACPI_DEBUG_OUTPUT */
 
-acpi_status
-acpi_get_data_full(acpi_handle object, acpi_object_handler handler, void **data,
-                  void (*callback)(void *));
+/*****************************************************************************
+ *
+ * ACPICA public interface prototypes
+ *
+ ****************************************************************************/
 
-acpi_status
-acpi_get_data(acpi_handle object, acpi_object_handler handler, void **data);
+/*
+ * Initialization
+ */
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init
+                           acpi_initialize_tables(struct acpi_table_desc
+                                                  *initial_storage,
+                                                  u32 initial_table_count,
+                                                  u8 allow_resize))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_initialize_subsystem(void))
 
-acpi_status
-acpi_debug_trace(char *name, u32 debug_level, u32 debug_layer, u32 flags);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_enable_subsystem(u32 flags))
+
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init
+                           acpi_initialize_objects(u32 flags))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_terminate(void))
 
 /*
- * Object manipulation and enumeration
+ * Miscellaneous global interfaces
  */
-acpi_status
-acpi_evaluate_object(acpi_handle object,
-                    acpi_string pathname,
-                    struct acpi_object_list *parameter_objects,
-                    struct acpi_buffer *return_object_buffer);
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable(void))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable(void))
+#ifdef ACPI_FUTURE_USAGE
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_subsystem_status(void))
+#endif
 
-acpi_status
-acpi_evaluate_object_typed(acpi_handle object,
-                          acpi_string pathname,
-                          struct acpi_object_list *external_params,
-                          struct acpi_buffer *return_buffer,
-                          acpi_object_type return_type);
+#ifdef ACPI_FUTURE_USAGE
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_get_system_info(struct acpi_buffer
+                                                *ret_buffer))
+#endif
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_get_statistics(struct acpi_statistics *stats))
+ACPI_EXTERNAL_RETURN_PTR(const char
+                         *acpi_format_exception(acpi_status exception))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_purge_cached_objects(void))
+
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_install_interface(acpi_string interface_name))
+
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_remove_interface(acpi_string interface_name))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_update_interfaces(u8 action))
+
+ACPI_EXTERNAL_RETURN_UINT32(u32
+                           acpi_check_address_range(acpi_adr_space_type
+                                                    space_id,
+                                                    acpi_physical_address
+                                                    address, acpi_size length,
+                                                    u8 warn))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_decode_pld_buffer(u8 *in_buffer,
+                                                   acpi_size length,
+                                                   struct acpi_pld_info
+                                                   **return_buffer))
 
-acpi_status
-acpi_get_object_info(acpi_handle object,
-                    struct acpi_device_info **return_buffer);
+/*
+ * ACPI table load/unload interfaces
+ */
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init
+                           acpi_install_table(acpi_physical_address address,
+                                              u8 physical))
 
-acpi_status acpi_install_method(u8 *buffer);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_load_table(struct acpi_table_header *table))
 
-acpi_status
-acpi_get_next_object(acpi_object_type type,
-                    acpi_handle parent,
-                    acpi_handle child, acpi_handle * out_handle);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_unload_parent_table(acpi_handle object))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_load_tables(void))
 
-acpi_status acpi_get_type(acpi_handle object, acpi_object_type * out_type);
+/*
+ * ACPI table manipulation interfaces
+ */
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_reallocate_root_table(void))
+
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init
+                           acpi_find_root_pointer(acpi_size * rsdp_address))
+
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_get_table_header(acpi_string signature,
+                                                 u32 instance,
+                                                 struct acpi_table_header
+                                                 *out_table_header))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_get_table(acpi_string signature, u32 instance,
+                                           struct acpi_table_header
+                                           **out_table))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_get_table_by_index(u32 table_index,
+                                                    struct acpi_table_header
+                                                    **out_table))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_install_table_handler(acpi_table_handler
+                                                       handler, void *context))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_remove_table_handler(acpi_table_handler
+                                                      handler))
 
-acpi_status acpi_get_id(acpi_handle object, acpi_owner_id * out_type);
+/*
+ * Namespace and name interfaces
+ */
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_walk_namespace(acpi_object_type type,
+                                               acpi_handle start_object,
+                                               u32 max_depth,
+                                               acpi_walk_callback
+                                               descending_callback,
+                                               acpi_walk_callback
+                                               ascending_callback,
+                                               void *context,
+                                               void **return_value))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_get_devices(const char *HID,
+                                             acpi_walk_callback user_function,
+                                             void *context,
+                                             void **return_value))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_get_name(acpi_handle object, u32 name_type,
+                                          struct acpi_buffer *ret_path_ptr))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_get_handle(acpi_handle parent,
+                                            acpi_string pathname,
+                                            acpi_handle * ret_handle))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_attach_data(acpi_handle object,
+                                             acpi_object_handler handler,
+                                             void *data))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_detach_data(acpi_handle object,
+                                             acpi_object_handler handler))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_get_data(acpi_handle object,
+                                          acpi_object_handler handler,
+                                          void **data))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_debug_trace(char *name, u32 debug_level,
+                                             u32 debug_layer, u32 flags))
 
-acpi_status acpi_get_parent(acpi_handle object, acpi_handle * out_handle);
+/*
+ * Object manipulation and enumeration
+ */
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_evaluate_object(acpi_handle object,
+                                                acpi_string pathname,
+                                                struct acpi_object_list
+                                                *parameter_objects,
+                                                struct acpi_buffer
+                                                *return_object_buffer))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_evaluate_object_typed(acpi_handle object,
+                                                       acpi_string pathname,
+                                                       struct acpi_object_list
+                                                       *external_params,
+                                                       struct acpi_buffer
+                                                       *return_buffer,
+                                                       acpi_object_type
+                                                       return_type))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_get_object_info(acpi_handle object,
+                                                 struct acpi_device_info
+                                                 **return_buffer))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_install_method(u8 *buffer))
+
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_get_next_object(acpi_object_type type,
+                                                acpi_handle parent,
+                                                acpi_handle child,
+                                                acpi_handle * out_handle))
+
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_get_type(acpi_handle object,
+                                         acpi_object_type * out_type))
+
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_get_parent(acpi_handle object,
+                                           acpi_handle * out_handle))
 
 /*
  * Handler interfaces
  */
-acpi_status
-acpi_install_initialization_handler(acpi_init_handler handler, u32 function);
-
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_install_initialization_handler
+                           (acpi_init_handler handler, u32 function))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
-                               acpi_install_sci_handler(acpi_sci_handler
-                                                        address,
-                                                        void *context))
+                                acpi_install_sci_handler(acpi_sci_handler
+                                                         address,
+                                                         void *context))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
                                 acpi_remove_sci_handler(acpi_sci_handler
                                                         address))
@@ -313,30 +558,42 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
                                                         u32 gpe_number,
                                                         acpi_gpe_handler
                                                         address))
-acpi_status acpi_install_notify_handler(acpi_handle device, u32 handler_type,
-                                        acpi_notify_handler handler,
-                                        void *context);
-
-acpi_status
-acpi_remove_notify_handler(acpi_handle device,
-                          u32 handler_type, acpi_notify_handler handler);
-
-acpi_status
-acpi_install_address_space_handler(acpi_handle device,
-                                  acpi_adr_space_type space_id,
-                                  acpi_adr_space_handler handler,
-                                  acpi_adr_space_setup setup, void *context);
-
-acpi_status
-acpi_remove_address_space_handler(acpi_handle device,
-                                 acpi_adr_space_type space_id,
-                                 acpi_adr_space_handler handler);
-
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_install_notify_handler(acpi_handle device,
+                                                        u32 handler_type,
+                                                        acpi_notify_handler
+                                                        handler,
+                                                        void *context))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_remove_notify_handler(acpi_handle device,
+                                                       u32 handler_type,
+                                                       acpi_notify_handler
+                                                       handler))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_install_address_space_handler(acpi_handle
+                                                               device,
+                                                               acpi_adr_space_type
+                                                               space_id,
+                                                               acpi_adr_space_handler
+                                                               handler,
+                                                               acpi_adr_space_setup
+                                                               setup,
+                                                               void *context))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_remove_address_space_handler(acpi_handle
+                                                              device,
+                                                              acpi_adr_space_type
+                                                              space_id,
+                                                              acpi_adr_space_handler
+                                                              handler))
 #ifdef ACPI_FUTURE_USAGE
-acpi_status acpi_install_exception_handler(acpi_exception_handler handler);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_install_exception_handler
+                            (acpi_exception_handler handler))
 #endif
-
-acpi_status acpi_install_interface_handler(acpi_interface_handler handler);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_install_interface_handler
+                            (acpi_interface_handler handler))
 
 /*
  * Global Lock interfaces
@@ -351,10 +608,14 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
 /*
  * Interfaces to AML mutex objects
  */
-acpi_status
-acpi_acquire_mutex(acpi_handle handle, acpi_string pathname, u16 timeout);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_acquire_mutex(acpi_handle handle,
+                                              acpi_string pathname,
+                                              u16 timeout))
 
-acpi_status acpi_release_mutex(acpi_handle handle, acpi_string pathname);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_release_mutex(acpi_handle handle,
+                                              acpi_string pathname))
 
 /*
  * Fixed Event interfaces
@@ -434,57 +695,69 @@ typedef
 acpi_status(*acpi_walk_resource_callback) (struct acpi_resource * resource,
                                           void *context);
 
-acpi_status
-acpi_get_vendor_resource(acpi_handle device,
-                        char *name,
-                        struct acpi_vendor_uuid *uuid,
-                        struct acpi_buffer *ret_buffer);
-
-acpi_status
-acpi_get_current_resources(acpi_handle device, struct acpi_buffer *ret_buffer);
-
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_get_vendor_resource(acpi_handle device,
+                                                    char *name,
+                                                    struct acpi_vendor_uuid
+                                                    *uuid,
+                                                    struct acpi_buffer
+                                                    *ret_buffer))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_get_current_resources(acpi_handle device,
+                                                       struct acpi_buffer
+                                                       *ret_buffer))
 #ifdef ACPI_FUTURE_USAGE
-acpi_status
-acpi_get_possible_resources(acpi_handle device, struct acpi_buffer *ret_buffer);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_get_possible_resources(acpi_handle device,
+                                                        struct acpi_buffer
+                                                        *ret_buffer))
 #endif
-
-acpi_status
-acpi_get_event_resources(acpi_handle device_handle,
-                        struct acpi_buffer *ret_buffer);
-
-acpi_status
-acpi_walk_resource_buffer(struct acpi_buffer *buffer,
-                         acpi_walk_resource_callback user_function,
-                         void *context);
-
-acpi_status
-acpi_walk_resources(acpi_handle device,
-                   char *name,
-                   acpi_walk_resource_callback user_function, void *context);
-
-acpi_status
-acpi_set_current_resources(acpi_handle device, struct acpi_buffer *in_buffer);
-
-acpi_status
-acpi_get_irq_routing_table(acpi_handle device, struct acpi_buffer *ret_buffer);
-
-acpi_status
-acpi_resource_to_address64(struct acpi_resource *resource,
-                          struct acpi_resource_address64 *out);
-
-acpi_status
-acpi_buffer_to_resource(u8 *aml_buffer,
-                       u16 aml_buffer_length,
-                       struct acpi_resource **resource_ptr);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_get_event_resources(acpi_handle device_handle,
+                                                     struct acpi_buffer
+                                                     *ret_buffer))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_walk_resource_buffer(struct acpi_buffer
+                                                      *buffer,
+                                                      acpi_walk_resource_callback
+                                                      user_function,
+                                                      void *context))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_walk_resources(acpi_handle device, char *name,
+                                                acpi_walk_resource_callback
+                                                user_function, void *context))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_set_current_resources(acpi_handle device,
+                                                       struct acpi_buffer
+                                                       *in_buffer))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_get_irq_routing_table(acpi_handle device,
+                                                       struct acpi_buffer
+                                                       *ret_buffer))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_resource_to_address64(struct acpi_resource
+                                                       *resource,
+                                                       struct
+                                                       acpi_resource_address64
+                                                       *out))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                            acpi_buffer_to_resource(u8 *aml_buffer,
+                                                    u16 aml_buffer_length,
+                                                    struct acpi_resource
+                                                    **resource_ptr))
 
 /*
  * Hardware (ACPI device) interfaces
  */
-acpi_status acpi_reset(void);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_reset(void))
 
-acpi_status acpi_read(u64 *value, struct acpi_generic_address *reg);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_read(u64 *value,
+                                     struct acpi_generic_address *reg))
 
-acpi_status acpi_write(u64 value, struct acpi_generic_address *reg);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_write(u64 value,
+                                      struct acpi_generic_address *reg))
 
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
                                acpi_read_bit_register(u32 register_id,
@@ -497,18 +770,20 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
 /*
  * Sleep/Wake interfaces
  */
-acpi_status
-acpi_get_sleep_type_data(u8 sleep_state, u8 *slp_typ_a, u8 *slp_typ_b);
-
-acpi_status acpi_enter_sleep_state_prep(u8 sleep_state);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_get_sleep_type_data(u8 sleep_state,
+                                                    u8 *slp_typ_a,
+                                                    u8 *slp_typ_b))
 
-acpi_status acpi_enter_sleep_state(u8 sleep_state);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_enter_sleep_state_prep(u8 sleep_state))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_enter_sleep_state(u8 sleep_state))
 
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enter_sleep_state_s4bios(void))
 
-acpi_status acpi_leave_sleep_state_prep(u8 sleep_state);
-
-acpi_status acpi_leave_sleep_state(u8 sleep_state);
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status
+                           acpi_leave_sleep_state_prep(u8 sleep_state))
+ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_leave_sleep_state(u8 sleep_state))
 
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
                                acpi_set_firmware_waking_vector(u32
@@ -535,53 +810,72 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
 /*
  * Error/Warning output
  */
-ACPI_PRINTF_LIKE(3)
-void ACPI_INTERNAL_VAR_XFACE
-acpi_error(const char *module_name, u32 line_number, const char *format, ...);
-
-ACPI_PRINTF_LIKE(4)
-void ACPI_INTERNAL_VAR_XFACE
-acpi_exception(const char *module_name,
-              u32 line_number, acpi_status status, const char *format, ...);
-
-ACPI_PRINTF_LIKE(3)
-void ACPI_INTERNAL_VAR_XFACE
-acpi_warning(const char *module_name, u32 line_number, const char *format, ...);
-
-ACPI_PRINTF_LIKE(3)
-void ACPI_INTERNAL_VAR_XFACE
-acpi_info(const char *module_name, u32 line_number, const char *format, ...);
-
-ACPI_PRINTF_LIKE(3)
-void ACPI_INTERNAL_VAR_XFACE
-acpi_bios_error(const char *module_name,
-               u32 line_number, const char *format, ...);
-
-ACPI_PRINTF_LIKE(3)
-void ACPI_INTERNAL_VAR_XFACE
-acpi_bios_warning(const char *module_name,
-                 u32 line_number, const char *format, ...);
+ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(3)
+                              void ACPI_INTERNAL_VAR_XFACE
+                              acpi_error(const char *module_name,
+                                         u32 line_number,
+                                         const char *format, ...))
+ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(4)
+                               void ACPI_INTERNAL_VAR_XFACE
+                               acpi_exception(const char *module_name,
+                                              u32 line_number,
+                                              acpi_status status,
+                                              const char *format, ...))
+ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(3)
+                               void ACPI_INTERNAL_VAR_XFACE
+                               acpi_warning(const char *module_name,
+                                            u32 line_number,
+                                            const char *format, ...))
+ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(3)
+                               void ACPI_INTERNAL_VAR_XFACE
+                               acpi_info(const char *module_name,
+                                         u32 line_number,
+                                         const char *format, ...))
+ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(3)
+                               void ACPI_INTERNAL_VAR_XFACE
+                               acpi_bios_error(const char *module_name,
+                                               u32 line_number,
+                                               const char *format, ...))
+ACPI_MSG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(3)
+                               void ACPI_INTERNAL_VAR_XFACE
+                               acpi_bios_warning(const char *module_name,
+                                                 u32 line_number,
+                                                 const char *format, ...))
 
 /*
  * Debug output
  */
-#ifdef ACPI_DEBUG_OUTPUT
+ACPI_DBG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(6)
+                              void ACPI_INTERNAL_VAR_XFACE
+                              acpi_debug_print(u32 requested_debug_level,
+                                               u32 line_number,
+                                               const char *function_name,
+                                               const char *module_name,
+                                               u32 component_id,
+                                               const char *format, ...))
+ACPI_DBG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(6)
+                               void ACPI_INTERNAL_VAR_XFACE
+                               acpi_debug_print_raw(u32 requested_debug_level,
+                                                    u32 line_number,
+                                                    const char *function_name,
+                                                    const char *module_name,
+                                                    u32 component_id,
+                                                    const char *format, ...))
 
-ACPI_PRINTF_LIKE(6)
-void ACPI_INTERNAL_VAR_XFACE
-acpi_debug_print(u32 requested_debug_level,
-                u32 line_number,
-                const char *function_name,
-                const char *module_name,
-                u32 component_id, const char *format, ...);
-
-ACPI_PRINTF_LIKE(6)
-void ACPI_INTERNAL_VAR_XFACE
-acpi_debug_print_raw(u32 requested_debug_level,
-                    u32 line_number,
-                    const char *function_name,
-                    const char *module_name,
-                    u32 component_id, const char *format, ...);
-#endif
+/*
+ * Divergences
+ */
+acpi_status acpi_get_id(acpi_handle object, acpi_owner_id * out_type);
+
+acpi_status acpi_unload_table_id(acpi_owner_id id);
+
+acpi_status
+acpi_get_table_with_size(acpi_string signature,
+              u32 instance, struct acpi_table_header **out_table,
+              acpi_size *tbl_size);
+
+acpi_status
+acpi_get_data_full(acpi_handle object, acpi_object_handler handler, void **data,
+                  void (*callback)(void *));
 
 #endif                         /* __ACXFACE_H__ */
index 3b30e36..1cc7ef1 100644 (file)
@@ -367,12 +367,11 @@ struct acpi_table_desc {
 
 /* Masks for Flags field above */
 
-#define ACPI_TABLE_ORIGIN_UNKNOWN       (0)
-#define ACPI_TABLE_ORIGIN_MAPPED        (1)
-#define ACPI_TABLE_ORIGIN_ALLOCATED     (2)
-#define ACPI_TABLE_ORIGIN_OVERRIDE      (4)
-#define ACPI_TABLE_ORIGIN_MASK          (7)
-#define ACPI_TABLE_IS_LOADED            (8)
+#define ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL  (0)        /* Virtual address, external maintained */
+#define ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL (1)        /* Physical address, internally mapped */
+#define ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL  (2)        /* Virtual address, internallly allocated */
+#define ACPI_TABLE_ORIGIN_MASK              (3)
+#define ACPI_TABLE_IS_LOADED                (8)
 
 /*
  * Get the remaining ACPI tables
index 212c65d..4ad7da8 100644 (file)
@@ -675,7 +675,7 @@ enum acpi_madt_type {
 };
 
 /*
- * MADT Sub-tables, correspond to Type in struct acpi_subtable_header
+ * MADT Subtables, correspond to Type in struct acpi_subtable_header
  */
 
 /* 0: Processor Local APIC */
@@ -918,7 +918,7 @@ enum acpi_srat_type {
 };
 
 /*
- * SRAT Sub-tables, correspond to Type in struct acpi_subtable_header
+ * SRAT Subtables, correspond to Type in struct acpi_subtable_header
  */
 
 /* 0: Processor Local APIC/SAPIC Affinity */
index c8adad9..860e5c8 100644 (file)
@@ -70,6 +70,7 @@
 #define ACPI_SIG_HPET           "HPET" /* High Precision Event Timer table */
 #define ACPI_SIG_IBFT           "IBFT" /* iSCSI Boot Firmware Table */
 #define ACPI_SIG_IVRS           "IVRS" /* I/O Virtualization Reporting Structure */
+#define ACPI_SIG_LPIT           "LPIT" /* Low Power Idle Table */
 #define ACPI_SIG_MCFG           "MCFG" /* PCI Memory Mapped Configuration table */
 #define ACPI_SIG_MCHI           "MCHI" /* Management Controller Host Interface table */
 #define ACPI_SIG_MTMR           "MTMR" /* MID Timer table */
@@ -456,7 +457,7 @@ struct acpi_dmar_pci_path {
 };
 
 /*
- * DMAR Sub-tables, correspond to Type in struct acpi_dmar_header
+ * DMAR Subtables, correspond to Type in struct acpi_dmar_header
  */
 
 /* 0: Hardware Unit Definition */
@@ -820,7 +821,71 @@ struct acpi_ivrs_memory {
 
 /*******************************************************************************
  *
- * MCFG - PCI Memory Mapped Configuration table and sub-table
+ * LPIT - Low Power Idle Table
+ *
+ * Conforms to "ACPI Low Power Idle Table (LPIT) and _LPD Proposal (DRAFT)"
+ *
+ ******************************************************************************/
+
+struct acpi_table_lpit {
+       struct acpi_table_header header;        /* Common ACPI table header */
+};
+
+/* LPIT subtable header */
+
+struct acpi_lpit_header {
+       u32 type;               /* Subtable type */
+       u32 length;             /* Subtable length */
+       u16 unique_id;
+       u16 reserved;
+       u32 flags;
+};
+
+/* Values for subtable Type above */
+
+enum acpi_lpit_type {
+       ACPI_LPIT_TYPE_NATIVE_CSTATE = 0x00,
+       ACPI_LPIT_TYPE_SIMPLE_IO = 0x01
+};
+
+/* Masks for Flags field above  */
+
+#define ACPI_LPIT_STATE_DISABLED    (1)
+#define ACPI_LPIT_NO_COUNTER        (1<<1)
+
+/*
+ * LPIT subtables, correspond to Type in struct acpi_lpit_header
+ */
+
+/* 0x00: Native C-state instruction based LPI structure */
+
+struct acpi_lpit_native {
+       struct acpi_lpit_header header;
+       struct acpi_generic_address entry_trigger;
+       u32 residency;
+       u32 latency;
+       struct acpi_generic_address residency_counter;
+       u64 counter_frequency;
+};
+
+/* 0x01: Simple I/O based LPI structure */
+
+struct acpi_lpit_io {
+       struct acpi_lpit_header header;
+       struct acpi_generic_address entry_trigger;
+       u32 trigger_action;
+       u64 trigger_value;
+       u64 trigger_mask;
+       struct acpi_generic_address minimum_idle_state;
+       u32 residency;
+       u32 latency;
+       struct acpi_generic_address residency_counter;
+       u64 counter_frequency;
+};
+
+/*******************************************************************************
+ *
+ * MCFG - PCI Memory Mapped Configuration table and subtable
  *        Version 1
  *
  * Conforms to "PCI Firmware Specification", Revision 3.0, June 20, 2005
@@ -923,7 +988,7 @@ enum acpi_slic_type {
 };
 
 /*
- * SLIC Sub-tables, correspond to Type in struct acpi_slic_header
+ * SLIC Subtables, correspond to Type in struct acpi_slic_header
  */
 
 /* 0: Public Key Structure */
index e763565..19b26bb 100644 (file)
@@ -329,6 +329,15 @@ typedef u32 acpi_physical_address;
  *
  ******************************************************************************/
 
+#ifdef ACPI_NO_MEM_ALLOCATIONS
+
+#define ACPI_ALLOCATE(a)                NULL
+#define ACPI_ALLOCATE_ZEROED(a)         NULL
+#define ACPI_FREE(a)
+#define ACPI_MEM_TRACKING(a)
+
+#else                          /* ACPI_NO_MEM_ALLOCATIONS */
+
 #ifdef ACPI_DBG_TRACK_ALLOCATIONS
 /*
  * Memory allocation tracking (used by acpi_exec to detect memory leaks)
@@ -350,6 +359,8 @@ typedef u32 acpi_physical_address;
 
 #endif                         /* ACPI_DBG_TRACK_ALLOCATIONS */
 
+#endif                         /* ACPI_NO_MEM_ALLOCATIONS */
+
 /******************************************************************************
  *
  * ACPI Specification constants (Do not change unless the specification changes)
@@ -928,9 +939,19 @@ struct acpi_object_list {
  * Miscellaneous common Data Structures used by the interfaces
  */
 #define ACPI_NO_BUFFER              0
+
+#ifdef ACPI_NO_MEM_ALLOCATIONS
+
+#define ACPI_ALLOCATE_BUFFER        (acpi_size) (0)
+#define ACPI_ALLOCATE_LOCAL_BUFFER  (acpi_size) (0)
+
+#else                          /* ACPI_NO_MEM_ALLOCATIONS */
+
 #define ACPI_ALLOCATE_BUFFER        (acpi_size) (-1)   /* Let ACPICA allocate buffer */
 #define ACPI_ALLOCATE_LOCAL_BUFFER  (acpi_size) (-2)   /* For internal use only (enables tracking) */
 
+#endif                         /* ACPI_NO_MEM_ALLOCATIONS */
+
 struct acpi_buffer {
        acpi_size length;       /* Length in bytes of the buffer */
        void *pointer;          /* pointer to buffer */
diff --git a/include/acpi/platform/acenvex.h b/include/acpi/platform/acenvex.h
new file mode 100644 (file)
index 0000000..2b61238
--- /dev/null
@@ -0,0 +1,63 @@
+/******************************************************************************
+ *
+ * Name: acenvex.h - Extra host and compiler configuration
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACENVEX_H__
+#define __ACENVEX_H__
+
+/*! [Begin] no source code translation */
+
+/******************************************************************************
+ *
+ * Extra host configuration files. All ACPICA headers are included before
+ * including these files.
+ *
+ *****************************************************************************/
+
+#if defined(_LINUX) || defined(__linux__)
+#include <acpi/platform/aclinuxex.h>
+
+#endif
+
+/*! [End] no source code translation !*/
+
+#endif                         /* __ACENVEX_H__ */
index a476b91..384875d 100644 (file)
  */
 #define ACPI_UNUSED_VAR __attribute__ ((unused))
 
+/*
+ * Some versions of gcc implement strchr() with a buggy macro. So,
+ * undef it here. Prevents error messages of this form (usually from the
+ * file getopt.c):
+ *
+ * error: logical '&&' with non-zero constant will always evaluate as true
+ */
+#ifdef strchr
+#undef strchr
+#endif
+
 #endif                         /* __ACGCC_H__ */
index 93c55ed..cd1f052 100644 (file)
@@ -48,7 +48,6 @@
 
 #define ACPI_USE_SYSTEM_CLIBRARY
 #define ACPI_USE_DO_WHILE_0
-#define ACPI_MUTEX_TYPE             ACPI_BINARY_SEMAPHORE
 
 #ifdef __KERNEL__
 
 #ifdef EXPORT_ACPI_INTERFACES
 #include <linux/export.h>
 #endif
-#include <asm/acpi.h>
+#include <asm/acenv.h>
 
-/* Host-dependent types and defines for in-kernel ACPICA */
+#ifndef CONFIG_ACPI
 
-#define ACPI_MACHINE_WIDTH          BITS_PER_LONG
-#define ACPI_EXPORT_SYMBOL(symbol)  EXPORT_SYMBOL(symbol);
-#define strtoul                     simple_strtoul
+/* External globals for __KERNEL__, stubs is needed */
 
-#define acpi_cache_t                        struct kmem_cache
-#define acpi_spinlock                       spinlock_t *
-#define acpi_cpu_flags                      unsigned long
+#define ACPI_GLOBAL(t,a)
+#define ACPI_INIT_GLOBAL(t,a,b)
 
-#else                          /* !__KERNEL__ */
+/* Generating stubs for configurable ACPICA macros */
 
-#include <stdarg.h>
-#include <string.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <unistd.h>
+#define ACPI_NO_MEM_ALLOCATIONS
 
-/* Disable kernel specific declarators */
+/* Generating stubs for configurable ACPICA functions */
 
-#ifndef __init
-#define __init
-#endif
-
-#ifndef __iomem
-#define __iomem
-#endif
+#define ACPI_NO_ERROR_MESSAGES
+#undef ACPI_DEBUG_OUTPUT
 
-/* Host-dependent types and defines for user-space ACPICA */
-
-#define ACPI_FLUSH_CPU_CACHE()
-#define ACPI_CAST_PTHREAD_T(pthread) ((acpi_thread_id) (pthread))
+/* External interface for __KERNEL__, stub is needed */
 
-#if defined(__ia64__) || defined(__x86_64__) || defined(__aarch64__)
-#define ACPI_MACHINE_WIDTH          64
-#define COMPILER_DEPENDENT_INT64    long
-#define COMPILER_DEPENDENT_UINT64   unsigned long
-#else
-#define ACPI_MACHINE_WIDTH          32
-#define COMPILER_DEPENDENT_INT64    long long
-#define COMPILER_DEPENDENT_UINT64   unsigned long long
-#define ACPI_USE_NATIVE_DIVIDE
-#endif
+#define ACPI_EXTERNAL_RETURN_STATUS(prototype) \
+       static ACPI_INLINE prototype {return(AE_NOT_CONFIGURED);}
+#define ACPI_EXTERNAL_RETURN_OK(prototype) \
+       static ACPI_INLINE prototype {return(AE_OK);}
+#define ACPI_EXTERNAL_RETURN_VOID(prototype) \
+       static ACPI_INLINE prototype {return;}
+#define ACPI_EXTERNAL_RETURN_UINT32(prototype) \
+       static ACPI_INLINE prototype {return(0);}
+#define ACPI_EXTERNAL_RETURN_PTR(prototype) \
+       static ACPI_INLINE prototype {return(NULL);}
 
-#ifndef __cdecl
-#define __cdecl
-#endif
+#endif                         /* CONFIG_ACPI */
 
-#endif                         /* __KERNEL__ */
+/* Host-dependent types and defines for in-kernel ACPICA */
 
-/* Linux uses GCC */
+#define ACPI_MACHINE_WIDTH          BITS_PER_LONG
+#define ACPI_EXPORT_SYMBOL(symbol)  EXPORT_SYMBOL(symbol);
+#define strtoul                     simple_strtoul
 
-#include <acpi/platform/acgcc.h>
+#define acpi_cache_t                        struct kmem_cache
+#define acpi_spinlock                       spinlock_t *
+#define acpi_cpu_flags                      unsigned long
 
-#ifdef __KERNEL__
+/* Use native linux version of acpi_os_allocate_zeroed */
 
-/*
- * FIXME: Inclusion of actypes.h
- * Linux kernel need this before defining inline OSL interfaces as
- * actypes.h need to be included to find ACPICA type definitions.
- * Since from ACPICA's perspective, the actypes.h should be included after
- * acenv.h (aclinux.h), this leads to a inclusion mis-ordering issue.
- */
-#include <acpi/actypes.h>
+#define USE_NATIVE_ALLOCATE_ZEROED
 
 /*
  * Overrides for in-kernel ACPICA
  */
-acpi_status __init acpi_os_initialize(void);
 #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize
-
-acpi_status acpi_os_terminate(void);
 #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate
-
-/*
- * Memory allocation/deallocation
- */
-
-/*
- * The irqs_disabled() check is for resume from RAM.
- * Interrupts are off during resume, just like they are for boot.
- * However, boot has  (system_state != SYSTEM_RUNNING)
- * to quiet __might_sleep() in kmalloc() and resume does not.
- */
-static inline void *acpi_os_allocate(acpi_size size)
-{
-       return kmalloc(size, irqs_disabled()? GFP_ATOMIC : GFP_KERNEL);
-}
-
 #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_allocate
-
-/* Use native linux version of acpi_os_allocate_zeroed */
-
-static inline void *acpi_os_allocate_zeroed(acpi_size size)
-{
-       return kzalloc(size, irqs_disabled()? GFP_ATOMIC : GFP_KERNEL);
-}
-
 #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_allocate_zeroed
-#define USE_NATIVE_ALLOCATE_ZEROED
-
-static inline void acpi_os_free(void *memory)
-{
-       kfree(memory);
-}
-
 #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_free
-
-static inline void *acpi_os_acquire_object(acpi_cache_t * cache)
-{
-       return kmem_cache_zalloc(cache,
-                                irqs_disabled()? GFP_ATOMIC : GFP_KERNEL);
-}
-
 #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_acquire_object
-
-static inline acpi_thread_id acpi_os_get_thread_id(void)
-{
-       return (acpi_thread_id) (unsigned long)current;
-}
-
 #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_thread_id
-
-#ifndef CONFIG_PREEMPT
-
-/*
- * Used within ACPICA to show where it is safe to preempt execution
- * when CONFIG_PREEMPT=n
- */
-#define ACPI_PREEMPTION_POINT() \
-       do { \
-               if (!irqs_disabled()) \
-                       cond_resched(); \
-       } while (0)
-
-#endif
-
-/*
- * When lockdep is enabled, the spin_lock_init() macro stringifies it's
- * argument and uses that as a name for the lock in debugging.
- * By executing spin_lock_init() in a macro the key changes from "lock" for
- * all locks to the name of the argument of acpi_os_create_lock(), which
- * prevents lockdep from reporting false positives for ACPICA locks.
- */
-#define acpi_os_create_lock(__handle) \
-       ({ \
-               spinlock_t *lock = ACPI_ALLOCATE(sizeof(*lock)); \
-               if (lock) { \
-                       *(__handle) = lock; \
-                       spin_lock_init(*(__handle)); \
-               } \
-               lock ? AE_OK : AE_NO_MEMORY; \
-       })
 #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_create_lock
 
-void __iomem *acpi_os_map_memory(acpi_physical_address where, acpi_size length);
-#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_map_memory
-
-void acpi_os_unmap_memory(void __iomem * logical_address, acpi_size size);
-#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_unmap_memory
-
 /*
  * OSL interfaces used by debugger/disassembler
  */
@@ -252,11 +147,45 @@ void acpi_os_unmap_memory(void __iomem * logical_address, acpi_size size);
 #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_next_filename
 #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_close_directory
 
-/*
- * OSL interfaces added by Linux
- */
-void early_acpi_os_unmap_memory(void __iomem * virt, acpi_size size);
+#else                          /* !__KERNEL__ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+
+/* Define/disable kernel-specific declarators */
+
+#ifndef __init
+#define __init
+#endif
+
+/* Host-dependent types and defines for user-space ACPICA */
+
+#define ACPI_FLUSH_CPU_CACHE()
+#define ACPI_CAST_PTHREAD_T(pthread) ((acpi_thread_id) (pthread))
+
+#if defined(__ia64__)    || defined(__x86_64__) ||\
+       defined(__aarch64__) || defined(__PPC64__)
+#define ACPI_MACHINE_WIDTH          64
+#define COMPILER_DEPENDENT_INT64    long
+#define COMPILER_DEPENDENT_UINT64   unsigned long
+#else
+#define ACPI_MACHINE_WIDTH          32
+#define COMPILER_DEPENDENT_INT64    long long
+#define COMPILER_DEPENDENT_UINT64   unsigned long long
+#define ACPI_USE_NATIVE_DIVIDE
+#endif
+
+#ifndef __cdecl
+#define __cdecl
+#endif
 
 #endif                         /* __KERNEL__ */
 
+/* Linux uses GCC */
+
+#include <acpi/platform/acgcc.h>
+
 #endif                         /* __ACLINUX_H__ */
diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h
new file mode 100644 (file)
index 0000000..191e741
--- /dev/null
@@ -0,0 +1,112 @@
+/******************************************************************************
+ *
+ * Name: aclinuxex.h - Extra OS specific defines, etc. for Linux
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef __ACLINUXEX_H__
+#define __ACLINUXEX_H__
+
+#ifdef __KERNEL__
+
+/*
+ * Overrides for in-kernel ACPICA
+ */
+acpi_status __init acpi_os_initialize(void);
+
+acpi_status acpi_os_terminate(void);
+
+/*
+ * The irqs_disabled() check is for resume from RAM.
+ * Interrupts are off during resume, just like they are for boot.
+ * However, boot has  (system_state != SYSTEM_RUNNING)
+ * to quiet __might_sleep() in kmalloc() and resume does not.
+ */
+static inline void *acpi_os_allocate(acpi_size size)
+{
+       return kmalloc(size, irqs_disabled()? GFP_ATOMIC : GFP_KERNEL);
+}
+
+static inline void *acpi_os_allocate_zeroed(acpi_size size)
+{
+       return kzalloc(size, irqs_disabled()? GFP_ATOMIC : GFP_KERNEL);
+}
+
+static inline void acpi_os_free(void *memory)
+{
+       kfree(memory);
+}
+
+static inline void *acpi_os_acquire_object(acpi_cache_t * cache)
+{
+       return kmem_cache_zalloc(cache,
+                                irqs_disabled()? GFP_ATOMIC : GFP_KERNEL);
+}
+
+static inline acpi_thread_id acpi_os_get_thread_id(void)
+{
+       return (acpi_thread_id) (unsigned long)current;
+}
+
+/*
+ * When lockdep is enabled, the spin_lock_init() macro stringifies it's
+ * argument and uses that as a name for the lock in debugging.
+ * By executing spin_lock_init() in a macro the key changes from "lock" for
+ * all locks to the name of the argument of acpi_os_create_lock(), which
+ * prevents lockdep from reporting false positives for ACPICA locks.
+ */
+#define acpi_os_create_lock(__handle) \
+       ({ \
+               spinlock_t *lock = ACPI_ALLOCATE(sizeof(*lock)); \
+               if (lock) { \
+                       *(__handle) = lock; \
+                       spin_lock_init(*(__handle)); \
+               } \
+               lock ? AE_OK : AE_NO_MEMORY; \
+       })
+
+/*
+ * OSL interfaces added by Linux
+ */
+void early_acpi_os_unmap_memory(void __iomem * virt, acpi_size size);
+
+#endif                         /* __KERNEL__ */
+
+#endif                         /* __ACLINUXEX_H__ */
index 61109f2..ea4c7bb 100644 (file)
@@ -19,11 +19,13 @@ struct acpi_device;
 #if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
 extern int acpi_video_register(void);
 extern void acpi_video_unregister(void);
+extern void acpi_video_unregister_backlight(void);
 extern int acpi_video_get_edid(struct acpi_device *device, int type,
                               int device_id, void **edid);
 #else
 static inline int acpi_video_register(void) { return 0; }
 static inline void acpi_video_unregister(void) { return; }
+static inline void acpi_video_unregister_backlight(void) { return; }
 static inline int acpi_video_get_edid(struct acpi_device *device, int type,
                                      int device_id, void **edid)
 {
index 146e4ff..d647637 100644 (file)
 #define TRACE_SYSCALLS()
 #endif
 
-#ifdef CONFIG_CLKSRC_OF
-#define CLKSRC_OF_TABLES() . = ALIGN(8);                               \
-                          VMLINUX_SYMBOL(__clksrc_of_table) = .;       \
-                          *(__clksrc_of_table)                         \
-                          *(__clksrc_of_table_end)
-#else
-#define CLKSRC_OF_TABLES()
-#endif
 
-#ifdef CONFIG_IRQCHIP
-#define IRQCHIP_OF_MATCH_TABLE()                                       \
+#define ___OF_TABLE(cfg, name) _OF_TABLE_##cfg(name)
+#define __OF_TABLE(cfg, name)  ___OF_TABLE(cfg, name)
+#define OF_TABLE(cfg, name)    __OF_TABLE(config_enabled(cfg), name)
+#define _OF_TABLE_0(name)
+#define _OF_TABLE_1(name)                                              \
        . = ALIGN(8);                                                   \
-       VMLINUX_SYMBOL(__irqchip_begin) = .;                            \
-       *(__irqchip_of_table)                                           \
-       *(__irqchip_of_end)
-#else
-#define IRQCHIP_OF_MATCH_TABLE()
-#endif
-
-#ifdef CONFIG_COMMON_CLK
-#define CLK_OF_TABLES() . = ALIGN(8);                          \
-                       VMLINUX_SYMBOL(__clk_of_table) = .;     \
-                       *(__clk_of_table)                       \
-                       *(__clk_of_table_end)
-#else
-#define CLK_OF_TABLES()
-#endif
-
-#ifdef CONFIG_OF_RESERVED_MEM
-#define RESERVEDMEM_OF_TABLES()                                \
-       . = ALIGN(8);                                   \
-       VMLINUX_SYMBOL(__reservedmem_of_table) = .;     \
-       *(__reservedmem_of_table)                       \
-       *(__reservedmem_of_table_end)
-#else
-#define RESERVEDMEM_OF_TABLES()
-#endif
+       VMLINUX_SYMBOL(__##name##_of_table) = .;                        \
+       *(__##name##_of_table)                                          \
+       *(__##name##_of_table_end)
 
-#ifdef CONFIG_SMP
-#define CPU_METHOD_OF_TABLES() . = ALIGN(8);                               \
-                          VMLINUX_SYMBOL(__cpu_method_of_table_begin) = .; \
-                          *(__cpu_method_of_table)                         \
-                          VMLINUX_SYMBOL(__cpu_method_of_table_end) = .;
-#else
-#define CPU_METHOD_OF_TABLES()
-#endif
+#define CLKSRC_OF_TABLES()     OF_TABLE(CONFIG_CLKSRC_OF, clksrc)
+#define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip)
+#define CLK_OF_TABLES()                OF_TABLE(CONFIG_COMMON_CLK, clk)
+#define RESERVEDMEM_OF_TABLES()        OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem)
+#define CPU_METHOD_OF_TABLES() OF_TABLE(CONFIG_SMP, cpu_method)
+#define EARLYCON_OF_TABLES()   OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon)
 
 #define KERNEL_DTB()                                                   \
        STRUCT_ALIGN();                                                 \
        CLKSRC_OF_TABLES()                                              \
        CPU_METHOD_OF_TABLES()                                          \
        KERNEL_DTB()                                                    \
-       IRQCHIP_OF_MATCH_TABLE()
+       IRQCHIP_OF_MATCH_TABLE()                                        \
+       EARLYCON_OF_TABLES()
 
 #define INIT_TEXT                                                      \
        *(.init.text)                                                   \
index 7a8f2cd..358c01b 100644 (file)
@@ -37,6 +37,7 @@
 
 #include <linux/list.h>
 #include <linux/mod_devicetable.h>
+#include <linux/dynamic_debug.h>
 
 #include <acpi/acpi.h>
 #include <acpi/acpi_bus.h>
@@ -184,6 +185,8 @@ extern int ec_transaction(u8 command,
                           u8 *rdata, unsigned rdata_len);
 extern acpi_handle ec_get_handle(void);
 
+extern bool acpi_is_pnp_device(struct acpi_device *);
+
 #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
 
 typedef void (*wmi_notify_handler) (u32 value, void *context);
@@ -554,14 +557,20 @@ static inline int acpi_subsys_runtime_resume(struct device *dev) { return 0; }
 int acpi_dev_suspend_late(struct device *dev);
 int acpi_dev_resume_early(struct device *dev);
 int acpi_subsys_prepare(struct device *dev);
+void acpi_subsys_complete(struct device *dev);
 int acpi_subsys_suspend_late(struct device *dev);
 int acpi_subsys_resume_early(struct device *dev);
+int acpi_subsys_suspend(struct device *dev);
+int acpi_subsys_freeze(struct device *dev);
 #else
 static inline int acpi_dev_suspend_late(struct device *dev) { return 0; }
 static inline int acpi_dev_resume_early(struct device *dev) { return 0; }
 static inline int acpi_subsys_prepare(struct device *dev) { return 0; }
+static inline void acpi_subsys_complete(struct device *dev) {}
 static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; }
 static inline int acpi_subsys_resume_early(struct device *dev) { return 0; }
+static inline int acpi_subsys_suspend(struct device *dev) { return 0; }
+static inline int acpi_subsys_freeze(struct device *dev) { return 0; }
 #endif
 
 #if defined(CONFIG_ACPI) && defined(CONFIG_PM)
@@ -589,6 +598,14 @@ static inline __printf(3, 4) void
 acpi_handle_printk(const char *level, void *handle, const char *fmt, ...) {}
 #endif /* !CONFIG_ACPI */
 
+#if defined(CONFIG_ACPI) && defined(CONFIG_DYNAMIC_DEBUG)
+__printf(3, 4)
+void __acpi_handle_debug(struct _ddebug *descriptor, acpi_handle handle, const char *fmt, ...);
+#else
+#define __acpi_handle_debug(descriptor, handle, fmt, ...)              \
+       acpi_handle_printk(KERN_DEBUG, handle, fmt, ##__VA_ARGS__);
+#endif
+
 /*
  * acpi_handle_<level>: Print message with ACPI prefix and object path
  *
@@ -610,11 +627,19 @@ acpi_handle_printk(const char *level, void *handle, const char *fmt, ...) {}
 #define acpi_handle_info(handle, fmt, ...)                             \
        acpi_handle_printk(KERN_INFO, handle, fmt, ##__VA_ARGS__)
 
-/* REVISIT: Support CONFIG_DYNAMIC_DEBUG when necessary */
-#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
+#if defined(DEBUG)
 #define acpi_handle_debug(handle, fmt, ...)                            \
        acpi_handle_printk(KERN_DEBUG, handle, fmt, ##__VA_ARGS__)
 #else
+#if defined(CONFIG_DYNAMIC_DEBUG)
+#define acpi_handle_debug(handle, fmt, ...)                            \
+do {                                                                   \
+       DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt);                 \
+       if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT))          \
+               __acpi_handle_debug(&descriptor, handle, pr_fmt(fmt),   \
+                               ##__VA_ARGS__);                         \
+} while (0)
+#else
 #define acpi_handle_debug(handle, fmt, ...)                            \
 ({                                                                     \
        if (0)                                                          \
@@ -622,5 +647,6 @@ acpi_handle_printk(const char *level, void *handle, const char *fmt, ...) {}
        0;                                                              \
 })
 #endif
+#endif
 
 #endif /*_LINUX_ACPI_H*/
index 7264742..adb14a8 100644 (file)
@@ -40,6 +40,11 @@ enum backlight_type {
        BACKLIGHT_TYPE_MAX,
 };
 
+enum backlight_notification {
+       BACKLIGHT_REGISTERED,
+       BACKLIGHT_UNREGISTERED,
+};
+
 struct backlight_device;
 struct fb_info;
 
@@ -133,6 +138,8 @@ extern void devm_backlight_device_unregister(struct device *dev,
 extern void backlight_force_update(struct backlight_device *bd,
                                   enum backlight_update_reason reason);
 extern bool backlight_device_registered(enum backlight_type type);
+extern int backlight_register_notifier(struct notifier_block *nb);
+extern int backlight_unregister_notifier(struct notifier_block *nb);
 
 #define to_backlight_device(obj) container_of(obj, struct backlight_device, dev)
 
index 5119174..f295bab 100644 (file)
@@ -413,6 +413,37 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
                const char *parent_name, unsigned long flags,
                unsigned int mult, unsigned int div);
 
+/**
+ * struct clk_fractional_divider - adjustable fractional divider clock
+ *
+ * @hw:                handle between common and hardware-specific interfaces
+ * @reg:       register containing the divider
+ * @mshift:    shift to the numerator bit field
+ * @mwidth:    width of the numerator bit field
+ * @nshift:    shift to the denominator bit field
+ * @nwidth:    width of the denominator bit field
+ * @lock:      register lock
+ *
+ * Clock with adjustable fractional divider affecting its output frequency.
+ */
+
+struct clk_fractional_divider {
+       struct clk_hw   hw;
+       void __iomem    *reg;
+       u8              mshift;
+       u32             mmask;
+       u8              nshift;
+       u32             nmask;
+       u8              flags;
+       spinlock_t      *lock;
+};
+
+extern const struct clk_ops clk_fractional_divider_ops;
+struct clk *clk_register_fractional_divider(struct device *dev,
+               const char *name, const char *parent_name, unsigned long flags,
+               void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
+               u8 clk_divider_flags, spinlock_t *lock);
+
 /***
  * struct clk_composite - aggregate clock of mux, divider and gate clocks
  *
@@ -498,10 +529,7 @@ struct clk_onecell_data {
 
 extern struct of_device_id __clk_of_table;
 
-#define CLK_OF_DECLARE(name, compat, fn)                       \
-       static const struct of_device_id __clk_of_table_##name  \
-               __used __section(__clk_of_table)                \
-               = { .compatible = compat, .data = fn };
+#define CLK_OF_DECLARE(name, compat, fn) OF_DECLARE_1(clk, name, compat, fn)
 
 #ifdef CONFIG_OF
 int of_clk_add_provider(struct device_node *np,
index 67301a4..a16b497 100644 (file)
@@ -339,23 +339,13 @@ extern int clocksource_mmio_init(void __iomem *, const char *,
 
 extern int clocksource_i8253_init(void);
 
-struct device_node;
-typedef void(*clocksource_of_init_fn)(struct device_node *);
+#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \
+       OF_DECLARE_1(clksrc, name, compat, fn)
+
 #ifdef CONFIG_CLKSRC_OF
 extern void clocksource_of_init(void);
-
-#define CLOCKSOURCE_OF_DECLARE(name, compat, fn)                       \
-       static const struct of_device_id __clksrc_of_table_##name       \
-               __used __section(__clksrc_of_table)                     \
-                = { .compatible = compat,                              \
-                    .data = (fn == (clocksource_of_init_fn)NULL) ? fn : fn }
 #else
 static inline void clocksource_of_init(void) {}
-#define CLOCKSOURCE_OF_DECLARE(name, compat, fn)                       \
-       static const struct of_device_id __clksrc_of_table_##name       \
-               __attribute__((unused))                                 \
-                = { .compatible = compat,                              \
-                    .data = (fn == (clocksource_of_init_fn)NULL) ? fn : fn }
 #endif
 
 #endif /* _LINUX_CLOCKSOURCE_H */
index 5ae5100..3f45889 100644 (file)
@@ -110,6 +110,7 @@ struct cpufreq_policy {
        bool                    transition_ongoing; /* Tracks transition status */
        spinlock_t              transition_lock;
        wait_queue_head_t       transition_wait;
+       struct task_struct      *transition_task; /* Task which is doing the transition */
 };
 
 /* Only for ACPI */
@@ -468,6 +469,55 @@ struct cpufreq_frequency_table {
                                    * order */
 };
 
+#if defined(CONFIG_CPU_FREQ) && defined(CONFIG_PM_OPP)
+int dev_pm_opp_init_cpufreq_table(struct device *dev,
+                                 struct cpufreq_frequency_table **table);
+void dev_pm_opp_free_cpufreq_table(struct device *dev,
+                                  struct cpufreq_frequency_table **table);
+#else
+static inline int dev_pm_opp_init_cpufreq_table(struct device *dev,
+                                               struct cpufreq_frequency_table
+                                               **table)
+{
+       return -EINVAL;
+}
+
+static inline void dev_pm_opp_free_cpufreq_table(struct device *dev,
+                                                struct cpufreq_frequency_table
+                                                **table)
+{
+}
+#endif
+
+static inline bool cpufreq_next_valid(struct cpufreq_frequency_table **pos)
+{
+       while ((*pos)->frequency != CPUFREQ_TABLE_END)
+               if ((*pos)->frequency != CPUFREQ_ENTRY_INVALID)
+                       return true;
+               else
+                       (*pos)++;
+       return false;
+}
+
+/*
+ * cpufreq_for_each_entry -    iterate over a cpufreq_frequency_table
+ * @pos:       the cpufreq_frequency_table * to use as a loop cursor.
+ * @table:     the cpufreq_frequency_table * to iterate over.
+ */
+
+#define cpufreq_for_each_entry(pos, table)     \
+       for (pos = table; pos->frequency != CPUFREQ_TABLE_END; pos++)
+
+/*
+ * cpufreq_for_each_valid_entry -     iterate over a cpufreq_frequency_table
+ *     excluding CPUFREQ_ENTRY_INVALID frequencies.
+ * @pos:        the cpufreq_frequency_table * to use as a loop cursor.
+ * @table:      the cpufreq_frequency_table * to iterate over.
+ */
+
+#define cpufreq_for_each_valid_entry(pos, table)       \
+       for (pos = table; cpufreq_next_valid(&pos); pos++)
+
 int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
                                    struct cpufreq_frequency_table *table);
 
index d48dc00..f1863dc 100644 (file)
@@ -181,6 +181,12 @@ extern struct devfreq *devfreq_add_device(struct device *dev,
                                  const char *governor_name,
                                  void *data);
 extern int devfreq_remove_device(struct devfreq *devfreq);
+extern struct devfreq *devm_devfreq_add_device(struct device *dev,
+                                 struct devfreq_dev_profile *profile,
+                                 const char *governor_name,
+                                 void *data);
+extern void devm_devfreq_remove_device(struct device *dev,
+                                 struct devfreq *devfreq);
 
 /* Supposed to be called by PM_SLEEP/PM_RUNTIME callbacks */
 extern int devfreq_suspend_device(struct devfreq *devfreq);
@@ -193,6 +199,10 @@ extern int devfreq_register_opp_notifier(struct device *dev,
                                         struct devfreq *devfreq);
 extern int devfreq_unregister_opp_notifier(struct device *dev,
                                           struct devfreq *devfreq);
+extern int devm_devfreq_register_opp_notifier(struct device *dev,
+                                             struct devfreq *devfreq);
+extern void devm_devfreq_unregister_opp_notifier(struct device *dev,
+                                               struct devfreq *devfreq);
 
 #if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND)
 /**
@@ -220,7 +230,7 @@ static inline struct devfreq *devfreq_add_device(struct device *dev,
                                          const char *governor_name,
                                          void *data)
 {
-       return NULL;
+       return ERR_PTR(-ENOSYS);
 }
 
 static inline int devfreq_remove_device(struct devfreq *devfreq)
@@ -228,6 +238,19 @@ static inline int devfreq_remove_device(struct devfreq *devfreq)
        return 0;
 }
 
+static inline struct devfreq *devm_devfreq_add_device(struct device *dev,
+                                       struct devfreq_dev_profile *profile,
+                                       const char *governor_name,
+                                       void *data)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline void devm_devfreq_remove_device(struct device *dev,
+                                       struct devfreq *devfreq)
+{
+}
+
 static inline int devfreq_suspend_device(struct devfreq *devfreq)
 {
        return 0;
@@ -256,6 +279,16 @@ static inline int devfreq_unregister_opp_notifier(struct device *dev,
        return -EINVAL;
 }
 
+static inline int devm_devfreq_register_opp_notifier(struct device *dev,
+                                                    struct devfreq *devfreq)
+{
+       return -EINVAL;
+}
+
+static inline void devm_devfreq_unregister_opp_notifier(struct device *dev,
+                                                       struct devfreq *devfreq)
+{
+}
 #endif /* CONFIG_PM_DEVFREQ */
 
 #endif /* __LINUX_DEVFREQ_H__ */
index fe6ac95..b6bfda9 100644 (file)
@@ -47,6 +47,7 @@ struct device_node;
 
 #define FB_MISC_PRIM_COLOR     1
 #define FB_MISC_1ST_DETAIL     2       /* First Detailed Timing is preferred */
+#define FB_MISC_HDMI           4
 struct fb_chroma {
        __u32 redx;     /* in fraction of 1024 */
        __u32 greenx;
@@ -641,7 +642,7 @@ static inline void unlock_fb_info(struct fb_info *info)
 static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
                                           u8 *src, u32 s_pitch, u32 height)
 {
-       int i, j;
+       u32 i, j;
 
        d_pitch -= s_pitch;
 
index c3683bd..d4b7683 100644 (file)
@@ -367,6 +367,9 @@ static inline int fw_stream_packet_destination_id(int tag, int channel, int sy)
        return tag << 14 | channel << 8 | sy;
 }
 
+void fw_schedule_bus_reset(struct fw_card *card, bool delayed,
+                          bool short_reset);
+
 struct fw_descriptor {
        struct list_head link;
        size_t length;
index 720e3a1..77632cf 100644 (file)
@@ -233,11 +233,6 @@ struct hid_item {
 #define HID_DG_BARRELSWITCH    0x000d0044
 #define HID_DG_ERASER          0x000d0045
 #define HID_DG_TABLETPICK      0x000d0046
-/*
- * as of May 20, 2009 the usages below are not yet in the official USB spec
- * but are being pushed by Microsft as described in their paper "Digitizer
- * Drivers for Windows Touch and Pen-Based Computers"
- */
 #define HID_DG_CONFIDENCE      0x000d0047
 #define HID_DG_WIDTH           0x000d0048
 #define HID_DG_HEIGHT          0x000d0049
@@ -246,6 +241,8 @@ struct hid_item {
 #define HID_DG_DEVICEINDEX     0x000d0053
 #define HID_DG_CONTACTCOUNT    0x000d0054
 #define HID_DG_CONTACTMAX      0x000d0055
+#define HID_DG_BARRELSWITCH2   0x000d005a
+#define HID_DG_TOOLSERIALNUMBER        0x000d005b
 
 /*
  * HID report types --- Ouch! HID spec says 1 2 3!
@@ -299,12 +296,20 @@ struct hid_item {
 
 /*
  * HID device groups
+ *
+ * Note: HID_GROUP_ANY is declared in linux/mod_devicetable.h
+ * and has a value of 0x0000
  */
 #define HID_GROUP_GENERIC                      0x0001
 #define HID_GROUP_MULTITOUCH                   0x0002
 #define HID_GROUP_SENSOR_HUB                   0x0003
 #define HID_GROUP_MULTITOUCH_WIN_8             0x0004
 
+/*
+ * Vendor specific HID device groups
+ */
+#define HID_GROUP_RMI                          0x0100
+
 /*
  * This is the global environment of the parser. This information is
  * persistent for main-items. The global environment can be saved and
@@ -570,6 +575,8 @@ struct hid_descriptor {
        .bus = BUS_USB, .vendor = (ven), .product = (prod)
 #define HID_BLUETOOTH_DEVICE(ven, prod)                                        \
        .bus = BUS_BLUETOOTH, .vendor = (ven), .product = (prod)
+#define HID_I2C_DEVICE(ven, prod)                              \
+       .bus = BUS_I2C, .vendor = (ven), .product = (prod)
 
 #define HID_REPORT_ID(rep) \
        .report_type = (rep)
index 7d21cf9..970c681 100644 (file)
@@ -134,6 +134,8 @@ static inline bool is_error_page(struct page *page)
 #define KVM_REQ_EPR_EXIT          20
 #define KVM_REQ_SCAN_IOAPIC       21
 #define KVM_REQ_GLOBAL_CLOCK_UPDATE 22
+#define KVM_REQ_ENABLE_IBS        23
+#define KVM_REQ_DISABLE_IBS       24
 
 #define KVM_USERSPACE_IRQ_SOURCE_ID            0
 #define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID       1
@@ -163,6 +165,7 @@ enum kvm_bus {
        KVM_MMIO_BUS,
        KVM_PIO_BUS,
        KVM_VIRTIO_CCW_NOTIFY_BUS,
+       KVM_FAST_MMIO_BUS,
        KVM_NR_BUSES
 };
 
@@ -367,6 +370,7 @@ struct kvm {
        struct mm_struct *mm; /* userspace tied to this vm */
        struct kvm_memslots *memslots;
        struct srcu_struct srcu;
+       struct srcu_struct irq_srcu;
 #ifdef CONFIG_KVM_APIC_ARCHITECTURE
        u32 bsp_vcpu_id;
 #endif
@@ -410,9 +414,7 @@ struct kvm {
        unsigned long mmu_notifier_seq;
        long mmu_notifier_count;
 #endif
-       /* Protected by mmu_lock */
-       bool tlbs_dirty;
-
+       long tlbs_dirty;
        struct list_head devices;
 };
 
@@ -879,6 +881,13 @@ static inline hpa_t pfn_to_hpa(pfn_t pfn)
        return (hpa_t)pfn << PAGE_SHIFT;
 }
 
+static inline bool kvm_is_error_gpa(struct kvm *kvm, gpa_t gpa)
+{
+       unsigned long hva = gfn_to_hva(kvm, gpa_to_gfn(gpa));
+
+       return kvm_is_error_hva(hva);
+}
+
 static inline void kvm_migrate_timers(struct kvm_vcpu *vcpu)
 {
        set_bit(KVM_REQ_MIGRATE_TIMER, &vcpu->requests);
index fa36286..196b34c 100644 (file)
@@ -764,4 +764,26 @@ static inline int of_get_available_child_count(const struct device_node *np)
        return num;
 }
 
+#ifdef CONFIG_OF
+#define _OF_DECLARE(table, name, compat, fn, fn_type)                  \
+       static const struct of_device_id __of_table_##name              \
+               __used __section(__##table##_of_table)                  \
+                = { .compatible = compat,                              \
+                    .data = (fn == (fn_type)NULL) ? fn : fn  }
+#else
+#define _OF_DECLARE(table, name, compat, fn, fn_type)                                  \
+       static const struct of_device_id __of_table_##name              \
+               __attribute__((unused))                                 \
+                = { .compatible = compat,                              \
+                    .data = (fn == (fn_type)NULL) ? fn : fn }
+#endif
+
+typedef int (*of_init_fn_2)(struct device_node *, struct device_node *);
+typedef void (*of_init_fn_1)(struct device_node *);
+
+#define OF_DECLARE_1(table, name, compat, fn) \
+               _OF_DECLARE(table, name, compat, fn, of_init_fn_1)
+#define OF_DECLARE_2(table, name, compat, fn) \
+               _OF_DECLARE(table, name, compat, fn, of_init_fn_2)
+
 #endif /* _LINUX_OF_H */
index 5f6ed6b..906ca76 100644 (file)
@@ -40,7 +40,6 @@ extern u64 of_translate_dma_address(struct device_node *dev,
 
 #ifdef CONFIG_OF_ADDRESS
 extern u64 of_translate_address(struct device_node *np, const __be32 *addr);
-extern bool of_can_translate_address(struct device_node *dev);
 extern int of_address_to_resource(struct device_node *dev, int index,
                                  struct resource *r);
 extern struct device_node *of_find_matching_node_by_address(
index ddd7219..0511789 100644 (file)
 
 /* Definitions used by the flattened device tree */
 #define OF_DT_HEADER           0xd00dfeed      /* marker */
-#define OF_DT_BEGIN_NODE       0x1             /* Start of node, full name */
-#define OF_DT_END_NODE         0x2             /* End node */
-#define OF_DT_PROP             0x3             /* Property: name off, size,
-                                                * content */
-#define OF_DT_NOP              0x4             /* nop */
-#define OF_DT_END              0x9
-
-#define OF_DT_VERSION          0x10
 
 #ifndef __ASSEMBLY__
-/*
- * This is what gets passed to the kernel by prom_init or kexec
- *
- * The dt struct contains the device tree structure, full pathes and
- * property contents. The dt strings contain a separate block with just
- * the strings for the property names, and is fully page aligned and
- * self contained in a page, so that it can be kept around by the kernel,
- * each property name appears only once in this page (cheap compression)
- *
- * the mem_rsvmap contains a map of reserved ranges of physical memory,
- * passing it here instead of in the device-tree itself greatly simplifies
- * the job of everybody. It's just a list of u64 pairs (base/size) that
- * ends when size is 0
- */
-struct boot_param_header {
-       __be32  magic;                  /* magic word OF_DT_HEADER */
-       __be32  totalsize;              /* total size of DT block */
-       __be32  off_dt_struct;          /* offset to structure */
-       __be32  off_dt_strings;         /* offset to strings */
-       __be32  off_mem_rsvmap;         /* offset to memory reserve map */
-       __be32  version;                /* format version */
-       __be32  last_comp_version;      /* last compatible version */
-       /* version 2 fields below */
-       __be32  boot_cpuid_phys;        /* Physical CPU id we're booting on */
-       /* version 3 fields below */
-       __be32  dt_strings_size;        /* size of the DT strings block */
-       /* version 17 fields below */
-       __be32  dt_struct_size;         /* size of the DT structure block */
-};
 
 #if defined(CONFIG_OF_FLATTREE)
 
 struct device_node;
 
 /* For scanning an arbitrary device-tree at any time */
-extern char *of_fdt_get_string(struct boot_param_header *blob, u32 offset);
-extern void *of_fdt_get_property(struct boot_param_header *blob,
+extern char *of_fdt_get_string(const void *blob, u32 offset);
+extern void *of_fdt_get_property(const void *blob,
                                 unsigned long node,
                                 const char *name,
-                                unsigned long *size);
-extern int of_fdt_is_compatible(struct boot_param_header *blob,
+                                int *size);
+extern int of_fdt_is_compatible(const void *blob,
                                unsigned long node,
                                const char *compat);
-extern int of_fdt_match(struct boot_param_header *blob, unsigned long node,
+extern int of_fdt_match(const void *blob, unsigned long node,
                        const char *const *compat);
 extern void of_fdt_unflatten_tree(unsigned long *blob,
                               struct device_node **mynodes);
@@ -78,21 +41,21 @@ extern void of_fdt_unflatten_tree(unsigned long *blob,
 /* TBD: Temporary export of fdt globals - remove when code fully merged */
 extern int __initdata dt_root_addr_cells;
 extern int __initdata dt_root_size_cells;
-extern struct boot_param_header *initial_boot_params;
+extern void *initial_boot_params;
+
+extern char __dtb_start[];
+extern char __dtb_end[];
 
 /* For scanning the flat device-tree at boot time */
-extern char *find_flat_dt_string(u32 offset);
 extern int of_scan_flat_dt(int (*it)(unsigned long node, const char *uname,
                                     int depth, void *data),
                           void *data);
-extern void *of_get_flat_dt_prop(unsigned long node, const char *name,
-                                unsigned long *size);
+extern const void *of_get_flat_dt_prop(unsigned long node, const char *name,
+                                      int *size);
 extern int of_flat_dt_is_compatible(unsigned long node, const char *name);
 extern int of_flat_dt_match(unsigned long node, const char *const *matches);
 extern unsigned long of_get_flat_dt_root(void);
-extern int of_scan_flat_dt_by_path(const char *path,
-       int (*it)(unsigned long node, const char *name, int depth, void *data),
-       void *data);
+extern int of_get_flat_dt_size(void);
 
 extern int early_init_dt_scan_chosen(unsigned long node, const char *uname,
                                     int depth, void *data);
@@ -103,7 +66,7 @@ extern void early_init_dt_add_memory_arch(u64 base, u64 size);
 extern int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size,
                                             bool no_map);
 extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align);
-extern u64 dt_mem_next_cell(int s, __be32 **cellp);
+extern u64 dt_mem_next_cell(int s, const __be32 **cellp);
 
 /* Early flat tree scan hooks */
 extern int early_init_dt_scan_root(unsigned long node, const char *uname,
@@ -120,6 +83,7 @@ extern void unflatten_device_tree(void);
 extern void unflatten_and_copy_device_tree(void);
 extern void early_init_devtree(void *);
 extern void early_get_first_memblock_info(void *, phys_addr_t *);
+extern u64 fdt_translate_address(const void *blob, int node_offset);
 #else /* CONFIG_OF_FLATTREE */
 static inline void early_init_fdt_scan_reserved_mem(void) {}
 static inline const char *of_flat_dt_get_machine_name(void) { return NULL; }
index 6404253..bfec136 100644 (file)
@@ -45,6 +45,7 @@ extern void of_irq_init(const struct of_device_id *matches);
 #ifdef CONFIG_OF_IRQ
 extern int of_irq_count(struct device_node *dev);
 extern int of_irq_get(struct device_node *dev, int index);
+extern int of_irq_get_byname(struct device_node *dev, const char *name);
 #else
 static inline int of_irq_count(struct device_node *dev)
 {
@@ -54,6 +55,10 @@ static inline int of_irq_get(struct device_node *dev, int index)
 {
        return 0;
 }
+static inline int of_irq_get_byname(struct device_node *dev, const char *name)
+{
+       return 0;
+}
 #endif
 
 #if defined(CONFIG_OF)
index 1a1f5ff..dde3a4a 100644 (file)
@@ -6,14 +6,44 @@
 
 struct pci_dev;
 struct of_phandle_args;
-int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq);
-int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
-
 struct device_node;
+
+#ifdef CONFIG_OF
+int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq);
 struct device_node *of_pci_find_child_device(struct device_node *parent,
                                             unsigned int devfn);
 int of_pci_get_devfn(struct device_node *np);
+int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
 int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
+#else
+static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
+{
+       return 0;
+}
+
+static inline struct device_node *of_pci_find_child_device(struct device_node *parent,
+                                            unsigned int devfn)
+{
+       return NULL;
+}
+
+static inline int of_pci_get_devfn(struct device_node *np)
+{
+       return -EINVAL;
+}
+
+static inline int
+of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+       return 0;
+}
+
+static inline int
+of_pci_parse_bus_range(struct device_node *node, struct resource *res)
+{
+       return -EINVAL;
+}
+#endif
 
 #if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI)
 int of_pci_msi_chip_add(struct msi_chip *chip);
index b1010ee..d96e1ba 100644 (file)
@@ -37,7 +37,7 @@
  * Note: Using an auxdata lookup table should be considered a last resort when
  * converting a platform to use the DT.  Normally the automatically generated
  * device name will not matter, and drivers should obtain data from the device
- * node instead of from an anonymouns platform_data pointer.
+ * node instead of from an anonymous platform_data pointer.
  */
 struct of_dev_auxdata {
        char *compatible;
index 9b1fbb7..4669ddf 100644 (file)
@@ -21,33 +21,19 @@ struct reserved_mem_ops {
                                  struct device *dev);
 };
 
-typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem,
-                                     unsigned long node, const char *uname);
+typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem);
+
+#define RESERVEDMEM_OF_DECLARE(name, compat, init)                     \
+       _OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn)
 
 #ifdef CONFIG_OF_RESERVED_MEM
 void fdt_init_reserved_mem(void);
 void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
                               phys_addr_t base, phys_addr_t size);
-
-#define RESERVEDMEM_OF_DECLARE(name, compat, init)                     \
-       static const struct of_device_id __reservedmem_of_table_##name  \
-               __used __section(__reservedmem_of_table)                \
-                = { .compatible = compat,                              \
-                    .data = (init == (reservedmem_of_init_fn)NULL) ?   \
-                               init : init }
-
 #else
 static inline void fdt_init_reserved_mem(void) { }
 static inline void fdt_reserved_mem_save_node(unsigned long node,
                const char *uname, phys_addr_t base, phys_addr_t size) { }
-
-#define RESERVEDMEM_OF_DECLARE(name, compat, init)                     \
-       static const struct of_device_id __reservedmem_of_table_##name  \
-               __attribute__((unused))                                 \
-                = { .compatible = compat,                              \
-                    .data = (init == (reservedmem_of_init_fn)NULL) ?   \
-                               init : init }
-
 #endif
 
 #endif /* __OF_RESERVED_MEM_H */
index 7944cdc..c29a6de 100644 (file)
@@ -393,7 +393,7 @@ extern int omap_modify_dma_chain_params(int chain_id,
 extern int omap_dma_chain_status(int chain_id);
 #endif
 
-#if defined(CONFIG_ARCH_OMAP1) && defined(CONFIG_FB_OMAP)
+#if defined(CONFIG_ARCH_OMAP1) && IS_ENABLED(CONFIG_FB_OMAP)
 #include <mach/lcd_dma.h>
 #else
 static inline int omap_lcd_dma_running(void)
diff --git a/include/linux/platform_data/adau17x1.h b/include/linux/platform_data/adau17x1.h
new file mode 100644 (file)
index 0000000..a81766c
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961/ADAU1781/ADAU1781 codecs
+ *
+ * Copyright 2011-2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __LINUX_PLATFORM_DATA_ADAU17X1_H__
+#define __LINUX_PLATFORM_DATA_ADAU17X1_H__
+
+/**
+ * enum adau17x1_micbias_voltage - Microphone bias voltage
+ * @ADAU17X1_MICBIAS_0_90_AVDD: 0.9 * AVDD
+ * @ADAU17X1_MICBIAS_0_65_AVDD: 0.65 * AVDD
+ */
+enum adau17x1_micbias_voltage {
+       ADAU17X1_MICBIAS_0_90_AVDD = 0,
+       ADAU17X1_MICBIAS_0_65_AVDD = 1,
+};
+
+/**
+ * enum adau1761_digmic_jackdet_pin_mode - Configuration of the JACKDET/MICIN pin
+ * @ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: Disable the pin
+ * @ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC: Configure the pin for usage as
+ *   digital microphone input.
+ * @ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT: Configure the pin for jack
+ *  insertion detection.
+ */
+enum adau1761_digmic_jackdet_pin_mode {
+       ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE,
+       ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC,
+       ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT,
+};
+
+/**
+ * adau1761_jackdetect_debounce_time - Jack insertion detection debounce time
+ * @ADAU1761_JACKDETECT_DEBOUNCE_5MS: 5 milliseconds
+ * @ADAU1761_JACKDETECT_DEBOUNCE_10MS: 10 milliseconds
+ * @ADAU1761_JACKDETECT_DEBOUNCE_20MS: 20 milliseconds
+ * @ADAU1761_JACKDETECT_DEBOUNCE_40MS: 40 milliseconds
+ */
+enum adau1761_jackdetect_debounce_time {
+       ADAU1761_JACKDETECT_DEBOUNCE_5MS = 0,
+       ADAU1761_JACKDETECT_DEBOUNCE_10MS = 1,
+       ADAU1761_JACKDETECT_DEBOUNCE_20MS = 2,
+       ADAU1761_JACKDETECT_DEBOUNCE_40MS = 3,
+};
+
+/**
+ * enum adau1761_output_mode - Output mode configuration
+ * @ADAU1761_OUTPUT_MODE_HEADPHONE: Headphone output
+ * @ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS: Capless headphone output
+ * @ADAU1761_OUTPUT_MODE_LINE: Line output
+ */
+enum adau1761_output_mode {
+       ADAU1761_OUTPUT_MODE_HEADPHONE,
+       ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS,
+       ADAU1761_OUTPUT_MODE_LINE,
+};
+
+/**
+ * struct adau1761_platform_data - ADAU1761 Codec driver platform data
+ * @input_differential: If true the input pins will be configured in
+ *  differential mode.
+ * @lineout_mode: Output mode for the LOUT/ROUT pins
+ * @headphone_mode: Output mode for the LHP/RHP pins
+ * @digmic_jackdetect_pin_mode: JACKDET/MICIN pin configuration
+ * @jackdetect_debounce_time: Jack insertion detection debounce time.
+ *  Note: This value will only be used, if the JACKDET/MICIN pin is configured
+ *  for jack insertion detection.
+ * @jackdetect_active_low: If true the jack insertion detection is active low.
+ *  Othwise it will be active high.
+ * @micbias_voltage: Microphone voltage bias
+ */
+struct adau1761_platform_data {
+       bool input_differential;
+       enum adau1761_output_mode lineout_mode;
+       enum adau1761_output_mode headphone_mode;
+
+       enum adau1761_digmic_jackdet_pin_mode digmic_jackdetect_pin_mode;
+
+       enum adau1761_jackdetect_debounce_time jackdetect_debounce_time;
+       bool jackdetect_active_low;
+
+       enum adau17x1_micbias_voltage micbias_voltage;
+};
+
+/**
+ * struct adau1781_platform_data - ADAU1781 Codec driver platform data
+ * @left_input_differential: If true configure the left input as
+ * differential input.
+ * @right_input_differential: If true configure the right input as differntial
+ *  input.
+ * @use_dmic: If true configure the MIC pins as digital microphone pins instead
+ *  of analog microphone pins.
+ * @micbias_voltage: Microphone voltage bias
+ */
+struct adau1781_platform_data {
+       bool left_input_differential;
+       bool right_input_differential;
+
+       bool use_dmic;
+
+       enum adau17x1_micbias_voltage micbias_voltage;
+};
+
+#endif
diff --git a/include/linux/platform_data/mipi-csis.h b/include/linux/platform_data/mipi-csis.h
deleted file mode 100644 (file)
index c2fd902..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd.
- *
- * Samsung S5P/Exynos SoC series MIPI CSIS device support
- *
- * 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 __PLAT_SAMSUNG_MIPI_CSIS_H_
-#define __PLAT_SAMSUNG_MIPI_CSIS_H_ __FILE__
-
-/**
- * struct s5p_platform_mipi_csis - platform data for S5P MIPI-CSIS driver
- * @clk_rate:    bus clock frequency
- * @wclk_source: CSI wrapper clock selection: 0 - bus clock, 1 - ext. SCLK_CAM
- * @lanes:       number of data lanes used
- * @hs_settle:   HS-RX settle time
- */
-struct s5p_platform_mipi_csis {
-       unsigned long clk_rate;
-       u8 wclk_source;
-       u8 lanes;
-       u8 hs_settle;
-};
-
-#endif /* __PLAT_SAMSUNG_MIPI_CSIS_H_ */
index d915d03..72c0fe0 100644 (file)
@@ -93,13 +93,23 @@ typedef struct pm_message {
  *     been registered) to recover from the race condition.
  *     This method is executed for all kinds of suspend transitions and is
  *     followed by one of the suspend callbacks: @suspend(), @freeze(), or
- *     @poweroff().  The PM core executes subsystem-level @prepare() for all
- *     devices before starting to invoke suspend callbacks for any of them, so
- *     generally devices may be assumed to be functional or to respond to
- *     runtime resume requests while @prepare() is being executed.  However,
- *     device drivers may NOT assume anything about the availability of user
- *     space at that time and it is NOT valid to request firmware from within
- *     @prepare() (it's too late to do that).  It also is NOT valid to allocate
+ *     @poweroff().  If the transition is a suspend to memory or standby (that
+ *     is, not related to hibernation), the return value of @prepare() may be
+ *     used to indicate to the PM core to leave the device in runtime suspend
+ *     if applicable.  Namely, if @prepare() returns a positive number, the PM
+ *     core will understand that as a declaration that the device appears to be
+ *     runtime-suspended and it may be left in that state during the entire
+ *     transition and during the subsequent resume if all of its descendants
+ *     are left in runtime suspend too.  If that happens, @complete() will be
+ *     executed directly after @prepare() and it must ensure the proper
+ *     functioning of the device after the system resume.
+ *     The PM core executes subsystem-level @prepare() for all devices before
+ *     starting to invoke suspend callbacks for any of them, so generally
+ *     devices may be assumed to be functional or to respond to runtime resume
+ *     requests while @prepare() is being executed.  However, device drivers
+ *     may NOT assume anything about the availability of user space at that
+ *     time and it is NOT valid to request firmware from within @prepare()
+ *     (it's too late to do that).  It also is NOT valid to allocate
  *     substantial amounts of memory from @prepare() in the GFP_KERNEL mode.
  *     [To work around these limitations, drivers may register suspend and
  *     hibernation notifiers to be executed before the freezing of tasks.]
@@ -112,7 +122,16 @@ typedef struct pm_message {
  *     of the other devices that the PM core has unsuccessfully attempted to
  *     suspend earlier).
  *     The PM core executes subsystem-level @complete() after it has executed
- *     the appropriate resume callbacks for all devices.
+ *     the appropriate resume callbacks for all devices.  If the corresponding
+ *     @prepare() at the beginning of the suspend transition returned a
+ *     positive number and the device was left in runtime suspend (without
+ *     executing any suspend and resume callbacks for it), @complete() will be
+ *     the only callback executed for the device during resume.  In that case,
+ *     @complete() must be prepared to do whatever is necessary to ensure the
+ *     proper functioning of the device after the system resume.  To this end,
+ *     @complete() can check the power.direct_complete flag of the device to
+ *     learn whether (unset) or not (set) the previous suspend and resume
+ *     callbacks have been executed for it.
  *
  * @suspend: Executed before putting the system into a sleep state in which the
  *     contents of main memory are preserved.  The exact action to perform
@@ -546,6 +565,7 @@ struct dev_pm_info {
        bool                    is_late_suspended:1;
        bool                    ignore_children:1;
        bool                    early_init:1;   /* Owned by the PM core */
+       bool                    direct_complete:1;      /* Owned by the PM core */
        spinlock_t              lock;
 #ifdef CONFIG_PM_SLEEP
        struct list_head        entry;
index 5151b00..0330217 100644 (file)
@@ -15,7 +15,6 @@
 #define __LINUX_OPP_H__
 
 #include <linux/err.h>
-#include <linux/cpufreq.h>
 #include <linux/notifier.h>
 
 struct dev_pm_opp;
@@ -117,23 +116,4 @@ static inline int of_init_opp_table(struct device *dev)
 }
 #endif
 
-#if defined(CONFIG_CPU_FREQ) && defined(CONFIG_PM_OPP)
-int dev_pm_opp_init_cpufreq_table(struct device *dev,
-                           struct cpufreq_frequency_table **table);
-void dev_pm_opp_free_cpufreq_table(struct device *dev,
-                               struct cpufreq_frequency_table **table);
-#else
-static inline int dev_pm_opp_init_cpufreq_table(struct device *dev,
-                           struct cpufreq_frequency_table **table)
-{
-       return -EINVAL;
-}
-
-static inline
-void dev_pm_opp_free_cpufreq_table(struct device *dev,
-                               struct cpufreq_frequency_table **table)
-{
-}
-#endif         /* CONFIG_CPU_FREQ */
-
 #endif         /* __LINUX_OPP_H__ */
index 2a5897a..43fd671 100644 (file)
@@ -101,6 +101,11 @@ static inline bool pm_runtime_status_suspended(struct device *dev)
        return dev->power.runtime_status == RPM_SUSPENDED;
 }
 
+static inline bool pm_runtime_suspended_if_enabled(struct device *dev)
+{
+       return pm_runtime_status_suspended(dev) && dev->power.disable_depth == 1;
+}
+
 static inline bool pm_runtime_enabled(struct device *dev)
 {
        return !dev->power.disable_depth;
@@ -150,6 +155,7 @@ static inline void device_set_run_wake(struct device *dev, bool enable) {}
 static inline bool pm_runtime_suspended(struct device *dev) { return false; }
 static inline bool pm_runtime_active(struct device *dev) { return true; }
 static inline bool pm_runtime_status_suspended(struct device *dev) { return false; }
+static inline bool pm_runtime_suspended_if_enabled(struct device *dev) { return false; }
 static inline bool pm_runtime_enabled(struct device *dev) { return false; }
 
 static inline void pm_runtime_no_callbacks(struct device *dev) {}
index c9dc4e0..f2b76ae 100644 (file)
@@ -264,6 +264,8 @@ static inline int power_supply_is_system_supplied(void) { return -ENOSYS; }
 
 extern int power_supply_register(struct device *parent,
                                 struct power_supply *psy);
+extern int power_supply_register_no_ws(struct device *parent,
+                                struct power_supply *psy);
 extern void power_supply_unregister(struct power_supply *psy);
 extern int power_supply_powers(struct power_supply *psy, struct device *dev);
 
index 7a15b5b..5bbb809 100644 (file)
@@ -294,6 +294,9 @@ struct earlycon_device {
 int setup_earlycon(char *buf, const char *match,
                   int (*setup)(struct earlycon_device *, const char *));
 
+extern int of_setup_earlycon(unsigned long addr,
+                            int (*setup)(struct earlycon_device *, const char *));
+
 #define EARLYCON_DECLARE(name, func) \
 static int __init name ## _setup_earlycon(char *buf) \
 { \
@@ -301,6 +304,9 @@ static int __init name ## _setup_earlycon(char *buf) \
 } \
 early_param("earlycon", name ## _setup_earlycon);
 
+#define OF_EARLYCON_DECLARE(name, compat, fn)                          \
+       _OF_DECLARE(earlycon, name, compat, fn, void *)
+
 struct uart_port *uart_get_console(struct uart_port *ports, int nr,
                                   struct console *c);
 void uart_parse_options(char *options, int *baud, int *parity, int *bits,
index ac889c5..d36977e 100644 (file)
@@ -52,6 +52,9 @@ extern int strncasecmp(const char *s1, const char *s2, size_t n);
 #ifndef __HAVE_ARCH_STRCHR
 extern char * strchr(const char *,int);
 #endif
+#ifndef __HAVE_ARCH_STRCHRNUL
+extern char * strchrnul(const char *,int);
+#endif
 #ifndef __HAVE_ARCH_STRNCHR
 extern char * strnchr(const char *, size_t, int);
 #endif
index f73cabf..91d66fd 100644 (file)
@@ -187,6 +187,11 @@ struct platform_suspend_ops {
        void (*recover)(void);
 };
 
+struct platform_freeze_ops {
+       int (*begin)(void);
+       void (*end)(void);
+};
+
 #ifdef CONFIG_SUSPEND
 /**
  * suspend_set_ops - set platform dependent suspend operations
@@ -194,6 +199,7 @@ struct platform_suspend_ops {
  */
 extern void suspend_set_ops(const struct platform_suspend_ops *ops);
 extern int suspend_valid_only_mem(suspend_state_t state);
+extern void freeze_set_ops(const struct platform_freeze_ops *ops);
 extern void freeze_wake(void);
 
 /**
@@ -220,6 +226,7 @@ extern int pm_suspend(suspend_state_t state);
 
 static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {}
 static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; }
+static inline void freeze_set_ops(const struct platform_freeze_ops *ops) {}
 static inline void freeze_wake(void) {}
 #endif /* !CONFIG_SUSPEND */
 
index d262a3a..c6b3937 100644 (file)
@@ -21,6 +21,8 @@
 #ifndef _ADV7604_
 #define _ADV7604_
 
+#include <linux/types.h>
+
 /* Analog input muxing modes (AFE register 0x02, [2:0]) */
 enum adv7604_ain_sel {
        ADV7604_AIN1_2_3_NC_SYNC_1_2 = 0,
index 8dffffe..637749a 100644 (file)
@@ -16,6 +16,7 @@
 /* Header files */
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-fh.h>
 #include <media/videobuf2-dma-contig.h>
 #include <media/davinci/vpbe_types.h>
 #include <media/davinci/vpbe_osd.h>
@@ -94,8 +95,6 @@ struct vpbe_layer {
         * has selected
         */
        enum v4l2_memory memory;
-       /* Used to keep track of state of the priority */
-       struct v4l2_prio_state prio;
        /* Used to store pixel format */
        struct v4l2_pix_format pix_fmt;
        enum v4l2_field buf_field;
@@ -134,14 +133,13 @@ struct vpbe_display {
 
 /* File handle structure */
 struct vpbe_fh {
+       struct v4l2_fh fh;
        /* vpbe device structure */
        struct vpbe_display *disp_dev;
        /* pointer to layer object for opened device */
        struct vpbe_layer *layer;
        /* Indicates whether this file handle is doing IO */
        unsigned char io_allowed;
-       /* Used to keep track priority of this instance */
-       enum v4l2_priority prio;
 };
 
 struct buf_config_params {
index cc973ed..288772e 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/videodev2.h>
 #include <linux/clk.h>
 #include <linux/i2c.h>
+#include <media/v4l2-fh.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-device.h>
 #include <media/videobuf-dma-contig.h>
@@ -110,8 +111,6 @@ struct vpfe_device {
        struct v4l2_device v4l2_dev;
        /* parent device */
        struct device *pdev;
-       /* Used to keep track of state of the priority */
-       struct v4l2_prio_state prio;
        /* number of open instances of the channel */
        u32 usrs;
        /* Indicates id of the field which is being displayed */
@@ -174,11 +173,10 @@ struct vpfe_device {
 
 /* File handle structure */
 struct vpfe_fh {
+       struct v4l2_fh fh;
        struct vpfe_device *vpfe_dev;
        /* Indicates whether this file handle is doing IO */
        u8 io_allowed;
-       /* Used to keep track priority of this instance */
-       enum v4l2_priority prio;
 };
 
 struct vpfe_config_params {
similarity index 87%
rename from include/media/s5p_fimc.h
rename to include/media/exynos-fimc.h
index b975c28..aa44660 100644 (file)
@@ -61,41 +61,20 @@ enum fimc_bus_type {
 #define GRP_ID_FLITE           (1 << 13)
 #define GRP_ID_FIMC_IS         (1 << 14)
 
-struct i2c_board_info;
-
 /**
  * struct fimc_source_info - video source description required for the host
  *                          interface configuration
  *
- * @board_info: pointer to I2C subdevice's board info
- * @clk_frequency: frequency of the clock the host interface provides to sensor
  * @fimc_bus_type: FIMC camera input type
  * @sensor_bus_type: image sensor bus type, MIPI, ITU-R BT.601 etc.
  * @flags: the parallel sensor bus flags defining signals polarity (V4L2_MBUS_*)
- * @i2c_bus_num: i2c control bus id the sensor is attached to
  * @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU)
- * @clk_id: index of the SoC peripheral clock for sensors
  */
 struct fimc_source_info {
-       struct i2c_board_info *board_info;
-       unsigned long clk_frequency;
        enum fimc_bus_type fimc_bus_type;
        enum fimc_bus_type sensor_bus_type;
        u16 flags;
-       u16 i2c_bus_num;
        u16 mux_id;
-       u8 clk_id;
-};
-
-/**
- * struct s5p_platform_fimc - camera host interface platform data
- *
- * @source_info: properties of an image source for the host interface setup
- * @num_clients: the number of attached image sources
- */
-struct s5p_platform_fimc {
-       struct fimc_source_info *source_info;
-       int num_clients;
 };
 
 /*
index 12155a9..6e6db78 100644 (file)
@@ -87,7 +87,9 @@ struct media_device {
 /* media_devnode to media_device */
 #define to_media_device(node) container_of(node, struct media_device, devnode)
 
-int __must_check media_device_register(struct media_device *mdev);
+int __must_check __media_device_register(struct media_device *mdev,
+                                        struct module *owner);
+#define media_device_register(mdev) __media_device_register(mdev, THIS_MODULE)
 void media_device_unregister(struct media_device *mdev);
 
 int __must_check media_device_register_entity(struct media_device *mdev,
index 3446af2..0dc7060 100644 (file)
@@ -82,7 +82,8 @@ struct media_devnode {
 /* dev to media_devnode */
 #define to_media_devnode(cd) container_of(cd, struct media_devnode, dev)
 
-int __must_check media_devnode_register(struct media_devnode *mdev);
+int __must_check media_devnode_register(struct media_devnode *mdev,
+                                       struct module *owner);
 void media_devnode_unregister(struct media_devnode *mdev);
 
 static inline struct media_devnode *media_devnode_data(struct file *filp)
index c9b1593..ffb69da 100644 (file)
@@ -120,6 +120,14 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);
 int __must_check
 v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev);
 
+/* Send a notification to v4l2_device. */
+static inline void v4l2_subdev_notify(struct v4l2_subdev *sd,
+                                     unsigned int notification, void *arg)
+{
+       if (sd && sd->v4l2_dev && sd->v4l2_dev->notify)
+               sd->v4l2_dev->notify(sd, notification, arg);
+}
+
 /* Iterate over all subdevs. */
 #define v4l2_device_for_each_subdev(sd, v4l2_dev)                      \
        list_for_each_entry(sd, &(v4l2_dev)->subdevs, list)
index be05d01..1ab9045 100644 (file)
@@ -132,4 +132,8 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
 void v4l2_event_unsubscribe_all(struct v4l2_fh *fh);
 int v4l2_event_subdev_unsubscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh,
                                  struct v4l2_event_subscription *sub);
+int v4l2_src_change_event_subscribe(struct v4l2_fh *fh,
+                               const struct v4l2_event_subscription *sub);
+int v4l2_src_change_event_subdev_subscribe(struct v4l2_subdev *sd,
+               struct v4l2_fh *fh, struct v4l2_event_subscription *sub);
 #endif /* V4L2_EVENT_H */
index 28f4d8c..9fab013 100644 (file)
@@ -159,8 +159,6 @@ struct v4l2_subdev_core_ops {
        int (*s_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
        int (*try_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
        int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);
-       int (*g_std)(struct v4l2_subdev *sd, v4l2_std_id *norm);
-       int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm);
        long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
 #ifdef CONFIG_COMPAT
        long (*compat_ioctl32)(struct v4l2_subdev *sd, unsigned int cmd,
@@ -234,15 +232,18 @@ struct v4l2_subdev_audio_ops {
 
 /* Indicates the @length field specifies maximum data length. */
 #define V4L2_MBUS_FRAME_DESC_FL_LEN_MAX                (1U << 0)
-/* Indicates user defined data format, i.e. non standard frame format. */
+/*
+ * Indicates that the format does not have line offsets, i.e. the
+ * receiver should use 1D DMA.
+ */
 #define V4L2_MBUS_FRAME_DESC_FL_BLOB           (1U << 1)
 
 /**
  * struct v4l2_mbus_frame_desc_entry - media bus frame description structure
  * @flags: V4L2_MBUS_FRAME_DESC_FL_* flags
  * @pixelcode: media bus pixel code, valid if FRAME_DESC_FL_BLOB is not set
- * @length: number of octets per frame, valid for compressed or unspecified
- *          formats
+ * @length: number of octets per frame, valid if V4L2_MBUS_FRAME_DESC_FL_BLOB
+ *         is set
  */
 struct v4l2_mbus_frame_desc_entry {
        u16 flags;
@@ -269,8 +270,11 @@ struct v4l2_mbus_frame_desc {
    g_std_output: get current standard for video OUTPUT devices. This is ignored
        by video input devices.
 
-   g_tvnorms_output: get v4l2_std_id with all standards supported by video
-       OUTPUT device. This is ignored by video input devices.
+   g_tvnorms: get v4l2_std_id with all standards supported by the video
+       CAPTURE device. This is ignored by video output devices.
+
+   g_tvnorms_output: get v4l2_std_id with all standards supported by the video
+       OUTPUT device. This is ignored by video capture devices.
 
    s_crystal_freq: sets the frequency of the crystal used to generate the
        clocks in Hz. An extra flags field allows device specific configuration
@@ -310,9 +314,12 @@ struct v4l2_mbus_frame_desc {
 struct v4l2_subdev_video_ops {
        int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);
        int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags);
+       int (*g_std)(struct v4l2_subdev *sd, v4l2_std_id *norm);
+       int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm);
        int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std);
        int (*g_std_output)(struct v4l2_subdev *sd, v4l2_std_id *std);
        int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std);
+       int (*g_tvnorms)(struct v4l2_subdev *sd, v4l2_std_id *std);
        int (*g_tvnorms_output)(struct v4l2_subdev *sd, v4l2_std_id *std);
        int (*g_input_status)(struct v4l2_subdev *sd, u32 *status);
        int (*s_stream)(struct v4l2_subdev *sd, int enable);
@@ -510,6 +517,10 @@ struct v4l2_subdev_pad_ops {
                             struct v4l2_subdev_selection *sel);
        int (*get_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
        int (*set_edid)(struct v4l2_subdev *sd, struct v4l2_edid *edid);
+       int (*dv_timings_cap)(struct v4l2_subdev *sd,
+                             struct v4l2_dv_timings_cap *cap);
+       int (*enum_dv_timings)(struct v4l2_subdev *sd,
+                              struct v4l2_enum_dv_timings *timings);
 #ifdef CONFIG_MEDIA_CONTROLLER
        int (*link_validate)(struct v4l2_subdev *sd, struct media_link *link,
                             struct v4l2_subdev_format *source_fmt,
@@ -584,6 +595,7 @@ struct v4l2_subdev {
 #endif
        struct list_head list;
        struct module *owner;
+       bool owner_v4l2_dev;
        u32 flags;
        struct v4l2_device *v4l2_dev;
        const struct v4l2_subdev_ops *ops;
@@ -685,17 +697,12 @@ void v4l2_subdev_init(struct v4l2_subdev *sd,
 /* Call an ops of a v4l2_subdev, doing the right checks against
    NULL pointers.
 
-   Example: err = v4l2_subdev_call(sd, core, s_std, norm);
+   Example: err = v4l2_subdev_call(sd, video, s_std, norm);
  */
 #define v4l2_subdev_call(sd, o, f, args...)                            \
        (!(sd) ? -ENODEV : (((sd)->ops->o && (sd)->ops->o->f) ? \
                (sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD))
 
-/* Send a notification to v4l2_device. */
-#define v4l2_subdev_notify(sd, notification, arg)                         \
-       ((!(sd) || !(sd)->v4l2_dev || !(sd)->v4l2_dev->notify) ? -ENODEV : \
-        (sd)->v4l2_dev->notify((sd), (notification), (arg)))
-
 #define v4l2_subdev_has_op(sd, o, f) \
        ((sd)->ops->o && (sd)->ops->o->f)
 
index af46211..bca25dc 100644 (file)
@@ -20,6 +20,7 @@
 
 struct vb2_alloc_ctx;
 struct vb2_fileio_data;
+struct vb2_threadio_data;
 
 /**
  * struct vb2_mem_ops - memory handling/memory allocator operations
@@ -323,7 +324,7 @@ struct vb2_ops {
        void (*buf_cleanup)(struct vb2_buffer *vb);
 
        int (*start_streaming)(struct vb2_queue *q, unsigned int count);
-       int (*stop_streaming)(struct vb2_queue *q);
+       void (*stop_streaming)(struct vb2_queue *q);
 
        void (*buf_queue)(struct vb2_buffer *vb);
 };
@@ -375,6 +376,7 @@ struct v4l2_fh;
  * @start_streaming_called: start_streaming() was called successfully and we
  *             started streaming.
  * @fileio:    file io emulator internal data, used only if emulator is active
+ * @threadio:  thread io internal data, used only if thread is active
  */
 struct vb2_queue {
        enum v4l2_buf_type              type;
@@ -411,6 +413,7 @@ struct vb2_queue {
        unsigned int                    start_streaming_called:1;
 
        struct vb2_fileio_data          *fileio;
+       struct vb2_threadio_data        *threadio;
 
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        /*
@@ -461,6 +464,35 @@ size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
                loff_t *ppos, int nonblock);
 size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
                loff_t *ppos, int nonblock);
+/**
+ * vb2_thread_fnc - callback function for use with vb2_thread
+ *
+ * This is called whenever a buffer is dequeued in the thread.
+ */
+typedef int (*vb2_thread_fnc)(struct vb2_buffer *vb, void *priv);
+
+/**
+ * vb2_thread_start() - start a thread for the given queue.
+ * @q:         videobuf queue
+ * @fnc:       callback function
+ * @priv:      priv pointer passed to the callback function
+ * @thread_name:the name of the thread. This will be prefixed with "vb2-".
+ *
+ * This starts a thread that will queue and dequeue until an error occurs
+ * or @vb2_thread_stop is called.
+ *
+ * This function should not be used for anything else but the videobuf2-dvb
+ * support. If you think you have another good use-case for this, then please
+ * contact the linux-media mailinglist first.
+ */
+int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv,
+                    const char *thread_name);
+
+/**
+ * vb2_thread_stop() - stop the thread for the given queue.
+ * @q:         videobuf queue
+ */
+int vb2_thread_stop(struct vb2_queue *q);
 
 /**
  * vb2_is_streaming() - return streaming status of the queue
@@ -471,6 +503,23 @@ static inline bool vb2_is_streaming(struct vb2_queue *q)
        return q->streaming;
 }
 
+/**
+ * vb2_fileio_is_active() - return true if fileio is active.
+ * @q:         videobuf queue
+ *
+ * This returns true if read() or write() is used to stream the data
+ * as opposed to stream I/O. This is almost never an important distinction,
+ * except in rare cases. One such case is that using read() or write() to
+ * stream a format using V4L2_FIELD_ALTERNATE is not allowed since there
+ * is no way you can pass the field information of each buffer to/from
+ * userspace. A driver that supports this field format should check for
+ * this in the queue_setup op and reject it if this function returns true.
+ */
+static inline bool vb2_fileio_is_active(struct vb2_queue *q)
+{
+       return q->fileio;
+}
+
 /**
  * vb2_is_busy() - return busy status of the queue
  * @q:         videobuf queue
diff --git a/include/media/videobuf2-dvb.h b/include/media/videobuf2-dvb.h
new file mode 100644 (file)
index 0000000..8f61456
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef _VIDEOBUF2_DVB_H_
+#define        _VIDEOBUF2_DVB_H_
+
+#include <dvbdev.h>
+#include <dmxdev.h>
+#include <dvb_demux.h>
+#include <dvb_net.h>
+#include <dvb_frontend.h>
+#include <media/videobuf2-core.h>
+
+struct vb2_dvb {
+       /* filling that the job of the driver */
+       char                    *name;
+       struct dvb_frontend     *frontend;
+       struct vb2_queue        dvbq;
+
+       /* video-buf-dvb state info */
+       struct mutex            lock;
+       int                     nfeeds;
+
+       /* vb2_dvb_(un)register manages this */
+       struct dvb_demux        demux;
+       struct dmxdev           dmxdev;
+       struct dmx_frontend     fe_hw;
+       struct dmx_frontend     fe_mem;
+       struct dvb_net          net;
+};
+
+struct vb2_dvb_frontend {
+       struct list_head felist;
+       int id;
+       struct vb2_dvb dvb;
+};
+
+struct vb2_dvb_frontends {
+       struct list_head felist;
+       struct mutex lock;
+       struct dvb_adapter adapter;
+       int active_fe_id; /* Indicates which frontend in the felist is in use */
+       int gate; /* Frontend with gate control 0=!MFE,1=fe0,2=fe1 etc */
+};
+
+int vb2_dvb_register_bus(struct vb2_dvb_frontends *f,
+                        struct module *module,
+                        void *adapter_priv,
+                        struct device *device,
+                        short *adapter_nr,
+                        int mfe_shared);
+
+void vb2_dvb_unregister_bus(struct vb2_dvb_frontends *f);
+
+struct vb2_dvb_frontend *vb2_dvb_alloc_frontend(struct vb2_dvb_frontends *f, int id);
+void vb2_dvb_dealloc_frontends(struct vb2_dvb_frontends *f);
+
+struct vb2_dvb_frontend *vb2_dvb_get_frontend(struct vb2_dvb_frontends *f, int id);
+int vb2_dvb_find_frontend(struct vb2_dvb_frontends *f, struct dvb_frontend *p);
+
+#endif                 /* _VIDEOBUF2_DVB_H_ */
index 98498e1..e52ef53 100644 (file)
@@ -483,8 +483,8 @@ void wimax_report_rfkill_sw(struct wimax_dev *, enum wimax_rf_state);
  * Be sure not to modify skb->data in the middle (ie: don't use
  * skb_push()/skb_pull()/skb_reserve() on the skb).
  *
- * "pipe_name" is any string, than can be interpreted as the name of
- * the pipe or destinatary; the interpretation of it is driver
+ * "pipe_name" is any string, that can be interpreted as the name of
+ * the pipe or recipient; the interpretation of it is driver
  * specific, so the recipient can multiplex it as wished. It can be
  * NULL, it won't be used - an example is using a "diagnostics" tag to
  * send diagnostics information that a device-specific diagnostics
index e6aabdb..00e6c28 100644 (file)
@@ -23,7 +23,6 @@
  * @reset_pin: GPIO pin wired to the reset input on the external AC97 codec,
  *             optional to use, set to -ENODEV if not in use. AC97 layer will
  *             try to do a software reset of the external codec anyway.
- * @flags: Flags for which directions should be enabled.
  *
  * If the user do not want to use a DMA channel for playback or capture, i.e.
  * only one feature is required on the board. The slave for playback or capture
@@ -33,7 +32,6 @@
 struct ac97c_platform_data {
        struct dw_dma_slave     rx_dws;
        struct dw_dma_slave     tx_dws;
-       unsigned int            flags;
        int                     reset_pin;
 };
 
index d3f5f81..eedda2c 100644 (file)
@@ -282,13 +282,6 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
                 struct module *module, int extra_size,
                 struct snd_card **card_ret);
 
-static inline int __deprecated
-snd_card_create(int idx, const char *id, struct module *module, int extra_size,
-               struct snd_card **ret)
-{
-       return snd_card_new(NULL, idx, id, module, extra_size, ret);
-}
-
 int snd_card_disconnect(struct snd_card *card);
 int snd_card_free(struct snd_card *card);
 int snd_card_free_when_closed(struct snd_card *card);
diff --git a/include/sound/cs42l56.h b/include/sound/cs42l56.h
new file mode 100644 (file)
index 0000000..2467c8f
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * linux/sound/cs42l56.h -- Platform data for CS42L56
+ *
+ * Copyright (c) 2014 Cirrus Logic 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 __CS42L56_H
+#define __CS42L56_H
+
+struct cs42l56_platform_data {
+
+       /* GPIO for Reset */
+       unsigned int gpio_nreset;
+
+       /* MICBIAS Level. Check datasheet Pg48 */
+       unsigned int micbias_lvl;
+
+       /* Analog Input 1A Reference 0=Single 1=Pseudo-Differential */
+       unsigned int ain1a_ref_cfg;
+
+       /* Analog Input 2A Reference 0=Single 1=Pseudo-Differential */
+       unsigned int ain2a_ref_cfg;
+
+       /* Analog Input 1B Reference 0=Single 1=Pseudo-Differential */
+       unsigned int ain1b_ref_cfg;
+
+       /* Analog Input 2B Reference 0=Single 1=Pseudo-Differential */
+       unsigned int ain2b_ref_cfg;
+
+       /* Charge Pump Freq. Check datasheet Pg62 */
+       unsigned int chgfreq;
+
+       /* HighPass Filter Right Channel Corner Frequency */
+       unsigned int hpfb_freq;
+
+       /* HighPass Filter Left Channel Corner Frequency */
+       unsigned int hpfa_freq;
+
+       /* Adaptive Power Control for LO/HP */
+       unsigned int adaptive_pwr;
+
+};
+
+#endif /* __CS42L56_H */
diff --git a/include/sound/omap-pcm.h b/include/sound/omap-pcm.h
new file mode 100644 (file)
index 0000000..c1d2f31
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * omap-pcm.h - OMAP PCM driver
+ *
+ * Copyright (C) 2014 Texas Instruments, Inc.
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@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.
+ */
+
+#ifndef __OMAP_PCM_H__
+#define __OMAP_PCM_H__
+
+#if IS_ENABLED(CONFIG_SND_OMAP_SOC)
+int omap_pcm_platform_register(struct device *dev);
+#else
+static inline int omap_pcm_platform_register(struct device *dev)
+{
+       return 0;
+}
+#endif /* CONFIG_SND_OMAP_SOC */
+
+#endif /* __OMAP_PCM_H__ */
index 34a3c02..f4a706f 100644 (file)
  * B : SSI direction
  */
 #define RSND_SSI_CLK_PIN_SHARE         (1 << 31)
-#define RSND_SSI_PLAY                  (1 << 24)
 
 #define RSND_SSI(_dma_id, _pio_irq, _flags)            \
 { .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags }
-#define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags)       \
-{ .dai_id = _dai_id, .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags }
 #define RSND_SSI_UNUSED \
-{ .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 }
+{ .dma_id = -1, .pio_irq = -1, .flags = 0 }
 
 struct rsnd_ssi_platform_info {
-       int dai_id;     /* will be removed */
        int dma_id;
        int pio_irq;
        u32 flags;
 };
 
-/*
- * flags
- */
-#define RSND_SCU_USE_HPBIF             (1 << 31) /* it needs RSND_SSI_DEPENDENT */
-
 #define RSND_SRC(rate, _dma_id)                                                \
-{ .flags = RSND_SCU_USE_HPBIF, .convert_rate = rate, .dma_id = _dma_id, }
-#define RSND_SRC_SET(rate, _dma_id)            \
-       { .flags = RSND_SCU_USE_HPBIF, .convert_rate = rate, .dma_id = _dma_id, }
+{ .convert_rate = rate, .dma_id = _dma_id, }
 #define RSND_SRC_UNUSED                                \
-       { .flags = 0, .convert_rate = 0, .dma_id = 0, }
-
-#define rsnd_scu_platform_info rsnd_src_platform_info
-#define src_info               scu_info
-#define src_info_nr            scu_info_nr
+{ .convert_rate = 0, .dma_id = -1, }
 
 struct rsnd_src_platform_info {
-       u32 flags;
        u32 convert_rate; /* sampling rate convert */
        int dma_id; /* for Gen2 SCU */
 };
 
+/*
+ * flags
+ */
+struct rsnd_dvc_platform_info {
+       u32 flags;
+};
+
 struct rsnd_dai_path_info {
        struct rsnd_ssi_platform_info *ssi;
        struct rsnd_src_platform_info *src;
+       struct rsnd_dvc_platform_info *dvc;
 };
 
 struct rsnd_dai_platform_info {
@@ -99,6 +91,8 @@ struct rcar_snd_info {
        int ssi_info_nr;
        struct rsnd_src_platform_info *src_info;
        int src_info_nr;
+       struct rsnd_dvc_platform_info *dvc_info;
+       int dvc_info_nr;
        struct rsnd_dai_platform_info *dai_info;
        int dai_info_nr;
        int (*start)(int id);
index 27cc75e..59d26dd 100644 (file)
@@ -16,6 +16,10 @@ struct rt5640_platform_data {
        bool in1_diff;
        bool in2_diff;
 
+       bool dmic_en;
+       bool dmic1_data_pin; /* 0 = IN1P; 1 = GPIO3 */
+       bool dmic2_data_pin; /* 0 = IN1N; 1 = GPIO4 */
+
        int ldo1_en; /* GPIO for LDO1_EN */
 };
 
diff --git a/include/sound/rt5645.h b/include/sound/rt5645.h
new file mode 100644 (file)
index 0000000..1de744c
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * linux/sound/rt5645.h -- Platform data for RT5645
+ *
+ * Copyright 2013 Realtek Microelectronics
+ *
+ * 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_RT5645_H
+#define __LINUX_SND_RT5645_H
+
+struct rt5645_platform_data {
+       /* IN2 can optionally be differential */
+       bool in2_diff;
+
+       bool dmic_en;
+       unsigned int dmic1_data_pin;
+       /* 0 = IN2N; 1 = GPIO5; 2 = GPIO11 */
+       unsigned int dmic2_data_pin;
+       /* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */
+};
+
+#endif
diff --git a/include/sound/rt5651.h b/include/sound/rt5651.h
new file mode 100644 (file)
index 0000000..d35de75
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * linux/sound/rt286.h -- Platform data for RT286
+ *
+ * Copyright 2013 Realtek Microelectronics
+ *
+ * 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_RT5651_H
+#define __LINUX_SND_RT5651_H
+
+struct rt5651_platform_data {
+       /* IN2 can optionally be differential */
+       bool in2_diff;
+
+       bool dmic_en;
+};
+
+#endif
diff --git a/include/sound/rt5677.h b/include/sound/rt5677.h
new file mode 100644 (file)
index 0000000..3da1431
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * linux/sound/rt5677.h -- Platform data for RT5677
+ *
+ * Copyright 2013 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.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 __LINUX_SND_RT5677_H
+#define __LINUX_SND_RT5677_H
+
+struct rt5677_platform_data {
+       /* IN1 IN2 can optionally be differential */
+       bool in1_diff;
+       bool in2_diff;
+};
+
+#endif
index fad7676..688f2ba 100644 (file)
@@ -252,7 +252,6 @@ struct snd_soc_dai {
        unsigned int symmetric_rates:1;
        unsigned int symmetric_channels:1;
        unsigned int symmetric_samplebits:1;
-       struct snd_pcm_runtime *runtime;
        unsigned int active;
        unsigned char probed:1;
 
@@ -277,7 +276,6 @@ struct snd_soc_dai {
        struct snd_soc_card *card;
 
        struct list_head list;
-       struct list_head card_list;
 };
 
 static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai *dai,
index ef78f56..6b59471 100644 (file)
@@ -107,10 +107,6 @@ struct device;
 {      .id = snd_soc_dapm_mux, .name = wname, \
        SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
        .kcontrol_news = wcontrols, .num_kcontrols = 1}
-#define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \
-       SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols)
-#define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \
-       SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols)
 
 /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
 #define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
@@ -166,10 +162,6 @@ struct device;
        SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
        .kcontrol_news = wcontrols, .num_kcontrols = 1, \
        .event = wevent, .event_flags = wflags}
-#define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
-       wevent, wflags) \
-       SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, wevent, \
-               wflags)
 
 /* additional sequencing control within an event type */
 #define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \
@@ -256,9 +248,8 @@ struct device;
 /* generic widgets */
 #define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
 {      .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
-       .reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
-       .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
-       .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
+       .reg = wreg, .shift = wshift, .mask = wmask, \
+       .on_val = won_val, .off_val = woff_val, }
 #define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \
 {      .id = snd_soc_dapm_supply, .name = wname, \
        SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
@@ -305,16 +296,12 @@ struct device;
        .get = snd_soc_dapm_get_enum_double, \
        .put = snd_soc_dapm_put_enum_double, \
        .private_value = (unsigned long)&xenum }
-#define SOC_DAPM_ENUM_VIRT(xname, xenum) \
-       SOC_DAPM_ENUM(xname, xenum)
 #define SOC_DAPM_ENUM_EXT(xname, xenum, xget, xput) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_enum_double, \
        .get = xget, \
        .put = xput, \
        .private_value = (unsigned long)&xenum }
-#define SOC_DAPM_VALUE_ENUM(xname, xenum) \
-       SOC_DAPM_ENUM(xname, xenum)
 #define SOC_DAPM_PIN_SWITCH(xname) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname " Switch", \
        .info = snd_soc_dapm_info_pin_switch, \
@@ -362,8 +349,6 @@ struct regulator;
 struct snd_soc_dapm_widget_list;
 struct snd_soc_dapm_update;
 
-int dapm_reg_event(struct snd_soc_dapm_widget *w,
-                  struct snd_kcontrol *kcontrol, int event);
 int dapm_regulator_event(struct snd_soc_dapm_widget *w,
                         struct snd_kcontrol *kcontrol, int event);
 int dapm_clock_event(struct snd_soc_dapm_widget *w,
@@ -606,6 +591,7 @@ struct snd_soc_dapm_context {
                             enum snd_soc_dapm_type, int);
 
        struct device *dev; /* from parent - for debug */
+       struct snd_soc_component *component; /* parent component */
        struct snd_soc_codec *codec; /* parent codec */
        struct snd_soc_platform *platform; /* parent platform */
        struct snd_soc_card *card; /* parent card */
index 0b83168..ed9e2d7 100644 (file)
        .info = snd_soc_info_enum_double, \
        .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \
        .private_value = (unsigned long)&xenum }
-#define SOC_VALUE_ENUM(xname, xenum) \
-       SOC_ENUM(xname, xenum)
 #define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\
         xhandler_get, xhandler_put) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
                {.base = xbase, .num_regs = xregs,            \
                 .mask = xmask }) }
 
+#define SND_SOC_BYTES_EXT(xname, xcount, xhandler_get, xhandler_put) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_bytes_info_ext, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct soc_bytes_ext) \
+               {.max = xcount} }
+
 #define SOC_SINGLE_XR_SX(xname, xregbase, xregcount, xnbits, \
                xmin, xmax, xinvert) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
@@ -377,6 +382,8 @@ int snd_soc_resume(struct device *dev);
 int snd_soc_poweroff(struct device *dev);
 int snd_soc_register_platform(struct device *dev,
                const struct snd_soc_platform_driver *platform_drv);
+int devm_snd_soc_register_platform(struct device *dev,
+               const struct snd_soc_platform_driver *platform_drv);
 void snd_soc_unregister_platform(struct device *dev);
 int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
                const struct snd_soc_platform_driver *platform_drv);
@@ -393,14 +400,6 @@ int devm_snd_soc_register_component(struct device *dev,
                         const struct snd_soc_component_driver *cmpnt_drv,
                         struct snd_soc_dai_driver *dai_drv, int num_dai);
 void snd_soc_unregister_component(struct device *dev);
-int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
-                                   unsigned int reg);
-int snd_soc_codec_readable_register(struct snd_soc_codec *codec,
-                                   unsigned int reg);
-int snd_soc_codec_writable_register(struct snd_soc_codec *codec,
-                                   unsigned int reg);
-int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
-                              struct regmap *regmap);
 int snd_soc_cache_sync(struct snd_soc_codec *codec);
 int snd_soc_cache_init(struct snd_soc_codec *codec);
 int snd_soc_cache_exit(struct snd_soc_codec *codec);
@@ -453,6 +452,9 @@ int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage);
 #ifdef CONFIG_GPIOLIB
 int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
                        struct snd_soc_jack_gpio *gpios);
+int snd_soc_jack_add_gpiods(struct device *gpiod_dev,
+                           struct snd_soc_jack *jack,
+                           int count, struct snd_soc_jack_gpio *gpios);
 void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
                        struct snd_soc_jack_gpio *gpios);
 #else
@@ -462,6 +464,14 @@ static inline int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
        return 0;
 }
 
+static inline int snd_soc_jack_add_gpiods(struct device *gpiod_dev,
+                                         struct snd_soc_jack *jack,
+                                         int count,
+                                         struct snd_soc_jack_gpio *gpios)
+{
+       return 0;
+}
+
 static inline void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
                                           struct snd_soc_jack_gpio *gpios)
 {
@@ -469,12 +479,12 @@ static inline void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
 #endif
 
 /* codec register bit access */
-int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
+int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg,
                                unsigned int mask, unsigned int value);
 int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
-                              unsigned short reg, unsigned int mask,
+                              unsigned int reg, unsigned int mask,
                               unsigned int value);
-int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
+int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg,
                                unsigned int mask, unsigned int value);
 
 int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
@@ -540,6 +550,8 @@ int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
                      struct snd_ctl_elem_value *ucontrol);
 int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
                      struct snd_ctl_elem_value *ucontrol);
+int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *ucontrol);
 int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_info *uinfo);
 int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
@@ -586,8 +598,12 @@ struct snd_soc_jack_zone {
 /**
  * struct snd_soc_jack_gpio - Describes a gpio pin for jack detection
  *
- * @gpio:         gpio number
- * @name:         gpio name
+ * @gpio:         legacy gpio number
+ * @idx:          gpio descriptor index within the function of the GPIO
+ *                consumer device
+ * @gpiod_dev     GPIO consumer device
+ * @name:         gpio name. Also as connection ID for the GPIO consumer
+ *                device function name lookup
  * @report:       value to report when jack detected
  * @invert:       report presence in low state
  * @debouce_time: debouce time in ms
@@ -598,6 +614,8 @@ struct snd_soc_jack_zone {
  */
 struct snd_soc_jack_gpio {
        unsigned int gpio;
+       unsigned int idx;
+       struct device *gpiod_dev;
        const char *name;
        int report;
        int invert;
@@ -606,6 +624,7 @@ struct snd_soc_jack_gpio {
 
        struct snd_soc_jack *jack;
        struct delayed_work work;
+       struct gpio_desc *desc;
 
        void *data;
        int (*jack_status_check)(void *data);
@@ -668,6 +687,7 @@ struct snd_soc_component {
        unsigned int active;
 
        unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */
+       unsigned int registered_as_component:1;
 
        struct list_head list;
 
@@ -677,6 +697,14 @@ struct snd_soc_component {
        const struct snd_soc_component_driver *driver;
 
        struct list_head dai_list;
+
+       int (*read)(struct snd_soc_component *, unsigned int, unsigned int *);
+       int (*write)(struct snd_soc_component *, unsigned int, unsigned int);
+
+       struct regmap *regmap;
+       int val_bytes;
+
+       struct mutex io_mutex;
 };
 
 /* SoC Audio Codec device */
@@ -691,10 +719,6 @@ struct snd_soc_codec {
        struct snd_soc_card *card;
        struct list_head list;
        struct list_head card_list;
-       int num_dai;
-       int (*volatile_register)(struct snd_soc_codec *, unsigned int);
-       int (*readable_register)(struct snd_soc_codec *, unsigned int);
-       int (*writable_register)(struct snd_soc_codec *, unsigned int);
 
        /* runtime */
        struct snd_ac97 *ac97;  /* for ad-hoc ac97 devices */
@@ -704,18 +728,14 @@ struct snd_soc_codec {
        unsigned int ac97_registered:1; /* Codec has been AC97 registered */
        unsigned int ac97_created:1; /* Codec has been created by SoC */
        unsigned int cache_init:1; /* codec cache has been initialized */
-       unsigned int using_regmap:1; /* using regmap access */
        u32 cache_only;  /* Suppress writes to hardware */
        u32 cache_sync; /* Cache needs to be synced to hardware */
 
        /* codec IO */
        void *control_data; /* codec control (i2c/3wire) data */
        hw_write_t hw_write;
-       unsigned int (*read)(struct snd_soc_codec *, unsigned int);
-       int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
        void *reg_cache;
        struct mutex cache_rw_mutex;
-       int val_bytes;
 
        /* component */
        struct snd_soc_component component;
@@ -754,13 +774,9 @@ struct snd_soc_codec_driver {
                unsigned int freq_in, unsigned int freq_out);
 
        /* codec IO */
+       struct regmap *(*get_regmap)(struct device *);
        unsigned int (*read)(struct snd_soc_codec *, unsigned int);
        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)(struct snd_soc_codec *, unsigned int);
-       int (*readable_register)(struct snd_soc_codec *, unsigned int);
-       int (*writable_register)(struct snd_soc_codec *, unsigned int);
        unsigned int reg_cache_size;
        short reg_cache_step;
        short reg_word_size;
@@ -791,6 +807,7 @@ struct snd_soc_platform_driver {
        int (*remove)(struct snd_soc_platform *);
        int (*suspend)(struct snd_soc_dai *dai);
        int (*resume)(struct snd_soc_dai *dai);
+       struct snd_soc_component_driver component_driver;
 
        /* pcm creation and destruction */
        int (*pcm_new)(struct snd_soc_pcm_runtime *);
@@ -835,7 +852,6 @@ struct snd_soc_platform {
        int id;
        struct device *dev;
        const struct snd_soc_platform_driver *driver;
-       struct mutex mutex;
 
        unsigned int suspended:1; /* platform is suspended */
        unsigned int probed:1;
@@ -844,6 +860,8 @@ struct snd_soc_platform {
        struct list_head list;
        struct list_head card_list;
 
+       struct snd_soc_component component;
+
        struct snd_soc_dapm_context dapm;
 
 #ifdef CONFIG_DEBUG_FS
@@ -931,7 +949,12 @@ struct snd_soc_dai_link {
 };
 
 struct snd_soc_codec_conf {
+       /*
+        * specify device either by device name, or by
+        * DT/OF node, but not both.
+        */
        const char *dev_name;
+       const struct device_node *of_node;
 
        /*
         * optional map of kcontrol, widget and path name prefixes that are
@@ -942,7 +965,13 @@ struct snd_soc_codec_conf {
 
 struct snd_soc_aux_dev {
        const char *name;               /* Codec name */
-       const char *codec_name;         /* for multi-codec */
+
+       /*
+        * specify multi-codec either by device name, or by
+        * DT/OF node, but not both.
+        */
+       const char *codec_name;
+       const struct device_node *codec_of_node;
 
        /* codec/machine specific init - e.g. add machine controls */
        int (*init)(struct snd_soc_dapm_context *dapm);
@@ -957,7 +986,6 @@ struct snd_soc_card {
        struct snd_card *snd_card;
        struct module *owner;
 
-       struct list_head list;
        struct mutex mutex;
        struct mutex dapm_mutex;
 
@@ -1020,7 +1048,6 @@ struct snd_soc_card {
        /* lists of probed devices belonging to this card */
        struct list_head codec_dev_list;
        struct list_head platform_dev_list;
-       struct list_head dai_dev_list;
 
        struct list_head widgets;
        struct list_head paths;
@@ -1090,6 +1117,10 @@ struct soc_bytes {
        u32 mask;
 };
 
+struct soc_bytes_ext {
+       int max;
+};
+
 /* multi register control */
 struct soc_mreg_control {
        long min, max;
@@ -1120,10 +1151,66 @@ static inline struct snd_soc_codec *snd_soc_component_to_codec(
        return container_of(component, struct snd_soc_codec, component);
 }
 
+/**
+ * snd_soc_component_to_platform() - Casts a component to the platform it is embedded in
+ * @component: The component to cast to a platform
+ *
+ * This function must only be used on components that are known to be platforms.
+ * Otherwise the behavior is undefined.
+ */
+static inline struct snd_soc_platform *snd_soc_component_to_platform(
+       struct snd_soc_component *component)
+{
+       return container_of(component, struct snd_soc_platform, component);
+}
+
+/**
+ * snd_soc_dapm_to_codec() - Casts a DAPM context to the CODEC it is embedded in
+ * @dapm: The DAPM context to cast to the CODEC
+ *
+ * This function must only be used on DAPM contexts that are known to be part of
+ * a CODEC (e.g. in a CODEC driver). Otherwise the behavior is undefined.
+ */
+static inline struct snd_soc_codec *snd_soc_dapm_to_codec(
+       struct snd_soc_dapm_context *dapm)
+{
+       return container_of(dapm, struct snd_soc_codec, dapm);
+}
+
+/**
+ * snd_soc_dapm_to_platform() - Casts a DAPM context to the platform it is
+ *  embedded in
+ * @dapm: The DAPM context to cast to the platform.
+ *
+ * This function must only be used on DAPM contexts that are known to be part of
+ * a platform (e.g. in a platform driver). Otherwise the behavior is undefined.
+ */
+static inline struct snd_soc_platform *snd_soc_dapm_to_platform(
+       struct snd_soc_dapm_context *dapm)
+{
+       return container_of(dapm, struct snd_soc_platform, dapm);
+}
+
 /* codec IO */
 unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg);
-unsigned int snd_soc_write(struct snd_soc_codec *codec,
-                          unsigned int reg, unsigned int val);
+int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg,
+       unsigned int val);
+
+/* component IO */
+int snd_soc_component_read(struct snd_soc_component *component,
+       unsigned int reg, unsigned int *val);
+int snd_soc_component_write(struct snd_soc_component *component,
+       unsigned int reg, unsigned int val);
+int snd_soc_component_update_bits(struct snd_soc_component *component,
+       unsigned int reg, unsigned int mask, unsigned int val);
+int snd_soc_component_update_bits_async(struct snd_soc_component *component,
+       unsigned int reg, unsigned int mask, unsigned int val);
+void snd_soc_component_async_complete(struct snd_soc_component *component);
+int snd_soc_component_test_bits(struct snd_soc_component *component,
+       unsigned int reg, unsigned int mask, unsigned int value);
+
+int snd_soc_component_init_io(struct snd_soc_component *component,
+       struct regmap *regmap);
 
 /* device driver data */
 
@@ -1173,7 +1260,6 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd)
 
 static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
 {
-       INIT_LIST_HEAD(&card->dai_dev_list);
        INIT_LIST_HEAD(&card->codec_dev_list);
        INIT_LIST_HEAD(&card->platform_dev_list);
        INIT_LIST_HEAD(&card->widgets);
@@ -1228,6 +1314,50 @@ static inline bool snd_soc_codec_is_active(struct snd_soc_codec *codec)
        return snd_soc_component_is_active(&codec->component);
 }
 
+/**
+ * snd_soc_kcontrol_component() - Returns the component that registered the
+ *  control
+ * @kcontrol: The control for which to get the component
+ *
+ * Note: This function will work correctly if the control has been registered
+ * for a component. Either with snd_soc_add_codec_controls() or
+ * snd_soc_add_platform_controls() or via  table based setup for either a
+ * CODEC, a platform or component driver. Otherwise the behavior is undefined.
+ */
+static inline struct snd_soc_component *snd_soc_kcontrol_component(
+       struct snd_kcontrol *kcontrol)
+{
+       return snd_kcontrol_chip(kcontrol);
+}
+
+/**
+ * snd_soc_kcontrol_codec() - Returns the CODEC that registered the control
+ * @kcontrol: The control for which to get the CODEC
+ *
+ * Note: This function will only work correctly if the control has been
+ * registered with snd_soc_add_codec_controls() or via table based setup of
+ * snd_soc_codec_driver. Otherwise the behavior is undefined.
+ */
+static inline struct snd_soc_codec *snd_soc_kcontrol_codec(
+       struct snd_kcontrol *kcontrol)
+{
+       return snd_soc_component_to_codec(snd_soc_kcontrol_component(kcontrol));
+}
+
+/**
+ * snd_soc_kcontrol_platform() - Returns the platform that registerd the control
+ * @kcontrol: The control for which to get the platform
+ *
+ * Note: This function will only work correctly if the control has been
+ * registered with snd_soc_add_platform_controls() or via table based setup of
+ * a snd_soc_platform_driver. Otherwise the behavior is undefined.
+ */
+static inline struct snd_soc_platform *snd_soc_kcontrol_platform(
+       struct snd_kcontrol *kcontrol)
+{
+       return snd_soc_component_to_platform(snd_soc_kcontrol_component(kcontrol));
+}
+
 int snd_soc_util_init(void);
 void snd_soc_util_exit(void);
 
@@ -1241,7 +1371,9 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np,
 int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                                   const char *propname);
 unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
-                                    const char *prefix);
+                                    const char *prefix,
+                                    struct device_node **bitclkmaster,
+                                    struct device_node **framemaster);
 int snd_soc_of_get_dai_name(struct device_node *of_node,
                            const char **dai_name);
 
diff --git a/include/sound/sta350.h b/include/sound/sta350.h
new file mode 100644 (file)
index 0000000..42edceb
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Platform data for ST STA350 ASoC codec driver.
+ *
+ * Copyright: 2014 Raumfeld GmbH
+ * Author: Sven Brandau <info@brandau.biz>
+ *
+ * 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 __LINUX_SND__STA350_H
+#define __LINUX_SND__STA350_H
+
+#define STA350_OCFG_2CH                0
+#define STA350_OCFG_2_1CH      1
+#define STA350_OCFG_1CH                3
+
+#define STA350_OM_CH1          0
+#define STA350_OM_CH2          1
+#define STA350_OM_CH3          2
+
+#define STA350_THERMAL_ADJUSTMENT_ENABLE       1
+#define STA350_THERMAL_RECOVERY_ENABLE         2
+#define STA350_FAULT_DETECT_RECOVERY_BYPASS    1
+
+#define STA350_FFX_PM_DROP_COMP                        0
+#define STA350_FFX_PM_TAPERED_COMP             1
+#define STA350_FFX_PM_FULL_POWER               2
+#define STA350_FFX_PM_VARIABLE_DROP_COMP       3
+
+
+struct sta350_platform_data {
+       u8 output_conf;
+       u8 ch1_output_mapping;
+       u8 ch2_output_mapping;
+       u8 ch3_output_mapping;
+       u8 ffx_power_output_mode;
+       u8 drop_compensation_ns;
+       u8 powerdown_delay_divider;
+       unsigned int thermal_warning_recovery:1;
+       unsigned int thermal_warning_adjustment:1;
+       unsigned int fault_detect_recovery:1;
+       unsigned int oc_warning_adjustment:1;
+       unsigned int max_power_use_mpcc:1;
+       unsigned int max_power_correction:1;
+       unsigned int am_reduction_mode:1;
+       unsigned int odd_pwm_speed_mode:1;
+       unsigned int distortion_compensation:1;
+       unsigned int invalid_input_detect_mute:1;
+       unsigned int activate_mute_output:1;
+       unsigned int bridge_immediate_off:1;
+       unsigned int noise_shape_dc_cut:1;
+       unsigned int powerdown_master_vol:1;
+};
+
+#endif /* __LINUX_SND__STA350_H */
index 03996b2..c75c795 100644 (file)
 
 struct snd_soc_jack;
 struct snd_soc_codec;
-struct snd_soc_platform;
 struct snd_soc_card;
 struct snd_soc_dapm_widget;
 struct snd_soc_dapm_path;
 
-/*
- * Log register events
- */
-DECLARE_EVENT_CLASS(snd_soc_reg,
-
-       TP_PROTO(struct snd_soc_codec *codec, unsigned int reg,
-                unsigned int val),
-
-       TP_ARGS(codec, reg, val),
-
-       TP_STRUCT__entry(
-               __string(       name,           codec->name     )
-               __field(        int,            id              )
-               __field(        unsigned int,   reg             )
-               __field(        unsigned int,   val             )
-       ),
-
-       TP_fast_assign(
-               __assign_str(name, codec->name);
-               __entry->id = codec->id;
-               __entry->reg = reg;
-               __entry->val = val;
-       ),
-
-       TP_printk("codec=%s.%d reg=%x val=%x", __get_str(name),
-                 (int)__entry->id, (unsigned int)__entry->reg,
-                 (unsigned int)__entry->val)
-);
-
-DEFINE_EVENT(snd_soc_reg, snd_soc_reg_write,
-
-       TP_PROTO(struct snd_soc_codec *codec, unsigned int reg,
-                unsigned int val),
-
-       TP_ARGS(codec, reg, val)
-
-);
-
-DEFINE_EVENT(snd_soc_reg, snd_soc_reg_read,
-
-       TP_PROTO(struct snd_soc_codec *codec, unsigned int reg,
-                unsigned int val),
-
-       TP_ARGS(codec, reg, val)
-
-);
-
-DECLARE_EVENT_CLASS(snd_soc_preg,
-
-       TP_PROTO(struct snd_soc_platform *platform, unsigned int reg,
-                unsigned int val),
-
-       TP_ARGS(platform, reg, val),
-
-       TP_STRUCT__entry(
-               __string(       name,           platform->name  )
-               __field(        int,            id              )
-               __field(        unsigned int,   reg             )
-               __field(        unsigned int,   val             )
-       ),
-
-       TP_fast_assign(
-               __assign_str(name, platform->name);
-               __entry->id = platform->id;
-               __entry->reg = reg;
-               __entry->val = val;
-       ),
-
-       TP_printk("platform=%s.%d reg=%x val=%x", __get_str(name),
-                 (int)__entry->id, (unsigned int)__entry->reg,
-                 (unsigned int)__entry->val)
-);
-
-DEFINE_EVENT(snd_soc_preg, snd_soc_preg_write,
-
-       TP_PROTO(struct snd_soc_platform *platform, unsigned int reg,
-                unsigned int val),
-
-       TP_ARGS(platform, reg, val)
-
-);
-
-DEFINE_EVENT(snd_soc_preg, snd_soc_preg_read,
-
-       TP_PROTO(struct snd_soc_platform *platform, unsigned int reg,
-                unsigned int val),
-
-       TP_ARGS(platform, reg, val)
-
-);
-
 DECLARE_EVENT_CLASS(snd_soc_card,
 
        TP_PROTO(struct snd_soc_card *card, int val),
diff --git a/include/trace/events/filelock.h b/include/trace/events/filelock.h
new file mode 100644 (file)
index 0000000..59d11c2
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Events for filesystem locks
+ *
+ * Copyright 2013 Jeff Layton <jlayton@poochiereds.net>
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM filelock
+
+#if !defined(_TRACE_FILELOCK_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_FILELOCK_H
+
+#include <linux/tracepoint.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/kdev_t.h>
+
+#define show_fl_flags(val)                                             \
+       __print_flags(val, "|",                                         \
+               { FL_POSIX,             "FL_POSIX" },                   \
+               { FL_FLOCK,             "FL_FLOCK" },                   \
+               { FL_DELEG,             "FL_DELEG" },                   \
+               { FL_ACCESS,            "FL_ACCESS" },                  \
+               { FL_EXISTS,            "FL_EXISTS" },                  \
+               { FL_LEASE,             "FL_LEASE" },                   \
+               { FL_CLOSE,             "FL_CLOSE" },                   \
+               { FL_SLEEP,             "FL_SLEEP" },                   \
+               { FL_DOWNGRADE_PENDING, "FL_DOWNGRADE_PENDING" },       \
+               { FL_UNLOCK_PENDING,    "FL_UNLOCK_PENDING" },          \
+               { FL_OFDLCK,            "FL_OFDLCK" })
+
+#define show_fl_type(val)                              \
+       __print_symbolic(val,                           \
+                       { F_RDLCK, "F_RDLCK" },         \
+                       { F_WRLCK, "F_WRLCK" },         \
+                       { F_UNLCK, "F_UNLCK" })
+
+DECLARE_EVENT_CLASS(filelock_lease,
+
+       TP_PROTO(struct inode *inode, struct file_lock *fl),
+
+       TP_ARGS(inode, fl),
+
+       TP_STRUCT__entry(
+               __field(struct file_lock *, fl)
+               __field(unsigned long, i_ino)
+               __field(dev_t, s_dev)
+               __field(struct file_lock *, fl_next)
+               __field(fl_owner_t, fl_owner)
+               __field(unsigned int, fl_flags)
+               __field(unsigned char, fl_type)
+               __field(unsigned long, fl_break_time)
+               __field(unsigned long, fl_downgrade_time)
+       ),
+
+       TP_fast_assign(
+               __entry->fl = fl;
+               __entry->s_dev = inode->i_sb->s_dev;
+               __entry->i_ino = inode->i_ino;
+               __entry->fl_next = fl->fl_next;
+               __entry->fl_owner = fl->fl_owner;
+               __entry->fl_flags = fl->fl_flags;
+               __entry->fl_type = fl->fl_type;
+               __entry->fl_break_time = fl->fl_break_time;
+               __entry->fl_downgrade_time = fl->fl_downgrade_time;
+       ),
+
+       TP_printk("fl=0x%p dev=0x%x:0x%x ino=0x%lx fl_next=0x%p fl_owner=0x%p fl_flags=%s fl_type=%s fl_break_time=%lu fl_downgrade_time=%lu",
+               __entry->fl, MAJOR(__entry->s_dev), MINOR(__entry->s_dev),
+               __entry->i_ino, __entry->fl_next, __entry->fl_owner,
+               show_fl_flags(__entry->fl_flags),
+               show_fl_type(__entry->fl_type),
+               __entry->fl_break_time, __entry->fl_downgrade_time)
+);
+
+DEFINE_EVENT(filelock_lease, break_lease_noblock, TP_PROTO(struct inode *inode, struct file_lock *fl),
+               TP_ARGS(inode, fl));
+
+DEFINE_EVENT(filelock_lease, break_lease_block, TP_PROTO(struct inode *inode, struct file_lock *fl),
+               TP_ARGS(inode, fl));
+
+DEFINE_EVENT(filelock_lease, break_lease_unblock, TP_PROTO(struct inode *inode, struct file_lock *fl),
+               TP_ARGS(inode, fl));
+
+DEFINE_EVENT(filelock_lease, generic_add_lease, TP_PROTO(struct inode *inode, struct file_lock *fl),
+               TP_ARGS(inode, fl));
+
+DEFINE_EVENT(filelock_lease, generic_delete_lease, TP_PROTO(struct inode *inode, struct file_lock *fl),
+               TP_ARGS(inode, fl));
+
+DEFINE_EVENT(filelock_lease, time_out_leases, TP_PROTO(struct inode *inode, struct file_lock *fl),
+               TP_ARGS(inode, fl));
+
+#endif /* _TRACE_FILELOCK_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 6929571..24e9033 100644 (file)
@@ -317,6 +317,7 @@ header-y += ppp-ioctl.h
 header-y += ppp_defs.h
 header-y += pps.h
 header-y += prctl.h
+header-y += psci.h
 header-y += ptp_clock.h
 header-y += ptrace.h
 header-y += qnx4_fs.h
index db3fdd0..1a763ea 100644 (file)
@@ -20,7 +20,7 @@
 
 #define GFS2_MOUNT_LOCK                0
 #define GFS2_LIVE_LOCK         1
-#define GFS2_TRANS_LOCK                2
+#define GFS2_FREEZE_LOCK       2
 #define GFS2_RENAME_LOCK       3
 #define GFS2_CONTROL_LOCK      4
 #define GFS2_MOUNTED_LOCK      5
index f484952..19df18c 100644 (file)
@@ -462,7 +462,10 @@ struct input_keymap_entry {
 #define KEY_VIDEO_NEXT         241     /* drive next video source */
 #define KEY_VIDEO_PREV         242     /* drive previous video source */
 #define KEY_BRIGHTNESS_CYCLE   243     /* brightness up, after max is min */
-#define KEY_BRIGHTNESS_ZERO    244     /* brightness off, use ambient */
+#define KEY_BRIGHTNESS_AUTO    244     /* Set Auto Brightness: manual
+                                         brightness control is off,
+                                         rely on ambient */
+#define KEY_BRIGHTNESS_ZERO    KEY_BRIGHTNESS_AUTO
 #define KEY_DISPLAY_OFF                245     /* display device to off state */
 
 #define KEY_WWAN               246     /* Wireless WAN (LTE, UMTS, GSM, etc.) */
@@ -632,6 +635,7 @@ struct input_keymap_entry {
 #define KEY_ADDRESSBOOK                0x1ad   /* AL Contacts/Address Book */
 #define KEY_MESSENGER          0x1ae   /* AL Instant Messaging */
 #define KEY_DISPLAYTOGGLE      0x1af   /* Turn display (LCD) on and off */
+#define KEY_BRIGHTNESS_TOGGLE  KEY_DISPLAYTOGGLE
 #define KEY_SPELLCHECK         0x1b0   /* AL Spell Check */
 #define KEY_LOGOFF             0x1b1   /* AL Logoff */
 
@@ -723,6 +727,17 @@ struct input_keymap_entry {
 
 #define KEY_ALS_TOGGLE         0x230   /* Ambient light sensor */
 
+#define KEY_BUTTONCONFIG               0x240   /* AL Button Configuration */
+#define KEY_TASKMANAGER                0x241   /* AL Task/Project Manager */
+#define KEY_JOURNAL            0x242   /* AL Log/Journal/Timecard */
+#define KEY_CONTROLPANEL               0x243   /* AL Control Panel */
+#define KEY_APPSELECT          0x244   /* AL Select Task/Application */
+#define KEY_SCREENSAVER                0x245   /* AL Screen Saver */
+#define KEY_VOICECOMMAND               0x246   /* Listening Voice Command */
+
+#define KEY_BRIGHTNESS_MIN             0x250   /* Set Brightness to Minimum */
+#define KEY_BRIGHTNESS_MAX             0x251   /* Set Brightness to Maximum */
+
 #define BTN_TRIGGER_HAPPY              0x2c0
 #define BTN_TRIGGER_HAPPY1             0x2c0
 #define BTN_TRIGGER_HAPPY2             0x2c1
index a8f4ee5..e11d8f1 100644 (file)
@@ -171,6 +171,7 @@ struct kvm_pit_config {
 #define KVM_EXIT_WATCHDOG         21
 #define KVM_EXIT_S390_TSCH        22
 #define KVM_EXIT_EPR              23
+#define KVM_EXIT_SYSTEM_EVENT     24
 
 /* For KVM_EXIT_INTERNAL_ERROR */
 /* Emulate instruction failed. */
@@ -301,6 +302,13 @@ struct kvm_run {
                struct {
                        __u32 epr;
                } epr;
+               /* KVM_EXIT_SYSTEM_EVENT */
+               struct {
+#define KVM_SYSTEM_EVENT_SHUTDOWN       1
+#define KVM_SYSTEM_EVENT_RESET          2
+                       __u32 type;
+                       __u64 flags;
+               } system_event;
                /* Fix the size of the union. */
                char padding[256];
        };
@@ -416,6 +424,8 @@ struct kvm_s390_psw {
 #define KVM_S390_INT_PFAULT_INIT       0xfffe0004u
 #define KVM_S390_INT_PFAULT_DONE       0xfffe0005u
 #define KVM_S390_MCHK                  0xfffe1000u
+#define KVM_S390_INT_CLOCK_COMP                0xffff1004u
+#define KVM_S390_INT_CPU_TIMER         0xffff1005u
 #define KVM_S390_INT_VIRTIO            0xffff2603u
 #define KVM_S390_INT_SERVICE           0xffff2401u
 #define KVM_S390_INT_EMERGENCY         0xffff1201u
@@ -515,6 +525,7 @@ enum {
        kvm_ioeventfd_flag_nr_pio,
        kvm_ioeventfd_flag_nr_deassign,
        kvm_ioeventfd_flag_nr_virtio_ccw_notify,
+       kvm_ioeventfd_flag_nr_fast_mmio,
        kvm_ioeventfd_flag_nr_max,
 };
 
@@ -529,7 +540,7 @@ enum {
 struct kvm_ioeventfd {
        __u64 datamatch;
        __u64 addr;        /* legal pio/mmio address */
-       __u32 len;         /* 1, 2, 4, or 8 bytes    */
+       __u32 len;         /* 1, 2, 4, or 8 bytes; or 0 to ignore length */
        __s32 fd;
        __u32 flags;
        __u8  pad[36];
@@ -743,6 +754,10 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_IOAPIC_POLARITY_IGNORED 97
 #define KVM_CAP_ENABLE_CAP_VM 98
 #define KVM_CAP_S390_IRQCHIP 99
+#define KVM_CAP_IOEVENTFD_NO_LENGTH 100
+#define KVM_CAP_VM_ATTRIBUTES 101
+#define KVM_CAP_ARM_PSCI_0_2 102
+#define KVM_CAP_PPC_FIXUP_HCALL 103
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
diff --git a/include/uapi/linux/psci.h b/include/uapi/linux/psci.h
new file mode 100644 (file)
index 0000000..310d83e
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * ARM Power State and Coordination Interface (PSCI) header
+ *
+ * This header holds common PSCI defines and macros shared
+ * by: ARM kernel, ARM64 kernel, KVM ARM/ARM64 and user space.
+ *
+ * Copyright (C) 2014 Linaro Ltd.
+ * Author: Anup Patel <anup.patel@linaro.org>
+ */
+
+#ifndef _UAPI_LINUX_PSCI_H
+#define _UAPI_LINUX_PSCI_H
+
+/*
+ * PSCI v0.1 interface
+ *
+ * The PSCI v0.1 function numbers are implementation defined.
+ *
+ * Only PSCI return values such as: SUCCESS, NOT_SUPPORTED,
+ * INVALID_PARAMS, and DENIED defined below are applicable
+ * to PSCI v0.1.
+ */
+
+/* PSCI v0.2 interface */
+#define PSCI_0_2_FN_BASE                       0x84000000
+#define PSCI_0_2_FN(n)                         (PSCI_0_2_FN_BASE + (n))
+#define PSCI_0_2_64BIT                         0x40000000
+#define PSCI_0_2_FN64_BASE                     \
+                                       (PSCI_0_2_FN_BASE + PSCI_0_2_64BIT)
+#define PSCI_0_2_FN64(n)                       (PSCI_0_2_FN64_BASE + (n))
+
+#define PSCI_0_2_FN_PSCI_VERSION               PSCI_0_2_FN(0)
+#define PSCI_0_2_FN_CPU_SUSPEND                        PSCI_0_2_FN(1)
+#define PSCI_0_2_FN_CPU_OFF                    PSCI_0_2_FN(2)
+#define PSCI_0_2_FN_CPU_ON                     PSCI_0_2_FN(3)
+#define PSCI_0_2_FN_AFFINITY_INFO              PSCI_0_2_FN(4)
+#define PSCI_0_2_FN_MIGRATE                    PSCI_0_2_FN(5)
+#define PSCI_0_2_FN_MIGRATE_INFO_TYPE          PSCI_0_2_FN(6)
+#define PSCI_0_2_FN_MIGRATE_INFO_UP_CPU                PSCI_0_2_FN(7)
+#define PSCI_0_2_FN_SYSTEM_OFF                 PSCI_0_2_FN(8)
+#define PSCI_0_2_FN_SYSTEM_RESET               PSCI_0_2_FN(9)
+
+#define PSCI_0_2_FN64_CPU_SUSPEND              PSCI_0_2_FN64(1)
+#define PSCI_0_2_FN64_CPU_ON                   PSCI_0_2_FN64(3)
+#define PSCI_0_2_FN64_AFFINITY_INFO            PSCI_0_2_FN64(4)
+#define PSCI_0_2_FN64_MIGRATE                  PSCI_0_2_FN64(5)
+#define PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU      PSCI_0_2_FN64(7)
+
+/* PSCI v0.2 power state encoding for CPU_SUSPEND function */
+#define PSCI_0_2_POWER_STATE_ID_MASK           0xffff
+#define PSCI_0_2_POWER_STATE_ID_SHIFT          0
+#define PSCI_0_2_POWER_STATE_TYPE_SHIFT                16
+#define PSCI_0_2_POWER_STATE_TYPE_MASK         \
+                               (0x1 << PSCI_0_2_POWER_STATE_TYPE_SHIFT)
+#define PSCI_0_2_POWER_STATE_AFFL_SHIFT                24
+#define PSCI_0_2_POWER_STATE_AFFL_MASK         \
+                               (0x3 << PSCI_0_2_POWER_STATE_AFFL_SHIFT)
+
+/* PSCI v0.2 affinity level state returned by AFFINITY_INFO */
+#define PSCI_0_2_AFFINITY_LEVEL_ON             0
+#define PSCI_0_2_AFFINITY_LEVEL_OFF            1
+#define PSCI_0_2_AFFINITY_LEVEL_ON_PENDING     2
+
+/* PSCI v0.2 multicore support in Trusted OS returned by MIGRATE_INFO_TYPE */
+#define PSCI_0_2_TOS_UP_MIGRATE                        0
+#define PSCI_0_2_TOS_UP_NO_MIGRATE             1
+#define PSCI_0_2_TOS_MP                                2
+
+/* PSCI version decoding (independent of PSCI version) */
+#define PSCI_VERSION_MAJOR_SHIFT               16
+#define PSCI_VERSION_MINOR_MASK                        \
+               ((1U << PSCI_VERSION_MAJOR_SHIFT) - 1)
+#define PSCI_VERSION_MAJOR_MASK                        ~PSCI_VERSION_MINOR_MASK
+#define PSCI_VERSION_MAJOR(ver)                        \
+               (((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT)
+#define PSCI_VERSION_MINOR(ver)                        \
+               ((ver) & PSCI_VERSION_MINOR_MASK)
+
+/* PSCI return values (inclusive of all PSCI versions) */
+#define PSCI_RET_SUCCESS                       0
+#define PSCI_RET_NOT_SUPPORTED                 -1
+#define PSCI_RET_INVALID_PARAMS                        -2
+#define PSCI_RET_DENIED                                -3
+#define PSCI_RET_ALREADY_ON                    -4
+#define PSCI_RET_ON_PENDING                    -5
+#define PSCI_RET_INTERNAL_FAILURE              -6
+#define PSCI_RET_NOT_PRESENT                   -7
+#define PSCI_RET_DISABLED                      -8
+
+#endif /* _UAPI_LINUX_PSCI_H */
index 9bf508a..2f6f8ca 100644 (file)
@@ -75,7 +75,7 @@ struct v4l2_edid {
        __u32 start_block;
        __u32 blocks;
        __u32 reserved[5];
-       __u8 __user *edid;
+       __u8  *edid;
 };
 
 #endif /* __V4L2_COMMON__ */
index b6a5fe0..6c8f159 100644 (file)
                V4L2_DV_FL_CAN_REDUCE_FPS) \
 }
 
+#define V4L2_DV_BT_CEA_3840X2160P24 { \
+       .type = V4L2_DV_BT_656_1120, \
+       V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+               297000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
+               V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+}
+
+#define V4L2_DV_BT_CEA_3840X2160P25 { \
+       .type = V4L2_DV_BT_656_1120, \
+       V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+               297000000, 1056, 88, 296, 8, 10, 72, 0, 0, 0, \
+               V4L2_DV_BT_STD_CEA861, 0) \
+}
+
+#define V4L2_DV_BT_CEA_3840X2160P30 { \
+       .type = V4L2_DV_BT_656_1120, \
+       V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+               297000000, 176, 88, 296, 8, 10, 72, 0, 0, 0, \
+               V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+}
+
+#define V4L2_DV_BT_CEA_3840X2160P50 { \
+       .type = V4L2_DV_BT_656_1120, \
+       V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+               594000000, 1056, 88, 296, 8, 10, 72, 0, 0, 0, \
+               V4L2_DV_BT_STD_CEA861, 0) \
+}
+
+#define V4L2_DV_BT_CEA_3840X2160P60 { \
+       .type = V4L2_DV_BT_656_1120, \
+       V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+               594000000, 176, 88, 296, 8, 10, 72, 0, 0, 0, \
+               V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+}
+
+#define V4L2_DV_BT_CEA_4096X2160P24 { \
+       .type = V4L2_DV_BT_656_1120, \
+       V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+               297000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
+               V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+}
+
+#define V4L2_DV_BT_CEA_4096X2160P25 { \
+       .type = V4L2_DV_BT_656_1120, \
+       V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+               297000000, 968, 88, 128, 8, 10, 72, 0, 0, 0, \
+               V4L2_DV_BT_STD_CEA861, 0) \
+}
+
+#define V4L2_DV_BT_CEA_4096X2160P30 { \
+       .type = V4L2_DV_BT_656_1120, \
+       V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+               297000000, 88, 88, 128, 8, 10, 72, 0, 0, 0, \
+               V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+}
+
+#define V4L2_DV_BT_CEA_4096X2160P50 { \
+       .type = V4L2_DV_BT_656_1120, \
+       V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+               594000000, 968, 88, 128, 8, 10, 72, 0, 0, 0, \
+               V4L2_DV_BT_STD_CEA861, 0) \
+}
+
+#define V4L2_DV_BT_CEA_4096X2160P60 { \
+       .type = V4L2_DV_BT_656_1120, \
+       V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+               594000000, 88, 88, 128, 8, 10, 72, 0, 0, 0, \
+               V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
+}
+
 
 /* VESA Discrete Monitor Timings as per version 1.0, revision 12 */
 
index b5c3aab..1445e85 100644 (file)
@@ -52,7 +52,7 @@ enum v4l2_mbus_pixelcode {
        V4L2_MBUS_FMT_RGB888_2X12_LE = 0x100c,
        V4L2_MBUS_FMT_ARGB8888_1X32 = 0x100d,
 
-       /* YUV (including grey) - next is 0x2018 */
+       /* YUV (including grey) - next is 0x2024 */
        V4L2_MBUS_FMT_Y8_1X8 = 0x2001,
        V4L2_MBUS_FMT_UV8_1X8 = 0x2015,
        V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002,
@@ -64,6 +64,8 @@ enum v4l2_mbus_pixelcode {
        V4L2_MBUS_FMT_YUYV8_2X8 = 0x2008,
        V4L2_MBUS_FMT_YVYU8_2X8 = 0x2009,
        V4L2_MBUS_FMT_Y10_1X10 = 0x200a,
+       V4L2_MBUS_FMT_UYVY10_2X10 = 0x2018,
+       V4L2_MBUS_FMT_VYUY10_2X10 = 0x2019,
        V4L2_MBUS_FMT_YUYV10_2X10 = 0x200b,
        V4L2_MBUS_FMT_YVYU10_2X10 = 0x200c,
        V4L2_MBUS_FMT_Y12_1X12 = 0x2013,
@@ -72,10 +74,20 @@ enum v4l2_mbus_pixelcode {
        V4L2_MBUS_FMT_YUYV8_1X16 = 0x2011,
        V4L2_MBUS_FMT_YVYU8_1X16 = 0x2012,
        V4L2_MBUS_FMT_YDYUYDYV8_1X16 = 0x2014,
+       V4L2_MBUS_FMT_UYVY10_1X20 = 0x201a,
+       V4L2_MBUS_FMT_VYUY10_1X20 = 0x201b,
        V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d,
        V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e,
        V4L2_MBUS_FMT_YUV10_1X30 = 0x2016,
        V4L2_MBUS_FMT_AYUV8_1X32 = 0x2017,
+       V4L2_MBUS_FMT_UYVY12_2X12 = 0x201c,
+       V4L2_MBUS_FMT_VYUY12_2X12 = 0x201d,
+       V4L2_MBUS_FMT_YUYV12_2X12 = 0x201e,
+       V4L2_MBUS_FMT_YVYU12_2X12 = 0x201f,
+       V4L2_MBUS_FMT_UYVY12_1X24 = 0x2020,
+       V4L2_MBUS_FMT_VYUY12_1X24 = 0x2021,
+       V4L2_MBUS_FMT_YUYV12_1X24 = 0x2022,
+       V4L2_MBUS_FMT_YVYU12_1X24 = 0x2023,
 
        /* Bayer - next is 0x3019 */
        V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001,
index 87e0515..a619cdd 100644 (file)
@@ -151,26 +151,24 @@ struct v4l2_subdev_selection {
 /* Backwards compatibility define --- to be removed */
 #define v4l2_subdev_edid v4l2_edid
 
-#define VIDIOC_SUBDEV_G_FMT    _IOWR('V',  4, struct v4l2_subdev_format)
-#define VIDIOC_SUBDEV_S_FMT    _IOWR('V',  5, struct v4l2_subdev_format)
-#define VIDIOC_SUBDEV_G_FRAME_INTERVAL \
-                       _IOWR('V', 21, struct v4l2_subdev_frame_interval)
-#define VIDIOC_SUBDEV_S_FRAME_INTERVAL \
-                       _IOWR('V', 22, struct v4l2_subdev_frame_interval)
-#define VIDIOC_SUBDEV_ENUM_MBUS_CODE \
-                       _IOWR('V',  2, struct v4l2_subdev_mbus_code_enum)
-#define VIDIOC_SUBDEV_ENUM_FRAME_SIZE \
-                       _IOWR('V', 74, struct v4l2_subdev_frame_size_enum)
-#define VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL \
-                       _IOWR('V', 75, struct v4l2_subdev_frame_interval_enum)
-#define VIDIOC_SUBDEV_G_CROP   _IOWR('V', 59, struct v4l2_subdev_crop)
-#define VIDIOC_SUBDEV_S_CROP   _IOWR('V', 60, struct v4l2_subdev_crop)
-#define VIDIOC_SUBDEV_G_SELECTION \
-       _IOWR('V', 61, struct v4l2_subdev_selection)
-#define VIDIOC_SUBDEV_S_SELECTION \
-       _IOWR('V', 62, struct v4l2_subdev_selection)
-/* These two G/S_EDID ioctls are identical to the ioctls in videodev2.h */
-#define VIDIOC_SUBDEV_G_EDID   _IOWR('V', 40, struct v4l2_edid)
-#define VIDIOC_SUBDEV_S_EDID   _IOWR('V', 41, struct v4l2_edid)
+#define VIDIOC_SUBDEV_G_FMT                    _IOWR('V',  4, struct v4l2_subdev_format)
+#define VIDIOC_SUBDEV_S_FMT                    _IOWR('V',  5, struct v4l2_subdev_format)
+#define VIDIOC_SUBDEV_G_FRAME_INTERVAL         _IOWR('V', 21, struct v4l2_subdev_frame_interval)
+#define VIDIOC_SUBDEV_S_FRAME_INTERVAL         _IOWR('V', 22, struct v4l2_subdev_frame_interval)
+#define VIDIOC_SUBDEV_ENUM_MBUS_CODE           _IOWR('V',  2, struct v4l2_subdev_mbus_code_enum)
+#define VIDIOC_SUBDEV_ENUM_FRAME_SIZE          _IOWR('V', 74, struct v4l2_subdev_frame_size_enum)
+#define VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL      _IOWR('V', 75, struct v4l2_subdev_frame_interval_enum)
+#define VIDIOC_SUBDEV_G_CROP                   _IOWR('V', 59, struct v4l2_subdev_crop)
+#define VIDIOC_SUBDEV_S_CROP                   _IOWR('V', 60, struct v4l2_subdev_crop)
+#define VIDIOC_SUBDEV_G_SELECTION              _IOWR('V', 61, struct v4l2_subdev_selection)
+#define VIDIOC_SUBDEV_S_SELECTION              _IOWR('V', 62, struct v4l2_subdev_selection)
+/* The following ioctls are identical to the ioctls in videodev2.h */
+#define VIDIOC_SUBDEV_G_EDID                   _IOWR('V', 40, struct v4l2_edid)
+#define VIDIOC_SUBDEV_S_EDID                   _IOWR('V', 41, struct v4l2_edid)
+#define VIDIOC_SUBDEV_S_DV_TIMINGS             _IOWR('V', 87, struct v4l2_dv_timings)
+#define VIDIOC_SUBDEV_G_DV_TIMINGS             _IOWR('V', 88, struct v4l2_dv_timings)
+#define VIDIOC_SUBDEV_ENUM_DV_TIMINGS          _IOWR('V', 98, struct v4l2_enum_dv_timings)
+#define VIDIOC_SUBDEV_QUERY_DV_TIMINGS         _IOR('V', 99, struct v4l2_dv_timings)
+#define VIDIOC_SUBDEV_DV_TIMINGS_CAP           _IOWR('V', 100, struct v4l2_dv_timings_cap)
 
 #endif
index ea468ee..168ff50 100644 (file)
@@ -649,7 +649,6 @@ struct v4l2_plane {
  * @length:    size in bytes of the buffer (NOT its payload) for single-plane
  *             buffers (when type != *_MPLANE); number of elements in the
  *             planes array for multi-plane buffers
- * @input:     input number from which the video data has has been captured
  *
  * Contains data exchanged by application and driver using one of the Streaming
  * I/O methods.
@@ -1107,12 +1106,15 @@ struct v4l2_dv_timings {
 
 /** struct v4l2_enum_dv_timings - DV timings enumeration
  * @index:     enumeration index
+ * @pad:       the pad number for which to enumerate timings (used with
+ *             v4l-subdev nodes only)
  * @reserved:  must be zeroed
  * @timings:   the timings for the given index
  */
 struct v4l2_enum_dv_timings {
        __u32 index;
-       __u32 reserved[3];
+       __u32 pad;
+       __u32 reserved[2];
        struct v4l2_dv_timings timings;
 };
 
@@ -1150,11 +1152,14 @@ struct v4l2_bt_timings_cap {
 
 /** struct v4l2_dv_timings_cap - DV timings capabilities
  * @type:      the type of the timings (same as in struct v4l2_dv_timings)
+ * @pad:       the pad number for which to query capabilities (used with
+ *             v4l-subdev nodes only)
  * @bt:                the BT656/1120 timings capabilities
  */
 struct v4l2_dv_timings_cap {
        __u32 type;
-       __u32 reserved[3];
+       __u32 pad;
+       __u32 reserved[2];
        union {
                struct v4l2_bt_timings_cap bt;
                __u32 raw_data[32];
@@ -1765,6 +1770,7 @@ struct v4l2_streamparm {
 #define V4L2_EVENT_EOS                         2
 #define V4L2_EVENT_CTRL                                3
 #define V4L2_EVENT_FRAME_SYNC                  4
+#define V4L2_EVENT_SOURCE_CHANGE               5
 #define V4L2_EVENT_PRIVATE_START               0x08000000
 
 /* Payload for V4L2_EVENT_VSYNC */
@@ -1796,12 +1802,19 @@ struct v4l2_event_frame_sync {
        __u32 frame_sequence;
 };
 
+#define V4L2_EVENT_SRC_CH_RESOLUTION           (1 << 0)
+
+struct v4l2_event_src_change {
+       __u32 changes;
+};
+
 struct v4l2_event {
        __u32                           type;
        union {
                struct v4l2_event_vsync         vsync;
                struct v4l2_event_ctrl          ctrl;
                struct v4l2_event_frame_sync    frame_sync;
+               struct v4l2_event_src_change    src_change;
                __u8                            data[64];
        } u;
        __u32                           pending;
index 9fc6219..2249483 100644 (file)
@@ -94,9 +94,11 @@ enum {
        SNDRV_HWDEP_IFACE_HDA,          /* HD-audio */
        SNDRV_HWDEP_IFACE_USB_STREAM,   /* direct access to usb stream */
        SNDRV_HWDEP_IFACE_FW_DICE,      /* TC DICE FireWire device */
+       SNDRV_HWDEP_IFACE_FW_FIREWORKS, /* Echo Audio Fireworks based device */
+       SNDRV_HWDEP_IFACE_FW_BEBOB,     /* BridgeCo BeBoB based device */
 
        /* Don't forget to change the following: */
-       SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DICE
+       SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_BEBOB
 };
 
 struct snd_hwdep_info {
index 59f5961..af4bd13 100644 (file)
@@ -2,11 +2,13 @@
 #define _UAPI_SOUND_FIREWIRE_H_INCLUDED
 
 #include <linux/ioctl.h>
+#include <linux/types.h>
 
 /* events can be read() from the hwdep device */
 
 #define SNDRV_FIREWIRE_EVENT_LOCK_STATUS       0x000010cc
 #define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e
+#define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE      0x4e617475
 
 struct snd_firewire_event_common {
        unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@@ -22,10 +24,27 @@ struct snd_firewire_event_dice_notification {
        unsigned int notification; /* DICE-specific bits */
 };
 
+#define SND_EFW_TRANSACTION_USER_SEQNUM_MAX    ((__u32)((__u16)~0) - 1)
+/* each field should be in big endian */
+struct snd_efw_transaction {
+       __be32 length;
+       __be32 version;
+       __be32 seqnum;
+       __be32 category;
+       __be32 command;
+       __be32 status;
+       __be32 params[0];
+};
+struct snd_firewire_event_efw_response {
+       unsigned int type;
+       __be32 response[0];     /* some responses */
+};
+
 union snd_firewire_event {
        struct snd_firewire_event_common            common;
        struct snd_firewire_event_lock_status       lock_status;
        struct snd_firewire_event_dice_notification dice_notification;
+       struct snd_firewire_event_efw_response      efw_response;
 };
 
 
@@ -34,7 +53,9 @@ union snd_firewire_event {
 #define SNDRV_FIREWIRE_IOCTL_UNLOCK    _IO('H', 0xfa)
 
 #define SNDRV_FIREWIRE_TYPE_DICE       1
-/* Fireworks, AV/C, RME, MOTU, ... */
+#define SNDRV_FIREWIRE_TYPE_FIREWORKS  2
+#define SNDRV_FIREWIRE_TYPE_BEBOB      3
+/* AV/C, RME, MOTU, ... */
 
 struct snd_firewire_get_info {
        unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
index 6adb445..fc06c5b 100644 (file)
@@ -319,6 +319,7 @@ enum omapdss_version {
        OMAPDSS_VER_OMAP4430_ES2,       /* OMAP4430 ES2.0, 2.1, 2.2 */
        OMAPDSS_VER_OMAP4,              /* All other OMAP4s */
        OMAPDSS_VER_OMAP5,
+       OMAPDSS_VER_AM43xx,
 };
 
 /* Board specific data */
@@ -388,8 +389,8 @@ struct omap_dss_cpr_coefs {
 };
 
 struct omap_overlay_info {
-       u32 paddr;
-       u32 p_uv_addr;  /* for NV12 format */
+       dma_addr_t paddr;
+       dma_addr_t p_uv_addr;  /* for NV12 format */
        u16 screen_width;
        u16 width;
        u16 height;
@@ -964,9 +965,6 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
                bool replication, const struct omap_video_timings *mgr_timings,
                bool mem_to_mem);
 
-#define to_dss_driver(x) container_of((x), struct omap_dss_driver, driver)
-#define to_dss_device(x) container_of((x), struct omap_dss_device, old_dev)
-
 int omapdss_compat_init(void);
 void omapdss_compat_uninit(void);
 
index 2fac9cc..9a83d78 100644 (file)
@@ -257,8 +257,7 @@ config ARCH_HAS_OPP
        bool
 
 config PM_OPP
-       bool "Operating Performance Point (OPP) Layer library"
-       depends on ARCH_HAS_OPP
+       bool
        ---help---
          SOCs have a standard set of tuples consisting of frequency and
          voltage pairs that the device will support per voltage domain. This
index f4f2073..df88d55 100644 (file)
@@ -35,7 +35,7 @@
 static int nocompress;
 static int noresume;
 static int resume_wait;
-static int resume_delay;
+static unsigned int resume_delay;
 static char resume_file[256] = CONFIG_PM_STD_PARTITION;
 dev_t swsusp_resume_device;
 sector_t swsusp_resume_block;
@@ -228,19 +228,23 @@ static void platform_recover(int platform_mode)
 void swsusp_show_speed(struct timeval *start, struct timeval *stop,
                        unsigned nr_pages, char *msg)
 {
-       s64 elapsed_centisecs64;
-       int centisecs;
-       int k;
-       int kps;
+       u64 elapsed_centisecs64;
+       unsigned int centisecs;
+       unsigned int k;
+       unsigned int kps;
 
        elapsed_centisecs64 = timeval_to_ns(stop) - timeval_to_ns(start);
+       /*
+        * If "(s64)elapsed_centisecs64 < 0", it will print long elapsed time,
+        * it is obvious enough for what went wrong.
+        */
        do_div(elapsed_centisecs64, NSEC_PER_SEC / 100);
        centisecs = elapsed_centisecs64;
        if (centisecs == 0)
                centisecs = 1;  /* avoid div-by-zero */
        k = nr_pages * (PAGE_SIZE / 1024);
        kps = (k * 100) / centisecs;
-       printk(KERN_INFO "PM: %s %d kbytes in %d.%02d seconds (%d.%02d MB/s)\n",
+       printk(KERN_INFO "PM: %s %u kbytes in %u.%02u seconds (%u.%02u MB/s)\n",
                        msg, k,
                        centisecs / 100, centisecs % 100,
                        kps / 1000, (kps % 1000) / 10);
@@ -595,7 +599,8 @@ static void power_down(void)
        case HIBERNATION_PLATFORM:
                hibernation_platform_enter();
        case HIBERNATION_SHUTDOWN:
-               kernel_power_off();
+               if (pm_power_off)
+                       kernel_power_off();
                break;
 #ifdef CONFIG_SUSPEND
        case HIBERNATION_SUSPEND:
@@ -623,7 +628,8 @@ static void power_down(void)
         * corruption after resume.
         */
        printk(KERN_CRIT "PM: Please power down manually\n");
-       while(1);
+       while (1)
+               cpu_relax();
 }
 
 /**
@@ -1109,7 +1115,10 @@ static int __init resumewait_setup(char *str)
 
 static int __init resumedelay_setup(char *str)
 {
-       resume_delay = simple_strtoul(str, NULL, 0);
+       int rc = kstrtouint(str, 0, &resume_delay);
+
+       if (rc)
+               return rc;
        return 1;
 }
 
index 6271bc4..573410d 100644 (file)
@@ -279,26 +279,26 @@ static inline void pm_print_times_init(void) {}
 struct kobject *power_kobj;
 
 /**
- *     state - control system power state.
+ * state - control system sleep states.
  *
- *     show() returns what states are supported, which is hard-coded to
- *     'freeze' (Low-Power Idle), 'standby' (Power-On Suspend),
- *     'mem' (Suspend-to-RAM), and 'disk' (Suspend-to-Disk).
+ * show() returns available sleep state labels, which may be "mem", "standby",
+ * "freeze" and "disk" (hibernation).  See Documentation/power/states.txt for a
+ * description of what they mean.
  *
- *     store() accepts one of those strings, translates it into the
- *     proper enumerated value, and initiates a suspend transition.
+ * store() accepts one of those strings, translates it into the proper
+ * enumerated value, and initiates a suspend transition.
  */
 static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
                          char *buf)
 {
        char *s = buf;
 #ifdef CONFIG_SUSPEND
-       int i;
+       suspend_state_t i;
+
+       for (i = PM_SUSPEND_MIN; i < PM_SUSPEND_MAX; i++)
+               if (pm_states[i].state)
+                       s += sprintf(s,"%s ", pm_states[i].label);
 
-       for (i = 0; i < PM_SUSPEND_MAX; i++) {
-               if (pm_states[i] && valid_state(i))
-                       s += sprintf(s,"%s ", pm_states[i]);
-       }
 #endif
 #ifdef CONFIG_HIBERNATION
        s += sprintf(s, "%s\n", "disk");
@@ -314,7 +314,7 @@ static suspend_state_t decode_state(const char *buf, size_t n)
 {
 #ifdef CONFIG_SUSPEND
        suspend_state_t state = PM_SUSPEND_MIN;
-       const char * const *s;
+       struct pm_sleep_state *s;
 #endif
        char *p;
        int len;
@@ -328,8 +328,9 @@ static suspend_state_t decode_state(const char *buf, size_t n)
 
 #ifdef CONFIG_SUSPEND
        for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++)
-               if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
-                       return state;
+               if (s->state && len == strlen(s->label)
+                   && !strncmp(buf, s->label, len))
+                       return s->state;
 #endif
 
        return PM_SUSPEND_ON;
@@ -447,8 +448,8 @@ static ssize_t autosleep_show(struct kobject *kobj,
 
 #ifdef CONFIG_SUSPEND
        if (state < PM_SUSPEND_MAX)
-               return sprintf(buf, "%s\n", valid_state(state) ?
-                                               pm_states[state] : "error");
+               return sprintf(buf, "%s\n", pm_states[state].state ?
+                                       pm_states[state].label : "error");
 #endif
 #ifdef CONFIG_HIBERNATION
        return sprintf(buf, "disk\n");
index 15f37ea..c60f13b 100644 (file)
@@ -178,17 +178,20 @@ extern void swsusp_show_speed(struct timeval *, struct timeval *,
                                unsigned int, char *);
 
 #ifdef CONFIG_SUSPEND
+struct pm_sleep_state {
+       const char *label;
+       suspend_state_t state;
+};
+
 /* kernel/power/suspend.c */
-extern const char *const pm_states[];
+extern struct pm_sleep_state pm_states[];
 
-extern bool valid_state(suspend_state_t state);
 extern int suspend_devices_and_enter(suspend_state_t state);
 #else /* !CONFIG_SUSPEND */
 static inline int suspend_devices_and_enter(suspend_state_t state)
 {
        return -ENOSYS;
 }
-static inline bool valid_state(suspend_state_t state) { return false; }
 #endif /* !CONFIG_SUSPEND */
 
 #ifdef CONFIG_PM_TEST_SUSPEND
index 155721f..963e6d0 100644 (file)
 
 #include "power.h"
 
-const char *const pm_states[PM_SUSPEND_MAX] = {
-       [PM_SUSPEND_FREEZE]     = "freeze",
-       [PM_SUSPEND_STANDBY]    = "standby",
-       [PM_SUSPEND_MEM]        = "mem",
+struct pm_sleep_state pm_states[PM_SUSPEND_MAX] = {
+       [PM_SUSPEND_FREEZE] = { .label = "freeze", .state = PM_SUSPEND_FREEZE },
+       [PM_SUSPEND_STANDBY] = { .label = "standby", },
+       [PM_SUSPEND_MEM] = { .label = "mem", },
 };
 
 static const struct platform_suspend_ops *suspend_ops;
+static const struct platform_freeze_ops *freeze_ops;
 
 static bool need_suspend_ops(suspend_state_t state)
 {
@@ -47,6 +48,13 @@ static bool need_suspend_ops(suspend_state_t state)
 static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head);
 static bool suspend_freeze_wake;
 
+void freeze_set_ops(const struct platform_freeze_ops *ops)
+{
+       lock_system_sleep();
+       freeze_ops = ops;
+       unlock_system_sleep();
+}
+
 static void freeze_begin(void)
 {
        suspend_freeze_wake = false;
@@ -68,42 +76,62 @@ void freeze_wake(void)
 }
 EXPORT_SYMBOL_GPL(freeze_wake);
 
+static bool valid_state(suspend_state_t state)
+{
+       /*
+        * PM_SUSPEND_STANDBY and PM_SUSPEND_MEM states need low level
+        * support and need to be valid to the low level
+        * implementation, no valid callback implies that none are valid.
+        */
+       return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
+}
+
+/*
+ * If this is set, the "mem" label always corresponds to the deepest sleep state
+ * available, the "standby" label corresponds to the second deepest sleep state
+ * available (if any), and the "freeze" label corresponds to the remaining
+ * available sleep state (if there is one).
+ */
+static bool relative_states;
+
+static int __init sleep_states_setup(char *str)
+{
+       relative_states = !strncmp(str, "1", 1);
+       if (relative_states) {
+               pm_states[PM_SUSPEND_MEM].state = PM_SUSPEND_FREEZE;
+               pm_states[PM_SUSPEND_FREEZE].state = 0;
+       }
+       return 1;
+}
+
+__setup("relative_sleep_states=", sleep_states_setup);
+
 /**
  * suspend_set_ops - Set the global suspend method table.
  * @ops: Suspend operations to use.
  */
 void suspend_set_ops(const struct platform_suspend_ops *ops)
 {
+       suspend_state_t i;
+       int j = PM_SUSPEND_MAX - 1;
+
        lock_system_sleep();
+
        suspend_ops = ops;
+       for (i = PM_SUSPEND_MEM; i >= PM_SUSPEND_STANDBY; i--)
+               if (valid_state(i))
+                       pm_states[j--].state = i;
+               else if (!relative_states)
+                       pm_states[j--].state = 0;
+
+       pm_states[j--].state = PM_SUSPEND_FREEZE;
+       while (j >= PM_SUSPEND_MIN)
+               pm_states[j--].state = 0;
+
        unlock_system_sleep();
 }
 EXPORT_SYMBOL_GPL(suspend_set_ops);
 
-bool valid_state(suspend_state_t state)
-{
-       if (state == PM_SUSPEND_FREEZE) {
-#ifdef CONFIG_PM_DEBUG
-               if (pm_test_level != TEST_NONE &&
-                   pm_test_level != TEST_FREEZER &&
-                   pm_test_level != TEST_DEVICES &&
-                   pm_test_level != TEST_PLATFORM) {
-                       printk(KERN_WARNING "Unsupported pm_test mode for "
-                                       "freeze state, please choose "
-                                       "none/freezer/devices/platform.\n");
-                       return false;
-               }
-#endif
-                       return true;
-       }
-       /*
-        * PM_SUSPEND_STANDBY and PM_SUSPEND_MEMORY states need lowlevel
-        * support and need to be valid to the lowlevel
-        * implementation, no valid callback implies that none are valid.
-        */
-       return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
-}
-
 /**
  * suspend_valid_only_mem - Generic memory-only valid callback.
  *
@@ -271,6 +299,10 @@ int suspend_devices_and_enter(suspend_state_t state)
                error = suspend_ops->begin(state);
                if (error)
                        goto Close;
+       } else if (state == PM_SUSPEND_FREEZE && freeze_ops->begin) {
+               error = freeze_ops->begin();
+               if (error)
+                       goto Close;
        }
        suspend_console();
        suspend_test_start();
@@ -296,6 +328,9 @@ int suspend_devices_and_enter(suspend_state_t state)
  Close:
        if (need_suspend_ops(state) && suspend_ops->end)
                suspend_ops->end();
+       else if (state == PM_SUSPEND_FREEZE && freeze_ops->end)
+               freeze_ops->end();
+
        trace_machine_suspend(PWR_EVENT_EXIT);
        return error;
 
@@ -330,9 +365,17 @@ static int enter_state(suspend_state_t state)
 {
        int error;
 
-       if (!valid_state(state))
-               return -ENODEV;
-
+       if (state == PM_SUSPEND_FREEZE) {
+#ifdef CONFIG_PM_DEBUG
+               if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
+                       pr_warning("PM: Unsupported test mode for freeze state,"
+                                  "please choose none/freezer/devices/platform.\n");
+                       return -EAGAIN;
+               }
+#endif
+       } else if (!valid_state(state)) {
+               return -EINVAL;
+       }
        if (!mutex_trylock(&pm_mutex))
                return -EBUSY;
 
@@ -343,7 +386,7 @@ static int enter_state(suspend_state_t state)
        sys_sync();
        printk("done.\n");
 
-       pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
+       pr_debug("PM: Preparing system for %s sleep\n", pm_states[state].label);
        error = suspend_prepare(state);
        if (error)
                goto Unlock;
@@ -351,7 +394,7 @@ static int enter_state(suspend_state_t state)
        if (suspend_test(TEST_FREEZER))
                goto Finish;
 
-       pr_debug("PM: Entering %s sleep\n", pm_states[state]);
+       pr_debug("PM: Entering %s sleep\n", pm_states[state].label);
        pm_restrict_gfp_mask();
        error = suspend_devices_and_enter(state);
        pm_restore_gfp_mask();
index 9b2a1d5..269b097 100644 (file)
@@ -92,13 +92,13 @@ static void __init test_wakealarm(struct rtc_device *rtc, suspend_state_t state)
        }
 
        if (state == PM_SUSPEND_MEM) {
-               printk(info_test, pm_states[state]);
+               printk(info_test, pm_states[state].label);
                status = pm_suspend(state);
                if (status == -ENODEV)
                        state = PM_SUSPEND_STANDBY;
        }
        if (state == PM_SUSPEND_STANDBY) {
-               printk(info_test, pm_states[state]);
+               printk(info_test, pm_states[state].label);
                status = pm_suspend(state);
        }
        if (status < 0)
@@ -136,18 +136,16 @@ static char warn_bad_state[] __initdata =
 
 static int __init setup_test_suspend(char *value)
 {
-       unsigned i;
+       suspend_state_t i;
 
        /* "=mem" ==> "mem" */
        value++;
-       for (i = 0; i < PM_SUSPEND_MAX; i++) {
-               if (!pm_states[i])
-                       continue;
-               if (strcmp(pm_states[i], value) != 0)
-                       continue;
-               test_state = (__force suspend_state_t) i;
-               return 0;
-       }
+       for (i = PM_SUSPEND_MIN; i < PM_SUSPEND_MAX; i++)
+               if (!strcmp(pm_states[i].label, value)) {
+                       test_state = pm_states[i].state;
+                       return 0;
+               }
+
        printk(warn_bad_state, value);
        return 0;
 }
@@ -164,8 +162,8 @@ static int __init test_suspend(void)
        /* PM is initialized by now; is that state testable? */
        if (test_state == PM_SUSPEND_ON)
                goto done;
-       if (!valid_state(test_state)) {
-               printk(warn_bad_state, pm_states[test_state]);
+       if (!pm_states[test_state].state) {
+               printk(warn_bad_state, pm_states[test_state].label);
                goto done;
        }
 
index 8c9a481..aaa3261 100644 (file)
@@ -567,7 +567,7 @@ static int lzo_compress_threadfn(void *data)
 
 /**
  * save_image_lzo - Save the suspend image data compressed with LZO.
- * @handle: Swap mam handle to use for saving the image.
+ * @handle: Swap map handle to use for saving the image.
  * @snapshot: Image to read data from.
  * @nr_to_write: Number of pages to save.
  */
index 819ac51..99c8bfe 100644 (file)
@@ -575,8 +575,8 @@ config DEBUG_HIGHMEM
        bool "Highmem debugging"
        depends on DEBUG_KERNEL && HIGHMEM
        help
-         This options enables addition error checking for high memory systems.
-         Disable for production systems.
+         This option enables additional error checking for high memory
+         systems.  Disable for production systems.
 
 config HAVE_DEBUG_STACKOVERFLOW
        bool
index 9b1f906..e0c20eb 100644 (file)
@@ -301,6 +301,24 @@ char *strchr(const char *s, int c)
 EXPORT_SYMBOL(strchr);
 #endif
 
+#ifndef __HAVE_ARCH_STRCHRNUL
+/**
+ * strchrnul - Find and return a character in a string, or end of string
+ * @s: The string to be searched
+ * @c: The character to search for
+ *
+ * Returns pointer to first occurrence of 'c' in s. If c is not found, then
+ * return a pointer to the null byte at the end of s.
+ */
+char *strchrnul(const char *s, int c)
+{
+       while (*s && *s != (char)c)
+               s++;
+       return (char *)s;
+}
+EXPORT_SYMBOL(strchrnul);
+#endif
+
 #ifndef __HAVE_ARCH_STRRCHR
 /**
  * strrchr - Find the last occurrence of a character in a string
index c69781e..8058fcd 100644 (file)
@@ -341,10 +341,10 @@ void *dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags,
                                continue;
                        if (pool->dev)
                                dev_err(pool->dev,
-                                       "dma_pool_alloc %s, %p (corruped)\n",
+                                       "dma_pool_alloc %s, %p (corrupted)\n",
                                        pool->name, retval);
                        else
-                               pr_err("dma_pool_alloc %s, %p (corruped)\n",
+                               pr_err("dma_pool_alloc %s, %p (corrupted)\n",
                                        pool->name, retval);
 
                        /*
index 0663556..ea7f953 100644 (file)
@@ -316,7 +316,7 @@ static struct symbol *sym_add_exported(const char *name, struct module *mod,
                             s->module->name,
                             is_vmlinux(s->module->name) ?"":".ko");
                } else {
-                       /* In case Modules.symvers was out of date */
+                       /* In case Module.symvers was out of date */
                        s->module = mod;
                }
        }
index f01bffb..401107b 100644 (file)
@@ -241,7 +241,7 @@ static struct snd_kcontrol_new inputgain_control = {
 static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "Line-In", "Microphone" };
+       static const char * const texts[] = { "Line-In", "Microphone" };
 
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
index e6c727b..83be8e3 100644 (file)
@@ -14,6 +14,8 @@
 #include <linux/dma-mapping.h>
 #include <linux/dmaengine.h>
 
+#include <mach/dma.h>
+
 #include <sound/core.h>
 #include <sound/pxa2xx-lib.h>
 #include <sound/dmaengine_pcm.h>
index 2a8fc08..0033098 100644 (file)
@@ -9,12 +9,11 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#include <mach/dma.h>
 
 struct pxa2xx_runtime_data {
        int dma_ch;
        struct snd_dmaengine_dai_dma_data *params;
-       pxa_dma_desc *dma_desc_array;
+       struct pxa_dma_desc *dma_desc_array;
        dma_addr_t dma_desc_array_phys;
 };
 
index 05ec049..a04d231 100644 (file)
@@ -1198,6 +1198,7 @@ static int atmel_ac97c_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver atmel_ac97c_driver = {
+       .probe          = atmel_ac97c_probe,
        .remove         = atmel_ac97c_remove,
        .driver         = {
                .name   = "atmel_ac97c",
@@ -1205,19 +1206,7 @@ static struct platform_driver atmel_ac97c_driver = {
                .pm     = ATMEL_AC97C_PM_OPS,
        },
 };
-
-static int __init atmel_ac97c_init(void)
-{
-       return platform_driver_probe(&atmel_ac97c_driver,
-                       atmel_ac97c_probe);
-}
-module_init(atmel_ac97c_init);
-
-static void __exit atmel_ac97c_exit(void)
-{
-       platform_driver_unregister(&atmel_ac97c_driver);
-}
-module_exit(atmel_ac97c_exit);
+module_platform_driver(atmel_ac97c_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Driver for Atmel AC97 controller");
index ce83def..9acc77e 100644 (file)
@@ -345,7 +345,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
                        snd_pcm_debug_name(substream, name, sizeof(name));
                        xrun_log_show(substream);
                        pcm_err(substream->pcm,
-                               "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
+                               "XRUN: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
                                name, pos, runtime->buffer_size,
                                runtime->period_size);
                }
index 3e05c55..a1fd77a 100644 (file)
@@ -362,13 +362,13 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
                if (! port->name[0]) {
                        if (info->name[0]) {
                                if (ports > 1)
-                                       snprintf(port->name, sizeof(port->name), "%s-%d", info->name, p);
+                                       snprintf(port->name, sizeof(port->name), "%s-%u", info->name, p);
                                else
                                        snprintf(port->name, sizeof(port->name), "%s", info->name);
                        } else {
                                /* last resort */
                                if (ports > 1)
-                                       sprintf(port->name, "MIDI %d-%d-%d", card->number, device, p);
+                                       sprintf(port->name, "MIDI %d-%d-%u", card->number, device, p);
                                else
                                        sprintf(port->name, "MIDI %d-%d", card->number, device);
                        }
index b3e274f..775ef2e 100644 (file)
@@ -9,12 +9,12 @@ if SND_FIREWIRE && FIREWIRE
 
 config SND_FIREWIRE_LIB
        tristate
-       depends on SND_PCM
+       select SND_PCM
+       select SND_RAWMIDI
 
 config SND_DICE
        tristate "DICE-based DACs (EXPERIMENTAL)"
        select SND_HWDEP
-       select SND_PCM
        select SND_FIREWIRE_LIB
        help
          Say Y here to include support for many DACs based on the DICE
@@ -28,7 +28,6 @@ config SND_DICE
 
 config SND_FIREWIRE_SPEAKERS
        tristate "FireWire speakers"
-       select SND_PCM
        select SND_FIREWIRE_LIB
        help
          Say Y here to include support for the Griffin FireWave Surround
@@ -39,7 +38,6 @@ config SND_FIREWIRE_SPEAKERS
 
 config SND_ISIGHT
        tristate "Apple iSight microphone"
-       select SND_PCM
        select SND_FIREWIRE_LIB
        help
          Say Y here to include support for the front and rear microphones
@@ -50,8 +48,6 @@ config SND_ISIGHT
 
 config SND_SCS1X
        tristate "Stanton Control System 1 MIDI"
-       select SND_PCM
-       select SND_RAWMIDI
        select SND_FIREWIRE_LIB
        help
          Say Y here to include support for the MIDI ports of the Stanton
@@ -61,4 +57,59 @@ config SND_SCS1X
          To compile this driver as a module, choose M here: the module
          will be called snd-scs1x.
 
+config SND_FIREWORKS
+       tristate "Echo Fireworks board module support"
+       select SND_FIREWIRE_LIB
+       select SND_HWDEP
+       help
+         Say Y here to include support for FireWire devices based
+         on Echo Digital Audio Fireworks board:
+          * Mackie Onyx 400F/1200F
+          * Echo AudioFire12/8(until 2009 July)
+          * Echo AudioFire2/4/Pre8/8(since 2009 July)
+          * Echo Fireworks 8/HDMI
+          * Gibson Robot Interface Pack/GoldTop
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-fireworks.
+
+config SND_BEBOB
+       tristate "BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware"
+       select SND_FIREWIRE_LIB
+       select SND_HWDEP
+        help
+        Say Y here to include support for FireWire devices based
+        on BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware:
+         * Edirol FA-66/FA-101
+         * PreSonus FIREBOX/FIREPOD/FP10/Inspire1394
+         * BridgeCo RDAudio1/Audio5
+         * Mackie Onyx 1220/1620/1640 (Firewire I/O Card)
+         * Mackie d.2 (Firewire Option)
+         * Stanton FinalScratch 2 (ScratchAmp)
+         * Tascam IF-FW/DM
+         * Behringer XENIX UFX 1204/1604
+         * Behringer Digital Mixer X32 series (X-UF Card)
+         * Apogee Rosetta 200/400 (X-FireWire card)
+         * Apogee DA/AD/DD-16X (X-FireWire card)
+         * Apogee Ensemble
+         * ESI Quotafire610
+         * AcousticReality eARMasterOne
+         * CME MatrixKFW
+         * Phonic Helix Board 12 MkII/18 MkII/24 MkII
+         * Phonic Helix Board 12 Universal/18 Universal/24 Universal
+         * Lynx Aurora 8/16 (LT-FW)
+         * ICON FireXon
+         * PrismSound Orpheus/ADA-8XR
+         * TerraTec PHASE 24 FW/PHASE X24 FW/PHASE 88 Rack FW
+         * Terratec EWS MIC2/EWS MIC4
+         * Terratec Aureon 7.1 Firewire
+         * Yamaha GO44/GO46
+         * Focusrite Saffire/Saffire LE/SaffirePro10 IO/SaffirePro26 IO
+         * M-Audio Firewire410/AudioPhile/Solo
+         * M-Audio Ozonic/NRV10/ProfireLightBridge
+         * M-Audio Firewire 1814/ProjectMix IO
+
+          To compile this driver as a module, choose M here: the module
+          will be called snd-bebob.
+
 endif # SND_FIREWIRE
index 5099550..fad8d49 100644 (file)
@@ -10,3 +10,5 @@ obj-$(CONFIG_SND_DICE) += snd-dice.o
 obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
 obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
 obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
+obj-$(CONFIG_SND_FIREWORKS) += fireworks/
+obj-$(CONFIG_SND_BEBOB) += bebob/
index 9048777..f96bf4c 100644 (file)
 #include <linux/firewire.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/sched.h>
 #include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/rawmidi.h>
 #include "amdtp.h"
 
 #define TICKS_PER_CYCLE                3072
 
 #define TRANSFER_DELAY_TICKS   0x2e00 /* 479.17 Âµs */
 
+/* isochronous header parameters */
+#define ISO_DATA_LENGTH_SHIFT  16
 #define TAG_CIP                        1
 
+/* common isochronous packet header parameters */
 #define CIP_EOH                        (1u << 31)
+#define CIP_EOH_MASK           0x80000000
 #define CIP_FMT_AM             (0x10 << 24)
-#define AMDTP_FDF_AM824                (0 << 19)
-#define AMDTP_FDF_SFC_SHIFT    16
+#define CIP_FMT_MASK           0x3f000000
+#define CIP_SYT_MASK           0x0000ffff
+#define CIP_SYT_NO_INFO                0xffff
+#define CIP_FDF_MASK           0x00ff0000
+#define CIP_FDF_SFC_SHIFT      16
+
+/*
+ * Audio and Music transfer protocol specific parameters
+ * only "Clock-based rate control mode" is supported
+ */
+#define AMDTP_FDF_AM824                (0 << (CIP_FDF_SFC_SHIFT + 3))
+#define AMDTP_FDF_NO_DATA      0xff
+#define AMDTP_DBS_MASK         0x00ff0000
+#define AMDTP_DBS_SHIFT                16
+#define AMDTP_DBC_MASK         0x000000ff
 
 /* TODO: make these configurable */
 #define INTERRUPT_INTERVAL     16
 #define QUEUE_LENGTH           48
 
+#define IN_PACKET_HEADER_SIZE  4
+#define OUT_PACKET_HEADER_SIZE 0
+
 static void pcm_period_tasklet(unsigned long data);
 
 /**
- * amdtp_out_stream_init - initialize an AMDTP output stream structure
- * @s: the AMDTP output stream to initialize
+ * amdtp_stream_init - initialize an AMDTP stream structure
+ * @s: the AMDTP stream to initialize
  * @unit: the target of the stream
+ * @dir: the direction of stream
  * @flags: the packet transmission method to use
  */
-int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
-                         enum cip_out_flags flags)
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+                     enum amdtp_stream_direction dir, enum cip_flags flags)
 {
        s->unit = fw_unit_get(unit);
+       s->direction = dir;
        s->flags = flags;
        s->context = ERR_PTR(-1);
        mutex_init(&s->mutex);
        tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
        s->packet_index = 0;
 
+       init_waitqueue_head(&s->callback_wait);
+       s->callbacked = false;
+       s->sync_slave = NULL;
+
+       s->rx_blocks_for_midi = UINT_MAX;
+
        return 0;
 }
-EXPORT_SYMBOL(amdtp_out_stream_init);
+EXPORT_SYMBOL(amdtp_stream_init);
 
 /**
- * amdtp_out_stream_destroy - free stream resources
- * @s: the AMDTP output stream to destroy
+ * amdtp_stream_destroy - free stream resources
+ * @s: the AMDTP stream to destroy
  */
-void amdtp_out_stream_destroy(struct amdtp_out_stream *s)
+void amdtp_stream_destroy(struct amdtp_stream *s)
 {
-       WARN_ON(amdtp_out_stream_running(s));
+       WARN_ON(amdtp_stream_running(s));
        mutex_destroy(&s->mutex);
        fw_unit_put(s->unit);
 }
-EXPORT_SYMBOL(amdtp_out_stream_destroy);
+EXPORT_SYMBOL(amdtp_stream_destroy);
 
 const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
        [CIP_SFC_32000]  =  8,
@@ -76,9 +107,75 @@ const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
 };
 EXPORT_SYMBOL(amdtp_syt_intervals);
 
+const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = {
+       [CIP_SFC_32000]  =  32000,
+       [CIP_SFC_44100]  =  44100,
+       [CIP_SFC_48000]  =  48000,
+       [CIP_SFC_88200]  =  88200,
+       [CIP_SFC_96000]  =  96000,
+       [CIP_SFC_176400] = 176400,
+       [CIP_SFC_192000] = 192000,
+};
+EXPORT_SYMBOL(amdtp_rate_table);
+
+/**
+ * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream
+ * @s:         the AMDTP stream, which must be initialized.
+ * @runtime:   the PCM substream runtime
+ */
+int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                       struct snd_pcm_runtime *runtime)
+{
+       int err;
+
+       /* AM824 in IEC 61883-6 can deliver 24bit data */
+       err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+       if (err < 0)
+               goto end;
+
+       /*
+        * Currently firewire-lib processes 16 packets in one software
+        * interrupt callback. This equals to 2msec but actually the
+        * interval of the interrupts has a jitter.
+        * Additionally, even if adding a constraint to fit period size to
+        * 2msec, actual calculated frames per period doesn't equal to 2msec,
+        * depending on sampling rate.
+        * Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec.
+        * Here let us use 5msec for safe period interrupt.
+        */
+       err = snd_pcm_hw_constraint_minmax(runtime,
+                                          SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+                                          5000, UINT_MAX);
+       if (err < 0)
+               goto end;
+
+       /* Non-Blocking stream has no more constraints */
+       if (!(s->flags & CIP_BLOCKING))
+               goto end;
+
+       /*
+        * One AMDTP packet can include some frames. In blocking mode, the
+        * number equals to SYT_INTERVAL. So the number is 8, 16 or 32,
+        * depending on its sampling rate. For accurate period interrupt, it's
+        * preferrable to aligh period/buffer sizes to current SYT_INTERVAL.
+        *
+        * TODO: These constraints can be improved with propper rules.
+        * Currently apply LCM of SYT_INTEVALs.
+        */
+       err = snd_pcm_hw_constraint_step(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
+       if (err < 0)
+               goto end;
+       err = snd_pcm_hw_constraint_step(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
+end:
+       return err;
+}
+EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
+
 /**
- * amdtp_out_stream_set_parameters - set stream parameters
- * @s: the AMDTP output stream to configure
+ * amdtp_stream_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
  * @rate: the sample rate
  * @pcm_channels: the number of PCM samples in each data block, to be encoded
  *                as AM824 multi-bit linear audio
@@ -87,41 +184,30 @@ EXPORT_SYMBOL(amdtp_syt_intervals);
  * The parameters must be set before the stream is started, and must not be
  * changed while the stream is running.
  */
-void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
-                                    unsigned int rate,
-                                    unsigned int pcm_channels,
-                                    unsigned int midi_ports)
+void amdtp_stream_set_parameters(struct amdtp_stream *s,
+                                unsigned int rate,
+                                unsigned int pcm_channels,
+                                unsigned int midi_ports)
 {
-       static const unsigned int rates[] = {
-               [CIP_SFC_32000]  =  32000,
-               [CIP_SFC_44100]  =  44100,
-               [CIP_SFC_48000]  =  48000,
-               [CIP_SFC_88200]  =  88200,
-               [CIP_SFC_96000]  =  96000,
-               [CIP_SFC_176400] = 176400,
-               [CIP_SFC_192000] = 192000,
-       };
-       unsigned int sfc;
+       unsigned int i, sfc, midi_channels;
+
+       midi_channels = DIV_ROUND_UP(midi_ports, 8);
 
-       if (WARN_ON(amdtp_out_stream_running(s)))
+       if (WARN_ON(amdtp_stream_running(s)) |
+           WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) |
+           WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
                return;
 
-       for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc)
-               if (rates[sfc] == rate)
+       for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc)
+               if (amdtp_rate_table[sfc] == rate)
                        goto sfc_found;
        WARN_ON(1);
        return;
 
 sfc_found:
-       s->dual_wire = (s->flags & CIP_HI_DUALWIRE) && sfc > CIP_SFC_96000;
-       if (s->dual_wire) {
-               sfc -= 2;
-               rate /= 2;
-               pcm_channels *= 2;
-       }
-       s->sfc = sfc;
-       s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8);
        s->pcm_channels = pcm_channels;
+       s->sfc = sfc;
+       s->data_block_quadlets = s->pcm_channels + midi_channels;
        s->midi_ports = midi_ports;
 
        s->syt_interval = amdtp_syt_intervals[sfc];
@@ -131,48 +217,50 @@ sfc_found:
        if (s->flags & CIP_BLOCKING)
                /* additional buffering needed to adjust for no-data packets */
                s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
+
+       /* init the position map for PCM and MIDI channels */
+       for (i = 0; i < pcm_channels; i++)
+               s->pcm_positions[i] = i;
+       s->midi_position = s->pcm_channels;
 }
-EXPORT_SYMBOL(amdtp_out_stream_set_parameters);
+EXPORT_SYMBOL(amdtp_stream_set_parameters);
 
 /**
- * amdtp_out_stream_get_max_payload - get the stream's packet size
- * @s: the AMDTP output stream
+ * amdtp_stream_get_max_payload - get the stream's packet size
+ * @s: the AMDTP stream
  *
  * This function must not be called before the stream has been configured
- * with amdtp_out_stream_set_parameters().
+ * with amdtp_stream_set_parameters().
  */
-unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s)
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
 {
        return 8 + s->syt_interval * s->data_block_quadlets * 4;
 }
-EXPORT_SYMBOL(amdtp_out_stream_get_max_payload);
+EXPORT_SYMBOL(amdtp_stream_get_max_payload);
 
-static void amdtp_write_s16(struct amdtp_out_stream *s,
+static void amdtp_write_s16(struct amdtp_stream *s,
                            struct snd_pcm_substream *pcm,
                            __be32 *buffer, unsigned int frames);
-static void amdtp_write_s32(struct amdtp_out_stream *s,
+static void amdtp_write_s32(struct amdtp_stream *s,
                            struct snd_pcm_substream *pcm,
                            __be32 *buffer, unsigned int frames);
-static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
-                                    struct snd_pcm_substream *pcm,
-                                    __be32 *buffer, unsigned int frames);
-static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
-                                    struct snd_pcm_substream *pcm,
-                                    __be32 *buffer, unsigned int frames);
+static void amdtp_read_s32(struct amdtp_stream *s,
+                          struct snd_pcm_substream *pcm,
+                          __be32 *buffer, unsigned int frames);
 
 /**
- * amdtp_out_stream_set_pcm_format - set the PCM format
- * @s: the AMDTP output stream to configure
+ * amdtp_stream_set_pcm_format - set the PCM format
+ * @s: the AMDTP stream to configure
  * @format: the format of the ALSA PCM device
  *
  * The sample format must be set after the other paramters (rate/PCM channels/
  * MIDI) and before the stream is started, and must not be changed while the
  * stream is running.
  */
-void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
-                                    snd_pcm_format_t format)
+void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
+                                snd_pcm_format_t format)
 {
-       if (WARN_ON(amdtp_out_stream_running(s)))
+       if (WARN_ON(amdtp_stream_pcm_running(s)))
                return;
 
        switch (format) {
@@ -180,41 +268,44 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
                WARN_ON(1);
                /* fall through */
        case SNDRV_PCM_FORMAT_S16:
-               if (s->dual_wire)
-                       s->transfer_samples = amdtp_write_s16_dualwire;
-               else
+               if (s->direction == AMDTP_OUT_STREAM) {
                        s->transfer_samples = amdtp_write_s16;
-               break;
+                       break;
+               }
+               WARN_ON(1);
+               /* fall through */
        case SNDRV_PCM_FORMAT_S32:
-               if (s->dual_wire)
-                       s->transfer_samples = amdtp_write_s32_dualwire;
-               else
+               if (s->direction == AMDTP_OUT_STREAM)
                        s->transfer_samples = amdtp_write_s32;
+               else
+                       s->transfer_samples = amdtp_read_s32;
                break;
        }
 }
-EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format);
+EXPORT_SYMBOL(amdtp_stream_set_pcm_format);
 
 /**
- * amdtp_out_stream_pcm_prepare - prepare PCM device for running
- * @s: the AMDTP output stream
+ * amdtp_stream_pcm_prepare - prepare PCM device for running
+ * @s: the AMDTP stream
  *
  * This function should be called from the PCM device's .prepare callback.
  */
-void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
 {
        tasklet_kill(&s->period_tasklet);
        s->pcm_buffer_pointer = 0;
        s->pcm_period_pointer = 0;
        s->pointer_flush = true;
 }
-EXPORT_SYMBOL(amdtp_out_stream_pcm_prepare);
+EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
 
-static unsigned int calculate_data_blocks(struct amdtp_out_stream *s)
+static unsigned int calculate_data_blocks(struct amdtp_stream *s)
 {
        unsigned int phase, data_blocks;
 
-       if (!cip_sfc_is_base_44100(s->sfc)) {
+       if (s->flags & CIP_BLOCKING)
+               data_blocks = s->syt_interval;
+       else if (!cip_sfc_is_base_44100(s->sfc)) {
                /* Sample_rate / 8000 is an integer, and precomputed. */
                data_blocks = s->data_block_state;
        } else {
@@ -243,7 +334,7 @@ static unsigned int calculate_data_blocks(struct amdtp_out_stream *s)
        return data_blocks;
 }
 
-static unsigned int calculate_syt(struct amdtp_out_stream *s,
+static unsigned int calculate_syt(struct amdtp_stream *s,
                                  unsigned int cycle)
 {
        unsigned int syt_offset, phase, index, syt;
@@ -280,175 +371,228 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s,
                syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
                syt += syt_offset % TICKS_PER_CYCLE;
 
-               return syt & 0xffff;
+               return syt & CIP_SYT_MASK;
        } else {
-               return 0xffff; /* no info */
+               return CIP_SYT_NO_INFO;
        }
 }
 
-static void amdtp_write_s32(struct amdtp_out_stream *s,
+static void amdtp_write_s32(struct amdtp_stream *s,
                            struct snd_pcm_substream *pcm,
                            __be32 *buffer, unsigned int frames)
 {
        struct snd_pcm_runtime *runtime = pcm->runtime;
-       unsigned int channels, remaining_frames, frame_step, i, c;
+       unsigned int channels, remaining_frames, i, c;
        const u32 *src;
 
        channels = s->pcm_channels;
        src = (void *)runtime->dma_area +
                        frames_to_bytes(runtime, s->pcm_buffer_pointer);
        remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-       frame_step = s->data_block_quadlets - channels;
 
        for (i = 0; i < frames; ++i) {
                for (c = 0; c < channels; ++c) {
-                       *buffer = cpu_to_be32((*src >> 8) | 0x40000000);
+                       buffer[s->pcm_positions[c]] =
+                                       cpu_to_be32((*src >> 8) | 0x40000000);
                        src++;
-                       buffer++;
                }
-               buffer += frame_step;
+               buffer += s->data_block_quadlets;
                if (--remaining_frames == 0)
                        src = (void *)runtime->dma_area;
        }
 }
 
-static void amdtp_write_s16(struct amdtp_out_stream *s,
+static void amdtp_write_s16(struct amdtp_stream *s,
                            struct snd_pcm_substream *pcm,
                            __be32 *buffer, unsigned int frames)
 {
        struct snd_pcm_runtime *runtime = pcm->runtime;
-       unsigned int channels, remaining_frames, frame_step, i, c;
+       unsigned int channels, remaining_frames, i, c;
        const u16 *src;
 
        channels = s->pcm_channels;
        src = (void *)runtime->dma_area +
                        frames_to_bytes(runtime, s->pcm_buffer_pointer);
        remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-       frame_step = s->data_block_quadlets - channels;
 
        for (i = 0; i < frames; ++i) {
                for (c = 0; c < channels; ++c) {
-                       *buffer = cpu_to_be32((*src << 8) | 0x40000000);
+                       buffer[s->pcm_positions[c]] =
+                                       cpu_to_be32((*src << 8) | 0x42000000);
                        src++;
-                       buffer++;
                }
-               buffer += frame_step;
+               buffer += s->data_block_quadlets;
                if (--remaining_frames == 0)
                        src = (void *)runtime->dma_area;
        }
 }
 
-static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
-                                    struct snd_pcm_substream *pcm,
-                                    __be32 *buffer, unsigned int frames)
+static void amdtp_read_s32(struct amdtp_stream *s,
+                          struct snd_pcm_substream *pcm,
+                          __be32 *buffer, unsigned int frames)
 {
        struct snd_pcm_runtime *runtime = pcm->runtime;
-       unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
-       const u32 *src;
+       unsigned int channels, remaining_frames, i, c;
+       u32 *dst;
 
        channels = s->pcm_channels;
-       src = (void *)runtime->dma_area +
-                       s->pcm_buffer_pointer * (runtime->frame_bits / 8);
-       frame_adjust_1 = channels - 1;
-       frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
+       dst  = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
 
-       channels /= 2;
        for (i = 0; i < frames; ++i) {
                for (c = 0; c < channels; ++c) {
-                       *buffer = cpu_to_be32((*src >> 8) | 0x40000000);
-                       src++;
-                       buffer += 2;
-               }
-               buffer -= frame_adjust_1;
-               for (c = 0; c < channels; ++c) {
-                       *buffer = cpu_to_be32((*src >> 8) | 0x40000000);
-                       src++;
-                       buffer += 2;
+                       *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8;
+                       dst++;
                }
-               buffer -= frame_adjust_2;
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       dst = (void *)runtime->dma_area;
        }
 }
 
-static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
-                                    struct snd_pcm_substream *pcm,
-                                    __be32 *buffer, unsigned int frames)
+static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
+                                  __be32 *buffer, unsigned int frames)
 {
-       struct snd_pcm_runtime *runtime = pcm->runtime;
-       unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
-       const u16 *src;
-
-       channels = s->pcm_channels;
-       src = (void *)runtime->dma_area +
-                       s->pcm_buffer_pointer * (runtime->frame_bits / 8);
-       frame_adjust_1 = channels - 1;
-       frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
+       unsigned int i, c;
 
-       channels /= 2;
        for (i = 0; i < frames; ++i) {
-               for (c = 0; c < channels; ++c) {
-                       *buffer = cpu_to_be32((*src << 8) | 0x40000000);
-                       src++;
-                       buffer += 2;
-               }
-               buffer -= frame_adjust_1;
-               for (c = 0; c < channels; ++c) {
-                       *buffer = cpu_to_be32((*src << 8) | 0x40000000);
-                       src++;
-                       buffer += 2;
-               }
-               buffer -= frame_adjust_2;
+               for (c = 0; c < s->pcm_channels; ++c)
+                       buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000);
+               buffer += s->data_block_quadlets;
        }
 }
 
-static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s,
-                                  __be32 *buffer, unsigned int frames)
+static void amdtp_fill_midi(struct amdtp_stream *s,
+                           __be32 *buffer, unsigned int frames)
 {
-       unsigned int i, c;
+       unsigned int f, port;
+       u8 *b;
+
+       for (f = 0; f < frames; f++) {
+               buffer[s->midi_position] = 0;
+               b = (u8 *)&buffer[s->midi_position];
+
+               port = (s->data_block_counter + f) % 8;
+               if ((f >= s->rx_blocks_for_midi) ||
+                   (s->midi[port] == NULL) ||
+                   (snd_rawmidi_transmit(s->midi[port], b + 1, 1) <= 0))
+                       b[0] = 0x80;
+               else
+                       b[0] = 0x81;
 
-       for (i = 0; i < frames; ++i) {
-               for (c = 0; c < s->pcm_channels; ++c)
-                       buffer[c] = cpu_to_be32(0x40000000);
                buffer += s->data_block_quadlets;
        }
 }
 
-static void amdtp_fill_midi(struct amdtp_out_stream *s,
+static void amdtp_pull_midi(struct amdtp_stream *s,
                            __be32 *buffer, unsigned int frames)
 {
-       unsigned int i;
+       unsigned int f, port;
+       int len;
+       u8 *b;
+
+       for (f = 0; f < frames; f++) {
+               port = (s->data_block_counter + f) % 8;
+               b = (u8 *)&buffer[s->midi_position];
+
+               len = b[0] - 0x80;
+               if ((1 <= len) &&  (len <= 3) && (s->midi[port]))
+                       snd_rawmidi_receive(s->midi[port], b + 1, len);
+
+               buffer += s->data_block_quadlets;
+       }
+}
+
+static void update_pcm_pointers(struct amdtp_stream *s,
+                               struct snd_pcm_substream *pcm,
+                               unsigned int frames)
+{      unsigned int ptr;
+
+       ptr = s->pcm_buffer_pointer + frames;
+       if (ptr >= pcm->runtime->buffer_size)
+               ptr -= pcm->runtime->buffer_size;
+       ACCESS_ONCE(s->pcm_buffer_pointer) = ptr;
+
+       s->pcm_period_pointer += frames;
+       if (s->pcm_period_pointer >= pcm->runtime->period_size) {
+               s->pcm_period_pointer -= pcm->runtime->period_size;
+               s->pointer_flush = false;
+               tasklet_hi_schedule(&s->period_tasklet);
+       }
+}
+
+static void pcm_period_tasklet(unsigned long data)
+{
+       struct amdtp_stream *s = (void *)data;
+       struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
 
-       for (i = 0; i < frames; ++i)
-               buffer[s->pcm_channels + i * s->data_block_quadlets] =
-                                               cpu_to_be32(0x80000000);
+       if (pcm)
+               snd_pcm_period_elapsed(pcm);
 }
 
-static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
+static int queue_packet(struct amdtp_stream *s,
+                       unsigned int header_length,
+                       unsigned int payload_length, bool skip)
+{
+       struct fw_iso_packet p = {0};
+       int err = 0;
+
+       if (IS_ERR(s->context))
+               goto end;
+
+       p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
+       p.tag = TAG_CIP;
+       p.header_length = header_length;
+       p.payload_length = (!skip) ? payload_length : 0;
+       p.skip = skip;
+       err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
+                                  s->buffer.packets[s->packet_index].offset);
+       if (err < 0) {
+               dev_err(&s->unit->device, "queueing error: %d\n", err);
+               goto end;
+       }
+
+       if (++s->packet_index >= QUEUE_LENGTH)
+               s->packet_index = 0;
+end:
+       return err;
+}
+
+static inline int queue_out_packet(struct amdtp_stream *s,
+                                  unsigned int payload_length, bool skip)
+{
+       return queue_packet(s, OUT_PACKET_HEADER_SIZE,
+                           payload_length, skip);
+}
+
+static inline int queue_in_packet(struct amdtp_stream *s)
+{
+       return queue_packet(s, IN_PACKET_HEADER_SIZE,
+                           amdtp_stream_get_max_payload(s), false);
+}
+
+static void handle_out_packet(struct amdtp_stream *s, unsigned int syt)
 {
        __be32 *buffer;
-       unsigned int index, data_blocks, syt, ptr;
+       unsigned int data_blocks, payload_length;
        struct snd_pcm_substream *pcm;
-       struct fw_iso_packet packet;
-       int err;
 
        if (s->packet_index < 0)
                return;
-       index = s->packet_index;
 
        /* this module generate empty packet for 'no data' */
-       syt = calculate_syt(s, cycle);
-       if (!(s->flags & CIP_BLOCKING))
+       if (!(s->flags & CIP_BLOCKING) || (syt != CIP_SYT_NO_INFO))
                data_blocks = calculate_data_blocks(s);
-       else if (syt != 0xffff)
-               data_blocks = s->syt_interval;
        else
                data_blocks = 0;
 
-       buffer = s->buffer.packets[index].buffer;
+       buffer = s->buffer.packets[s->packet_index].buffer;
        buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
-                               (s->data_block_quadlets << 16) |
+                               (s->data_block_quadlets << AMDTP_DBS_SHIFT) |
                                s->data_block_counter);
        buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 |
-                               (s->sfc << AMDTP_FDF_SFC_SHIFT) | syt);
+                               (s->sfc << CIP_FDF_SFC_SHIFT) | syt);
        buffer += 2;
 
        pcm = ACCESS_ONCE(s->pcm);
@@ -461,58 +605,127 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
 
        s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
 
-       packet.payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
-       packet.interrupt = IS_ALIGNED(index + 1, INTERRUPT_INTERVAL);
-       packet.skip = 0;
-       packet.tag = TAG_CIP;
-       packet.sy = 0;
-       packet.header_length = 0;
-
-       err = fw_iso_context_queue(s->context, &packet, &s->buffer.iso_buffer,
-                                  s->buffer.packets[index].offset);
-       if (err < 0) {
-               dev_err(&s->unit->device, "queueing error: %d\n", err);
+       payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
+       if (queue_out_packet(s, payload_length, false) < 0) {
                s->packet_index = -1;
-               amdtp_out_stream_pcm_abort(s);
+               amdtp_stream_pcm_abort(s);
                return;
        }
 
-       if (++index >= QUEUE_LENGTH)
-               index = 0;
-       s->packet_index = index;
+       if (pcm)
+               update_pcm_pointers(s, pcm, data_blocks);
+}
 
-       if (pcm) {
-               if (s->dual_wire)
-                       data_blocks *= 2;
-
-               ptr = s->pcm_buffer_pointer + data_blocks;
-               if (ptr >= pcm->runtime->buffer_size)
-                       ptr -= pcm->runtime->buffer_size;
-               ACCESS_ONCE(s->pcm_buffer_pointer) = ptr;
-
-               s->pcm_period_pointer += data_blocks;
-               if (s->pcm_period_pointer >= pcm->runtime->period_size) {
-                       s->pcm_period_pointer -= pcm->runtime->period_size;
-                       s->pointer_flush = false;
-                       tasklet_hi_schedule(&s->period_tasklet);
+static void handle_in_packet(struct amdtp_stream *s,
+                            unsigned int payload_quadlets,
+                            __be32 *buffer)
+{
+       u32 cip_header[2];
+       unsigned int data_blocks, data_block_quadlets, data_block_counter,
+                    dbc_interval;
+       struct snd_pcm_substream *pcm = NULL;
+       bool lost;
+
+       cip_header[0] = be32_to_cpu(buffer[0]);
+       cip_header[1] = be32_to_cpu(buffer[1]);
+
+       /*
+        * This module supports 'Two-quadlet CIP header with SYT field'.
+        * For convenience, also check FMT field is AM824 or not.
+        */
+       if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
+           ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) ||
+           ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) {
+               dev_info_ratelimited(&s->unit->device,
+                               "Invalid CIP header for AMDTP: %08X:%08X\n",
+                               cip_header[0], cip_header[1]);
+               goto end;
+       }
+
+       /* Calculate data blocks */
+       if (payload_quadlets < 3 ||
+           ((cip_header[1] & CIP_FDF_MASK) ==
+                               (AMDTP_FDF_NO_DATA << CIP_FDF_SFC_SHIFT))) {
+               data_blocks = 0;
+       } else {
+               data_block_quadlets =
+                       (cip_header[0] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT;
+               /* avoid division by zero */
+               if (data_block_quadlets == 0) {
+                       dev_info_ratelimited(&s->unit->device,
+                               "Detect invalid value in dbs field: %08X\n",
+                               cip_header[0]);
+                       goto err;
                }
+               if (s->flags & CIP_WRONG_DBS)
+                       data_block_quadlets = s->data_block_quadlets;
+
+               data_blocks = (payload_quadlets - 2) / data_block_quadlets;
        }
-}
 
-static void pcm_period_tasklet(unsigned long data)
-{
-       struct amdtp_out_stream *s = (void *)data;
-       struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+       /* Check data block counter continuity */
+       data_block_counter = cip_header[0] & AMDTP_DBC_MASK;
+       if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
+           s->data_block_counter != UINT_MAX)
+               data_block_counter = s->data_block_counter;
+
+       if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) && data_block_counter == 0) ||
+           (s->data_block_counter == UINT_MAX)) {
+               lost = false;
+       } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
+               lost = data_block_counter != s->data_block_counter;
+       } else {
+               if ((data_blocks > 0) && (s->tx_dbc_interval > 0))
+                       dbc_interval = s->tx_dbc_interval;
+               else
+                       dbc_interval = data_blocks;
+
+               lost = data_block_counter !=
+                      ((s->data_block_counter + dbc_interval) & 0xff);
+       }
+
+       if (lost) {
+               dev_info(&s->unit->device,
+                        "Detect discontinuity of CIP: %02X %02X\n",
+                        s->data_block_counter, data_block_counter);
+               goto err;
+       }
+
+       if (data_blocks > 0) {
+               buffer += 2;
+
+               pcm = ACCESS_ONCE(s->pcm);
+               if (pcm)
+                       s->transfer_samples(s, pcm, buffer, data_blocks);
+
+               if (s->midi_ports)
+                       amdtp_pull_midi(s, buffer, data_blocks);
+       }
+
+       if (s->flags & CIP_DBC_IS_END_EVENT)
+               s->data_block_counter = data_block_counter;
+       else
+               s->data_block_counter =
+                               (data_block_counter + data_blocks) & 0xff;
+end:
+       if (queue_in_packet(s) < 0)
+               goto err;
 
        if (pcm)
-               snd_pcm_period_elapsed(pcm);
+               update_pcm_pointers(s, pcm, data_blocks);
+
+       return;
+err:
+       s->packet_index = -1;
+       amdtp_stream_pcm_abort(s);
 }
 
-static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
-                               size_t header_length, void *header, void *data)
+static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
+                               size_t header_length, void *header,
+                               void *private_data)
 {
-       struct amdtp_out_stream *s = data;
-       unsigned int i, packets = header_length / 4;
+       struct amdtp_stream *s = private_data;
+       unsigned int i, syt, packets = header_length / 4;
 
        /*
         * Compute the cycle of the last queued packet.
@@ -521,43 +734,102 @@ static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
         */
        cycle += QUEUE_LENGTH - packets;
 
-       for (i = 0; i < packets; ++i)
-               queue_out_packet(s, ++cycle);
+       for (i = 0; i < packets; ++i) {
+               syt = calculate_syt(s, ++cycle);
+               handle_out_packet(s, syt);
+       }
        fw_iso_context_queue_flush(s->context);
 }
 
-static int queue_initial_skip_packets(struct amdtp_out_stream *s)
+static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
+                              size_t header_length, void *header,
+                              void *private_data)
 {
-       struct fw_iso_packet skip_packet = {
-               .skip = 1,
-       };
-       unsigned int i;
-       int err;
+       struct amdtp_stream *s = private_data;
+       unsigned int p, syt, packets, payload_quadlets;
+       __be32 *buffer, *headers = header;
 
-       for (i = 0; i < QUEUE_LENGTH; ++i) {
-               skip_packet.interrupt = IS_ALIGNED(s->packet_index + 1,
-                                                  INTERRUPT_INTERVAL);
-               err = fw_iso_context_queue(s->context, &skip_packet, NULL, 0);
-               if (err < 0)
-                       return err;
-               if (++s->packet_index >= QUEUE_LENGTH)
-                       s->packet_index = 0;
+       /* The number of packets in buffer */
+       packets = header_length / IN_PACKET_HEADER_SIZE;
+
+       for (p = 0; p < packets; p++) {
+               if (s->packet_index < 0)
+                       break;
+
+               buffer = s->buffer.packets[s->packet_index].buffer;
+
+               /* Process sync slave stream */
+               if (s->sync_slave && s->sync_slave->callbacked) {
+                       syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
+                       handle_out_packet(s->sync_slave, syt);
+               }
+
+               /* The number of quadlets in this packet */
+               payload_quadlets =
+                       (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
+               handle_in_packet(s, payload_quadlets, buffer);
        }
 
-       return 0;
+       /* Queueing error or detecting discontinuity */
+       if (s->packet_index < 0) {
+               /* Abort sync slave. */
+               if (s->sync_slave) {
+                       s->sync_slave->packet_index = -1;
+                       amdtp_stream_pcm_abort(s->sync_slave);
+               }
+               return;
+       }
+
+       /* when sync to device, flush the packets for slave stream */
+       if (s->sync_slave && s->sync_slave->callbacked)
+               fw_iso_context_queue_flush(s->sync_slave->context);
+
+       fw_iso_context_queue_flush(s->context);
+}
+
+/* processing is done by master callback */
+static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
+                                 size_t header_length, void *header,
+                                 void *private_data)
+{
+       return;
+}
+
+/* this is executed one time */
+static void amdtp_stream_first_callback(struct fw_iso_context *context,
+                                       u32 cycle, size_t header_length,
+                                       void *header, void *private_data)
+{
+       struct amdtp_stream *s = private_data;
+
+       /*
+        * For in-stream, first packet has come.
+        * For out-stream, prepared to transmit first packet
+        */
+       s->callbacked = true;
+       wake_up(&s->callback_wait);
+
+       if (s->direction == AMDTP_IN_STREAM)
+               context->callback.sc = in_stream_callback;
+       else if ((s->flags & CIP_BLOCKING) && (s->flags & CIP_SYNC_TO_DEVICE))
+               context->callback.sc = slave_stream_callback;
+       else
+               context->callback.sc = out_stream_callback;
+
+       context->callback.sc(context, cycle, header_length, header, s);
 }
 
 /**
- * amdtp_out_stream_start - start sending packets
- * @s: the AMDTP output stream to start
+ * amdtp_stream_start - start transferring packets
+ * @s: the AMDTP stream to start
  * @channel: the isochronous channel on the bus
  * @speed: firewire speed code
  *
  * The stream cannot be started until it has been configured with
- * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(),
- * and it must be started before any PCM or MIDI device can be started.
+ * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
+ * device can be started.
  */
-int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
+int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 {
        static const struct {
                unsigned int data_block;
@@ -571,47 +843,72 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
                [CIP_SFC_88200]  = {  0,   67 },
                [CIP_SFC_176400] = {  0,   67 },
        };
-       int err;
+       unsigned int header_size;
+       enum dma_data_direction dir;
+       int type, tag, err;
 
        mutex_lock(&s->mutex);
 
-       if (WARN_ON(amdtp_out_stream_running(s) ||
-                   (!s->pcm_channels && !s->midi_ports))) {
+       if (WARN_ON(amdtp_stream_running(s) ||
+                   (s->data_block_quadlets < 1))) {
                err = -EBADFD;
                goto err_unlock;
        }
 
+       if (s->direction == AMDTP_IN_STREAM &&
+           s->flags & CIP_SKIP_INIT_DBC_CHECK)
+               s->data_block_counter = UINT_MAX;
+       else
+               s->data_block_counter = 0;
        s->data_block_state = initial_state[s->sfc].data_block;
        s->syt_offset_state = initial_state[s->sfc].syt_offset;
        s->last_syt_offset = TICKS_PER_CYCLE;
 
+       /* initialize packet buffer */
+       if (s->direction == AMDTP_IN_STREAM) {
+               dir = DMA_FROM_DEVICE;
+               type = FW_ISO_CONTEXT_RECEIVE;
+               header_size = IN_PACKET_HEADER_SIZE;
+       } else {
+               dir = DMA_TO_DEVICE;
+               type = FW_ISO_CONTEXT_TRANSMIT;
+               header_size = OUT_PACKET_HEADER_SIZE;
+       }
        err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
-                                     amdtp_out_stream_get_max_payload(s),
-                                     DMA_TO_DEVICE);
+                                     amdtp_stream_get_max_payload(s), dir);
        if (err < 0)
                goto err_unlock;
 
        s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
-                                          FW_ISO_CONTEXT_TRANSMIT,
-                                          channel, speed, 0,
-                                          out_packet_callback, s);
+                                          type, channel, speed, header_size,
+                                          amdtp_stream_first_callback, s);
        if (IS_ERR(s->context)) {
                err = PTR_ERR(s->context);
                if (err == -EBUSY)
                        dev_err(&s->unit->device,
-                               "no free output stream on this controller\n");
+                               "no free stream on this controller\n");
                goto err_buffer;
        }
 
-       amdtp_out_stream_update(s);
+       amdtp_stream_update(s);
 
        s->packet_index = 0;
-       s->data_block_counter = 0;
-       err = queue_initial_skip_packets(s);
-       if (err < 0)
-               goto err_context;
+       do {
+               if (s->direction == AMDTP_IN_STREAM)
+                       err = queue_in_packet(s);
+               else
+                       err = queue_out_packet(s, 0, true);
+               if (err < 0)
+                       goto err_context;
+       } while (s->packet_index > 0);
 
-       err = fw_iso_context_start(s->context, -1, 0, 0);
+       /* NOTE: TAG1 matches CIP. This just affects in stream. */
+       tag = FW_ISO_CONTEXT_MATCH_TAG1;
+       if (s->flags & CIP_EMPTY_WITH_TAG0)
+               tag |= FW_ISO_CONTEXT_MATCH_TAG0;
+
+       s->callbacked = false;
+       err = fw_iso_context_start(s->context, -1, 0, tag);
        if (err < 0)
                goto err_context;
 
@@ -629,49 +926,49 @@ err_unlock:
 
        return err;
 }
-EXPORT_SYMBOL(amdtp_out_stream_start);
+EXPORT_SYMBOL(amdtp_stream_start);
 
 /**
- * amdtp_out_stream_pcm_pointer - get the PCM buffer position
- * @s: the AMDTP output stream that transports the PCM data
+ * amdtp_stream_pcm_pointer - get the PCM buffer position
+ * @s: the AMDTP stream that transports the PCM data
  *
  * Returns the current buffer position, in frames.
  */
-unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s)
+unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
 {
        /* this optimization is allowed to be racy */
-       if (s->pointer_flush)
+       if (s->pointer_flush && amdtp_stream_running(s))
                fw_iso_context_flush_completions(s->context);
        else
                s->pointer_flush = true;
 
        return ACCESS_ONCE(s->pcm_buffer_pointer);
 }
-EXPORT_SYMBOL(amdtp_out_stream_pcm_pointer);
+EXPORT_SYMBOL(amdtp_stream_pcm_pointer);
 
 /**
- * amdtp_out_stream_update - update the stream after a bus reset
- * @s: the AMDTP output stream
+ * amdtp_stream_update - update the stream after a bus reset
+ * @s: the AMDTP stream
  */
-void amdtp_out_stream_update(struct amdtp_out_stream *s)
+void amdtp_stream_update(struct amdtp_stream *s)
 {
        ACCESS_ONCE(s->source_node_id_field) =
                (fw_parent_device(s->unit)->card->node_id & 0x3f) << 24;
 }
-EXPORT_SYMBOL(amdtp_out_stream_update);
+EXPORT_SYMBOL(amdtp_stream_update);
 
 /**
- * amdtp_out_stream_stop - stop sending packets
- * @s: the AMDTP output stream to stop
+ * amdtp_stream_stop - stop sending packets
+ * @s: the AMDTP stream to stop
  *
  * All PCM and MIDI devices of the stream must be stopped before the stream
  * itself can be stopped.
  */
-void amdtp_out_stream_stop(struct amdtp_out_stream *s)
+void amdtp_stream_stop(struct amdtp_stream *s)
 {
        mutex_lock(&s->mutex);
 
-       if (!amdtp_out_stream_running(s)) {
+       if (!amdtp_stream_running(s)) {
                mutex_unlock(&s->mutex);
                return;
        }
@@ -682,18 +979,20 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s)
        s->context = ERR_PTR(-1);
        iso_packets_buffer_destroy(&s->buffer, s->unit);
 
+       s->callbacked = false;
+
        mutex_unlock(&s->mutex);
 }
-EXPORT_SYMBOL(amdtp_out_stream_stop);
+EXPORT_SYMBOL(amdtp_stream_stop);
 
 /**
- * amdtp_out_stream_pcm_abort - abort the running PCM device
+ * amdtp_stream_pcm_abort - abort the running PCM device
  * @s: the AMDTP stream about to be stopped
  *
  * If the isochronous stream needs to be stopped asynchronously, call this
  * function first to stop the PCM device.
  */
-void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s)
+void amdtp_stream_pcm_abort(struct amdtp_stream *s)
 {
        struct snd_pcm_substream *pcm;
 
@@ -705,4 +1004,4 @@ void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s)
                snd_pcm_stream_unlock_irq(pcm);
        }
 }
-EXPORT_SYMBOL(amdtp_out_stream_pcm_abort);
+EXPORT_SYMBOL(amdtp_stream_pcm_abort);
index 2746ecd..d8ee7b0 100644 (file)
@@ -8,7 +8,7 @@
 #include "packets-buffer.h"
 
 /**
- * enum cip_out_flags - describes details of the streaming protocol
+ * enum cip_flags - describes details of the streaming protocol
  * @CIP_NONBLOCKING: In non-blocking mode, each packet contains
  *     sample_rate/8000 samples, with rounding up or down to adjust
  *     for clock skew and left-over fractional samples.  This should
  * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
  *     SYT_INTERVAL samples, with these two types alternating so that
  *     the overall sample rate comes out right.
- * @CIP_HI_DUALWIRE: At rates above 96 kHz, pretend that the stream runs
- *     at half the actual sample rate with twice the number of channels;
- *     two samples of a channel are stored consecutively in the packet.
- *     Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size.
+ * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
+ *     generated by in packets. Defaultly this driver generates timestamp.
+ * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
+ * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
+ *     corresponds to the end of event in the packet. Out of IEC 61883.
+ * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets.
+ *     The value of data_block_quadlets is used instead of reported value.
+ * @SKIP_DBC_ZERO_CHECK: Only for in-stream.  Packets with zero in dbc is
+ *     skipped for detecting discontinuity.
+ * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first
+ *     packet is not continuous from an initial value.
+ * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
+ *     packet is wrong but the others are correct.
  */
-enum cip_out_flags {
-       CIP_NONBLOCKING = 0x00,
-       CIP_BLOCKING    = 0x01,
-       CIP_HI_DUALWIRE = 0x02,
+enum cip_flags {
+       CIP_NONBLOCKING         = 0x00,
+       CIP_BLOCKING            = 0x01,
+       CIP_SYNC_TO_DEVICE      = 0x02,
+       CIP_EMPTY_WITH_TAG0     = 0x04,
+       CIP_DBC_IS_END_EVENT    = 0x08,
+       CIP_WRONG_DBS           = 0x10,
+       CIP_SKIP_DBC_ZERO_CHECK = 0x20,
+       CIP_SKIP_INIT_DBC_CHECK = 0x40,
+       CIP_EMPTY_HAS_WRONG_DBC = 0x80,
 };
 
 /**
@@ -41,27 +56,55 @@ enum cip_sfc {
        CIP_SFC_COUNT
 };
 
+#define AMDTP_IN_PCM_FORMAT_BITS       SNDRV_PCM_FMTBIT_S32
+
 #define AMDTP_OUT_PCM_FORMAT_BITS      (SNDRV_PCM_FMTBIT_S16 | \
                                         SNDRV_PCM_FMTBIT_S32)
 
+
+/*
+ * This module supports maximum 64 PCM channels for one PCM stream
+ * This is for our convenience.
+ */
+#define AMDTP_MAX_CHANNELS_FOR_PCM     64
+
+/*
+ * AMDTP packet can include channels for MIDI conformant data.
+ * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
+ * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
+ *
+ * This module supports maximum 1 MIDI conformant data channels.
+ * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
+ */
+#define AMDTP_MAX_CHANNELS_FOR_MIDI    1
+
 struct fw_unit;
 struct fw_iso_context;
 struct snd_pcm_substream;
+struct snd_pcm_runtime;
+struct snd_rawmidi_substream;
 
-struct amdtp_out_stream {
+enum amdtp_stream_direction {
+       AMDTP_OUT_STREAM = 0,
+       AMDTP_IN_STREAM
+};
+
+struct amdtp_stream {
        struct fw_unit *unit;
-       enum cip_out_flags flags;
+       enum cip_flags flags;
+       enum amdtp_stream_direction direction;
        struct fw_iso_context *context;
        struct mutex mutex;
 
        enum cip_sfc sfc;
-       bool dual_wire;
        unsigned int data_block_quadlets;
        unsigned int pcm_channels;
        unsigned int midi_ports;
-       void (*transfer_samples)(struct amdtp_out_stream *s,
+       void (*transfer_samples)(struct amdtp_stream *s,
                                 struct snd_pcm_substream *pcm,
                                 __be32 *buffer, unsigned int frames);
+       u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM];
+       u8 midi_position;
 
        unsigned int syt_interval;
        unsigned int transfer_delay;
@@ -82,65 +125,148 @@ struct amdtp_out_stream {
        unsigned int pcm_buffer_pointer;
        unsigned int pcm_period_pointer;
        bool pointer_flush;
+
+       struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
+
+       /* quirk: fixed interval of dbc between previos/current packets. */
+       unsigned int tx_dbc_interval;
+
+       /* quirk: the first count of data blocks in an rx packet for MIDI */
+       unsigned int rx_blocks_for_midi;
+
+       bool callbacked;
+       wait_queue_head_t callback_wait;
+       struct amdtp_stream *sync_slave;
 };
 
-int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
-                         enum cip_out_flags flags);
-void amdtp_out_stream_destroy(struct amdtp_out_stream *s);
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+                     enum amdtp_stream_direction dir,
+                     enum cip_flags flags);
+void amdtp_stream_destroy(struct amdtp_stream *s);
 
-void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
-                                    unsigned int rate,
-                                    unsigned int pcm_channels,
-                                    unsigned int midi_ports);
-unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s);
+void amdtp_stream_set_parameters(struct amdtp_stream *s,
+                                unsigned int rate,
+                                unsigned int pcm_channels,
+                                unsigned int midi_ports);
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
 
-int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed);
-void amdtp_out_stream_update(struct amdtp_out_stream *s);
-void amdtp_out_stream_stop(struct amdtp_out_stream *s);
+int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
+void amdtp_stream_update(struct amdtp_stream *s);
+void amdtp_stream_stop(struct amdtp_stream *s);
 
-void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
-                                    snd_pcm_format_t format);
-void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
-unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s);
-void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);
+int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                       struct snd_pcm_runtime *runtime);
+void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
+                                snd_pcm_format_t format);
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
+unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
+void amdtp_stream_pcm_abort(struct amdtp_stream *s);
 
 extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
+extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT];
 
-static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s)
+/**
+ * amdtp_stream_running - check stream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, the stream is running.
+ */
+static inline bool amdtp_stream_running(struct amdtp_stream *s)
 {
        return !IS_ERR(s->context);
 }
 
 /**
- * amdtp_out_streaming_error - check for streaming error
- * @s: the AMDTP output stream
+ * amdtp_streaming_error - check for streaming error
+ * @s: the AMDTP stream
  *
  * If this function returns true, the stream's packet queue has stopped due to
  * an asynchronous error.
  */
-static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s)
+static inline bool amdtp_streaming_error(struct amdtp_stream *s)
 {
        return s->packet_index < 0;
 }
 
 /**
- * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device
- * @s: the AMDTP output stream
+ * amdtp_stream_pcm_running - check PCM substream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, PCM substream in the AMDTP stream is running.
+ */
+static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s)
+{
+       return !!s->pcm;
+}
+
+/**
+ * amdtp_stream_pcm_trigger - start/stop playback from a PCM device
+ * @s: the AMDTP stream
  * @pcm: the PCM device to be started, or %NULL to stop the current device
  *
  * Call this function on a running isochronous stream to enable the actual
  * transmission of PCM data.  This function should be called from the PCM
  * device's .trigger callback.
  */
-static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s,
-                                               struct snd_pcm_substream *pcm)
+static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
+                                           struct snd_pcm_substream *pcm)
 {
        ACCESS_ONCE(s->pcm) = pcm;
 }
 
+/**
+ * amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device
+ * @s: the AMDTP stream
+ * @port: index of MIDI port
+ * @midi: the MIDI device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of MIDI data.  This function should be called from the MIDI
+ * device's .trigger callback.
+ */
+static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s,
+                                            unsigned int port,
+                                            struct snd_rawmidi_substream *midi)
+{
+       if (port < s->midi_ports)
+               ACCESS_ONCE(s->midi[port]) = midi;
+}
+
 static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
 {
        return sfc & 1;
 }
 
+static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
+                                        struct amdtp_stream *master,
+                                        struct amdtp_stream *slave)
+{
+       if (sync_mode == CIP_SYNC_TO_DEVICE) {
+               master->flags |= CIP_SYNC_TO_DEVICE;
+               slave->flags |= CIP_SYNC_TO_DEVICE;
+               master->sync_slave = slave;
+       } else {
+               master->flags &= ~CIP_SYNC_TO_DEVICE;
+               slave->flags &= ~CIP_SYNC_TO_DEVICE;
+               master->sync_slave = NULL;
+       }
+
+       slave->sync_slave = NULL;
+}
+
+/**
+ * amdtp_stream_wait_callback - sleep till callbacked or timeout
+ * @s: the AMDTP stream
+ * @timeout: msec till timeout
+ *
+ * If this function return false, the AMDTP stream should be stopped.
+ */
+static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
+                                             unsigned int timeout)
+{
+       return wait_event_timeout(s->callback_wait,
+                                 s->callbacked == true,
+                                 msecs_to_jiffies(timeout)) > 0;
+}
+
 #endif
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
new file mode 100644 (file)
index 0000000..6cf470c
--- /dev/null
@@ -0,0 +1,4 @@
+snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
+                 bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \
+                 bebob_focusrite.o bebob_maudio.o bebob.o
+obj-m += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
new file mode 100644 (file)
index 0000000..fc19c99
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * bebob.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * BeBoB is 'BridgeCo enhanced Breakout Box'. This is installed to firewire
+ * devices with DM1000/DM1100/DM1500 chipset. It gives common way for host
+ * system to handle BeBoB based devices.
+ */
+
+#include "bebob.h"
+
+MODULE_DESCRIPTION("BridgeCo BeBoB driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static int index[SNDRV_CARDS]  = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS]   = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS]        = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable BeBoB sound card");
+
+static DEFINE_MUTEX(devices_mutex);
+static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
+
+/* Offsets from information register. */
+#define INFO_OFFSET_GUID               0x10
+#define INFO_OFFSET_HW_MODEL_ID                0x18
+#define INFO_OFFSET_HW_MODEL_REVISION  0x1c
+
+#define VEN_EDIROL     0x000040ab
+#define VEN_PRESONUS   0x00000a92
+#define VEN_BRIDGECO   0x000007f5
+#define VEN_MACKIE     0x0000000f
+#define VEN_STANTON    0x00001260
+#define VEN_TASCAM     0x0000022e
+#define VEN_BEHRINGER  0x00001564
+#define VEN_APOGEE     0x000003db
+#define VEN_ESI                0x00000f1b
+#define VEN_ACOUSTIC   0x00000002
+#define VEN_CME                0x0000000a
+#define VEN_PHONIC     0x00001496
+#define VEN_LYNX       0x000019e5
+#define VEN_ICON       0x00001a9e
+#define VEN_PRISMSOUND 0x00001198
+#define VEN_TERRATEC   0x00000aac
+#define VEN_YAMAHA     0x0000a0de
+#define VEN_FOCUSRITE  0x0000130e
+#define VEN_MAUDIO1    0x00000d6c
+#define VEN_MAUDIO2    0x000007f5
+
+#define MODEL_FOCUSRITE_SAFFIRE_BOTH   0x00000000
+#define MODEL_MAUDIO_AUDIOPHILE_BOTH   0x00010060
+#define MODEL_MAUDIO_FW1814            0x00010071
+#define MODEL_MAUDIO_PROJECTMIX                0x00010091
+
+static int
+name_device(struct snd_bebob *bebob, unsigned int vendor_id)
+{
+       struct fw_device *fw_dev = fw_parent_device(bebob->unit);
+       char vendor[24] = {0};
+       char model[32] = {0};
+       u32 hw_id;
+       u32 data[2] = {0};
+       u32 revision;
+       int err;
+
+       /* get vendor name from root directory */
+       err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR,
+                           vendor, sizeof(vendor));
+       if (err < 0)
+               goto end;
+
+       /* get model name from unit directory */
+       err = fw_csr_string(bebob->unit->directory, CSR_MODEL,
+                           model, sizeof(model));
+       if (err < 0)
+               goto end;
+
+       /* get hardware id */
+       err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_ID,
+                                 &hw_id);
+       if (err < 0)
+               goto end;
+
+       /* get hardware revision */
+       err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_REVISION,
+                                 &revision);
+       if (err < 0)
+               goto end;
+
+       /* get GUID */
+       err = snd_bebob_read_block(bebob->unit, INFO_OFFSET_GUID,
+                                  data, sizeof(data));
+       if (err < 0)
+               goto end;
+
+       strcpy(bebob->card->driver, "BeBoB");
+       strcpy(bebob->card->shortname, model);
+       strcpy(bebob->card->mixername, model);
+       snprintf(bebob->card->longname, sizeof(bebob->card->longname),
+                "%s %s (id:%d, rev:%d), GUID %08x%08x at %s, S%d",
+                vendor, model, hw_id, revision,
+                data[0], data[1], dev_name(&bebob->unit->device),
+                100 << fw_dev->max_speed);
+end:
+       return err;
+}
+
+static void
+bebob_card_free(struct snd_card *card)
+{
+       struct snd_bebob *bebob = card->private_data;
+
+       if (bebob->card_index >= 0) {
+               mutex_lock(&devices_mutex);
+               clear_bit(bebob->card_index, devices_used);
+               mutex_unlock(&devices_mutex);
+       }
+
+       mutex_destroy(&bebob->mutex);
+}
+
+static const struct snd_bebob_spec *
+get_saffire_spec(struct fw_unit *unit)
+{
+       char name[24] = {0};
+
+       if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0)
+               return NULL;
+
+       if (strcmp(name, "SaffireLE") == 0)
+               return &saffire_le_spec;
+       else
+               return &saffire_spec;
+}
+
+static bool
+check_audiophile_booted(struct fw_unit *unit)
+{
+       char name[24] = {0};
+
+       if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0)
+               return false;
+
+       return strncmp(name, "FW Audiophile Bootloader", 15) != 0;
+}
+
+static int
+bebob_probe(struct fw_unit *unit,
+           const struct ieee1394_device_id *entry)
+{
+       struct snd_card *card;
+       struct snd_bebob *bebob;
+       const struct snd_bebob_spec *spec;
+       unsigned int card_index;
+       int err;
+
+       mutex_lock(&devices_mutex);
+
+       for (card_index = 0; card_index < SNDRV_CARDS; card_index++) {
+               if (!test_bit(card_index, devices_used) && enable[card_index])
+                       break;
+       }
+       if (card_index >= SNDRV_CARDS) {
+               err = -ENOENT;
+               goto end;
+       }
+
+       if ((entry->vendor_id == VEN_FOCUSRITE) &&
+           (entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH))
+               spec = get_saffire_spec(unit);
+       else if ((entry->vendor_id == VEN_MAUDIO1) &&
+                (entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH) &&
+                !check_audiophile_booted(unit))
+               spec = NULL;
+       else
+               spec = (const struct snd_bebob_spec *)entry->driver_data;
+
+       if (spec == NULL) {
+               if ((entry->vendor_id == VEN_MAUDIO1) ||
+                   (entry->vendor_id == VEN_MAUDIO2))
+                       err = snd_bebob_maudio_load_firmware(unit);
+               else
+                       err = -ENOSYS;
+               goto end;
+       }
+
+       err = snd_card_new(&unit->device, index[card_index], id[card_index],
+                          THIS_MODULE, sizeof(struct snd_bebob), &card);
+       if (err < 0)
+               goto end;
+       bebob = card->private_data;
+       bebob->card_index = card_index;
+       set_bit(card_index, devices_used);
+       card->private_free = bebob_card_free;
+
+       bebob->card = card;
+       bebob->unit = unit;
+       bebob->spec = spec;
+       mutex_init(&bebob->mutex);
+       spin_lock_init(&bebob->lock);
+       init_waitqueue_head(&bebob->hwdep_wait);
+
+       err = name_device(bebob, entry->vendor_id);
+       if (err < 0)
+               goto error;
+
+       if ((entry->vendor_id == VEN_MAUDIO1) &&
+           (entry->model_id == MODEL_MAUDIO_FW1814))
+               err = snd_bebob_maudio_special_discover(bebob, true);
+       else if ((entry->vendor_id == VEN_MAUDIO1) &&
+                (entry->model_id == MODEL_MAUDIO_PROJECTMIX))
+               err = snd_bebob_maudio_special_discover(bebob, false);
+       else
+               err = snd_bebob_stream_discover(bebob);
+       if (err < 0)
+               goto error;
+
+       snd_bebob_proc_init(bebob);
+
+       if ((bebob->midi_input_ports > 0) ||
+           (bebob->midi_output_ports > 0)) {
+               err = snd_bebob_create_midi_devices(bebob);
+               if (err < 0)
+                       goto error;
+       }
+
+       err = snd_bebob_create_pcm_devices(bebob);
+       if (err < 0)
+               goto error;
+
+       err = snd_bebob_create_hwdep_device(bebob);
+       if (err < 0)
+               goto error;
+
+       err = snd_bebob_stream_init_duplex(bebob);
+       if (err < 0)
+               goto error;
+
+       if (!bebob->maudio_special_quirk) {
+               err = snd_card_register(card);
+               if (err < 0) {
+                       snd_bebob_stream_destroy_duplex(bebob);
+                       goto error;
+               }
+       } else {
+               /*
+                * This is a workaround. This bus reset seems to have an effect
+                * to make devices correctly handling transactions. Without
+                * this, the devices have gap_count mismatch. This causes much
+                * failure of transaction.
+                *
+                * Just after registration, user-land application receive
+                * signals from dbus and starts I/Os. To avoid I/Os till the
+                * future bus reset, registration is done in next update().
+                */
+               bebob->deferred_registration = true;
+               fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card,
+                                     false, true);
+       }
+
+       dev_set_drvdata(&unit->device, bebob);
+end:
+       mutex_unlock(&devices_mutex);
+       return err;
+error:
+       mutex_unlock(&devices_mutex);
+       snd_card_free(card);
+       return err;
+}
+
+static void
+bebob_update(struct fw_unit *unit)
+{
+       struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
+
+       if (bebob == NULL)
+               return;
+
+       fcp_bus_reset(bebob->unit);
+       snd_bebob_stream_update_duplex(bebob);
+
+       if (bebob->deferred_registration) {
+               if (snd_card_register(bebob->card) < 0) {
+                       snd_bebob_stream_destroy_duplex(bebob);
+                       snd_card_free(bebob->card);
+               }
+               bebob->deferred_registration = false;
+       }
+}
+
+static void bebob_remove(struct fw_unit *unit)
+{
+       struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
+
+       if (bebob == NULL)
+               return;
+
+       kfree(bebob->maudio_special_quirk);
+
+       snd_bebob_stream_destroy_duplex(bebob);
+       snd_card_disconnect(bebob->card);
+       snd_card_free_when_closed(bebob->card);
+}
+
+static struct snd_bebob_rate_spec normal_rate_spec = {
+       .get    = &snd_bebob_stream_get_rate,
+       .set    = &snd_bebob_stream_set_rate
+};
+static const struct snd_bebob_spec spec_normal = {
+       .clock  = NULL,
+       .rate   = &normal_rate_spec,
+       .meter  = NULL
+};
+
+static const struct ieee1394_device_id bebob_id_table[] = {
+       /* Edirol, FA-66 */
+       SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010049, &spec_normal),
+       /* Edirol, FA-101 */
+       SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010048, &spec_normal),
+       /* Presonus, FIREBOX */
+       SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010000, &spec_normal),
+       /* PreSonus, FIREPOD/FP10 */
+       SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010066, &spec_normal),
+       /* PreSonus, Inspire1394 */
+       SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010001, &spec_normal),
+       /* BridgeCo, RDAudio1 */
+       SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010048, &spec_normal),
+       /* BridgeCo, Audio5 */
+       SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal),
+       /* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
+       SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal),
+       /* Mackie, d.2 (Firewire Option) */
+       SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal),
+       /* Stanton, ScratchAmp */
+       SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal),
+       /* Tascam, IF-FW DM */
+       SND_BEBOB_DEV_ENTRY(VEN_TASCAM, 0x00010067, &spec_normal),
+       /* Behringer, XENIX UFX 1204 */
+       SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001204, &spec_normal),
+       /* Behringer, XENIX UFX 1604 */
+       SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604, &spec_normal),
+       /* Behringer, Digital Mixer X32 series (X-UF Card) */
+       SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006, &spec_normal),
+       /* Apogee Electronics, Rosetta 200/400 (X-FireWire card) */
+       /* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */
+       SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048, &spec_normal),
+       /* Apogee Electronics, Ensemble */
+       SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00001eee, &spec_normal),
+       /* ESI, Quatafire610 */
+       SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064, &spec_normal),
+       /* AcousticReality, eARMasterOne */
+       SND_BEBOB_DEV_ENTRY(VEN_ACOUSTIC, 0x00000002, &spec_normal),
+       /* CME, MatrixKFW */
+       SND_BEBOB_DEV_ENTRY(VEN_CME, 0x00030000, &spec_normal),
+       /* Phonic, Helix Board 12 MkII */
+       SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00050000, &spec_normal),
+       /* Phonic, Helix Board 18 MkII */
+       SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00060000, &spec_normal),
+       /* Phonic, Helix Board 24 MkII */
+       SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00070000, &spec_normal),
+       /* Phonic, Helix Board 12 Universal/18 Universal/24 Universal */
+       SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00000000, &spec_normal),
+       /* Lynx, Aurora 8/16 (LT-FW) */
+       SND_BEBOB_DEV_ENTRY(VEN_LYNX, 0x00000001, &spec_normal),
+       /* ICON, FireXon */
+       SND_BEBOB_DEV_ENTRY(VEN_ICON, 0x00000001, &spec_normal),
+       /* PrismSound, Orpheus */
+       SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x00010048, &spec_normal),
+       /* PrismSound, ADA-8XR */
+       SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x0000ada8, &spec_normal),
+       /* TerraTec Electronic GmbH, PHASE 88 Rack FW */
+       SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000003, &phase88_rack_spec),
+       /* TerraTec Electronic GmbH, PHASE 24 FW */
+       SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000004, &phase24_series_spec),
+       /* TerraTec Electronic GmbH, Phase X24 FW */
+       SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000007, &phase24_series_spec),
+       /* TerraTec Electronic GmbH, EWS MIC2/MIC8 */
+       SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000005, &spec_normal),
+       /* Terratec Electronic GmbH, Aureon 7.1 Firewire */
+       SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000002, &spec_normal),
+       /* Yamaha, GO44 */
+       SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000b, &yamaha_go_spec),
+       /* YAMAHA, GO46 */
+       SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000c, &yamaha_go_spec),
+       /* Focusrite, SaffirePro 26 I/O */
+       SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000003, &saffirepro_26_spec),
+       /* Focusrite, SaffirePro 10 I/O */
+       SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000006, &saffirepro_10_spec),
+       /* Focusrite, Saffire(no label and LE) */
+       SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, MODEL_FOCUSRITE_SAFFIRE_BOTH,
+                           &saffire_spec),
+       /* M-Audio, Firewire 410 */
+       SND_BEBOB_DEV_ENTRY(VEN_MAUDIO2, 0x00010058, NULL),     /* bootloader */
+       SND_BEBOB_DEV_ENTRY(VEN_MAUDIO2, 0x00010046, &maudio_fw410_spec),
+       /* M-Audio, Firewire Audiophile */
+       SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_AUDIOPHILE_BOTH,
+                           &maudio_audiophile_spec),
+       /* M-Audio, Firewire Solo */
+       SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010062, &maudio_solo_spec),
+       /* M-Audio, Ozonic */
+       SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x0000000a, &maudio_ozonic_spec),
+       /* M-Audio NRV10 */
+       SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010081, &maudio_nrv10_spec),
+       /* M-Audio, ProFireLightbridge */
+       SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x000100a1, &spec_normal),
+       /* Firewire 1814 */
+       SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010070, NULL),     /* bootloader */
+       SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_FW1814,
+                           &maudio_special_spec),
+       /* M-Audio ProjectMix */
+       SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_PROJECTMIX,
+                           &maudio_special_spec),
+       /* IDs are unknown but able to be supported */
+       /*  Apogee, Mini-ME Firewire */
+       /*  Apogee, Mini-DAC Firewire */
+       /*  Behringer, F-Control Audio 1616 */
+       /*  Behringer, F-Control Audio 610 */
+       /*  Cakawalk, Sonar Power Studio 66 */
+       /*  CME, UF400e */
+       /*  ESI, Quotafire XL */
+       /*  Infrasonic, DewX */
+       /*  Infrasonic, Windy6 */
+       /*  Mackie, Digital X Bus x.200 */
+       /*  Mackie, Digital X Bus x.400 */
+       /*  Phonic, HB 12 */
+       /*  Phonic, HB 24 */
+       /*  Phonic, HB 18 */
+       /*  Phonic, FireFly 202 */
+       /*  Phonic, FireFly 302 */
+       /*  Rolf Spuler, Firewire Guitar */
+       {}
+};
+MODULE_DEVICE_TABLE(ieee1394, bebob_id_table);
+
+static struct fw_driver bebob_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "snd-bebob",
+               .bus    = &fw_bus_type,
+       },
+       .probe    = bebob_probe,
+       .update   = bebob_update,
+       .remove   = bebob_remove,
+       .id_table = bebob_id_table,
+};
+
+static int __init
+snd_bebob_init(void)
+{
+       return driver_register(&bebob_driver.driver);
+}
+
+static void __exit
+snd_bebob_exit(void)
+{
+       driver_unregister(&bebob_driver.driver);
+}
+
+module_init(snd_bebob_init);
+module_exit(snd_bebob_exit);
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
new file mode 100644 (file)
index 0000000..d1c93a1
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * bebob.h - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_BEBOB_H_INCLUDED
+#define SOUND_BEBOB_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../lib.h"
+#include "../fcp.h"
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp.h"
+#include "../cmp.h"
+
+/* basic register addresses on DM1000/DM1100/DM1500 */
+#define BEBOB_ADDR_REG_INFO    0xffffc8020000ULL
+#define BEBOB_ADDR_REG_REQ     0xffffc8021000ULL
+
+struct snd_bebob;
+
+#define SND_BEBOB_STRM_FMT_ENTRIES     7
+struct snd_bebob_stream_formation {
+       unsigned int pcm;
+       unsigned int midi;
+};
+/* this is a lookup table for index of stream formations */
+extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES];
+
+/* device specific operations */
+#define SND_BEBOB_CLOCK_INTERNAL       "Internal"
+struct snd_bebob_clock_spec {
+       unsigned int num;
+       char *const *labels;
+       int (*get)(struct snd_bebob *bebob, unsigned int *id);
+};
+struct snd_bebob_rate_spec {
+       int (*get)(struct snd_bebob *bebob, unsigned int *rate);
+       int (*set)(struct snd_bebob *bebob, unsigned int rate);
+};
+struct snd_bebob_meter_spec {
+       unsigned int num;
+       char *const *labels;
+       int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
+};
+struct snd_bebob_spec {
+       struct snd_bebob_clock_spec *clock;
+       struct snd_bebob_rate_spec *rate;
+       struct snd_bebob_meter_spec *meter;
+};
+
+struct snd_bebob {
+       struct snd_card *card;
+       struct fw_unit *unit;
+       int card_index;
+
+       struct mutex mutex;
+       spinlock_t lock;
+
+       const struct snd_bebob_spec *spec;
+
+       unsigned int midi_input_ports;
+       unsigned int midi_output_ports;
+
+       /* for bus reset quirk */
+       struct completion bus_reset;
+       bool connected;
+
+       struct amdtp_stream *master;
+       struct amdtp_stream tx_stream;
+       struct amdtp_stream rx_stream;
+       struct cmp_connection out_conn;
+       struct cmp_connection in_conn;
+       atomic_t capture_substreams;
+       atomic_t playback_substreams;
+
+       struct snd_bebob_stream_formation
+               tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
+       struct snd_bebob_stream_formation
+               rx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
+
+       int sync_input_plug;
+
+       /* for uapi */
+       int dev_lock_count;
+       bool dev_lock_changed;
+       wait_queue_head_t hwdep_wait;
+
+       /* for M-Audio special devices */
+       void *maudio_special_quirk;
+       bool deferred_registration;
+};
+
+static inline int
+snd_bebob_read_block(struct fw_unit *unit, u64 addr, void *buf, int size)
+{
+       return snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
+                                 BEBOB_ADDR_REG_INFO + addr,
+                                 buf, size, 0);
+}
+
+static inline int
+snd_bebob_read_quad(struct fw_unit *unit, u64 addr, u32 *buf)
+{
+       return snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+                                 BEBOB_ADDR_REG_INFO + addr,
+                                 (void *)buf, sizeof(u32), 0);
+}
+
+/* AV/C Audio Subunit Specification 1.0 (Oct 2000, 1394TA) */
+int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
+                          unsigned int fb_id, unsigned int num);
+int avc_audio_get_selector(struct fw_unit *unit, unsigned  int subunit_id,
+                          unsigned int fb_id, unsigned int *num);
+
+/*
+ * AVC command extensions, AV/C Unit and Subunit, Revision 17
+ * (Nov 2003, BridgeCo)
+ */
+#define        AVC_BRIDGECO_ADDR_BYTES 6
+enum avc_bridgeco_plug_dir {
+       AVC_BRIDGECO_PLUG_DIR_IN        = 0x00,
+       AVC_BRIDGECO_PLUG_DIR_OUT       = 0x01
+};
+enum avc_bridgeco_plug_mode {
+       AVC_BRIDGECO_PLUG_MODE_UNIT             = 0x00,
+       AVC_BRIDGECO_PLUG_MODE_SUBUNIT          = 0x01,
+       AVC_BRIDGECO_PLUG_MODE_FUNCTION_BLOCK   = 0x02
+};
+enum avc_bridgeco_plug_unit {
+       AVC_BRIDGECO_PLUG_UNIT_ISOC     = 0x00,
+       AVC_BRIDGECO_PLUG_UNIT_EXT      = 0x01,
+       AVC_BRIDGECO_PLUG_UNIT_ASYNC    = 0x02
+};
+enum avc_bridgeco_plug_type {
+       AVC_BRIDGECO_PLUG_TYPE_ISOC     = 0x00,
+       AVC_BRIDGECO_PLUG_TYPE_ASYNC    = 0x01,
+       AVC_BRIDGECO_PLUG_TYPE_MIDI     = 0x02,
+       AVC_BRIDGECO_PLUG_TYPE_SYNC     = 0x03,
+       AVC_BRIDGECO_PLUG_TYPE_ANA      = 0x04,
+       AVC_BRIDGECO_PLUG_TYPE_DIG      = 0x05
+};
+static inline void
+avc_bridgeco_fill_unit_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
+                           enum avc_bridgeco_plug_dir dir,
+                           enum avc_bridgeco_plug_unit unit,
+                           unsigned int pid)
+{
+       buf[0] = 0xff;  /* Unit */
+       buf[1] = dir;
+       buf[2] = AVC_BRIDGECO_PLUG_MODE_UNIT;
+       buf[3] = unit;
+       buf[4] = 0xff & pid;
+       buf[5] = 0xff;  /* reserved */
+}
+static inline void
+avc_bridgeco_fill_msu_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
+                          enum avc_bridgeco_plug_dir dir,
+                          unsigned int pid)
+{
+       buf[0] = 0x60;  /* Music subunit */
+       buf[1] = dir;
+       buf[2] = AVC_BRIDGECO_PLUG_MODE_SUBUNIT;
+       buf[3] = 0xff & pid;
+       buf[4] = 0xff;  /* reserved */
+       buf[5] = 0xff;  /* reserved */
+}
+int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
+                                u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+                                u8 *buf, unsigned int len);
+int avc_bridgeco_get_plug_type(struct fw_unit *unit,
+                              u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+                              enum avc_bridgeco_plug_type *type);
+int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
+                                      u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+                                      unsigned int id, u8 *type);
+int avc_bridgeco_get_plug_input(struct fw_unit *unit,
+                               u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+                               u8 input[7]);
+int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
+                                  u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
+                                  unsigned int *len, unsigned int eid);
+
+/* for AMDTP streaming */
+int snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *rate);
+int snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate);
+int snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob,
+                                         bool *internal);
+int snd_bebob_stream_discover(struct snd_bebob *bebob);
+int snd_bebob_stream_map(struct snd_bebob *bebob,
+                        struct amdtp_stream *stream);
+int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
+int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
+void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
+void snd_bebob_stream_update_duplex(struct snd_bebob *bebob);
+void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
+
+void snd_bebob_stream_lock_changed(struct snd_bebob *bebob);
+int snd_bebob_stream_lock_try(struct snd_bebob *bebob);
+void snd_bebob_stream_lock_release(struct snd_bebob *bebob);
+
+void snd_bebob_proc_init(struct snd_bebob *bebob);
+
+int snd_bebob_create_midi_devices(struct snd_bebob *bebob);
+
+int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
+
+int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
+
+/* model specific operations */
+extern struct snd_bebob_spec phase88_rack_spec;
+extern struct snd_bebob_spec phase24_series_spec;
+extern struct snd_bebob_spec yamaha_go_spec;
+extern struct snd_bebob_spec saffirepro_26_spec;
+extern struct snd_bebob_spec saffirepro_10_spec;
+extern struct snd_bebob_spec saffire_le_spec;
+extern struct snd_bebob_spec saffire_spec;
+extern struct snd_bebob_spec maudio_fw410_spec;
+extern struct snd_bebob_spec maudio_audiophile_spec;
+extern struct snd_bebob_spec maudio_solo_spec;
+extern struct snd_bebob_spec maudio_ozonic_spec;
+extern struct snd_bebob_spec maudio_nrv10_spec;
+extern struct snd_bebob_spec maudio_special_spec;
+int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
+int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
+
+#define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
+{ \
+       .match_flags    = IEEE1394_MATCH_VENDOR_ID | \
+                         IEEE1394_MATCH_MODEL_ID, \
+       .vendor_id      = vendor, \
+       .model_id       = model, \
+       .driver_data    = (kernel_ulong_t)data \
+}
+
+#endif
diff --git a/sound/firewire/bebob/bebob_command.c b/sound/firewire/bebob/bebob_command.c
new file mode 100644 (file)
index 0000000..9402cc1
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * bebob_command.c - driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
+                          unsigned int fb_id, unsigned int num)
+{
+       u8 *buf;
+       int err;
+
+       buf = kzalloc(12, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       buf[0]  = 0x00;         /* AV/C CONTROL */
+       buf[1]  = 0x08 | (0x07 & subunit_id);   /* AUDIO SUBUNIT ID */
+       buf[2]  = 0xb8;         /* FUNCTION BLOCK  */
+       buf[3]  = 0x80;         /* type is 'selector'*/
+       buf[4]  = 0xff & fb_id; /* function block id */
+       buf[5]  = 0x10;         /* control attribute is CURRENT */
+       buf[6]  = 0x02;         /* selector length is 2 */
+       buf[7]  = 0xff & num;   /* input function block plug number */
+       buf[8]  = 0x01;         /* control selector is SELECTOR_CONTROL */
+
+       err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+                                 BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+                                 BIT(6) | BIT(7) | BIT(8));
+       if (err > 0 && err < 9)
+               err = -EIO;
+       else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a) /* REJECTED */
+               err = -EINVAL;
+       else if (err > 0)
+               err = 0;
+
+       kfree(buf);
+       return err;
+}
+
+int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
+                          unsigned int fb_id, unsigned int *num)
+{
+       u8 *buf;
+       int err;
+
+       buf = kzalloc(12, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       buf[0]  = 0x01;         /* AV/C STATUS */
+       buf[1]  = 0x08 | (0x07 & subunit_id);   /* AUDIO SUBUNIT ID */
+       buf[2]  = 0xb8;         /* FUNCTION BLOCK */
+       buf[3]  = 0x80;         /* type is 'selector'*/
+       buf[4]  = 0xff & fb_id; /* function block id */
+       buf[5]  = 0x10;         /* control attribute is CURRENT */
+       buf[6]  = 0x02;         /* selector length is 2 */
+       buf[7]  = 0xff;         /* input function block plug number */
+       buf[8]  = 0x01;         /* control selector is SELECTOR_CONTROL */
+
+       err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+                                 BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+                                 BIT(6) | BIT(8));
+       if (err > 0 && err < 9)
+               err = -EIO;
+       else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a) /* REJECTED */
+               err = -EINVAL;
+       else if (buf[0] == 0x0b) /* IN TRANSITION */
+               err = -EAGAIN;
+       if (err < 0)
+               goto end;
+
+       *num = buf[7];
+       err = 0;
+end:
+       kfree(buf);
+       return err;
+}
+
+static inline void
+avc_bridgeco_fill_extension_addr(u8 *buf, u8 *addr)
+{
+       buf[1] = addr[0];
+       memcpy(buf + 4, addr + 1, 5);
+}
+
+static inline void
+avc_bridgeco_fill_plug_info_extension_command(u8 *buf, u8 *addr,
+                                             unsigned int itype)
+{
+       buf[0] = 0x01;  /* AV/C STATUS */
+       buf[2] = 0x02;  /* AV/C GENERAL PLUG INFO */
+       buf[3] = 0xc0;  /* BridgeCo extension */
+       avc_bridgeco_fill_extension_addr(buf, addr);
+       buf[9] = itype; /* info type */
+}
+
+int avc_bridgeco_get_plug_type(struct fw_unit *unit,
+                              u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+                              enum avc_bridgeco_plug_type *type)
+{
+       u8 *buf;
+       int err;
+
+       buf = kzalloc(12, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       /* Info type is 'plug type'. */
+       avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x00);
+
+       err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+                                 BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+                                 BIT(6) | BIT(7) | BIT(9));
+       if ((err >= 0) && (err < 8))
+               err = -EIO;
+       else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a) /* REJECTED */
+               err = -EINVAL;
+       else if (buf[0] == 0x0b) /* IN TRANSITION */
+               err = -EAGAIN;
+       if (err < 0)
+               goto end;
+
+       *type = buf[10];
+       err = 0;
+end:
+       kfree(buf);
+       return err;
+}
+
+int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
+                                u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+                                u8 *buf, unsigned int len)
+{
+       int err;
+
+       /* Info type is 'channel position'. */
+       avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x03);
+
+       err = fcp_avc_transaction(unit, buf, 12, buf, 256,
+                                 BIT(1) | BIT(2) | BIT(3) | BIT(4) |
+                                 BIT(5) | BIT(6) | BIT(7) | BIT(9));
+       if ((err >= 0) && (err < 8))
+               err = -EIO;
+       else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a) /* REJECTED */
+               err = -EINVAL;
+       else if (buf[0] == 0x0b) /* IN TRANSITION */
+               err = -EAGAIN;
+       if (err < 0)
+               goto end;
+
+       /* Pick up specific data. */
+       memmove(buf, buf + 10, err - 10);
+       err = 0;
+end:
+       return err;
+}
+
+int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
+                                      u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+                                      unsigned int id, u8 *type)
+{
+       u8 *buf;
+       int err;
+
+       /* section info includes charactors but this module don't need it */
+       buf = kzalloc(12, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       /* Info type is 'section info'. */
+       avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x07);
+       buf[10] = 0xff & ++id;  /* section id */
+
+       err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+                                 BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+                                 BIT(6) | BIT(7) | BIT(9) | BIT(10));
+       if ((err >= 0) && (err < 8))
+               err = -EIO;
+       else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a) /* REJECTED */
+               err = -EINVAL;
+       else if (buf[0] == 0x0b) /* IN TRANSITION */
+               err = -EAGAIN;
+       if (err < 0)
+               goto end;
+
+       *type = buf[11];
+       err = 0;
+end:
+       kfree(buf);
+       return err;
+}
+
+int avc_bridgeco_get_plug_input(struct fw_unit *unit,
+                               u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 input[7])
+{
+       int err;
+       u8 *buf;
+
+       buf = kzalloc(18, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       /* Info type is 'plug input'. */
+       avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x05);
+
+       err = fcp_avc_transaction(unit, buf, 16, buf, 16,
+                                 BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+                                 BIT(6) | BIT(7));
+       if ((err >= 0) && (err < 8))
+               err = -EIO;
+       else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a) /* REJECTED */
+               err = -EINVAL;
+       else if (buf[0] == 0x0b) /* IN TRANSITION */
+               err = -EAGAIN;
+       if (err < 0)
+               goto end;
+
+       memcpy(input, buf + 10, 5);
+       err = 0;
+end:
+       kfree(buf);
+       return err;
+}
+
+int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
+                                  u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
+                                  unsigned int *len, unsigned int eid)
+{
+       int err;
+
+       /* check given buffer */
+       if ((buf == NULL) || (*len < 12)) {
+               err = -EINVAL;
+               goto end;
+       }
+
+       buf[0] = 0x01;  /* AV/C STATUS */
+       buf[2] = 0x2f;  /* AV/C STREAM FORMAT SUPPORT */
+       buf[3] = 0xc1;  /* Bridgeco extension - List Request */
+       avc_bridgeco_fill_extension_addr(buf, addr);
+       buf[10] = 0xff & eid;   /* Entry ID */
+
+       err = fcp_avc_transaction(unit, buf, 12, buf, *len,
+                                 BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+                                 BIT(6) | BIT(7) | BIT(10));
+       if ((err >= 0) && (err < 12))
+               err = -EIO;
+       else if (buf[0] == 0x08)        /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a)        /* REJECTED */
+               err = -EINVAL;
+       else if (buf[0] == 0x0b)        /* IN TRANSITION */
+               err = -EAGAIN;
+       else if (buf[10] != eid)
+               err = -EIO;
+       if (err < 0)
+               goto end;
+
+       /* Pick up 'stream format info'. */
+       memmove(buf, buf + 11, err - 11);
+       *len = err - 11;
+       err = 0;
+end:
+       return err;
+}
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
new file mode 100644 (file)
index 0000000..45a0eed
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * bebob_focusrite.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+#define ANA_IN "Analog In"
+#define DIG_IN "Digital In"
+#define ANA_OUT        "Analog Out"
+#define DIG_OUT        "Digital Out"
+#define STM_IN "Stream In"
+
+#define SAFFIRE_ADDRESS_BASE                   0x000100000000ULL
+
+#define SAFFIRE_OFFSET_CLOCK_SOURCE            0x00f8
+#define SAFFIREPRO_OFFSET_CLOCK_SOURCE         0x0174
+
+/* whether sync to external device or not */
+#define SAFFIRE_OFFSET_CLOCK_SYNC_EXT          0x013c
+#define SAFFIRE_LE_OFFSET_CLOCK_SYNC_EXT       0x0432
+#define SAFFIREPRO_OFFSET_CLOCK_SYNC_EXT       0x0164
+
+#define SAFFIRE_CLOCK_SOURCE_INTERNAL          0
+#define SAFFIRE_CLOCK_SOURCE_SPDIF             1
+
+/* '1' is absent, why... */
+#define SAFFIREPRO_CLOCK_SOURCE_INTERNAL       0
+#define SAFFIREPRO_CLOCK_SOURCE_SPDIF          2
+#define SAFFIREPRO_CLOCK_SOURCE_ADAT1          3
+#define SAFFIREPRO_CLOCK_SOURCE_ADAT2          4
+#define SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK      5
+
+/* S/PDIF, ADAT1, ADAT2 is enabled or not. three quadlets */
+#define SAFFIREPRO_ENABLE_DIG_IFACES           0x01a4
+
+/* saffirepro has its own parameter for sampling frequency */
+#define SAFFIREPRO_RATE_NOREBOOT               0x01cc
+/* index is the value for this register */
+static const unsigned int rates[] = {
+       [0] = 0,
+       [1] = 44100,
+       [2] = 48000,
+       [3] = 88200,
+       [4] = 96000,
+       [5] = 176400,
+       [6] = 192000
+};
+
+/* saffire(no label)/saffire LE has metering */
+#define SAFFIRE_OFFSET_METER                   0x0100
+#define SAFFIRE_LE_OFFSET_METER                        0x0168
+
+static inline int
+saffire_read_block(struct snd_bebob *bebob, u64 offset,
+                  u32 *buf, unsigned int size)
+{
+       unsigned int i;
+       int err;
+       __be32 *tmp = (__be32 *)buf;
+
+       err =  snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
+                                 SAFFIRE_ADDRESS_BASE + offset,
+                                 tmp, size, 0);
+       if (err < 0)
+               goto end;
+
+       for (i = 0; i < size / sizeof(u32); i++)
+               buf[i] = be32_to_cpu(tmp[i]);
+end:
+       return err;
+}
+
+static inline int
+saffire_read_quad(struct snd_bebob *bebob, u64 offset, u32 *value)
+{
+       int err;
+       __be32 tmp;
+
+       err = snd_fw_transaction(bebob->unit, TCODE_READ_QUADLET_REQUEST,
+                                SAFFIRE_ADDRESS_BASE + offset,
+                                &tmp, sizeof(__be32), 0);
+       if (err < 0)
+               goto end;
+
+       *value = be32_to_cpu(tmp);
+end:
+       return err;
+}
+
+static inline int
+saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value)
+{
+       __be32 data = cpu_to_be32(value);
+
+       return snd_fw_transaction(bebob->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                 SAFFIRE_ADDRESS_BASE + offset,
+                                 &data, sizeof(__be32), 0);
+}
+
+static char *const saffirepro_26_clk_src_labels[] = {
+       SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "ADAT1", "ADAT2", "Word Clock"
+};
+
+static char *const saffirepro_10_clk_src_labels[] = {
+       SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "Word Clock"
+};
+static int
+saffirepro_both_clk_freq_get(struct snd_bebob *bebob, unsigned int *rate)
+{
+       u32 id;
+       int err;
+
+       err = saffire_read_quad(bebob, SAFFIREPRO_RATE_NOREBOOT, &id);
+       if (err < 0)
+               goto end;
+       if (id >= ARRAY_SIZE(rates))
+               err = -EIO;
+       else
+               *rate = rates[id];
+end:
+       return err;
+}
+static int
+saffirepro_both_clk_freq_set(struct snd_bebob *bebob, unsigned int rate)
+{
+       u32 id;
+
+       for (id = 0; id < ARRAY_SIZE(rates); id++) {
+               if (rates[id] == rate)
+                       break;
+       }
+       if (id == ARRAY_SIZE(rates))
+               return -EINVAL;
+
+       return saffire_write_quad(bebob, SAFFIREPRO_RATE_NOREBOOT, id);
+}
+static int
+saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+       int err;
+       u32 value;
+
+       err = saffire_read_quad(bebob, SAFFIREPRO_OFFSET_CLOCK_SOURCE, &value);
+       if (err < 0)
+               goto end;
+
+       if (bebob->spec->clock->labels == saffirepro_10_clk_src_labels) {
+               if (value == SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK)
+                       *id = 2;
+               else if (value == SAFFIREPRO_CLOCK_SOURCE_SPDIF)
+                       *id = 1;
+       } else if (value > 1) {
+               *id = value - 1;
+       }
+end:
+       return err;
+}
+
+struct snd_bebob_spec saffire_le_spec;
+static char *const saffire_both_clk_src_labels[] = {
+       SND_BEBOB_CLOCK_INTERNAL, "S/PDIF"
+};
+static int
+saffire_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+       int err;
+       u32 value;
+
+       err = saffire_read_quad(bebob, SAFFIRE_OFFSET_CLOCK_SOURCE, &value);
+       if (err >= 0)
+               *id = 0xff & value;
+
+       return err;
+};
+static char *const saffire_le_meter_labels[] = {
+       ANA_IN, ANA_IN, DIG_IN,
+       ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
+       STM_IN, STM_IN
+};
+static char *const saffire_meter_labels[] = {
+       ANA_IN, ANA_IN,
+       STM_IN, STM_IN, STM_IN, STM_IN, STM_IN,
+};
+static int
+saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+       struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+       unsigned int channels;
+       u64 offset;
+       int err;
+
+       if (spec->labels == saffire_le_meter_labels)
+               offset = SAFFIRE_LE_OFFSET_METER;
+       else
+               offset = SAFFIRE_OFFSET_METER;
+
+       channels = spec->num * 2;
+       if (size < channels * sizeof(u32))
+               return -EIO;
+
+       err = saffire_read_block(bebob, offset, buf, size);
+       if (err >= 0 && spec->labels == saffire_le_meter_labels) {
+               swap(buf[1], buf[3]);
+               swap(buf[2], buf[3]);
+               swap(buf[3], buf[4]);
+
+               swap(buf[7], buf[10]);
+               swap(buf[8], buf[10]);
+               swap(buf[9], buf[11]);
+               swap(buf[11], buf[12]);
+
+               swap(buf[15], buf[16]);
+       }
+
+       return err;
+}
+
+static struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
+       .get    = &saffirepro_both_clk_freq_get,
+       .set    = &saffirepro_both_clk_freq_set,
+};
+/* Saffire Pro 26 I/O  */
+static struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
+       .num    = ARRAY_SIZE(saffirepro_26_clk_src_labels),
+       .labels = saffirepro_26_clk_src_labels,
+       .get    = &saffirepro_both_clk_src_get,
+};
+struct snd_bebob_spec saffirepro_26_spec = {
+       .clock  = &saffirepro_26_clk_spec,
+       .rate   = &saffirepro_both_rate_spec,
+       .meter  = NULL
+};
+/* Saffire Pro 10 I/O */
+static struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
+       .num    = ARRAY_SIZE(saffirepro_10_clk_src_labels),
+       .labels = saffirepro_10_clk_src_labels,
+       .get    = &saffirepro_both_clk_src_get,
+};
+struct snd_bebob_spec saffirepro_10_spec = {
+       .clock  = &saffirepro_10_clk_spec,
+       .rate   = &saffirepro_both_rate_spec,
+       .meter  = NULL
+};
+
+static struct snd_bebob_rate_spec saffire_both_rate_spec = {
+       .get    = &snd_bebob_stream_get_rate,
+       .set    = &snd_bebob_stream_set_rate,
+};
+static struct snd_bebob_clock_spec saffire_both_clk_spec = {
+       .num    = ARRAY_SIZE(saffire_both_clk_src_labels),
+       .labels = saffire_both_clk_src_labels,
+       .get    = &saffire_both_clk_src_get,
+};
+/* Saffire LE */
+static struct snd_bebob_meter_spec saffire_le_meter_spec = {
+       .num    = ARRAY_SIZE(saffire_le_meter_labels),
+       .labels = saffire_le_meter_labels,
+       .get    = &saffire_meter_get,
+};
+struct snd_bebob_spec saffire_le_spec = {
+       .clock  = &saffire_both_clk_spec,
+       .rate   = &saffire_both_rate_spec,
+       .meter  = &saffire_le_meter_spec
+};
+/* Saffire */
+static struct snd_bebob_meter_spec saffire_meter_spec = {
+       .num    = ARRAY_SIZE(saffire_meter_labels),
+       .labels = saffire_meter_labels,
+       .get    = &saffire_meter_get,
+};
+struct snd_bebob_spec saffire_spec = {
+       .clock  = &saffire_both_clk_spec,
+       .rate   = &saffire_both_rate_spec,
+       .meter  = &saffire_meter_spec
+};
diff --git a/sound/firewire/bebob/bebob_hwdep.c b/sound/firewire/bebob/bebob_hwdep.c
new file mode 100644 (file)
index 0000000..ce731f4
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * bebob_hwdep.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node infomation
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "bebob.h"
+
+static long
+hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
+          loff_t *offset)
+{
+       struct snd_bebob *bebob = hwdep->private_data;
+       DEFINE_WAIT(wait);
+       union snd_firewire_event event;
+
+       spin_lock_irq(&bebob->lock);
+
+       while (!bebob->dev_lock_changed) {
+               prepare_to_wait(&bebob->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+               spin_unlock_irq(&bebob->lock);
+               schedule();
+               finish_wait(&bebob->hwdep_wait, &wait);
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+               spin_lock_irq(&bebob->lock);
+       }
+
+       memset(&event, 0, sizeof(event));
+       if (bebob->dev_lock_changed) {
+               event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+               event.lock_status.status = (bebob->dev_lock_count > 0);
+               bebob->dev_lock_changed = false;
+
+               count = min_t(long, count, sizeof(event.lock_status));
+       }
+
+       spin_unlock_irq(&bebob->lock);
+
+       if (copy_to_user(buf, &event, count))
+               return -EFAULT;
+
+       return count;
+}
+
+static unsigned int
+hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
+{
+       struct snd_bebob *bebob = hwdep->private_data;
+       unsigned int events;
+
+       poll_wait(file, &bebob->hwdep_wait, wait);
+
+       spin_lock_irq(&bebob->lock);
+       if (bebob->dev_lock_changed)
+               events = POLLIN | POLLRDNORM;
+       else
+               events = 0;
+       spin_unlock_irq(&bebob->lock);
+
+       return events;
+}
+
+static int
+hwdep_get_info(struct snd_bebob *bebob, void __user *arg)
+{
+       struct fw_device *dev = fw_parent_device(bebob->unit);
+       struct snd_firewire_get_info info;
+
+       memset(&info, 0, sizeof(info));
+       info.type = SNDRV_FIREWIRE_TYPE_BEBOB;
+       info.card = dev->card->index;
+       *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+       *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+       strlcpy(info.device_name, dev_name(&dev->device),
+               sizeof(info.device_name));
+
+       if (copy_to_user(arg, &info, sizeof(info)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int
+hwdep_lock(struct snd_bebob *bebob)
+{
+       int err;
+
+       spin_lock_irq(&bebob->lock);
+
+       if (bebob->dev_lock_count == 0) {
+               bebob->dev_lock_count = -1;
+               err = 0;
+       } else {
+               err = -EBUSY;
+       }
+
+       spin_unlock_irq(&bebob->lock);
+
+       return err;
+}
+
+static int
+hwdep_unlock(struct snd_bebob *bebob)
+{
+       int err;
+
+       spin_lock_irq(&bebob->lock);
+
+       if (bebob->dev_lock_count == -1) {
+               bebob->dev_lock_count = 0;
+               err = 0;
+       } else {
+               err = -EBADFD;
+       }
+
+       spin_unlock_irq(&bebob->lock);
+
+       return err;
+}
+
+static int
+hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+       struct snd_bebob *bebob = hwdep->private_data;
+
+       spin_lock_irq(&bebob->lock);
+       if (bebob->dev_lock_count == -1)
+               bebob->dev_lock_count = 0;
+       spin_unlock_irq(&bebob->lock);
+
+       return 0;
+}
+
+static int
+hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+           unsigned int cmd, unsigned long arg)
+{
+       struct snd_bebob *bebob = hwdep->private_data;
+
+       switch (cmd) {
+       case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+               return hwdep_get_info(bebob, (void __user *)arg);
+       case SNDRV_FIREWIRE_IOCTL_LOCK:
+               return hwdep_lock(bebob);
+       case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+               return hwdep_unlock(bebob);
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+#ifdef CONFIG_COMPAT
+static int
+hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+                  unsigned int cmd, unsigned long arg)
+{
+       return hwdep_ioctl(hwdep, file, cmd,
+                          (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+       .read           = hwdep_read,
+       .release        = hwdep_release,
+       .poll           = hwdep_poll,
+       .ioctl          = hwdep_ioctl,
+       .ioctl_compat   = hwdep_compat_ioctl,
+};
+
+int snd_bebob_create_hwdep_device(struct snd_bebob *bebob)
+{
+       struct snd_hwdep *hwdep;
+       int err;
+
+       err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep);
+       if (err < 0)
+               goto end;
+       strcpy(hwdep->name, "BeBoB");
+       hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB;
+       hwdep->ops = hwdep_ops;
+       hwdep->private_data = bebob;
+       hwdep->exclusive = true;
+end:
+       return err;
+}
+
diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c
new file mode 100644 (file)
index 0000000..6af50eb
--- /dev/null
@@ -0,0 +1,792 @@
+/*
+ * bebob_maudio.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+#include <sound/control.h>
+
+/*
+ * Just powering on, Firewire 410/Audiophile/1814 and ProjectMix I/O wait to
+ * download firmware blob. To enable these devices, drivers should upload
+ * firmware blob and send a command to initialize configuration to factory
+ * settings when completing uploading. Then these devices generate bus reset
+ * and are recognized as new devices with the firmware.
+ *
+ * But with firmware version 5058 or later, the firmware is stored to flash
+ * memory in the device and drivers can tell bootloader to load the firmware
+ * by sending a cue. This cue must be sent one time.
+ *
+ * For streaming, both of output and input streams are needed for Firewire 410
+ * and Ozonic. The single stream is OK for the other devices even if the clock
+ * source is not SYT-Match (I note no devices use SYT-Match).
+ *
+ * Without streaming, the devices except for Firewire Audiophile can mix any
+ * input and output. For this reason, Audiophile cannot be used as standalone
+ * mixer.
+ *
+ * Firewire 1814 and ProjectMix I/O uses special firmware. It will be freezed
+ * when receiving any commands which the firmware can't understand. These
+ * devices utilize completely different system to control. It is some
+ * write-transaction directly into a certain address. All of addresses for mixer
+ * functionality is between 0xffc700700000 to 0xffc70070009c.
+ */
+
+/* Offset from information register */
+#define INFO_OFFSET_SW_DATE    0x20
+
+/* Bootloader Protocol Version 1 */
+#define MAUDIO_BOOTLOADER_CUE1 0x00000001
+/*
+ * Initializing configuration to factory settings (= 0x1101), (swapped in line),
+ * Command code is zero (= 0x00),
+ * the number of operands is zero (= 0x00)(at least significant byte)
+ */
+#define MAUDIO_BOOTLOADER_CUE2 0x01110000
+/* padding */
+#define MAUDIO_BOOTLOADER_CUE3 0x00000000
+
+#define MAUDIO_SPECIFIC_ADDRESS        0xffc700000000ULL
+
+#define METER_OFFSET           0x00600000
+
+/* some device has sync info after metering data */
+#define METER_SIZE_SPECIAL     84      /* with sync info */
+#define METER_SIZE_FW410       76      /* with sync info */
+#define METER_SIZE_AUDIOPHILE  60      /* with sync info */
+#define METER_SIZE_SOLO                52      /* with sync info */
+#define METER_SIZE_OZONIC      48
+#define METER_SIZE_NRV10       80
+
+/* labels for metering */
+#define ANA_IN         "Analog In"
+#define ANA_OUT                "Analog Out"
+#define DIG_IN         "Digital In"
+#define SPDIF_IN       "S/PDIF In"
+#define ADAT_IN                "ADAT In"
+#define DIG_OUT                "Digital Out"
+#define SPDIF_OUT      "S/PDIF Out"
+#define ADAT_OUT       "ADAT Out"
+#define STRM_IN                "Stream In"
+#define AUX_OUT                "Aux Out"
+#define HP_OUT         "HP Out"
+/* for NRV */
+#define UNKNOWN_METER  "Unknown"
+
+struct special_params {
+       bool is1814;
+       unsigned int clk_src;
+       unsigned int dig_in_fmt;
+       unsigned int dig_out_fmt;
+       unsigned int clk_lock;
+       struct snd_ctl_elem_id *ctl_id_sync;
+};
+
+/*
+ * For some M-Audio devices, this module just send cue to load firmware. After
+ * loading, the device generates bus reset and newly detected.
+ *
+ * If we make any transactions to load firmware, the operation may failed.
+ */
+int snd_bebob_maudio_load_firmware(struct fw_unit *unit)
+{
+       struct fw_device *device = fw_parent_device(unit);
+       int err, rcode;
+       u64 date;
+       __be32 cues[3] = {
+               MAUDIO_BOOTLOADER_CUE1,
+               MAUDIO_BOOTLOADER_CUE2,
+               MAUDIO_BOOTLOADER_CUE3
+       };
+
+       /* check date of software used to build */
+       err = snd_bebob_read_block(unit, INFO_OFFSET_SW_DATE,
+                                  &date, sizeof(u64));
+       if (err < 0)
+               goto end;
+       /*
+        * firmware version 5058 or later has date later than "20070401", but
+        * 'date' is not null-terminated.
+        */
+       if (date < 0x3230303730343031LL) {
+               dev_err(&unit->device,
+                       "Use firmware version 5058 or later\n");
+               err = -ENOSYS;
+               goto end;
+       }
+
+       rcode = fw_run_transaction(device->card, TCODE_WRITE_BLOCK_REQUEST,
+                                  device->node_id, device->generation,
+                                  device->max_speed, BEBOB_ADDR_REG_REQ,
+                                  cues, sizeof(cues));
+       if (rcode != RCODE_COMPLETE) {
+               dev_err(&unit->device,
+                       "Failed to send a cue to load firmware\n");
+               err = -EIO;
+       }
+end:
+       return err;
+}
+
+static inline int
+get_meter(struct snd_bebob *bebob, void *buf, unsigned int size)
+{
+       return snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
+                                 MAUDIO_SPECIFIC_ADDRESS + METER_OFFSET,
+                                 buf, size, 0);
+}
+
+static int
+check_clk_sync(struct snd_bebob *bebob, unsigned int size, bool *sync)
+{
+       int err;
+       u8 *buf;
+
+       buf = kmalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       err = get_meter(bebob, buf, size);
+       if (err < 0)
+               goto end;
+
+       /* if synced, this value is the same as SFC of FDF in CIP header */
+       *sync = (buf[size - 2] != 0xff);
+end:
+       kfree(buf);
+       return err;
+}
+
+/*
+ * dig_fmt: 0x00:S/PDIF, 0x01:ADAT
+ * clk_lock: 0x00:unlock, 0x01:lock
+ */
+static int
+avc_maudio_set_special_clk(struct snd_bebob *bebob, unsigned int clk_src,
+                          unsigned int dig_in_fmt, unsigned int dig_out_fmt,
+                          unsigned int clk_lock)
+{
+       struct special_params *params = bebob->maudio_special_quirk;
+       int err;
+       u8 *buf;
+
+       if (amdtp_stream_running(&bebob->rx_stream) ||
+           amdtp_stream_running(&bebob->tx_stream))
+               return -EBUSY;
+
+       buf = kmalloc(12, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       buf[0]  = 0x00;         /* CONTROL */
+       buf[1]  = 0xff;         /* UNIT */
+       buf[2]  = 0x00;         /* vendor dependent */
+       buf[3]  = 0x04;         /* company ID high */
+       buf[4]  = 0x00;         /* company ID middle */
+       buf[5]  = 0x04;         /* company ID low */
+       buf[6]  = 0xff & clk_src;       /* clock source */
+       buf[7]  = 0xff & dig_in_fmt;    /* input digital format */
+       buf[8]  = 0xff & dig_out_fmt;   /* output digital format */
+       buf[9]  = 0xff & clk_lock;      /* lock these settings */
+       buf[10] = 0x00;         /* padding  */
+       buf[11] = 0x00;         /* padding */
+
+       err = fcp_avc_transaction(bebob->unit, buf, 12, buf, 12,
+                                 BIT(1) | BIT(2) | BIT(3) | BIT(4) |
+                                 BIT(5) | BIT(6) | BIT(7) | BIT(8) |
+                                 BIT(9));
+       if ((err > 0) && (err < 10))
+               err = -EIO;
+       else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a) /* REJECTED */
+               err = -EINVAL;
+       if (err < 0)
+               goto end;
+
+       params->clk_src         = buf[6];
+       params->dig_in_fmt      = buf[7];
+       params->dig_out_fmt     = buf[8];
+       params->clk_lock        = buf[9];
+
+       if (params->ctl_id_sync)
+               snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              params->ctl_id_sync);
+
+       err = 0;
+end:
+       kfree(buf);
+       return err;
+}
+static void
+special_stream_formation_set(struct snd_bebob *bebob)
+{
+       static const unsigned int ch_table[2][2][3] = {
+               /* AMDTP_OUT_STREAM */
+               { {  6,  6,  4 },       /* SPDIF */
+                 { 12,  8,  4 } },     /* ADAT */
+               /* AMDTP_IN_STREAM */
+               { { 10, 10,  2 },       /* SPDIF */
+                 { 16, 12,  2 } }      /* ADAT */
+       };
+       struct special_params *params = bebob->maudio_special_quirk;
+       unsigned int i, max;
+
+       max = SND_BEBOB_STRM_FMT_ENTRIES - 1;
+       if (!params->is1814)
+               max -= 2;
+
+       for (i = 0; i < max; i++) {
+               bebob->tx_stream_formations[i + 1].pcm =
+                       ch_table[AMDTP_IN_STREAM][params->dig_in_fmt][i / 2];
+               bebob->tx_stream_formations[i + 1].midi = 1;
+
+               bebob->rx_stream_formations[i + 1].pcm =
+                       ch_table[AMDTP_OUT_STREAM][params->dig_out_fmt][i / 2];
+               bebob->rx_stream_formations[i + 1].midi = 1;
+       }
+}
+
+static int add_special_controls(struct snd_bebob *bebob);
+int
+snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814)
+{
+       struct special_params *params;
+       int err;
+
+       params = kzalloc(sizeof(struct special_params), GFP_KERNEL);
+       if (params == NULL)
+               return -ENOMEM;
+
+       mutex_lock(&bebob->mutex);
+
+       bebob->maudio_special_quirk = (void *)params;
+       params->is1814 = is1814;
+
+       /* initialize these parameters because driver is not allowed to ask */
+       bebob->rx_stream.context = ERR_PTR(-1);
+       bebob->tx_stream.context = ERR_PTR(-1);
+       err = avc_maudio_set_special_clk(bebob, 0x03, 0x00, 0x00, 0x00);
+       if (err < 0) {
+               dev_err(&bebob->unit->device,
+                       "fail to initialize clock params: %d\n", err);
+               goto end;
+       }
+
+       err = add_special_controls(bebob);
+       if (err < 0)
+               goto end;
+
+       special_stream_formation_set(bebob);
+
+       if (params->is1814) {
+               bebob->midi_input_ports = 1;
+               bebob->midi_output_ports = 1;
+       } else {
+               bebob->midi_input_ports = 2;
+               bebob->midi_output_ports = 2;
+       }
+end:
+       if (err < 0) {
+               kfree(params);
+               bebob->maudio_special_quirk = NULL;
+       }
+       mutex_unlock(&bebob->mutex);
+       return err;
+}
+
+/* Input plug shows actual rate. Output plug is needless for this purpose. */
+static int special_get_rate(struct snd_bebob *bebob, unsigned int *rate)
+{
+       int err, trials;
+
+       trials = 0;
+       do {
+               err = avc_general_get_sig_fmt(bebob->unit, rate,
+                                             AVC_GENERAL_PLUG_DIR_IN, 0);
+       } while (err == -EAGAIN && ++trials < 3);
+
+       return err;
+}
+static int special_set_rate(struct snd_bebob *bebob, unsigned int rate)
+{
+       struct special_params *params = bebob->maudio_special_quirk;
+       int err;
+
+       err = avc_general_set_sig_fmt(bebob->unit, rate,
+                                     AVC_GENERAL_PLUG_DIR_OUT, 0);
+       if (err < 0)
+               goto end;
+
+       /*
+        * Just after changing sampling rate for output, a followed command
+        * for input is easy to fail. This is a workaround fot this issue.
+        */
+       msleep(100);
+
+       err = avc_general_set_sig_fmt(bebob->unit, rate,
+                                     AVC_GENERAL_PLUG_DIR_IN, 0);
+       if (err < 0)
+               goto end;
+
+       if (params->ctl_id_sync)
+               snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              params->ctl_id_sync);
+end:
+       return err;
+}
+
+/* Clock source control for special firmware */
+static char *const special_clk_labels[] = {
+       SND_BEBOB_CLOCK_INTERNAL " with Digital Mute", "Digital",
+       "Word Clock", SND_BEBOB_CLOCK_INTERNAL};
+static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
+{
+       struct special_params *params = bebob->maudio_special_quirk;
+       *id = params->clk_src;
+       return 0;
+}
+static int special_clk_ctl_info(struct snd_kcontrol *kctl,
+                               struct snd_ctl_elem_info *einf)
+{
+       einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       einf->count = 1;
+       einf->value.enumerated.items = ARRAY_SIZE(special_clk_labels);
+
+       if (einf->value.enumerated.item >= einf->value.enumerated.items)
+               einf->value.enumerated.item = einf->value.enumerated.items - 1;
+
+       strcpy(einf->value.enumerated.name,
+              special_clk_labels[einf->value.enumerated.item]);
+
+       return 0;
+}
+static int special_clk_ctl_get(struct snd_kcontrol *kctl,
+                              struct snd_ctl_elem_value *uval)
+{
+       struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+       struct special_params *params = bebob->maudio_special_quirk;
+       uval->value.enumerated.item[0] = params->clk_src;
+       return 0;
+}
+static int special_clk_ctl_put(struct snd_kcontrol *kctl,
+                              struct snd_ctl_elem_value *uval)
+{
+       struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+       struct special_params *params = bebob->maudio_special_quirk;
+       int err, id;
+
+       mutex_lock(&bebob->mutex);
+
+       id = uval->value.enumerated.item[0];
+       if (id >= ARRAY_SIZE(special_clk_labels))
+               return 0;
+
+       err = avc_maudio_set_special_clk(bebob, id,
+                                        params->dig_in_fmt,
+                                        params->dig_out_fmt,
+                                        params->clk_lock);
+       mutex_unlock(&bebob->mutex);
+
+       return err >= 0;
+}
+static struct snd_kcontrol_new special_clk_ctl = {
+       .name   = "Clock Source",
+       .iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .info   = special_clk_ctl_info,
+       .get    = special_clk_ctl_get,
+       .put    = special_clk_ctl_put
+};
+
+/* Clock synchronization control for special firmware */
+static int special_sync_ctl_info(struct snd_kcontrol *kctl,
+                                struct snd_ctl_elem_info *einf)
+{
+       einf->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       einf->count = 1;
+       einf->value.integer.min = 0;
+       einf->value.integer.max = 1;
+
+       return 0;
+}
+static int special_sync_ctl_get(struct snd_kcontrol *kctl,
+                               struct snd_ctl_elem_value *uval)
+{
+       struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+       int err;
+       bool synced = 0;
+
+       err = check_clk_sync(bebob, METER_SIZE_SPECIAL, &synced);
+       if (err >= 0)
+               uval->value.integer.value[0] = synced;
+
+       return 0;
+}
+static struct snd_kcontrol_new special_sync_ctl = {
+       .name   = "Sync Status",
+       .iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .access = SNDRV_CTL_ELEM_ACCESS_READ,
+       .info   = special_sync_ctl_info,
+       .get    = special_sync_ctl_get,
+};
+
+/* Digital interface control for special firmware */
+static char *const special_dig_iface_labels[] = {
+       "S/PDIF Optical", "S/PDIF Coaxial", "ADAT Optical"
+};
+static int special_dig_in_iface_ctl_info(struct snd_kcontrol *kctl,
+                                        struct snd_ctl_elem_info *einf)
+{
+       einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       einf->count = 1;
+       einf->value.enumerated.items = ARRAY_SIZE(special_dig_iface_labels);
+
+       if (einf->value.enumerated.item >= einf->value.enumerated.items)
+               einf->value.enumerated.item = einf->value.enumerated.items - 1;
+
+       strcpy(einf->value.enumerated.name,
+              special_dig_iface_labels[einf->value.enumerated.item]);
+
+       return 0;
+}
+static int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl,
+                                       struct snd_ctl_elem_value *uval)
+{
+       struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+       struct special_params *params = bebob->maudio_special_quirk;
+       unsigned int dig_in_iface;
+       int err, val;
+
+       mutex_lock(&bebob->mutex);
+
+       err = avc_audio_get_selector(bebob->unit, 0x00, 0x04,
+                                    &dig_in_iface);
+       if (err < 0) {
+               dev_err(&bebob->unit->device,
+                       "fail to get digital input interface: %d\n", err);
+               goto end;
+       }
+
+       /* encoded id for user value */
+       val = (params->dig_in_fmt << 1) | (dig_in_iface & 0x01);
+
+       /* for ADAT Optical */
+       if (val > 2)
+               val = 2;
+
+       uval->value.enumerated.item[0] = val;
+end:
+       mutex_unlock(&bebob->mutex);
+       return err;
+}
+static int special_dig_in_iface_ctl_set(struct snd_kcontrol *kctl,
+                                       struct snd_ctl_elem_value *uval)
+{
+       struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+       struct special_params *params = bebob->maudio_special_quirk;
+       unsigned int id, dig_in_fmt, dig_in_iface;
+       int err;
+
+       mutex_lock(&bebob->mutex);
+
+       id = uval->value.enumerated.item[0];
+
+       /* decode user value */
+       dig_in_fmt = (id >> 1) & 0x01;
+       dig_in_iface = id & 0x01;
+
+       err = avc_maudio_set_special_clk(bebob,
+                                        params->clk_src,
+                                        dig_in_fmt,
+                                        params->dig_out_fmt,
+                                        params->clk_lock);
+       if ((err < 0) || (params->dig_in_fmt > 0)) /* ADAT */
+               goto end;
+
+       err = avc_audio_set_selector(bebob->unit, 0x00, 0x04, dig_in_iface);
+       if (err < 0)
+               dev_err(&bebob->unit->device,
+                       "fail to set digital input interface: %d\n", err);
+end:
+       special_stream_formation_set(bebob);
+       mutex_unlock(&bebob->mutex);
+       return err;
+}
+static struct snd_kcontrol_new special_dig_in_iface_ctl = {
+       .name   = "Digital Input Interface",
+       .iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .info   = special_dig_in_iface_ctl_info,
+       .get    = special_dig_in_iface_ctl_get,
+       .put    = special_dig_in_iface_ctl_set
+};
+
+static int special_dig_out_iface_ctl_info(struct snd_kcontrol *kctl,
+                                         struct snd_ctl_elem_info *einf)
+{
+       einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       einf->count = 1;
+       einf->value.enumerated.items = ARRAY_SIZE(special_dig_iface_labels) - 1;
+
+       if (einf->value.enumerated.item >= einf->value.enumerated.items)
+               einf->value.enumerated.item = einf->value.enumerated.items - 1;
+
+       strcpy(einf->value.enumerated.name,
+              special_dig_iface_labels[einf->value.enumerated.item + 1]);
+
+       return 0;
+}
+static int special_dig_out_iface_ctl_get(struct snd_kcontrol *kctl,
+                                        struct snd_ctl_elem_value *uval)
+{
+       struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+       struct special_params *params = bebob->maudio_special_quirk;
+       mutex_lock(&bebob->mutex);
+       uval->value.enumerated.item[0] = params->dig_out_fmt;
+       mutex_unlock(&bebob->mutex);
+       return 0;
+}
+static int special_dig_out_iface_ctl_set(struct snd_kcontrol *kctl,
+                                        struct snd_ctl_elem_value *uval)
+{
+       struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+       struct special_params *params = bebob->maudio_special_quirk;
+       unsigned int id;
+       int err;
+
+       mutex_lock(&bebob->mutex);
+
+       id = uval->value.enumerated.item[0];
+
+       err = avc_maudio_set_special_clk(bebob,
+                                        params->clk_src,
+                                        params->dig_in_fmt,
+                                        id, params->clk_lock);
+       if (err >= 0)
+               special_stream_formation_set(bebob);
+
+       mutex_unlock(&bebob->mutex);
+       return err;
+}
+static struct snd_kcontrol_new special_dig_out_iface_ctl = {
+       .name   = "Digital Output Interface",
+       .iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .info   = special_dig_out_iface_ctl_info,
+       .get    = special_dig_out_iface_ctl_get,
+       .put    = special_dig_out_iface_ctl_set
+};
+
+static int add_special_controls(struct snd_bebob *bebob)
+{
+       struct snd_kcontrol *kctl;
+       struct special_params *params = bebob->maudio_special_quirk;
+       int err;
+
+       kctl = snd_ctl_new1(&special_clk_ctl, bebob);
+       err = snd_ctl_add(bebob->card, kctl);
+       if (err < 0)
+               goto end;
+
+       kctl = snd_ctl_new1(&special_sync_ctl, bebob);
+       err = snd_ctl_add(bebob->card, kctl);
+       if (err < 0)
+               goto end;
+       params->ctl_id_sync = &kctl->id;
+
+       kctl = snd_ctl_new1(&special_dig_in_iface_ctl, bebob);
+       err = snd_ctl_add(bebob->card, kctl);
+       if (err < 0)
+               goto end;
+
+       kctl = snd_ctl_new1(&special_dig_out_iface_ctl, bebob);
+       err = snd_ctl_add(bebob->card, kctl);
+end:
+       return err;
+}
+
+/* Hardware metering for special firmware */
+static char *const special_meter_labels[] = {
+       ANA_IN, ANA_IN, ANA_IN, ANA_IN,
+       SPDIF_IN,
+       ADAT_IN, ADAT_IN, ADAT_IN, ADAT_IN,
+       ANA_OUT, ANA_OUT,
+       SPDIF_OUT,
+       ADAT_OUT, ADAT_OUT, ADAT_OUT, ADAT_OUT,
+       HP_OUT, HP_OUT,
+       AUX_OUT
+};
+static int
+special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)
+{
+       u16 *buf;
+       unsigned int i, c, channels;
+       int err;
+
+       channels = ARRAY_SIZE(special_meter_labels) * 2;
+       if (size < channels * sizeof(u32))
+               return -EINVAL;
+
+       /* omit last 4 bytes because it's clock info. */
+       buf = kmalloc(METER_SIZE_SPECIAL - 4, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       err = get_meter(bebob, (void *)buf, METER_SIZE_SPECIAL - 4);
+       if (err < 0)
+               goto end;
+
+       /* Its format is u16 and some channels are unknown. */
+       i = 0;
+       for (c = 2; c < channels + 2; c++)
+               target[i++] = be16_to_cpu(buf[c]) << 16;
+end:
+       kfree(buf);
+       return err;
+}
+
+/* last 4 bytes are omitted because it's clock info. */
+static char *const fw410_meter_labels[] = {
+       ANA_IN, DIG_IN,
+       ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT,
+       HP_OUT
+};
+static char *const audiophile_meter_labels[] = {
+       ANA_IN, DIG_IN,
+       ANA_OUT, ANA_OUT, DIG_OUT,
+       HP_OUT, AUX_OUT,
+};
+static char *const solo_meter_labels[] = {
+       ANA_IN, DIG_IN,
+       STRM_IN, STRM_IN,
+       ANA_OUT, DIG_OUT
+};
+
+/* no clock info */
+static char *const ozonic_meter_labels[] = {
+       ANA_IN, ANA_IN,
+       STRM_IN, STRM_IN,
+       ANA_OUT, ANA_OUT
+};
+/* TODO: need testers. these positions are based on authour's assumption */
+static char *const nrv10_meter_labels[] = {
+       ANA_IN, ANA_IN, ANA_IN, ANA_IN,
+       DIG_IN,
+       ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
+       DIG_IN
+};
+static int
+normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+       struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+       unsigned int c, channels;
+       int err;
+
+       channels = spec->num * 2;
+       if (size < channels * sizeof(u32))
+               return -EINVAL;
+
+       err = get_meter(bebob, (void *)buf, size);
+       if (err < 0)
+               goto end;
+
+       for (c = 0; c < channels; c++)
+               be32_to_cpus(&buf[c]);
+
+       /* swap stream channels because inverted */
+       if (spec->labels == solo_meter_labels) {
+               swap(buf[4], buf[6]);
+               swap(buf[5], buf[7]);
+       }
+end:
+       return err;
+}
+
+/* for special customized devices */
+static struct snd_bebob_rate_spec special_rate_spec = {
+       .get    = &special_get_rate,
+       .set    = &special_set_rate,
+};
+static struct snd_bebob_clock_spec special_clk_spec = {
+       .num    = ARRAY_SIZE(special_clk_labels),
+       .labels = special_clk_labels,
+       .get    = &special_clk_get,
+};
+static struct snd_bebob_meter_spec special_meter_spec = {
+       .num    = ARRAY_SIZE(special_meter_labels),
+       .labels = special_meter_labels,
+       .get    = &special_meter_get
+};
+struct snd_bebob_spec maudio_special_spec = {
+       .clock  = &special_clk_spec,
+       .rate   = &special_rate_spec,
+       .meter  = &special_meter_spec
+};
+
+/* Firewire 410 specification */
+static struct snd_bebob_rate_spec usual_rate_spec = {
+       .get    = &snd_bebob_stream_get_rate,
+       .set    = &snd_bebob_stream_set_rate,
+};
+static struct snd_bebob_meter_spec fw410_meter_spec = {
+       .num    = ARRAY_SIZE(fw410_meter_labels),
+       .labels = fw410_meter_labels,
+       .get    = &normal_meter_get
+};
+struct snd_bebob_spec maudio_fw410_spec = {
+       .clock  = NULL,
+       .rate   = &usual_rate_spec,
+       .meter  = &fw410_meter_spec
+};
+
+/* Firewire Audiophile specification */
+static struct snd_bebob_meter_spec audiophile_meter_spec = {
+       .num    = ARRAY_SIZE(audiophile_meter_labels),
+       .labels = audiophile_meter_labels,
+       .get    = &normal_meter_get
+};
+struct snd_bebob_spec maudio_audiophile_spec = {
+       .clock  = NULL,
+       .rate   = &usual_rate_spec,
+       .meter  = &audiophile_meter_spec
+};
+
+/* Firewire Solo specification */
+static struct snd_bebob_meter_spec solo_meter_spec = {
+       .num    = ARRAY_SIZE(solo_meter_labels),
+       .labels = solo_meter_labels,
+       .get    = &normal_meter_get
+};
+struct snd_bebob_spec maudio_solo_spec = {
+       .clock  = NULL,
+       .rate   = &usual_rate_spec,
+       .meter  = &solo_meter_spec
+};
+
+/* Ozonic specification */
+static struct snd_bebob_meter_spec ozonic_meter_spec = {
+       .num    = ARRAY_SIZE(ozonic_meter_labels),
+       .labels = ozonic_meter_labels,
+       .get    = &normal_meter_get
+};
+struct snd_bebob_spec maudio_ozonic_spec = {
+       .clock  = NULL,
+       .rate   = &usual_rate_spec,
+       .meter  = &ozonic_meter_spec
+};
+
+/* NRV10 specification */
+static struct snd_bebob_meter_spec nrv10_meter_spec = {
+       .num    = ARRAY_SIZE(nrv10_meter_labels),
+       .labels = nrv10_meter_labels,
+       .get    = &normal_meter_get
+};
+struct snd_bebob_spec maudio_nrv10_spec = {
+       .clock  = NULL,
+       .rate   = &usual_rate_spec,
+       .meter  = &nrv10_meter_spec
+};
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c
new file mode 100644 (file)
index 0000000..63343d5
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * bebob_midi.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "bebob.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+       struct snd_bebob *bebob = substream->rmidi->private_data;
+       int err;
+
+       err = snd_bebob_stream_lock_try(bebob);
+       if (err < 0)
+               goto end;
+
+       atomic_inc(&bebob->capture_substreams);
+       err = snd_bebob_stream_start_duplex(bebob, 0);
+       if (err < 0)
+               snd_bebob_stream_lock_release(bebob);
+end:
+       return err;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+       struct snd_bebob *bebob = substream->rmidi->private_data;
+       int err;
+
+       err = snd_bebob_stream_lock_try(bebob);
+       if (err < 0)
+               goto end;
+
+       atomic_inc(&bebob->playback_substreams);
+       err = snd_bebob_stream_start_duplex(bebob, 0);
+       if (err < 0)
+               snd_bebob_stream_lock_release(bebob);
+end:
+       return err;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+       struct snd_bebob *bebob = substream->rmidi->private_data;
+
+       atomic_dec(&bebob->capture_substreams);
+       snd_bebob_stream_stop_duplex(bebob);
+
+       snd_bebob_stream_lock_release(bebob);
+       return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+       struct snd_bebob *bebob = substream->rmidi->private_data;
+
+       atomic_dec(&bebob->playback_substreams);
+       snd_bebob_stream_stop_duplex(bebob);
+
+       snd_bebob_stream_lock_release(bebob);
+       return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+       struct snd_bebob *bebob = substrm->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&bebob->lock, flags);
+
+       if (up)
+               amdtp_stream_midi_trigger(&bebob->tx_stream,
+                                         substrm->number, substrm);
+       else
+               amdtp_stream_midi_trigger(&bebob->tx_stream,
+                                         substrm->number, NULL);
+
+       spin_unlock_irqrestore(&bebob->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+       struct snd_bebob *bebob = substrm->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&bebob->lock, flags);
+
+       if (up)
+               amdtp_stream_midi_trigger(&bebob->rx_stream,
+                                         substrm->number, substrm);
+       else
+               amdtp_stream_midi_trigger(&bebob->rx_stream,
+                                         substrm->number, NULL);
+
+       spin_unlock_irqrestore(&bebob->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+       .open           = midi_capture_open,
+       .close          = midi_capture_close,
+       .trigger        = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+       .open           = midi_playback_open,
+       .close          = midi_playback_close,
+       .trigger        = midi_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_bebob *bebob,
+                                    struct snd_rawmidi_str *str)
+{
+       struct snd_rawmidi_substream *subs;
+
+       list_for_each_entry(subs, &str->substreams, list) {
+               snprintf(subs->name, sizeof(subs->name),
+                        "%s MIDI %d",
+                        bebob->card->shortname, subs->number + 1);
+       }
+}
+
+int snd_bebob_create_midi_devices(struct snd_bebob *bebob)
+{
+       struct snd_rawmidi *rmidi;
+       struct snd_rawmidi_str *str;
+       int err;
+
+       /* create midi ports */
+       err = snd_rawmidi_new(bebob->card, bebob->card->driver, 0,
+                             bebob->midi_output_ports, bebob->midi_input_ports,
+                             &rmidi);
+       if (err < 0)
+               return err;
+
+       snprintf(rmidi->name, sizeof(rmidi->name),
+                "%s MIDI", bebob->card->shortname);
+       rmidi->private_data = bebob;
+
+       if (bebob->midi_input_ports > 0) {
+               rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+               snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+                                   &midi_capture_ops);
+
+               str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+               set_midi_substream_names(bebob, str);
+       }
+
+       if (bebob->midi_output_ports > 0) {
+               rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+               snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+                                   &midi_playback_ops);
+
+               str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+               set_midi_substream_names(bebob, str);
+       }
+
+       if ((bebob->midi_output_ports > 0) && (bebob->midi_input_ports > 0))
+               rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+       return 0;
+}
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
new file mode 100644 (file)
index 0000000..4a55561
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * bebob_pcm.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+static int
+hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+       struct snd_bebob_stream_formation *formations = rule->private;
+       struct snd_interval *r =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       const struct snd_interval *c =
+               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval t = {
+               .min = UINT_MAX, .max = 0, .integer = 1
+       };
+       unsigned int i;
+
+       for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+               /* entry is invalid */
+               if (formations[i].pcm == 0)
+                       continue;
+
+               if (!snd_interval_test(c, formations[i].pcm))
+                       continue;
+
+               t.min = min(t.min, snd_bebob_rate_table[i]);
+               t.max = max(t.max, snd_bebob_rate_table[i]);
+
+       }
+       return snd_interval_refine(r, &t);
+}
+
+static int
+hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+       struct snd_bebob_stream_formation *formations = rule->private;
+       struct snd_interval *c =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       const struct snd_interval *r =
+               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval t = {
+               .min = UINT_MAX, .max = 0, .integer = 1
+       };
+
+       unsigned int i;
+
+       for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+               /* entry is invalid */
+               if (formations[i].pcm == 0)
+                       continue;
+
+               if (!snd_interval_test(r, snd_bebob_rate_table[i]))
+                       continue;
+
+               t.min = min(t.min, formations[i].pcm);
+               t.max = max(t.max, formations[i].pcm);
+       }
+
+       return snd_interval_refine(c, &t);
+}
+
+static void
+limit_channels_and_rates(struct snd_pcm_hardware *hw,
+                        struct snd_bebob_stream_formation *formations)
+{
+       unsigned int i;
+
+       hw->channels_min = UINT_MAX;
+       hw->channels_max = 0;
+
+       hw->rate_min = UINT_MAX;
+       hw->rate_max = 0;
+       hw->rates = 0;
+
+       for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+               /* entry has no PCM channels */
+               if (formations[i].pcm == 0)
+                       continue;
+
+               hw->channels_min = min(hw->channels_min, formations[i].pcm);
+               hw->channels_max = max(hw->channels_max, formations[i].pcm);
+
+               hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]);
+               hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]);
+               hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]);
+       }
+}
+
+static void
+limit_period_and_buffer(struct snd_pcm_hardware *hw)
+{
+       hw->periods_min = 2;            /* SNDRV_PCM_INFO_BATCH */
+       hw->periods_max = UINT_MAX;
+
+       hw->period_bytes_min = 4 * hw->channels_max;    /* bytes for a frame */
+
+       /* Just to prevent from allocating much pages. */
+       hw->period_bytes_max = hw->period_bytes_min * 2048;
+       hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
+}
+
+static int
+pcm_init_hw_params(struct snd_bebob *bebob,
+                  struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct amdtp_stream *s;
+       struct snd_bebob_stream_formation *formations;
+       int err;
+
+       runtime->hw.info = SNDRV_PCM_INFO_BATCH |
+                          SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                          SNDRV_PCM_INFO_INTERLEAVED |
+                          SNDRV_PCM_INFO_JOINT_DUPLEX |
+                          SNDRV_PCM_INFO_MMAP |
+                          SNDRV_PCM_INFO_MMAP_VALID;
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+               s = &bebob->tx_stream;
+               formations = bebob->tx_stream_formations;
+       } else {
+               runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+               s = &bebob->rx_stream;
+               formations = bebob->rx_stream_formations;
+       }
+
+       limit_channels_and_rates(&runtime->hw, formations);
+       limit_period_and_buffer(&runtime->hw);
+
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                 hw_rule_channels, formations,
+                                 SNDRV_PCM_HW_PARAM_RATE, -1);
+       if (err < 0)
+               goto end;
+
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                 hw_rule_rate, formations,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+       if (err < 0)
+               goto end;
+
+       err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+end:
+       return err;
+}
+
+static int
+pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_bebob *bebob = substream->private_data;
+       struct snd_bebob_rate_spec *spec = bebob->spec->rate;
+       unsigned int sampling_rate;
+       bool internal;
+       int err;
+
+       err = snd_bebob_stream_lock_try(bebob);
+       if (err < 0)
+               goto end;
+
+       err = pcm_init_hw_params(bebob, substream);
+       if (err < 0)
+               goto err_locked;
+
+       err = snd_bebob_stream_check_internal_clock(bebob, &internal);
+       if (err < 0)
+               goto err_locked;
+
+       /*
+        * When source of clock is internal or any PCM stream are running,
+        * the available sampling rate is limited at current sampling rate.
+        */
+       if (!internal ||
+           amdtp_stream_pcm_running(&bebob->tx_stream) ||
+           amdtp_stream_pcm_running(&bebob->rx_stream)) {
+               err = spec->get(bebob, &sampling_rate);
+               if (err < 0) {
+                       dev_err(&bebob->unit->device,
+                               "fail to get sampling rate: %d\n", err);
+                       goto err_locked;
+               }
+
+               substream->runtime->hw.rate_min = sampling_rate;
+               substream->runtime->hw.rate_max = sampling_rate;
+       }
+
+       snd_pcm_set_sync(substream);
+end:
+       return err;
+err_locked:
+       snd_bebob_stream_lock_release(bebob);
+       return err;
+}
+
+static int
+pcm_close(struct snd_pcm_substream *substream)
+{
+       struct snd_bebob *bebob = substream->private_data;
+       snd_bebob_stream_lock_release(bebob);
+       return 0;
+}
+
+static int
+pcm_capture_hw_params(struct snd_pcm_substream *substream,
+                     struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_bebob *bebob = substream->private_data;
+
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+               atomic_inc(&bebob->capture_substreams);
+       amdtp_stream_set_pcm_format(&bebob->tx_stream,
+                                   params_format(hw_params));
+       return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                               params_buffer_bytes(hw_params));
+}
+static int
+pcm_playback_hw_params(struct snd_pcm_substream *substream,
+                      struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_bebob *bebob = substream->private_data;
+
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+               atomic_inc(&bebob->playback_substreams);
+       amdtp_stream_set_pcm_format(&bebob->rx_stream,
+                                   params_format(hw_params));
+       return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                               params_buffer_bytes(hw_params));
+}
+
+static int
+pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_bebob *bebob = substream->private_data;
+
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+               atomic_dec(&bebob->capture_substreams);
+
+       snd_bebob_stream_stop_duplex(bebob);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+static int
+pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_bebob *bebob = substream->private_data;
+
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+               atomic_dec(&bebob->playback_substreams);
+
+       snd_bebob_stream_stop_duplex(bebob);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int
+pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_bebob *bebob = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
+       if (err >= 0)
+               amdtp_stream_pcm_prepare(&bebob->tx_stream);
+
+       return err;
+}
+static int
+pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_bebob *bebob = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
+       if (err >= 0)
+               amdtp_stream_pcm_prepare(&bebob->rx_stream);
+
+       return err;
+}
+
+static int
+pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_bebob *bebob = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               amdtp_stream_pcm_trigger(&bebob->tx_stream, substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+static int
+pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_bebob *bebob = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               amdtp_stream_pcm_trigger(&bebob->rx_stream, substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t
+pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+       struct snd_bebob *bebob = sbstrm->private_data;
+       return amdtp_stream_pcm_pointer(&bebob->tx_stream);
+}
+static snd_pcm_uframes_t
+pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+       struct snd_bebob *bebob = sbstrm->private_data;
+       return amdtp_stream_pcm_pointer(&bebob->rx_stream);
+}
+
+static const struct snd_pcm_ops pcm_capture_ops = {
+       .open           = pcm_open,
+       .close          = pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = pcm_capture_hw_params,
+       .hw_free        = pcm_capture_hw_free,
+       .prepare        = pcm_capture_prepare,
+       .trigger        = pcm_capture_trigger,
+       .pointer        = pcm_capture_pointer,
+       .page           = snd_pcm_lib_get_vmalloc_page,
+};
+static const struct snd_pcm_ops pcm_playback_ops = {
+       .open           = pcm_open,
+       .close          = pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = pcm_playback_hw_params,
+       .hw_free        = pcm_playback_hw_free,
+       .prepare        = pcm_playback_prepare,
+       .trigger        = pcm_playback_trigger,
+       .pointer        = pcm_playback_pointer,
+       .page           = snd_pcm_lib_get_vmalloc_page,
+       .mmap           = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
+{
+       struct snd_pcm *pcm;
+       int err;
+
+       err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm);
+       if (err < 0)
+               goto end;
+
+       pcm->private_data = bebob;
+       snprintf(pcm->name, sizeof(pcm->name),
+                "%s PCM", bebob->card->shortname);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+end:
+       return err;
+}
diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c
new file mode 100644 (file)
index 0000000..335da64
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * bebob_proc.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+/* contents of information register */
+struct hw_info {
+       u64 manufacturer;
+       u32 protocol_ver;
+       u32 bld_ver;
+       u32 guid[2];
+       u32 model_id;
+       u32 model_rev;
+       u64 fw_date;
+       u64 fw_time;
+       u32 fw_id;
+       u32 fw_ver;
+       u32 base_addr;
+       u32 max_size;
+       u64 bld_date;
+       u64 bld_time;
+/* may not used in product
+       u64 dbg_date;
+       u64 dbg_time;
+       u32 dbg_id;
+       u32 dbg_version;
+*/
+} __packed;
+
+static void
+proc_read_hw_info(struct snd_info_entry *entry,
+                 struct snd_info_buffer *buffer)
+{
+       struct snd_bebob *bebob = entry->private_data;
+       struct hw_info *info;
+
+       info = kzalloc(sizeof(struct hw_info), GFP_KERNEL);
+       if (info == NULL)
+               return;
+
+       if (snd_bebob_read_block(bebob->unit, 0,
+                                  info, sizeof(struct hw_info)) < 0)
+               goto end;
+
+       snd_iprintf(buffer, "Manufacturer:\t%.8s\n",
+                   (char *)&info->manufacturer);
+       snd_iprintf(buffer, "Protocol Ver:\t%d\n", info->protocol_ver);
+       snd_iprintf(buffer, "Build Ver:\t%d\n", info->bld_ver);
+       snd_iprintf(buffer, "GUID:\t\t0x%.8X%.8X\n",
+                   info->guid[0], info->guid[1]);
+       snd_iprintf(buffer, "Model ID:\t0x%02X\n", info->model_id);
+       snd_iprintf(buffer, "Model Rev:\t%d\n", info->model_rev);
+       snd_iprintf(buffer, "Firmware Date:\t%.8s\n", (char *)&info->fw_date);
+       snd_iprintf(buffer, "Firmware Time:\t%.8s\n", (char *)&info->fw_time);
+       snd_iprintf(buffer, "Firmware ID:\t0x%X\n", info->fw_id);
+       snd_iprintf(buffer, "Firmware Ver:\t%d\n", info->fw_ver);
+       snd_iprintf(buffer, "Base Addr:\t0x%X\n", info->base_addr);
+       snd_iprintf(buffer, "Max Size:\t%d\n", info->max_size);
+       snd_iprintf(buffer, "Loader Date:\t%.8s\n", (char *)&info->bld_date);
+       snd_iprintf(buffer, "Loader Time:\t%.8s\n", (char *)&info->bld_time);
+
+end:
+       kfree(info);
+}
+
+static void
+proc_read_meters(struct snd_info_entry *entry,
+                struct snd_info_buffer *buffer)
+{
+       struct snd_bebob *bebob = entry->private_data;
+       struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+       u32 *buf;
+       unsigned int i, c, channels, size;
+
+       if (spec == NULL)
+               return;
+
+       channels = spec->num * 2;
+       size = channels * sizeof(u32);
+       buf = kmalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return;
+
+       if (spec->get(bebob, buf, size) < 0)
+               goto end;
+
+       for (i = 0, c = 1; i < channels; i++) {
+               snd_iprintf(buffer, "%s %d:\t%d\n",
+                           spec->labels[i / 2], c++, buf[i]);
+               if ((i + 1 < channels - 1) &&
+                   (strcmp(spec->labels[i / 2],
+                           spec->labels[(i + 1) / 2]) != 0))
+                       c = 1;
+       }
+end:
+       kfree(buf);
+}
+
+static void
+proc_read_formation(struct snd_info_entry *entry,
+               struct snd_info_buffer *buffer)
+{
+       struct snd_bebob *bebob = entry->private_data;
+       struct snd_bebob_stream_formation *formation;
+       unsigned int i;
+
+       snd_iprintf(buffer, "Output Stream from device:\n");
+       snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+       formation = bebob->tx_stream_formations;
+       for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+               snd_iprintf(buffer,
+                           "\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
+                           formation[i].pcm, formation[i].midi);
+       }
+
+       snd_iprintf(buffer, "Input Stream to device:\n");
+       snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+       formation = bebob->rx_stream_formations;
+       for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+               snd_iprintf(buffer,
+                           "\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
+                           formation[i].pcm, formation[i].midi);
+       }
+}
+
+static void
+proc_read_clock(struct snd_info_entry *entry,
+               struct snd_info_buffer *buffer)
+{
+       struct snd_bebob *bebob = entry->private_data;
+       struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+       struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+       unsigned int rate, id;
+       bool internal;
+
+       if (rate_spec->get(bebob, &rate) >= 0)
+               snd_iprintf(buffer, "Sampling rate: %d\n", rate);
+
+       if (clk_spec) {
+               if (clk_spec->get(bebob, &id) >= 0)
+                       snd_iprintf(buffer, "Clock Source: %s\n",
+                                   clk_spec->labels[id]);
+       } else {
+               if (snd_bebob_stream_check_internal_clock(bebob,
+                                                         &internal) >= 0)
+                       snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)\n",
+                                   (internal) ? "Internal" : "External",
+                                   bebob->sync_input_plug);
+       }
+}
+
+static void
+add_node(struct snd_bebob *bebob, struct snd_info_entry *root, const char *name,
+        void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
+{
+       struct snd_info_entry *entry;
+
+       entry = snd_info_create_card_entry(bebob->card, name, root);
+       if (entry == NULL)
+               return;
+
+       snd_info_set_text_ops(entry, bebob, op);
+       if (snd_info_register(entry) < 0)
+               snd_info_free_entry(entry);
+}
+
+void snd_bebob_proc_init(struct snd_bebob *bebob)
+{
+       struct snd_info_entry *root;
+
+       /*
+        * All nodes are automatically removed at snd_card_disconnect(),
+        * by following to link list.
+        */
+       root = snd_info_create_card_entry(bebob->card, "firewire",
+                                         bebob->card->proc_root);
+       if (root == NULL)
+               return;
+       root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+       if (snd_info_register(root) < 0) {
+               snd_info_free_entry(root);
+               return;
+       }
+
+       add_node(bebob, root, "clock", proc_read_clock);
+       add_node(bebob, root, "firmware", proc_read_hw_info);
+       add_node(bebob, root, "formation", proc_read_formation);
+
+       if (bebob->spec->meter != NULL)
+               add_node(bebob, root, "meter", proc_read_meters);
+}
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
new file mode 100644 (file)
index 0000000..bc4f827
--- /dev/null
@@ -0,0 +1,1021 @@
+/*
+ * bebob_stream.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+#define CALLBACK_TIMEOUT       1000
+#define FW_ISO_RESOURCE_DELAY  1000
+
+/*
+ * NOTE;
+ * For BeBoB streams, Both of input and output CMP connection are important.
+ *
+ * For most devices, each CMP connection starts to transmit/receive a
+ * corresponding stream. But for a few devices, both of CMP connection needs
+ * to start transmitting stream. An example is 'M-Audio Firewire 410'.
+ */
+
+/* 128 is an arbitrary length but it seems to be enough */
+#define FORMAT_MAXIMUM_LENGTH 128
+
+const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES] = {
+       [0] = 32000,
+       [1] = 44100,
+       [2] = 48000,
+       [3] = 88200,
+       [4] = 96000,
+       [5] = 176400,
+       [6] = 192000,
+};
+
+/*
+ * See: Table 51: Extended Stream Format Info â€˜Sampling Frequency’
+ * in Additional AVC commands (Nov 2003, BridgeCo)
+ */
+static const unsigned int bridgeco_freq_table[] = {
+       [0] = 0x02,
+       [1] = 0x03,
+       [2] = 0x04,
+       [3] = 0x0a,
+       [4] = 0x05,
+       [5] = 0x06,
+       [6] = 0x07,
+};
+
+static unsigned int
+get_formation_index(unsigned int rate)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(snd_bebob_rate_table); i++) {
+               if (snd_bebob_rate_table[i] == rate)
+                       return i;
+       }
+       return -EINVAL;
+}
+
+int
+snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *curr_rate)
+{
+       unsigned int tx_rate, rx_rate, trials;
+       int err;
+
+       trials = 0;
+       do {
+               err = avc_general_get_sig_fmt(bebob->unit, &tx_rate,
+                                             AVC_GENERAL_PLUG_DIR_OUT, 0);
+       } while (err == -EAGAIN && ++trials < 3);
+       if (err < 0)
+               goto end;
+
+       trials = 0;
+       do {
+               err = avc_general_get_sig_fmt(bebob->unit, &rx_rate,
+                                             AVC_GENERAL_PLUG_DIR_IN, 0);
+       } while (err == -EAGAIN && ++trials < 3);
+       if (err < 0)
+               goto end;
+
+       *curr_rate = rx_rate;
+       if (rx_rate == tx_rate)
+               goto end;
+
+       /* synchronize receive stream rate to transmit stream rate */
+       err = avc_general_set_sig_fmt(bebob->unit, rx_rate,
+                                     AVC_GENERAL_PLUG_DIR_IN, 0);
+end:
+       return err;
+}
+
+int
+snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate)
+{
+       int err;
+
+       err = avc_general_set_sig_fmt(bebob->unit, rate,
+                                     AVC_GENERAL_PLUG_DIR_OUT, 0);
+       if (err < 0)
+               goto end;
+
+       err = avc_general_set_sig_fmt(bebob->unit, rate,
+                                     AVC_GENERAL_PLUG_DIR_IN, 0);
+       if (err < 0)
+               goto end;
+
+       /*
+        * Some devices need a bit time for transition.
+        * 300msec is got by some experiments.
+        */
+       msleep(300);
+end:
+       return err;
+}
+
+int
+snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
+{
+       struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+       u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
+       unsigned int id;
+       int err = 0;
+
+       *internal = false;
+
+       /* 1.The device has its own operation to switch source of clock */
+       if (clk_spec) {
+               err = clk_spec->get(bebob, &id);
+               if (err < 0)
+                       dev_err(&bebob->unit->device,
+                               "fail to get clock source: %d\n", err);
+               else if (strncmp(clk_spec->labels[id], SND_BEBOB_CLOCK_INTERNAL,
+                                strlen(SND_BEBOB_CLOCK_INTERNAL)) == 0)
+                       *internal = true;
+               goto end;
+       }
+
+       /*
+        * 2.The device don't support to switch source of clock then assumed
+        *   to use internal clock always
+        */
+       if (bebob->sync_input_plug < 0) {
+               *internal = true;
+               goto end;
+       }
+
+       /*
+        * 3.The device supports to switch source of clock by an usual way.
+        *   Let's check input for 'Music Sub Unit Sync Input' plug.
+        */
+       avc_bridgeco_fill_msu_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN,
+                                  bebob->sync_input_plug);
+       err = avc_bridgeco_get_plug_input(bebob->unit, addr, input);
+       if (err < 0) {
+               dev_err(&bebob->unit->device,
+                       "fail to get an input for MSU in plug %d: %d\n",
+                       bebob->sync_input_plug, err);
+               goto end;
+       }
+
+       /*
+        * If there are no input plugs, all of fields are 0xff.
+        * Here check the first field. This field is used for direction.
+        */
+       if (input[0] == 0xff) {
+               *internal = true;
+               goto end;
+       }
+
+       /*
+        * If source of clock is internal CSR, Music Sub Unit Sync Input is
+        * a destination of Music Sub Unit Sync Output.
+        */
+       *internal = ((input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) &&
+                    (input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT) &&
+                    (input[2] == 0x0c) &&
+                    (input[3] == 0x00));
+end:
+       return err;
+}
+
+static unsigned int
+map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
+{
+       unsigned int sec, sections, ch, channels;
+       unsigned int pcm, midi, location;
+       unsigned int stm_pos, sec_loc, pos;
+       u8 *buf, addr[AVC_BRIDGECO_ADDR_BYTES], type;
+       enum avc_bridgeco_plug_dir dir;
+       int err;
+
+       /*
+        * The length of return value of this command cannot be expected. Here
+        * use the maximum length of FCP.
+        */
+       buf = kzalloc(256, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       if (s == &bebob->tx_stream)
+               dir = AVC_BRIDGECO_PLUG_DIR_OUT;
+       else
+               dir = AVC_BRIDGECO_PLUG_DIR_IN;
+
+       avc_bridgeco_fill_unit_addr(addr, dir, AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+       err = avc_bridgeco_get_plug_ch_pos(bebob->unit, addr, buf, 256);
+       if (err < 0) {
+               dev_err(&bebob->unit->device,
+                       "fail to get channel position for isoc %s plug 0: %d\n",
+                       (dir == AVC_BRIDGECO_PLUG_DIR_IN) ? "in" : "out",
+                       err);
+               goto end;
+       }
+       pos = 0;
+
+       /* positions in I/O buffer */
+       pcm = 0;
+       midi = 0;
+
+       /* the number of sections in AMDTP packet */
+       sections = buf[pos++];
+
+       for (sec = 0; sec < sections; sec++) {
+               /* type of this section */
+               avc_bridgeco_fill_unit_addr(addr, dir,
+                                           AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+               err = avc_bridgeco_get_plug_section_type(bebob->unit, addr,
+                                                        sec, &type);
+               if (err < 0) {
+                       dev_err(&bebob->unit->device,
+                       "fail to get section type for isoc %s plug 0: %d\n",
+                               (dir == AVC_BRIDGECO_PLUG_DIR_IN) ? "in" :
+                                                                   "out",
+                               err);
+                       goto end;
+               }
+               /* NoType */
+               if (type == 0xff) {
+                       err = -ENOSYS;
+                       goto end;
+               }
+
+               /* the number of channels in this section */
+               channels = buf[pos++];
+
+               for (ch = 0; ch < channels; ch++) {
+                       /* position of this channel in AMDTP packet */
+                       stm_pos = buf[pos++] - 1;
+                       /* location of this channel in this section */
+                       sec_loc = buf[pos++] - 1;
+
+                       /*
+                        * Basically the number of location is within the
+                        * number of channels in this section. But some models
+                        * of M-Audio don't follow this. Its location for MIDI
+                        * is the position of MIDI channels in AMDTP packet.
+                        */
+                       if (sec_loc >= channels)
+                               sec_loc = ch;
+
+                       switch (type) {
+                       /* for MIDI conformant data channel */
+                       case 0x0a:
+                               /* AMDTP_MAX_CHANNELS_FOR_MIDI is 1. */
+                               if ((midi > 0) && (stm_pos != midi)) {
+                                       err = -ENOSYS;
+                                       goto end;
+                               }
+                               s->midi_position = stm_pos;
+                               midi = stm_pos;
+                               break;
+                       /* for PCM data channel */
+                       case 0x01:      /* Headphone */
+                       case 0x02:      /* Microphone */
+                       case 0x03:      /* Line */
+                       case 0x04:      /* SPDIF */
+                       case 0x05:      /* ADAT */
+                       case 0x06:      /* TDIF */
+                       case 0x07:      /* MADI */
+                       /* for undefined/changeable signal  */
+                       case 0x08:      /* Analog */
+                       case 0x09:      /* Digital */
+                       default:
+                               location = pcm + sec_loc;
+                               if (location >= AMDTP_MAX_CHANNELS_FOR_PCM) {
+                                       err = -ENOSYS;
+                                       goto end;
+                               }
+                               s->pcm_positions[location] = stm_pos;
+                               break;
+                       }
+               }
+
+               if (type != 0x0a)
+                       pcm += channels;
+               else
+                       midi += channels;
+       }
+end:
+       kfree(buf);
+       return err;
+}
+
+static int
+init_both_connections(struct snd_bebob *bebob)
+{
+       int err;
+
+       err = cmp_connection_init(&bebob->in_conn,
+                                 bebob->unit, CMP_INPUT, 0);
+       if (err < 0)
+               goto end;
+
+       err = cmp_connection_init(&bebob->out_conn,
+                                 bebob->unit, CMP_OUTPUT, 0);
+       if (err < 0)
+               cmp_connection_destroy(&bebob->in_conn);
+end:
+       return err;
+}
+
+static int
+check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s)
+{
+       struct cmp_connection *conn;
+       bool used;
+       int err;
+
+       if (s == &bebob->tx_stream)
+               conn = &bebob->out_conn;
+       else
+               conn = &bebob->in_conn;
+
+       err = cmp_connection_check_used(conn, &used);
+       if ((err >= 0) && used && !amdtp_stream_running(s)) {
+               dev_err(&bebob->unit->device,
+                       "Connection established by others: %cPCR[%d]\n",
+                       (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+                       conn->pcr_index);
+               err = -EBUSY;
+       }
+
+       return err;
+}
+
+static int
+make_both_connections(struct snd_bebob *bebob, unsigned int rate)
+{
+       int index, pcm_channels, midi_channels, err = 0;
+
+       if (bebob->connected)
+               goto end;
+
+       /* confirm params for both streams */
+       index = get_formation_index(rate);
+       pcm_channels = bebob->tx_stream_formations[index].pcm;
+       midi_channels = bebob->tx_stream_formations[index].midi;
+       amdtp_stream_set_parameters(&bebob->tx_stream,
+                                   rate, pcm_channels, midi_channels * 8);
+       pcm_channels = bebob->rx_stream_formations[index].pcm;
+       midi_channels = bebob->rx_stream_formations[index].midi;
+       amdtp_stream_set_parameters(&bebob->rx_stream,
+                                   rate, pcm_channels, midi_channels * 8);
+
+       /* establish connections for both streams */
+       err = cmp_connection_establish(&bebob->out_conn,
+                       amdtp_stream_get_max_payload(&bebob->tx_stream));
+       if (err < 0)
+               goto end;
+       err = cmp_connection_establish(&bebob->in_conn,
+                       amdtp_stream_get_max_payload(&bebob->rx_stream));
+       if (err < 0) {
+               cmp_connection_break(&bebob->out_conn);
+               goto end;
+       }
+
+       bebob->connected = true;
+end:
+       return err;
+}
+
+static void
+break_both_connections(struct snd_bebob *bebob)
+{
+       cmp_connection_break(&bebob->in_conn);
+       cmp_connection_break(&bebob->out_conn);
+
+       bebob->connected = false;
+
+       /* These models seems to be in transition state for a longer time. */
+       if (bebob->maudio_special_quirk != NULL)
+               msleep(200);
+}
+
+static void
+destroy_both_connections(struct snd_bebob *bebob)
+{
+       break_both_connections(bebob);
+
+       cmp_connection_destroy(&bebob->in_conn);
+       cmp_connection_destroy(&bebob->out_conn);
+}
+
+static int
+get_sync_mode(struct snd_bebob *bebob, enum cip_flags *sync_mode)
+{
+       /* currently this module doesn't support SYT-Match mode */
+       *sync_mode = CIP_SYNC_TO_DEVICE;
+       return 0;
+}
+
+static int
+start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
+            unsigned int rate)
+{
+       struct cmp_connection *conn;
+       int err = 0;
+
+       if (stream == &bebob->rx_stream)
+               conn = &bebob->in_conn;
+       else
+               conn = &bebob->out_conn;
+
+       /* channel mapping */
+       if (bebob->maudio_special_quirk == NULL) {
+               err = map_data_channels(bebob, stream);
+               if (err < 0)
+                       goto end;
+       }
+
+       /* start amdtp stream */
+       err = amdtp_stream_start(stream,
+                                conn->resources.channel,
+                                conn->speed);
+end:
+       return err;
+}
+
+int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
+{
+       int err;
+
+       err = init_both_connections(bebob);
+       if (err < 0)
+               goto end;
+
+       err = amdtp_stream_init(&bebob->tx_stream, bebob->unit,
+                               AMDTP_IN_STREAM, CIP_BLOCKING);
+       if (err < 0) {
+               amdtp_stream_destroy(&bebob->tx_stream);
+               destroy_both_connections(bebob);
+               goto end;
+       }
+       /* See comments in next function */
+       init_completion(&bebob->bus_reset);
+       bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
+       /*
+        * At high sampling rate, M-Audio special firmware transmits empty
+        * packet with the value of dbc incremented by 8 but the others are
+        * valid to IEC 61883-1.
+        */
+       if (bebob->maudio_special_quirk)
+               bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
+
+       err = amdtp_stream_init(&bebob->rx_stream, bebob->unit,
+                               AMDTP_OUT_STREAM, CIP_BLOCKING);
+       if (err < 0) {
+               amdtp_stream_destroy(&bebob->tx_stream);
+               amdtp_stream_destroy(&bebob->rx_stream);
+               destroy_both_connections(bebob);
+       }
+       /*
+        * The firmware for these devices ignore MIDI messages in more than
+        * first 8 data blocks of an received AMDTP packet.
+        */
+       if (bebob->spec == &maudio_fw410_spec ||
+           bebob->spec == &maudio_special_spec)
+               bebob->rx_stream.rx_blocks_for_midi = 8;
+end:
+       return err;
+}
+
+int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
+{
+       struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+       struct amdtp_stream *master, *slave;
+       atomic_t *slave_substreams;
+       enum cip_flags sync_mode;
+       unsigned int curr_rate;
+       bool updated = false;
+       int err = 0;
+
+       /*
+        * Normal BeBoB firmware has a quirk at bus reset to transmits packets
+        * with discontinuous value in dbc field.
+        *
+        * This 'struct completion' is used to call .update() at first to update
+        * connections/streams. Next following codes handle streaming error.
+        */
+       if (amdtp_streaming_error(&bebob->tx_stream)) {
+               if (completion_done(&bebob->bus_reset))
+                       reinit_completion(&bebob->bus_reset);
+
+               updated = (wait_for_completion_interruptible_timeout(
+                               &bebob->bus_reset,
+                               msecs_to_jiffies(FW_ISO_RESOURCE_DELAY)) > 0);
+       }
+
+       mutex_lock(&bebob->mutex);
+
+       /* Need no substreams */
+       if (atomic_read(&bebob->playback_substreams) == 0 &&
+           atomic_read(&bebob->capture_substreams)  == 0)
+               goto end;
+
+       err = get_sync_mode(bebob, &sync_mode);
+       if (err < 0)
+               goto end;
+       if (sync_mode == CIP_SYNC_TO_DEVICE) {
+               master = &bebob->tx_stream;
+               slave  = &bebob->rx_stream;
+               slave_substreams = &bebob->playback_substreams;
+       } else {
+               master = &bebob->rx_stream;
+               slave  = &bebob->tx_stream;
+               slave_substreams = &bebob->capture_substreams;
+       }
+
+       /*
+        * Considering JACK/FFADO streaming:
+        * TODO: This can be removed hwdep functionality becomes popular.
+        */
+       err = check_connection_used_by_others(bebob, master);
+       if (err < 0)
+               goto end;
+
+       /*
+        * packet queueing error or detecting discontinuity
+        *
+        * At bus reset, connections should not be broken here. So streams need
+        * to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag.
+        */
+       if (amdtp_streaming_error(master))
+               amdtp_stream_stop(master);
+       if (amdtp_streaming_error(slave))
+               amdtp_stream_stop(slave);
+       if (!updated &&
+           !amdtp_stream_running(master) && !amdtp_stream_running(slave))
+               break_both_connections(bebob);
+
+       /* stop streams if rate is different */
+       err = rate_spec->get(bebob, &curr_rate);
+       if (err < 0) {
+               dev_err(&bebob->unit->device,
+                       "fail to get sampling rate: %d\n", err);
+               goto end;
+       }
+       if (rate == 0)
+               rate = curr_rate;
+       if (rate != curr_rate) {
+               amdtp_stream_stop(master);
+               amdtp_stream_stop(slave);
+               break_both_connections(bebob);
+       }
+
+       /* master should be always running */
+       if (!amdtp_stream_running(master)) {
+               amdtp_stream_set_sync(sync_mode, master, slave);
+               bebob->master = master;
+
+               /*
+                * NOTE:
+                * If establishing connections at first, Yamaha GO46
+                * (and maybe Terratec X24) don't generate sound.
+                *
+                * For firmware customized by M-Audio, refer to next NOTE.
+                */
+               if (bebob->maudio_special_quirk == NULL) {
+                       err = rate_spec->set(bebob, rate);
+                       if (err < 0) {
+                               dev_err(&bebob->unit->device,
+                                       "fail to set sampling rate: %d\n",
+                                       err);
+                               goto end;
+                       }
+               }
+
+               err = make_both_connections(bebob, rate);
+               if (err < 0)
+                       goto end;
+
+               err = start_stream(bebob, master, rate);
+               if (err < 0) {
+                       dev_err(&bebob->unit->device,
+                               "fail to run AMDTP master stream:%d\n", err);
+                       break_both_connections(bebob);
+                       goto end;
+               }
+
+               /*
+                * NOTE:
+                * The firmware customized by M-Audio uses these commands to
+                * start transmitting stream. This is not usual way.
+                */
+               if (bebob->maudio_special_quirk != NULL) {
+                       err = rate_spec->set(bebob, rate);
+                       if (err < 0) {
+                               dev_err(&bebob->unit->device,
+                                       "fail to ensure sampling rate: %d\n",
+                                       err);
+                               amdtp_stream_stop(master);
+                               break_both_connections(bebob);
+                               goto end;
+                       }
+               }
+
+               /* wait first callback */
+               if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT)) {
+                       amdtp_stream_stop(master);
+                       break_both_connections(bebob);
+                       err = -ETIMEDOUT;
+                       goto end;
+               }
+       }
+
+       /* start slave if needed */
+       if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) {
+               err = start_stream(bebob, slave, rate);
+               if (err < 0) {
+                       dev_err(&bebob->unit->device,
+                               "fail to run AMDTP slave stream:%d\n", err);
+                       amdtp_stream_stop(master);
+                       break_both_connections(bebob);
+                       goto end;
+               }
+
+               /* wait first callback */
+               if (!amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) {
+                       amdtp_stream_stop(slave);
+                       amdtp_stream_stop(master);
+                       break_both_connections(bebob);
+                       err = -ETIMEDOUT;
+               }
+       }
+end:
+       mutex_unlock(&bebob->mutex);
+       return err;
+}
+
+void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
+{
+       struct amdtp_stream *master, *slave;
+       atomic_t *master_substreams, *slave_substreams;
+
+       mutex_lock(&bebob->mutex);
+
+       if (bebob->master == &bebob->rx_stream) {
+               slave  = &bebob->tx_stream;
+               master = &bebob->rx_stream;
+               slave_substreams  = &bebob->capture_substreams;
+               master_substreams = &bebob->playback_substreams;
+       } else {
+               slave  = &bebob->rx_stream;
+               master = &bebob->tx_stream;
+               slave_substreams  = &bebob->playback_substreams;
+               master_substreams = &bebob->capture_substreams;
+       }
+
+       if (atomic_read(slave_substreams) == 0) {
+               amdtp_stream_pcm_abort(slave);
+               amdtp_stream_stop(slave);
+
+               if (atomic_read(master_substreams) == 0) {
+                       amdtp_stream_pcm_abort(master);
+                       amdtp_stream_stop(master);
+                       break_both_connections(bebob);
+               }
+       }
+
+       mutex_unlock(&bebob->mutex);
+}
+
+void snd_bebob_stream_update_duplex(struct snd_bebob *bebob)
+{
+       /* vs. XRUN recovery due to discontinuity at bus reset */
+       mutex_lock(&bebob->mutex);
+
+       if ((cmp_connection_update(&bebob->in_conn) < 0) ||
+           (cmp_connection_update(&bebob->out_conn) < 0)) {
+               amdtp_stream_pcm_abort(&bebob->rx_stream);
+               amdtp_stream_pcm_abort(&bebob->tx_stream);
+               amdtp_stream_stop(&bebob->rx_stream);
+               amdtp_stream_stop(&bebob->tx_stream);
+               break_both_connections(bebob);
+       } else {
+               amdtp_stream_update(&bebob->rx_stream);
+               amdtp_stream_update(&bebob->tx_stream);
+       }
+
+       /* wake up stream_start_duplex() */
+       if (!completion_done(&bebob->bus_reset))
+               complete_all(&bebob->bus_reset);
+
+       mutex_unlock(&bebob->mutex);
+}
+
+void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob)
+{
+       mutex_lock(&bebob->mutex);
+
+       amdtp_stream_pcm_abort(&bebob->rx_stream);
+       amdtp_stream_pcm_abort(&bebob->tx_stream);
+
+       amdtp_stream_stop(&bebob->rx_stream);
+       amdtp_stream_stop(&bebob->tx_stream);
+
+       amdtp_stream_destroy(&bebob->rx_stream);
+       amdtp_stream_destroy(&bebob->tx_stream);
+
+       destroy_both_connections(bebob);
+
+       mutex_unlock(&bebob->mutex);
+}
+
+/*
+ * See: Table 50: Extended Stream Format Info Format Hierarchy Level 2’
+ * in Additional AVC commands (Nov 2003, BridgeCo)
+ * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005
+ */
+static int
+parse_stream_formation(u8 *buf, unsigned int len,
+                      struct snd_bebob_stream_formation *formation)
+{
+       unsigned int i, e, channels, format;
+
+       /*
+        * this module can support a hierarchy combination that:
+        *  Root:       Audio and Music (0x90)
+        *  Level 1:    AM824 Compound  (0x40)
+        */
+       if ((buf[0] != 0x90) || (buf[1] != 0x40))
+               return -ENOSYS;
+
+       /* check sampling rate */
+       for (i = 0; i < ARRAY_SIZE(bridgeco_freq_table); i++) {
+               if (buf[2] == bridgeco_freq_table[i])
+                       break;
+       }
+       if (i == ARRAY_SIZE(bridgeco_freq_table))
+               return -ENOSYS;
+
+       /* Avoid double count by different entries for the same rate. */
+       memset(&formation[i], 0, sizeof(struct snd_bebob_stream_formation));
+
+       for (e = 0; e < buf[4]; e++) {
+               channels = buf[5 + e * 2];
+               format = buf[6 + e * 2];
+
+               switch (format) {
+               /* IEC 60958 Conformant, currently handled as MBLA */
+               case 0x00:
+               /* Multi bit linear audio */
+               case 0x06:      /* Raw */
+                       formation[i].pcm += channels;
+                       break;
+               /* MIDI Conformant */
+               case 0x0d:
+                       formation[i].midi += channels;
+                       break;
+               /* IEC 61937-3 to 7 */
+               case 0x01:
+               case 0x02:
+               case 0x03:
+               case 0x04:
+               case 0x05:
+               /* Multi bit linear audio */
+               case 0x07:      /* DVD-Audio */
+               case 0x0c:      /* High Precision */
+               /* One Bit Audio */
+               case 0x08:      /* (Plain) Raw */
+               case 0x09:      /* (Plain) SACD */
+               case 0x0a:      /* (Encoded) Raw */
+               case 0x0b:      /* (Encoded) SACD */
+               /* Synchronization Stream (Stereo Raw audio) */
+               case 0x40:
+               /* Don't care */
+               case 0xff:
+               default:
+                       return -ENOSYS; /* not supported */
+               }
+       }
+
+       if (formation[i].pcm  > AMDTP_MAX_CHANNELS_FOR_PCM ||
+           formation[i].midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+               return -ENOSYS;
+
+       return 0;
+}
+
+static int
+fill_stream_formations(struct snd_bebob *bebob, enum avc_bridgeco_plug_dir dir,
+                      unsigned short pid)
+{
+       u8 *buf;
+       struct snd_bebob_stream_formation *formations;
+       unsigned int len, eid;
+       u8 addr[AVC_BRIDGECO_ADDR_BYTES];
+       int err;
+
+       buf = kmalloc(FORMAT_MAXIMUM_LENGTH, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       if (dir == AVC_BRIDGECO_PLUG_DIR_IN)
+               formations = bebob->rx_stream_formations;
+       else
+               formations = bebob->tx_stream_formations;
+
+       for (eid = 0; eid < SND_BEBOB_STRM_FMT_ENTRIES; eid++) {
+               len = FORMAT_MAXIMUM_LENGTH;
+               avc_bridgeco_fill_unit_addr(addr, dir,
+                                           AVC_BRIDGECO_PLUG_UNIT_ISOC, pid);
+               err = avc_bridgeco_get_plug_strm_fmt(bebob->unit, addr, buf,
+                                                    &len, eid);
+               /* No entries remained. */
+               if (err == -EINVAL && eid > 0) {
+                       err = 0;
+                       break;
+               } else if (err < 0) {
+                       dev_err(&bebob->unit->device,
+                       "fail to get stream format %d for isoc %s plug %d:%d\n",
+                               eid,
+                               (dir == AVC_BRIDGECO_PLUG_DIR_IN) ? "in" :
+                                                                   "out",
+                               pid, err);
+                       break;
+               }
+
+               err = parse_stream_formation(buf, len, formations);
+               if (err < 0)
+                       break;
+       }
+
+       kfree(buf);
+       return err;
+}
+
+static int
+seek_msu_sync_input_plug(struct snd_bebob *bebob)
+{
+       u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
+       unsigned int i;
+       enum avc_bridgeco_plug_type type;
+       int err;
+
+       /* Get the number of Music Sub Unit for both direction. */
+       err = avc_general_get_plug_info(bebob->unit, 0x0c, 0x00, 0x00, plugs);
+       if (err < 0) {
+               dev_err(&bebob->unit->device,
+                       "fail to get info for MSU in/out plugs: %d\n",
+                       err);
+               goto end;
+       }
+
+       /* seek destination plugs for 'MSU sync input' */
+       bebob->sync_input_plug = -1;
+       for (i = 0; i < plugs[0]; i++) {
+               avc_bridgeco_fill_msu_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN, i);
+               err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+               if (err < 0) {
+                       dev_err(&bebob->unit->device,
+                               "fail to get type for MSU in plug %d: %d\n",
+                               i, err);
+                       goto end;
+               }
+
+               if (type == AVC_BRIDGECO_PLUG_TYPE_SYNC) {
+                       bebob->sync_input_plug = i;
+                       break;
+               }
+       }
+end:
+       return err;
+}
+
+int snd_bebob_stream_discover(struct snd_bebob *bebob)
+{
+       struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+       u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
+       enum avc_bridgeco_plug_type type;
+       unsigned int i;
+       int err;
+
+       /* the number of plugs for isoc in/out, ext in/out  */
+       err = avc_general_get_plug_info(bebob->unit, 0x1f, 0x07, 0x00, plugs);
+       if (err < 0) {
+               dev_err(&bebob->unit->device,
+               "fail to get info for isoc/external in/out plugs: %d\n",
+                       err);
+               goto end;
+       }
+
+       /*
+        * This module supports at least one isoc input plug and one isoc
+        * output plug.
+        */
+       if ((plugs[0] == 0) || (plugs[1] == 0)) {
+               err = -ENOSYS;
+               goto end;
+       }
+
+       avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN,
+                                   AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+       err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+       if (err < 0) {
+               dev_err(&bebob->unit->device,
+                       "fail to get type for isoc in plug 0: %d\n", err);
+               goto end;
+       } else if (type != AVC_BRIDGECO_PLUG_TYPE_ISOC) {
+               err = -ENOSYS;
+               goto end;
+       }
+       err = fill_stream_formations(bebob, AVC_BRIDGECO_PLUG_DIR_IN, 0);
+       if (err < 0)
+               goto end;
+
+       avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_OUT,
+                                   AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+       err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+       if (err < 0) {
+               dev_err(&bebob->unit->device,
+                       "fail to get type for isoc out plug 0: %d\n", err);
+               goto end;
+       } else if (type != AVC_BRIDGECO_PLUG_TYPE_ISOC) {
+               err = -ENOSYS;
+               goto end;
+       }
+       err = fill_stream_formations(bebob, AVC_BRIDGECO_PLUG_DIR_OUT, 0);
+       if (err < 0)
+               goto end;
+
+       /* count external input plugs for MIDI */
+       bebob->midi_input_ports = 0;
+       for (i = 0; i < plugs[2]; i++) {
+               avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN,
+                                           AVC_BRIDGECO_PLUG_UNIT_EXT, i);
+               err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+               if (err < 0) {
+                       dev_err(&bebob->unit->device,
+                       "fail to get type for external in plug %d: %d\n",
+                               i, err);
+                       goto end;
+               } else if (type == AVC_BRIDGECO_PLUG_TYPE_MIDI) {
+                       bebob->midi_input_ports++;
+               }
+       }
+
+       /* count external output plugs for MIDI */
+       bebob->midi_output_ports = 0;
+       for (i = 0; i < plugs[3]; i++) {
+               avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_OUT,
+                                           AVC_BRIDGECO_PLUG_UNIT_EXT, i);
+               err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+               if (err < 0) {
+                       dev_err(&bebob->unit->device,
+                       "fail to get type for external out plug %d: %d\n",
+                               i, err);
+                       goto end;
+               } else if (type == AVC_BRIDGECO_PLUG_TYPE_MIDI) {
+                       bebob->midi_output_ports++;
+               }
+       }
+
+       /* for check source of clock later */
+       if (!clk_spec)
+               err = seek_msu_sync_input_plug(bebob);
+end:
+       return err;
+}
+
+void snd_bebob_stream_lock_changed(struct snd_bebob *bebob)
+{
+       bebob->dev_lock_changed = true;
+       wake_up(&bebob->hwdep_wait);
+}
+
+int snd_bebob_stream_lock_try(struct snd_bebob *bebob)
+{
+       int err;
+
+       spin_lock_irq(&bebob->lock);
+
+       /* user land lock this */
+       if (bebob->dev_lock_count < 0) {
+               err = -EBUSY;
+               goto end;
+       }
+
+       /* this is the first time */
+       if (bebob->dev_lock_count++ == 0)
+               snd_bebob_stream_lock_changed(bebob);
+       err = 0;
+end:
+       spin_unlock_irq(&bebob->lock);
+       return err;
+}
+
+void snd_bebob_stream_lock_release(struct snd_bebob *bebob)
+{
+       spin_lock_irq(&bebob->lock);
+
+       if (WARN_ON(bebob->dev_lock_count <= 0))
+               goto end;
+       if (--bebob->dev_lock_count == 0)
+               snd_bebob_stream_lock_changed(bebob);
+end:
+       spin_unlock_irq(&bebob->lock);
+}
diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c
new file mode 100644 (file)
index 0000000..eef8ea7
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * bebob_terratec.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+static char *const phase88_rack_clk_src_labels[] = {
+       SND_BEBOB_CLOCK_INTERNAL, "Digital In", "Word Clock"
+};
+static int
+phase88_rack_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+       unsigned int enable_ext, enable_word;
+       int err;
+
+       err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_ext);
+       if (err < 0)
+               goto end;
+       err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_word);
+       if (err < 0)
+               goto end;
+
+       *id = (enable_ext & 0x01) | ((enable_word & 0x01) << 1);
+end:
+       return err;
+}
+
+static char *const phase24_series_clk_src_labels[] = {
+       SND_BEBOB_CLOCK_INTERNAL, "Digital In"
+};
+static int
+phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+       return avc_audio_get_selector(bebob->unit, 0, 4, id);
+}
+
+static struct snd_bebob_rate_spec phase_series_rate_spec = {
+       .get    = &snd_bebob_stream_get_rate,
+       .set    = &snd_bebob_stream_set_rate,
+};
+
+/* PHASE 88 Rack FW */
+static struct snd_bebob_clock_spec phase88_rack_clk = {
+       .num    = ARRAY_SIZE(phase88_rack_clk_src_labels),
+       .labels = phase88_rack_clk_src_labels,
+       .get    = &phase88_rack_clk_src_get,
+};
+struct snd_bebob_spec phase88_rack_spec = {
+       .clock  = &phase88_rack_clk,
+       .rate   = &phase_series_rate_spec,
+       .meter  = NULL
+};
+
+/* 'PHASE 24 FW' and 'PHASE X24 FW' */
+static struct snd_bebob_clock_spec phase24_series_clk = {
+       .num    = ARRAY_SIZE(phase24_series_clk_src_labels),
+       .labels = phase24_series_clk_src_labels,
+       .get    = &phase24_series_clk_src_get,
+};
+struct snd_bebob_spec phase24_series_spec = {
+       .clock  = &phase24_series_clk,
+       .rate   = &phase_series_rate_spec,
+       .meter  = NULL
+};
diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c
new file mode 100644 (file)
index 0000000..9b7e798
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * bebob_yamaha.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+/*
+ * NOTE:
+ * Yamaha GO44 is not designed to be used as stand-alone mixer. So any streams
+ * must be accompanied. If changing the state, a LED on the device starts to
+ * blink and its sync status is false. In this state, the device sounds nothing
+ * even if streaming. To start streaming at the current sampling rate is only
+ * way to revocer this state. GO46 is better for stand-alone mixer.
+ *
+ * Both of them have a capability to change its sampling rate up to 192.0kHz.
+ * At 192.0kHz, the device reports 4 PCM-in, 1 MIDI-in, 6 PCM-out, 1 MIDI-out.
+ * But Yamaha's driver reduce 2 PCM-in, 1 MIDI-in, 2 PCM-out, 1 MIDI-out to use
+ * 'Extended Stream Format Information Command - Single Request' in 'Additional
+ * AVC commands' defined by BridgeCo.
+ * This ALSA driver don't do this because a bit tiresome. Then isochronous
+ * streaming with many asynchronous transactions brings sounds with noises.
+ * Unfortunately current 'ffado-mixer' generated many asynchronous transaction
+ * to observe device's state, mainly check cmp connection and signal format. I
+ * reccomend users to close ffado-mixer at 192.0kHz if mixer is needless.
+ */
+
+static char *const clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"};
+static int
+clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+       return avc_audio_get_selector(bebob->unit, 0, 4, id);
+}
+static struct snd_bebob_clock_spec clock_spec = {
+       .num    = ARRAY_SIZE(clk_src_labels),
+       .labels = clk_src_labels,
+       .get    = &clk_src_get,
+};
+static struct snd_bebob_rate_spec rate_spec = {
+       .get    = &snd_bebob_stream_get_rate,
+       .set    = &snd_bebob_stream_set_rate,
+};
+struct snd_bebob_spec yamaha_go_spec = {
+       .clock  = &clock_spec,
+       .rate   = &rate_spec,
+       .meter  = NULL
+};
index efdbf58..ba8df5a 100644 (file)
 #include "iso-resources.h"
 #include "cmp.h"
 
-#define IMPR_SPEED_MASK                0xc0000000
-#define IMPR_SPEED_SHIFT       30
-#define IMPR_XSPEED_MASK       0x00000060
-#define IMPR_XSPEED_SHIFT      5
-#define IMPR_PLUGS_MASK                0x0000001f
-
-#define IPCR_ONLINE            0x80000000
-#define IPCR_BCAST_CONN                0x40000000
-#define IPCR_P2P_CONN_MASK     0x3f000000
-#define IPCR_P2P_CONN_SHIFT    24
-#define IPCR_CHANNEL_MASK      0x003f0000
-#define IPCR_CHANNEL_SHIFT     16
+/* MPR common fields */
+#define MPR_SPEED_MASK         0xc0000000
+#define MPR_SPEED_SHIFT                30
+#define MPR_XSPEED_MASK                0x00000060
+#define MPR_XSPEED_SHIFT       5
+#define MPR_PLUGS_MASK         0x0000001f
+
+/* PCR common fields */
+#define PCR_ONLINE             0x80000000
+#define PCR_BCAST_CONN         0x40000000
+#define PCR_P2P_CONN_MASK      0x3f000000
+#define PCR_P2P_CONN_SHIFT     24
+#define PCR_CHANNEL_MASK       0x003f0000
+#define PCR_CHANNEL_SHIFT      16
+
+/* oPCR specific fields */
+#define OPCR_XSPEED_MASK       0x00C00000
+#define OPCR_XSPEED_SHIFT      22
+#define OPCR_SPEED_MASK                0x0000C000
+#define OPCR_SPEED_SHIFT       14
+#define OPCR_OVERHEAD_ID_MASK  0x00003C00
+#define OPCR_OVERHEAD_ID_SHIFT 10
 
 enum bus_reset_handling {
        ABORT_ON_BUS_RESET,
@@ -39,10 +49,27 @@ void cmp_error(struct cmp_connection *c, const char *fmt, ...)
 
        va_start(va, fmt);
        dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
-               'i', c->pcr_index, &(struct va_format){ fmt, &va });
+               (c->direction == CMP_INPUT) ? 'i' : 'o',
+               c->pcr_index, &(struct va_format){ fmt, &va });
        va_end(va);
 }
 
+static u64 mpr_address(struct cmp_connection *c)
+{
+       if (c->direction == CMP_INPUT)
+               return CSR_REGISTER_BASE + CSR_IMPR;
+       else
+               return CSR_REGISTER_BASE + CSR_OMPR;
+}
+
+static u64 pcr_address(struct cmp_connection *c)
+{
+       if (c->direction == CMP_INPUT)
+               return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index);
+       else
+               return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index);
+}
+
 static int pcr_modify(struct cmp_connection *c,
                      __be32 (*modify)(struct cmp_connection *c, __be32 old),
                      int (*check)(struct cmp_connection *c, __be32 pcr),
@@ -58,8 +85,7 @@ static int pcr_modify(struct cmp_connection *c,
 
                err = snd_fw_transaction(
                                c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
-                               CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index),
-                               buffer, 8,
+                               pcr_address(c), buffer, 8,
                                FW_FIXED_GENERATION | c->resources.generation);
 
                if (err < 0) {
@@ -88,24 +114,25 @@ static int pcr_modify(struct cmp_connection *c,
  * cmp_connection_init - initializes a connection manager
  * @c: the connection manager to initialize
  * @unit: a unit of the target device
- * @ipcr_index: the index of the iPCR on the target device
+ * @pcr_index: the index of the iPCR/oPCR on the target device
  */
 int cmp_connection_init(struct cmp_connection *c,
                        struct fw_unit *unit,
-                       unsigned int ipcr_index)
+                       enum cmp_direction direction,
+                       unsigned int pcr_index)
 {
-       __be32 impr_be;
-       u32 impr;
+       __be32 mpr_be;
+       u32 mpr;
        int err;
 
+       c->direction = direction;
        err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
-                                CSR_REGISTER_BASE + CSR_IMPR,
-                                &impr_be, 4, 0);
+                                mpr_address(c), &mpr_be, 4, 0);
        if (err < 0)
                return err;
-       impr = be32_to_cpu(impr_be);
+       mpr = be32_to_cpu(mpr_be);
 
-       if (ipcr_index >= (impr & IMPR_PLUGS_MASK))
+       if (pcr_index >= (mpr & MPR_PLUGS_MASK))
                return -EINVAL;
 
        err = fw_iso_resources_init(&c->resources, unit);
@@ -115,15 +142,35 @@ int cmp_connection_init(struct cmp_connection *c,
        c->connected = false;
        mutex_init(&c->mutex);
        c->last_pcr_value = cpu_to_be32(0x80000000);
-       c->pcr_index = ipcr_index;
-       c->max_speed = (impr & IMPR_SPEED_MASK) >> IMPR_SPEED_SHIFT;
+       c->pcr_index = pcr_index;
+       c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
        if (c->max_speed == SCODE_BETA)
-               c->max_speed += (impr & IMPR_XSPEED_MASK) >> IMPR_XSPEED_SHIFT;
+               c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
 
        return 0;
 }
 EXPORT_SYMBOL(cmp_connection_init);
 
+/**
+ * cmp_connection_check_used - check connection is already esablished or not
+ * @c: the connection manager to be checked
+ */
+int cmp_connection_check_used(struct cmp_connection *c, bool *used)
+{
+       __be32 pcr;
+       int err;
+
+       err = snd_fw_transaction(
+                       c->resources.unit, TCODE_READ_QUADLET_REQUEST,
+                       pcr_address(c), &pcr, 4, 0);
+       if (err >= 0)
+               *used = !!(pcr & cpu_to_be32(PCR_BCAST_CONN |
+                                            PCR_P2P_CONN_MASK));
+
+       return err;
+}
+EXPORT_SYMBOL(cmp_connection_check_used);
+
 /**
  * cmp_connection_destroy - free connection manager resources
  * @c: the connection manager
@@ -139,23 +186,70 @@ EXPORT_SYMBOL(cmp_connection_destroy);
 
 static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
 {
-       ipcr &= ~cpu_to_be32(IPCR_BCAST_CONN |
-                            IPCR_P2P_CONN_MASK |
-                            IPCR_CHANNEL_MASK);
-       ipcr |= cpu_to_be32(1 << IPCR_P2P_CONN_SHIFT);
-       ipcr |= cpu_to_be32(c->resources.channel << IPCR_CHANNEL_SHIFT);
+       ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
+                            PCR_P2P_CONN_MASK |
+                            PCR_CHANNEL_MASK);
+       ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
+       ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
 
        return ipcr;
 }
 
-static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr)
+static int get_overhead_id(struct cmp_connection *c)
 {
-       if (ipcr & cpu_to_be32(IPCR_BCAST_CONN |
-                              IPCR_P2P_CONN_MASK)) {
+       int id;
+
+       /*
+        * apply "oPCR overhead ID encoding"
+        * the encoding table can convert up to 512.
+        * here the value over 512 is converted as the same way as 512.
+        */
+       for (id = 1; id < 16; id++) {
+               if (c->resources.bandwidth_overhead < (id << 5))
+                       break;
+       }
+       if (id == 16)
+               id = 0;
+
+       return id;
+}
+
+static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
+{
+       unsigned int spd, xspd;
+
+       /* generate speed and extended speed field value */
+       if (c->speed > SCODE_400) {
+               spd  = SCODE_800;
+               xspd = c->speed - SCODE_800;
+       } else {
+               spd = c->speed;
+               xspd = 0;
+       }
+
+       opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
+                            PCR_P2P_CONN_MASK |
+                            OPCR_XSPEED_MASK |
+                            PCR_CHANNEL_MASK |
+                            OPCR_SPEED_MASK |
+                            OPCR_OVERHEAD_ID_MASK);
+       opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
+       opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
+       opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
+       opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
+       opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
+
+       return opcr;
+}
+
+static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
+{
+       if (pcr & cpu_to_be32(PCR_BCAST_CONN |
+                             PCR_P2P_CONN_MASK)) {
                cmp_error(c, "plug is already in use\n");
                return -EBUSY;
        }
-       if (!(ipcr & cpu_to_be32(IPCR_ONLINE))) {
+       if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
                cmp_error(c, "plug is not on-line\n");
                return -ECONNREFUSED;
        }
@@ -170,9 +264,9 @@ static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr)
  *
  * This function establishes a point-to-point connection from the local
  * computer to the target by allocating isochronous resources (channel and
- * bandwidth) and setting the target's input plug control register.  When this
- * function succeeds, the caller is responsible for starting transmitting
- * packets.
+ * bandwidth) and setting the target's input/output plug control register.
+ * When this function succeeds, the caller is responsible for starting
+ * transmitting packets.
  */
 int cmp_connection_establish(struct cmp_connection *c,
                             unsigned int max_payload_bytes)
@@ -193,8 +287,13 @@ retry_after_bus_reset:
        if (err < 0)
                goto err_mutex;
 
-       err = pcr_modify(c, ipcr_set_modify, ipcr_set_check,
-                        ABORT_ON_BUS_RESET);
+       if (c->direction == CMP_OUTPUT)
+               err = pcr_modify(c, opcr_set_modify, pcr_set_check,
+                                ABORT_ON_BUS_RESET);
+       else
+               err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
+                                ABORT_ON_BUS_RESET);
+
        if (err == -EAGAIN) {
                fw_iso_resources_free(&c->resources);
                goto retry_after_bus_reset;
@@ -221,8 +320,8 @@ EXPORT_SYMBOL(cmp_connection_establish);
  * cmp_connection_update - update the connection after a bus reset
  * @c: the connection manager
  *
- * This function must be called from the driver's .update handler to reestablish
- * any connection that might have been active.
+ * This function must be called from the driver's .update handler to
+ * reestablish any connection that might have been active.
  *
  * Returns zero on success, or a negative error code.  On an error, the
  * connection is broken and the caller must stop transmitting iso packets.
@@ -242,8 +341,13 @@ int cmp_connection_update(struct cmp_connection *c)
        if (err < 0)
                goto err_unconnect;
 
-       err = pcr_modify(c, ipcr_set_modify, ipcr_set_check,
-                        SUCCEED_ON_BUS_RESET);
+       if (c->direction == CMP_OUTPUT)
+               err = pcr_modify(c, opcr_set_modify, pcr_set_check,
+                                SUCCEED_ON_BUS_RESET);
+       else
+               err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
+                                SUCCEED_ON_BUS_RESET);
+
        if (err < 0)
                goto err_resources;
 
@@ -261,19 +365,18 @@ err_unconnect:
 }
 EXPORT_SYMBOL(cmp_connection_update);
 
-
-static __be32 ipcr_break_modify(struct cmp_connection *c, __be32 ipcr)
+static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
 {
-       return ipcr & ~cpu_to_be32(IPCR_BCAST_CONN | IPCR_P2P_CONN_MASK);
+       return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
 }
 
 /**
  * cmp_connection_break - break the connection to the target
  * @c: the connection manager
  *
- * This function deactives the connection in the target's input plug control
- * register, and frees the isochronous resources of the connection.  Before
- * calling this function, the caller should cease transmitting packets.
+ * This function deactives the connection in the target's input/output plug
+ * control register, and frees the isochronous resources of the connection.
+ * Before calling this function, the caller should cease transmitting packets.
  */
 void cmp_connection_break(struct cmp_connection *c)
 {
@@ -286,7 +389,7 @@ void cmp_connection_break(struct cmp_connection *c)
                return;
        }
 
-       err = pcr_modify(c, ipcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
+       err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
        if (err < 0)
                cmp_error(c, "plug is still connected\n");
 
index f47de08..ebcb484 100644 (file)
@@ -7,12 +7,17 @@
 
 struct fw_unit;
 
+enum cmp_direction {
+       CMP_INPUT = 0,
+       CMP_OUTPUT,
+};
+
 /**
  * struct cmp_connection - manages an isochronous connection to a device
  * @speed: the connection's actual speed
  *
- * This structure manages (using CMP) an isochronous stream from the local
- * computer to a device's input plug (iPCR).
+ * This structure manages (using CMP) an isochronous stream between the local
+ * computer and a device's input plug (iPCR) and output plug (oPCR).
  *
  * There is no corresponding oPCR created on the local computer, so it is not
  * possible to overlay connections on top of this one.
@@ -26,11 +31,14 @@ struct cmp_connection {
        __be32 last_pcr_value;
        unsigned int pcr_index;
        unsigned int max_speed;
+       enum cmp_direction direction;
 };
 
 int cmp_connection_init(struct cmp_connection *connection,
                        struct fw_unit *unit,
-                       unsigned int ipcr_index);
+                       enum cmp_direction direction,
+                       unsigned int pcr_index);
+int cmp_connection_check_used(struct cmp_connection *connection, bool *used);
 void cmp_connection_destroy(struct cmp_connection *connection);
 
 int cmp_connection_establish(struct cmp_connection *connection,
index 0c39486..a9a30c0 100644 (file)
@@ -51,7 +51,7 @@ struct dice {
        wait_queue_head_t hwdep_wait;
        u32 notification_bits;
        struct fw_iso_resources resources;
-       struct amdtp_out_stream stream;
+       struct amdtp_stream stream;
 };
 
 MODULE_DESCRIPTION("DICE driver");
@@ -420,22 +420,7 @@ static int dice_open(struct snd_pcm_substream *substream)
        if (err < 0)
                goto err_lock;
 
-       err = snd_pcm_hw_constraint_step(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
-       if (err < 0)
-               goto err_lock;
-       err = snd_pcm_hw_constraint_step(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
-       if (err < 0)
-               goto err_lock;
-
-       err = snd_pcm_hw_constraint_minmax(runtime,
-                                          SNDRV_PCM_HW_PARAM_PERIOD_TIME,
-                                          5000, UINT_MAX);
-       if (err < 0)
-               goto err_lock;
-
-       err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+       err = amdtp_stream_add_pcm_hw_constraints(&dice->stream, runtime);
        if (err < 0)
                goto err_lock;
 
@@ -460,17 +445,17 @@ static int dice_stream_start_packets(struct dice *dice)
 {
        int err;
 
-       if (amdtp_out_stream_running(&dice->stream))
+       if (amdtp_stream_running(&dice->stream))
                return 0;
 
-       err = amdtp_out_stream_start(&dice->stream, dice->resources.channel,
-                                    fw_parent_device(dice->unit)->max_speed);
+       err = amdtp_stream_start(&dice->stream, dice->resources.channel,
+                                fw_parent_device(dice->unit)->max_speed);
        if (err < 0)
                return err;
 
        err = dice_enable_set(dice);
        if (err < 0) {
-               amdtp_out_stream_stop(&dice->stream);
+               amdtp_stream_stop(&dice->stream);
                return err;
        }
 
@@ -484,7 +469,7 @@ static int dice_stream_start(struct dice *dice)
 
        if (!dice->resources.allocated) {
                err = fw_iso_resources_allocate(&dice->resources,
-                               amdtp_out_stream_get_max_payload(&dice->stream),
+                               amdtp_stream_get_max_payload(&dice->stream),
                                fw_parent_device(dice->unit)->max_speed);
                if (err < 0)
                        goto error;
@@ -516,9 +501,9 @@ error:
 
 static void dice_stream_stop_packets(struct dice *dice)
 {
-       if (amdtp_out_stream_running(&dice->stream)) {
+       if (amdtp_stream_running(&dice->stream)) {
                dice_enable_clear(dice);
-               amdtp_out_stream_stop(&dice->stream);
+               amdtp_stream_stop(&dice->stream);
        }
 }
 
@@ -563,7 +548,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
                          struct snd_pcm_hw_params *hw_params)
 {
        struct dice *dice = substream->private_data;
-       unsigned int rate_index, mode;
+       unsigned int rate_index, mode, rate, channels, i;
        int err;
 
        mutex_lock(&dice->mutex);
@@ -575,18 +560,39 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
        if (err < 0)
                return err;
 
-       rate_index = rate_to_index(params_rate(hw_params));
+       rate = params_rate(hw_params);
+       rate_index = rate_to_index(rate);
        err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
        if (err < 0)
                return err;
 
+       /*
+        * At rates above 96 kHz, pretend that the stream runs at half the
+        * actual sample rate with twice the number of channels; two samples
+        * of a channel are stored consecutively in the packet. Requires
+        * blocking mode and PCM buffer size should be aligned to SYT_INTERVAL.
+        */
+       channels = params_channels(hw_params);
+       if (rate_index > 4) {
+               if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) {
+                       err = -ENOSYS;
+                       return err;
+               }
+
+               for (i = 0; i < channels; i++) {
+                       dice->stream.pcm_positions[i * 2] = i;
+                       dice->stream.pcm_positions[i * 2 + 1] = i + channels;
+               }
+
+               rate /= 2;
+               channels *= 2;
+       }
+
        mode = rate_index_to_mode(rate_index);
-       amdtp_out_stream_set_parameters(&dice->stream,
-                                       params_rate(hw_params),
-                                       params_channels(hw_params),
-                                       dice->rx_midi_ports[mode]);
-       amdtp_out_stream_set_pcm_format(&dice->stream,
-                                       params_format(hw_params));
+       amdtp_stream_set_parameters(&dice->stream, rate, channels,
+                                   dice->rx_midi_ports[mode]);
+       amdtp_stream_set_pcm_format(&dice->stream,
+                                   params_format(hw_params));
 
        return 0;
 }
@@ -609,7 +615,7 @@ static int dice_prepare(struct snd_pcm_substream *substream)
 
        mutex_lock(&dice->mutex);
 
-       if (amdtp_out_streaming_error(&dice->stream))
+       if (amdtp_streaming_error(&dice->stream))
                dice_stream_stop_packets(dice);
 
        err = dice_stream_start(dice);
@@ -620,7 +626,7 @@ static int dice_prepare(struct snd_pcm_substream *substream)
 
        mutex_unlock(&dice->mutex);
 
-       amdtp_out_stream_pcm_prepare(&dice->stream);
+       amdtp_stream_pcm_prepare(&dice->stream);
 
        return 0;
 }
@@ -640,7 +646,7 @@ static int dice_trigger(struct snd_pcm_substream *substream, int cmd)
        default:
                return -EINVAL;
        }
-       amdtp_out_stream_pcm_trigger(&dice->stream, pcm);
+       amdtp_stream_pcm_trigger(&dice->stream, pcm);
 
        return 0;
 }
@@ -649,7 +655,7 @@ static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream)
 {
        struct dice *dice = substream->private_data;
 
-       return amdtp_out_stream_pcm_pointer(&dice->stream);
+       return amdtp_stream_pcm_pointer(&dice->stream);
 }
 
 static int dice_create_pcm(struct dice *dice)
@@ -1104,7 +1110,7 @@ static void dice_card_free(struct snd_card *card)
 {
        struct dice *dice = card->private_data;
 
-       amdtp_out_stream_destroy(&dice->stream);
+       amdtp_stream_destroy(&dice->stream);
        fw_core_remove_address_handler(&dice->notification_handler);
        mutex_destroy(&dice->mutex);
 }
@@ -1360,8 +1366,8 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
                goto err_owner;
        dice->resources.channels_mask = 0x00000000ffffffffuLL;
 
-       err = amdtp_out_stream_init(&dice->stream, unit,
-                                   CIP_BLOCKING | CIP_HI_DUALWIRE);
+       err = amdtp_stream_init(&dice->stream, unit, AMDTP_OUT_STREAM,
+                               CIP_BLOCKING);
        if (err < 0)
                goto err_resources;
 
@@ -1417,7 +1423,7 @@ static void dice_remove(struct fw_unit *unit)
 {
        struct dice *dice = dev_get_drvdata(&unit->device);
 
-       amdtp_out_stream_pcm_abort(&dice->stream);
+       amdtp_stream_pcm_abort(&dice->stream);
 
        snd_card_disconnect(dice->card);
 
@@ -1443,7 +1449,7 @@ static void dice_bus_reset(struct fw_unit *unit)
         * to stop so that the application can restart them in an orderly
         * manner.
         */
-       amdtp_out_stream_pcm_abort(&dice->stream);
+       amdtp_stream_pcm_abort(&dice->stream);
 
        mutex_lock(&dice->mutex);
 
index 860c080..0619597 100644 (file)
 #include <linux/firewire-constants.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <linux/wait.h>
 #include <linux/delay.h>
 #include "fcp.h"
 #include "lib.h"
+#include "amdtp.h"
 
 #define CTS_AVC 0x00
 
 #define ERROR_DELAY_MS 5
 #define FCP_TIMEOUT_MS 125
 
+int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
+                           enum avc_general_plug_dir dir,
+                           unsigned short pid)
+{
+       unsigned int sfc;
+       u8 *buf;
+       bool flag;
+       int err;
+
+       flag = false;
+       for (sfc = 0; sfc < CIP_SFC_COUNT; sfc++) {
+               if (amdtp_rate_table[sfc] == rate) {
+                       flag = true;
+                       break;
+               }
+       }
+       if (!flag)
+               return -EINVAL;
+
+       buf = kzalloc(8, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       buf[0] = 0x00;          /* AV/C CONTROL */
+       buf[1] = 0xff;          /* UNIT */
+       if (dir == AVC_GENERAL_PLUG_DIR_IN)
+               buf[2] = 0x19;  /* INPUT PLUG SIGNAL FORMAT */
+       else
+               buf[2] = 0x18;  /* OUTPUT PLUG SIGNAL FORMAT */
+       buf[3] = 0xff & pid;    /* plug id */
+       buf[4] = 0x90;          /* EOH_1, Form_1, FMT. AM824 */
+       buf[5] = 0x07 & sfc;    /* FDF-hi. AM824, frequency */
+       buf[6] = 0xff;          /* FDF-mid. AM824, SYT hi (not used)*/
+       buf[7] = 0xff;          /* FDF-low. AM824, SYT lo (not used) */
+
+       /* do transaction and check buf[1-5] are the same against command */
+       err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+                                 BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
+       if (err >= 0 && err < 8)
+               err = -EIO;
+       else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a) /* REJECTED */
+               err = -EINVAL;
+       if (err < 0)
+               goto end;
+
+       err = 0;
+end:
+       kfree(buf);
+       return err;
+}
+EXPORT_SYMBOL(avc_general_set_sig_fmt);
+
+int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
+                           enum avc_general_plug_dir dir,
+                           unsigned short pid)
+{
+       unsigned int sfc;
+       u8 *buf;
+       int err;
+
+       buf = kzalloc(8, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       buf[0] = 0x01;          /* AV/C STATUS */
+       buf[1] = 0xff;          /* Unit */
+       if (dir == AVC_GENERAL_PLUG_DIR_IN)
+               buf[2] = 0x19;  /* INPUT PLUG SIGNAL FORMAT */
+       else
+               buf[2] = 0x18;  /* OUTPUT PLUG SIGNAL FORMAT */
+       buf[3] = 0xff & pid;    /* plug id */
+       buf[4] = 0x90;          /* EOH_1, Form_1, FMT. AM824 */
+       buf[5] = 0xff;          /* FDF-hi. AM824, frequency */
+       buf[6] = 0xff;          /* FDF-mid. AM824, SYT hi (not used) */
+       buf[7] = 0xff;          /* FDF-low. AM824, SYT lo (not used) */
+
+       /* do transaction and check buf[1-4] are the same against command */
+       err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+                                 BIT(1) | BIT(2) | BIT(3) | BIT(4));
+       if (err >= 0 && err < 8)
+               err = -EIO;
+       else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a) /* REJECTED */
+               err = -EINVAL;
+       else if (buf[0] == 0x0b) /* IN TRANSITION */
+               err = -EAGAIN;
+       if (err < 0)
+               goto end;
+
+       /* check sfc field and pick up rate */
+       sfc = 0x07 & buf[5];
+       if (sfc >= CIP_SFC_COUNT) {
+               err = -EAGAIN;  /* also in transition */
+               goto end;
+       }
+
+       *rate = amdtp_rate_table[sfc];
+       err = 0;
+end:
+       kfree(buf);
+       return err;
+}
+EXPORT_SYMBOL(avc_general_get_sig_fmt);
+
+int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
+                             unsigned int subunit_id, unsigned int subfunction,
+                             u8 info[AVC_PLUG_INFO_BUF_BYTES])
+{
+       u8 *buf;
+       int err;
+
+       /* extended subunit in spec.4.2 is not supported */
+       if ((subunit_type == 0x1E) || (subunit_id == 5))
+               return -EINVAL;
+
+       buf = kzalloc(8, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       buf[0] = 0x01;  /* AV/C STATUS */
+       /* UNIT or Subunit, Functionblock */
+       buf[1] = ((subunit_type & 0x1f) << 3) | (subunit_id & 0x7);
+       buf[2] = 0x02;  /* PLUG INFO */
+       buf[3] = 0xff & subfunction;
+
+       err = fcp_avc_transaction(unit, buf, 8, buf, 8, BIT(1) | BIT(2));
+       if (err >= 0 && err < 8)
+               err = -EIO;
+       else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+               err = -ENOSYS;
+       else if (buf[0] == 0x0a) /* REJECTED */
+               err = -EINVAL;
+       else if (buf[0] == 0x0b) /* IN TRANSITION */
+               err = -EAGAIN;
+       if (err < 0)
+               goto end;
+
+       info[0] = buf[4];
+       info[1] = buf[5];
+       info[2] = buf[6];
+       info[3] = buf[7];
+
+       err = 0;
+end:
+       kfree(buf);
+       return err;
+}
+EXPORT_SYMBOL(avc_general_get_plug_info);
+
 static DEFINE_SPINLOCK(transactions_lock);
 static LIST_HEAD(transactions);
 
@@ -30,6 +184,7 @@ enum fcp_state {
        STATE_PENDING,
        STATE_BUS_RESET,
        STATE_COMPLETE,
+       STATE_DEFERRED,
 };
 
 struct fcp_transaction {
@@ -40,6 +195,7 @@ struct fcp_transaction {
        unsigned int response_match_bytes;
        enum fcp_state state;
        wait_queue_head_t wait;
+       bool deferrable;
 };
 
 /**
@@ -62,8 +218,6 @@ struct fcp_transaction {
  *
  * @command and @response can point to the same buffer.
  *
- * Asynchronous operation (INTERIM, NOTIFY) is not supported at the moment.
- *
  * Returns the actual size of the response frame, or a negative error code.
  */
 int fcp_avc_transaction(struct fw_unit *unit,
@@ -81,6 +235,9 @@ int fcp_avc_transaction(struct fw_unit *unit,
        t.state = STATE_PENDING;
        init_waitqueue_head(&t.wait);
 
+       if (*(const u8 *)command == 0x00 || *(const u8 *)command == 0x03)
+               t.deferrable = true;
+
        spin_lock_irq(&transactions_lock);
        list_add_tail(&t.list, &transactions);
        spin_unlock_irq(&transactions_lock);
@@ -93,11 +250,21 @@ int fcp_avc_transaction(struct fw_unit *unit,
                                         (void *)command, command_size, 0);
                if (ret < 0)
                        break;
-
+deferred:
                wait_event_timeout(t.wait, t.state != STATE_PENDING,
                                   msecs_to_jiffies(FCP_TIMEOUT_MS));
 
-               if (t.state == STATE_COMPLETE) {
+               if (t.state == STATE_DEFERRED) {
+                       /*
+                        * 'AV/C General Specification' define no time limit
+                        * on command completion once an INTERIM response has
+                        * been sent. but we promise to finish this function
+                        * for a caller. Here we use FCP_TIMEOUT_MS for next
+                        * interval. This is not in the specification.
+                        */
+                       t.state = STATE_PENDING;
+                       goto deferred;
+               } else if (t.state == STATE_COMPLETE) {
                        ret = t.response_size;
                        break;
                } else if (t.state == STATE_BUS_RESET) {
@@ -132,7 +299,8 @@ void fcp_bus_reset(struct fw_unit *unit)
        spin_lock_irq(&transactions_lock);
        list_for_each_entry(t, &transactions, list) {
                if (t->unit == unit &&
-                   t->state == STATE_PENDING) {
+                   (t->state == STATE_PENDING ||
+                    t->state == STATE_DEFERRED)) {
                        t->state = STATE_BUS_RESET;
                        wake_up(&t->wait);
                }
@@ -186,10 +354,15 @@ static void fcp_response(struct fw_card *card, struct fw_request *request,
 
                if (t->state == STATE_PENDING &&
                    is_matching_response(t, data, length)) {
-                       t->state = STATE_COMPLETE;
-                       t->response_size = min((unsigned int)length,
-                                              t->response_size);
-                       memcpy(t->response_buffer, data, t->response_size);
+                       if (t->deferrable && *(const u8 *)data == 0x0f) {
+                               t->state = STATE_DEFERRED;
+                       } else {
+                               t->state = STATE_COMPLETE;
+                               t->response_size = min_t(unsigned int, length,
+                                                        t->response_size);
+                               memcpy(t->response_buffer, data,
+                                      t->response_size);
+                       }
                        wake_up(&t->wait);
                }
        }
index 8659568..63ae4f7 100644 (file)
@@ -1,8 +1,29 @@
 #ifndef SOUND_FIREWIRE_FCP_H_INCLUDED
 #define SOUND_FIREWIRE_FCP_H_INCLUDED
 
+#define        AVC_PLUG_INFO_BUF_BYTES 4
+
 struct fw_unit;
 
+/*
+ * AV/C Digital Interface Command Set General Specification 4.2
+ * (Sep 2004, 1394TA)
+ */
+enum avc_general_plug_dir {
+       AVC_GENERAL_PLUG_DIR_IN         = 0,
+       AVC_GENERAL_PLUG_DIR_OUT        = 1,
+       AVC_GENERAL_PLUG_DIR_COUNT
+};
+int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
+                           enum avc_general_plug_dir dir,
+                           unsigned short plug);
+int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
+                           enum avc_general_plug_dir dir,
+                           unsigned short plug);
+int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
+                             unsigned int subunit_id, unsigned int subfunction,
+                             u8 info[AVC_PLUG_INFO_BUF_BYTES]);
+
 int fcp_avc_transaction(struct fw_unit *unit,
                        const void *command, unsigned int command_size,
                        void *response, unsigned int response_size,
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
new file mode 100644 (file)
index 0000000..0c74408
--- /dev/null
@@ -0,0 +1,4 @@
+snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
+                     fireworks_stream.o fireworks_proc.o fireworks_midi.o \
+                     fireworks_pcm.o fireworks_hwdep.o fireworks.o
+obj-m += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
new file mode 100644 (file)
index 0000000..996fdc4
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * fireworks.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * Fireworks is a board module which Echo Audio produced. This module consists
+ * of three chipsets:
+ *  - Communication chipset for IEEE1394 PHY/Link and IEC 61883-1/6
+ *  - DSP or/and FPGA for signal processing
+ *  - Flash Memory to store firmwares
+ */
+
+#include "fireworks.h"
+
+MODULE_DESCRIPTION("Echo Fireworks driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static int index[SNDRV_CARDS]  = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS]   = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS]        = SNDRV_DEFAULT_ENABLE_PNP;
+unsigned int snd_efw_resp_buf_size     = 1024;
+bool snd_efw_resp_buf_debug            = false;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable Fireworks sound card");
+module_param_named(resp_buf_size, snd_efw_resp_buf_size, uint, 0444);
+MODULE_PARM_DESC(resp_buf_size,
+                "response buffer size (max 4096, default 1024)");
+module_param_named(resp_buf_debug, snd_efw_resp_buf_debug, bool, 0444);
+MODULE_PARM_DESC(resp_buf_debug, "store all responses to buffer");
+
+static DEFINE_MUTEX(devices_mutex);
+static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
+
+#define VENDOR_LOUD                    0x000ff2
+#define  MODEL_MACKIE_400F             0x00400f
+#define  MODEL_MACKIE_1200F            0x01200f
+
+#define VENDOR_ECHO                    0x001486
+#define  MODEL_ECHO_AUDIOFIRE_12       0x00af12
+#define  MODEL_ECHO_AUDIOFIRE_12HD     0x0af12d
+#define  MODEL_ECHO_AUDIOFIRE_12_APPLE 0x0af12a
+/* This is applied for AudioFire8 (until 2009 July) */
+#define  MODEL_ECHO_AUDIOFIRE_8                0x000af8
+#define  MODEL_ECHO_AUDIOFIRE_2                0x000af2
+#define  MODEL_ECHO_AUDIOFIRE_4                0x000af4
+/* AudioFire9 is applied for AudioFire8(since 2009 July) and AudioFirePre8 */
+#define  MODEL_ECHO_AUDIOFIRE_9                0x000af9
+/* unknown as product */
+#define  MODEL_ECHO_FIREWORKS_8                0x0000f8
+#define  MODEL_ECHO_FIREWORKS_HDMI     0x00afd1
+
+#define VENDOR_GIBSON                  0x00075b
+/* for Robot Interface Pack of Dark Fire, Dusk Tiger, Les Paul Standard 2010 */
+#define  MODEL_GIBSON_RIP              0x00afb2
+/* unknown as product */
+#define  MODEL_GIBSON_GOLDTOP          0x00afb9
+
+/* part of hardware capability flags */
+#define FLAG_RESP_ADDR_CHANGABLE       0
+
+static int
+get_hardware_info(struct snd_efw *efw)
+{
+       struct fw_device *fw_dev = fw_parent_device(efw->unit);
+       struct snd_efw_hwinfo *hwinfo;
+       char version[12] = {0};
+       int err;
+
+       hwinfo = kzalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
+       if (hwinfo == NULL)
+               return -ENOMEM;
+
+       err = snd_efw_command_get_hwinfo(efw, hwinfo);
+       if (err < 0)
+               goto end;
+
+       /* firmware version for communication chipset */
+       snprintf(version, sizeof(version), "%u.%u",
+                (hwinfo->arm_version >> 24) & 0xff,
+                (hwinfo->arm_version >> 16) & 0xff);
+       efw->firmware_version = hwinfo->arm_version;
+
+       strcpy(efw->card->driver, "Fireworks");
+       strcpy(efw->card->shortname, hwinfo->model_name);
+       strcpy(efw->card->mixername, hwinfo->model_name);
+       snprintf(efw->card->longname, sizeof(efw->card->longname),
+                "%s %s v%s, GUID %08x%08x at %s, S%d",
+                hwinfo->vendor_name, hwinfo->model_name, version,
+                hwinfo->guid_hi, hwinfo->guid_lo,
+                dev_name(&efw->unit->device), 100 << fw_dev->max_speed);
+
+       if (hwinfo->flags & BIT(FLAG_RESP_ADDR_CHANGABLE))
+               efw->resp_addr_changable = true;
+
+       efw->supported_sampling_rate = 0;
+       if ((hwinfo->min_sample_rate <= 22050)
+        && (22050 <= hwinfo->max_sample_rate))
+               efw->supported_sampling_rate |= SNDRV_PCM_RATE_22050;
+       if ((hwinfo->min_sample_rate <= 32000)
+        && (32000 <= hwinfo->max_sample_rate))
+               efw->supported_sampling_rate |= SNDRV_PCM_RATE_32000;
+       if ((hwinfo->min_sample_rate <= 44100)
+        && (44100 <= hwinfo->max_sample_rate))
+               efw->supported_sampling_rate |= SNDRV_PCM_RATE_44100;
+       if ((hwinfo->min_sample_rate <= 48000)
+        && (48000 <= hwinfo->max_sample_rate))
+               efw->supported_sampling_rate |= SNDRV_PCM_RATE_48000;
+       if ((hwinfo->min_sample_rate <= 88200)
+        && (88200 <= hwinfo->max_sample_rate))
+               efw->supported_sampling_rate |= SNDRV_PCM_RATE_88200;
+       if ((hwinfo->min_sample_rate <= 96000)
+        && (96000 <= hwinfo->max_sample_rate))
+               efw->supported_sampling_rate |= SNDRV_PCM_RATE_96000;
+       if ((hwinfo->min_sample_rate <= 176400)
+        && (176400 <= hwinfo->max_sample_rate))
+               efw->supported_sampling_rate |= SNDRV_PCM_RATE_176400;
+       if ((hwinfo->min_sample_rate <= 192000)
+        && (192000 <= hwinfo->max_sample_rate))
+               efw->supported_sampling_rate |= SNDRV_PCM_RATE_192000;
+
+       /* the number of MIDI ports, not of MIDI conformant data channels */
+       if (hwinfo->midi_out_ports > SND_EFW_MAX_MIDI_OUT_PORTS ||
+           hwinfo->midi_in_ports > SND_EFW_MAX_MIDI_IN_PORTS) {
+               err = -EIO;
+               goto end;
+       }
+       efw->midi_out_ports = hwinfo->midi_out_ports;
+       efw->midi_in_ports = hwinfo->midi_in_ports;
+
+       if (hwinfo->amdtp_tx_pcm_channels    > AMDTP_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_tx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_tx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_rx_pcm_channels    > AMDTP_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_rx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_rx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM) {
+               err = -ENOSYS;
+               goto end;
+       }
+       efw->pcm_capture_channels[0] = hwinfo->amdtp_tx_pcm_channels;
+       efw->pcm_capture_channels[1] = hwinfo->amdtp_tx_pcm_channels_2x;
+       efw->pcm_capture_channels[2] = hwinfo->amdtp_tx_pcm_channels_4x;
+       efw->pcm_playback_channels[0] = hwinfo->amdtp_rx_pcm_channels;
+       efw->pcm_playback_channels[1] = hwinfo->amdtp_rx_pcm_channels_2x;
+       efw->pcm_playback_channels[2] = hwinfo->amdtp_rx_pcm_channels_4x;
+
+       /* Hardware metering. */
+       if (hwinfo->phys_in_grp_count  > HWINFO_MAX_CAPS_GROUPS ||
+           hwinfo->phys_out_grp_count > HWINFO_MAX_CAPS_GROUPS) {
+               err = -EIO;
+               goto end;
+       }
+       efw->phys_in = hwinfo->phys_in;
+       efw->phys_out = hwinfo->phys_out;
+       efw->phys_in_grp_count = hwinfo->phys_in_grp_count;
+       efw->phys_out_grp_count = hwinfo->phys_out_grp_count;
+       memcpy(&efw->phys_in_grps, hwinfo->phys_in_grps,
+              sizeof(struct snd_efw_phys_grp) * hwinfo->phys_in_grp_count);
+       memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps,
+              sizeof(struct snd_efw_phys_grp) * hwinfo->phys_out_grp_count);
+end:
+       kfree(hwinfo);
+       return err;
+}
+
+static void
+efw_card_free(struct snd_card *card)
+{
+       struct snd_efw *efw = card->private_data;
+
+       if (efw->card_index >= 0) {
+               mutex_lock(&devices_mutex);
+               clear_bit(efw->card_index, devices_used);
+               mutex_unlock(&devices_mutex);
+       }
+
+       mutex_destroy(&efw->mutex);
+       kfree(efw->resp_buf);
+}
+
+static int
+efw_probe(struct fw_unit *unit,
+         const struct ieee1394_device_id *entry)
+{
+       struct snd_card *card;
+       struct snd_efw *efw;
+       int card_index, err;
+
+       mutex_lock(&devices_mutex);
+
+       /* check registered cards */
+       for (card_index = 0; card_index < SNDRV_CARDS; ++card_index) {
+               if (!test_bit(card_index, devices_used) && enable[card_index])
+                       break;
+       }
+       if (card_index >= SNDRV_CARDS) {
+               err = -ENOENT;
+               goto end;
+       }
+
+       err = snd_card_new(&unit->device, index[card_index], id[card_index],
+                          THIS_MODULE, sizeof(struct snd_efw), &card);
+       if (err < 0)
+               goto end;
+       efw = card->private_data;
+       efw->card_index = card_index;
+       set_bit(card_index, devices_used);
+       card->private_free = efw_card_free;
+
+       efw->card = card;
+       efw->unit = unit;
+       mutex_init(&efw->mutex);
+       spin_lock_init(&efw->lock);
+       init_waitqueue_head(&efw->hwdep_wait);
+
+       /* prepare response buffer */
+       snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size,
+                                     SND_EFW_RESPONSE_MAXIMUM_BYTES, 4096U);
+       efw->resp_buf = kzalloc(snd_efw_resp_buf_size, GFP_KERNEL);
+       if (efw->resp_buf == NULL) {
+               err = -ENOMEM;
+               goto error;
+       }
+       efw->pull_ptr = efw->push_ptr = efw->resp_buf;
+       snd_efw_transaction_add_instance(efw);
+
+       err = get_hardware_info(efw);
+       if (err < 0)
+               goto error;
+       if (entry->model_id == MODEL_ECHO_AUDIOFIRE_9)
+               efw->is_af9 = true;
+
+       snd_efw_proc_init(efw);
+
+       if (efw->midi_out_ports || efw->midi_in_ports) {
+               err = snd_efw_create_midi_devices(efw);
+               if (err < 0)
+                       goto error;
+       }
+
+       err = snd_efw_create_pcm_devices(efw);
+       if (err < 0)
+               goto error;
+
+       err = snd_efw_create_hwdep_device(efw);
+       if (err < 0)
+               goto error;
+
+       err = snd_efw_stream_init_duplex(efw);
+       if (err < 0)
+               goto error;
+
+       err = snd_card_register(card);
+       if (err < 0) {
+               snd_efw_stream_destroy_duplex(efw);
+               goto error;
+       }
+
+       dev_set_drvdata(&unit->device, efw);
+end:
+       mutex_unlock(&devices_mutex);
+       return err;
+error:
+       snd_efw_transaction_remove_instance(efw);
+       mutex_unlock(&devices_mutex);
+       snd_card_free(card);
+       return err;
+}
+
+static void efw_update(struct fw_unit *unit)
+{
+       struct snd_efw *efw = dev_get_drvdata(&unit->device);
+
+       snd_efw_transaction_bus_reset(efw->unit);
+       snd_efw_stream_update_duplex(efw);
+}
+
+static void efw_remove(struct fw_unit *unit)
+{
+       struct snd_efw *efw = dev_get_drvdata(&unit->device);
+
+       snd_efw_stream_destroy_duplex(efw);
+       snd_efw_transaction_remove_instance(efw);
+
+       snd_card_disconnect(efw->card);
+       snd_card_free_when_closed(efw->card);
+}
+
+static const struct ieee1394_device_id efw_id_table[] = {
+       SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_400F),
+       SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_1200F),
+       SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_8),
+       SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12),
+       SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12HD),
+       SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12_APPLE),
+       SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_2),
+       SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_4),
+       SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_9),
+       SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_8),
+       SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_HDMI),
+       SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_RIP),
+       SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_GOLDTOP),
+       {}
+};
+MODULE_DEVICE_TABLE(ieee1394, efw_id_table);
+
+static struct fw_driver efw_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "snd-fireworks",
+               .bus = &fw_bus_type,
+       },
+       .probe    = efw_probe,
+       .update   = efw_update,
+       .remove   = efw_remove,
+       .id_table = efw_id_table,
+};
+
+static int __init snd_efw_init(void)
+{
+       int err;
+
+       err = snd_efw_transaction_register();
+       if (err < 0)
+               goto end;
+
+       err = driver_register(&efw_driver.driver);
+       if (err < 0)
+               snd_efw_transaction_unregister();
+
+end:
+       return err;
+}
+
+static void __exit snd_efw_exit(void)
+{
+       snd_efw_transaction_unregister();
+       driver_unregister(&efw_driver.driver);
+       mutex_destroy(&devices_mutex);
+}
+
+module_init(snd_efw_init);
+module_exit(snd_efw_exit);
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
new file mode 100644 (file)
index 0000000..d2b36be
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * fireworks.h - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#ifndef SOUND_FIREWORKS_H_INCLUDED
+#define SOUND_FIREWORKS_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp.h"
+#include "../cmp.h"
+#include "../lib.h"
+
+#define SND_EFW_MAX_MIDI_OUT_PORTS     2
+#define SND_EFW_MAX_MIDI_IN_PORTS      2
+
+#define SND_EFW_MULTIPLIER_MODES       3
+#define HWINFO_NAME_SIZE_BYTES         32
+#define HWINFO_MAX_CAPS_GROUPS         8
+
+/*
+ * This should be greater than maximum bytes for EFW response content.
+ * Currently response against command for isochronous channel mapping is
+ * confirmed to be the maximum one. But for flexibility, use maximum data
+ * payload for asynchronous primary packets at S100 (Cable base rate) in
+ * IEEE Std 1394-1995.
+ */
+#define SND_EFW_RESPONSE_MAXIMUM_BYTES 0x200U
+
+extern unsigned int snd_efw_resp_buf_size;
+extern bool snd_efw_resp_buf_debug;
+
+struct snd_efw_phys_grp {
+       u8 type;        /* see enum snd_efw_grp_type */
+       u8 count;
+} __packed;
+
+struct snd_efw {
+       struct snd_card *card;
+       struct fw_unit *unit;
+       int card_index;
+
+       struct mutex mutex;
+       spinlock_t lock;
+
+       /* for transaction */
+       u32 seqnum;
+       bool resp_addr_changable;
+
+       /* for quirks */
+       bool is_af9;
+       u32 firmware_version;
+
+       unsigned int midi_in_ports;
+       unsigned int midi_out_ports;
+
+       unsigned int supported_sampling_rate;
+       unsigned int pcm_capture_channels[SND_EFW_MULTIPLIER_MODES];
+       unsigned int pcm_playback_channels[SND_EFW_MULTIPLIER_MODES];
+
+       struct amdtp_stream *master;
+       struct amdtp_stream tx_stream;
+       struct amdtp_stream rx_stream;
+       struct cmp_connection out_conn;
+       struct cmp_connection in_conn;
+       atomic_t capture_substreams;
+       atomic_t playback_substreams;
+
+       /* hardware metering parameters */
+       unsigned int phys_out;
+       unsigned int phys_in;
+       unsigned int phys_out_grp_count;
+       unsigned int phys_in_grp_count;
+       struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
+       struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
+
+       /* for uapi */
+       int dev_lock_count;
+       bool dev_lock_changed;
+       wait_queue_head_t hwdep_wait;
+
+       /* response queue */
+       u8 *resp_buf;
+       u8 *pull_ptr;
+       u8 *push_ptr;
+       unsigned int resp_queues;
+};
+
+int snd_efw_transaction_cmd(struct fw_unit *unit,
+                           const void *cmd, unsigned int size);
+int snd_efw_transaction_run(struct fw_unit *unit,
+                           const void *cmd, unsigned int cmd_size,
+                           void *resp, unsigned int resp_size);
+int snd_efw_transaction_register(void);
+void snd_efw_transaction_unregister(void);
+void snd_efw_transaction_bus_reset(struct fw_unit *unit);
+void snd_efw_transaction_add_instance(struct snd_efw *efw);
+void snd_efw_transaction_remove_instance(struct snd_efw *efw);
+
+struct snd_efw_hwinfo {
+       u32 flags;
+       u32 guid_hi;
+       u32 guid_lo;
+       u32 type;
+       u32 version;
+       char vendor_name[HWINFO_NAME_SIZE_BYTES];
+       char model_name[HWINFO_NAME_SIZE_BYTES];
+       u32 supported_clocks;
+       u32 amdtp_rx_pcm_channels;
+       u32 amdtp_tx_pcm_channels;
+       u32 phys_out;
+       u32 phys_in;
+       u32 phys_out_grp_count;
+       struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
+       u32 phys_in_grp_count;
+       struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
+       u32 midi_out_ports;
+       u32 midi_in_ports;
+       u32 max_sample_rate;
+       u32 min_sample_rate;
+       u32 dsp_version;
+       u32 arm_version;
+       u32 mixer_playback_channels;
+       u32 mixer_capture_channels;
+       u32 fpga_version;
+       u32 amdtp_rx_pcm_channels_2x;
+       u32 amdtp_tx_pcm_channels_2x;
+       u32 amdtp_rx_pcm_channels_4x;
+       u32 amdtp_tx_pcm_channels_4x;
+       u32 reserved[16];
+} __packed;
+enum snd_efw_grp_type {
+       SND_EFW_CH_TYPE_ANALOG                  = 0,
+       SND_EFW_CH_TYPE_SPDIF                   = 1,
+       SND_EFW_CH_TYPE_ADAT                    = 2,
+       SND_EFW_CH_TYPE_SPDIF_OR_ADAT           = 3,
+       SND_EFW_CH_TYPE_ANALOG_MIRRORING        = 4,
+       SND_EFW_CH_TYPE_HEADPHONES              = 5,
+       SND_EFW_CH_TYPE_I2S                     = 6,
+       SND_EFW_CH_TYPE_GUITAR                  = 7,
+       SND_EFW_CH_TYPE_PIEZO_GUITAR            = 8,
+       SND_EFW_CH_TYPE_GUITAR_STRING           = 9,
+       SND_EFW_CH_TYPE_VIRTUAL                 = 0x10000,
+       SND_EFW_CH_TYPE_DUMMY
+};
+struct snd_efw_phys_meters {
+       u32 status;     /* guitar state/midi signal/clock input detect */
+       u32 reserved0;
+       u32 reserved1;
+       u32 reserved2;
+       u32 reserved3;
+       u32 out_meters;
+       u32 in_meters;
+       u32 reserved4;
+       u32 reserved5;
+       u32 values[0];
+} __packed;
+enum snd_efw_clock_source {
+       SND_EFW_CLOCK_SOURCE_INTERNAL   = 0,
+       SND_EFW_CLOCK_SOURCE_SYTMATCH   = 1,
+       SND_EFW_CLOCK_SOURCE_WORDCLOCK  = 2,
+       SND_EFW_CLOCK_SOURCE_SPDIF      = 3,
+       SND_EFW_CLOCK_SOURCE_ADAT_1     = 4,
+       SND_EFW_CLOCK_SOURCE_ADAT_2     = 5,
+       SND_EFW_CLOCK_SOURCE_CONTINUOUS = 6     /* internal variable clock */
+};
+enum snd_efw_transport_mode {
+       SND_EFW_TRANSPORT_MODE_WINDOWS  = 0,
+       SND_EFW_TRANSPORT_MODE_IEC61883 = 1,
+};
+int snd_efw_command_set_resp_addr(struct snd_efw *efw,
+                                 u16 addr_high, u32 addr_low);
+int snd_efw_command_set_tx_mode(struct snd_efw *efw,
+                               enum snd_efw_transport_mode mode);
+int snd_efw_command_get_hwinfo(struct snd_efw *efw,
+                              struct snd_efw_hwinfo *hwinfo);
+int snd_efw_command_get_phys_meters(struct snd_efw *efw,
+                                   struct snd_efw_phys_meters *meters,
+                                   unsigned int len);
+int snd_efw_command_get_clock_source(struct snd_efw *efw,
+                                    enum snd_efw_clock_source *source);
+int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate);
+int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate);
+
+int snd_efw_stream_init_duplex(struct snd_efw *efw);
+int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate);
+void snd_efw_stream_stop_duplex(struct snd_efw *efw);
+void snd_efw_stream_update_duplex(struct snd_efw *efw);
+void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
+void snd_efw_stream_lock_changed(struct snd_efw *efw);
+int snd_efw_stream_lock_try(struct snd_efw *efw);
+void snd_efw_stream_lock_release(struct snd_efw *efw);
+
+void snd_efw_proc_init(struct snd_efw *efw);
+
+int snd_efw_create_midi_devices(struct snd_efw *efw);
+
+int snd_efw_create_pcm_devices(struct snd_efw *efw);
+int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode);
+
+int snd_efw_create_hwdep_device(struct snd_efw *efw);
+
+#define SND_EFW_DEV_ENTRY(vendor, model) \
+{ \
+       .match_flags    = IEEE1394_MATCH_VENDOR_ID | \
+                         IEEE1394_MATCH_MODEL_ID, \
+       .vendor_id      = vendor,\
+       .model_id       = model \
+}
+
+#endif
diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c
new file mode 100644 (file)
index 0000000..166f805
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * fireworks_command.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./fireworks.h"
+
+/*
+ * This driver uses transaction version 1 or later to use extended hardware
+ * information. Then too old devices are not available.
+ *
+ * Each commands are not required to have continuous sequence numbers. This
+ * number is just used to match command and response.
+ *
+ * This module support a part of commands. Please see FFADO if you want to see
+ * whole commands. But there are some commands which FFADO don't implement.
+ *
+ * Fireworks also supports AV/C general commands and AV/C Stream Format
+ * Information commands. But this module don't use them.
+ */
+
+#define KERNEL_SEQNUM_MIN      (SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 2)
+#define KERNEL_SEQNUM_MAX      ((u32)~0)
+
+/* for clock source and sampling rate */
+struct efc_clock {
+       u32 source;
+       u32 sampling_rate;
+       u32 index;
+};
+
+/* command categories */
+enum efc_category {
+       EFC_CAT_HWINFO          = 0,
+       EFC_CAT_TRANSPORT       = 2,
+       EFC_CAT_HWCTL           = 3,
+};
+
+/* hardware info category commands */
+enum efc_cmd_hwinfo {
+       EFC_CMD_HWINFO_GET_CAPS         = 0,
+       EFC_CMD_HWINFO_GET_POLLED       = 1,
+       EFC_CMD_HWINFO_SET_RESP_ADDR    = 2
+};
+
+enum efc_cmd_transport {
+       EFC_CMD_TRANSPORT_SET_TX_MODE   = 0
+};
+
+/* hardware control category commands */
+enum efc_cmd_hwctl {
+       EFC_CMD_HWCTL_SET_CLOCK         = 0,
+       EFC_CMD_HWCTL_GET_CLOCK         = 1,
+       EFC_CMD_HWCTL_IDENTIFY          = 5
+};
+
+/* return values in response */
+enum efr_status {
+       EFR_STATUS_OK                   = 0,
+       EFR_STATUS_BAD                  = 1,
+       EFR_STATUS_BAD_COMMAND          = 2,
+       EFR_STATUS_COMM_ERR             = 3,
+       EFR_STATUS_BAD_QUAD_COUNT       = 4,
+       EFR_STATUS_UNSUPPORTED          = 5,
+       EFR_STATUS_1394_TIMEOUT         = 6,
+       EFR_STATUS_DSP_TIMEOUT          = 7,
+       EFR_STATUS_BAD_RATE             = 8,
+       EFR_STATUS_BAD_CLOCK            = 9,
+       EFR_STATUS_BAD_CHANNEL          = 10,
+       EFR_STATUS_BAD_PAN              = 11,
+       EFR_STATUS_FLASH_BUSY           = 12,
+       EFR_STATUS_BAD_MIRROR           = 13,
+       EFR_STATUS_BAD_LED              = 14,
+       EFR_STATUS_BAD_PARAMETER        = 15,
+       EFR_STATUS_INCOMPLETE           = 0x80000000
+};
+
+static const char *const efr_status_names[] = {
+       [EFR_STATUS_OK]                 = "OK",
+       [EFR_STATUS_BAD]                = "bad",
+       [EFR_STATUS_BAD_COMMAND]        = "bad command",
+       [EFR_STATUS_COMM_ERR]           = "comm err",
+       [EFR_STATUS_BAD_QUAD_COUNT]     = "bad quad count",
+       [EFR_STATUS_UNSUPPORTED]        = "unsupported",
+       [EFR_STATUS_1394_TIMEOUT]       = "1394 timeout",
+       [EFR_STATUS_DSP_TIMEOUT]        = "DSP timeout",
+       [EFR_STATUS_BAD_RATE]           = "bad rate",
+       [EFR_STATUS_BAD_CLOCK]          = "bad clock",
+       [EFR_STATUS_BAD_CHANNEL]        = "bad channel",
+       [EFR_STATUS_BAD_PAN]            = "bad pan",
+       [EFR_STATUS_FLASH_BUSY]         = "flash busy",
+       [EFR_STATUS_BAD_MIRROR]         = "bad mirror",
+       [EFR_STATUS_BAD_LED]            = "bad LED",
+       [EFR_STATUS_BAD_PARAMETER]      = "bad parameter",
+       [EFR_STATUS_BAD_PARAMETER + 1]  = "incomplete"
+};
+
+static int
+efw_transaction(struct snd_efw *efw, unsigned int category,
+               unsigned int command,
+               const __be32 *params, unsigned int param_bytes,
+               const __be32 *resp, unsigned int resp_bytes)
+{
+       struct snd_efw_transaction *header;
+       __be32 *buf;
+       u32 seqnum;
+       unsigned int buf_bytes, cmd_bytes;
+       int err;
+
+       /* calculate buffer size*/
+       buf_bytes = sizeof(struct snd_efw_transaction) +
+                   max(param_bytes, resp_bytes);
+
+       /* keep buffer */
+       buf = kzalloc(buf_bytes, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       /* to keep consistency of sequence number */
+       spin_lock(&efw->lock);
+       if ((efw->seqnum < KERNEL_SEQNUM_MIN) ||
+           (efw->seqnum >= KERNEL_SEQNUM_MAX - 2))
+               efw->seqnum = KERNEL_SEQNUM_MIN;
+       else
+               efw->seqnum += 2;
+       seqnum = efw->seqnum;
+       spin_unlock(&efw->lock);
+
+       /* fill transaction header fields */
+       cmd_bytes = sizeof(struct snd_efw_transaction) + param_bytes;
+       header = (struct snd_efw_transaction *)buf;
+       header->length   = cpu_to_be32(cmd_bytes / sizeof(__be32));
+       header->version  = cpu_to_be32(1);
+       header->seqnum   = cpu_to_be32(seqnum);
+       header->category = cpu_to_be32(category);
+       header->command  = cpu_to_be32(command);
+       header->status   = 0;
+
+       /* fill transaction command parameters */
+       memcpy(header->params, params, param_bytes);
+
+       err = snd_efw_transaction_run(efw->unit, buf, cmd_bytes,
+                                     buf, buf_bytes);
+       if (err < 0)
+               goto end;
+
+       /* check transaction header fields */
+       if ((be32_to_cpu(header->version) < 1) ||
+           (be32_to_cpu(header->category) != category) ||
+           (be32_to_cpu(header->command) != command) ||
+           (be32_to_cpu(header->status) != EFR_STATUS_OK)) {
+               dev_err(&efw->unit->device, "EFW command failed [%u/%u]: %s\n",
+                       be32_to_cpu(header->category),
+                       be32_to_cpu(header->command),
+                       efr_status_names[be32_to_cpu(header->status)]);
+               err = -EIO;
+               goto end;
+       }
+
+       if (resp == NULL)
+               goto end;
+
+       /* fill transaction response parameters */
+       memset((void *)resp, 0, resp_bytes);
+       resp_bytes = min_t(unsigned int, resp_bytes,
+                          be32_to_cpu(header->length) * sizeof(__be32) -
+                               sizeof(struct snd_efw_transaction));
+       memcpy((void *)resp, &buf[6], resp_bytes);
+end:
+       kfree(buf);
+       return err;
+}
+
+/*
+ * The address in host system for transaction response is changable when the
+ * device supports. struct hwinfo.flags includes its flag. The default is
+ * MEMORY_SPACE_EFW_RESPONSE.
+ */
+int snd_efw_command_set_resp_addr(struct snd_efw *efw,
+                                 u16 addr_high, u32 addr_low)
+{
+       __be32 addr[2];
+
+       addr[0] = cpu_to_be32(addr_high);
+       addr[1] = cpu_to_be32(addr_low);
+
+       if (!efw->resp_addr_changable)
+               return -ENOSYS;
+
+       return efw_transaction(efw, EFC_CAT_HWCTL,
+                              EFC_CMD_HWINFO_SET_RESP_ADDR,
+                              addr, sizeof(addr), NULL, 0);
+}
+
+/*
+ * This is for timestamp processing. In Windows mode, all 32bit fields of second
+ * CIP header in AMDTP transmit packet is used for 'presentation timestamp'. In
+ * 'no data' packet the value of this field is 0x90ffffff.
+ */
+int snd_efw_command_set_tx_mode(struct snd_efw *efw,
+                               enum snd_efw_transport_mode mode)
+{
+       __be32 param = cpu_to_be32(mode);
+       return efw_transaction(efw, EFC_CAT_TRANSPORT,
+                              EFC_CMD_TRANSPORT_SET_TX_MODE,
+                              &param, sizeof(param), NULL, 0);
+}
+
+int snd_efw_command_get_hwinfo(struct snd_efw *efw,
+                              struct snd_efw_hwinfo *hwinfo)
+{
+       int err;
+
+       err  = efw_transaction(efw, EFC_CAT_HWINFO,
+                              EFC_CMD_HWINFO_GET_CAPS,
+                              NULL, 0, (__be32 *)hwinfo, sizeof(*hwinfo));
+       if (err < 0)
+               goto end;
+
+       be32_to_cpus(&hwinfo->flags);
+       be32_to_cpus(&hwinfo->guid_hi);
+       be32_to_cpus(&hwinfo->guid_lo);
+       be32_to_cpus(&hwinfo->type);
+       be32_to_cpus(&hwinfo->version);
+       be32_to_cpus(&hwinfo->supported_clocks);
+       be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels);
+       be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels);
+       be32_to_cpus(&hwinfo->phys_out);
+       be32_to_cpus(&hwinfo->phys_in);
+       be32_to_cpus(&hwinfo->phys_out_grp_count);
+       be32_to_cpus(&hwinfo->phys_in_grp_count);
+       be32_to_cpus(&hwinfo->midi_out_ports);
+       be32_to_cpus(&hwinfo->midi_in_ports);
+       be32_to_cpus(&hwinfo->max_sample_rate);
+       be32_to_cpus(&hwinfo->min_sample_rate);
+       be32_to_cpus(&hwinfo->dsp_version);
+       be32_to_cpus(&hwinfo->arm_version);
+       be32_to_cpus(&hwinfo->mixer_playback_channels);
+       be32_to_cpus(&hwinfo->mixer_capture_channels);
+       be32_to_cpus(&hwinfo->fpga_version);
+       be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_2x);
+       be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_2x);
+       be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_4x);
+       be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_4x);
+
+       /* ensure terminated */
+       hwinfo->vendor_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0';
+       hwinfo->model_name[HWINFO_NAME_SIZE_BYTES  - 1] = '\0';
+end:
+       return err;
+}
+
+int snd_efw_command_get_phys_meters(struct snd_efw *efw,
+                                   struct snd_efw_phys_meters *meters,
+                                   unsigned int len)
+{
+       __be32 *buf = (__be32 *)meters;
+       unsigned int i;
+       int err;
+
+       err = efw_transaction(efw, EFC_CAT_HWINFO,
+                             EFC_CMD_HWINFO_GET_POLLED,
+                             NULL, 0, (__be32 *)meters, len);
+       if (err >= 0)
+               for (i = 0; i < len / sizeof(u32); i++)
+                       be32_to_cpus(&buf[i]);
+
+       return err;
+}
+
+static int
+command_get_clock(struct snd_efw *efw, struct efc_clock *clock)
+{
+       int err;
+
+       err = efw_transaction(efw, EFC_CAT_HWCTL,
+                             EFC_CMD_HWCTL_GET_CLOCK,
+                             NULL, 0,
+                             (__be32 *)clock, sizeof(struct efc_clock));
+       if (err >= 0) {
+               be32_to_cpus(&clock->source);
+               be32_to_cpus(&clock->sampling_rate);
+               be32_to_cpus(&clock->index);
+       }
+
+       return err;
+}
+
+/* give UINT_MAX if set nothing */
+static int
+command_set_clock(struct snd_efw *efw,
+                 unsigned int source, unsigned int rate)
+{
+       struct efc_clock clock = {0};
+       int err;
+
+       /* check arguments */
+       if ((source == UINT_MAX) && (rate == UINT_MAX)) {
+               err = -EINVAL;
+               goto end;
+       }
+
+       /* get current status */
+       err = command_get_clock(efw, &clock);
+       if (err < 0)
+               goto end;
+
+       /* no need */
+       if ((clock.source == source) && (clock.sampling_rate == rate))
+               goto end;
+
+       /* set params */
+       if ((source != UINT_MAX) && (clock.source != source))
+               clock.source = source;
+       if ((rate != UINT_MAX) && (clock.sampling_rate != rate))
+               clock.sampling_rate = rate;
+       clock.index = 0;
+
+       cpu_to_be32s(&clock.source);
+       cpu_to_be32s(&clock.sampling_rate);
+       cpu_to_be32s(&clock.index);
+
+       err = efw_transaction(efw, EFC_CAT_HWCTL,
+                             EFC_CMD_HWCTL_SET_CLOCK,
+                             (__be32 *)&clock, sizeof(struct efc_clock),
+                             NULL, 0);
+       if (err < 0)
+               goto end;
+
+       /*
+        * With firmware version 5.8, just after changing clock state, these
+        * parameters are not immediately retrieved by get command. In my
+        * trial, there needs to be 100msec to get changed parameters.
+        */
+       msleep(150);
+end:
+       return err;
+}
+
+int snd_efw_command_get_clock_source(struct snd_efw *efw,
+                                    enum snd_efw_clock_source *source)
+{
+       int err;
+       struct efc_clock clock = {0};
+
+       err = command_get_clock(efw, &clock);
+       if (err >= 0)
+               *source = clock.source;
+
+       return err;
+}
+
+int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate)
+{
+       int err;
+       struct efc_clock clock = {0};
+
+       err = command_get_clock(efw, &clock);
+       if (err >= 0)
+               *rate = clock.sampling_rate;
+
+       return err;
+}
+
+int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate)
+{
+       return command_set_clock(efw, UINT_MAX, rate);
+}
+
diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c
new file mode 100644 (file)
index 0000000..4f8216f
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * fireworks_hwdep.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes have five functionalities.
+ *
+ * 1.get information about firewire node
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock streaming
+ * 4.transmit command of EFW transaction
+ * 5.receive response of EFW transaction
+ *
+ */
+
+#include "fireworks.h"
+
+static long
+hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
+                   loff_t *offset)
+{
+       unsigned int length, till_end, type;
+       struct snd_efw_transaction *t;
+       long count = 0;
+
+       if (remained < sizeof(type) + sizeof(struct snd_efw_transaction))
+               return -ENOSPC;
+
+       /* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */
+       type = SNDRV_FIREWIRE_EVENT_EFW_RESPONSE;
+       if (copy_to_user(buf, &type, sizeof(type)))
+               return -EFAULT;
+       remained -= sizeof(type);
+       buf += sizeof(type);
+
+       /* write into buffer as many responses as possible */
+       while (efw->resp_queues > 0) {
+               t = (struct snd_efw_transaction *)(efw->pull_ptr);
+               length = be32_to_cpu(t->length) * sizeof(__be32);
+
+               /* confirm enough space for this response */
+               if (remained < length)
+                       break;
+
+               /* copy from ring buffer to user buffer */
+               while (length > 0) {
+                       till_end = snd_efw_resp_buf_size -
+                               (unsigned int)(efw->pull_ptr - efw->resp_buf);
+                       till_end = min_t(unsigned int, length, till_end);
+
+                       if (copy_to_user(buf, efw->pull_ptr, till_end))
+                               return -EFAULT;
+
+                       efw->pull_ptr += till_end;
+                       if (efw->pull_ptr >= efw->resp_buf +
+                                            snd_efw_resp_buf_size)
+                               efw->pull_ptr = efw->resp_buf;
+
+                       length -= till_end;
+                       buf += till_end;
+                       count += till_end;
+                       remained -= till_end;
+               }
+
+               efw->resp_queues--;
+       }
+
+       return count;
+}
+
+static long
+hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
+                 loff_t *offset)
+{
+       union snd_firewire_event event;
+
+       memset(&event, 0, sizeof(event));
+
+       event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+       event.lock_status.status = (efw->dev_lock_count > 0);
+       efw->dev_lock_changed = false;
+
+       count = min_t(long, count, sizeof(event.lock_status));
+
+       if (copy_to_user(buf, &event, count))
+               return -EFAULT;
+
+       return count;
+}
+
+static long
+hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+          loff_t *offset)
+{
+       struct snd_efw *efw = hwdep->private_data;
+       DEFINE_WAIT(wait);
+
+       spin_lock_irq(&efw->lock);
+
+       while ((!efw->dev_lock_changed) && (efw->resp_queues == 0)) {
+               prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+               spin_unlock_irq(&efw->lock);
+               schedule();
+               finish_wait(&efw->hwdep_wait, &wait);
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+               spin_lock_irq(&efw->lock);
+       }
+
+       if (efw->dev_lock_changed)
+               count = hwdep_read_locked(efw, buf, count, offset);
+       else if (efw->resp_queues > 0)
+               count = hwdep_read_resp_buf(efw, buf, count, offset);
+
+       spin_unlock_irq(&efw->lock);
+
+       return count;
+}
+
+static long
+hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
+           loff_t *offset)
+{
+       struct snd_efw *efw = hwdep->private_data;
+       u32 seqnum;
+       u8 *buf;
+
+       if (count < sizeof(struct snd_efw_transaction) ||
+           SND_EFW_RESPONSE_MAXIMUM_BYTES < count)
+               return -EINVAL;
+
+       buf = memdup_user(data, count);
+       if (IS_ERR(buf))
+               return PTR_ERR(buf);
+
+       /* check seqnum is not for kernel-land */
+       seqnum = be32_to_cpu(((struct snd_efw_transaction *)buf)->seqnum);
+       if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX) {
+               count = -EINVAL;
+               goto end;
+       }
+
+       if (snd_efw_transaction_cmd(efw->unit, buf, count) < 0)
+               count = -EIO;
+end:
+       kfree(buf);
+       return count;
+}
+
+static unsigned int
+hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
+{
+       struct snd_efw *efw = hwdep->private_data;
+       unsigned int events;
+
+       poll_wait(file, &efw->hwdep_wait, wait);
+
+       spin_lock_irq(&efw->lock);
+       if (efw->dev_lock_changed || (efw->resp_queues > 0))
+               events = POLLIN | POLLRDNORM;
+       else
+               events = 0;
+       spin_unlock_irq(&efw->lock);
+
+       return events | POLLOUT;
+}
+
+static int
+hwdep_get_info(struct snd_efw *efw, void __user *arg)
+{
+       struct fw_device *dev = fw_parent_device(efw->unit);
+       struct snd_firewire_get_info info;
+
+       memset(&info, 0, sizeof(info));
+       info.type = SNDRV_FIREWIRE_TYPE_FIREWORKS;
+       info.card = dev->card->index;
+       *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+       *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+       strlcpy(info.device_name, dev_name(&dev->device),
+               sizeof(info.device_name));
+
+       if (copy_to_user(arg, &info, sizeof(info)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int
+hwdep_lock(struct snd_efw *efw)
+{
+       int err;
+
+       spin_lock_irq(&efw->lock);
+
+       if (efw->dev_lock_count == 0) {
+               efw->dev_lock_count = -1;
+               err = 0;
+       } else {
+               err = -EBUSY;
+       }
+
+       spin_unlock_irq(&efw->lock);
+
+       return err;
+}
+
+static int
+hwdep_unlock(struct snd_efw *efw)
+{
+       int err;
+
+       spin_lock_irq(&efw->lock);
+
+       if (efw->dev_lock_count == -1) {
+               efw->dev_lock_count = 0;
+               err = 0;
+       } else {
+               err = -EBADFD;
+       }
+
+       spin_unlock_irq(&efw->lock);
+
+       return err;
+}
+
+static int
+hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+       struct snd_efw *efw = hwdep->private_data;
+
+       spin_lock_irq(&efw->lock);
+       if (efw->dev_lock_count == -1)
+               efw->dev_lock_count = 0;
+       spin_unlock_irq(&efw->lock);
+
+       return 0;
+}
+
+static int
+hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+           unsigned int cmd, unsigned long arg)
+{
+       struct snd_efw *efw = hwdep->private_data;
+
+       switch (cmd) {
+       case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+               return hwdep_get_info(efw, (void __user *)arg);
+       case SNDRV_FIREWIRE_IOCTL_LOCK:
+               return hwdep_lock(efw);
+       case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+               return hwdep_unlock(efw);
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+#ifdef CONFIG_COMPAT
+static int
+hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+                  unsigned int cmd, unsigned long arg)
+{
+       return hwdep_ioctl(hwdep, file, cmd,
+                          (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+       .read           = hwdep_read,
+       .write          = hwdep_write,
+       .release        = hwdep_release,
+       .poll           = hwdep_poll,
+       .ioctl          = hwdep_ioctl,
+       .ioctl_compat   = hwdep_compat_ioctl,
+};
+
+int snd_efw_create_hwdep_device(struct snd_efw *efw)
+{
+       struct snd_hwdep *hwdep;
+       int err;
+
+       err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep);
+       if (err < 0)
+               goto end;
+       strcpy(hwdep->name, "Fireworks");
+       hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS;
+       hwdep->ops = hwdep_ops;
+       hwdep->private_data = efw;
+       hwdep->exclusive = true;
+end:
+       return err;
+}
+
diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c
new file mode 100644 (file)
index 0000000..cf9c652
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * fireworks_midi.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#include "fireworks.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+       struct snd_efw *efw = substream->rmidi->private_data;
+       int err;
+
+       err = snd_efw_stream_lock_try(efw);
+       if (err < 0)
+               goto end;
+
+       atomic_inc(&efw->capture_substreams);
+       err = snd_efw_stream_start_duplex(efw, 0);
+       if (err < 0)
+               snd_efw_stream_lock_release(efw);
+
+end:
+       return err;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+       struct snd_efw *efw = substream->rmidi->private_data;
+       int err;
+
+       err = snd_efw_stream_lock_try(efw);
+       if (err < 0)
+               goto end;
+
+       atomic_inc(&efw->playback_substreams);
+       err = snd_efw_stream_start_duplex(efw, 0);
+       if (err < 0)
+               snd_efw_stream_lock_release(efw);
+end:
+       return err;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+       struct snd_efw *efw = substream->rmidi->private_data;
+
+       atomic_dec(&efw->capture_substreams);
+       snd_efw_stream_stop_duplex(efw);
+
+       snd_efw_stream_lock_release(efw);
+       return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+       struct snd_efw *efw = substream->rmidi->private_data;
+
+       atomic_dec(&efw->playback_substreams);
+       snd_efw_stream_stop_duplex(efw);
+
+       snd_efw_stream_lock_release(efw);
+       return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+       struct snd_efw *efw = substrm->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&efw->lock, flags);
+
+       if (up)
+               amdtp_stream_midi_trigger(&efw->tx_stream,
+                                         substrm->number, substrm);
+       else
+               amdtp_stream_midi_trigger(&efw->tx_stream,
+                                         substrm->number, NULL);
+
+       spin_unlock_irqrestore(&efw->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+       struct snd_efw *efw = substrm->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&efw->lock, flags);
+
+       if (up)
+               amdtp_stream_midi_trigger(&efw->rx_stream,
+                                         substrm->number, substrm);
+       else
+               amdtp_stream_midi_trigger(&efw->rx_stream,
+                                         substrm->number, NULL);
+
+       spin_unlock_irqrestore(&efw->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+       .open           = midi_capture_open,
+       .close          = midi_capture_close,
+       .trigger        = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+       .open           = midi_playback_open,
+       .close          = midi_playback_close,
+       .trigger        = midi_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_efw *efw,
+                                    struct snd_rawmidi_str *str)
+{
+       struct snd_rawmidi_substream *subs;
+
+       list_for_each_entry(subs, &str->substreams, list) {
+               snprintf(subs->name, sizeof(subs->name),
+                        "%s MIDI %d", efw->card->shortname, subs->number + 1);
+       }
+}
+
+int snd_efw_create_midi_devices(struct snd_efw *efw)
+{
+       struct snd_rawmidi *rmidi;
+       struct snd_rawmidi_str *str;
+       int err;
+
+       /* create midi ports */
+       err = snd_rawmidi_new(efw->card, efw->card->driver, 0,
+                             efw->midi_out_ports, efw->midi_in_ports,
+                             &rmidi);
+       if (err < 0)
+               return err;
+
+       snprintf(rmidi->name, sizeof(rmidi->name),
+                "%s MIDI", efw->card->shortname);
+       rmidi->private_data = efw;
+
+       if (efw->midi_in_ports > 0) {
+               rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+               snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+                                   &midi_capture_ops);
+
+               str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+               set_midi_substream_names(efw, str);
+       }
+
+       if (efw->midi_out_ports > 0) {
+               rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+               snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+                                   &midi_playback_ops);
+
+               str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+               set_midi_substream_names(efw, str);
+       }
+
+       if ((efw->midi_out_ports > 0) && (efw->midi_in_ports > 0))
+               rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+       return 0;
+}
diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c
new file mode 100644 (file)
index 0000000..8a34753
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * fireworks_pcm.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#include "./fireworks.h"
+
+/*
+ * NOTE:
+ * Fireworks changes its AMDTP channels for PCM data according to its sampling
+ * rate. There are three modes. Here _XX is either _rx or _tx.
+ *  0:  32.0- 48.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels applied
+ *  1:  88.2- 96.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_2x applied
+ *  2: 176.4-192.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_4x applied
+ *
+ * The number of PCM channels for analog input and output are always fixed but
+ * the number of PCM channels for digital input and output are differed.
+ *
+ * Additionally, according to "AudioFire Owner's Manual Version 2.2", in some
+ * model, the number of PCM channels for digital input has more restriction
+ * depending on which digital interface is selected.
+ *  - S/PDIF coaxial and optical       : use input 1-2
+ *  - ADAT optical at 32.0-48.0 kHz    : use input 1-8
+ *  - ADAT optical at 88.2-96.0 kHz    : use input 1-4 (S/MUX format)
+ *
+ * The data in AMDTP channels for blank PCM channels are zero.
+ */
+static const unsigned int freq_table[] = {
+       /* multiplier mode 0 */
+       [0] = 32000,
+       [1] = 44100,
+       [2] = 48000,
+       /* multiplier mode 1 */
+       [3] = 88200,
+       [4] = 96000,
+       /* multiplier mode 2 */
+       [5] = 176400,
+       [6] = 192000,
+};
+
+static inline unsigned int
+get_multiplier_mode_with_index(unsigned int index)
+{
+       return ((int)index - 1) / 2;
+}
+
+int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+               if (freq_table[i] == sampling_rate) {
+                       *mode = get_multiplier_mode_with_index(i);
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int
+hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+       unsigned int *pcm_channels = rule->private;
+       struct snd_interval *r =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       const struct snd_interval *c =
+               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval t = {
+               .min = UINT_MAX, .max = 0, .integer = 1
+       };
+       unsigned int i, mode;
+
+       for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+               mode = get_multiplier_mode_with_index(i);
+               if (!snd_interval_test(c, pcm_channels[mode]))
+                       continue;
+
+               t.min = min(t.min, freq_table[i]);
+               t.max = max(t.max, freq_table[i]);
+       }
+
+       return snd_interval_refine(r, &t);
+}
+
+static int
+hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+       unsigned int *pcm_channels = rule->private;
+       struct snd_interval *c =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       const struct snd_interval *r =
+               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval t = {
+               .min = UINT_MAX, .max = 0, .integer = 1
+       };
+       unsigned int i, mode;
+
+       for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+               mode = get_multiplier_mode_with_index(i);
+               if (!snd_interval_test(r, freq_table[i]))
+                       continue;
+
+               t.min = min(t.min, pcm_channels[mode]);
+               t.max = max(t.max, pcm_channels[mode]);
+       }
+
+       return snd_interval_refine(c, &t);
+}
+
+static void
+limit_channels(struct snd_pcm_hardware *hw, unsigned int *pcm_channels)
+{
+       unsigned int i, mode;
+
+       hw->channels_min = UINT_MAX;
+       hw->channels_max = 0;
+
+       for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+               mode = get_multiplier_mode_with_index(i);
+               if (pcm_channels[mode] == 0)
+                       continue;
+
+               hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
+               hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
+       }
+}
+
+static void
+limit_period_and_buffer(struct snd_pcm_hardware *hw)
+{
+       hw->periods_min = 2;            /* SNDRV_PCM_INFO_BATCH */
+       hw->periods_max = UINT_MAX;
+
+       hw->period_bytes_min = 4 * hw->channels_max;    /* bytes for a frame */
+
+       /* Just to prevent from allocating much pages. */
+       hw->period_bytes_max = hw->period_bytes_min * 2048;
+       hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
+}
+
+static int
+pcm_init_hw_params(struct snd_efw *efw,
+                  struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct amdtp_stream *s;
+       unsigned int *pcm_channels;
+       int err;
+
+       runtime->hw.info = SNDRV_PCM_INFO_BATCH |
+                          SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                          SNDRV_PCM_INFO_INTERLEAVED |
+                          SNDRV_PCM_INFO_JOINT_DUPLEX |
+                          SNDRV_PCM_INFO_MMAP |
+                          SNDRV_PCM_INFO_MMAP_VALID;
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+               s = &efw->tx_stream;
+               pcm_channels = efw->pcm_capture_channels;
+       } else {
+               runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+               s = &efw->rx_stream;
+               pcm_channels = efw->pcm_playback_channels;
+       }
+
+       /* limit rates */
+       runtime->hw.rates = efw->supported_sampling_rate,
+       snd_pcm_limit_hw_rates(runtime);
+
+       limit_channels(&runtime->hw, pcm_channels);
+       limit_period_and_buffer(&runtime->hw);
+
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                 hw_rule_channels, pcm_channels,
+                                 SNDRV_PCM_HW_PARAM_RATE, -1);
+       if (err < 0)
+               goto end;
+
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                 hw_rule_rate, pcm_channels,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+       if (err < 0)
+               goto end;
+
+       err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+end:
+       return err;
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_efw *efw = substream->private_data;
+       unsigned int sampling_rate;
+       enum snd_efw_clock_source clock_source;
+       int err;
+
+       err = snd_efw_stream_lock_try(efw);
+       if (err < 0)
+               goto end;
+
+       err = pcm_init_hw_params(efw, substream);
+       if (err < 0)
+               goto err_locked;
+
+       err = snd_efw_command_get_clock_source(efw, &clock_source);
+       if (err < 0)
+               goto err_locked;
+
+       /*
+        * When source of clock is not internal or any PCM streams are running,
+        * available sampling rate is limited at current sampling rate.
+        */
+       if ((clock_source != SND_EFW_CLOCK_SOURCE_INTERNAL) ||
+           amdtp_stream_pcm_running(&efw->tx_stream) ||
+           amdtp_stream_pcm_running(&efw->rx_stream)) {
+               err = snd_efw_command_get_sampling_rate(efw, &sampling_rate);
+               if (err < 0)
+                       goto err_locked;
+               substream->runtime->hw.rate_min = sampling_rate;
+               substream->runtime->hw.rate_max = sampling_rate;
+       }
+
+       snd_pcm_set_sync(substream);
+end:
+       return err;
+err_locked:
+       snd_efw_stream_lock_release(efw);
+       return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+       struct snd_efw *efw = substream->private_data;
+       snd_efw_stream_lock_release(efw);
+       return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_efw *efw = substream->private_data;
+
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+               atomic_inc(&efw->capture_substreams);
+       amdtp_stream_set_pcm_format(&efw->tx_stream, params_format(hw_params));
+
+       return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                               params_buffer_bytes(hw_params));
+}
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_efw *efw = substream->private_data;
+
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+               atomic_inc(&efw->playback_substreams);
+       amdtp_stream_set_pcm_format(&efw->rx_stream, params_format(hw_params));
+
+       return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                               params_buffer_bytes(hw_params));
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_efw *efw = substream->private_data;
+
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+               atomic_dec(&efw->capture_substreams);
+
+       snd_efw_stream_stop_duplex(efw);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_efw *efw = substream->private_data;
+
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+               atomic_dec(&efw->playback_substreams);
+
+       snd_efw_stream_stop_duplex(efw);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_efw *efw = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       err = snd_efw_stream_start_duplex(efw, runtime->rate);
+       if (err >= 0)
+               amdtp_stream_pcm_prepare(&efw->tx_stream);
+
+       return err;
+}
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_efw *efw = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       err = snd_efw_stream_start_duplex(efw, runtime->rate);
+       if (err >= 0)
+               amdtp_stream_pcm_prepare(&efw->rx_stream);
+
+       return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_efw *efw = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               amdtp_stream_pcm_trigger(&efw->tx_stream, substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               amdtp_stream_pcm_trigger(&efw->tx_stream, NULL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_efw *efw = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               amdtp_stream_pcm_trigger(&efw->rx_stream, substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               amdtp_stream_pcm_trigger(&efw->rx_stream, NULL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+       struct snd_efw *efw = sbstrm->private_data;
+       return amdtp_stream_pcm_pointer(&efw->tx_stream);
+}
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+       struct snd_efw *efw = sbstrm->private_data;
+       return amdtp_stream_pcm_pointer(&efw->rx_stream);
+}
+
+static const struct snd_pcm_ops pcm_capture_ops = {
+       .open           = pcm_open,
+       .close          = pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = pcm_capture_hw_params,
+       .hw_free        = pcm_capture_hw_free,
+       .prepare        = pcm_capture_prepare,
+       .trigger        = pcm_capture_trigger,
+       .pointer        = pcm_capture_pointer,
+       .page           = snd_pcm_lib_get_vmalloc_page,
+};
+
+static const struct snd_pcm_ops pcm_playback_ops = {
+       .open           = pcm_open,
+       .close          = pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = pcm_playback_hw_params,
+       .hw_free        = pcm_playback_hw_free,
+       .prepare        = pcm_playback_prepare,
+       .trigger        = pcm_playback_trigger,
+       .pointer        = pcm_playback_pointer,
+       .page           = snd_pcm_lib_get_vmalloc_page,
+       .mmap           = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_efw_create_pcm_devices(struct snd_efw *efw)
+{
+       struct snd_pcm *pcm;
+       int err;
+
+       err = snd_pcm_new(efw->card, efw->card->driver, 0, 1, 1, &pcm);
+       if (err < 0)
+               goto end;
+
+       pcm->private_data = efw;
+       snprintf(pcm->name, sizeof(pcm->name), "%s PCM", efw->card->shortname);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+end:
+       return err;
+}
+
diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c
new file mode 100644 (file)
index 0000000..f29d4aa
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * fireworks_proc.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./fireworks.h"
+
+static inline const char*
+get_phys_name(struct snd_efw_phys_grp *grp, bool input)
+{
+       const char *const ch_type[] = {
+               "Analog", "S/PDIF", "ADAT", "S/PDIF or ADAT", "Mirroring",
+               "Headphones", "I2S", "Guitar", "Pirzo Guitar", "Guitar String",
+       };
+
+       if (grp->type < ARRAY_SIZE(ch_type))
+               return ch_type[grp->type];
+       else if (input)
+               return "Input";
+       else
+               return "Output";
+}
+
+static void
+proc_read_hwinfo(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+       struct snd_efw *efw = entry->private_data;
+       unsigned short i;
+       struct snd_efw_hwinfo *hwinfo;
+
+       hwinfo = kmalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
+       if (hwinfo == NULL)
+               return;
+
+       if (snd_efw_command_get_hwinfo(efw, hwinfo) < 0)
+               goto end;
+
+       snd_iprintf(buffer, "guid_hi: 0x%X\n", hwinfo->guid_hi);
+       snd_iprintf(buffer, "guid_lo: 0x%X\n", hwinfo->guid_lo);
+       snd_iprintf(buffer, "type: 0x%X\n", hwinfo->type);
+       snd_iprintf(buffer, "version: 0x%X\n", hwinfo->version);
+       snd_iprintf(buffer, "vendor_name: %s\n", hwinfo->vendor_name);
+       snd_iprintf(buffer, "model_name: %s\n", hwinfo->model_name);
+
+       snd_iprintf(buffer, "dsp_version: 0x%X\n", hwinfo->dsp_version);
+       snd_iprintf(buffer, "arm_version: 0x%X\n", hwinfo->arm_version);
+       snd_iprintf(buffer, "fpga_version: 0x%X\n", hwinfo->fpga_version);
+
+       snd_iprintf(buffer, "flags: 0x%X\n", hwinfo->flags);
+
+       snd_iprintf(buffer, "max_sample_rate: 0x%X\n", hwinfo->max_sample_rate);
+       snd_iprintf(buffer, "min_sample_rate: 0x%X\n", hwinfo->min_sample_rate);
+       snd_iprintf(buffer, "supported_clock: 0x%X\n",
+                   hwinfo->supported_clocks);
+
+       snd_iprintf(buffer, "phys out: 0x%X\n", hwinfo->phys_out);
+       snd_iprintf(buffer, "phys in: 0x%X\n", hwinfo->phys_in);
+
+       snd_iprintf(buffer, "phys in grps: 0x%X\n",
+                   hwinfo->phys_in_grp_count);
+       for (i = 0; i < hwinfo->phys_in_grp_count; i++) {
+               snd_iprintf(buffer,
+                           "phys in grp[0x%d]: type 0x%d, count 0x%d\n",
+                           i, hwinfo->phys_out_grps[i].type,
+                           hwinfo->phys_out_grps[i].count);
+       }
+
+       snd_iprintf(buffer, "phys out grps: 0x%X\n",
+                   hwinfo->phys_out_grp_count);
+       for (i = 0; i < hwinfo->phys_out_grp_count; i++) {
+               snd_iprintf(buffer,
+                           "phys out grps[0x%d]: type 0x%d, count 0x%d\n",
+                           i, hwinfo->phys_out_grps[i].type,
+                           hwinfo->phys_out_grps[i].count);
+       }
+
+       snd_iprintf(buffer, "amdtp rx pcm channels 1x: 0x%X\n",
+                   hwinfo->amdtp_rx_pcm_channels);
+       snd_iprintf(buffer, "amdtp tx pcm channels 1x: 0x%X\n",
+                   hwinfo->amdtp_tx_pcm_channels);
+       snd_iprintf(buffer, "amdtp rx pcm channels 2x: 0x%X\n",
+                   hwinfo->amdtp_rx_pcm_channels_2x);
+       snd_iprintf(buffer, "amdtp tx pcm channels 2x: 0x%X\n",
+                   hwinfo->amdtp_tx_pcm_channels_2x);
+       snd_iprintf(buffer, "amdtp rx pcm channels 4x: 0x%X\n",
+                   hwinfo->amdtp_rx_pcm_channels_4x);
+       snd_iprintf(buffer, "amdtp tx pcm channels 4x: 0x%X\n",
+                   hwinfo->amdtp_tx_pcm_channels_4x);
+
+       snd_iprintf(buffer, "midi out ports: 0x%X\n", hwinfo->midi_out_ports);
+       snd_iprintf(buffer, "midi in ports: 0x%X\n", hwinfo->midi_in_ports);
+
+       snd_iprintf(buffer, "mixer playback channels: 0x%X\n",
+                   hwinfo->mixer_playback_channels);
+       snd_iprintf(buffer, "mixer capture channels: 0x%X\n",
+                   hwinfo->mixer_capture_channels);
+end:
+       kfree(hwinfo);
+}
+
+static void
+proc_read_clock(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+       struct snd_efw *efw = entry->private_data;
+       enum snd_efw_clock_source clock_source;
+       unsigned int sampling_rate;
+
+       if (snd_efw_command_get_clock_source(efw, &clock_source) < 0)
+               return;
+
+       if (snd_efw_command_get_sampling_rate(efw, &sampling_rate) < 0)
+               return;
+
+       snd_iprintf(buffer, "Clock Source: %d\n", clock_source);
+       snd_iprintf(buffer, "Sampling Rate: %d\n", sampling_rate);
+}
+
+/*
+ * NOTE:
+ *  dB = 20 * log10(linear / 0x01000000)
+ *  -144.0 dB when linear is 0
+ */
+static void
+proc_read_phys_meters(struct snd_info_entry *entry,
+                     struct snd_info_buffer *buffer)
+{
+       struct snd_efw *efw = entry->private_data;
+       struct snd_efw_phys_meters *meters;
+       unsigned int g, c, m, max, size;
+       const char *name;
+       u32 *linear;
+       int err;
+
+       size = sizeof(struct snd_efw_phys_meters) +
+              (efw->phys_in + efw->phys_out) * sizeof(u32);
+       meters = kzalloc(size, GFP_KERNEL);
+       if (meters == NULL)
+               return;
+
+       err = snd_efw_command_get_phys_meters(efw, meters, size);
+       if (err < 0)
+               goto end;
+
+       snd_iprintf(buffer, "Physical Meters:\n");
+
+       m = 0;
+       max = min(efw->phys_out, meters->out_meters);
+       linear = meters->values;
+       snd_iprintf(buffer, " %d Outputs:\n", max);
+       for (g = 0; g < efw->phys_out_grp_count; g++) {
+               name = get_phys_name(&efw->phys_out_grps[g], false);
+               for (c = 0; c < efw->phys_out_grps[g].count; c++) {
+                       if (m < max)
+                               snd_iprintf(buffer, "\t%s [%d]: %d\n",
+                                           name, c, linear[m++]);
+               }
+       }
+
+       m = 0;
+       max = min(efw->phys_in, meters->in_meters);
+       linear = meters->values + meters->out_meters;
+       snd_iprintf(buffer, " %d Inputs:\n", max);
+       for (g = 0; g < efw->phys_in_grp_count; g++) {
+               name = get_phys_name(&efw->phys_in_grps[g], true);
+               for (c = 0; c < efw->phys_in_grps[g].count; c++)
+                       if (m < max)
+                               snd_iprintf(buffer, "\t%s [%d]: %d\n",
+                                           name, c, linear[m++]);
+       }
+end:
+       kfree(meters);
+}
+
+static void
+proc_read_queues_state(struct snd_info_entry *entry,
+                      struct snd_info_buffer *buffer)
+{
+       struct snd_efw *efw = entry->private_data;
+       unsigned int consumed;
+
+       if (efw->pull_ptr > efw->push_ptr)
+               consumed = snd_efw_resp_buf_size -
+                          (unsigned int)(efw->pull_ptr - efw->push_ptr);
+       else
+               consumed = (unsigned int)(efw->push_ptr - efw->pull_ptr);
+
+       snd_iprintf(buffer, "%d %d/%d\n",
+                   efw->resp_queues, consumed, snd_efw_resp_buf_size);
+}
+
+static void
+add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name,
+        void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
+{
+       struct snd_info_entry *entry;
+
+       entry = snd_info_create_card_entry(efw->card, name, root);
+       if (entry == NULL)
+               return;
+
+       snd_info_set_text_ops(entry, efw, op);
+       if (snd_info_register(entry) < 0)
+               snd_info_free_entry(entry);
+}
+
+void snd_efw_proc_init(struct snd_efw *efw)
+{
+       struct snd_info_entry *root;
+
+       /*
+        * All nodes are automatically removed at snd_card_disconnect(),
+        * by following to link list.
+        */
+       root = snd_info_create_card_entry(efw->card, "firewire",
+                                         efw->card->proc_root);
+       if (root == NULL)
+               return;
+       root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+       if (snd_info_register(root) < 0) {
+               snd_info_free_entry(root);
+               return;
+       }
+
+       add_node(efw, root, "clock", proc_read_clock);
+       add_node(efw, root, "firmware", proc_read_hwinfo);
+       add_node(efw, root, "meters", proc_read_phys_meters);
+       add_node(efw, root, "queues", proc_read_queues_state);
+}
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
new file mode 100644 (file)
index 0000000..5415690
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * fireworks_stream.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#include "./fireworks.h"
+
+#define CALLBACK_TIMEOUT       100
+
+static int
+init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
+{
+       struct cmp_connection *conn;
+       enum cmp_direction c_dir;
+       enum amdtp_stream_direction s_dir;
+       int err;
+
+       if (stream == &efw->tx_stream) {
+               conn = &efw->out_conn;
+               c_dir = CMP_OUTPUT;
+               s_dir = AMDTP_IN_STREAM;
+       } else {
+               conn = &efw->in_conn;
+               c_dir = CMP_INPUT;
+               s_dir = AMDTP_OUT_STREAM;
+       }
+
+       err = cmp_connection_init(conn, efw->unit, c_dir, 0);
+       if (err < 0)
+               goto end;
+
+       err = amdtp_stream_init(stream, efw->unit, s_dir, CIP_BLOCKING);
+       if (err < 0) {
+               amdtp_stream_destroy(stream);
+               cmp_connection_destroy(conn);
+       }
+end:
+       return err;
+}
+
+static void
+stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
+{
+       amdtp_stream_pcm_abort(stream);
+       amdtp_stream_stop(stream);
+
+       if (stream == &efw->tx_stream)
+               cmp_connection_break(&efw->out_conn);
+       else
+               cmp_connection_break(&efw->in_conn);
+}
+
+static int
+start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
+            unsigned int sampling_rate)
+{
+       struct cmp_connection *conn;
+       unsigned int mode, pcm_channels, midi_ports;
+       int err;
+
+       err = snd_efw_get_multiplier_mode(sampling_rate, &mode);
+       if (err < 0)
+               goto end;
+       if (stream == &efw->tx_stream) {
+               conn = &efw->out_conn;
+               pcm_channels = efw->pcm_capture_channels[mode];
+               midi_ports = efw->midi_out_ports;
+       } else {
+               conn = &efw->in_conn;
+               pcm_channels = efw->pcm_playback_channels[mode];
+               midi_ports = efw->midi_in_ports;
+       }
+
+       amdtp_stream_set_parameters(stream, sampling_rate,
+                                   pcm_channels, midi_ports);
+
+       /*  establish connection via CMP */
+       err = cmp_connection_establish(conn,
+                               amdtp_stream_get_max_payload(stream));
+       if (err < 0)
+               goto end;
+
+       /* start amdtp stream */
+       err = amdtp_stream_start(stream,
+                                conn->resources.channel,
+                                conn->speed);
+       if (err < 0) {
+               stop_stream(efw, stream);
+               goto end;
+       }
+
+       /* wait first callback */
+       if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
+               stop_stream(efw, stream);
+               err = -ETIMEDOUT;
+       }
+end:
+       return err;
+}
+
+static void
+destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
+{
+       stop_stream(efw, stream);
+
+       amdtp_stream_destroy(stream);
+
+       if (stream == &efw->tx_stream)
+               cmp_connection_destroy(&efw->out_conn);
+       else
+               cmp_connection_destroy(&efw->in_conn);
+}
+
+static int
+get_sync_mode(struct snd_efw *efw, enum cip_flags *sync_mode)
+{
+       enum snd_efw_clock_source clock_source;
+       int err;
+
+       err = snd_efw_command_get_clock_source(efw, &clock_source);
+       if (err < 0)
+               return err;
+
+       if (clock_source == SND_EFW_CLOCK_SOURCE_SYTMATCH)
+               return -ENOSYS;
+
+       *sync_mode = CIP_SYNC_TO_DEVICE;
+       return 0;
+}
+
+static int
+check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
+{
+       struct cmp_connection *conn;
+       bool used;
+       int err;
+
+       if (s == &efw->tx_stream)
+               conn = &efw->out_conn;
+       else
+               conn = &efw->in_conn;
+
+       err = cmp_connection_check_used(conn, &used);
+       if ((err >= 0) && used && !amdtp_stream_running(s)) {
+               dev_err(&efw->unit->device,
+                       "Connection established by others: %cPCR[%d]\n",
+                       (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+                       conn->pcr_index);
+               err = -EBUSY;
+       }
+
+       return err;
+}
+
+int snd_efw_stream_init_duplex(struct snd_efw *efw)
+{
+       int err;
+
+       err = init_stream(efw, &efw->tx_stream);
+       if (err < 0)
+               goto end;
+       /* Fireworks transmits NODATA packets with TAG0. */
+       efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
+       /* Fireworks has its own meaning for dbc. */
+       efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
+       /* Fireworks reset dbc at bus reset. */
+       efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
+       /* AudioFire9 always reports wrong dbs. */
+       if (efw->is_af9)
+               efw->tx_stream.flags |= CIP_WRONG_DBS;
+       /* Firmware version 5.5 reports fixed interval for dbc. */
+       if (efw->firmware_version == 0x5050000)
+               efw->tx_stream.tx_dbc_interval = 8;
+
+       err = init_stream(efw, &efw->rx_stream);
+       if (err < 0) {
+               destroy_stream(efw, &efw->tx_stream);
+               goto end;
+       }
+       /*
+        * Fireworks ignores MIDI messages in more than first 8 data
+        * blocks of an received AMDTP packet.
+        */
+       efw->rx_stream.rx_blocks_for_midi = 8;
+
+       /* set IEC61883 compliant mode (actually not fully compliant...) */
+       err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
+       if (err < 0) {
+               destroy_stream(efw, &efw->tx_stream);
+               destroy_stream(efw, &efw->rx_stream);
+       }
+end:
+       return err;
+}
+
+int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
+{
+       struct amdtp_stream *master, *slave;
+       atomic_t *slave_substreams;
+       enum cip_flags sync_mode;
+       unsigned int curr_rate;
+       int err = 0;
+
+       mutex_lock(&efw->mutex);
+
+       /* Need no substreams */
+       if ((atomic_read(&efw->playback_substreams) == 0) &&
+           (atomic_read(&efw->capture_substreams)  == 0))
+               goto end;
+
+       err = get_sync_mode(efw, &sync_mode);
+       if (err < 0)
+               goto end;
+       if (sync_mode == CIP_SYNC_TO_DEVICE) {
+               master = &efw->tx_stream;
+               slave  = &efw->rx_stream;
+               slave_substreams  = &efw->playback_substreams;
+       } else {
+               master = &efw->rx_stream;
+               slave  = &efw->tx_stream;
+               slave_substreams = &efw->capture_substreams;
+       }
+
+       /*
+        * Considering JACK/FFADO streaming:
+        * TODO: This can be removed hwdep functionality becomes popular.
+        */
+       err = check_connection_used_by_others(efw, master);
+       if (err < 0)
+               goto end;
+
+       /* packet queueing error */
+       if (amdtp_streaming_error(slave))
+               stop_stream(efw, slave);
+       if (amdtp_streaming_error(master))
+               stop_stream(efw, master);
+
+       /* stop streams if rate is different */
+       err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
+       if (err < 0)
+               goto end;
+       if (rate == 0)
+               rate = curr_rate;
+       if (rate != curr_rate) {
+               stop_stream(efw, slave);
+               stop_stream(efw, master);
+       }
+
+       /* master should be always running */
+       if (!amdtp_stream_running(master)) {
+               amdtp_stream_set_sync(sync_mode, master, slave);
+               efw->master = master;
+
+               err = snd_efw_command_set_sampling_rate(efw, rate);
+               if (err < 0)
+                       goto end;
+
+               err = start_stream(efw, master, rate);
+               if (err < 0) {
+                       dev_err(&efw->unit->device,
+                               "fail to start AMDTP master stream:%d\n", err);
+                       goto end;
+               }
+       }
+
+       /* start slave if needed */
+       if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) {
+               err = start_stream(efw, slave, rate);
+               if (err < 0) {
+                       dev_err(&efw->unit->device,
+                               "fail to start AMDTP slave stream:%d\n", err);
+                       stop_stream(efw, master);
+               }
+       }
+end:
+       mutex_unlock(&efw->mutex);
+       return err;
+}
+
+void snd_efw_stream_stop_duplex(struct snd_efw *efw)
+{
+       struct amdtp_stream *master, *slave;
+       atomic_t *master_substreams, *slave_substreams;
+
+       mutex_lock(&efw->mutex);
+
+       if (efw->master == &efw->rx_stream) {
+               slave  = &efw->tx_stream;
+               master = &efw->rx_stream;
+               slave_substreams  = &efw->capture_substreams;
+               master_substreams = &efw->playback_substreams;
+       } else {
+               slave  = &efw->rx_stream;
+               master = &efw->tx_stream;
+               slave_substreams  = &efw->playback_substreams;
+               master_substreams = &efw->capture_substreams;
+       }
+
+       if (atomic_read(slave_substreams) == 0) {
+               stop_stream(efw, slave);
+
+               if (atomic_read(master_substreams) == 0)
+                       stop_stream(efw, master);
+       }
+
+       mutex_unlock(&efw->mutex);
+}
+
+void snd_efw_stream_update_duplex(struct snd_efw *efw)
+{
+       if ((cmp_connection_update(&efw->out_conn) < 0) ||
+           (cmp_connection_update(&efw->in_conn) < 0)) {
+               mutex_lock(&efw->mutex);
+               stop_stream(efw, &efw->rx_stream);
+               stop_stream(efw, &efw->tx_stream);
+               mutex_unlock(&efw->mutex);
+       } else {
+               amdtp_stream_update(&efw->rx_stream);
+               amdtp_stream_update(&efw->tx_stream);
+       }
+}
+
+void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
+{
+       mutex_lock(&efw->mutex);
+
+       destroy_stream(efw, &efw->rx_stream);
+       destroy_stream(efw, &efw->tx_stream);
+
+       mutex_unlock(&efw->mutex);
+}
+
+void snd_efw_stream_lock_changed(struct snd_efw *efw)
+{
+       efw->dev_lock_changed = true;
+       wake_up(&efw->hwdep_wait);
+}
+
+int snd_efw_stream_lock_try(struct snd_efw *efw)
+{
+       int err;
+
+       spin_lock_irq(&efw->lock);
+
+       /* user land lock this */
+       if (efw->dev_lock_count < 0) {
+               err = -EBUSY;
+               goto end;
+       }
+
+       /* this is the first time */
+       if (efw->dev_lock_count++ == 0)
+               snd_efw_stream_lock_changed(efw);
+       err = 0;
+end:
+       spin_unlock_irq(&efw->lock);
+       return err;
+}
+
+void snd_efw_stream_lock_release(struct snd_efw *efw)
+{
+       spin_lock_irq(&efw->lock);
+
+       if (WARN_ON(efw->dev_lock_count <= 0))
+               goto end;
+       if (--efw->dev_lock_count == 0)
+               snd_efw_stream_lock_changed(efw);
+end:
+       spin_unlock_irq(&efw->lock);
+}
diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c
new file mode 100644 (file)
index 0000000..aa56b8a
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * fireworks_transaction.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * Fireworks have its own transaction. The transaction can be delivered by AV/C
+ * Vendor Specific command. But at least Windows driver and firmware version 5.5
+ * or later don't use it.
+ *
+ * Transaction substance:
+ *  At first, 6 data exist. Following to the 6 data, parameters for each
+ *  commands exists. All of parameters are 32 bit alighed to big endian.
+ *   data[0]:  Length of transaction substance
+ *   data[1]:  Transaction version
+ *   data[2]:  Sequence number. This is incremented by the device
+ *   data[3]:  transaction category
+ *   data[4]:  transaction command
+ *   data[5]:  return value in response.
+ *   data[6-]: parameters
+ *
+ * Transaction address:
+ *  command:   0xecc000000000
+ *  response:  0xecc080000000 (default)
+ *
+ * I note that the address for response can be changed by command. But this
+ * module uses the default address.
+ */
+#include "./fireworks.h"
+
+#define MEMORY_SPACE_EFW_COMMAND       0xecc000000000ULL
+#define MEMORY_SPACE_EFW_RESPONSE      0xecc080000000ULL
+
+#define ERROR_RETRIES 3
+#define ERROR_DELAY_MS 5
+#define EFC_TIMEOUT_MS 125
+
+static DEFINE_SPINLOCK(instances_lock);
+static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+static DEFINE_SPINLOCK(transaction_queues_lock);
+static LIST_HEAD(transaction_queues);
+
+enum transaction_queue_state {
+       STATE_PENDING,
+       STATE_BUS_RESET,
+       STATE_COMPLETE
+};
+
+struct transaction_queue {
+       struct list_head list;
+       struct fw_unit *unit;
+       void *buf;
+       unsigned int size;
+       u32 seqnum;
+       enum transaction_queue_state state;
+       wait_queue_head_t wait;
+};
+
+int snd_efw_transaction_cmd(struct fw_unit *unit,
+                           const void *cmd, unsigned int size)
+{
+       return snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST,
+                                 MEMORY_SPACE_EFW_COMMAND,
+                                 (void *)cmd, size, 0);
+}
+
+int snd_efw_transaction_run(struct fw_unit *unit,
+                           const void *cmd, unsigned int cmd_size,
+                           void *resp, unsigned int resp_size)
+{
+       struct transaction_queue t;
+       unsigned int tries;
+       int ret;
+
+       t.unit = unit;
+       t.buf = resp;
+       t.size = resp_size;
+       t.seqnum = be32_to_cpu(((struct snd_efw_transaction *)cmd)->seqnum) + 1;
+       t.state = STATE_PENDING;
+       init_waitqueue_head(&t.wait);
+
+       spin_lock_irq(&transaction_queues_lock);
+       list_add_tail(&t.list, &transaction_queues);
+       spin_unlock_irq(&transaction_queues_lock);
+
+       tries = 0;
+       do {
+               ret = snd_efw_transaction_cmd(t.unit, (void *)cmd, cmd_size);
+               if (ret < 0)
+                       break;
+
+               wait_event_timeout(t.wait, t.state != STATE_PENDING,
+                                  msecs_to_jiffies(EFC_TIMEOUT_MS));
+
+               if (t.state == STATE_COMPLETE) {
+                       ret = t.size;
+                       break;
+               } else if (t.state == STATE_BUS_RESET) {
+                       msleep(ERROR_DELAY_MS);
+               } else if (++tries >= ERROR_RETRIES) {
+                       dev_err(&t.unit->device, "EFW transaction timed out\n");
+                       ret = -EIO;
+                       break;
+               }
+       } while (1);
+
+       spin_lock_irq(&transaction_queues_lock);
+       list_del(&t.list);
+       spin_unlock_irq(&transaction_queues_lock);
+
+       return ret;
+}
+
+static void
+copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
+{
+       size_t capacity, till_end;
+       struct snd_efw_transaction *t;
+
+       spin_lock_irq(&efw->lock);
+
+       t = (struct snd_efw_transaction *)data;
+       length = min_t(size_t, t->length * sizeof(t->length), length);
+
+       if (efw->push_ptr < efw->pull_ptr)
+               capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr);
+       else
+               capacity = snd_efw_resp_buf_size -
+                          (unsigned int)(efw->push_ptr - efw->pull_ptr);
+
+       /* confirm enough space for this response */
+       if (capacity < length) {
+               *rcode = RCODE_CONFLICT_ERROR;
+               goto end;
+       }
+
+       /* copy to ring buffer */
+       while (length > 0) {
+               till_end = snd_efw_resp_buf_size -
+                          (unsigned int)(efw->push_ptr - efw->resp_buf);
+               till_end = min_t(unsigned int, length, till_end);
+
+               memcpy(efw->push_ptr, data, till_end);
+
+               efw->push_ptr += till_end;
+               if (efw->push_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
+                       efw->push_ptr = efw->resp_buf;
+
+               length -= till_end;
+               data += till_end;
+       }
+
+       /* for hwdep */
+       efw->resp_queues++;
+       wake_up(&efw->hwdep_wait);
+
+       *rcode = RCODE_COMPLETE;
+end:
+       spin_unlock_irq(&efw->lock);
+}
+
+static void
+handle_resp_for_user(struct fw_card *card, int generation, int source,
+                    void *data, size_t length, int *rcode)
+{
+       struct fw_device *device;
+       struct snd_efw *efw;
+       unsigned int i;
+
+       spin_lock_irq(&instances_lock);
+
+       for (i = 0; i < SNDRV_CARDS; i++) {
+               efw = instances[i];
+               if (efw == NULL)
+                       continue;
+               device = fw_parent_device(efw->unit);
+               if ((device->card != card) ||
+                   (device->generation != generation))
+                       continue;
+               smp_rmb();      /* node id vs. generation */
+               if (device->node_id != source)
+                       continue;
+
+               break;
+       }
+       if (i == SNDRV_CARDS)
+               goto end;
+
+       copy_resp_to_buf(efw, data, length, rcode);
+end:
+       spin_unlock_irq(&instances_lock);
+}
+
+static void
+handle_resp_for_kernel(struct fw_card *card, int generation, int source,
+                      void *data, size_t length, int *rcode, u32 seqnum)
+{
+       struct fw_device *device;
+       struct transaction_queue *t;
+       unsigned long flags;
+
+       spin_lock_irqsave(&transaction_queues_lock, flags);
+       list_for_each_entry(t, &transaction_queues, list) {
+               device = fw_parent_device(t->unit);
+               if ((device->card != card) ||
+                   (device->generation != generation))
+                       continue;
+               smp_rmb();      /* node_id vs. generation */
+               if (device->node_id != source)
+                       continue;
+
+               if ((t->state == STATE_PENDING) && (t->seqnum == seqnum)) {
+                       t->state = STATE_COMPLETE;
+                       t->size = min_t(unsigned int, length, t->size);
+                       memcpy(t->buf, data, t->size);
+                       wake_up(&t->wait);
+                       *rcode = RCODE_COMPLETE;
+               }
+       }
+       spin_unlock_irqrestore(&transaction_queues_lock, flags);
+}
+
+static void
+efw_response(struct fw_card *card, struct fw_request *request,
+            int tcode, int destination, int source,
+            int generation, unsigned long long offset,
+            void *data, size_t length, void *callback_data)
+{
+       int rcode, dummy;
+       u32 seqnum;
+
+       rcode = RCODE_TYPE_ERROR;
+       if (length < sizeof(struct snd_efw_transaction)) {
+               rcode = RCODE_DATA_ERROR;
+               goto end;
+       } else if (offset != MEMORY_SPACE_EFW_RESPONSE) {
+               rcode = RCODE_ADDRESS_ERROR;
+               goto end;
+       }
+
+       seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);
+       if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 1) {
+               handle_resp_for_kernel(card, generation, source,
+                                      data, length, &rcode, seqnum);
+               if (snd_efw_resp_buf_debug)
+                       handle_resp_for_user(card, generation, source,
+                                            data, length, &dummy);
+       } else {
+               handle_resp_for_user(card, generation, source,
+                                    data, length, &rcode);
+       }
+end:
+       fw_send_response(card, request, rcode);
+}
+
+void snd_efw_transaction_add_instance(struct snd_efw *efw)
+{
+       unsigned int i;
+
+       spin_lock_irq(&instances_lock);
+
+       for (i = 0; i < SNDRV_CARDS; i++) {
+               if (instances[i] != NULL)
+                       continue;
+               instances[i] = efw;
+               break;
+       }
+
+       spin_unlock_irq(&instances_lock);
+}
+
+void snd_efw_transaction_remove_instance(struct snd_efw *efw)
+{
+       unsigned int i;
+
+       spin_lock_irq(&instances_lock);
+
+       for (i = 0; i < SNDRV_CARDS; i++) {
+               if (instances[i] != efw)
+                       continue;
+               instances[i] = NULL;
+       }
+
+       spin_unlock_irq(&instances_lock);
+}
+
+void snd_efw_transaction_bus_reset(struct fw_unit *unit)
+{
+       struct transaction_queue *t;
+
+       spin_lock_irq(&transaction_queues_lock);
+       list_for_each_entry(t, &transaction_queues, list) {
+               if ((t->unit == unit) &&
+                   (t->state == STATE_PENDING)) {
+                       t->state = STATE_BUS_RESET;
+                       wake_up(&t->wait);
+               }
+       }
+       spin_unlock_irq(&transaction_queues_lock);
+}
+
+static struct fw_address_handler resp_register_handler = {
+       .length = SND_EFW_RESPONSE_MAXIMUM_BYTES,
+       .address_callback = efw_response
+};
+
+int snd_efw_transaction_register(void)
+{
+       static const struct fw_address_region resp_register_region = {
+               .start  = MEMORY_SPACE_EFW_RESPONSE,
+               .end    = MEMORY_SPACE_EFW_RESPONSE +
+                         SND_EFW_RESPONSE_MAXIMUM_BYTES
+       };
+       return fw_core_add_address_handler(&resp_register_handler,
+                                          &resp_register_region);
+}
+
+void snd_efw_transaction_unregister(void)
+{
+       WARN_ON(!list_empty(&transaction_queues));
+       fw_core_remove_address_handler(&resp_register_handler);
+}
index 9f7ef21..768d40d 100644 (file)
@@ -51,7 +51,7 @@ struct fwspk {
        const struct device_info *device_info;
        struct mutex mutex;
        struct cmp_connection connection;
-       struct amdtp_out_stream stream;
+       struct amdtp_stream stream;
        bool mute;
        s16 volume[6];
        s16 volume_min;
@@ -167,13 +167,7 @@ static int fwspk_open(struct snd_pcm_substream *substream)
        if (err < 0)
                return err;
 
-       err = snd_pcm_hw_constraint_minmax(runtime,
-                                          SNDRV_PCM_HW_PARAM_PERIOD_TIME,
-                                          5000, UINT_MAX);
-       if (err < 0)
-               return err;
-
-       err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+       err = amdtp_stream_add_pcm_hw_constraints(&fwspk->stream, runtime);
        if (err < 0)
                return err;
 
@@ -187,48 +181,12 @@ static int fwspk_close(struct snd_pcm_substream *substream)
 
 static void fwspk_stop_stream(struct fwspk *fwspk)
 {
-       if (amdtp_out_stream_running(&fwspk->stream)) {
-               amdtp_out_stream_stop(&fwspk->stream);
+       if (amdtp_stream_running(&fwspk->stream)) {
+               amdtp_stream_stop(&fwspk->stream);
                cmp_connection_break(&fwspk->connection);
        }
 }
 
-static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc)
-{
-       u8 *buf;
-       int err;
-
-       buf = kmalloc(8, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       buf[0] = 0x00;          /* AV/C, CONTROL */
-       buf[1] = 0xff;          /* unit */
-       buf[2] = 0x19;          /* INPUT PLUG SIGNAL FORMAT */
-       buf[3] = 0x00;          /* plug 0 */
-       buf[4] = 0x90;          /* format: audio */
-       buf[5] = 0x00 | sfc;    /* AM824, frequency */
-       buf[6] = 0xff;          /* SYT (not used) */
-       buf[7] = 0xff;
-
-       err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8,
-                                 BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
-       if (err < 0)
-               goto error;
-       if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) {
-               dev_err(&fwspk->unit->device, "failed to set sample rate\n");
-               err = -EIO;
-               goto error;
-       }
-
-       err = 0;
-
-error:
-       kfree(buf);
-
-       return err;
-}
-
 static int fwspk_hw_params(struct snd_pcm_substream *substream,
                           struct snd_pcm_hw_params *hw_params)
 {
@@ -244,17 +202,20 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream,
        if (err < 0)
                goto error;
 
-       amdtp_out_stream_set_parameters(&fwspk->stream,
-                                       params_rate(hw_params),
-                                       params_channels(hw_params),
-                                       0);
+       amdtp_stream_set_parameters(&fwspk->stream,
+                                   params_rate(hw_params),
+                                   params_channels(hw_params),
+                                   0);
 
-       amdtp_out_stream_set_pcm_format(&fwspk->stream,
-                                       params_format(hw_params));
+       amdtp_stream_set_pcm_format(&fwspk->stream,
+                                   params_format(hw_params));
 
-       err = fwspk_set_rate(fwspk, fwspk->stream.sfc);
-       if (err < 0)
+       err = avc_general_set_sig_fmt(fwspk->unit, params_rate(hw_params),
+                                     AVC_GENERAL_PLUG_DIR_IN, 0);
+       if (err < 0) {
+               dev_err(&fwspk->unit->device, "failed to set sample rate\n");
                goto err_buffer;
+       }
 
        return 0;
 
@@ -282,25 +243,25 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
 
        mutex_lock(&fwspk->mutex);
 
-       if (amdtp_out_streaming_error(&fwspk->stream))
+       if (amdtp_streaming_error(&fwspk->stream))
                fwspk_stop_stream(fwspk);
 
-       if (!amdtp_out_stream_running(&fwspk->stream)) {
+       if (!amdtp_stream_running(&fwspk->stream)) {
                err = cmp_connection_establish(&fwspk->connection,
-                       amdtp_out_stream_get_max_payload(&fwspk->stream));
+                       amdtp_stream_get_max_payload(&fwspk->stream));
                if (err < 0)
                        goto err_mutex;
 
-               err = amdtp_out_stream_start(&fwspk->stream,
-                                       fwspk->connection.resources.channel,
-                                       fwspk->connection.speed);
+               err = amdtp_stream_start(&fwspk->stream,
+                                        fwspk->connection.resources.channel,
+                                        fwspk->connection.speed);
                if (err < 0)
                        goto err_connection;
        }
 
        mutex_unlock(&fwspk->mutex);
 
-       amdtp_out_stream_pcm_prepare(&fwspk->stream);
+       amdtp_stream_pcm_prepare(&fwspk->stream);
 
        return 0;
 
@@ -327,7 +288,7 @@ static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd)
        default:
                return -EINVAL;
        }
-       amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm);
+       amdtp_stream_pcm_trigger(&fwspk->stream, pcm);
        return 0;
 }
 
@@ -335,7 +296,7 @@ static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream)
 {
        struct fwspk *fwspk = substream->private_data;
 
-       return amdtp_out_stream_pcm_pointer(&fwspk->stream);
+       return amdtp_stream_pcm_pointer(&fwspk->stream);
 }
 
 static int fwspk_create_pcm(struct fwspk *fwspk)
@@ -653,7 +614,7 @@ static void fwspk_card_free(struct snd_card *card)
 {
        struct fwspk *fwspk = card->private_data;
 
-       amdtp_out_stream_destroy(&fwspk->stream);
+       amdtp_stream_destroy(&fwspk->stream);
        cmp_connection_destroy(&fwspk->connection);
        fw_unit_put(fwspk->unit);
        mutex_destroy(&fwspk->mutex);
@@ -679,11 +640,12 @@ static int fwspk_probe(struct fw_unit *unit,
        fwspk->unit = fw_unit_get(unit);
        fwspk->device_info = (const struct device_info *)id->driver_data;
 
-       err = cmp_connection_init(&fwspk->connection, unit, 0);
+       err = cmp_connection_init(&fwspk->connection, unit, CMP_INPUT, 0);
        if (err < 0)
                goto err_unit;
 
-       err = amdtp_out_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING);
+       err = amdtp_stream_init(&fwspk->stream, unit, AMDTP_OUT_STREAM,
+                               CIP_NONBLOCKING);
        if (err < 0)
                goto err_connection;
 
@@ -733,21 +695,21 @@ static void fwspk_bus_reset(struct fw_unit *unit)
        fcp_bus_reset(fwspk->unit);
 
        if (cmp_connection_update(&fwspk->connection) < 0) {
-               amdtp_out_stream_pcm_abort(&fwspk->stream);
+               amdtp_stream_pcm_abort(&fwspk->stream);
                mutex_lock(&fwspk->mutex);
                fwspk_stop_stream(fwspk);
                mutex_unlock(&fwspk->mutex);
                return;
        }
 
-       amdtp_out_stream_update(&fwspk->stream);
+       amdtp_stream_update(&fwspk->stream);
 }
 
 static void fwspk_remove(struct fw_unit *unit)
 {
        struct fwspk *fwspk = dev_get_drvdata(&unit->device);
 
-       amdtp_out_stream_pcm_abort(&fwspk->stream);
+       amdtp_stream_pcm_abort(&fwspk->stream);
        snd_card_disconnect(fwspk->card);
 
        mutex_lock(&fwspk->mutex);
index 5abbbe4..ad55e5c 100644 (file)
@@ -442,17 +442,11 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus)
        for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) {
                for (i = 0; i < 8; ++i)
                        iwave[i] = snd_gf1_peek(gus, bank_pos + i);
-#ifdef CONFIG_SND_DEBUG_ROM
-               printk(KERN_DEBUG "ROM at 0x%06x = %8phC\n", bank_pos, iwave);
-#endif
                if (strncmp(iwave, "INTRWAVE", 8))
                        continue;       /* first check */
                csum = 0;
                for (i = 0; i < sizeof(struct rom_hdr); i++)
                        csum += snd_gf1_peek(gus, bank_pos + i);
-#ifdef CONFIG_SND_DEBUG_ROM
-               printk(KERN_DEBUG "ROM checksum = 0x%x (computed)\n", csum);
-#endif
                if (csum != 0)
                        continue;       /* not valid rom */
                gus->gf1.rom_banks++;
index d10ef76..fbcaa54 100644 (file)
@@ -648,14 +648,14 @@ static int au1000_ac97_probe(struct platform_device *pdev)
                goto out;
 
        err = -EBUSY;
-       au1000->ac97_res_port = request_mem_region(r->start,
-                                       r->end - r->start + 1, pdev->name);
+       au1000->ac97_res_port = request_mem_region(r->start, resource_size(r),
+                                                  pdev->name);
        if (!au1000->ac97_res_port) {
                snd_printk(KERN_ERR "ALSA AC97: can't grab AC97 port\n");
                goto out;
        }
 
-       io = ioremap(r->start, r->end - r->start + 1);
+       io = ioremap(r->start, resource_size(r));
        if (!io)
                goto out;
 
index 25e4609..3bbc3ec 100644 (file)
@@ -567,7 +567,6 @@ static int mpu401_out(int dev, unsigned char midi_byte)
 static int mpu401_command(int dev, mpu_command_rec * cmd)
 {
        int i, timeout, ok;
-       int ret = 0;
        unsigned long   flags;
        struct mpu_config *devc;
 
@@ -644,7 +643,6 @@ retry:
                        }
                }
        }
-       ret = 0;
        cmd->data[0] = 0;
 
        if (cmd->nr_returns)
@@ -666,7 +664,7 @@ retry:
                }
        }
        spin_unlock_irqrestore(&devc->lock,flags);
-       return ret;
+       return 0;
 }
 
 static int mpu_cmd(int dev, int cmd, int data)
index f851fd0..a33e8ce 100644 (file)
@@ -2625,15 +2625,12 @@ static int __init cs4297a_init(void)
        u32 pwr, id;
        mm_segment_t fs;
        int rval;
-#ifndef CONFIG_BCM_CS4297A_CSWARM
        u64 cfg;
        int mdio_val;
-#endif
 
        CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO 
                "cs4297a: cs4297a_init_module()+ \n"));
 
-#ifndef CONFIG_BCM_CS4297A_CSWARM
         mdio_val = __raw_readq(KSEG1 + A_MAC_REGISTER(2, R_MAC_MDIO)) &
                 (M_MAC_MDIO_DIR|M_MAC_MDIO_OUT);
 
@@ -2659,7 +2656,6 @@ static int __init cs4297a_init(void)
         __raw_writeq(mdio_val | M_MAC_GENC, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO));
         /* Give the codec some time to finish resetting (start the bit clock) */
         udelay(100);
-#endif
 
        if (!(s = kzalloc(sizeof(struct cs4297a_state), GFP_KERNEL))) {
                CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
index db18cca..529f5f4 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/io.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/module.h>
@@ -34,8 +35,6 @@
 #include <sound/opl3.h>
 #include <sound/initval.h>
 
-#include <asm/io.h>
-
 #ifdef CONFIG_SND_FM801_TEA575X_BOOL
 #include <media/tea575x.h>
 #endif
@@ -80,7 +79,10 @@ MODULE_PARM_DESC(radio_nr, "Radio device numbers");
  *  Direct registers
  */
 
-#define FM801_REG(chip, reg)   (chip->port + FM801_##reg)
+#define fm801_writew(chip,reg,value)   outw((value), chip->port + FM801_##reg)
+#define fm801_readw(chip,reg)          inw(chip->port + FM801_##reg)
+
+#define fm801_writel(chip,reg,value)   outl((value), chip->port + FM801_##reg)
 
 #define FM801_PCM_VOL          0x00    /* PCM Output Volume */
 #define FM801_FM_VOL           0x02    /* FM Output Volume */
@@ -156,21 +158,27 @@ MODULE_PARM_DESC(radio_nr, "Radio device numbers");
 #define FM801_GPIO_GS3         (1<<15)
 #define FM801_GPIO_GS(x)       (1<<(12+(x)))
        
-/*
-
+/**
+ * struct fm801 - describes FM801 chip
+ * @port:              I/O port number
+ * @multichannel:      multichannel support
+ * @secondary:         secondary codec
+ * @secondary_addr:    address of the secondary codec
+ * @tea575x_tuner:     tuner access method & flags
+ * @ply_ctrl:          playback control
+ * @cap_ctrl:          capture control
  */
-
 struct fm801 {
        int irq;
 
-       unsigned long port;     /* I/O port number */
-       unsigned int multichannel: 1,   /* multichannel support */
-                    secondary: 1;      /* secondary codec */
-       unsigned char secondary_addr;   /* address of the secondary codec */
-       unsigned int tea575x_tuner;     /* tuner access method & flags */
+       unsigned long port;
+       unsigned int multichannel: 1,
+                    secondary: 1;
+       unsigned char secondary_addr;
+       unsigned int tea575x_tuner;
 
-       unsigned short ply_ctrl; /* playback control */
-       unsigned short cap_ctrl; /* capture control */
+       unsigned short ply_ctrl;
+       unsigned short cap_ctrl;
 
        unsigned long ply_buffer;
        unsigned int ply_buf;
@@ -222,6 +230,30 @@ MODULE_DEVICE_TABLE(pci, snd_fm801_ids);
  *  common I/O routines
  */
 
+static bool fm801_ac97_is_ready(struct fm801 *chip, unsigned int iterations)
+{
+       unsigned int idx;
+
+       for (idx = 0; idx < iterations; idx++) {
+               if (!(fm801_readw(chip, AC97_CMD) & FM801_AC97_BUSY))
+                       return true;
+               udelay(10);
+       }
+       return false;
+}
+
+static bool fm801_ac97_is_valid(struct fm801 *chip, unsigned int iterations)
+{
+       unsigned int idx;
+
+       for (idx = 0; idx < iterations; idx++) {
+               if (fm801_readw(chip, AC97_CMD) & FM801_AC97_VALID)
+                       return true;
+               udelay(10);
+       }
+       return false;
+}
+
 static int snd_fm801_update_bits(struct fm801 *chip, unsigned short reg,
                                 unsigned short mask, unsigned short value)
 {
@@ -244,73 +276,54 @@ static void snd_fm801_codec_write(struct snd_ac97 *ac97,
                                  unsigned short val)
 {
        struct fm801 *chip = ac97->private_data;
-       int idx;
 
        /*
         *  Wait until the codec interface is not ready..
         */
-       for (idx = 0; idx < 100; idx++) {
-               if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
-                       goto ok1;
-               udelay(10);
+       if (!fm801_ac97_is_ready(chip, 100)) {
+               dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
+               return;
        }
-       dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
-       return;
 
- ok1:
        /* write data and address */
-       outw(val, FM801_REG(chip, AC97_DATA));
-       outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
+       fm801_writew(chip, AC97_DATA, val);
+       fm801_writew(chip, AC97_CMD, reg | (ac97->addr << FM801_AC97_ADDR_SHIFT));
        /*
         *  Wait until the write command is not completed..
-         */
-       for (idx = 0; idx < 1000; idx++) {
-               if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
-                       return;
-               udelay(10);
-       }
-       dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n", ac97->num);
+        */
+       if (!fm801_ac97_is_ready(chip, 1000))
+               dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n",
+               ac97->num);
 }
 
 static unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short reg)
 {
        struct fm801 *chip = ac97->private_data;
-       int idx;
 
        /*
         *  Wait until the codec interface is not ready..
         */
-       for (idx = 0; idx < 100; idx++) {
-               if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
-                       goto ok1;
-               udelay(10);
+       if (!fm801_ac97_is_ready(chip, 100)) {
+               dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
+               return 0;
        }
-       dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
-       return 0;
 
- ok1:
        /* read command */
-       outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ,
-            FM801_REG(chip, AC97_CMD));
-       for (idx = 0; idx < 100; idx++) {
-               if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
-                       goto ok2;
-               udelay(10);
+       fm801_writew(chip, AC97_CMD,
+                    reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
+       if (!fm801_ac97_is_ready(chip, 100)) {
+               dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n",
+                       ac97->num);
+               return 0;
        }
-       dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n", ac97->num);
-       return 0;
 
- ok2:
-       for (idx = 0; idx < 1000; idx++) {
-               if (inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_VALID)
-                       goto ok3;
-               udelay(10);
+       if (!fm801_ac97_is_valid(chip, 1000)) {
+               dev_err(chip->card->dev,
+                       "AC'97 interface #%d is not valid (2)\n", ac97->num);
+               return 0;
        }
-       dev_err(chip->card->dev, "AC'97 interface #%d is not valid (2)\n", ac97->num);
-       return 0;
 
- ok3:
-       return inw(FM801_REG(chip, AC97_DATA));
+       return fm801_readw(chip, AC97_DATA);
 }
 
 static unsigned int rates[] = {
@@ -384,7 +397,7 @@ static int snd_fm801_playback_trigger(struct snd_pcm_substream *substream,
                snd_BUG();
                return -EINVAL;
        }
-       outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
+       fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
        spin_unlock(&chip->reg_lock);
        return 0;
 }
@@ -419,7 +432,7 @@ static int snd_fm801_capture_trigger(struct snd_pcm_substream *substream,
                snd_BUG();
                return -EINVAL;
        }
-       outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
+       fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
        spin_unlock(&chip->reg_lock);
        return 0;
 }
@@ -457,12 +470,13 @@ static int snd_fm801_playback_prepare(struct snd_pcm_substream *substream)
        }
        chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
        chip->ply_buf = 0;
-       outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
-       outw(chip->ply_count - 1, FM801_REG(chip, PLY_COUNT));
+       fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
+       fm801_writew(chip, PLY_COUNT, chip->ply_count - 1);
        chip->ply_buffer = runtime->dma_addr;
        chip->ply_pos = 0;
-       outl(chip->ply_buffer, FM801_REG(chip, PLY_BUF1));
-       outl(chip->ply_buffer + (chip->ply_count % chip->ply_size), FM801_REG(chip, PLY_BUF2));
+       fm801_writel(chip, PLY_BUF1, chip->ply_buffer);
+       fm801_writel(chip, PLY_BUF2,
+                    chip->ply_buffer + (chip->ply_count % chip->ply_size));
        spin_unlock_irq(&chip->reg_lock);
        return 0;
 }
@@ -483,12 +497,13 @@ static int snd_fm801_capture_prepare(struct snd_pcm_substream *substream)
                chip->cap_ctrl |= FM801_STEREO;
        chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
        chip->cap_buf = 0;
-       outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
-       outw(chip->cap_count - 1, FM801_REG(chip, CAP_COUNT));
+       fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
+       fm801_writew(chip, CAP_COUNT, chip->cap_count - 1);
        chip->cap_buffer = runtime->dma_addr;
        chip->cap_pos = 0;
-       outl(chip->cap_buffer, FM801_REG(chip, CAP_BUF1));
-       outl(chip->cap_buffer + (chip->cap_count % chip->cap_size), FM801_REG(chip, CAP_BUF2));
+       fm801_writel(chip, CAP_BUF1, chip->cap_buffer);
+       fm801_writel(chip, CAP_BUF2,
+                    chip->cap_buffer + (chip->cap_count % chip->cap_size));
        spin_unlock_irq(&chip->reg_lock);
        return 0;
 }
@@ -501,8 +516,8 @@ static snd_pcm_uframes_t snd_fm801_playback_pointer(struct snd_pcm_substream *su
        if (!(chip->ply_ctrl & FM801_START))
                return 0;
        spin_lock(&chip->reg_lock);
-       ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT));
-       if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) {
+       ptr = chip->ply_pos + (chip->ply_count - 1) - fm801_readw(chip, PLY_COUNT);
+       if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_PLAYBACK) {
                ptr += chip->ply_count;
                ptr %= chip->ply_size;
        }
@@ -518,8 +533,8 @@ static snd_pcm_uframes_t snd_fm801_capture_pointer(struct snd_pcm_substream *sub
        if (!(chip->cap_ctrl & FM801_START))
                return 0;
        spin_lock(&chip->reg_lock);
-       ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT));
-       if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) {
+       ptr = chip->cap_pos + (chip->cap_count - 1) - fm801_readw(chip, CAP_COUNT);
+       if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_CAPTURE) {
                ptr += chip->cap_count;
                ptr %= chip->cap_size;
        }
@@ -533,12 +548,12 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
        unsigned short status;
        unsigned int tmp;
 
-       status = inw(FM801_REG(chip, IRQ_STATUS));
+       status = fm801_readw(chip, IRQ_STATUS);
        status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME;
        if (! status)
                return IRQ_NONE;
        /* ack first */
-       outw(status, FM801_REG(chip, IRQ_STATUS));
+       fm801_writew(chip, IRQ_STATUS, status);
        if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) {
                spin_lock(&chip->reg_lock);
                chip->ply_buf++;
@@ -546,10 +561,10 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
                chip->ply_pos %= chip->ply_size;
                tmp = chip->ply_pos + chip->ply_count;
                tmp %= chip->ply_size;
-               outl(chip->ply_buffer + tmp,
-                               (chip->ply_buf & 1) ?
-                                       FM801_REG(chip, PLY_BUF1) :
-                                       FM801_REG(chip, PLY_BUF2));
+               if (chip->ply_buf & 1)
+                       fm801_writel(chip, PLY_BUF1, chip->ply_buffer + tmp);
+               else
+                       fm801_writel(chip, PLY_BUF2, chip->ply_buffer + tmp);
                spin_unlock(&chip->reg_lock);
                snd_pcm_period_elapsed(chip->playback_substream);
        }
@@ -560,10 +575,10 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
                chip->cap_pos %= chip->cap_size;
                tmp = chip->cap_pos + chip->cap_count;
                tmp %= chip->cap_size;
-               outl(chip->cap_buffer + tmp,
-                               (chip->cap_buf & 1) ?
-                                       FM801_REG(chip, CAP_BUF1) :
-                                       FM801_REG(chip, CAP_BUF2));
+               if (chip->cap_buf & 1)
+                       fm801_writel(chip, CAP_BUF1, chip->cap_buffer + tmp);
+               else
+                       fm801_writel(chip, CAP_BUF2, chip->cap_buffer + tmp);
                spin_unlock(&chip->reg_lock);
                snd_pcm_period_elapsed(chip->capture_substream);
        }
@@ -747,7 +762,7 @@ static struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = {
 static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
 {
        struct fm801 *chip = tea->private_data;
-       unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+       unsigned short reg = fm801_readw(chip, GPIO_CTRL);
        struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
 
        reg &= ~(FM801_GPIO_GP(gpio.data) |
@@ -759,13 +774,13 @@ static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
        /* WRITE_ENABLE is inverted */
        reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren);
 
-       outw(reg, FM801_REG(chip, GPIO_CTRL));
+       fm801_writew(chip, GPIO_CTRL, reg);
 }
 
 static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea)
 {
        struct fm801 *chip = tea->private_data;
-       unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+       unsigned short reg = fm801_readw(chip, GPIO_CTRL);
        struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
        u8 ret;
 
@@ -780,7 +795,7 @@ static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea)
 static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output)
 {
        struct fm801 *chip = tea->private_data;
-       unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
+       unsigned short reg = fm801_readw(chip, GPIO_CTRL);
        struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
 
        /* use GPIO lines and set write enable bit */
@@ -811,7 +826,7 @@ static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output
                         FM801_GPIO_GP(gpio.clk));
        }
 
-       outw(reg, FM801_REG(chip, GPIO_CTRL));
+       fm801_writew(chip, GPIO_CTRL, reg);
 }
 
 static struct snd_tea575x_ops snd_fm801_tea_ops = {
@@ -962,7 +977,7 @@ static int snd_fm801_get_mux(struct snd_kcontrol *kcontrol,
        struct fm801 *chip = snd_kcontrol_chip(kcontrol);
         unsigned short val;
  
-       val = inw(FM801_REG(chip, REC_SRC)) & 7;
+       val = fm801_readw(chip, REC_SRC) & 7;
        if (val > 4)
                val = 4;
         ucontrol->value.enumerated.item[0] = val;
@@ -1073,12 +1088,12 @@ static int wait_for_codec(struct fm801 *chip, unsigned int codec_id,
 {
        unsigned long timeout = jiffies + waits;
 
-       outw(FM801_AC97_READ | (codec_id << FM801_AC97_ADDR_SHIFT) | reg,
-            FM801_REG(chip, AC97_CMD));
+       fm801_writew(chip, AC97_CMD,
+                    reg | (codec_id << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
        udelay(5);
        do {
-               if ((inw(FM801_REG(chip, AC97_CMD)) & (FM801_AC97_VALID|FM801_AC97_BUSY))
-                   == FM801_AC97_VALID)
+               if ((fm801_readw(chip, AC97_CMD) &
+                    (FM801_AC97_VALID | FM801_AC97_BUSY)) == FM801_AC97_VALID)
                        return 0;
                schedule_timeout_uninterruptible(1);
        } while (time_after(timeout, jiffies));
@@ -1093,10 +1108,10 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
                goto __ac97_ok;
 
        /* codec cold reset + AC'97 warm reset */
-       outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL));
-       inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */
+       fm801_writew(chip, CODEC_CTRL, (1 << 5) | (1 << 6));
+       fm801_readw(chip, CODEC_CTRL); /* flush posting data */
        udelay(100);
-       outw(0, FM801_REG(chip, CODEC_CTRL));
+       fm801_writew(chip, CODEC_CTRL, 0);
 
        if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0)
                if (!resume) {
@@ -1117,7 +1132,7 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
                        for (i = 3; i > 0; i--) {
                                if (!wait_for_codec(chip, i, AC97_VENDOR_ID1,
                                                     msecs_to_jiffies(50))) {
-                                       cmdw = inw(FM801_REG(chip, AC97_DATA));
+                                       cmdw = fm801_readw(chip, AC97_DATA);
                                        if (cmdw != 0xffff && cmdw != 0) {
                                                chip->secondary = 1;
                                                chip->secondary_addr = i;
@@ -1135,23 +1150,24 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
       __ac97_ok:
 
        /* init volume */
-       outw(0x0808, FM801_REG(chip, PCM_VOL));
-       outw(0x9f1f, FM801_REG(chip, FM_VOL));
-       outw(0x8808, FM801_REG(chip, I2S_VOL));
+       fm801_writew(chip, PCM_VOL, 0x0808);
+       fm801_writew(chip, FM_VOL, 0x9f1f);
+       fm801_writew(chip, I2S_VOL, 0x8808);
 
        /* I2S control - I2S mode */
-       outw(0x0003, FM801_REG(chip, I2S_MODE));
+       fm801_writew(chip, I2S_MODE, 0x0003);
 
        /* interrupt setup */
-       cmdw = inw(FM801_REG(chip, IRQ_MASK));
+       cmdw = fm801_readw(chip, IRQ_MASK);
        if (chip->irq < 0)
                cmdw |= 0x00c3;         /* mask everything, no PCM nor MPU */
        else
                cmdw &= ~0x0083;        /* unmask MPU, PLAYBACK & CAPTURE */
-       outw(cmdw, FM801_REG(chip, IRQ_MASK));
+       fm801_writew(chip, IRQ_MASK, cmdw);
 
        /* interrupt clear */
-       outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS));
+       fm801_writew(chip, IRQ_STATUS,
+                    FM801_IRQ_PLAYBACK | FM801_IRQ_CAPTURE | FM801_IRQ_MPU);
 
        return 0;
 }
@@ -1165,9 +1181,9 @@ static int snd_fm801_free(struct fm801 *chip)
                goto __end_hw;
 
        /* interrupt setup - mask everything */
-       cmdw = inw(FM801_REG(chip, IRQ_MASK));
+       cmdw = fm801_readw(chip, IRQ_MASK);
        cmdw |= 0x00c3;
-       outw(cmdw, FM801_REG(chip, IRQ_MASK));
+       fm801_writew(chip, IRQ_MASK, cmdw);
 
       __end_hw:
 #ifdef CONFIG_SND_FM801_TEA575X_BOOL
@@ -1339,15 +1355,15 @@ static int snd_card_fm801_probe(struct pci_dev *pci,
                return err;
        }
        if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
-                                      FM801_REG(chip, MPU401_DATA),
+                                      chip->port + FM801_MPU401_DATA,
                                       MPU401_INFO_INTEGRATED |
                                       MPU401_INFO_IRQ_HOOK,
                                       -1, &chip->rmidi)) < 0) {
                snd_card_free(card);
                return err;
        }
-       if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0),
-                                  FM801_REG(chip, OPL3_BANK1),
+       if ((err = snd_opl3_create(card, chip->port + FM801_OPL3_BANK0,
+                                  chip->port + FM801_OPL3_BANK1,
                                   OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) {
                snd_card_free(card);
                return err;
index ac17c3f..ebf4c2f 100644 (file)
@@ -20,6 +20,21 @@ config SND_HDA_INTEL
          To compile this driver as a module, choose M here: the module
          will be called snd-hda-intel.
 
+config SND_HDA_TEGRA
+       tristate "NVIDIA Tegra HD Audio"
+       depends on ARCH_TEGRA
+       select SND_HDA
+       help
+         Say Y here to support the HDA controller present in NVIDIA
+         Tegra SoCs
+
+         This options enables support for the HD Audio controller
+         present in some NVIDIA Tegra SoCs, used to communicate audio
+         to the HDMI output.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-hda-tegra.
+
 if SND_HDA
 
 config SND_HDA_DSP_LOADER
index d0d0c19..194f309 100644 (file)
@@ -1,5 +1,6 @@
 snd-hda-intel-objs := hda_intel.o
 snd-hda-controller-objs := hda_controller.o
+snd-hda-tegra-objs := hda_tegra.o
 # for haswell power well
 snd-hda-intel-$(CONFIG_SND_HDA_I915) +=        hda_i915.o
 
@@ -47,3 +48,4 @@ obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o
 # otherwise the codec patches won't be hooked before the PCI probe
 # when built in kernel
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
+obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o
index 90d2fda..b684c6e 100644 (file)
@@ -839,6 +839,43 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action)
 }
 EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
 
+static bool pin_config_match(struct hda_codec *codec,
+                            const struct hda_pintbl *pins)
+{
+       for (; pins->nid; pins++) {
+               u32 def_conf = snd_hda_codec_get_pincfg(codec, pins->nid);
+               if (pins->val != def_conf)
+                       return false;
+       }
+       return true;
+}
+
+void snd_hda_pick_pin_fixup(struct hda_codec *codec,
+                           const struct snd_hda_pin_quirk *pin_quirk,
+                           const struct hda_fixup *fixlist)
+{
+       const struct snd_hda_pin_quirk *pq;
+
+       if (codec->fixup_forced)
+               return;
+
+       for (pq = pin_quirk; pq->subvendor; pq++) {
+               if ((codec->subsystem_id & 0xffff0000) != (pq->subvendor << 16))
+                       continue;
+               if (codec->vendor_id != pq->codec)
+                       continue;
+               if (pin_config_match(codec, pq->pins)) {
+                       codec->fixup_id = pq->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+                       codec->fixup_name = pq->name;
+#endif
+                       codec->fixup_list = fixlist;
+                       return;
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup);
+
 void snd_hda_pick_fixup(struct hda_codec *codec,
                        const struct hda_model_fixup *models,
                        const struct snd_pci_quirk *quirk,
@@ -852,15 +889,17 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
        if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
                codec->fixup_list = NULL;
                codec->fixup_id = -1;
+               codec->fixup_forced = 1;
                return;
        }
 
        if (codec->modelname && models) {
                while (models->name) {
                        if (!strcmp(codec->modelname, models->name)) {
-                               id = models->id;
-                               name = models->name;
-                               break;
+                               codec->fixup_id = models->id;
+                               codec->fixup_name = models->name;
+                               codec->fixup_forced = 1;
+                               return;
                        }
                        models++;
                }
@@ -889,6 +928,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
                }
        }
 
+       codec->fixup_forced = 0;
        codec->fixup_id = id;
        if (id >= 0) {
                codec->fixup_list = fixlist;
index a423313..5825aa1 100644 (file)
@@ -402,6 +402,7 @@ struct hda_codec {
 
        /* fix-up list */
        int fixup_id;
+       unsigned int fixup_forced:1; /* fixup explicitly set by user */
        const struct hda_fixup *fixup_list;
        const char *fixup_name;
 
index 1613388..589e47c 100644 (file)
@@ -3722,7 +3722,7 @@ static void parse_digital(struct hda_codec *codec)
                } else {
                        spec->multiout.slave_dig_outs = spec->slave_dig_outs;
                        if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
-                       break;
+                               break;
                        spec->slave_dig_outs[nums - 1] = dig_nid;
                }
                nums++;
index 6cc3cf2..cd77b9b 100644 (file)
@@ -1730,7 +1730,7 @@ static void azx_remove(struct pci_dev *pci)
 }
 
 /* PCI IDs */
-static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
+static const struct pci_device_id azx_ids[] = {
        /* CPT */
        { PCI_DEVICE(0x8086, 0x1c20),
          .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
index e51d155..ebd1fa6 100644 (file)
@@ -407,6 +407,16 @@ struct hda_fixup {
        } v;
 };
 
+struct snd_hda_pin_quirk {
+       unsigned int codec;             /* Codec vendor/device ID */
+       unsigned short subvendor;       /* PCI subvendor ID */
+       const struct hda_pintbl *pins;  /* list of matching pins */
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       const char *name;
+#endif
+       int value;                      /* quirk value */
+};
+
 /* fixup types */
 enum {
        HDA_FIXUP_INVALID,
@@ -434,6 +444,10 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
                        const struct hda_model_fixup *models,
                        const struct snd_pci_quirk *quirk,
                        const struct hda_fixup *fixlist);
+void snd_hda_pick_pin_fixup(struct hda_codec *codec,
+                           const struct snd_hda_pin_quirk *pin_quirk,
+                           const struct hda_fixup *fixlist);
+
 
 /*
  * unsolicited event handler
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
new file mode 100644 (file)
index 0000000..a366ba9
--- /dev/null
@@ -0,0 +1,588 @@
+/*
+ *
+ * Implementation of primary ALSA driver code base for NVIDIA Tegra HDA.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clocksource.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include "hda_codec.h"
+#include "hda_controller.h"
+#include "hda_priv.h"
+
+/* Defines for Nvidia Tegra HDA support */
+#define HDA_BAR0           0x8000
+
+#define HDA_CFG_CMD        0x1004
+#define HDA_CFG_BAR0       0x1010
+
+#define HDA_ENABLE_IO_SPACE       (1 << 0)
+#define HDA_ENABLE_MEM_SPACE      (1 << 1)
+#define HDA_ENABLE_BUS_MASTER     (1 << 2)
+#define HDA_ENABLE_SERR           (1 << 8)
+#define HDA_DISABLE_INTR          (1 << 10)
+#define HDA_BAR0_INIT_PROGRAM     0xFFFFFFFF
+#define HDA_BAR0_FINAL_PROGRAM    (1 << 14)
+
+/* IPFS */
+#define HDA_IPFS_CONFIG           0x180
+#define HDA_IPFS_EN_FPCI          0x1
+
+#define HDA_IPFS_FPCI_BAR0        0x80
+#define HDA_FPCI_BAR0_START       0x40
+
+#define HDA_IPFS_INTR_MASK        0x188
+#define HDA_IPFS_EN_INTR          (1 << 16)
+
+/* max number of SDs */
+#define NUM_CAPTURE_SD 1
+#define NUM_PLAYBACK_SD 1
+
+struct hda_tegra {
+       struct azx chip;
+       struct device *dev;
+       struct clk *hda_clk;
+       struct clk *hda2codec_2x_clk;
+       struct clk *hda2hdmi_clk;
+       void __iomem *regs;
+};
+
+#ifdef CONFIG_PM
+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
+module_param(power_save, bint, 0644);
+MODULE_PARM_DESC(power_save,
+                "Automatic power-saving timeout (in seconds, 0 = disable).");
+#else
+static int power_save = 0;
+#endif
+
+/*
+ * DMA page allocation ops.
+ */
+static int dma_alloc_pages(struct azx *chip, int type, size_t size,
+                          struct snd_dma_buffer *buf)
+{
+       return snd_dma_alloc_pages(type, chip->card->dev, size, buf);
+}
+
+static void dma_free_pages(struct azx *chip, struct snd_dma_buffer *buf)
+{
+       snd_dma_free_pages(buf);
+}
+
+static int substream_alloc_pages(struct azx *chip,
+                                struct snd_pcm_substream *substream,
+                                size_t size)
+{
+       struct azx_dev *azx_dev = get_azx_dev(substream);
+
+       azx_dev->bufsize = 0;
+       azx_dev->period_bytes = 0;
+       azx_dev->format_val = 0;
+       return snd_pcm_lib_malloc_pages(substream, size);
+}
+
+static int substream_free_pages(struct azx *chip,
+                               struct snd_pcm_substream *substream)
+{
+       return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ * Register access ops. Tegra HDA register access is DWORD only.
+ */
+static void hda_tegra_writel(u32 value, u32 *addr)
+{
+       writel(value, addr);
+}
+
+static u32 hda_tegra_readl(u32 *addr)
+{
+       return readl(addr);
+}
+
+static void hda_tegra_writew(u16 value, u16 *addr)
+{
+       unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+       void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+       u32 v;
+
+       v = readl(dword_addr);
+       v &= ~(0xffff << shift);
+       v |= value << shift;
+       writel(v, dword_addr);
+}
+
+static u16 hda_tegra_readw(u16 *addr)
+{
+       unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+       void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+       u32 v;
+
+       v = readl(dword_addr);
+       return (v >> shift) & 0xffff;
+}
+
+static void hda_tegra_writeb(u8 value, u8 *addr)
+{
+       unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+       void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+       u32 v;
+
+       v = readl(dword_addr);
+       v &= ~(0xff << shift);
+       v |= value << shift;
+       writel(v, dword_addr);
+}
+
+static u8 hda_tegra_readb(u8 *addr)
+{
+       unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+       void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+       u32 v;
+
+       v = readl(dword_addr);
+       return (v >> shift) & 0xff;
+}
+
+static const struct hda_controller_ops hda_tegra_ops = {
+       .reg_writel = hda_tegra_writel,
+       .reg_readl = hda_tegra_readl,
+       .reg_writew = hda_tegra_writew,
+       .reg_readw = hda_tegra_readw,
+       .reg_writeb = hda_tegra_writeb,
+       .reg_readb = hda_tegra_readb,
+       .dma_alloc_pages = dma_alloc_pages,
+       .dma_free_pages = dma_free_pages,
+       .substream_alloc_pages = substream_alloc_pages,
+       .substream_free_pages = substream_free_pages,
+};
+
+static void hda_tegra_init(struct hda_tegra *hda)
+{
+       u32 v;
+
+       /* Enable PCI access */
+       v = readl(hda->regs + HDA_IPFS_CONFIG);
+       v |= HDA_IPFS_EN_FPCI;
+       writel(v, hda->regs + HDA_IPFS_CONFIG);
+
+       /* Enable MEM/IO space and bus master */
+       v = readl(hda->regs + HDA_CFG_CMD);
+       v &= ~HDA_DISABLE_INTR;
+       v |= HDA_ENABLE_MEM_SPACE | HDA_ENABLE_IO_SPACE |
+               HDA_ENABLE_BUS_MASTER | HDA_ENABLE_SERR;
+       writel(v, hda->regs + HDA_CFG_CMD);
+
+       writel(HDA_BAR0_INIT_PROGRAM, hda->regs + HDA_CFG_BAR0);
+       writel(HDA_BAR0_FINAL_PROGRAM, hda->regs + HDA_CFG_BAR0);
+       writel(HDA_FPCI_BAR0_START, hda->regs + HDA_IPFS_FPCI_BAR0);
+
+       v = readl(hda->regs + HDA_IPFS_INTR_MASK);
+       v |= HDA_IPFS_EN_INTR;
+       writel(v, hda->regs + HDA_IPFS_INTR_MASK);
+}
+
+static int hda_tegra_enable_clocks(struct hda_tegra *data)
+{
+       int rc;
+
+       rc = clk_prepare_enable(data->hda_clk);
+       if (rc)
+               return rc;
+       rc = clk_prepare_enable(data->hda2codec_2x_clk);
+       if (rc)
+               goto disable_hda;
+       rc = clk_prepare_enable(data->hda2hdmi_clk);
+       if (rc)
+               goto disable_codec_2x;
+
+       return 0;
+
+disable_codec_2x:
+       clk_disable_unprepare(data->hda2codec_2x_clk);
+disable_hda:
+       clk_disable_unprepare(data->hda_clk);
+       return rc;
+}
+
+static void hda_tegra_disable_clocks(struct hda_tegra *data)
+{
+       clk_disable_unprepare(data->hda2hdmi_clk);
+       clk_disable_unprepare(data->hda2codec_2x_clk);
+       clk_disable_unprepare(data->hda_clk);
+}
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * power management
+ */
+static int hda_tegra_suspend(struct device *dev)
+{
+       struct snd_card *card = dev_get_drvdata(dev);
+       struct azx *chip = card->private_data;
+       struct azx_pcm *p;
+       struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+       list_for_each_entry(p, &chip->pcm_list, list)
+               snd_pcm_suspend_all(p->pcm);
+       if (chip->initialized)
+               snd_hda_suspend(chip->bus);
+
+       azx_stop_chip(chip);
+       azx_enter_link_reset(chip);
+       hda_tegra_disable_clocks(hda);
+
+       return 0;
+}
+
+static int hda_tegra_resume(struct device *dev)
+{
+       struct snd_card *card = dev_get_drvdata(dev);
+       struct azx *chip = card->private_data;
+       struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+       int status;
+
+       hda_tegra_enable_clocks(hda);
+
+       /* Read STATESTS before controller reset */
+       status = azx_readw(chip, STATESTS);
+
+       hda_tegra_init(hda);
+
+       azx_init_chip(chip, 1);
+
+       snd_hda_resume(chip->bus);
+       snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops hda_tegra_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume)
+};
+
+/*
+ * reboot notifier for hang-up problem at power-down
+ */
+static int hda_tegra_halt(struct notifier_block *nb, unsigned long event,
+                         void *buf)
+{
+       struct azx *chip = container_of(nb, struct azx, reboot_notifier);
+       snd_hda_bus_reboot_notify(chip->bus);
+       azx_stop_chip(chip);
+       return NOTIFY_OK;
+}
+
+static void hda_tegra_notifier_register(struct azx *chip)
+{
+       chip->reboot_notifier.notifier_call = hda_tegra_halt;
+       register_reboot_notifier(&chip->reboot_notifier);
+}
+
+static void hda_tegra_notifier_unregister(struct azx *chip)
+{
+       if (chip->reboot_notifier.notifier_call)
+               unregister_reboot_notifier(&chip->reboot_notifier);
+}
+
+/*
+ * destructor
+ */
+static int hda_tegra_dev_free(struct snd_device *device)
+{
+       int i;
+       struct azx *chip = device->device_data;
+
+       hda_tegra_notifier_unregister(chip);
+
+       if (chip->initialized) {
+               for (i = 0; i < chip->num_streams; i++)
+                       azx_stream_stop(chip, &chip->azx_dev[i]);
+               azx_stop_chip(chip);
+       }
+
+       azx_free_stream_pages(chip);
+
+       return 0;
+}
+
+static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)
+{
+       struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+       struct device *dev = hda->dev;
+       struct resource *res;
+       int err;
+
+       hda->hda_clk = devm_clk_get(dev, "hda");
+       if (IS_ERR(hda->hda_clk))
+               return PTR_ERR(hda->hda_clk);
+       hda->hda2codec_2x_clk = devm_clk_get(dev, "hda2codec_2x");
+       if (IS_ERR(hda->hda2codec_2x_clk))
+               return PTR_ERR(hda->hda2codec_2x_clk);
+       hda->hda2hdmi_clk = devm_clk_get(dev, "hda2hdmi");
+       if (IS_ERR(hda->hda2hdmi_clk))
+               return PTR_ERR(hda->hda2hdmi_clk);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       hda->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(chip->remap_addr))
+               return PTR_ERR(chip->remap_addr);
+
+       chip->remap_addr = hda->regs + HDA_BAR0;
+       chip->addr = res->start + HDA_BAR0;
+
+       err = hda_tegra_enable_clocks(hda);
+       if (err)
+               return err;
+
+       hda_tegra_init(hda);
+
+       return 0;
+}
+
+/*
+ * The codecs were powered up in snd_hda_codec_new().
+ * Now all initialization done, so turn them down if possible
+ */
+static void power_down_all_codecs(struct azx *chip)
+{
+       struct hda_codec *codec;
+       list_for_each_entry(codec, &chip->bus->codec_list, list)
+               snd_hda_power_down(codec);
+}
+
+static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
+{
+       struct snd_card *card = chip->card;
+       int err;
+       unsigned short gcap;
+       int irq_id = platform_get_irq(pdev, 0);
+
+       err = hda_tegra_init_chip(chip, pdev);
+       if (err)
+               return err;
+
+       err = devm_request_irq(chip->card->dev, irq_id, azx_interrupt,
+                            IRQF_SHARED, KBUILD_MODNAME, chip);
+       if (err) {
+               dev_err(chip->card->dev,
+                       "unable to request IRQ %d, disabling device\n",
+                       irq_id);
+               return err;
+       }
+       chip->irq = irq_id;
+
+       synchronize_irq(chip->irq);
+
+       gcap = azx_readw(chip, GCAP);
+       dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
+
+       /* read number of streams from GCAP register instead of using
+        * hardcoded value
+        */
+       chip->capture_streams = (gcap >> 8) & 0x0f;
+       chip->playback_streams = (gcap >> 12) & 0x0f;
+       if (!chip->playback_streams && !chip->capture_streams) {
+               /* gcap didn't give any info, switching to old method */
+               chip->playback_streams = NUM_PLAYBACK_SD;
+               chip->capture_streams = NUM_CAPTURE_SD;
+       }
+       chip->capture_index_offset = 0;
+       chip->playback_index_offset = chip->capture_streams;
+       chip->num_streams = chip->playback_streams + chip->capture_streams;
+       chip->azx_dev = devm_kcalloc(card->dev, chip->num_streams,
+                                    sizeof(*chip->azx_dev), GFP_KERNEL);
+       if (!chip->azx_dev)
+               return -ENOMEM;
+
+       err = azx_alloc_stream_pages(chip);
+       if (err < 0)
+               return err;
+
+       /* initialize streams */
+       azx_init_stream(chip);
+
+       /* initialize chip */
+       azx_init_chip(chip, 1);
+
+       /* codec detection */
+       if (!chip->codec_mask) {
+               dev_err(card->dev, "no codecs found!\n");
+               return -ENODEV;
+       }
+
+       strcpy(card->driver, "tegra-hda");
+       strcpy(card->shortname, "tegra-hda");
+       snprintf(card->longname, sizeof(card->longname),
+                "%s at 0x%lx irq %i",
+                card->shortname, chip->addr, chip->irq);
+
+       return 0;
+}
+
+/*
+ * constructor
+ */
+static int hda_tegra_create(struct snd_card *card,
+                           unsigned int driver_caps,
+                           const struct hda_controller_ops *hda_ops,
+                           struct hda_tegra *hda)
+{
+       static struct snd_device_ops ops = {
+               .dev_free = hda_tegra_dev_free,
+       };
+       struct azx *chip;
+       int err;
+
+       chip = &hda->chip;
+
+       spin_lock_init(&chip->reg_lock);
+       mutex_init(&chip->open_mutex);
+       chip->card = card;
+       chip->ops = hda_ops;
+       chip->irq = -1;
+       chip->driver_caps = driver_caps;
+       chip->driver_type = driver_caps & 0xff;
+       chip->dev_index = 0;
+       INIT_LIST_HEAD(&chip->pcm_list);
+       INIT_LIST_HEAD(&chip->list);
+
+       chip->position_fix[0] = POS_FIX_AUTO;
+       chip->position_fix[1] = POS_FIX_AUTO;
+       chip->codec_probe_mask = -1;
+
+       chip->single_cmd = false;
+       chip->snoop = true;
+
+       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+       if (err < 0) {
+               dev_err(card->dev, "Error creating device\n");
+               return err;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id hda_tegra_match[] = {
+       { .compatible = "nvidia,tegra30-hda" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, hda_tegra_match);
+
+static int hda_tegra_probe(struct platform_device *pdev)
+{
+       struct snd_card *card;
+       struct azx *chip;
+       struct hda_tegra *hda;
+       int err;
+       const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY;
+
+       hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
+       if (!hda)
+               return -ENOMEM;
+       hda->dev = &pdev->dev;
+       chip = &hda->chip;
+
+       err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+                          THIS_MODULE, 0, &card);
+       if (err < 0) {
+               dev_err(&pdev->dev, "Error creating card!\n");
+               return err;
+       }
+
+       err = hda_tegra_create(card, driver_flags, &hda_tegra_ops, hda);
+       if (err < 0)
+               goto out_free;
+       card->private_data = chip;
+
+       dev_set_drvdata(&pdev->dev, card);
+
+       err = hda_tegra_first_init(chip, pdev);
+       if (err < 0)
+               goto out_free;
+
+       /* create codec instances */
+       err = azx_codec_create(chip, NULL, 0, &power_save);
+       if (err < 0)
+               goto out_free;
+
+       err = azx_codec_configure(chip);
+       if (err < 0)
+               goto out_free;
+
+       /* create PCM streams */
+       err = snd_hda_build_pcms(chip->bus);
+       if (err < 0)
+               goto out_free;
+
+       /* create mixer controls */
+       err = azx_mixer_create(chip);
+       if (err < 0)
+               goto out_free;
+
+       err = snd_card_register(chip->card);
+       if (err < 0)
+               goto out_free;
+
+       chip->running = 1;
+       power_down_all_codecs(chip);
+       hda_tegra_notifier_register(chip);
+
+       return 0;
+
+out_free:
+       snd_card_free(card);
+       return err;
+}
+
+static int hda_tegra_remove(struct platform_device *pdev)
+{
+       return snd_card_free(dev_get_drvdata(&pdev->dev));
+}
+
+static struct platform_driver tegra_platform_hda = {
+       .driver = {
+               .name = "tegra-hda",
+               .pm = &hda_tegra_pm,
+               .of_match_table = hda_tegra_match,
+       },
+       .probe = hda_tegra_probe,
+       .remove = hda_tegra_remove,
+};
+module_platform_driver(tegra_platform_hda);
+
+MODULE_DESCRIPTION("Tegra HDA bus driver");
+MODULE_LICENSE("GPL v2");
index 40ba06e..06275f8 100644 (file)
@@ -332,6 +332,7 @@ static const struct hda_fixup ad1986a_fixups[] = {
 
 static const struct snd_pci_quirk ad1986a_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC),
+       SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8JN", AD1986A_FIXUP_EAPD),
        SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK),
        SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK),
        SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK),
index b4218a1..be0a9ee 100644 (file)
@@ -1127,10 +1127,6 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
                                            AMP_OUT_UNMUTE);
 
        eld = &per_pin->sink_eld;
-       if (!eld->monitor_present) {
-               hdmi_set_channel_count(codec, per_pin->cvt_nid, channels);
-               return;
-       }
 
        if (!non_pcm && per_pin->chmap_set)
                ca = hdmi_manual_channel_allocation(channels, per_pin->chmap);
@@ -3324,6 +3320,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
 { .id = 0x10de001a, .name = "GPU 1a HDMI/DP",  .patch = patch_nvhdmi },
 { .id = 0x10de001b, .name = "GPU 1b HDMI/DP",  .patch = patch_nvhdmi },
 { .id = 0x10de001c, .name = "GPU 1c HDMI/DP",  .patch = patch_nvhdmi },
+{ .id = 0x10de0028, .name = "Tegra12x HDMI",   .patch = patch_nvhdmi },
 { .id = 0x10de0040, .name = "GPU 40 HDMI/DP",  .patch = patch_nvhdmi },
 { .id = 0x10de0041, .name = "GPU 41 HDMI/DP",  .patch = patch_nvhdmi },
 { .id = 0x10de0042, .name = "GPU 42 HDMI/DP",  .patch = patch_nvhdmi },
@@ -3380,6 +3377,7 @@ MODULE_ALIAS("snd-hda-codec-id:10de0019");
 MODULE_ALIAS("snd-hda-codec-id:10de001a");
 MODULE_ALIAS("snd-hda-codec-id:10de001b");
 MODULE_ALIAS("snd-hda-codec-id:10de001c");
+MODULE_ALIAS("snd-hda-codec-id:10de0028");
 MODULE_ALIAS("snd-hda-codec-id:10de0040");
 MODULE_ALIAS("snd-hda-codec-id:10de0041");
 MODULE_ALIAS("snd-hda-codec-id:10de0042");
index 49e884f..12fb411 100644 (file)
@@ -951,7 +951,9 @@ static struct alc_codec_rename_pci_table rename_pci_tbl[] = {
        { 0x10ec0280, 0x1028, 0, "ALC3220" },
        { 0x10ec0282, 0x1028, 0, "ALC3221" },
        { 0x10ec0283, 0x1028, 0, "ALC3223" },
+       { 0x10ec0288, 0x1028, 0, "ALC3263" },
        { 0x10ec0292, 0x1028, 0, "ALC3226" },
+       { 0x10ec0293, 0x1028, 0, "ALC3235" },
        { 0x10ec0255, 0x1028, 0, "ALC3234" },
        { 0x10ec0668, 0x1028, 0, "ALC3661" },
        { } /* terminator */
@@ -1647,12 +1649,10 @@ static const struct hda_fixup alc260_fixups[] = {
        [ALC260_FIXUP_COEF] = {
                .type = HDA_FIXUP_VERBS,
                .v.verbs = (const struct hda_verb[]) {
-                       { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
-                       { 0x20, AC_VERB_SET_PROC_COEF,  0x3040 },
+                       { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 },
+                       { 0x1a, AC_VERB_SET_PROC_COEF,  0x3040 },
                        { }
                },
-               .chained = true,
-               .chain_id = ALC260_FIXUP_HP_PIN_0F,
        },
        [ALC260_FIXUP_GPIO1] = {
                .type = HDA_FIXUP_VERBS,
@@ -1667,8 +1667,8 @@ static const struct hda_fixup alc260_fixups[] = {
        [ALC260_FIXUP_REPLACER] = {
                .type = HDA_FIXUP_VERBS,
                .v.verbs = (const struct hda_verb[]) {
-                       { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
-                       { 0x20, AC_VERB_SET_PROC_COEF,  0x3050 },
+                       { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 },
+                       { 0x1a, AC_VERB_SET_PROC_COEF,  0x3050 },
                        { }
                },
                .chained = true,
@@ -3522,6 +3522,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
                /* Direct Drive HP Amp control */
                alc_write_coefex_idx(codec, 0x57, 0x03, 0x8aa6);
                break;
+       case 0x10ec0233:
        case 0x10ec0283:
                alc_write_coef_idx(codec, 0x1b, 0x0c0b);
                alc_write_coef_idx(codec, 0x45, 0xc429);
@@ -3538,6 +3539,25 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
                alc_write_coef_idx(codec, 0x18, 0x7308);
                alc_write_coef_idx(codec, 0x6b, 0xc429);
                break;
+       case 0x10ec0293:
+               /* SET Line1 JD to 0 */
+               val = alc_read_coef_idx(codec, 0x10);
+               alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 6<<8);
+               /* SET charge pump by verb */
+               val = alc_read_coefex_idx(codec, 0x57, 0x05);
+               alc_write_coefex_idx(codec, 0x57, 0x05, (val & ~(1<<15|1<<13)) | 0x0);
+               /* SET EN_OSW to 1 */
+               val = alc_read_coefex_idx(codec, 0x57, 0x03);
+               alc_write_coefex_idx(codec, 0x57, 0x03, (val & ~(1<<10)) | (1<<10) );
+               /* Combo JD gating with LINE1-VREFO */
+               val = alc_read_coef_idx(codec, 0x1a);
+               alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | (1<<3));
+               /* Set to TRS type */
+               alc_write_coef_idx(codec, 0x45, 0xc429);
+               /* Combo Jack auto detect */
+               val = alc_read_coef_idx(codec, 0x4a);
+               alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x000e);
+               break;
        case 0x10ec0668:
                alc_write_coef_idx(codec, 0x15, 0x0d40);
                alc_write_coef_idx(codec, 0xb7, 0x802b);
@@ -3561,6 +3581,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
                alc_write_coef_idx(codec, 0x06, 0x6100);
                snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
                break;
+       case 0x10ec0233:
        case 0x10ec0283:
                alc_write_coef_idx(codec, 0x45, 0xc429);
                snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
@@ -3576,6 +3597,21 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
                alc_write_coef_idx(codec, 0x19, 0xa208);
                alc_write_coef_idx(codec, 0x2e, 0xacf0);
                break;
+       case 0x10ec0293:
+               /* Set to TRS mode */
+               alc_write_coef_idx(codec, 0x45, 0xc429);
+               snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+               /* SET charge pump by verb */
+               val = alc_read_coefex_idx(codec, 0x57, 0x05);
+               alc_write_coefex_idx(codec, 0x57, 0x05, (val & ~(1<<15|1<<13)) | (1<<15|1<<13));
+               /* SET EN_OSW to 0 */
+               val = alc_read_coefex_idx(codec, 0x57, 0x03);
+               alc_write_coefex_idx(codec, 0x57, 0x03, (val & ~(1<<10)) | 0x0);
+               /* Combo JD gating without LINE1-VREFO */
+               val = alc_read_coef_idx(codec, 0x1a);
+               alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | 0x0);
+               snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+               break;
        case 0x10ec0668:
                alc_write_coef_idx(codec, 0x11, 0x0001);
                snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
@@ -3591,6 +3627,8 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
 
 static void alc_headset_mode_default(struct hda_codec *codec)
 {
+       int val;
+
        switch (codec->vendor_id) {
        case 0x10ec0255:
                alc_write_coef_idx(codec, 0x45, 0xc089);
@@ -3598,6 +3636,7 @@ static void alc_headset_mode_default(struct hda_codec *codec)
                alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
                alc_write_coef_idx(codec, 0x49, 0x0049);
                break;
+       case 0x10ec0233:
        case 0x10ec0283:
                alc_write_coef_idx(codec, 0x06, 0x2100);
                alc_write_coef_idx(codec, 0x32, 0x4ea3);
@@ -3608,6 +3647,16 @@ static void alc_headset_mode_default(struct hda_codec *codec)
                alc_write_coef_idx(codec, 0x6b, 0xc429);
                alc_write_coef_idx(codec, 0x18, 0x7308);
                break;
+       case 0x10ec0293:
+               /* Combo Jack auto detect */
+               val = alc_read_coef_idx(codec, 0x4a);
+               alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x000e);
+               /* Set to TRS type */
+               alc_write_coef_idx(codec, 0x45, 0xC429);
+               /* Combo JD gating without LINE1-VREFO */
+               val = alc_read_coef_idx(codec, 0x1a);
+               alc_write_coef_idx(codec, 0x1a, (val & ~(1<<3)) | 0x0);
+               break;
        case 0x10ec0668:
                alc_write_coef_idx(codec, 0x11, 0x0041);
                alc_write_coef_idx(codec, 0x15, 0x0d40);
@@ -3620,6 +3669,8 @@ static void alc_headset_mode_default(struct hda_codec *codec)
 /* Iphone type */
 static void alc_headset_mode_ctia(struct hda_codec *codec)
 {
+       int val;
+
        switch (codec->vendor_id) {
        case 0x10ec0255:
                /* Set to CTIA type */
@@ -3627,6 +3678,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
                alc_write_coef_idx(codec, 0x1b, 0x0c2b);
                alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
                break;
+       case 0x10ec0233:
        case 0x10ec0283:
                alc_write_coef_idx(codec, 0x45, 0xd429);
                alc_write_coef_idx(codec, 0x1b, 0x0c2b);
@@ -3637,6 +3689,13 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
                alc_write_coef_idx(codec, 0x76, 0x0008);
                alc_write_coef_idx(codec, 0x18, 0x7388);
                break;
+       case 0x10ec0293:
+               /* Set to ctia type */
+               alc_write_coef_idx(codec, 0x45, 0xd429);
+               /* SET Line1 JD to 1 */
+               val = alc_read_coef_idx(codec, 0x10);
+               alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 7<<8);
+               break;
        case 0x10ec0668:
                alc_write_coef_idx(codec, 0x11, 0x0001);
                alc_write_coef_idx(codec, 0x15, 0x0d60);
@@ -3649,6 +3708,8 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
 /* Nokia type */
 static void alc_headset_mode_omtp(struct hda_codec *codec)
 {
+       int val;
+
        switch (codec->vendor_id) {
        case 0x10ec0255:
                /* Set to OMTP Type */
@@ -3656,6 +3717,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
                alc_write_coef_idx(codec, 0x1b, 0x0c2b);
                alc_write_coefex_idx(codec, 0x57, 0x03, 0x8ea6);
                break;
+       case 0x10ec0233:
        case 0x10ec0283:
                alc_write_coef_idx(codec, 0x45, 0xe429);
                alc_write_coef_idx(codec, 0x1b, 0x0c2b);
@@ -3666,6 +3728,13 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
                alc_write_coef_idx(codec, 0x76, 0x0008);
                alc_write_coef_idx(codec, 0x18, 0x7388);
                break;
+       case 0x10ec0293:
+               /* Set to omtp type */
+               alc_write_coef_idx(codec, 0x45, 0xe429);
+               /* SET Line1 JD to 1 */
+               val = alc_read_coef_idx(codec, 0x10);
+               alc_write_coef_idx(codec, 0x10, (val & ~(7<<8)) | 7<<8);
+               break;
        case 0x10ec0668:
                alc_write_coef_idx(codec, 0x11, 0x0001);
                alc_write_coef_idx(codec, 0x15, 0x0d50);
@@ -3691,6 +3760,7 @@ static void alc_determine_headset_type(struct hda_codec *codec)
                val = alc_read_coef_idx(codec, 0x46);
                is_ctia = (val & 0x0070) == 0x0070;
                break;
+       case 0x10ec0233:
        case 0x10ec0283:
                alc_write_coef_idx(codec, 0x45, 0xd029);
                msleep(300);
@@ -3703,6 +3773,16 @@ static void alc_determine_headset_type(struct hda_codec *codec)
                val = alc_read_coef_idx(codec, 0x6c);
                is_ctia = (val & 0x001c) == 0x001c;
                break;
+       case 0x10ec0293:
+               /* Combo Jack auto detect */
+               val = alc_read_coef_idx(codec, 0x4a);
+               alc_write_coef_idx(codec, 0x4a, (val & 0xfff0) | 0x0008);
+               /* Set to ctia type */
+               alc_write_coef_idx(codec, 0x45, 0xD429);
+               msleep(300);
+               val = alc_read_coef_idx(codec, 0x46);
+               is_ctia = (val & 0x0070) == 0x0070;
+               break;
        case 0x10ec0668:
                alc_write_coef_idx(codec, 0x11, 0x0001);
                alc_write_coef_idx(codec, 0xb7, 0x802b);
@@ -3894,6 +3974,39 @@ static void alc_fixup_no_shutup(struct hda_codec *codec,
        }
 }
 
+static void alc_fixup_disable_aamix(struct hda_codec *codec,
+                                   const struct hda_fixup *fix, int action)
+{
+       if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+               struct alc_spec *spec = codec->spec;
+               /* Disable AA-loopback as it causes white noise */
+               spec->gen.mixer_nid = 0;
+       }
+}
+
+static unsigned int alc_power_filter_xps13(struct hda_codec *codec,
+                               hda_nid_t nid,
+                               unsigned int power_state)
+{
+       struct alc_spec *spec = codec->spec;
+
+       /* Avoid pop noises when headphones are plugged in */
+       if (spec->gen.hp_jack_present)
+               if (nid == codec->afg || nid == 0x02)
+                       return AC_PWRST_D0;
+       return power_state;
+}
+
+static void alc_fixup_dell_xps13(struct hda_codec *codec,
+                               const struct hda_fixup *fix, int action)
+{
+       if (action == HDA_FIXUP_ACT_PROBE) {
+               struct alc_spec *spec = codec->spec;
+               spec->shutup = alc_no_shutup;
+               codec->power_filter = alc_power_filter_xps13;
+       }
+}
+
 static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
                                const struct hda_fixup *fix, int action)
 {
@@ -4110,6 +4223,7 @@ enum {
        ALC269_FIXUP_ASUS_G73JW,
        ALC269_FIXUP_LENOVO_EAPD,
        ALC275_FIXUP_SONY_HWEQ,
+       ALC275_FIXUP_SONY_DISABLE_AAMIX,
        ALC271_FIXUP_DMIC,
        ALC269_FIXUP_PCM_44K,
        ALC269_FIXUP_STEREO_DMIC,
@@ -4159,6 +4273,8 @@ enum {
        ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
        ALC255_FIXUP_HEADSET_MODE,
        ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC,
+       ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+       ALC292_FIXUP_TPT440_DOCK,
 };
 
 static const struct hda_fixup alc269_fixups[] = {
@@ -4213,6 +4329,12 @@ static const struct hda_fixup alc269_fixups[] = {
                .chained = true,
                .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2
        },
+       [ALC275_FIXUP_SONY_DISABLE_AAMIX] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc_fixup_disable_aamix,
+               .chained = true,
+               .chain_id = ALC269_FIXUP_SONY_VAIO
+       },
        [ALC271_FIXUP_DMIC] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc271_fixup_dmic,
@@ -4552,6 +4674,26 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc_fixup_headset_mode_alc255_no_hp_mic,
        },
+       [ALC293_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x18, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+                       { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC269_FIXUP_HEADSET_MODE
+       },
+       [ALC292_FIXUP_TPT440_DOCK] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x16, 0x21211010 }, /* dock headphone */
+                       { 0x19, 0x21a11010 }, /* dock mic */
+                       { }
+               },
+               .chained = true,
+               .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -4595,31 +4737,16 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1028, 0x0606, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x060f, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0610, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0614, "Dell Inspiron 3135", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
        SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
-       SND_PCI_QUIRK(0x1028, 0x061f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0629, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x062c, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x062e, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0632, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
-       SND_PCI_QUIRK(0x1028, 0x063e, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0640, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x064d, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0651, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0652, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0653, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0657, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0658, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x065c, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x065f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0662, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0667, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0668, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0669, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0674, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -4629,6 +4756,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1028, 0x0684, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
        SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
@@ -4702,6 +4831,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2),
        SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
        SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
+       SND_PCI_QUIRK(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX),
        SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
        SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
@@ -4715,7 +4845,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
-       SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+       SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440_DOCK),
+       SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -4793,9 +4924,215 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
        {.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"},
        {.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"},
        {.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"},
+       {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"},
        {}
 };
 
+static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
+       {
+               .codec = 0x10ec0255,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x90a60140},
+                       {0x14, 0x90170110},
+                       {0x17, 0x40000000},
+                       {0x18, 0x411111f0},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x40700001},
+                       {0x1e, 0x411111f0},
+                       {0x21, 0x02211020},
+               },
+               .value = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+       },
+       {
+               .codec = 0x10ec0255,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x90a60160},
+                       {0x14, 0x90170120},
+                       {0x17, 0x40000000},
+                       {0x18, 0x411111f0},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x40700001},
+                       {0x1e, 0x411111f0},
+                       {0x21, 0x02211030},
+               },
+               .value = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+       },
+       {
+               .codec = 0x10ec0255,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x90a60160},
+                       {0x14, 0x90170130},
+                       {0x17, 0x40000000},
+                       {0x18, 0x411111f0},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x40700001},
+                       {0x1e, 0x411111f0},
+                       {0x21, 0x02211040},
+               },
+               .value = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+       },
+       {
+               .codec = 0x10ec0255,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x90a60160},
+                       {0x14, 0x90170140},
+                       {0x17, 0x40000000},
+                       {0x18, 0x411111f0},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x40700001},
+                       {0x1e, 0x411111f0},
+                       {0x21, 0x02211050},
+               },
+               .value = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+       },
+       {
+               .codec = 0x10ec0255,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x90a60170},
+                       {0x14, 0x90170120},
+                       {0x17, 0x40000000},
+                       {0x18, 0x411111f0},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x40700001},
+                       {0x1e, 0x411111f0},
+                       {0x21, 0x02211030},
+               },
+               .value = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+       },
+       {
+               .codec = 0x10ec0255,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x90a60170},
+                       {0x14, 0x90170130},
+                       {0x17, 0x40000000},
+                       {0x18, 0x411111f0},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x40700001},
+                       {0x1e, 0x411111f0},
+                       {0x21, 0x02211040},
+               },
+               .value = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+       },
+       {
+               .codec = 0x10ec0283,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x90a60130},
+                       {0x14, 0x90170110},
+                       {0x17, 0x40020008},
+                       {0x18, 0x411111f0},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x40e00001},
+                       {0x1e, 0x411111f0},
+                       {0x21, 0x0321101f},
+               },
+               .value = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+       },
+       {
+               .codec = 0x10ec0283,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x90a60160},
+                       {0x14, 0x90170120},
+                       {0x17, 0x40000000},
+                       {0x18, 0x411111f0},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x40700001},
+                       {0x1e, 0x411111f0},
+                       {0x21, 0x02211030},
+               },
+               .value = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+       },
+       {
+               .codec = 0x10ec0292,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x90a60140},
+                       {0x13, 0x411111f0},
+                       {0x14, 0x90170110},
+                       {0x15, 0x0221401f},
+                       {0x16, 0x411111f0},
+                       {0x18, 0x411111f0},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x40700001},
+                       {0x1e, 0x411111f0},
+               },
+               .value = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+       },
+       {
+               .codec = 0x10ec0293,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x40000000},
+                       {0x13, 0x90a60140},
+                       {0x14, 0x90170110},
+                       {0x15, 0x0221401f},
+                       {0x16, 0x21014020},
+                       {0x18, 0x411111f0},
+                       {0x19, 0x21a19030},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x40700001},
+                       {0x1e, 0x411111f0},
+               },
+               .value = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+       },
+       {}
+};
 
 static void alc269_fill_coef(struct hda_codec *codec)
 {
@@ -4857,6 +5194,7 @@ static int patch_alc269(struct hda_codec *codec)
 
        snd_hda_pick_fixup(codec, alc269_fixup_models,
                       alc269_fixup_tbl, alc269_fixups);
+       snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups);
        snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
 
        alc_auto_parse_customize_define(codec);
@@ -5313,6 +5651,8 @@ enum {
        ALC662_FIXUP_BASS_1A,
        ALC662_FIXUP_BASS_CHMAP,
        ALC668_FIXUP_AUTO_MUTE,
+       ALC668_FIXUP_DELL_DISABLE_AAMIX,
+       ALC668_FIXUP_DELL_XPS13,
 };
 
 static const struct hda_fixup alc662_fixups[] = {
@@ -5479,6 +5819,18 @@ static const struct hda_fixup alc662_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc_fixup_inv_dmic_0x12,
        },
+       [ALC668_FIXUP_DELL_XPS13] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc_fixup_dell_xps13,
+               .chained = true,
+               .chain_id = ALC668_FIXUP_DELL_DISABLE_AAMIX
+       },
+       [ALC668_FIXUP_DELL_DISABLE_AAMIX] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc_fixup_disable_aamix,
+               .chained = true,
+               .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE
+       },
        [ALC668_FIXUP_AUTO_MUTE] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc_fixup_auto_mute_via_amp,
@@ -5539,13 +5891,9 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
        SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0623, "Dell", ALC668_FIXUP_AUTO_MUTE),
-       SND_PCI_QUIRK(0x1028, 0x0624, "Dell", ALC668_FIXUP_AUTO_MUTE),
+       SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_XPS13),
        SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0628, "Dell", ALC668_FIXUP_AUTO_MUTE),
-       SND_PCI_QUIRK(0x1028, 0x064e, "Dell", ALC668_FIXUP_AUTO_MUTE),
        SND_PCI_QUIRK(0x1028, 0x0696, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
@@ -5637,6 +5985,73 @@ static const struct hda_model_fixup alc662_fixup_models[] = {
        {}
 };
 
+static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
+       {
+               .codec = 0x10ec0668,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x99a30130},
+                       {0x14, 0x90170110},
+                       {0x15, 0x0321101f},
+                       {0x16, 0x03011020},
+                       {0x18, 0x40000008},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x41000001},
+                       {0x1e, 0x411111f0},
+                       {0x1f, 0x411111f0},
+               },
+               .value = ALC668_FIXUP_AUTO_MUTE,
+       },
+       {
+               .codec = 0x10ec0668,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x99a30150},
+                       {0x14, 0x90170110},
+                       {0x15, 0x0321101f},
+                       {0x16, 0x03011020},
+                       {0x18, 0x40000008},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x41000001},
+                       {0x1e, 0x411111f0},
+                       {0x1f, 0x411111f0},
+               },
+               .value = ALC668_FIXUP_AUTO_MUTE,
+       },
+       {
+               .codec = 0x10ec0668,
+               .subvendor = 0x1028,
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               .name = "Dell",
+#endif
+               .pins = (const struct hda_pintbl[]) {
+                       {0x12, 0x411111f0},
+                       {0x14, 0x90170110},
+                       {0x15, 0x0321101f},
+                       {0x16, 0x03011020},
+                       {0x18, 0x40000008},
+                       {0x19, 0x411111f0},
+                       {0x1a, 0x411111f0},
+                       {0x1b, 0x411111f0},
+                       {0x1d, 0x41000001},
+                       {0x1e, 0x411111f0},
+                       {0x1f, 0x411111f0},
+               },
+               .value = ALC668_FIXUP_AUTO_MUTE,
+       },
+       {}
+};
+
 static void alc662_fill_coef(struct hda_codec *codec)
 {
        int val, coef;
@@ -5686,6 +6101,7 @@ static int patch_alc662(struct hda_codec *codec)
 
        snd_hda_pick_fixup(codec, alc662_fixup_models,
                       alc662_fixup_tbl, alc662_fixups);
+       snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups);
        snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
 
        alc_auto_parse_customize_define(codec);
index 75515b4..7f40a15 100644 (file)
@@ -795,7 +795,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
        }
 
        while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
-               if (sscanf(dev->name, "HP_Mute_LED_%d_%x",
+               if (sscanf(dev->name, "HP_Mute_LED_%u_%x",
                           &spec->gpio_led_polarity,
                           &spec->gpio_led) == 2) {
                        unsigned int max_gpio;
@@ -808,7 +808,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
                                spec->vref_mute_led_nid = spec->gpio_led;
                        return 1;
                }
-               if (sscanf(dev->name, "HP_Mute_LED_%d",
+               if (sscanf(dev->name, "HP_Mute_LED_%u",
                           &spec->gpio_led_polarity) == 1) {
                        set_hp_led_gpio(codec);
                        return 1;
index 04df83d..c241dc0 100644 (file)
@@ -151,7 +151,7 @@ static void lola_proc_codec_rw_write(struct snd_info_entry *entry,
        char line[64];
        unsigned int id, verb, data, extdata;
        while (!snd_info_get_line(buffer, line, sizeof(line))) {
-               if (sscanf(line, "%i %i %i %i", &id, &verb, &data, &extdata) != 4)
+               if (sscanf(line, "%u %u %u %u", &id, &verb, &data, &extdata) != 4)
                        continue;
                lola_codec_read(chip, id, verb, data, extdata,
                                &chip->debug_res,
index 2d8e95e..e8f38e5 100644 (file)
@@ -24,6 +24,7 @@
 
 /* #define RMH_DEBUG 1 */
 
+#include <linux/bitops.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
@@ -429,11 +430,6 @@ int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data)
        return ret;
 }
 
-#define CSES_TIMEOUT        100     /* microseconds */
-#define CSES_CE             0x0001
-#define CSES_BROADCAST      0x0002
-#define CSES_UPDATE_LDSV    0x0004
-
 #define PIPE_INFO_TO_CMD(capture, pipe)                                        \
        ((u32)((u32)(pipe) | ((capture) ? ID_IS_CAPTURE : 0L)) << ID_OFFSET)
 
@@ -519,7 +515,6 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
                                *r_needed += 1;
                }
 
-#if 0
                dev_dbg(chip->card->dev,
                        "CMD_08_ASK_BUFFERS: needed %d, freed %d\n",
                            *r_needed, *r_freed);
@@ -530,7 +525,6 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
                                            chip->rmh.stat[i],
                                            chip->rmh.stat[i] & MASK_DATA_SIZE);
                }
-#endif
        }
 
        spin_unlock_irqrestore(&chip->msg_lock, flags);
@@ -971,9 +965,9 @@ int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
 
 /* interrupt handling */
 #define PCX_IRQ_NONE 0
-#define IRQCS_ACTIVE_PCIDB  0x00002000L         /* Bit nÃ\83¸ 13 */
-#define IRQCS_ENABLE_PCIIRQ 0x00000100L         /* Bit nÃ\83¸ 08 */
-#define IRQCS_ENABLE_PCIDB  0x00000200L         /* Bit nÃ\83¸ 09 */
+#define IRQCS_ACTIVE_PCIDB     BIT(13)
+#define IRQCS_ENABLE_PCIIRQ    BIT(8)
+#define IRQCS_ENABLE_PCIDB     BIT(9)
 
 static u32 lx_interrupt_test_ack(struct lx6464es *chip)
 {
@@ -1030,25 +1024,21 @@ static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc,
        int err;
        u32 stat[9];            /* answer from CMD_04_GET_EVENT */
 
-       /* On peut optimiser pour ne pas lire les evenements vides
-        * les mots de rÃ\83©ponse sont dans l'ordre suivant :
-        * Stat[0]      mot de status gÃ\83©nÃ\83©ral
-        * Stat[1]      fin de buffer OUT pF
-        * Stat[2]      fin de buffer OUT pf
-        * Stat[3]      fin de buffer IN pF
-        * Stat[4]      fin de buffer IN pf
-        * Stat[5]      underrun poid fort
-        * Stat[6]      underrun poid faible
-        * Stat[7]      overrun poid fort
-        * Stat[8]      overrun poid faible
+       /* We can optimize this to not read dumb events.
+        * Answer words are in the following order:
+        * Stat[0]      general status
+        * Stat[1]      end of buffer OUT pF
+        * Stat[2]      end of buffer OUT pf
+        * Stat[3]      end of buffer IN pF
+        * Stat[4]      end of buffer IN pf
+        * Stat[5]      MSB underrun
+        * Stat[6]      LSB underrun
+        * Stat[7]      MSB overrun
+        * Stat[8]      LSB overrun
         * */
 
        u64 orun_mask;
        u64 urun_mask;
-#if 0
-       int has_underrun   = (irqsrc & MASK_SYS_STATUS_URUN) ? 1 : 0;
-       int has_overrun    = (irqsrc & MASK_SYS_STATUS_ORUN) ? 1 : 0;
-#endif
        int eb_pending_out = (irqsrc & MASK_SYS_STATUS_EOBO) ? 1 : 0;
        int eb_pending_in  = (irqsrc & MASK_SYS_STATUS_EOBI) ? 1 : 0;
 
@@ -1199,9 +1189,8 @@ irqreturn_t lx_interrupt(int irq, void *dev_id)
        if (irqsrc & MASK_SYS_STATUS_CMD_DONE)
                goto exit;
 
-#if 0
        if (irqsrc & MASK_SYS_STATUS_EOBI)
-               dev_dgg(chip->card->dev, "interrupt: EOBI\n");
+               dev_dbg(chip->card->dev, "interrupt: EOBI\n");
 
        if (irqsrc & MASK_SYS_STATUS_EOBO)
                dev_dbg(chip->card->dev, "interrupt: EOBO\n");
@@ -1211,7 +1200,6 @@ irqreturn_t lx_interrupt(int irq, void *dev_id)
 
        if (irqsrc & MASK_SYS_STATUS_ORUN)
                dev_dbg(chip->card->dev, "interrupt: ORUN\n");
-#endif
 
        if (async_pending) {
                u64 notified_in_pipe_mask = 0;
@@ -1238,7 +1226,6 @@ irqreturn_t lx_interrupt(int irq, void *dev_id)
        }
 
        if (async_escmd) {
-#if 0
                /* backdoor for ethersound commands
                 *
                 * for now, we do not need this
@@ -1246,7 +1233,6 @@ irqreturn_t lx_interrupt(int irq, void *dev_id)
                 * */
 
                dev_dbg(chip->card->dev, "interrupt requests escmd handling\n");
-#endif
        }
 
 exit:
index 4789619..27e3fc4 100644 (file)
@@ -35,7 +35,7 @@ config SND_AT91_SOC_SAM9G20_WM8731
 
 config SND_ATMEL_SOC_WM8904
        tristate "Atmel ASoC driver for boards using WM8904 codec"
-       depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC
+       depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC && I2C
        select SND_ATMEL_SOC_SSC
        select SND_ATMEL_SOC_DMA
        select SND_SOC_WM8904
index 33ec592..a366b35 100644 (file)
@@ -76,12 +76,6 @@ struct atmel_runtime_data {
        size_t period_size;
 
        dma_addr_t period_ptr;          /* physical address of next period */
-
-       /* PDC register save */
-       u32 pdc_xpr_save;
-       u32 pdc_xcr_save;
-       u32 pdc_xnpr_save;
-       u32 pdc_xncr_save;
 };
 
 /*--------------------------------------------------------------------------*\
@@ -320,67 +314,10 @@ static struct snd_pcm_ops atmel_pcm_ops = {
        .mmap           = atmel_pcm_mmap,
 };
 
-
-/*--------------------------------------------------------------------------*\
- * ASoC platform driver
-\*--------------------------------------------------------------------------*/
-#ifdef CONFIG_PM
-static int atmel_pcm_suspend(struct snd_soc_dai *dai)
-{
-       struct snd_pcm_runtime *runtime = dai->runtime;
-       struct atmel_runtime_data *prtd;
-       struct atmel_pcm_dma_params *params;
-
-       if (!runtime)
-               return 0;
-
-       prtd = runtime->private_data;
-       params = prtd->params;
-
-       /* disable the PDC and save the PDC registers */
-
-       ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
-
-       prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
-       prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
-       prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
-       prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
-
-       return 0;
-}
-
-static int atmel_pcm_resume(struct snd_soc_dai *dai)
-{
-       struct snd_pcm_runtime *runtime = dai->runtime;
-       struct atmel_runtime_data *prtd;
-       struct atmel_pcm_dma_params *params;
-
-       if (!runtime)
-               return 0;
-
-       prtd = runtime->private_data;
-       params = prtd->params;
-
-       /* restore the PDC registers and enable the PDC */
-       ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
-       ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
-       ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
-       ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
-
-       ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
-       return 0;
-}
-#else
-#define atmel_pcm_suspend      NULL
-#define atmel_pcm_resume       NULL
-#endif
-
 static struct snd_soc_platform_driver atmel_soc_platform = {
        .ops            = &atmel_pcm_ops,
        .pcm_new        = atmel_pcm_new,
        .pcm_free       = atmel_pcm_free,
-       .suspend        = atmel_pcm_suspend,
-       .resume         = atmel_pcm_resume,
 };
 
 int atmel_pcm_pdc_platform_register(struct device *dev)
index f65f08b..9579799 100644 (file)
@@ -80,17 +80,6 @@ static const struct snd_soc_dapm_route afeb9260_audio_map[] = {
        {"MICIN", NULL, "Mic Jack"},
 };
 
-static int afeb9260_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
-       snd_soc_dapm_enable_pin(dapm, "Line In");
-       snd_soc_dapm_enable_pin(dapm, "Mic Jack");
-
-       return 0;
-}
 
 /* Digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link afeb9260_dai = {
@@ -100,7 +89,6 @@ static struct snd_soc_dai_link afeb9260_dai = {
        .codec_dai_name = "tlv320aic23-hifi",
        .platform_name = "atmel_pcm-audio",
        .codec_name = "tlv320aic23-codec.0-001a",
-       .init = afeb9260_tlv320aic23_init,
        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
                   SND_SOC_DAIFMT_CBM_CFM,
        .ops = &afeb9260_ops,
index 6347d59..6410aa2 100644 (file)
@@ -43,6 +43,32 @@ config SND_SOC_BFIN_EVAL_ADAU1373
          Note: This driver assumes that first ADAU1373 DAI is connected to the
          first SPORT port on the BF5XX board.
 
+config SND_SOC_BFIN_EVAL_ADAU1X61
+       tristate "Support for the EVAL-ADAU1X61 board on Blackfin eval boards"
+       depends on SND_BF5XX_I2S && I2C
+       select SND_BF5XX_SOC_I2S
+       select SND_SOC_ADAU1761_I2C
+       help
+         Say Y if you want to add support for the Analog Devices EVAL-ADAU1X61
+         board connected to one of the Blackfin evaluation boards like the
+         BF5XX-STAMP or BF5XX-EZKIT.
+
+         Note: This driver assumes that the ADAU1X61 is connected to the
+         first SPORT port on the BF5XX board.
+
+config SND_SOC_BFIN_EVAL_ADAU1X81
+       tristate "Support for the EVAL-ADAU1X81 boards on Blackfin eval boards"
+       depends on SND_BF5XX_I2S && I2C
+       select SND_BF5XX_SOC_I2S
+       select SND_SOC_ADAU1781_I2C
+       help
+         Say Y if you want to add support for the Analog Devices EVAL-ADAU1X81
+         board connected to one of the Blackfin evaluation boards like the
+         BF5XX-STAMP or BF5XX-EZKIT.
+
+         Note: This driver assumes that the ADAU1X81 is connected to the
+         first SPORT port on the BF5XX board.
+
 config SND_SOC_BFIN_EVAL_ADAV80X
        tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards"
        depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI
index ad0a6e9..f21e948 100644 (file)
@@ -22,6 +22,8 @@ snd-ssm2602-objs := bf5xx-ssm2602.o
 snd-ad73311-objs := bf5xx-ad73311.o
 snd-ad193x-objs := bf5xx-ad193x.o
 snd-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o
+snd-soc-bfin-eval-adau1x61-objs := bfin-eval-adau1x61.o
+snd-soc-bfin-eval-adau1x81-objs := bfin-eval-adau1x81.o
 snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
 snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o
 
@@ -31,5 +33,7 @@ obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
 obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
 obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o
 obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373) += snd-soc-bfin-eval-adau1373.o
+obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X61) += snd-soc-bfin-eval-adau1x61.o
+obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X81) += snd-soc-bfin-eval-adau1x81.o
 obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o
 obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o
diff --git a/sound/soc/blackfin/bfin-eval-adau1x61.c b/sound/soc/blackfin/bfin-eval-adau1x61.c
new file mode 100644 (file)
index 0000000..3011906
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Machine driver for EVAL-ADAU1x61MINIZ on Analog Devices bfin
+ * evaluation boards.
+ *
+ * Copyright 2011-2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/adau17x1.h"
+
+static const struct snd_soc_dapm_widget bfin_eval_adau1x61_dapm_widgets[] = {
+       SND_SOC_DAPM_LINE("In 1", NULL),
+       SND_SOC_DAPM_LINE("In 2", NULL),
+       SND_SOC_DAPM_LINE("In 3-4", NULL),
+
+       SND_SOC_DAPM_LINE("Diff Out L", NULL),
+       SND_SOC_DAPM_LINE("Diff Out R", NULL),
+       SND_SOC_DAPM_LINE("Stereo Out", NULL),
+       SND_SOC_DAPM_HP("Capless HP Out", NULL),
+};
+
+static const struct snd_soc_dapm_route bfin_eval_adau1x61_dapm_routes[] = {
+       { "LAUX", NULL, "In 3-4" },
+       { "RAUX", NULL, "In 3-4" },
+       { "LINP", NULL, "In 1" },
+       { "LINN", NULL, "In 1"},
+       { "RINP", NULL, "In 2" },
+       { "RINN", NULL, "In 2" },
+
+       { "In 1", NULL, "MICBIAS" },
+       { "In 2", NULL, "MICBIAS" },
+
+       { "Capless HP Out", NULL, "LHP" },
+       { "Capless HP Out", NULL, "RHP" },
+       { "Diff Out L", NULL, "LOUT" },
+       { "Diff Out R", NULL, "ROUT" },
+       { "Stereo Out", NULL, "LOUT" },
+       { "Stereo Out", NULL, "ROUT" },
+};
+
+static int bfin_eval_adau1x61_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->codec_dai;
+       int pll_rate;
+       int ret;
+
+       switch (params_rate(params)) {
+       case 48000:
+       case 8000:
+       case 12000:
+       case 16000:
+       case 24000:
+       case 32000:
+       case 96000:
+               pll_rate = 48000 * 1024;
+               break;
+       case 44100:
+       case 7350:
+       case 11025:
+       case 14700:
+       case 22050:
+       case 29400:
+       case 88200:
+               pll_rate = 44100 * 1024;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL,
+                       ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, pll_rate,
+                       SND_SOC_CLOCK_IN);
+
+       return ret;
+}
+
+static const struct snd_soc_ops bfin_eval_adau1x61_ops = {
+       .hw_params = bfin_eval_adau1x61_hw_params,
+};
+
+static struct snd_soc_dai_link bfin_eval_adau1x61_dai = {
+       .name = "adau1x61",
+       .stream_name = "adau1x61",
+       .cpu_dai_name = "bfin-i2s.0",
+       .codec_dai_name = "adau-hifi",
+       .platform_name = "bfin-i2s-pcm-audio",
+       .codec_name = "adau1761.0-0038",
+       .ops = &bfin_eval_adau1x61_ops,
+       .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+               SND_SOC_DAIFMT_CBM_CFM,
+};
+
+static struct snd_soc_card bfin_eval_adau1x61 = {
+       .name = "bfin-eval-adau1x61",
+       .driver_name = "eval-adau1x61",
+       .dai_link = &bfin_eval_adau1x61_dai,
+       .num_links = 1,
+
+       .dapm_widgets = bfin_eval_adau1x61_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1x61_dapm_widgets),
+       .dapm_routes = bfin_eval_adau1x61_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1x61_dapm_routes),
+       .fully_routed = true,
+};
+
+static int bfin_eval_adau1x61_probe(struct platform_device *pdev)
+{
+       bfin_eval_adau1x61.dev = &pdev->dev;
+
+       return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1x61);
+}
+
+static struct platform_driver bfin_eval_adau1x61_driver = {
+       .driver = {
+               .name = "bfin-eval-adau1x61",
+               .owner = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+       },
+       .probe = bfin_eval_adau1x61_probe,
+};
+module_platform_driver(bfin_eval_adau1x61_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ALSA SoC bfin adau1x61 driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bfin-eval-adau1x61");
diff --git a/sound/soc/blackfin/bfin-eval-adau1x81.c b/sound/soc/blackfin/bfin-eval-adau1x81.c
new file mode 100644 (file)
index 0000000..5c380f6
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Machine driver for EVAL-ADAU1x81 on Analog Devices bfin
+ * evaluation boards.
+ *
+ * Copyright 2011-2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/adau17x1.h"
+
+static const struct snd_soc_dapm_widget bfin_eval_adau1x81_dapm_widgets[] = {
+       SND_SOC_DAPM_LINE("Stereo In", NULL),
+       SND_SOC_DAPM_LINE("Beep", NULL),
+
+       SND_SOC_DAPM_SPK("Speaker", NULL),
+       SND_SOC_DAPM_HP("Headphone", NULL),
+};
+
+static const struct snd_soc_dapm_route bfin_eval_adau1x81_dapm_routes[] = {
+       { "BEEP", NULL, "Beep" },
+       { "LMIC", NULL, "Stereo In" },
+       { "LMIC", NULL, "Stereo In" },
+
+       { "Headphone", NULL, "AOUTL" },
+       { "Headphone", NULL, "AOUTR" },
+       { "Speaker", NULL, "SP" },
+};
+
+static int bfin_eval_adau1x81_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->codec_dai;
+       int pll_rate;
+       int ret;
+
+       switch (params_rate(params)) {
+       case 48000:
+       case 8000:
+       case 12000:
+       case 16000:
+       case 24000:
+       case 32000:
+       case 96000:
+               pll_rate = 48000 * 1024;
+               break;
+       case 44100:
+       case 7350:
+       case 11025:
+       case 14700:
+       case 22050:
+       case 29400:
+       case 88200:
+               pll_rate = 44100 * 1024;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL,
+                       ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, pll_rate,
+                       SND_SOC_CLOCK_IN);
+
+       return ret;
+}
+
+static const struct snd_soc_ops bfin_eval_adau1x81_ops = {
+       .hw_params = bfin_eval_adau1x81_hw_params,
+};
+
+static struct snd_soc_dai_link bfin_eval_adau1x81_dai = {
+       .name = "adau1x81",
+       .stream_name = "adau1x81",
+       .cpu_dai_name = "bfin-i2s.0",
+       .codec_dai_name = "adau-hifi",
+       .platform_name = "bfin-i2s-pcm-audio",
+       .codec_name = "adau1781.0-0038",
+       .ops = &bfin_eval_adau1x81_ops,
+       .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+               SND_SOC_DAIFMT_CBM_CFM,
+};
+
+static struct snd_soc_card bfin_eval_adau1x81 = {
+       .name = "bfin-eval-adau1x81",
+       .driver_name = "eval-adau1x81",
+       .dai_link = &bfin_eval_adau1x81_dai,
+       .num_links = 1,
+
+       .dapm_widgets = bfin_eval_adau1x81_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1x81_dapm_widgets),
+       .dapm_routes = bfin_eval_adau1x81_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1x81_dapm_routes),
+       .fully_routed = true,
+};
+
+static int bfin_eval_adau1x81_probe(struct platform_device *pdev)
+{
+       bfin_eval_adau1x81.dev = &pdev->dev;
+
+       return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1x81);
+}
+
+static struct platform_driver bfin_eval_adau1x81_driver = {
+       .driver = {
+               .name = "bfin-eval-adau1x81",
+               .owner = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+       },
+       .probe = bfin_eval_adau1x81_probe,
+};
+module_platform_driver(bfin_eval_adau1x81_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ALSA SoC bfin adau1x81 driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bfin-eval-adau1x81");
index b07e171..3c4b10f 100644 (file)
@@ -276,7 +276,7 @@ static int snd_soc_get_volsw_2r_st(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);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int reg2 = mc->rreg;
        int val[2], val2[2], i;
@@ -300,7 +300,7 @@ static int snd_soc_put_volsw_2r_st(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);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int reg2 = mc->rreg;
        int err;
@@ -333,7 +333,7 @@ static int snd_soc_get_volsw_2r_out(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);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int reg2 = mc->rreg;
        unsigned int shift = mc->shift;
@@ -353,7 +353,7 @@ static int snd_soc_put_volsw_2r_out(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);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int reg2 = mc->rreg;
        unsigned int shift = mc->shift;
@@ -1327,10 +1327,6 @@ static int pm860x_probe(struct snd_soc_codec *codec)
 
        pm860x->codec = codec;
 
-       ret = snd_soc_codec_set_cache_io(codec, pm860x->regmap);
-       if (ret)
-               return ret;
-
        for (i = 0; i < 4; i++) {
                ret = request_threaded_irq(pm860x->irq[i], NULL,
                                           pm860x_codec_handler, IRQF_ONESHOT,
@@ -1362,10 +1358,18 @@ static int pm860x_remove(struct snd_soc_codec *codec)
        return 0;
 }
 
+static struct regmap *pm860x_get_regmap(struct device *dev)
+{
+       struct pm860x_priv *pm860x = dev_get_drvdata(dev);
+
+       return pm860x->regmap;
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_pm860x = {
        .probe          = pm860x_probe,
        .remove         = pm860x_remove,
        .set_bias_level = pm860x_set_bias_level,
+       .get_regmap     = pm860x_get_regmap,
 
        .controls = pm860x_snd_controls,
        .num_controls = ARRAY_SIZE(pm860x_snd_controls),
index f0e8401..cbfa1e1 100644 (file)
@@ -23,6 +23,10 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_AD1980 if SND_SOC_AC97_BUS
        select SND_SOC_AD73311
        select SND_SOC_ADAU1373 if I2C
+       select SND_SOC_ADAU1761_I2C if I2C
+       select SND_SOC_ADAU1761_SPI if SPI
+       select SND_SOC_ADAU1781_I2C if I2C
+       select SND_SOC_ADAU1781_SPI if SPI
        select SND_SOC_ADAV801 if SPI_MASTER
        select SND_SOC_ADAV803 if I2C
        select SND_SOC_ADAU1977_SPI if SPI_MASTER
@@ -39,8 +43,9 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_ALC5623 if I2C
        select SND_SOC_ALC5632 if I2C
        select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
-       select SND_SOC_CS42L51 if I2C
-       select SND_SOC_CS42L52 if I2C
+       select SND_SOC_CS42L51_I2C if I2C
+       select SND_SOC_CS42L52 if I2C && INPUT
+       select SND_SOC_CS42L56 if I2C && INPUT
        select SND_SOC_CS42L73 if I2C
        select SND_SOC_CS4270 if I2C
        select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
@@ -71,6 +76,9 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_PCM512x_SPI if SPI_MASTER
        select SND_SOC_RT5631 if I2C
        select SND_SOC_RT5640 if I2C
+       select SND_SOC_RT5645 if I2C
+       select SND_SOC_RT5651 if I2C
+       select SND_SOC_RT5677 if I2C
        select SND_SOC_SGTL5000 if I2C
        select SND_SOC_SI476X if MFD_SI476X_CORE
        select SND_SOC_SIRF_AUDIO_CODEC
@@ -80,6 +88,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_SSM2602_SPI if SPI_MASTER
        select SND_SOC_SSM2602_I2C if I2C
        select SND_SOC_STA32X if I2C
+       select SND_SOC_STA350 if I2C
        select SND_SOC_STA529 if I2C
        select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
        select SND_SOC_TAS5086 if I2C
@@ -127,7 +136,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_WM8955 if I2C
        select SND_SOC_WM8960 if I2C
        select SND_SOC_WM8961 if I2C
-       select SND_SOC_WM8962 if I2C
+       select SND_SOC_WM8962 if I2C && INPUT
        select SND_SOC_WM8971 if I2C
        select SND_SOC_WM8974 if I2C
        select SND_SOC_WM8978 if I2C
@@ -210,13 +219,45 @@ config SND_SOC_AD1980
 config SND_SOC_AD73311
        tristate
 
+config SND_SOC_ADAU1373
+       tristate
+
 config SND_SOC_ADAU1701
        tristate "Analog Devices ADAU1701 CODEC"
        depends on I2C
        select SND_SOC_SIGMADSP
 
-config SND_SOC_ADAU1373
+config SND_SOC_ADAU17X1
+       tristate
+       select SND_SOC_SIGMADSP
+
+config SND_SOC_ADAU1761
+       tristate
+       select SND_SOC_ADAU17X1
+
+config SND_SOC_ADAU1761_I2C
+       tristate
+       select SND_SOC_ADAU1761
+       select REGMAP_I2C
+
+config SND_SOC_ADAU1761_SPI
+       tristate
+       select SND_SOC_ADAU1761
+       select REGMAP_SPI
+
+config SND_SOC_ADAU1781
+       select SND_SOC_ADAU17X1
+       tristate
+
+config SND_SOC_ADAU1781_I2C
+       tristate
+       select SND_SOC_ADAU1781
+       select REGMAP_I2C
+
+config SND_SOC_ADAU1781_SPI
        tristate
+       select SND_SOC_ADAU1781
+       select REGMAP_SPI
 
 config SND_SOC_ADAU1977
        tristate
@@ -269,7 +310,8 @@ config SND_SOC_AK5386
        tristate "AKM AK5638 CODEC"
 
 config SND_SOC_ALC5623
-       tristate
+       tristate "Realtek ALC5623 CODEC"
+       depends on I2C
 
 config SND_SOC_ALC5632
        tristate
@@ -280,9 +322,17 @@ config SND_SOC_CQ0093VC
 config SND_SOC_CS42L51
        tristate
 
+config SND_SOC_CS42L51_I2C
+       tristate
+       select SND_SOC_CS42L51
+
 config SND_SOC_CS42L52
        tristate "Cirrus Logic CS42L52 CODEC"
-       depends on I2C
+       depends on I2C && INPUT
+
+config SND_SOC_CS42L56
+       tristate "Cirrus Logic CS42L56 CODEC"
+       depends on I2C && INPUT
 
 config SND_SOC_CS42L73
        tristate "Cirrus Logic CS42L73 CODEC"
@@ -390,12 +440,30 @@ config SND_SOC_PCM512x_SPI
        select SND_SOC_PCM512x
        select REGMAP_SPI
 
+config SND_SOC_RL6231
+       tristate
+       default y if SND_SOC_RT5640=y
+       default y if SND_SOC_RT5645=y
+       default y if SND_SOC_RT5651=y
+       default m if SND_SOC_RT5640=m
+       default m if SND_SOC_RT5645=m
+       default m if SND_SOC_RT5651=m
+
 config SND_SOC_RT5631
        tristate
 
 config SND_SOC_RT5640
        tristate
 
+config SND_SOC_RT5645
+        tristate
+
+config SND_SOC_RT5651
+       tristate
+
+config SND_SOC_RT5677
+       tristate
+
 #Freescale sgtl5000 codec
 config SND_SOC_SGTL5000
        tristate "Freescale SGTL5000 CODEC"
@@ -435,6 +503,10 @@ config SND_SOC_SSM2602_I2C
 config SND_SOC_STA32X
        tristate
 
+config SND_SOC_STA350
+       tristate "STA350 speaker amplifier"
+       depends on I2C
+
 config SND_SOC_STA529
        tristate
 
@@ -598,7 +670,7 @@ config SND_SOC_WM8961
 
 config SND_SOC_WM8962
        tristate "Wolfson Microelectronics WM8962 CODEC"
-       depends on I2C
+       depends on I2C && INPUT
 
 config SND_SOC_WM8971
        tristate
index 3c4d275..be3377b 100644 (file)
@@ -7,8 +7,15 @@ snd-soc-ad193x-spi-objs := ad193x-spi.o
 snd-soc-ad193x-i2c-objs := ad193x-i2c.o
 snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
-snd-soc-adau1701-objs := adau1701.o
 snd-soc-adau1373-objs := adau1373.o
+snd-soc-adau1701-objs := adau1701.o
+snd-soc-adau17x1-objs := adau17x1.o
+snd-soc-adau1761-objs := adau1761.o
+snd-soc-adau1761-i2c-objs := adau1761-i2c.o
+snd-soc-adau1761-spi-objs := adau1761-spi.o
+snd-soc-adau1781-objs := adau1781.o
+snd-soc-adau1781-i2c-objs := adau1781-i2c.o
+snd-soc-adau1781-spi-objs := adau1781-spi.o
 snd-soc-adau1977-objs := adau1977.o
 snd-soc-adau1977-spi-objs := adau1977-spi.o
 snd-soc-adau1977-i2c-objs := adau1977-i2c.o
@@ -26,7 +33,9 @@ snd-soc-ak5386-objs := ak5386.o
 snd-soc-arizona-objs := arizona.o
 snd-soc-cq93vc-objs := cq93vc.o
 snd-soc-cs42l51-objs := cs42l51.o
+snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
 snd-soc-cs42l52-objs := cs42l52.o
+snd-soc-cs42l56-objs := cs42l56.o
 snd-soc-cs42l73-objs := cs42l73.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cs4271-objs := cs4271.o
@@ -58,8 +67,12 @@ snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-pcm512x-objs := pcm512x.o
 snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
 snd-soc-pcm512x-spi-objs := pcm512x-spi.o
+snd-soc-rl6231-objs := rl6231.o
 snd-soc-rt5631-objs := rt5631.o
 snd-soc-rt5640-objs := rt5640.o
+snd-soc-rt5645-objs := rt5645.o
+snd-soc-rt5651-objs := rt5651.o
+snd-soc-rt5677-objs := rt5677.o
 snd-soc-sgtl5000-objs := sgtl5000.o
 snd-soc-alc5623-objs := alc5623.o
 snd-soc-alc5632-objs := alc5632.o
@@ -74,6 +87,7 @@ snd-soc-ssm2602-objs := ssm2602.o
 snd-soc-ssm2602-spi-objs := ssm2602-spi.o
 snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o
 snd-soc-sta32x-objs := sta32x.o
+snd-soc-sta350-objs := sta350.o
 snd-soc-sta529-objs := sta529.o
 snd-soc-stac9766-objs := stac9766.o
 snd-soc-tas5086-objs := tas5086.o
@@ -157,10 +171,17 @@ obj-$(CONFIG_SND_SOC_AD193X_I2C)  += snd-soc-ad193x-i2c.o
 obj-$(CONFIG_SND_SOC_AD1980)   += snd-soc-ad1980.o
 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
 obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o
+obj-$(CONFIG_SND_SOC_ADAU1701)         += snd-soc-adau1701.o
+obj-$(CONFIG_SND_SOC_ADAU17X1)         += snd-soc-adau17x1.o
+obj-$(CONFIG_SND_SOC_ADAU1761)         += snd-soc-adau1761.o
+obj-$(CONFIG_SND_SOC_ADAU1761_I2C)     += snd-soc-adau1761-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU1761_SPI)     += snd-soc-adau1761-spi.o
+obj-$(CONFIG_SND_SOC_ADAU1781)         += snd-soc-adau1781.o
+obj-$(CONFIG_SND_SOC_ADAU1781_I2C)     += snd-soc-adau1781-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU1781_SPI)     += snd-soc-adau1781-spi.o
 obj-$(CONFIG_SND_SOC_ADAU1977)         += snd-soc-adau1977.o
 obj-$(CONFIG_SND_SOC_ADAU1977_SPI)     += snd-soc-adau1977-spi.o
 obj-$(CONFIG_SND_SOC_ADAU1977_I2C)     += snd-soc-adau1977-i2c.o
-obj-$(CONFIG_SND_SOC_ADAU1701)  += snd-soc-adau1701.o
 obj-$(CONFIG_SND_SOC_ADAV80X)  += snd-soc-adav80x.o
 obj-$(CONFIG_SND_SOC_ADAV801)  += snd-soc-adav801.o
 obj-$(CONFIG_SND_SOC_ADAV803)  += snd-soc-adav803.o
@@ -177,7 +198,9 @@ obj-$(CONFIG_SND_SOC_ALC5632)       += snd-soc-alc5632.o
 obj-$(CONFIG_SND_SOC_ARIZONA)  += snd-soc-arizona.o
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CS42L51)  += snd-soc-cs42l51.o
+obj-$(CONFIG_SND_SOC_CS42L51_I2C)      += snd-soc-cs42l51-i2c.o
 obj-$(CONFIG_SND_SOC_CS42L52)  += snd-soc-cs42l52.o
+obj-$(CONFIG_SND_SOC_CS42L56)  += snd-soc-cs42l56.o
 obj-$(CONFIG_SND_SOC_CS42L73)  += snd-soc-cs42l73.o
 obj-$(CONFIG_SND_SOC_CS4270)   += snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CS4271)   += snd-soc-cs4271.o
@@ -209,8 +232,12 @@ obj-$(CONFIG_SND_SOC_PCM3008)      += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_PCM512x)  += snd-soc-pcm512x.o
 obj-$(CONFIG_SND_SOC_PCM512x_I2C)      += snd-soc-pcm512x-i2c.o
 obj-$(CONFIG_SND_SOC_PCM512x_SPI)      += snd-soc-pcm512x-spi.o
+obj-$(CONFIG_SND_SOC_RL6231)   += snd-soc-rl6231.o
 obj-$(CONFIG_SND_SOC_RT5631)   += snd-soc-rt5631.o
 obj-$(CONFIG_SND_SOC_RT5640)   += snd-soc-rt5640.o
+obj-$(CONFIG_SND_SOC_RT5645)   += snd-soc-rt5645.o
+obj-$(CONFIG_SND_SOC_RT5651)   += snd-soc-rt5651.o
+obj-$(CONFIG_SND_SOC_RT5677)   += snd-soc-rt5677.o
 obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
 obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
 obj-$(CONFIG_SND_SOC_SI476X)   += snd-soc-si476x.o
@@ -221,6 +248,7 @@ obj-$(CONFIG_SND_SOC_SSM2602)       += snd-soc-ssm2602.o
 obj-$(CONFIG_SND_SOC_SSM2602_SPI)      += snd-soc-ssm2602-spi.o
 obj-$(CONFIG_SND_SOC_SSM2602_I2C)      += snd-soc-ssm2602-i2c.o
 obj-$(CONFIG_SND_SOC_STA32X)   += snd-soc-sta32x.o
+obj-$(CONFIG_SND_SOC_STA350)   += snd-soc-sta350.o
 obj-$(CONFIG_SND_SOC_STA529)   += snd-soc-sta529.o
 obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
 obj-$(CONFIG_SND_SOC_TAS5086)  += snd-soc-tas5086.o
index 1ad92cb..1fb4402 100644 (file)
@@ -1139,7 +1139,7 @@ static void anc_configure(struct snd_soc_codec *codec,
 static int sid_status_control_get(struct snd_kcontrol *kcontrol,
                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
 
        mutex_lock(&codec->mutex);
@@ -1153,7 +1153,7 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol,
 static int sid_status_control_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
        unsigned int param, sidconf, val;
        int status = 1;
@@ -1208,7 +1208,7 @@ out:
 static int anc_status_control_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
 
        mutex_lock(&codec->mutex);
@@ -1221,7 +1221,7 @@ static int anc_status_control_get(struct snd_kcontrol *kcontrol,
 static int anc_status_control_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
        struct device *dev = codec->dev;
        bool apply_fir, apply_iir;
@@ -1306,7 +1306,7 @@ static int filter_control_info(struct snd_kcontrol *kcontrol,
 static int filter_control_get(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct filter_control *fc =
                        (struct filter_control *)kcontrol->private_value;
        unsigned int i;
@@ -1322,7 +1322,7 @@ static int filter_control_get(struct snd_kcontrol *kcontrol,
 static int filter_control_put(struct snd_kcontrol *kcontrol,
                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct filter_control *fc =
                        (struct filter_control *)kcontrol->private_value;
        unsigned int i;
index 34d965a..304d300 100644 (file)
@@ -189,28 +189,27 @@ static struct snd_soc_dai_driver ad1980_dai = {
 
 static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
 {
-       u16 retry_cnt = 0;
+       unsigned int retry_cnt = 0;
 
-retry:
-       if (try_warm && soc_ac97_ops->warm_reset) {
-               soc_ac97_ops->warm_reset(codec->ac97);
-               if (ac97_read(codec, AC97_RESET) == 0x0090)
-                       return 1;
-       }
-
-       soc_ac97_ops->reset(codec->ac97);
-       /* Set bit 16slot in register 74h, then every slot will has only 16
-        * bits. This command is sent out in 20bit mode, in which case the
-        * first nibble of data is eaten by the addr. (Tag is always 16 bit)*/
-       ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
-
-       if (ac97_read(codec, AC97_RESET)  != 0x0090)
-               goto err;
-       return 0;
+       do {
+               if (try_warm && soc_ac97_ops->warm_reset) {
+                       soc_ac97_ops->warm_reset(codec->ac97);
+                       if (ac97_read(codec, AC97_RESET) == 0x0090)
+                               return 1;
+               }
 
-err:
-       while (retry_cnt++ < 10)
-               goto retry;
+               soc_ac97_ops->reset(codec->ac97);
+               /*
+                * Set bit 16slot in register 74h, then every slot will has only
+                * 16 bits. This command is sent out in 20bit mode, in which
+                * case the first nibble of data is eaten by the addr. (Tag is
+                * always 16 bit)
+                */
+               ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
+
+               if (ac97_read(codec, AC97_RESET)  == 0x0090)
+                       return 0;
+       } while (retry_cnt++ < 10);
 
        printk(KERN_ERR "AD1980 AC97 reset failed\n");
        return -EIO;
index 877f573..1ff7d4d 100644 (file)
@@ -519,8 +519,7 @@ static const struct snd_kcontrol_new adau1373_controls[] = {
        SOC_ENUM("HPF Channel", adau1373_hpf_channel_enum),
 
        SOC_ENUM("Bass HPF Cutoff", adau1373_bass_hpf_cutoff_enum),
-       SOC_VALUE_ENUM("Bass Clip Level Threshold",
-           adau1373_bass_clip_level_enum),
+       SOC_ENUM("Bass Clip Level Threshold", adau1373_bass_clip_level_enum),
        SOC_ENUM("Bass LPF Cutoff", adau1373_bass_lpf_cutoff_enum),
        SOC_DOUBLE("Bass Playback Switch", ADAU1373_BASS2, 0, 1, 1, 0),
        SOC_SINGLE_TLV("Bass Playback Volume", ADAU1373_BASS2, 2, 7, 0,
@@ -580,7 +579,7 @@ static SOC_ENUM_SINGLE_VIRT_DECL(adau1373_decimator_enum,
        adau1373_decimator_text);
 
 static const struct snd_kcontrol_new adau1373_decimator_mux =
-       SOC_DAPM_ENUM_VIRT("Decimator Mux", adau1373_decimator_enum);
+       SOC_DAPM_ENUM("Decimator Mux", adau1373_decimator_enum);
 
 static const struct snd_kcontrol_new adau1373_left_adc_mixer_controls[] = {
        SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_LADC_MIXER, 4, 1, 0),
@@ -694,7 +693,7 @@ static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
        SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1373_DIGMICCTRL, 0, 0),
        SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1373_DIGMICCTRL, 2, 0),
 
-       SND_SOC_DAPM_VIRT_MUX("Decimator Mux", SND_SOC_NOPM, 0, 0,
+       SND_SOC_DAPM_MUX("Decimator Mux", SND_SOC_NOPM, 0, 0,
                &adau1373_decimator_mux),
 
        SND_SOC_DAPM_SUPPLY("MICBIAS2", ADAU1373_PWDN_CTRL1, 5, 0, NULL, 0),
diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c
new file mode 100644 (file)
index 0000000..862796d
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "adau1761.h"
+
+static int adau1761_i2c_probe(struct i2c_client *client,
+       const struct i2c_device_id *id)
+{
+       struct regmap_config config;
+
+       config = adau1761_regmap_config;
+       config.val_bits = 8;
+       config.reg_bits = 16;
+
+       return adau1761_probe(&client->dev,
+               devm_regmap_init_i2c(client, &config),
+               id->driver_data, NULL);
+}
+
+static int adau1761_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static const struct i2c_device_id adau1761_i2c_ids[] = {
+       { "adau1361", ADAU1361 },
+       { "adau1461", ADAU1761 },
+       { "adau1761", ADAU1761 },
+       { "adau1961", ADAU1361 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids);
+
+static struct i2c_driver adau1761_i2c_driver = {
+       .driver = {
+               .name = "adau1761",
+               .owner = THIS_MODULE,
+       },
+       .probe = adau1761_i2c_probe,
+       .remove = adau1761_i2c_remove,
+       .id_table = adau1761_i2c_ids,
+};
+module_i2c_driver(adau1761_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC I2C driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c
new file mode 100644 (file)
index 0000000..cce2f11
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "adau1761.h"
+
+static void adau1761_spi_switch_mode(struct device *dev)
+{
+       struct spi_device *spi = to_spi_device(dev);
+
+       /*
+        * To get the device into SPI mode CLATCH has to be pulled low three
+        * times.  Do this by issuing three dummy reads.
+        */
+       spi_w8r8(spi, 0x00);
+       spi_w8r8(spi, 0x00);
+       spi_w8r8(spi, 0x00);
+}
+
+static int adau1761_spi_probe(struct spi_device *spi)
+{
+       const struct spi_device_id *id = spi_get_device_id(spi);
+       struct regmap_config config;
+
+       if (!id)
+               return -EINVAL;
+
+       config = adau1761_regmap_config;
+       config.val_bits = 8;
+       config.reg_bits = 24;
+       config.read_flag_mask = 0x1;
+
+       return adau1761_probe(&spi->dev,
+               devm_regmap_init_spi(spi, &config),
+               id->driver_data, adau1761_spi_switch_mode);
+}
+
+static int adau1761_spi_remove(struct spi_device *spi)
+{
+       snd_soc_unregister_codec(&spi->dev);
+       return 0;
+}
+
+static const struct spi_device_id adau1761_spi_id[] = {
+       { "adau1361", ADAU1361 },
+       { "adau1461", ADAU1761 },
+       { "adau1761", ADAU1761 },
+       { "adau1961", ADAU1361 },
+       { }
+};
+MODULE_DEVICE_TABLE(spi, adau1761_spi_id);
+
+static struct spi_driver adau1761_spi_driver = {
+       .driver = {
+               .name = "adau1761",
+               .owner = THIS_MODULE,
+       },
+       .probe = adau1761_spi_probe,
+       .remove = adau1761_spi_remove,
+       .id_table = adau1761_spi_id,
+};
+module_spi_driver(adau1761_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC SPI driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
new file mode 100644 (file)
index 0000000..848cab8
--- /dev/null
@@ -0,0 +1,803 @@
+/*
+ * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ *
+ * Copyright 2011-2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/platform_data/adau17x1.h>
+
+#include "adau17x1.h"
+#include "adau1761.h"
+
+#define ADAU1761_DIGMIC_JACKDETECT     0x4008
+#define ADAU1761_REC_MIXER_LEFT0       0x400a
+#define ADAU1761_REC_MIXER_LEFT1       0x400b
+#define ADAU1761_REC_MIXER_RIGHT0      0x400c
+#define ADAU1761_REC_MIXER_RIGHT1      0x400d
+#define ADAU1761_LEFT_DIFF_INPUT_VOL   0x400e
+#define ADAU1761_RIGHT_DIFF_INPUT_VOL  0x400f
+#define ADAU1761_PLAY_LR_MIXER_LEFT    0x4020
+#define ADAU1761_PLAY_MIXER_LEFT0      0x401c
+#define ADAU1761_PLAY_MIXER_LEFT1      0x401d
+#define ADAU1761_PLAY_MIXER_RIGHT0     0x401e
+#define ADAU1761_PLAY_MIXER_RIGHT1     0x401f
+#define ADAU1761_PLAY_LR_MIXER_RIGHT   0x4021
+#define ADAU1761_PLAY_MIXER_MONO       0x4022
+#define ADAU1761_PLAY_HP_LEFT_VOL      0x4023
+#define ADAU1761_PLAY_HP_RIGHT_VOL     0x4024
+#define ADAU1761_PLAY_LINE_LEFT_VOL    0x4025
+#define ADAU1761_PLAY_LINE_RIGHT_VOL   0x4026
+#define ADAU1761_PLAY_MONO_OUTPUT_VOL  0x4027
+#define ADAU1761_POP_CLICK_SUPPRESS    0x4028
+#define ADAU1761_JACK_DETECT_PIN       0x4031
+#define ADAU1761_DEJITTER              0x4036
+#define ADAU1761_CLK_ENABLE0           0x40f9
+#define ADAU1761_CLK_ENABLE1           0x40fa
+
+#define ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW  BIT(0)
+#define ADAU1761_DIGMIC_JACKDETECT_DIGMIC      BIT(5)
+
+#define ADAU1761_DIFF_INPUT_VOL_LDEN           BIT(0)
+
+#define ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP  BIT(0)
+#define ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE   BIT(1)
+
+#define ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP     BIT(0)
+
+#define ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP    BIT(0)
+
+#define ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP   BIT(0)
+
+
+#define ADAU1761_FIRMWARE "adau1761.bin"
+
+static const struct reg_default adau1761_reg_defaults[] = {
+       { ADAU1761_DEJITTER,                    0x03 },
+       { ADAU1761_DIGMIC_JACKDETECT,           0x00 },
+       { ADAU1761_REC_MIXER_LEFT0,             0x00 },
+       { ADAU1761_REC_MIXER_LEFT1,             0x00 },
+       { ADAU1761_REC_MIXER_RIGHT0,            0x00 },
+       { ADAU1761_REC_MIXER_RIGHT1,            0x00 },
+       { ADAU1761_LEFT_DIFF_INPUT_VOL,         0x00 },
+       { ADAU1761_RIGHT_DIFF_INPUT_VOL,        0x00 },
+       { ADAU1761_PLAY_LR_MIXER_LEFT,          0x00 },
+       { ADAU1761_PLAY_MIXER_LEFT0,            0x00 },
+       { ADAU1761_PLAY_MIXER_LEFT1,            0x00 },
+       { ADAU1761_PLAY_MIXER_RIGHT0,           0x00 },
+       { ADAU1761_PLAY_MIXER_RIGHT1,           0x00 },
+       { ADAU1761_PLAY_LR_MIXER_RIGHT,         0x00 },
+       { ADAU1761_PLAY_MIXER_MONO,             0x00 },
+       { ADAU1761_PLAY_HP_LEFT_VOL,            0x00 },
+       { ADAU1761_PLAY_HP_RIGHT_VOL,           0x00 },
+       { ADAU1761_PLAY_LINE_LEFT_VOL,          0x00 },
+       { ADAU1761_PLAY_LINE_RIGHT_VOL,         0x00 },
+       { ADAU1761_PLAY_MONO_OUTPUT_VOL,        0x00 },
+       { ADAU1761_POP_CLICK_SUPPRESS,          0x00 },
+       { ADAU1761_JACK_DETECT_PIN,             0x00 },
+       { ADAU1761_CLK_ENABLE0,                 0x00 },
+       { ADAU1761_CLK_ENABLE1,                 0x00 },
+       { ADAU17X1_CLOCK_CONTROL,               0x00 },
+       { ADAU17X1_PLL_CONTROL,                 0x00 },
+       { ADAU17X1_REC_POWER_MGMT,              0x00 },
+       { ADAU17X1_MICBIAS,                     0x00 },
+       { ADAU17X1_SERIAL_PORT0,                0x00 },
+       { ADAU17X1_SERIAL_PORT1,                0x00 },
+       { ADAU17X1_CONVERTER0,                  0x00 },
+       { ADAU17X1_CONVERTER1,                  0x00 },
+       { ADAU17X1_LEFT_INPUT_DIGITAL_VOL,      0x00 },
+       { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL,     0x00 },
+       { ADAU17X1_ADC_CONTROL,                 0x00 },
+       { ADAU17X1_PLAY_POWER_MGMT,             0x00 },
+       { ADAU17X1_DAC_CONTROL0,                0x00 },
+       { ADAU17X1_DAC_CONTROL1,                0x00 },
+       { ADAU17X1_DAC_CONTROL2,                0x00 },
+       { ADAU17X1_SERIAL_PORT_PAD,             0xaa },
+       { ADAU17X1_CONTROL_PORT_PAD0,           0xaa },
+       { ADAU17X1_CONTROL_PORT_PAD1,           0x00 },
+       { ADAU17X1_DSP_SAMPLING_RATE,           0x01 },
+       { ADAU17X1_SERIAL_INPUT_ROUTE,          0x00 },
+       { ADAU17X1_SERIAL_OUTPUT_ROUTE,         0x00 },
+       { ADAU17X1_DSP_ENABLE,                  0x00 },
+       { ADAU17X1_DSP_RUN,                     0x00 },
+       { ADAU17X1_SERIAL_SAMPLING_RATE,        0x00 },
+};
+
+static const DECLARE_TLV_DB_SCALE(adau1761_sing_in_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_diff_in_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_out_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(adau1761_sidetone_tlv, -1800, 300, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_boost_tlv, -600, 600, 1);
+static const DECLARE_TLV_DB_SCALE(adau1761_pga_boost_tlv, -2000, 2000, 1);
+
+static const unsigned int adau1761_bias_select_values[] = {
+       0, 2, 3,
+};
+
+static const char * const adau1761_bias_select_text[] = {
+       "Normal operation", "Enhanced performance", "Power saving",
+};
+
+static const char * const adau1761_bias_select_extreme_text[] = {
+       "Normal operation", "Extreme power saving", "Enhanced performance",
+       "Power saving",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1761_adc_bias_enum,
+               ADAU17X1_REC_POWER_MGMT, 3, adau1761_bias_select_extreme_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_hp_bias_enum,
+               ADAU17X1_PLAY_POWER_MGMT, 6, adau1761_bias_select_extreme_text);
+static SOC_ENUM_SINGLE_DECL(adau1761_dac_bias_enum,
+               ADAU17X1_PLAY_POWER_MGMT, 4, adau1761_bias_select_extreme_text);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_playback_bias_enum,
+               ADAU17X1_PLAY_POWER_MGMT, 2, 0x3, adau1761_bias_select_text,
+               adau1761_bias_select_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_capture_bias_enum,
+               ADAU17X1_REC_POWER_MGMT, 1, 0x3, adau1761_bias_select_text,
+               adau1761_bias_select_values);
+
+static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = {
+       SOC_SINGLE("Speaker Auto-mute Switch", ADAU1761_DIGMIC_JACKDETECT,
+               4, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1761_differential_mode_controls[] = {
+       SOC_DOUBLE_R_TLV("Capture Volume", ADAU1761_LEFT_DIFF_INPUT_VOL,
+               ADAU1761_RIGHT_DIFF_INPUT_VOL, 2, 0x3f, 0,
+               adau1761_diff_in_tlv),
+       SOC_DOUBLE_R("Capture Switch", ADAU1761_LEFT_DIFF_INPUT_VOL,
+               ADAU1761_RIGHT_DIFF_INPUT_VOL, 1, 1, 0),
+
+       SOC_DOUBLE_R_TLV("PGA Boost Capture Volume", ADAU1761_REC_MIXER_LEFT1,
+               ADAU1761_REC_MIXER_RIGHT1, 3, 2, 0, adau1761_pga_boost_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_single_mode_controls[] = {
+       SOC_SINGLE_TLV("Input 1 Capture Volume", ADAU1761_REC_MIXER_LEFT0,
+               4, 7, 0, adau1761_sing_in_tlv),
+       SOC_SINGLE_TLV("Input 2 Capture Volume", ADAU1761_REC_MIXER_LEFT0,
+               1, 7, 0, adau1761_sing_in_tlv),
+       SOC_SINGLE_TLV("Input 3 Capture Volume", ADAU1761_REC_MIXER_RIGHT0,
+               4, 7, 0, adau1761_sing_in_tlv),
+       SOC_SINGLE_TLV("Input 4 Capture Volume", ADAU1761_REC_MIXER_RIGHT0,
+               1, 7, 0, adau1761_sing_in_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_controls[] = {
+       SOC_DOUBLE_R_TLV("Aux Capture Volume", ADAU1761_REC_MIXER_LEFT1,
+               ADAU1761_REC_MIXER_RIGHT1, 0, 7, 0, adau1761_sing_in_tlv),
+
+       SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1761_PLAY_HP_LEFT_VOL,
+               ADAU1761_PLAY_HP_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv),
+       SOC_DOUBLE_R("Headphone Playback Switch", ADAU1761_PLAY_HP_LEFT_VOL,
+               ADAU1761_PLAY_HP_RIGHT_VOL, 1, 1, 0),
+       SOC_DOUBLE_R_TLV("Lineout Playback Volume", ADAU1761_PLAY_LINE_LEFT_VOL,
+               ADAU1761_PLAY_LINE_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv),
+       SOC_DOUBLE_R("Lineout Playback Switch", ADAU1761_PLAY_LINE_LEFT_VOL,
+               ADAU1761_PLAY_LINE_RIGHT_VOL, 1, 1, 0),
+
+       SOC_ENUM("ADC Bias", adau1761_adc_bias_enum),
+       SOC_ENUM("DAC Bias", adau1761_dac_bias_enum),
+       SOC_ENUM("Capture Bias", adau1761_capture_bias_enum),
+       SOC_ENUM("Playback Bias", adau1761_playback_bias_enum),
+       SOC_ENUM("Headphone Bias", adau1761_hp_bias_enum),
+};
+
+static const struct snd_kcontrol_new adau1761_mono_controls[] = {
+       SOC_SINGLE_TLV("Mono Playback Volume", ADAU1761_PLAY_MONO_OUTPUT_VOL,
+               2, 0x3f, 0, adau1761_out_tlv),
+       SOC_SINGLE("Mono Playback Switch", ADAU1761_PLAY_MONO_OUTPUT_VOL,
+               1, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1761_left_mixer_controls[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch",
+               ADAU1761_PLAY_MIXER_LEFT0, 5, 1, 0),
+       SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch",
+               ADAU1761_PLAY_MIXER_LEFT0, 6, 1, 0),
+       SOC_DAPM_SINGLE_TLV("Aux Bypass Volume",
+               ADAU1761_PLAY_MIXER_LEFT0, 1, 8, 0, adau1761_sidetone_tlv),
+       SOC_DAPM_SINGLE_TLV("Right Bypass Volume",
+               ADAU1761_PLAY_MIXER_LEFT1, 4, 8, 0, adau1761_sidetone_tlv),
+       SOC_DAPM_SINGLE_TLV("Left Bypass Volume",
+               ADAU1761_PLAY_MIXER_LEFT1, 0, 8, 0, adau1761_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_right_mixer_controls[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch",
+               ADAU1761_PLAY_MIXER_RIGHT0, 5, 1, 0),
+       SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch",
+               ADAU1761_PLAY_MIXER_RIGHT0, 6, 1, 0),
+       SOC_DAPM_SINGLE_TLV("Aux Bypass Volume",
+               ADAU1761_PLAY_MIXER_RIGHT0, 1, 8, 0, adau1761_sidetone_tlv),
+       SOC_DAPM_SINGLE_TLV("Right Bypass Volume",
+               ADAU1761_PLAY_MIXER_RIGHT1, 4, 8, 0, adau1761_sidetone_tlv),
+       SOC_DAPM_SINGLE_TLV("Left Bypass Volume",
+               ADAU1761_PLAY_MIXER_RIGHT1, 0, 8, 0, adau1761_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_left_lr_mixer_controls[] = {
+       SOC_DAPM_SINGLE_TLV("Left Volume",
+               ADAU1761_PLAY_LR_MIXER_LEFT, 1, 2, 0, adau1761_boost_tlv),
+       SOC_DAPM_SINGLE_TLV("Right Volume",
+               ADAU1761_PLAY_LR_MIXER_LEFT, 3, 2, 0, adau1761_boost_tlv),
+};
+
+static const struct snd_kcontrol_new adau1761_right_lr_mixer_controls[] = {
+       SOC_DAPM_SINGLE_TLV("Left Volume",
+               ADAU1761_PLAY_LR_MIXER_RIGHT, 1, 2, 0, adau1761_boost_tlv),
+       SOC_DAPM_SINGLE_TLV("Right Volume",
+               ADAU1761_PLAY_LR_MIXER_RIGHT, 3, 2, 0, adau1761_boost_tlv),
+};
+
+static const char * const adau1761_input_mux_text[] = {
+       "ADC", "DMIC",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1761_input_mux_enum,
+       ADAU17X1_ADC_CONTROL, 2, adau1761_input_mux_text);
+
+static const struct snd_kcontrol_new adau1761_input_mux_control =
+       SOC_DAPM_ENUM("Input Select", adau1761_input_mux_enum);
+
+static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+
+       /* After any power changes have been made the dejitter circuit
+        * has to be reinitialized. */
+       regmap_write(adau->regmap, ADAU1761_DEJITTER, 0);
+       if (!adau->master)
+               regmap_write(adau->regmap, ADAU1761_DEJITTER, 3);
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget adau1x61_dapm_widgets[] = {
+       SND_SOC_DAPM_MIXER("Left Input Mixer", ADAU1761_REC_MIXER_LEFT0, 0, 0,
+               NULL, 0),
+       SND_SOC_DAPM_MIXER("Right Input Mixer", ADAU1761_REC_MIXER_RIGHT0, 0, 0,
+               NULL, 0),
+
+       SOC_MIXER_ARRAY("Left Playback Mixer", ADAU1761_PLAY_MIXER_LEFT0,
+               0, 0, adau1761_left_mixer_controls),
+       SOC_MIXER_ARRAY("Right Playback Mixer", ADAU1761_PLAY_MIXER_RIGHT0,
+               0, 0, adau1761_right_mixer_controls),
+       SOC_MIXER_ARRAY("Left LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_LEFT,
+               0, 0, adau1761_left_lr_mixer_controls),
+       SOC_MIXER_ARRAY("Right LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_RIGHT,
+               0, 0, adau1761_right_lr_mixer_controls),
+
+       SND_SOC_DAPM_SUPPLY("Headphone", ADAU1761_PLAY_HP_LEFT_VOL,
+               0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY_S("SYSCLK", 2, SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_POST("Dejitter fixup", adau1761_dejitter_fixup),
+
+       SND_SOC_DAPM_INPUT("LAUX"),
+       SND_SOC_DAPM_INPUT("RAUX"),
+       SND_SOC_DAPM_INPUT("LINP"),
+       SND_SOC_DAPM_INPUT("LINN"),
+       SND_SOC_DAPM_INPUT("RINP"),
+       SND_SOC_DAPM_INPUT("RINN"),
+
+       SND_SOC_DAPM_OUTPUT("LOUT"),
+       SND_SOC_DAPM_OUTPUT("ROUT"),
+       SND_SOC_DAPM_OUTPUT("LHP"),
+       SND_SOC_DAPM_OUTPUT("RHP"),
+};
+
+static const struct snd_soc_dapm_widget adau1761_mono_dapm_widgets[] = {
+       SND_SOC_DAPM_MIXER("Mono Playback Mixer", ADAU1761_PLAY_MIXER_MONO,
+               0, 0, NULL, 0),
+
+       SND_SOC_DAPM_OUTPUT("MONOOUT"),
+};
+
+static const struct snd_soc_dapm_widget adau1761_capless_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY_S("Headphone VGND", 1, ADAU1761_PLAY_MIXER_MONO,
+               0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route adau1x61_dapm_routes[] = {
+       { "Left Input Mixer", NULL, "LINP" },
+       { "Left Input Mixer", NULL, "LINN" },
+       { "Left Input Mixer", NULL, "LAUX" },
+
+       { "Right Input Mixer", NULL, "RINP" },
+       { "Right Input Mixer", NULL, "RINN" },
+       { "Right Input Mixer", NULL, "RAUX" },
+
+       { "Left Playback Mixer", NULL, "Left Playback Enable"},
+       { "Right Playback Mixer", NULL, "Right Playback Enable"},
+       { "Left LR Playback Mixer", NULL, "Left Playback Enable"},
+       { "Right LR Playback Mixer", NULL, "Right Playback Enable"},
+
+       { "Left Playback Mixer", "Left DAC Switch", "Left DAC" },
+       { "Left Playback Mixer", "Right DAC Switch", "Right DAC" },
+
+       { "Right Playback Mixer", "Left DAC Switch", "Left DAC" },
+       { "Right Playback Mixer", "Right DAC Switch", "Right DAC" },
+
+       { "Left LR Playback Mixer", "Left Volume", "Left Playback Mixer" },
+       { "Left LR Playback Mixer", "Right Volume", "Right Playback Mixer" },
+
+       { "Right LR Playback Mixer", "Left Volume", "Left Playback Mixer" },
+       { "Right LR Playback Mixer", "Right Volume", "Right Playback Mixer" },
+
+       { "LHP", NULL, "Left Playback Mixer" },
+       { "RHP", NULL, "Right Playback Mixer" },
+
+       { "LHP", NULL, "Headphone" },
+       { "RHP", NULL, "Headphone" },
+
+       { "LOUT", NULL, "Left LR Playback Mixer" },
+       { "ROUT", NULL, "Right LR Playback Mixer" },
+
+       { "Left Playback Mixer", "Aux Bypass Volume", "LAUX" },
+       { "Left Playback Mixer", "Left Bypass Volume", "Left Input Mixer" },
+       { "Left Playback Mixer", "Right Bypass Volume", "Right Input Mixer" },
+       { "Right Playback Mixer", "Aux Bypass Volume", "RAUX" },
+       { "Right Playback Mixer", "Left Bypass Volume", "Left Input Mixer" },
+       { "Right Playback Mixer", "Right Bypass Volume", "Right Input Mixer" },
+};
+
+static const struct snd_soc_dapm_route adau1761_mono_dapm_routes[] = {
+       { "Mono Playback Mixer", NULL, "Left Playback Mixer" },
+       { "Mono Playback Mixer", NULL, "Right Playback Mixer" },
+
+       { "MONOOUT", NULL, "Mono Playback Mixer" },
+};
+
+static const struct snd_soc_dapm_route adau1761_capless_dapm_routes[] = {
+       { "Headphone", NULL, "Headphone VGND" },
+};
+
+static const struct snd_soc_dapm_widget adau1761_dmic_widgets[] = {
+       SND_SOC_DAPM_MUX("Left Decimator Mux", SND_SOC_NOPM, 0, 0,
+               &adau1761_input_mux_control),
+       SND_SOC_DAPM_MUX("Right Decimator Mux", SND_SOC_NOPM, 0, 0,
+               &adau1761_input_mux_control),
+
+       SND_SOC_DAPM_INPUT("DMIC"),
+};
+
+static const struct snd_soc_dapm_route adau1761_dmic_routes[] = {
+       { "Left Decimator Mux", "ADC", "Left Input Mixer" },
+       { "Left Decimator Mux", "DMIC", "DMIC" },
+       { "Right Decimator Mux", "ADC", "Right Input Mixer" },
+       { "Right Decimator Mux", "DMIC", "DMIC" },
+
+       { "Left Decimator", NULL, "Left Decimator Mux" },
+       { "Right Decimator", NULL, "Right Decimator Mux" },
+};
+
+static const struct snd_soc_dapm_route adau1761_no_dmic_routes[] = {
+       { "Left Decimator", NULL, "Left Input Mixer" },
+       { "Right Decimator", NULL, "Right Input Mixer" },
+};
+
+static const struct snd_soc_dapm_widget adau1761_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY("Serial Port Clock", ADAU1761_CLK_ENABLE0,
+               0, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Serial Input Routing Clock", ADAU1761_CLK_ENABLE0,
+               1, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Serial Output Routing Clock", ADAU1761_CLK_ENABLE0,
+               3, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("Decimator Resync Clock", ADAU1761_CLK_ENABLE0,
+               4, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Interpolator Resync Clock", ADAU1761_CLK_ENABLE0,
+               2, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("Slew Clock", ADAU1761_CLK_ENABLE0, 6, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY_S("Digital Clock 0", 1, ADAU1761_CLK_ENABLE1,
+               0, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("Digital Clock 1", 1, ADAU1761_CLK_ENABLE1,
+               1, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
+       { "Left Decimator", NULL, "Digital Clock 0", },
+       { "Right Decimator", NULL, "Digital Clock 0", },
+       { "Left DAC", NULL, "Digital Clock 0", },
+       { "Right DAC", NULL, "Digital Clock 0", },
+
+       { "AIFCLK", NULL, "Digital Clock 1" },
+
+       { "Playback", NULL, "Serial Port Clock" },
+       { "Capture", NULL, "Serial Port Clock" },
+       { "Playback", NULL, "Serial Input Routing Clock" },
+       { "Capture", NULL, "Serial Output Routing Clock" },
+
+       { "Left Decimator", NULL, "Decimator Resync Clock" },
+       { "Right Decimator", NULL, "Decimator Resync Clock" },
+       { "Left DAC", NULL, "Interpolator Resync Clock" },
+       { "Right DAC", NULL, "Interpolator Resync Clock" },
+
+       { "DSP", NULL, "Digital Clock 0" },
+
+       { "Slew Clock", NULL, "Digital Clock 0" },
+       { "Right Playback Mixer", NULL, "Slew Clock" },
+       { "Left Playback Mixer", NULL, "Slew Clock" },
+
+       { "Digital Clock 0", NULL, "SYSCLK" },
+       { "Digital Clock 1", NULL, "SYSCLK" },
+};
+
+static int adau1761_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+                       ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
+                       ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+               break;
+       case SND_SOC_BIAS_OFF:
+               regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+                       ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
+               break;
+
+       }
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+static enum adau1761_output_mode adau1761_get_lineout_mode(
+       struct snd_soc_codec *codec)
+{
+       struct adau1761_platform_data *pdata = codec->dev->platform_data;
+
+       if (pdata)
+               return pdata->lineout_mode;
+
+       return ADAU1761_OUTPUT_MODE_LINE;
+}
+
+static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
+{
+       struct adau1761_platform_data *pdata = codec->dev->platform_data;
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+       enum adau1761_digmic_jackdet_pin_mode mode;
+       unsigned int val = 0;
+       int ret;
+
+       if (pdata)
+               mode = pdata->digmic_jackdetect_pin_mode;
+       else
+               mode = ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE;
+
+       switch (mode) {
+       case ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT:
+               switch (pdata->jackdetect_debounce_time) {
+               case ADAU1761_JACKDETECT_DEBOUNCE_5MS:
+               case ADAU1761_JACKDETECT_DEBOUNCE_10MS:
+               case ADAU1761_JACKDETECT_DEBOUNCE_20MS:
+               case ADAU1761_JACKDETECT_DEBOUNCE_40MS:
+                       val |= pdata->jackdetect_debounce_time << 6;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               if (pdata->jackdetect_active_low)
+                       val |= ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW;
+
+               ret = snd_soc_add_codec_controls(codec,
+                       adau1761_jack_detect_controls,
+                       ARRAY_SIZE(adau1761_jack_detect_controls));
+               if (ret)
+                       return ret;
+       case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: /* fallthrough */
+               ret = snd_soc_dapm_add_routes(&codec->dapm,
+                       adau1761_no_dmic_routes,
+                       ARRAY_SIZE(adau1761_no_dmic_routes));
+               if (ret)
+                       return ret;
+               break;
+       case ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC:
+               ret = snd_soc_dapm_new_controls(&codec->dapm,
+                       adau1761_dmic_widgets,
+                       ARRAY_SIZE(adau1761_dmic_widgets));
+               if (ret)
+                       return ret;
+
+               ret = snd_soc_dapm_add_routes(&codec->dapm,
+                       adau1761_dmic_routes,
+                       ARRAY_SIZE(adau1761_dmic_routes));
+               if (ret)
+                       return ret;
+
+               val |= ADAU1761_DIGMIC_JACKDETECT_DIGMIC;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_write(adau->regmap, ADAU1761_DIGMIC_JACKDETECT, val);
+
+       return 0;
+}
+
+static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
+{
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+       struct adau1761_platform_data *pdata = codec->dev->platform_data;
+       enum adau1761_output_mode mode;
+       int ret;
+
+       if (pdata)
+               mode = pdata->headphone_mode;
+       else
+               mode = ADAU1761_OUTPUT_MODE_HEADPHONE;
+
+       switch (mode) {
+       case ADAU1761_OUTPUT_MODE_LINE:
+               break;
+       case ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS:
+               regmap_update_bits(adau->regmap, ADAU1761_PLAY_MONO_OUTPUT_VOL,
+                       ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP |
+                       ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE,
+                       ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP |
+                       ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE);
+               /* fallthrough */
+       case ADAU1761_OUTPUT_MODE_HEADPHONE:
+               regmap_update_bits(adau->regmap, ADAU1761_PLAY_HP_RIGHT_VOL,
+                       ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP,
+                       ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (mode == ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS) {
+               ret = snd_soc_dapm_new_controls(&codec->dapm,
+                       adau1761_capless_dapm_widgets,
+                       ARRAY_SIZE(adau1761_capless_dapm_widgets));
+               if (ret)
+                       return ret;
+               ret = snd_soc_dapm_add_routes(&codec->dapm,
+                       adau1761_capless_dapm_routes,
+                       ARRAY_SIZE(adau1761_capless_dapm_routes));
+       } else {
+               ret = snd_soc_add_codec_controls(codec, adau1761_mono_controls,
+                       ARRAY_SIZE(adau1761_mono_controls));
+               if (ret)
+                       return ret;
+               ret = snd_soc_dapm_new_controls(&codec->dapm,
+                       adau1761_mono_dapm_widgets,
+                       ARRAY_SIZE(adau1761_mono_dapm_widgets));
+               if (ret)
+                       return ret;
+               ret = snd_soc_dapm_add_routes(&codec->dapm,
+                       adau1761_mono_dapm_routes,
+                       ARRAY_SIZE(adau1761_mono_dapm_routes));
+       }
+
+       return ret;
+}
+
+static bool adau1761_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ADAU1761_DIGMIC_JACKDETECT:
+       case ADAU1761_REC_MIXER_LEFT0:
+       case ADAU1761_REC_MIXER_LEFT1:
+       case ADAU1761_REC_MIXER_RIGHT0:
+       case ADAU1761_REC_MIXER_RIGHT1:
+       case ADAU1761_LEFT_DIFF_INPUT_VOL:
+       case ADAU1761_RIGHT_DIFF_INPUT_VOL:
+       case ADAU1761_PLAY_LR_MIXER_LEFT:
+       case ADAU1761_PLAY_MIXER_LEFT0:
+       case ADAU1761_PLAY_MIXER_LEFT1:
+       case ADAU1761_PLAY_MIXER_RIGHT0:
+       case ADAU1761_PLAY_MIXER_RIGHT1:
+       case ADAU1761_PLAY_LR_MIXER_RIGHT:
+       case ADAU1761_PLAY_MIXER_MONO:
+       case ADAU1761_PLAY_HP_LEFT_VOL:
+       case ADAU1761_PLAY_HP_RIGHT_VOL:
+       case ADAU1761_PLAY_LINE_LEFT_VOL:
+       case ADAU1761_PLAY_LINE_RIGHT_VOL:
+       case ADAU1761_PLAY_MONO_OUTPUT_VOL:
+       case ADAU1761_POP_CLICK_SUPPRESS:
+       case ADAU1761_JACK_DETECT_PIN:
+       case ADAU1761_DEJITTER:
+       case ADAU1761_CLK_ENABLE0:
+       case ADAU1761_CLK_ENABLE1:
+               return true;
+       default:
+               break;
+       }
+
+       return adau17x1_readable_register(dev, reg);
+}
+
+static int adau1761_codec_probe(struct snd_soc_codec *codec)
+{
+       struct adau1761_platform_data *pdata = codec->dev->platform_data;
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       ret = adau17x1_add_widgets(codec);
+       if (ret < 0)
+               return ret;
+
+       if (pdata && pdata->input_differential) {
+               regmap_update_bits(adau->regmap, ADAU1761_LEFT_DIFF_INPUT_VOL,
+                       ADAU1761_DIFF_INPUT_VOL_LDEN,
+                       ADAU1761_DIFF_INPUT_VOL_LDEN);
+               regmap_update_bits(adau->regmap, ADAU1761_RIGHT_DIFF_INPUT_VOL,
+                       ADAU1761_DIFF_INPUT_VOL_LDEN,
+                       ADAU1761_DIFF_INPUT_VOL_LDEN);
+               ret = snd_soc_add_codec_controls(codec,
+                       adau1761_differential_mode_controls,
+                       ARRAY_SIZE(adau1761_differential_mode_controls));
+               if (ret)
+                       return ret;
+       } else {
+               ret = snd_soc_add_codec_controls(codec,
+                       adau1761_single_mode_controls,
+                       ARRAY_SIZE(adau1761_single_mode_controls));
+               if (ret)
+                       return ret;
+       }
+
+       switch (adau1761_get_lineout_mode(codec)) {
+       case ADAU1761_OUTPUT_MODE_LINE:
+               break;
+       case ADAU1761_OUTPUT_MODE_HEADPHONE:
+               regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_LEFT_VOL,
+                       ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP,
+                       ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP);
+               regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_RIGHT_VOL,
+                       ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP,
+                       ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = adau1761_setup_headphone_mode(codec);
+       if (ret)
+               return ret;
+
+       ret = adau1761_setup_digmic_jackdetect(codec);
+       if (ret)
+               return ret;
+
+       if (adau->type == ADAU1761) {
+               ret = snd_soc_dapm_new_controls(&codec->dapm,
+                       adau1761_dapm_widgets,
+                       ARRAY_SIZE(adau1761_dapm_widgets));
+               if (ret)
+                       return ret;
+
+               ret = snd_soc_dapm_add_routes(&codec->dapm,
+                       adau1761_dapm_routes,
+                       ARRAY_SIZE(adau1761_dapm_routes));
+               if (ret)
+                       return ret;
+
+               ret = adau17x1_load_firmware(adau, codec->dev,
+                       ADAU1761_FIRMWARE);
+               if (ret)
+                       dev_warn(codec->dev, "Failed to firmware\n");
+       }
+
+       ret = adau17x1_add_routes(codec);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static const struct snd_soc_codec_driver adau1761_codec_driver = {
+       .probe = adau1761_codec_probe,
+       .suspend = adau17x1_suspend,
+       .resume = adau17x1_resume,
+       .set_bias_level = adau1761_set_bias_level,
+
+       .controls = adau1761_controls,
+       .num_controls = ARRAY_SIZE(adau1761_controls),
+       .dapm_widgets = adau1x61_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(adau1x61_dapm_widgets),
+       .dapm_routes = adau1x61_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(adau1x61_dapm_routes),
+};
+
+#define ADAU1761_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+       SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver adau1361_dai_driver = {
+       .name = "adau-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 4,
+               .rates = SNDRV_PCM_RATE_8000_96000,
+               .formats = ADAU1761_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 2,
+               .channels_max = 4,
+               .rates = SNDRV_PCM_RATE_8000_96000,
+               .formats = ADAU1761_FORMATS,
+       },
+       .ops = &adau17x1_dai_ops,
+};
+
+static struct snd_soc_dai_driver adau1761_dai_driver = {
+       .name = "adau-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 8,
+               .rates = SNDRV_PCM_RATE_8000_96000,
+               .formats = ADAU1761_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 2,
+               .channels_max = 8,
+               .rates = SNDRV_PCM_RATE_8000_96000,
+               .formats = ADAU1761_FORMATS,
+       },
+       .ops = &adau17x1_dai_ops,
+};
+
+int adau1761_probe(struct device *dev, struct regmap *regmap,
+       enum adau17x1_type type, void (*switch_mode)(struct device *dev))
+{
+       struct snd_soc_dai_driver *dai_drv;
+       int ret;
+
+       ret = adau17x1_probe(dev, regmap, type, switch_mode);
+       if (ret)
+               return ret;
+
+       if (type == ADAU1361)
+               dai_drv = &adau1361_dai_driver;
+       else
+               dai_drv = &adau1761_dai_driver;
+
+       return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);
+}
+EXPORT_SYMBOL_GPL(adau1761_probe);
+
+const struct regmap_config adau1761_regmap_config = {
+       .val_bits = 8,
+       .reg_bits = 16,
+       .max_register = 0x40fa,
+       .reg_defaults = adau1761_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(adau1761_reg_defaults),
+       .readable_reg = adau1761_readable_register,
+       .volatile_reg = adau17x1_volatile_register,
+       .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(adau1761_regmap_config);
+
+MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1761.h b/sound/soc/codecs/adau1761.h
new file mode 100644 (file)
index 0000000..a9e0d28
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * ADAU1361/ADAU1461/ADAU1761/ADAU1961 driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef __SOUND_SOC_CODECS_ADAU1761_H__
+#define __SOUND_SOC_CODECS_ADAU1761_H__
+
+#include <linux/regmap.h>
+#include "adau17x1.h"
+
+struct device;
+
+int adau1761_probe(struct device *dev, struct regmap *regmap,
+       enum adau17x1_type type, void (*switch_mode)(struct device *dev));
+
+extern const struct regmap_config adau1761_regmap_config;
+
+#endif
diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c
new file mode 100644 (file)
index 0000000..2ce4362
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Driver for ADAU1381/ADAU1781 CODEC
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "adau1781.h"
+
+static int adau1781_i2c_probe(struct i2c_client *client,
+       const struct i2c_device_id *id)
+{
+       struct regmap_config config;
+
+       config = adau1781_regmap_config;
+       config.val_bits = 8;
+       config.reg_bits = 16;
+
+       return adau1781_probe(&client->dev,
+               devm_regmap_init_i2c(client, &config),
+               id->driver_data, NULL);
+}
+
+static int adau1781_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static const struct i2c_device_id adau1781_i2c_ids[] = {
+       { "adau1381", ADAU1381 },
+       { "adau1781", ADAU1781 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids);
+
+static struct i2c_driver adau1781_i2c_driver = {
+       .driver = {
+               .name = "adau1781",
+               .owner = THIS_MODULE,
+       },
+       .probe = adau1781_i2c_probe,
+       .remove = adau1781_i2c_remove,
+       .id_table = adau1781_i2c_ids,
+};
+module_i2c_driver(adau1781_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 CODEC I2C driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c
new file mode 100644 (file)
index 0000000..1946867
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Driver for ADAU1381/ADAU1781 CODEC
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "adau1781.h"
+
+static void adau1781_spi_switch_mode(struct device *dev)
+{
+       struct spi_device *spi = to_spi_device(dev);
+
+       /*
+        * To get the device into SPI mode CLATCH has to be pulled low three
+        * times.  Do this by issuing three dummy reads.
+        */
+       spi_w8r8(spi, 0x00);
+       spi_w8r8(spi, 0x00);
+       spi_w8r8(spi, 0x00);
+}
+
+static int adau1781_spi_probe(struct spi_device *spi)
+{
+       const struct spi_device_id *id = spi_get_device_id(spi);
+       struct regmap_config config;
+
+       if (!id)
+               return -EINVAL;
+
+       config = adau1781_regmap_config;
+       config.val_bits = 8;
+       config.reg_bits = 24;
+       config.read_flag_mask = 0x1;
+
+       return adau1781_probe(&spi->dev,
+               devm_regmap_init_spi(spi, &config),
+               id->driver_data, adau1781_spi_switch_mode);
+}
+
+static int adau1781_spi_remove(struct spi_device *spi)
+{
+       snd_soc_unregister_codec(&spi->dev);
+       return 0;
+}
+
+static const struct spi_device_id adau1781_spi_id[] = {
+       { "adau1381", ADAU1381 },
+       { "adau1781", ADAU1781 },
+       { }
+};
+MODULE_DEVICE_TABLE(spi, adau1781_spi_id);
+
+static struct spi_driver adau1781_spi_driver = {
+       .driver = {
+               .name = "adau1781",
+               .owner = THIS_MODULE,
+       },
+       .probe = adau1781_spi_probe,
+       .remove = adau1781_spi_remove,
+       .id_table = adau1781_spi_id,
+};
+module_spi_driver(adau1781_spi_driver);
+
+MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 CODEC SPI driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
new file mode 100644 (file)
index 0000000..045a614
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+ * Driver for ADAU1781/ADAU1781 codec
+ *
+ * Copyright 2011-2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/platform_data/adau17x1.h>
+
+#include "adau17x1.h"
+#include "adau1781.h"
+
+#define ADAU1781_DMIC_BEEP_CTRL                0x4008
+#define ADAU1781_LEFT_PGA              0x400e
+#define ADAU1781_RIGHT_PGA             0x400f
+#define ADAU1781_LEFT_PLAYBACK_MIXER   0x401c
+#define ADAU1781_RIGHT_PLAYBACK_MIXER  0x401e
+#define ADAU1781_MONO_PLAYBACK_MIXER   0x401f
+#define ADAU1781_LEFT_LINEOUT          0x4025
+#define ADAU1781_RIGHT_LINEOUT         0x4026
+#define ADAU1781_SPEAKER               0x4027
+#define ADAU1781_BEEP_ZC               0x4028
+#define ADAU1781_DEJITTER              0x4032
+#define ADAU1781_DIG_PWDN0             0x4080
+#define ADAU1781_DIG_PWDN1             0x4081
+
+#define ADAU1781_INPUT_DIFFERNTIAL BIT(3)
+
+#define ADAU1381_FIRMWARE "adau1381.bin"
+#define ADAU1781_FIRMWARE "adau1781.bin"
+
+static const struct reg_default adau1781_reg_defaults[] = {
+       { ADAU1781_DMIC_BEEP_CTRL,              0x00 },
+       { ADAU1781_LEFT_PGA,                    0xc7 },
+       { ADAU1781_RIGHT_PGA,                   0xc7 },
+       { ADAU1781_LEFT_PLAYBACK_MIXER,         0x00 },
+       { ADAU1781_RIGHT_PLAYBACK_MIXER,        0x00 },
+       { ADAU1781_MONO_PLAYBACK_MIXER,         0x00 },
+       { ADAU1781_LEFT_LINEOUT,                0x00 },
+       { ADAU1781_RIGHT_LINEOUT,               0x00 },
+       { ADAU1781_SPEAKER,                     0x00 },
+       { ADAU1781_BEEP_ZC,                     0x19 },
+       { ADAU1781_DEJITTER,                    0x60 },
+       { ADAU1781_DIG_PWDN1,                   0x0c },
+       { ADAU1781_DIG_PWDN1,                   0x00 },
+       { ADAU17X1_CLOCK_CONTROL,               0x00 },
+       { ADAU17X1_PLL_CONTROL,                 0x00 },
+       { ADAU17X1_REC_POWER_MGMT,              0x00 },
+       { ADAU17X1_MICBIAS,                     0x04 },
+       { ADAU17X1_SERIAL_PORT0,                0x00 },
+       { ADAU17X1_SERIAL_PORT1,                0x00 },
+       { ADAU17X1_CONVERTER0,                  0x00 },
+       { ADAU17X1_CONVERTER1,                  0x00 },
+       { ADAU17X1_LEFT_INPUT_DIGITAL_VOL,      0x00 },
+       { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL,     0x00 },
+       { ADAU17X1_ADC_CONTROL,                 0x00 },
+       { ADAU17X1_PLAY_POWER_MGMT,             0x00 },
+       { ADAU17X1_DAC_CONTROL0,                0x00 },
+       { ADAU17X1_DAC_CONTROL1,                0x00 },
+       { ADAU17X1_DAC_CONTROL2,                0x00 },
+       { ADAU17X1_SERIAL_PORT_PAD,             0x00 },
+       { ADAU17X1_CONTROL_PORT_PAD0,           0x00 },
+       { ADAU17X1_CONTROL_PORT_PAD1,           0x00 },
+       { ADAU17X1_DSP_SAMPLING_RATE,           0x01 },
+       { ADAU17X1_SERIAL_INPUT_ROUTE,          0x00 },
+       { ADAU17X1_SERIAL_OUTPUT_ROUTE,         0x00 },
+       { ADAU17X1_DSP_ENABLE,                  0x00 },
+       { ADAU17X1_DSP_RUN,                     0x00 },
+       { ADAU17X1_SERIAL_SAMPLING_RATE,        0x00 },
+};
+
+static const DECLARE_TLV_DB_SCALE(adau1781_speaker_tlv, 0, 200, 0);
+
+static const DECLARE_TLV_DB_RANGE(adau1781_pga_tlv,
+       0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
+       2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
+       4, 4, TLV_DB_SCALE_ITEM(1700, 0, 0),
+       5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)
+);
+
+static const DECLARE_TLV_DB_RANGE(adau1781_beep_tlv,
+       0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
+       2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0),
+       4, 4, TLV_DB_SCALE_ITEM(-2300, 0, 0),
+       5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(adau1781_sidetone_tlv, -1800, 300, 1);
+
+static const char * const adau1781_speaker_bias_select_text[] = {
+       "Normal operation", "Power saving", "Enhanced performance",
+};
+
+static const char * const adau1781_bias_select_text[] = {
+       "Normal operation", "Extreme power saving", "Power saving",
+       "Enhanced performance",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau1781_adc_bias_enum,
+               ADAU17X1_REC_POWER_MGMT, 3, adau1781_bias_select_text);
+static SOC_ENUM_SINGLE_DECL(adau1781_speaker_bias_enum,
+               ADAU17X1_PLAY_POWER_MGMT, 6, adau1781_speaker_bias_select_text);
+static SOC_ENUM_SINGLE_DECL(adau1781_dac_bias_enum,
+               ADAU17X1_PLAY_POWER_MGMT, 4, adau1781_bias_select_text);
+static SOC_ENUM_SINGLE_DECL(adau1781_playback_bias_enum,
+               ADAU17X1_PLAY_POWER_MGMT, 2, adau1781_bias_select_text);
+static SOC_ENUM_SINGLE_DECL(adau1781_capture_bias_enum,
+               ADAU17X1_REC_POWER_MGMT, 1, adau1781_bias_select_text);
+
+static const struct snd_kcontrol_new adau1781_controls[] = {
+       SOC_SINGLE_TLV("Beep Capture Volume", ADAU1781_DMIC_BEEP_CTRL, 0, 7, 0,
+               adau1781_beep_tlv),
+       SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAU1781_LEFT_PGA,
+               ADAU1781_RIGHT_PGA, 5, 7, 0, adau1781_pga_tlv),
+       SOC_DOUBLE_R("PGA Capture Switch", ADAU1781_LEFT_PGA,
+               ADAU1781_RIGHT_PGA, 1, 1, 0),
+
+       SOC_DOUBLE_R("Lineout Playback Switch", ADAU1781_LEFT_LINEOUT,
+               ADAU1781_RIGHT_LINEOUT, 1, 1, 0),
+       SOC_SINGLE("Beep ZC Switch", ADAU1781_BEEP_ZC, 0, 1, 0),
+
+       SOC_SINGLE("Mono Playback Switch", ADAU1781_MONO_PLAYBACK_MIXER,
+               0, 1, 0),
+       SOC_SINGLE_TLV("Mono Playback Volume", ADAU1781_SPEAKER, 6, 3, 0,
+               adau1781_speaker_tlv),
+
+       SOC_ENUM("ADC Bias", adau1781_adc_bias_enum),
+       SOC_ENUM("DAC Bias", adau1781_dac_bias_enum),
+       SOC_ENUM("Capture Bias", adau1781_capture_bias_enum),
+       SOC_ENUM("Playback Bias", adau1781_playback_bias_enum),
+       SOC_ENUM("Speaker Bias", adau1781_speaker_bias_enum),
+};
+
+static const struct snd_kcontrol_new adau1781_beep_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Beep Capture Switch", ADAU1781_DMIC_BEEP_CTRL,
+               3, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1781_left_mixer_controls[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+               ADAU1781_LEFT_PLAYBACK_MIXER, 5, 1, 0),
+       SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
+               ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1781_right_mixer_controls[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+               ADAU1781_RIGHT_PLAYBACK_MIXER, 6, 1, 0),
+       SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
+               ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Left Switch",
+               ADAU1781_MONO_PLAYBACK_MIXER, 7, 1, 0),
+       SOC_DAPM_SINGLE_AUTODISABLE("Right Switch",
+                ADAU1781_MONO_PLAYBACK_MIXER, 6, 1, 0),
+       SOC_DAPM_SINGLE_TLV("Beep Playback Volume",
+               ADAU1781_MONO_PLAYBACK_MIXER, 2, 8, 0, adau1781_sidetone_tlv),
+};
+
+static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+       /* After any power changes have been made the dejitter circuit
+        * has to be reinitialized. */
+       regmap_write(adau->regmap, ADAU1781_DEJITTER, 0);
+       if (!adau->master)
+               regmap_write(adau->regmap, ADAU1781_DEJITTER, 5);
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget adau1781_dapm_widgets[] = {
+       SND_SOC_DAPM_PGA("Left PGA", ADAU1781_LEFT_PGA, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Right PGA", ADAU1781_RIGHT_PGA, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_OUT_DRV("Speaker", ADAU1781_SPEAKER, 0, 0, NULL, 0),
+
+       SOC_MIXER_NAMED_CTL_ARRAY("Beep Mixer", ADAU17X1_MICBIAS, 4, 0,
+               adau1781_beep_mixer_controls),
+
+       SOC_MIXER_ARRAY("Left Lineout Mixer", SND_SOC_NOPM, 0, 0,
+               adau1781_left_mixer_controls),
+       SOC_MIXER_ARRAY("Right Lineout Mixer", SND_SOC_NOPM, 0, 0,
+               adau1781_right_mixer_controls),
+       SOC_MIXER_ARRAY("Mono Mixer", SND_SOC_NOPM, 0, 0,
+               adau1781_mono_mixer_controls),
+
+       SND_SOC_DAPM_SUPPLY("Serial Input Routing", ADAU1781_DIG_PWDN0,
+               2, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Serial Output Routing", ADAU1781_DIG_PWDN0,
+               3, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Clock Domain Transfer", ADAU1781_DIG_PWDN0,
+               5, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Serial Ports", ADAU1781_DIG_PWDN0, 4, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC Engine", ADAU1781_DIG_PWDN0, 7, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DAC Engine", ADAU1781_DIG_PWDN1, 0, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Digital Mic", ADAU1781_DIG_PWDN1, 1, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("Sound Engine", ADAU1781_DIG_PWDN0, 0, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, ADAU1781_DIG_PWDN0, 1, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("Zero Crossing Detector", ADAU1781_DIG_PWDN1, 2, 0,
+               NULL, 0),
+
+       SND_SOC_DAPM_POST("Dejitter fixup", adau1781_dejitter_fixup),
+
+       SND_SOC_DAPM_INPUT("BEEP"),
+
+       SND_SOC_DAPM_OUTPUT("AOUTL"),
+       SND_SOC_DAPM_OUTPUT("AOUTR"),
+       SND_SOC_DAPM_OUTPUT("SP"),
+       SND_SOC_DAPM_INPUT("LMIC"),
+       SND_SOC_DAPM_INPUT("RMIC"),
+};
+
+static const struct snd_soc_dapm_route adau1781_dapm_routes[] = {
+       { "Left Lineout Mixer", NULL, "Left Playback Enable" },
+       { "Right Lineout Mixer", NULL, "Right Playback Enable" },
+
+       { "Left Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
+       { "Left Lineout Mixer", "Switch", "Left DAC" },
+
+       { "Right Lineout Mixer", "Beep Playback Volume", "Beep Mixer" },
+       { "Right Lineout Mixer", "Switch", "Right DAC" },
+
+       { "Mono Mixer", "Beep Playback Volume", "Beep Mixer" },
+       { "Mono Mixer", "Right Switch", "Right DAC" },
+       { "Mono Mixer", "Left Switch", "Left DAC" },
+       { "Speaker", NULL, "Mono Mixer" },
+
+       { "Mono Mixer", NULL, "SYSCLK" },
+       { "Left Lineout Mixer", NULL, "SYSCLK" },
+       { "Left Lineout Mixer", NULL, "SYSCLK" },
+
+       { "Beep Mixer", "Beep Capture Switch", "BEEP" },
+       { "Beep Mixer", NULL, "Zero Crossing Detector" },
+
+       { "Left DAC", NULL, "DAC Engine" },
+       { "Right DAC", NULL, "DAC Engine" },
+
+       { "Sound Engine", NULL, "SYSCLK" },
+       { "DSP", NULL, "Sound Engine" },
+
+       { "Left Decimator", NULL, "ADC Engine" },
+       { "Right Decimator", NULL, "ADC Engine" },
+
+       { "AIFCLK", NULL, "SYSCLK" },
+
+       { "Playback", NULL, "Serial Input Routing" },
+       { "Playback", NULL, "Serial Ports" },
+       { "Playback", NULL, "Clock Domain Transfer" },
+       { "Capture", NULL, "Serial Output Routing" },
+       { "Capture", NULL, "Serial Ports" },
+       { "Capture", NULL, "Clock Domain Transfer" },
+
+       { "AOUTL", NULL, "Left Lineout Mixer" },
+       { "AOUTR", NULL, "Right Lineout Mixer" },
+       { "SP", NULL, "Speaker" },
+};
+
+static const struct snd_soc_dapm_route adau1781_adc_dapm_routes[] = {
+       { "Left PGA", NULL, "LMIC" },
+       { "Right PGA", NULL, "RMIC" },
+
+       { "Left Decimator", NULL, "Left PGA" },
+       { "Right Decimator", NULL, "Right PGA" },
+};
+
+static const char * const adau1781_dmic_select_text[] = {
+       "DMIC1", "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_VIRT_DECL(adau1781_dmic_select_enum,
+       adau1781_dmic_select_text);
+
+static const struct snd_kcontrol_new adau1781_dmic_mux =
+       SOC_DAPM_ENUM("DMIC Select", adau1781_dmic_select_enum);
+
+static const struct snd_soc_dapm_widget adau1781_dmic_dapm_widgets[] = {
+       SND_SOC_DAPM_MUX("DMIC Select", SND_SOC_NOPM, 0, 0, &adau1781_dmic_mux),
+
+       SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1781_DMIC_BEEP_CTRL, 4, 0),
+       SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1781_DMIC_BEEP_CTRL, 5, 0),
+};
+
+static const struct snd_soc_dapm_route adau1781_dmic_dapm_routes[] = {
+       { "DMIC1", NULL, "LMIC" },
+       { "DMIC2", NULL, "RMIC" },
+
+       { "DMIC1", NULL, "Digital Mic" },
+       { "DMIC2", NULL, "Digital Mic" },
+
+       { "DMIC Select", "DMIC1", "DMIC1" },
+       { "DMIC Select", "DMIC2", "DMIC2" },
+
+       { "Left Decimator", NULL, "DMIC Select" },
+       { "Right Decimator", NULL, "DMIC Select" },
+};
+
+static int adau1781_set_bias_level(struct snd_soc_codec *codec,
+               enum snd_soc_bias_level level)
+{
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+                       ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
+                       ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+
+               /* Precharge */
+               regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0x8, 0x8);
+               break;
+       case SND_SOC_BIAS_OFF:
+               regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0xc, 0x0);
+               regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+                       ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
+               break;
+       }
+
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+static bool adau1781_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ADAU1781_DMIC_BEEP_CTRL:
+       case ADAU1781_LEFT_PGA:
+       case ADAU1781_RIGHT_PGA:
+       case ADAU1781_LEFT_PLAYBACK_MIXER:
+       case ADAU1781_RIGHT_PLAYBACK_MIXER:
+       case ADAU1781_MONO_PLAYBACK_MIXER:
+       case ADAU1781_LEFT_LINEOUT:
+       case ADAU1781_RIGHT_LINEOUT:
+       case ADAU1781_SPEAKER:
+       case ADAU1781_BEEP_ZC:
+       case ADAU1781_DEJITTER:
+       case ADAU1781_DIG_PWDN0:
+       case ADAU1781_DIG_PWDN1:
+               return true;
+       default:
+               break;
+       }
+
+       return adau17x1_readable_register(dev, reg);
+}
+
+static int adau1781_set_input_mode(struct adau *adau, unsigned int reg,
+       bool differential)
+{
+       unsigned int val;
+
+       if (differential)
+               val = ADAU1781_INPUT_DIFFERNTIAL;
+       else
+               val = 0;
+
+       return regmap_update_bits(adau->regmap, reg,
+               ADAU1781_INPUT_DIFFERNTIAL, val);
+}
+
+static int adau1781_codec_probe(struct snd_soc_codec *codec)
+{
+       struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev);
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+       const char *firmware;
+       int ret;
+
+       ret = adau17x1_add_widgets(codec);
+       if (ret)
+               return ret;
+
+       if (pdata) {
+               ret = adau1781_set_input_mode(adau, ADAU1781_LEFT_PGA,
+                       pdata->left_input_differential);
+               if (ret)
+                       return ret;
+               ret = adau1781_set_input_mode(adau, ADAU1781_RIGHT_PGA,
+                       pdata->right_input_differential);
+               if (ret)
+                       return ret;
+       }
+
+       if (pdata && pdata->use_dmic) {
+               ret = snd_soc_dapm_new_controls(&codec->dapm,
+                       adau1781_dmic_dapm_widgets,
+                       ARRAY_SIZE(adau1781_dmic_dapm_widgets));
+               if (ret)
+                       return ret;
+               ret = snd_soc_dapm_add_routes(&codec->dapm,
+                       adau1781_dmic_dapm_routes,
+                       ARRAY_SIZE(adau1781_dmic_dapm_routes));
+               if (ret)
+                       return ret;
+       } else {
+               ret = snd_soc_dapm_add_routes(&codec->dapm,
+                       adau1781_adc_dapm_routes,
+                       ARRAY_SIZE(adau1781_adc_dapm_routes));
+               if (ret)
+                       return ret;
+       }
+
+       switch (adau->type) {
+       case ADAU1381:
+               firmware = ADAU1381_FIRMWARE;
+               break;
+       case ADAU1781:
+               firmware = ADAU1781_FIRMWARE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = adau17x1_add_routes(codec);
+       if (ret < 0)
+               return ret;
+
+       ret = adau17x1_load_firmware(adau, codec->dev, firmware);
+       if (ret)
+               dev_warn(codec->dev, "Failed to load firmware\n");
+
+       return 0;
+}
+
+static const struct snd_soc_codec_driver adau1781_codec_driver = {
+       .probe = adau1781_codec_probe,
+       .suspend = adau17x1_suspend,
+       .resume = adau17x1_resume,
+       .set_bias_level = adau1781_set_bias_level,
+
+       .controls = adau1781_controls,
+       .num_controls = ARRAY_SIZE(adau1781_controls),
+       .dapm_widgets = adau1781_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(adau1781_dapm_widgets),
+       .dapm_routes = adau1781_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(adau1781_dapm_routes),
+};
+
+#define ADAU1781_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+       SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver adau1781_dai_driver = {
+       .name = "adau-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 8,
+               .rates = SNDRV_PCM_RATE_8000_96000,
+               .formats = ADAU1781_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 2,
+               .channels_max = 8,
+               .rates = SNDRV_PCM_RATE_8000_96000,
+               .formats = ADAU1781_FORMATS,
+       },
+       .ops = &adau17x1_dai_ops,
+};
+
+const struct regmap_config adau1781_regmap_config = {
+       .val_bits               = 8,
+       .reg_bits               = 16,
+       .max_register           = 0x40f8,
+       .reg_defaults           = adau1781_reg_defaults,
+       .num_reg_defaults       = ARRAY_SIZE(adau1781_reg_defaults),
+       .readable_reg           = adau1781_readable_register,
+       .volatile_reg           = adau17x1_volatile_register,
+       .cache_type             = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(adau1781_regmap_config);
+
+int adau1781_probe(struct device *dev, struct regmap *regmap,
+       enum adau17x1_type type, void (*switch_mode)(struct device *dev))
+{
+       int ret;
+
+       ret = adau17x1_probe(dev, regmap, type, switch_mode);
+       if (ret)
+               return ret;
+
+       return snd_soc_register_codec(dev, &adau1781_codec_driver,
+               &adau1781_dai_driver, 1);
+}
+EXPORT_SYMBOL_GPL(adau1781_probe);
+
+MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1781.h b/sound/soc/codecs/adau1781.h
new file mode 100644 (file)
index 0000000..2b96e0a
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * ADAU1381/ADAU1781 driver
+ *
+ * Copyright 2014 Analog Devices Inc.
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef __SOUND_SOC_CODECS_ADAU1781_H__
+#define __SOUND_SOC_CODECS_ADAU1781_H__
+
+#include <linux/regmap.h>
+#include "adau17x1.h"
+
+struct device;
+
+int adau1781_probe(struct device *dev, struct regmap *regmap,
+       enum adau17x1_type type, void (*switch_mode)(struct device *dev));
+
+extern const struct regmap_config adau1781_regmap_config;
+
+#endif
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
new file mode 100644 (file)
index 0000000..2961fae
--- /dev/null
@@ -0,0 +1,866 @@
+/*
+ * Common code for ADAU1X61 and ADAU1X81 codecs
+ *
+ * Copyright 2011-2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/gcd.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include "sigmadsp.h"
+#include "adau17x1.h"
+
+static const char * const adau17x1_capture_mixer_boost_text[] = {
+       "Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau17x1_capture_boost_enum,
+       ADAU17X1_REC_POWER_MGMT, 5, adau17x1_capture_mixer_boost_text);
+
+static const char * const adau17x1_mic_bias_mode_text[] = {
+       "Normal operation", "High performance",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau17x1_mic_bias_mode_enum,
+       ADAU17X1_MICBIAS, 3, adau17x1_mic_bias_mode_text);
+
+static const DECLARE_TLV_DB_MINMAX(adau17x1_digital_tlv, -9563, 0);
+
+static const struct snd_kcontrol_new adau17x1_controls[] = {
+       SOC_DOUBLE_R_TLV("Digital Capture Volume",
+               ADAU17X1_LEFT_INPUT_DIGITAL_VOL,
+               ADAU17X1_RIGHT_INPUT_DIGITAL_VOL,
+               0, 0xff, 1, adau17x1_digital_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume", ADAU17X1_DAC_CONTROL1,
+               ADAU17X1_DAC_CONTROL2, 0, 0xff, 1, adau17x1_digital_tlv),
+
+       SOC_SINGLE("ADC High Pass Filter Switch", ADAU17X1_ADC_CONTROL,
+               5, 1, 0),
+       SOC_SINGLE("Playback De-emphasis Switch", ADAU17X1_DAC_CONTROL0,
+               2, 1, 0),
+
+       SOC_ENUM("Capture Boost", adau17x1_capture_boost_enum),
+
+       SOC_ENUM("Mic Bias Mode", adau17x1_mic_bias_mode_enum),
+};
+
+static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+       int ret;
+
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               adau->pll_regs[5] = 1;
+       } else {
+               adau->pll_regs[5] = 0;
+               /* Bypass the PLL when disabled, otherwise registers will become
+                * inaccessible. */
+               regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+                       ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL, 0);
+       }
+
+       /* The PLL register is 6 bytes long and can only be written at once. */
+       ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
+                       adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
+
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               mdelay(5);
+               regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
+                       ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL,
+                       ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL);
+       }
+
+       return 0;
+}
+
+static const char * const adau17x1_mono_stereo_text[] = {
+       "Stereo",
+       "Mono Left Channel (L+R)",
+       "Mono Right Channel (L+R)",
+       "Mono (L+R)",
+};
+
+static SOC_ENUM_SINGLE_DECL(adau17x1_dac_mode_enum,
+       ADAU17X1_DAC_CONTROL0, 6, adau17x1_mono_stereo_text);
+
+static const struct snd_kcontrol_new adau17x1_dac_mode_mux =
+       SOC_DAPM_ENUM("DAC Mono-Stereo-Mode", adau17x1_dac_mode_enum);
+
+static const struct snd_soc_dapm_widget adau17x1_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY_S("PLL", 3, SND_SOC_NOPM, 0, 0, adau17x1_pll_event,
+               SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_SUPPLY("AIFCLK", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("MICBIAS", ADAU17X1_MICBIAS, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("Left Playback Enable", ADAU17X1_PLAY_POWER_MGMT,
+               0, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Right Playback Enable", ADAU17X1_PLAY_POWER_MGMT,
+               1, 0, NULL, 0),
+
+       SND_SOC_DAPM_MUX("Left DAC Mode Mux", SND_SOC_NOPM, 0, 0,
+               &adau17x1_dac_mode_mux),
+       SND_SOC_DAPM_MUX("Right DAC Mode Mux", SND_SOC_NOPM, 0, 0,
+               &adau17x1_dac_mode_mux),
+
+       SND_SOC_DAPM_ADC("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0),
+       SND_SOC_DAPM_ADC("Right Decimator", NULL, ADAU17X1_ADC_CONTROL, 1, 0),
+       SND_SOC_DAPM_DAC("Left DAC", NULL, ADAU17X1_DAC_CONTROL0, 0, 0),
+       SND_SOC_DAPM_DAC("Right DAC", NULL, ADAU17X1_DAC_CONTROL0, 1, 0),
+};
+
+static const struct snd_soc_dapm_route adau17x1_dapm_routes[] = {
+       { "Left Decimator", NULL, "SYSCLK" },
+       { "Right Decimator", NULL, "SYSCLK" },
+       { "Left DAC", NULL, "SYSCLK" },
+       { "Right DAC", NULL, "SYSCLK" },
+       { "Capture", NULL, "SYSCLK" },
+       { "Playback", NULL, "SYSCLK" },
+
+       { "Left DAC", NULL, "Left DAC Mode Mux" },
+       { "Right DAC", NULL, "Right DAC Mode Mux" },
+
+       { "Capture", NULL, "AIFCLK" },
+       { "Playback", NULL, "AIFCLK" },
+};
+
+static const struct snd_soc_dapm_route adau17x1_dapm_pll_route = {
+       "SYSCLK", NULL, "PLL",
+};
+
+/*
+ * The MUX register for the Capture and Playback MUXs selects either DSP as
+ * source/destination or one of the TDM slots. The TDM slot is selected via
+ * snd_soc_dai_set_tdm_slot(), so we only expose whether to go to the DSP or
+ * directly to the DAI interface with this control.
+ */
+static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       struct snd_soc_dapm_update update;
+       unsigned int stream = e->shift_l;
+       unsigned int val, change;
+       int reg;
+
+       if (ucontrol->value.enumerated.item[0] >= e->items)
+               return -EINVAL;
+
+       switch (ucontrol->value.enumerated.item[0]) {
+       case 0:
+               val = 0;
+               adau->dsp_bypass[stream] = false;
+               break;
+       default:
+               val = (adau->tdm_slot[stream] * 2) + 1;
+               adau->dsp_bypass[stream] = true;
+               break;
+       }
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               reg = ADAU17X1_SERIAL_INPUT_ROUTE;
+       else
+               reg = ADAU17X1_SERIAL_OUTPUT_ROUTE;
+
+       change = snd_soc_test_bits(codec, reg, 0xff, val);
+       if (change) {
+               update.kcontrol = kcontrol;
+               update.mask = 0xff;
+               update.reg = reg;
+               update.val = val;
+
+               snd_soc_dapm_mux_update_power(&codec->dapm, kcontrol,
+                               ucontrol->value.enumerated.item[0], e, &update);
+       }
+
+       return change;
+}
+
+static int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int stream = e->shift_l;
+       unsigned int reg, val;
+       int ret;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               reg = ADAU17X1_SERIAL_INPUT_ROUTE;
+       else
+               reg = ADAU17X1_SERIAL_OUTPUT_ROUTE;
+
+       ret = regmap_read(adau->regmap, reg, &val);
+       if (ret)
+               return ret;
+
+       if (val != 0)
+               val = 1;
+       ucontrol->value.enumerated.item[0] = val;
+
+       return 0;
+}
+
+#define DECLARE_ADAU17X1_DSP_MUX_CTRL(_name, _label, _stream, _text) \
+       const struct snd_kcontrol_new _name = \
+               SOC_DAPM_ENUM_EXT(_label, (const struct soc_enum)\
+                       SOC_ENUM_SINGLE(SND_SOC_NOPM, _stream, \
+                               ARRAY_SIZE(_text), _text), \
+                       adau17x1_dsp_mux_enum_get, adau17x1_dsp_mux_enum_put)
+
+static const char * const adau17x1_dac_mux_text[] = {
+       "DSP",
+       "AIFIN",
+};
+
+static const char * const adau17x1_capture_mux_text[] = {
+       "DSP",
+       "Decimator",
+};
+
+static DECLARE_ADAU17X1_DSP_MUX_CTRL(adau17x1_dac_mux, "DAC Playback Mux",
+       SNDRV_PCM_STREAM_PLAYBACK, adau17x1_dac_mux_text);
+
+static DECLARE_ADAU17X1_DSP_MUX_CTRL(adau17x1_capture_mux, "Capture Mux",
+       SNDRV_PCM_STREAM_CAPTURE, adau17x1_capture_mux_text);
+
+static const struct snd_soc_dapm_widget adau17x1_dsp_dapm_widgets[] = {
+       SND_SOC_DAPM_PGA("DSP", ADAU17X1_DSP_RUN, 0, 0, NULL, 0),
+       SND_SOC_DAPM_SIGGEN("DSP Siggen"),
+
+       SND_SOC_DAPM_MUX("DAC Playback Mux", SND_SOC_NOPM, 0, 0,
+               &adau17x1_dac_mux),
+       SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0,
+               &adau17x1_capture_mux),
+};
+
+static const struct snd_soc_dapm_route adau17x1_dsp_dapm_routes[] = {
+       { "DAC Playback Mux", "DSP", "DSP" },
+       { "DAC Playback Mux", "AIFIN", "Playback" },
+
+       { "Left DAC Mode Mux", "Stereo", "DAC Playback Mux" },
+       { "Left DAC Mode Mux", "Mono (L+R)", "DAC Playback Mux" },
+       { "Left DAC Mode Mux", "Mono Left Channel (L+R)", "DAC Playback Mux" },
+       { "Right DAC Mode Mux", "Stereo", "DAC Playback Mux" },
+       { "Right DAC Mode Mux", "Mono (L+R)", "DAC Playback Mux" },
+       { "Right DAC Mode Mux", "Mono Right Channel (L+R)", "DAC Playback Mux" },
+
+       { "Capture Mux", "DSP", "DSP" },
+       { "Capture Mux", "Decimator", "Left Decimator" },
+       { "Capture Mux", "Decimator", "Right Decimator" },
+
+       { "Capture", NULL, "Capture Mux" },
+
+       { "DSP", NULL, "DSP Siggen" },
+
+       { "DSP", NULL, "Left Decimator" },
+       { "DSP", NULL, "Right Decimator" },
+};
+
+static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = {
+       { "Left DAC Mode Mux", "Stereo", "Playback" },
+       { "Left DAC Mode Mux", "Mono (L+R)", "Playback" },
+       { "Left DAC Mode Mux", "Mono Left Channel (L+R)", "Playback" },
+       { "Right DAC Mode Mux", "Stereo", "Playback" },
+       { "Right DAC Mode Mux", "Mono (L+R)", "Playback" },
+       { "Right DAC Mode Mux", "Mono Right Channel (L+R)", "Playback" },
+       { "Capture", NULL, "Left Decimator" },
+       { "Capture", NULL, "Right Decimator" },
+};
+
+bool adau17x1_has_dsp(struct adau *adau)
+{
+       switch (adau->type) {
+       case ADAU1761:
+       case ADAU1381:
+       case ADAU1781:
+               return true;
+       default:
+               return false;
+       }
+}
+EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
+
+static int adau17x1_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 adau *adau = snd_soc_codec_get_drvdata(codec);
+       unsigned int val, div, dsp_div;
+       unsigned int freq;
+
+       if (adau->clk_src == ADAU17X1_CLK_SRC_PLL)
+               freq = adau->pll_freq;
+       else
+               freq = adau->sysclk;
+
+       if (freq % params_rate(params) != 0)
+               return -EINVAL;
+
+       switch (freq / params_rate(params)) {
+       case 1024: /* fs */
+               div = 0;
+               dsp_div = 1;
+               break;
+       case 6144: /* fs / 6 */
+               div = 1;
+               dsp_div = 6;
+               break;
+       case 4096: /* fs / 4 */
+               div = 2;
+               dsp_div = 5;
+               break;
+       case 3072: /* fs / 3 */
+               div = 3;
+               dsp_div = 4;
+               break;
+       case 2048: /* fs / 2 */
+               div = 4;
+               dsp_div = 3;
+               break;
+       case 1536: /* fs / 1.5 */
+               div = 5;
+               dsp_div = 2;
+               break;
+       case 512: /* fs / 0.5 */
+               div = 6;
+               dsp_div = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
+               ADAU17X1_CONVERTER0_CONVSR_MASK, div);
+       if (adau17x1_has_dsp(adau)) {
+               regmap_write(adau->regmap, ADAU17X1_SERIAL_SAMPLING_RATE, div);
+               regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div);
+       }
+
+       if (adau->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
+               return 0;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               val = ADAU17X1_SERIAL_PORT1_DELAY16;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               val = ADAU17X1_SERIAL_PORT1_DELAY8;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               val = ADAU17X1_SERIAL_PORT1_DELAY0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
+                       ADAU17X1_SERIAL_PORT1_DELAY_MASK, val);
+}
+
+static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
+       int source, unsigned int freq_in, unsigned int freq_out)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+       unsigned int r, n, m, i, j;
+       unsigned int div;
+       int ret;
+
+       if (freq_in < 8000000 || freq_in > 27000000)
+               return -EINVAL;
+
+       if (!freq_out) {
+               r = 0;
+               n = 0;
+               m = 0;
+               div = 0;
+       } else {
+               if (freq_out % freq_in != 0) {
+                       div = DIV_ROUND_UP(freq_in, 13500000);
+                       freq_in /= div;
+                       r = freq_out / freq_in;
+                       i = freq_out % freq_in;
+                       j = gcd(i, freq_in);
+                       n = i / j;
+                       m = freq_in / j;
+                       div--;
+               } else {
+                       r = freq_out / freq_in;
+                       n = 0;
+                       m = 0;
+                       div = 0;
+               }
+               if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
+                       return -EINVAL;
+       }
+
+       adau->pll_regs[0] = m >> 8;
+       adau->pll_regs[1] = m & 0xff;
+       adau->pll_regs[2] = n >> 8;
+       adau->pll_regs[3] = n & 0xff;
+       adau->pll_regs[4] = (r << 3) | (div << 1);
+       if (m != 0)
+               adau->pll_regs[4] |= 1; /* Fractional mode */
+
+       /* The PLL register is 6 bytes long and can only be written at once. */
+       ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
+                       adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
+       if (ret)
+               return ret;
+
+       adau->pll_freq = freq_out;
+
+       return 0;
+}
+
+static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+       struct snd_soc_dapm_context *dapm = &dai->codec->dapm;
+
+       switch (clk_id) {
+       case ADAU17X1_CLK_SRC_MCLK:
+       case ADAU17X1_CLK_SRC_PLL:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       adau->sysclk = freq;
+
+       if (adau->clk_src != clk_id) {
+               if (clk_id == ADAU17X1_CLK_SRC_PLL) {
+                       snd_soc_dapm_add_routes(dapm,
+                               &adau17x1_dapm_pll_route, 1);
+               } else {
+                       snd_soc_dapm_del_routes(dapm,
+                               &adau17x1_dapm_pll_route, 1);
+               }
+       }
+
+       adau->clk_src = clk_id;
+
+       return 0;
+}
+
+static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
+               unsigned int fmt)
+{
+       struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+       unsigned int ctrl0, ctrl1;
+       int lrclk_pol;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               ctrl0 = ADAU17X1_SERIAL_PORT0_MASTER;
+               adau->master = true;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               ctrl0 = 0;
+               adau->master = false;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               lrclk_pol = 0;
+               ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+       case SND_SOC_DAIFMT_RIGHT_J:
+               lrclk_pol = 1;
+               ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               lrclk_pol = 1;
+               ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE;
+               ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               lrclk_pol = 1;
+               ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE;
+               ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               lrclk_pol = !lrclk_pol;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL;
+               lrclk_pol = !lrclk_pol;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (lrclk_pol)
+               ctrl0 |= ADAU17X1_SERIAL_PORT0_LRCLK_POL;
+
+       regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT0, ctrl0);
+       regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT1, ctrl1);
+
+       adau->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+       return 0;
+}
+
+static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
+       unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+       struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+       unsigned int ser_ctrl0, ser_ctrl1;
+       unsigned int conv_ctrl0, conv_ctrl1;
+
+       /* I2S mode */
+       if (slots == 0) {
+               slots = 2;
+               rx_mask = 3;
+               tx_mask = 3;
+               slot_width = 32;
+       }
+
+       switch (slots) {
+       case 2:
+               ser_ctrl0 = ADAU17X1_SERIAL_PORT0_STEREO;
+               break;
+       case 4:
+               ser_ctrl0 = ADAU17X1_SERIAL_PORT0_TDM4;
+               break;
+       case 8:
+               if (adau->type == ADAU1361)
+                       return -EINVAL;
+
+               ser_ctrl0 = ADAU17X1_SERIAL_PORT0_TDM8;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (slot_width * slots) {
+       case 32:
+               if (adau->type == ADAU1761)
+                       return -EINVAL;
+
+               ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32;
+               break;
+       case 64:
+               ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK64;
+               break;
+       case 48:
+               ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK48;
+               break;
+       case 128:
+               ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK128;
+               break;
+       case 256:
+               if (adau->type == ADAU1361)
+                       return -EINVAL;
+
+               ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK256;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (rx_mask) {
+       case 0x03:
+               conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(1);
+               adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 0;
+               break;
+       case 0x0c:
+               conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(2);
+               adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 1;
+               break;
+       case 0x30:
+               conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(3);
+               adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 2;
+               break;
+       case 0xc0:
+               conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(4);
+               adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 3;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (tx_mask) {
+       case 0x03:
+               conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(1);
+               adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 0;
+               break;
+       case 0x0c:
+               conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(2);
+               adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 1;
+               break;
+       case 0x30:
+               conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(3);
+               adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 2;
+               break;
+       case 0xc0:
+               conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(4);
+               adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 3;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
+               ADAU17X1_CONVERTER0_DAC_PAIR_MASK, conv_ctrl0);
+       regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER1,
+               ADAU17X1_CONVERTER1_ADC_PAIR_MASK, conv_ctrl1);
+       regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT0,
+               ADAU17X1_SERIAL_PORT0_TDM_MASK, ser_ctrl0);
+       regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
+               ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1);
+
+       if (!adau17x1_has_dsp(adau))
+               return 0;
+
+       if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) {
+               regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE,
+                       (adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] * 2) + 1);
+       }
+
+       if (adau->dsp_bypass[SNDRV_PCM_STREAM_CAPTURE]) {
+               regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE,
+                       (adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] * 2) + 1);
+       }
+
+       return 0;
+}
+
+const struct snd_soc_dai_ops adau17x1_dai_ops = {
+       .hw_params      = adau17x1_hw_params,
+       .set_sysclk     = adau17x1_set_dai_sysclk,
+       .set_fmt        = adau17x1_set_dai_fmt,
+       .set_pll        = adau17x1_set_dai_pll,
+       .set_tdm_slot   = adau17x1_set_dai_tdm_slot,
+};
+EXPORT_SYMBOL_GPL(adau17x1_dai_ops);
+
+int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
+       enum adau17x1_micbias_voltage micbias)
+{
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+       switch (micbias) {
+       case ADAU17X1_MICBIAS_0_90_AVDD:
+       case ADAU17X1_MICBIAS_0_65_AVDD:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_write(adau->regmap, ADAU17X1_MICBIAS, micbias << 2);
+}
+EXPORT_SYMBOL_GPL(adau17x1_set_micbias_voltage);
+
+bool adau17x1_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ADAU17X1_CLOCK_CONTROL:
+       case ADAU17X1_PLL_CONTROL:
+       case ADAU17X1_REC_POWER_MGMT:
+       case ADAU17X1_MICBIAS:
+       case ADAU17X1_SERIAL_PORT0:
+       case ADAU17X1_SERIAL_PORT1:
+       case ADAU17X1_CONVERTER0:
+       case ADAU17X1_CONVERTER1:
+       case ADAU17X1_LEFT_INPUT_DIGITAL_VOL:
+       case ADAU17X1_RIGHT_INPUT_DIGITAL_VOL:
+       case ADAU17X1_ADC_CONTROL:
+       case ADAU17X1_PLAY_POWER_MGMT:
+       case ADAU17X1_DAC_CONTROL0:
+       case ADAU17X1_DAC_CONTROL1:
+       case ADAU17X1_DAC_CONTROL2:
+       case ADAU17X1_SERIAL_PORT_PAD:
+       case ADAU17X1_CONTROL_PORT_PAD0:
+       case ADAU17X1_CONTROL_PORT_PAD1:
+       case ADAU17X1_DSP_SAMPLING_RATE:
+       case ADAU17X1_SERIAL_INPUT_ROUTE:
+       case ADAU17X1_SERIAL_OUTPUT_ROUTE:
+       case ADAU17X1_DSP_ENABLE:
+       case ADAU17X1_DSP_RUN:
+       case ADAU17X1_SERIAL_SAMPLING_RATE:
+               return true;
+       default:
+               break;
+       }
+       return false;
+}
+EXPORT_SYMBOL_GPL(adau17x1_readable_register);
+
+bool adau17x1_volatile_register(struct device *dev, unsigned int reg)
+{
+       /* SigmaDSP parameter and program memory */
+       if (reg < 0x4000)
+               return true;
+
+       switch (reg) {
+       /* The PLL register is 6 bytes long */
+       case ADAU17X1_PLL_CONTROL:
+       case ADAU17X1_PLL_CONTROL + 1:
+       case ADAU17X1_PLL_CONTROL + 2:
+       case ADAU17X1_PLL_CONTROL + 3:
+       case ADAU17X1_PLL_CONTROL + 4:
+       case ADAU17X1_PLL_CONTROL + 5:
+               return true;
+       default:
+               break;
+       }
+
+       return false;
+}
+EXPORT_SYMBOL_GPL(adau17x1_volatile_register);
+
+int adau17x1_load_firmware(struct adau *adau, struct device *dev,
+       const char *firmware)
+{
+       int ret;
+       int dspsr;
+
+       ret = regmap_read(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, &dspsr);
+       if (ret)
+               return ret;
+
+       regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 1);
+       regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, 0xf);
+
+       ret = process_sigma_firmware_regmap(dev, adau->regmap, firmware);
+       if (ret) {
+               regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 0);
+               return ret;
+       }
+       regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dspsr);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_load_firmware);
+
+int adau17x1_add_widgets(struct snd_soc_codec *codec)
+{
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       ret = snd_soc_add_codec_controls(codec, adau17x1_controls,
+               ARRAY_SIZE(adau17x1_controls));
+       if (ret)
+               return ret;
+       ret = snd_soc_dapm_new_controls(&codec->dapm, adau17x1_dapm_widgets,
+               ARRAY_SIZE(adau17x1_dapm_widgets));
+       if (ret)
+               return ret;
+
+       if (adau17x1_has_dsp(adau)) {
+               ret = snd_soc_dapm_new_controls(&codec->dapm,
+                       adau17x1_dsp_dapm_widgets,
+                       ARRAY_SIZE(adau17x1_dsp_dapm_widgets));
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(adau17x1_add_widgets);
+
+int adau17x1_add_routes(struct snd_soc_codec *codec)
+{
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       ret = snd_soc_dapm_add_routes(&codec->dapm, adau17x1_dapm_routes,
+               ARRAY_SIZE(adau17x1_dapm_routes));
+       if (ret)
+               return ret;
+
+       if (adau17x1_has_dsp(adau)) {
+               ret = snd_soc_dapm_add_routes(&codec->dapm,
+                       adau17x1_dsp_dapm_routes,
+                       ARRAY_SIZE(adau17x1_dsp_dapm_routes));
+       } else {
+               ret = snd_soc_dapm_add_routes(&codec->dapm,
+                       adau17x1_no_dsp_dapm_routes,
+                       ARRAY_SIZE(adau17x1_no_dsp_dapm_routes));
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(adau17x1_add_routes);
+
+int adau17x1_suspend(struct snd_soc_codec *codec)
+{
+       codec->driver->set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_suspend);
+
+int adau17x1_resume(struct snd_soc_codec *codec)
+{
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+       if (adau->switch_mode)
+               adau->switch_mode(codec->dev);
+
+       codec->driver->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       regcache_sync(adau->regmap);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_resume);
+
+int adau17x1_probe(struct device *dev, struct regmap *regmap,
+       enum adau17x1_type type, void (*switch_mode)(struct device *dev))
+{
+       struct adau *adau;
+
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       adau = devm_kzalloc(dev, sizeof(*adau), GFP_KERNEL);
+       if (!adau)
+               return -ENOMEM;
+
+       adau->regmap = regmap;
+       adau->switch_mode = switch_mode;
+       adau->type = type;
+
+       dev_set_drvdata(dev, adau);
+
+       if (switch_mode)
+               switch_mode(dev);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(adau17x1_probe);
+
+MODULE_DESCRIPTION("ASoC ADAU1X61/ADAU1X81 common code");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h
new file mode 100644 (file)
index 0000000..3ffabaf
--- /dev/null
@@ -0,0 +1,124 @@
+#ifndef __ADAU17X1_H__
+#define __ADAU17X1_H__
+
+#include <linux/regmap.h>
+#include <linux/platform_data/adau17x1.h>
+
+enum adau17x1_type {
+       ADAU1361,
+       ADAU1761,
+       ADAU1381,
+       ADAU1781,
+};
+
+enum adau17x1_pll {
+       ADAU17X1_PLL,
+};
+
+enum adau17x1_pll_src {
+       ADAU17X1_PLL_SRC_MCLK,
+};
+
+enum adau17x1_clk_src {
+       ADAU17X1_CLK_SRC_MCLK,
+       ADAU17X1_CLK_SRC_PLL,
+};
+
+struct adau {
+       unsigned int sysclk;
+       unsigned int pll_freq;
+
+       enum adau17x1_clk_src clk_src;
+       enum adau17x1_type type;
+       void (*switch_mode)(struct device *dev);
+
+       unsigned int dai_fmt;
+
+       uint8_t pll_regs[6];
+
+       bool master;
+
+       unsigned int tdm_slot[2];
+       bool dsp_bypass[2];
+
+       struct regmap *regmap;
+};
+
+int adau17x1_add_widgets(struct snd_soc_codec *codec);
+int adau17x1_add_routes(struct snd_soc_codec *codec);
+int adau17x1_probe(struct device *dev, struct regmap *regmap,
+       enum adau17x1_type type, void (*switch_mode)(struct device *dev));
+int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
+       enum adau17x1_micbias_voltage micbias);
+bool adau17x1_readable_register(struct device *dev, unsigned int reg);
+bool adau17x1_volatile_register(struct device *dev, unsigned int reg);
+int adau17x1_suspend(struct snd_soc_codec *codec);
+int adau17x1_resume(struct snd_soc_codec *codec);
+
+extern const struct snd_soc_dai_ops adau17x1_dai_ops;
+
+int adau17x1_load_firmware(struct adau *adau, struct device *dev,
+       const char *firmware);
+bool adau17x1_has_dsp(struct adau *adau);
+
+#define ADAU17X1_CLOCK_CONTROL                 0x4000
+#define ADAU17X1_PLL_CONTROL                   0x4002
+#define ADAU17X1_REC_POWER_MGMT                        0x4009
+#define ADAU17X1_MICBIAS                       0x4010
+#define ADAU17X1_SERIAL_PORT0                  0x4015
+#define ADAU17X1_SERIAL_PORT1                  0x4016
+#define ADAU17X1_CONVERTER0                    0x4017
+#define ADAU17X1_CONVERTER1                    0x4018
+#define ADAU17X1_LEFT_INPUT_DIGITAL_VOL                0x401a
+#define ADAU17X1_RIGHT_INPUT_DIGITAL_VOL       0x401b
+#define ADAU17X1_ADC_CONTROL                   0x4019
+#define ADAU17X1_PLAY_POWER_MGMT               0x4029
+#define ADAU17X1_DAC_CONTROL0                  0x402a
+#define ADAU17X1_DAC_CONTROL1                  0x402b
+#define ADAU17X1_DAC_CONTROL2                  0x402c
+#define ADAU17X1_SERIAL_PORT_PAD               0x402d
+#define ADAU17X1_CONTROL_PORT_PAD0             0x402f
+#define ADAU17X1_CONTROL_PORT_PAD1             0x4030
+#define ADAU17X1_DSP_SAMPLING_RATE             0x40eb
+#define ADAU17X1_SERIAL_INPUT_ROUTE            0x40f2
+#define ADAU17X1_SERIAL_OUTPUT_ROUTE           0x40f3
+#define ADAU17X1_DSP_ENABLE                    0x40f5
+#define ADAU17X1_DSP_RUN                       0x40f6
+#define ADAU17X1_SERIAL_SAMPLING_RATE          0x40f8
+
+#define ADAU17X1_SERIAL_PORT0_BCLK_POL         BIT(4)
+#define ADAU17X1_SERIAL_PORT0_LRCLK_POL                BIT(3)
+#define ADAU17X1_SERIAL_PORT0_MASTER           BIT(0)
+
+#define ADAU17X1_SERIAL_PORT1_DELAY1           0x00
+#define ADAU17X1_SERIAL_PORT1_DELAY0           0x01
+#define ADAU17X1_SERIAL_PORT1_DELAY8           0x02
+#define ADAU17X1_SERIAL_PORT1_DELAY16          0x03
+#define ADAU17X1_SERIAL_PORT1_DELAY_MASK       0x03
+
+#define ADAU17X1_CLOCK_CONTROL_INFREQ_MASK     0x6
+#define ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL BIT(3)
+#define ADAU17X1_CLOCK_CONTROL_SYSCLK_EN       BIT(0)
+
+#define ADAU17X1_SERIAL_PORT1_BCLK32           (0x0 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK48           (0x1 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK64           (0x2 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK128          (0x3 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK256          (0x4 << 5)
+#define ADAU17X1_SERIAL_PORT1_BCLK_MASK                (0x7 << 5)
+
+#define ADAU17X1_SERIAL_PORT0_STEREO           (0x0 << 1)
+#define ADAU17X1_SERIAL_PORT0_TDM4             (0x1 << 1)
+#define ADAU17X1_SERIAL_PORT0_TDM8             (0x2 << 1)
+#define ADAU17X1_SERIAL_PORT0_TDM_MASK         (0x3 << 1)
+#define ADAU17X1_SERIAL_PORT0_PULSE_MODE       BIT(5)
+
+#define ADAU17X1_CONVERTER0_DAC_PAIR(x)                (((x) - 1) << 5)
+#define ADAU17X1_CONVERTER0_DAC_PAIR_MASK      (0x3 << 5)
+#define ADAU17X1_CONVERTER1_ADC_PAIR(x)                ((x) - 1)
+#define ADAU17X1_CONVERTER1_ADC_PAIR_MASK      0x3
+
+#define ADAU17X1_CONVERTER0_CONVSR_MASK                0x7
+
+
+#endif
index 5062e34..c43b93f 100644 (file)
@@ -172,14 +172,14 @@ static ADAV80X_MUX_ENUM_DECL(adav80x_capture_enum, ADAV80X_DPATH_CTRL1, 3);
 static ADAV80X_MUX_ENUM_DECL(adav80x_dac_enum, ADAV80X_DPATH_CTRL2, 3);
 
 static const struct snd_kcontrol_new adav80x_aux_capture_mux_ctrl =
-       SOC_DAPM_VALUE_ENUM("Route", adav80x_aux_capture_enum);
+       SOC_DAPM_ENUM("Route", adav80x_aux_capture_enum);
 static const struct snd_kcontrol_new adav80x_capture_mux_ctrl =
-       SOC_DAPM_VALUE_ENUM("Route", adav80x_capture_enum);
+       SOC_DAPM_ENUM("Route", adav80x_capture_enum);
 static const struct snd_kcontrol_new adav80x_dac_mux_ctrl =
-       SOC_DAPM_VALUE_ENUM("Route", adav80x_dac_enum);
+       SOC_DAPM_ENUM("Route", adav80x_dac_enum);
 
 #define ADAV80X_MUX(name, ctrl) \
-       SND_SOC_DAPM_VALUE_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+       SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
 
 static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
        SND_SOC_DAPM_DAC("DAC", NULL, ADAV80X_DAC_CTRL1, 7, 1),
@@ -315,7 +315,7 @@ static int adav80x_set_deemph(struct snd_soc_codec *codec)
 static int adav80x_put_deemph(struct snd_kcontrol *kcontrol,
                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
        unsigned int deemph = ucontrol->value.enumerated.item[0];
 
@@ -330,7 +330,7 @@ static int adav80x_put_deemph(struct snd_kcontrol *kcontrol,
 static int adav80x_get_deemph(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = adav80x->deemph;
index 10adf25..1fd7f72 100644 (file)
 
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/soc.h>
-#include <sound/initval.h>
 #include <linux/spi/spi.h>
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
 #include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
 
 /* AK4104 registers addresses */
 #define AK4104_REG_CONTROL1            0x00
@@ -47,6 +48,7 @@
 
 struct ak4104_private {
        struct regmap *regmap;
+       struct regulator *regulator;
 };
 
 static const struct snd_soc_dapm_widget ak4104_dapm_widgets[] = {
@@ -174,20 +176,30 @@ static int ak4104_probe(struct snd_soc_codec *codec)
        struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
        int ret;
 
+       ret = regulator_enable(ak4104->regulator);
+       if (ret < 0) {
+               dev_err(codec->dev, "Unable to enable regulator: %d\n", ret);
+               return ret;
+       }
+
        /* set power-up and non-reset bits */
        ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
                                 AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN,
                                 AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN);
        if (ret < 0)
-               return ret;
+               goto exit_disable_regulator;
 
        /* enable transmitter */
        ret = regmap_update_bits(ak4104->regmap, AK4104_REG_TX,
                                 AK4104_TX_TXE, AK4104_TX_TXE);
        if (ret < 0)
-               return ret;
+               goto exit_disable_regulator;
 
        return 0;
+
+exit_disable_regulator:
+       regulator_disable(ak4104->regulator);
+       return ret;
 }
 
 static int ak4104_remove(struct snd_soc_codec *codec)
@@ -196,13 +208,42 @@ static int ak4104_remove(struct snd_soc_codec *codec)
 
        regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1,
                           AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN, 0);
+       regulator_disable(ak4104->regulator);
 
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int ak4104_soc_suspend(struct snd_soc_codec *codec)
+{
+       struct ak4104_private *priv = snd_soc_codec_get_drvdata(codec);
+
+       regulator_disable(priv->regulator);
+
+       return 0;
+}
+
+static int ak4104_soc_resume(struct snd_soc_codec *codec)
+{
+       struct ak4104_private *priv = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       ret = regulator_enable(priv->regulator);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+#else
+#define ak4104_soc_suspend     NULL
+#define ak4104_soc_resume      NULL
+#endif /* CONFIG_PM */
+
 static struct snd_soc_codec_driver soc_codec_device_ak4104 = {
-       .probe =        ak4104_probe,
-       .remove =       ak4104_remove,
+       .probe = ak4104_probe,
+       .remove = ak4104_remove,
+       .suspend = ak4104_soc_suspend,
+       .resume = ak4104_soc_resume,
 
        .dapm_widgets = ak4104_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets),
@@ -239,6 +280,13 @@ static int ak4104_spi_probe(struct spi_device *spi)
        if (ak4104 == NULL)
                return -ENOMEM;
 
+       ak4104->regulator = devm_regulator_get(&spi->dev, "vdd");
+       if (IS_ERR(ak4104->regulator)) {
+               ret = PTR_ERR(ak4104->regulator);
+               dev_err(&spi->dev, "Unable to get Vdd regulator: %d\n", ret);
+               return ret;
+       }
+
        ak4104->regmap = devm_regmap_init_spi(spi, &ak4104_regmap);
        if (IS_ERR(ak4104->regmap)) {
                ret = PTR_ERR(ak4104->regmap);
index 868c0e2..7afe8f4 100644 (file)
@@ -74,7 +74,7 @@ static int ak4641_set_deemph(struct snd_soc_codec *codec)
 static int ak4641_put_deemph(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
        int deemph = ucontrol->value.enumerated.item[0];
 
@@ -89,7 +89,7 @@ static int ak4641_put_deemph(struct snd_kcontrol *kcontrol,
 static int ak4641_get_deemph(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = ak4641->deemph;
index 92655cc..3ba4c0f 100644 (file)
@@ -98,7 +98,7 @@
 #define MGAIN0         (1 << 0) /* MIC amp gain*/
 
 /* TIMER */
-#define ZTM(param)     ((param & 0x3) << 4) /* ALC Zoro Crossing TimeOut */
+#define ZTM(param)     ((param & 0x3) << 4) /* ALC Zero Crossing TimeOut */
 #define WTM(param)     (((param & 0x4) << 4) | ((param & 0x3) << 2))
 
 /* ALC_CTL1 */
 /* MD_CTL4 */
 #define DACH           (1 << 0)
 
+struct ak4642_drvdata {
+       const struct regmap_config *regmap_config;
+       int extended_frequencies;
+};
+
+struct ak4642_priv {
+       const struct ak4642_drvdata *drvdata;
+};
+
 /*
  * Playback Volume (table 39)
  *
@@ -148,6 +157,8 @@ static const struct snd_kcontrol_new ak4642_snd_controls[] = {
 
        SOC_DOUBLE_R_TLV("Digital Playback Volume", L_DVC, R_DVC,
                         0, 0xFF, 1, out_tlv),
+       SOC_SINGLE("ALC Capture Switch", ALC_CTL1, 5, 1, 0),
+       SOC_SINGLE("ALC Capture ZC Switch", ALC_CTL1, 4, 1, 1),
 };
 
 static const struct snd_kcontrol_new ak4642_headphone_control =
@@ -287,7 +298,9 @@ 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 *priv = snd_soc_codec_get_drvdata(codec);
        u8 pll;
+       int extended_freq = 0;
 
        switch (freq) {
        case 11289600:
@@ -308,9 +321,25 @@ static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai,
        case 27000000:
                pll = PLL3 | PLL2 | PLL0;
                break;
+       case 19200000:
+               pll = PLL3;
+               extended_freq = 1;
+               break;
+       case 13000000:
+               pll = PLL3 | PLL2 | PLL1;
+               extended_freq = 1;
+               break;
+       case 26000000:
+               pll = PLL3 | PLL2 | PLL1 | PLL0;
+               extended_freq = 1;
+               break;
        default:
                return -EINVAL;
        }
+
+       if (extended_freq && !priv->drvdata->extended_frequencies)
+               return -EINVAL;
+
        snd_soc_update_bits(codec, MD_CTL1, PLL_MASK, pll);
 
        return 0;
@@ -505,30 +534,52 @@ static const struct regmap_config ak4648_regmap = {
        .num_reg_defaults       = ARRAY_SIZE(ak4648_reg),
 };
 
+static const struct ak4642_drvdata ak4642_drvdata = {
+       .regmap_config = &ak4642_regmap,
+};
+
+static const struct ak4642_drvdata ak4643_drvdata = {
+       .regmap_config = &ak4642_regmap,
+};
+
+static const struct ak4642_drvdata ak4648_drvdata = {
+       .regmap_config = &ak4648_regmap,
+       .extended_frequencies = 1,
+};
+
 static struct of_device_id ak4642_of_match[];
 static int ak4642_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
        struct device_node *np = i2c->dev.of_node;
-       const struct regmap_config *regmap_config = NULL;
+       const struct ak4642_drvdata *drvdata = NULL;
        struct regmap *regmap;
+       struct ak4642_priv *priv;
 
        if (np) {
                const struct of_device_id *of_id;
 
                of_id = of_match_device(ak4642_of_match, &i2c->dev);
                if (of_id)
-                       regmap_config = of_id->data;
+                       drvdata = of_id->data;
        } else {
-               regmap_config = (const struct regmap_config *)id->driver_data;
+               drvdata = (const struct ak4642_drvdata *)id->driver_data;
        }
 
-       if (!regmap_config) {
+       if (!drvdata) {
                dev_err(&i2c->dev, "Unknown device type\n");
                return -EINVAL;
        }
 
-       regmap = devm_regmap_init_i2c(i2c, regmap_config);
+       priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->drvdata = drvdata;
+
+       i2c_set_clientdata(i2c, priv);
+
+       regmap = devm_regmap_init_i2c(i2c, drvdata->regmap_config);
        if (IS_ERR(regmap))
                return PTR_ERR(regmap);
 
@@ -543,17 +594,17 @@ static int ak4642_i2c_remove(struct i2c_client *client)
 }
 
 static struct of_device_id ak4642_of_match[] = {
-       { .compatible = "asahi-kasei,ak4642",   .data = &ak4642_regmap},
-       { .compatible = "asahi-kasei,ak4643",   .data = &ak4642_regmap},
-       { .compatible = "asahi-kasei,ak4648",   .data = &ak4648_regmap},
+       { .compatible = "asahi-kasei,ak4642",   .data = &ak4642_drvdata},
+       { .compatible = "asahi-kasei,ak4643",   .data = &ak4643_drvdata},
+       { .compatible = "asahi-kasei,ak4648",   .data = &ak4648_drvdata},
        {},
 };
 MODULE_DEVICE_TABLE(of, ak4642_of_match);
 
 static const struct i2c_device_id ak4642_i2c_id[] = {
-       { "ak4642", (kernel_ulong_t)&ak4642_regmap },
-       { "ak4643", (kernel_ulong_t)&ak4642_regmap },
-       { "ak4648", (kernel_ulong_t)&ak4648_regmap },
+       { "ak4642", (kernel_ulong_t)&ak4642_drvdata },
+       { "ak4643", (kernel_ulong_t)&ak4643_drvdata },
+       { "ak4648", (kernel_ulong_t)&ak4648_drvdata },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id);
index 2acf82f..9d0755a 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/i2c.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -998,8 +999,10 @@ static int alc5623_i2c_probe(struct i2c_client *client,
 {
        struct alc5623_platform_data *pdata;
        struct alc5623_priv *alc5623;
+       struct device_node *np;
        unsigned int vid1, vid2;
        int ret;
+       u32 val32;
 
        alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv),
                               GFP_KERNEL);
@@ -1040,6 +1043,16 @@ static int alc5623_i2c_probe(struct i2c_client *client,
        if (pdata) {
                alc5623->add_ctrl = pdata->add_ctrl;
                alc5623->jack_det_ctrl = pdata->jack_det_ctrl;
+       } else {
+               if (client->dev.of_node) {
+                       np = client->dev.of_node;
+                       ret = of_property_read_u32(np, "add-ctrl", &val32);
+                       if (!ret)
+                               alc5623->add_ctrl = val32;
+                       ret = of_property_read_u32(np, "jack-det-ctrl", &val32);
+                       if (!ret)
+                               alc5623->jack_det_ctrl = val32;
+               }
        }
 
        alc5623->id = vid2;
@@ -1081,11 +1094,18 @@ static const struct i2c_device_id alc5623_i2c_table[] = {
 };
 MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table);
 
+static const struct of_device_id alc5623_of_match[] = {
+       { .compatible = "realtek,alc5623", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, alc5623_of_match);
+
 /*  i2c codec control layer */
 static struct i2c_driver alc5623_i2c_driver = {
        .driver = {
                .name = "alc562x-codec",
                .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(alc5623_of_match),
        },
        .probe = alc5623_i2c_probe,
        .remove =  alc5623_i2c_remove,
index 16df0f9..05ae17f 100644 (file)
@@ -107,7 +107,7 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
 
 #define ARIZONA_MUX_CTL_DECL(name) \
        const struct snd_kcontrol_new name##_mux =      \
-               SOC_DAPM_VALUE_ENUM("Route", name##_enum)
+               SOC_DAPM_ENUM("Route", name##_enum)
 
 #define ARIZONA_MUX_ENUMS(name, base_reg) \
        static ARIZONA_MUX_ENUM_DECL(name##_enum, base_reg);      \
@@ -128,7 +128,7 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
        ARIZONA_MUX_ENUMS(name##_aux6, base_reg + 40)
 
 #define ARIZONA_MUX(name, ctrl) \
-       SND_SOC_DAPM_VALUE_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+       SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
 
 #define ARIZONA_MUX_WIDGETS(name, name_str) \
        ARIZONA_MUX(name_str " Input", &name##_mux)
index 1e25c7a..537327c 100644 (file)
@@ -139,8 +139,6 @@ static int cq93vc_probe(struct snd_soc_codec *codec)
 
        davinci_vc->cq93vc.codec = codec;
 
-       snd_soc_codec_set_cache_io(codec, davinci_vc->regmap);
-
        /* Off, with power on */
        cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
@@ -154,11 +152,19 @@ static int cq93vc_remove(struct snd_soc_codec *codec)
        return 0;
 }
 
+static struct regmap *cq93vc_get_regmap(struct device *dev)
+{
+       struct davinci_vc *davinci_vc = dev->platform_data;
+
+       return davinci_vc->regmap;
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_cq93vc = {
        .set_bias_level = cq93vc_set_bias_level,
        .probe = cq93vc_probe,
        .remove = cq93vc_remove,
        .resume = cq93vc_resume,
+       .get_regmap = cq93vc_get_regmap,
        .controls = cq93vc_snd_controls,
        .num_controls = ARRAY_SIZE(cq93vc_snd_controls),
 };
index 3920e62..9947a95 100644 (file)
@@ -438,7 +438,7 @@ static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
 static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
        int left = !ucontrol->value.integer.value[0];
        int right = !ucontrol->value.integer.value[1];
index aef4965..93cec52 100644 (file)
@@ -284,7 +284,7 @@ static int cs4271_set_deemph(struct snd_soc_codec *codec)
 static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = cs4271->deemph;
@@ -294,7 +294,7 @@ static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
 static int cs4271_put_deemph(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
 
        cs4271->deemph = ucontrol->value.enumerated.item[0];
diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c
new file mode 100644 (file)
index 0000000..cee51ae
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * cs42l56.c -- CS42L51 ALSA SoC I2C audio driver
+ *
+ * Copyright 2014 CirrusLogic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.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/i2c.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "cs42l51.h"
+
+static struct i2c_device_id cs42l51_i2c_id[] = {
+       {"cs42l51", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);
+
+static int cs42l51_i2c_probe(struct i2c_client *i2c,
+                            const struct i2c_device_id *id)
+{
+       struct regmap_config config;
+
+       config = cs42l51_regmap;
+       config.val_bits = 8;
+       config.reg_bits = 8;
+
+       return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config));
+}
+
+static int cs42l51_i2c_remove(struct i2c_client *i2c)
+{
+       snd_soc_unregister_codec(&i2c->dev);
+
+       return 0;
+}
+
+static struct i2c_driver cs42l51_i2c_driver = {
+       .driver = {
+               .name = "cs42l51",
+               .owner = THIS_MODULE,
+       },
+       .probe = cs42l51_i2c_probe,
+       .remove = cs42l51_i2c_remove,
+       .id_table = cs42l51_i2c_id,
+};
+
+module_i2c_driver(cs42l51_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L51 I2C Driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
index 6c0da2b..09488d9 100644 (file)
@@ -29,7 +29,6 @@
 #include <sound/initval.h>
 #include <sound/pcm_params.h>
 #include <sound/pcm.h>
-#include <linux/i2c.h>
 #include <linux/regmap.h>
 
 #include "cs42l51.h"
@@ -55,7 +54,7 @@ struct cs42l51_private {
 static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned long value = snd_soc_read(codec, CS42L51_PCM_MIXER)&3;
 
        switch (value) {
@@ -83,7 +82,7 @@ static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
 static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned char val;
 
        switch (ucontrol->value.integer.value[0]) {
@@ -483,7 +482,7 @@ static struct snd_soc_dai_driver cs42l51_dai = {
        .ops = &cs42l51_dai_ops,
 };
 
-static int cs42l51_probe(struct snd_soc_codec *codec)
+static int cs42l51_codec_probe(struct snd_soc_codec *codec)
 {
        int ret, reg;
 
@@ -504,7 +503,7 @@ static int cs42l51_probe(struct snd_soc_codec *codec)
 }
 
 static struct snd_soc_codec_driver soc_codec_device_cs42l51 = {
-       .probe = cs42l51_probe,
+       .probe = cs42l51_codec_probe,
 
        .controls = cs42l51_snd_controls,
        .num_controls = ARRAY_SIZE(cs42l51_snd_controls),
@@ -514,91 +513,56 @@ static struct snd_soc_codec_driver soc_codec_device_cs42l51 = {
        .num_dapm_routes = ARRAY_SIZE(cs42l51_routes),
 };
 
-static const struct regmap_config cs42l51_regmap = {
-       .reg_bits = 8,
-       .val_bits = 8,
-
+const struct regmap_config cs42l51_regmap = {
        .max_register = CS42L51_CHARGE_FREQ,
        .cache_type = REGCACHE_RBTREE,
 };
+EXPORT_SYMBOL_GPL(cs42l51_regmap);
 
-static int cs42l51_i2c_probe(struct i2c_client *i2c_client,
-       const struct i2c_device_id *id)
+int cs42l51_probe(struct device *dev, struct regmap *regmap)
 {
        struct cs42l51_private *cs42l51;
-       struct regmap *regmap;
        unsigned int val;
        int ret;
 
-       regmap = devm_regmap_init_i2c(i2c_client, &cs42l51_regmap);
-       if (IS_ERR(regmap)) {
-               ret = PTR_ERR(regmap);
-               dev_err(&i2c_client->dev, "Failed to create regmap: %d\n",
-                       ret);
-               return ret;
-       }
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       cs42l51 = devm_kzalloc(dev, sizeof(struct cs42l51_private),
+                              GFP_KERNEL);
+       if (!cs42l51)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, cs42l51);
 
        /* Verify that we have a CS42L51 */
        ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
        if (ret < 0) {
-               dev_err(&i2c_client->dev, "failed to read I2C\n");
+               dev_err(dev, "failed to read I2C\n");
                goto error;
        }
 
        if ((val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
            (val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
-               dev_err(&i2c_client->dev, "Invalid chip id: %x\n", val);
+               dev_err(dev, "Invalid chip id: %x\n", val);
                ret = -ENODEV;
                goto error;
        }
+       dev_info(dev, "Cirrus Logic CS42L51, Revision: %02X\n",
+                val & CS42L51_CHIP_REV_MASK);
 
-       dev_info(&i2c_client->dev, "found device cs42l51 rev %d\n",
-                val & 7);
-
-       cs42l51 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l51_private),
-                              GFP_KERNEL);
-       if (!cs42l51)
-               return -ENOMEM;
-
-       i2c_set_clientdata(i2c_client, cs42l51);
-
-       ret =  snd_soc_register_codec(&i2c_client->dev,
+       ret =  snd_soc_register_codec(dev,
                        &soc_codec_device_cs42l51, &cs42l51_dai, 1);
 error:
        return ret;
 }
-
-static int cs42l51_i2c_remove(struct i2c_client *client)
-{
-       snd_soc_unregister_codec(&client->dev);
-       return 0;
-}
-
-static const struct i2c_device_id cs42l51_id[] = {
-       {"cs42l51", 0},
-       {}
-};
-MODULE_DEVICE_TABLE(i2c, cs42l51_id);
+EXPORT_SYMBOL_GPL(cs42l51_probe);
 
 static const struct of_device_id cs42l51_of_match[] = {
        { .compatible = "cirrus,cs42l51", },
        { }
 };
 MODULE_DEVICE_TABLE(of, cs42l51_of_match);
-
-static struct i2c_driver cs42l51_i2c_driver = {
-       .driver = {
-               .name = "cs42l51-codec",
-               .owner = THIS_MODULE,
-               .of_match_table = cs42l51_of_match,
-       },
-       .id_table = cs42l51_id,
-       .probe = cs42l51_i2c_probe,
-       .remove = cs42l51_i2c_remove,
-};
-
-module_i2c_driver(cs42l51_i2c_driver);
-
 MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
 MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
 MODULE_LICENSE("GPL");
index 2beeb17..8c55bf3 100644 (file)
 #ifndef _CS42L51_H
 #define _CS42L51_H
 
+struct device;
+
+extern const struct regmap_config cs42l51_regmap;
+int cs42l51_probe(struct device *dev, struct regmap *regmap);
+
 #define CS42L51_CHIP_ID                        0x1B
 #define CS42L51_CHIP_REV_A             0x00
 #define CS42L51_CHIP_REV_B             0x01
+#define CS42L51_CHIP_REV_MASK          0x07
 
 #define CS42L51_CHIP_REV_ID            0x01
 #define CS42L51_MK_CHIP_REV(a, b)      ((a)<<3|(b))
index 2213a03..071fc77 100644 (file)
@@ -50,11 +50,9 @@ struct  cs42l52_private {
        u8 mclksel;
        u32 mclk;
        u8 flags;
-#if IS_ENABLED(CONFIG_INPUT)
        struct input_dev *beep;
        struct work_struct beep_work;
        int beep_rate;
-#endif
 };
 
 static const struct reg_default cs42l52_reg_defaults[] = {
@@ -962,7 +960,6 @@ static int cs42l52_resume(struct snd_soc_codec *codec)
        return 0;
 }
 
-#if IS_ENABLED(CONFIG_INPUT)
 static int beep_rates[] = {
        261, 522, 585, 667, 706, 774, 889, 1000,
        1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182
@@ -1096,15 +1093,6 @@ static void cs42l52_free_beep(struct snd_soc_codec *codec)
        snd_soc_update_bits(codec, CS42L52_BEEP_TONE_CTL,
                            CS42L52_BEEP_EN_MASK, 0);
 }
-#else
-static void cs42l52_init_beep(struct snd_soc_codec *codec)
-{
-}
-
-static void cs42l52_free_beep(struct snd_soc_codec *codec)
-{
-}
-#endif
 
 static int cs42l52_probe(struct snd_soc_codec *codec)
 {
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
new file mode 100644 (file)
index 0000000..fdc4bd2
--- /dev/null
@@ -0,0 +1,1419 @@
+/*
+ * cs42l56.c -- CS42L56 ALSA SoC audio driver
+ *
+ * Copyright 2014 CirrusLogic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.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 <sound/cs42l56.h>
+#include "cs42l56.h"
+
+#define CS42L56_NUM_SUPPLIES 3
+static const char *const cs42l56_supply_names[CS42L56_NUM_SUPPLIES] = {
+       "VA",
+       "VCP",
+       "VLDO",
+};
+
+struct  cs42l56_private {
+       struct regmap *regmap;
+       struct snd_soc_codec *codec;
+       struct device *dev;
+       struct cs42l56_platform_data pdata;
+       struct regulator_bulk_data supplies[CS42L56_NUM_SUPPLIES];
+       u32 mclk;
+       u8 mclk_prediv;
+       u8 mclk_div2;
+       u8 mclk_ratio;
+       u8 iface;
+       u8 iface_fmt;
+       u8 iface_inv;
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+       struct input_dev *beep;
+       struct work_struct beep_work;
+       int beep_rate;
+#endif
+};
+
+static const struct reg_default cs42l56_reg_defaults[] = {
+       { 1, 0x56 },    /* r01  - ID 1 */
+       { 2, 0x04 },    /* r02  - ID 2 */
+       { 3, 0x7f },    /* r03  - Power Ctl 1 */
+       { 4, 0xff },    /* r04  - Power Ctl 2 */
+       { 5, 0x00 },    /* ro5  - Clocking Ctl 1 */
+       { 6, 0x0b },    /* r06  - Clocking Ctl 2 */
+       { 7, 0x00 },    /* r07  - Serial Format */
+       { 8, 0x05 },    /* r08  - Class H Ctl */
+       { 9, 0x0c },    /* r09  - Misc Ctl */
+       { 10, 0x80 },   /* r0a  - INT Status */
+       { 11, 0x00 },   /* r0b  - Playback Ctl */
+       { 12, 0x0c },   /* r0c  - DSP Mute Ctl */
+       { 13, 0x00 },   /* r0d  - ADCA Mixer Volume */
+       { 14, 0x00 },   /* r0e  - ADCB Mixer Volume */
+       { 15, 0x00 },   /* r0f  - PCMA Mixer Volume */
+       { 16, 0x00 },   /* r10  - PCMB Mixer Volume */
+       { 17, 0x00 },   /* r11  - Analog Input Advisory Volume */
+       { 18, 0x00 },   /* r12  - Digital Input Advisory Volume */
+       { 19, 0x00 },   /* r13  - Master A Volume */
+       { 20, 0x00 },   /* r14  - Master B Volume */
+       { 21, 0x00 },   /* r15  - Beep Freq / On Time */
+       { 22, 0x00 },   /* r16  - Beep Volume / Off Time */
+       { 23, 0x00 },   /* r17  - Beep Tone Ctl */
+       { 24, 0x88 },   /* r18  - Tone Ctl */
+       { 25, 0x00 },   /* r19  - Channel Mixer & Swap */
+       { 26, 0x00 },   /* r1a  - AIN Ref Config / ADC Mux */
+       { 27, 0xa0 },   /* r1b  - High-Pass Filter Ctl */
+       { 28, 0x00 },   /* r1c  - Misc ADC Ctl */
+       { 29, 0x00 },   /* r1d  - Gain & Bias Ctl */
+       { 30, 0x00 },   /* r1e  - PGAA Mux & Volume */
+       { 31, 0x00 },   /* r1f  - PGAB Mux & Volume */
+       { 32, 0x00 },   /* r20  - ADCA Attenuator */
+       { 33, 0x00 },   /* r21  - ADCB Attenuator */
+       { 34, 0x00 },   /* r22  - ALC Enable & Attack Rate */
+       { 35, 0xbf },   /* r23  - ALC Release Rate */
+       { 36, 0x00 },   /* r24  - ALC Threshold */
+       { 37, 0x00 },   /* r25  - Noise Gate Ctl */
+       { 38, 0x00 },   /* r26  - ALC, Limiter, SFT, ZeroCross */
+       { 39, 0x00 },   /* r27  - Analog Mute, LO & HP Mux */
+       { 40, 0x00 },   /* r28  - HP A Volume */
+       { 41, 0x00 },   /* r29  - HP B Volume */
+       { 42, 0x00 },   /* r2a  - LINEOUT A Volume */
+       { 43, 0x00 },   /* r2b  - LINEOUT B Volume */
+       { 44, 0x00 },   /* r2c  - Limit Threshold Ctl */
+       { 45, 0x7f },   /* r2d  - Limiter Ctl & Release Rate */
+       { 46, 0x00 },   /* r2e  - Limiter Attack Rate */
+};
+
+static bool cs42l56_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS42L56_CHIP_ID_1:
+       case CS42L56_CHIP_ID_2:
+       case CS42L56_PWRCTL_1:
+       case CS42L56_PWRCTL_2:
+       case CS42L56_CLKCTL_1:
+       case CS42L56_CLKCTL_2:
+       case CS42L56_SERIAL_FMT:
+       case CS42L56_CLASSH_CTL:
+       case CS42L56_MISC_CTL:
+       case CS42L56_INT_STATUS:
+       case CS42L56_PLAYBACK_CTL:
+       case CS42L56_DSP_MUTE_CTL:
+       case CS42L56_ADCA_MIX_VOLUME:
+       case CS42L56_ADCB_MIX_VOLUME:
+       case CS42L56_PCMA_MIX_VOLUME:
+       case CS42L56_PCMB_MIX_VOLUME:
+       case CS42L56_ANAINPUT_ADV_VOLUME:
+       case CS42L56_DIGINPUT_ADV_VOLUME:
+       case CS42L56_MASTER_A_VOLUME:
+       case CS42L56_MASTER_B_VOLUME:
+       case CS42L56_BEEP_FREQ_ONTIME:
+       case CS42L56_BEEP_FREQ_OFFTIME:
+       case CS42L56_BEEP_TONE_CFG:
+       case CS42L56_TONE_CTL:
+       case CS42L56_CHAN_MIX_SWAP:
+       case CS42L56_AIN_REFCFG_ADC_MUX:
+       case CS42L56_HPF_CTL:
+       case CS42L56_MISC_ADC_CTL:
+       case CS42L56_GAIN_BIAS_CTL:
+       case CS42L56_PGAA_MUX_VOLUME:
+       case CS42L56_PGAB_MUX_VOLUME:
+       case CS42L56_ADCA_ATTENUATOR:
+       case CS42L56_ADCB_ATTENUATOR:
+       case CS42L56_ALC_EN_ATTACK_RATE:
+       case CS42L56_ALC_RELEASE_RATE:
+       case CS42L56_ALC_THRESHOLD:
+       case CS42L56_NOISE_GATE_CTL:
+       case CS42L56_ALC_LIM_SFT_ZC:
+       case CS42L56_AMUTE_HPLO_MUX:
+       case CS42L56_HPA_VOLUME:
+       case CS42L56_HPB_VOLUME:
+       case CS42L56_LOA_VOLUME:
+       case CS42L56_LOB_VOLUME:
+       case CS42L56_LIM_THRESHOLD_CTL:
+       case CS42L56_LIM_CTL_RELEASE_RATE:
+       case CS42L56_LIM_ATTACK_RATE:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool cs42l56_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS42L56_INT_STATUS:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static DECLARE_TLV_DB_SCALE(beep_tlv, -5000, 200, 0);
+static DECLARE_TLV_DB_SCALE(hl_tlv, -6000, 50, 0);
+static DECLARE_TLV_DB_SCALE(adv_tlv, -10200, 50, 0);
+static DECLARE_TLV_DB_SCALE(adc_tlv, -9600, 100, 0);
+static DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
+static DECLARE_TLV_DB_SCALE(preamp_tlv, 0, 1000, 0);
+static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
+
+static const unsigned int ngnb_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 1, TLV_DB_SCALE_ITEM(-8200, 600, 0),
+       2, 5, TLV_DB_SCALE_ITEM(-7600, 300, 0),
+};
+static const unsigned int ngb_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 2, TLV_DB_SCALE_ITEM(-6400, 600, 0),
+       3, 7, TLV_DB_SCALE_ITEM(-4600, 300, 0),
+};
+static const unsigned int alc_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0),
+       3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0),
+};
+
+static const char * const beep_config_text[] = {
+       "Off", "Single", "Multiple", "Continuous"
+};
+
+static const struct soc_enum beep_config_enum =
+       SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 6,
+                       ARRAY_SIZE(beep_config_text), beep_config_text);
+
+static const char * const beep_pitch_text[] = {
+       "C4", "C5", "D5", "E5", "F5", "G5", "A5", "B5",
+       "C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7"
+};
+
+static const struct soc_enum beep_pitch_enum =
+       SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_ONTIME, 4,
+                       ARRAY_SIZE(beep_pitch_text), beep_pitch_text);
+
+static const char * const beep_ontime_text[] = {
+       "86 ms", "430 ms", "780 ms", "1.20 s", "1.50 s",
+       "1.80 s", "2.20 s", "2.50 s", "2.80 s", "3.20 s",
+       "3.50 s", "3.80 s", "4.20 s", "4.50 s", "4.80 s", "5.20 s"
+};
+
+static const struct soc_enum beep_ontime_enum =
+       SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_ONTIME, 0,
+                       ARRAY_SIZE(beep_ontime_text), beep_ontime_text);
+
+static const char * const beep_offtime_text[] = {
+       "1.23 s", "2.58 s", "3.90 s", "5.20 s",
+       "6.60 s", "8.05 s", "9.35 s", "10.80 s"
+};
+
+static const struct soc_enum beep_offtime_enum =
+       SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_OFFTIME, 5,
+                       ARRAY_SIZE(beep_offtime_text), beep_offtime_text);
+
+static const char * const beep_treble_text[] = {
+       "5kHz", "7kHz", "10kHz", "15kHz"
+};
+
+static const struct soc_enum beep_treble_enum =
+       SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 3,
+                       ARRAY_SIZE(beep_treble_text), beep_treble_text);
+
+static const char * const beep_bass_text[] = {
+       "50Hz", "100Hz", "200Hz", "250Hz"
+};
+
+static const struct soc_enum beep_bass_enum =
+       SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 1,
+                       ARRAY_SIZE(beep_bass_text), beep_bass_text);
+
+static const char * const adc_swap_text[] = {
+       "None", "A+B/2", "A-B/2", "Swap"
+};
+
+static const struct soc_enum adc_swap_enum =
+       SOC_ENUM_SINGLE(CS42L56_MISC_ADC_CTL, 3,
+                       ARRAY_SIZE(adc_swap_text), adc_swap_text);
+
+static const char * const pgaa_mux_text[] = {
+       "AIN1A", "AIN2A", "AIN3A"};
+
+static const struct soc_enum pgaa_mux_enum =
+       SOC_ENUM_SINGLE(CS42L56_PGAA_MUX_VOLUME, 0,
+                             ARRAY_SIZE(pgaa_mux_text),
+                             pgaa_mux_text);
+
+static const struct snd_kcontrol_new pgaa_mux =
+       SOC_DAPM_ENUM("Route", pgaa_mux_enum);
+
+static const char * const pgab_mux_text[] = {
+       "AIN1B", "AIN2B", "AIN3B"};
+
+static const struct soc_enum pgab_mux_enum =
+       SOC_ENUM_SINGLE(CS42L56_PGAB_MUX_VOLUME, 0,
+                             ARRAY_SIZE(pgab_mux_text),
+                             pgab_mux_text);
+
+static const struct snd_kcontrol_new pgab_mux =
+       SOC_DAPM_ENUM("Route", pgab_mux_enum);
+
+static const char * const adca_mux_text[] = {
+       "PGAA", "AIN1A", "AIN2A", "AIN3A"};
+
+static const struct soc_enum adca_mux_enum =
+       SOC_ENUM_SINGLE(CS42L56_AIN_REFCFG_ADC_MUX, 0,
+                             ARRAY_SIZE(adca_mux_text),
+                             adca_mux_text);
+
+static const struct snd_kcontrol_new adca_mux =
+       SOC_DAPM_ENUM("Route", adca_mux_enum);
+
+static const char * const adcb_mux_text[] = {
+       "PGAB", "AIN1B", "AIN2B", "AIN3B"};
+
+static const struct soc_enum adcb_mux_enum =
+       SOC_ENUM_SINGLE(CS42L56_AIN_REFCFG_ADC_MUX, 2,
+                             ARRAY_SIZE(adcb_mux_text),
+                             adcb_mux_text);
+
+static const struct snd_kcontrol_new adcb_mux =
+       SOC_DAPM_ENUM("Route", adcb_mux_enum);
+
+static const char * const left_swap_text[] = {
+       "Left", "LR 2", "Right"};
+
+static const char * const right_swap_text[] = {
+       "Right", "LR 2", "Left"};
+
+static const unsigned int swap_values[] = { 0, 1, 3 };
+
+static const struct soc_enum adca_swap_enum =
+       SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 0, 3,
+                             ARRAY_SIZE(left_swap_text),
+                             left_swap_text,
+                             swap_values);
+
+static const struct soc_enum pcma_swap_enum =
+       SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 4, 3,
+                             ARRAY_SIZE(left_swap_text),
+                             left_swap_text,
+                             swap_values);
+
+static const struct soc_enum adcb_swap_enum =
+       SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 2, 3,
+                             ARRAY_SIZE(right_swap_text),
+                             right_swap_text,
+                             swap_values);
+
+static const struct soc_enum pcmb_swap_enum =
+       SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 6, 3,
+                             ARRAY_SIZE(right_swap_text),
+                             right_swap_text,
+                             swap_values);
+
+static const struct snd_kcontrol_new hpa_switch =
+       SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 6, 1, 1);
+
+static const struct snd_kcontrol_new hpb_switch =
+       SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 4, 1, 1);
+
+static const struct snd_kcontrol_new loa_switch =
+       SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 2, 1, 1);
+
+static const struct snd_kcontrol_new lob_switch =
+       SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 0, 1, 1);
+
+static const char * const hploa_input_text[] = {
+       "DACA", "PGAA"};
+
+static const struct soc_enum lineouta_input_enum =
+       SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 2,
+                             ARRAY_SIZE(hploa_input_text),
+                             hploa_input_text);
+
+static const struct snd_kcontrol_new lineouta_input =
+       SOC_DAPM_ENUM("Route", lineouta_input_enum);
+
+static const struct soc_enum hpa_input_enum =
+       SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 0,
+                             ARRAY_SIZE(hploa_input_text),
+                             hploa_input_text);
+
+static const struct snd_kcontrol_new hpa_input =
+       SOC_DAPM_ENUM("Route", hpa_input_enum);
+
+static const char * const hplob_input_text[] = {
+       "DACB", "PGAB"};
+
+static const struct soc_enum lineoutb_input_enum =
+       SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 3,
+                             ARRAY_SIZE(hplob_input_text),
+                             hplob_input_text);
+
+static const struct snd_kcontrol_new lineoutb_input =
+       SOC_DAPM_ENUM("Route", lineoutb_input_enum);
+
+static const struct soc_enum hpb_input_enum =
+       SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 1,
+                             ARRAY_SIZE(hplob_input_text),
+                             hplob_input_text);
+
+static const struct snd_kcontrol_new hpb_input =
+       SOC_DAPM_ENUM("Route", hpb_input_enum);
+
+static const char * const dig_mux_text[] = {
+       "ADC", "DSP"};
+
+static const struct soc_enum dig_mux_enum =
+       SOC_ENUM_SINGLE(CS42L56_MISC_CTL, 7,
+                             ARRAY_SIZE(dig_mux_text),
+                             dig_mux_text);
+
+static const struct snd_kcontrol_new dig_mux =
+       SOC_DAPM_ENUM("Route", dig_mux_enum);
+
+static const char * const hpf_freq_text[] = {
+       "1.8Hz", "119Hz", "236Hz", "464Hz"
+};
+
+static const struct soc_enum hpfa_freq_enum =
+       SOC_ENUM_SINGLE(CS42L56_HPF_CTL, 0,
+                       ARRAY_SIZE(hpf_freq_text), hpf_freq_text);
+
+static const struct soc_enum hpfb_freq_enum =
+       SOC_ENUM_SINGLE(CS42L56_HPF_CTL, 2,
+                       ARRAY_SIZE(hpf_freq_text), hpf_freq_text);
+
+static const char * const ng_delay_text[] = {
+       "50ms", "100ms", "150ms", "200ms"
+};
+
+static const struct soc_enum ng_delay_enum =
+       SOC_ENUM_SINGLE(CS42L56_NOISE_GATE_CTL, 0,
+                       ARRAY_SIZE(ng_delay_text), ng_delay_text);
+
+static const struct snd_kcontrol_new cs42l56_snd_controls[] = {
+
+       SOC_DOUBLE_R_SX_TLV("Master Volume", CS42L56_MASTER_A_VOLUME,
+                             CS42L56_MASTER_B_VOLUME, 0, 0x34, 0xfd, adv_tlv),
+       SOC_DOUBLE("Master Mute Switch", CS42L56_DSP_MUTE_CTL, 0, 1, 1, 1),
+
+       SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume", CS42L56_ADCA_MIX_VOLUME,
+                             CS42L56_ADCB_MIX_VOLUME, 0, 0x88, 0xa9, hl_tlv),
+       SOC_DOUBLE("ADC Mixer Mute Switch", CS42L56_DSP_MUTE_CTL, 6, 7, 1, 1),
+
+       SOC_DOUBLE_R_SX_TLV("PCM Mixer Volume", CS42L56_PCMA_MIX_VOLUME,
+                             CS42L56_PCMB_MIX_VOLUME, 0, 0x88, 0xa9, hl_tlv),
+       SOC_DOUBLE("PCM Mixer Mute Switch", CS42L56_DSP_MUTE_CTL, 4, 5, 1, 1),
+
+       SOC_SINGLE_TLV("Analog Advisory Volume",
+                         CS42L56_ANAINPUT_ADV_VOLUME, 0, 0x00, 1, adv_tlv),
+       SOC_SINGLE_TLV("Digital Advisory Volume",
+                         CS42L56_DIGINPUT_ADV_VOLUME, 0, 0x00, 1, adv_tlv),
+
+       SOC_DOUBLE_R_SX_TLV("PGA Volume", CS42L56_PGAA_MUX_VOLUME,
+                             CS42L56_PGAB_MUX_VOLUME, 0, 0x34, 0xfd, pga_tlv),
+       SOC_DOUBLE_R_TLV("ADC Volume", CS42L56_ADCA_ATTENUATOR,
+                             CS42L56_ADCB_ATTENUATOR, 0, 0x00, 1, adc_tlv),
+       SOC_DOUBLE("ADC Mute Switch", CS42L56_MISC_ADC_CTL, 2, 3, 1, 1),
+       SOC_DOUBLE("ADC Boost Switch", CS42L56_GAIN_BIAS_CTL, 3, 2, 1, 1),
+
+       SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L56_HPA_VOLUME,
+                             CS42L56_HPA_VOLUME, 0, 0x44, 0x55, hl_tlv),
+       SOC_DOUBLE_R_SX_TLV("LineOut Volume", CS42L56_LOA_VOLUME,
+                             CS42L56_LOA_VOLUME, 0, 0x44, 0x55, hl_tlv),
+
+       SOC_SINGLE_TLV("Bass Shelving Volume", CS42L56_TONE_CTL,
+                       0, 0x00, 1, tone_tlv),
+       SOC_SINGLE_TLV("Treble Shelving Volume", CS42L56_TONE_CTL,
+                       4, 0x00, 1, tone_tlv),
+
+       SOC_DOUBLE_TLV("PGA Preamp Volume", CS42L56_GAIN_BIAS_CTL,
+                       4, 6, 0x02, 1, preamp_tlv),
+
+       SOC_SINGLE("DSP Switch", CS42L56_PLAYBACK_CTL, 7, 1, 1),
+       SOC_SINGLE("Gang Playback Switch", CS42L56_PLAYBACK_CTL, 4, 1, 1),
+       SOC_SINGLE("Gang ADC Switch", CS42L56_MISC_ADC_CTL, 7, 1, 1),
+       SOC_SINGLE("Gang PGA Switch", CS42L56_MISC_ADC_CTL, 6, 1, 1),
+
+       SOC_SINGLE("PCMA Invert", CS42L56_PLAYBACK_CTL, 2, 1, 1),
+       SOC_SINGLE("PCMB Invert", CS42L56_PLAYBACK_CTL, 3, 1, 1),
+       SOC_SINGLE("ADCA Invert", CS42L56_MISC_ADC_CTL, 2, 1, 1),
+       SOC_SINGLE("ADCB Invert", CS42L56_MISC_ADC_CTL, 3, 1, 1),
+
+       SOC_ENUM("PCMA Swap", pcma_swap_enum),
+       SOC_ENUM("PCMB Swap", pcmb_swap_enum),
+       SOC_ENUM("ADCA Swap", adca_swap_enum),
+       SOC_ENUM("ADCB Swap", adcb_swap_enum),
+
+       SOC_DOUBLE("HPF Switch", CS42L56_HPF_CTL, 5, 7, 1, 1),
+       SOC_DOUBLE("HPF Freeze Switch", CS42L56_HPF_CTL, 4, 6, 1, 1),
+       SOC_ENUM("HPFA Corner Freq", hpfa_freq_enum),
+       SOC_ENUM("HPFB Corner Freq", hpfb_freq_enum),
+
+       SOC_SINGLE("Analog Soft Ramp", CS42L56_MISC_CTL, 4, 1, 1),
+       SOC_DOUBLE("Analog Soft Ramp Disable", CS42L56_ALC_LIM_SFT_ZC,
+               7, 5, 1, 1),
+       SOC_SINGLE("Analog Zero Cross", CS42L56_MISC_CTL, 3, 1, 1),
+       SOC_DOUBLE("Analog Zero Cross Disable", CS42L56_ALC_LIM_SFT_ZC,
+               6, 4, 1, 1),
+       SOC_SINGLE("Digital Soft Ramp", CS42L56_MISC_CTL, 2, 1, 1),
+       SOC_SINGLE("Digital Soft Ramp Disable", CS42L56_ALC_LIM_SFT_ZC,
+               3, 1, 1),
+
+       SOC_SINGLE("HL Deemphasis", CS42L56_PLAYBACK_CTL, 6, 1, 1),
+
+       SOC_SINGLE("ALC Switch", CS42L56_ALC_EN_ATTACK_RATE, 6, 1, 1),
+       SOC_SINGLE("ALC Limit All Switch", CS42L56_ALC_RELEASE_RATE, 7, 1, 1),
+       SOC_SINGLE_RANGE("ALC Attack", CS42L56_ALC_EN_ATTACK_RATE,
+                       0, 0, 0x3f, 0),
+       SOC_SINGLE_RANGE("ALC Release", CS42L56_ALC_RELEASE_RATE,
+                       0, 0x3f, 0, 0),
+       SOC_SINGLE_TLV("ALC MAX", CS42L56_ALC_THRESHOLD,
+                       5, 0x07, 1, alc_tlv),
+       SOC_SINGLE_TLV("ALC MIN", CS42L56_ALC_THRESHOLD,
+                       2, 0x07, 1, alc_tlv),
+
+       SOC_SINGLE("Limiter Switch", CS42L56_LIM_CTL_RELEASE_RATE, 7, 1, 1),
+       SOC_SINGLE("Limit All Switch", CS42L56_LIM_CTL_RELEASE_RATE, 6, 1, 1),
+       SOC_SINGLE_RANGE("Limiter Attack", CS42L56_LIM_ATTACK_RATE,
+                       0, 0, 0x3f, 0),
+       SOC_SINGLE_RANGE("Limiter Release", CS42L56_LIM_CTL_RELEASE_RATE,
+                       0, 0x3f, 0, 0),
+       SOC_SINGLE_TLV("Limiter MAX", CS42L56_LIM_THRESHOLD_CTL,
+                       5, 0x07, 1, alc_tlv),
+       SOC_SINGLE_TLV("Limiter Cushion", CS42L56_ALC_THRESHOLD,
+                       2, 0x07, 1, alc_tlv),
+
+       SOC_SINGLE("NG Switch", CS42L56_NOISE_GATE_CTL, 6, 1, 1),
+       SOC_SINGLE("NG All Switch", CS42L56_NOISE_GATE_CTL, 7, 1, 1),
+       SOC_SINGLE("NG Boost Switch", CS42L56_NOISE_GATE_CTL, 5, 1, 1),
+       SOC_SINGLE_TLV("NG Unboost Threshold", CS42L56_NOISE_GATE_CTL,
+                       2, 0x07, 1, ngnb_tlv),
+       SOC_SINGLE_TLV("NG Boost Threshold", CS42L56_NOISE_GATE_CTL,
+                       2, 0x07, 1, ngb_tlv),
+       SOC_ENUM("NG Delay", ng_delay_enum),
+
+       SOC_ENUM("Beep Config", beep_config_enum),
+       SOC_ENUM("Beep Pitch", beep_pitch_enum),
+       SOC_ENUM("Beep on Time", beep_ontime_enum),
+       SOC_ENUM("Beep off Time", beep_offtime_enum),
+       SOC_SINGLE_SX_TLV("Beep Volume", CS42L56_BEEP_FREQ_OFFTIME,
+                       0, 0x07, 0x23, beep_tlv),
+       SOC_SINGLE("Beep Tone Ctl Switch", CS42L56_BEEP_TONE_CFG, 0, 1, 1),
+       SOC_ENUM("Beep Treble Corner Freq", beep_treble_enum),
+       SOC_ENUM("Beep Bass Corner Freq", beep_bass_enum),
+
+};
+
+static const struct snd_soc_dapm_widget cs42l56_dapm_widgets[] = {
+
+       SND_SOC_DAPM_SIGGEN("Beep"),
+       SND_SOC_DAPM_SUPPLY("VBUF", CS42L56_PWRCTL_1, 5, 1, NULL, 0),
+       SND_SOC_DAPM_MICBIAS("MIC1 Bias", CS42L56_PWRCTL_1, 4, 1),
+       SND_SOC_DAPM_SUPPLY("Charge Pump", CS42L56_PWRCTL_1, 3, 1, NULL, 0),
+
+       SND_SOC_DAPM_INPUT("AIN1A"),
+       SND_SOC_DAPM_INPUT("AIN2A"),
+       SND_SOC_DAPM_INPUT("AIN1B"),
+       SND_SOC_DAPM_INPUT("AIN2B"),
+       SND_SOC_DAPM_INPUT("AIN3A"),
+       SND_SOC_DAPM_INPUT("AIN3B"),
+
+       SND_SOC_DAPM_AIF_OUT("SDOUT", NULL,  0,
+                       SND_SOC_NOPM, 0, 0),
+
+       SND_SOC_DAPM_AIF_IN("SDIN", NULL,  0,
+                       SND_SOC_NOPM, 0, 0),
+
+       SND_SOC_DAPM_MUX("Digital Output Mux", SND_SOC_NOPM,
+                        0, 0, &dig_mux),
+
+       SND_SOC_DAPM_PGA("PGAA", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("PGAB", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MUX("PGAA Input Mux",
+                       SND_SOC_NOPM, 0, 0, &pgaa_mux),
+       SND_SOC_DAPM_MUX("PGAB Input Mux",
+                       SND_SOC_NOPM, 0, 0, &pgab_mux),
+
+       SND_SOC_DAPM_MUX("ADCA Mux", SND_SOC_NOPM,
+                        0, 0, &adca_mux),
+       SND_SOC_DAPM_MUX("ADCB Mux", SND_SOC_NOPM,
+                        0, 0, &adcb_mux),
+
+       SND_SOC_DAPM_ADC("ADCA", NULL, CS42L56_PWRCTL_1, 1, 1),
+       SND_SOC_DAPM_ADC("ADCB", NULL, CS42L56_PWRCTL_1, 2, 1),
+
+       SND_SOC_DAPM_DAC("DACA", NULL, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_DAC("DACB", NULL, SND_SOC_NOPM, 0, 0),
+
+       SND_SOC_DAPM_OUTPUT("HPA"),
+       SND_SOC_DAPM_OUTPUT("LOA"),
+       SND_SOC_DAPM_OUTPUT("HPB"),
+       SND_SOC_DAPM_OUTPUT("LOB"),
+
+       SND_SOC_DAPM_SWITCH("Headphone Right",
+                           CS42L56_PWRCTL_2, 4, 1, &hpb_switch),
+       SND_SOC_DAPM_SWITCH("Headphone Left",
+                           CS42L56_PWRCTL_2, 6, 1, &hpa_switch),
+
+       SND_SOC_DAPM_SWITCH("Lineout Right",
+                           CS42L56_PWRCTL_2, 0, 1, &lob_switch),
+       SND_SOC_DAPM_SWITCH("Lineout Left",
+                           CS42L56_PWRCTL_2, 2, 1, &loa_switch),
+
+       SND_SOC_DAPM_MUX("LINEOUTA Input Mux", SND_SOC_NOPM,
+                        0, 0, &lineouta_input),
+       SND_SOC_DAPM_MUX("LINEOUTB Input Mux", SND_SOC_NOPM,
+                        0, 0, &lineoutb_input),
+       SND_SOC_DAPM_MUX("HPA Input Mux", SND_SOC_NOPM,
+                        0, 0, &hpa_input),
+       SND_SOC_DAPM_MUX("HPB Input Mux", SND_SOC_NOPM,
+                        0, 0, &hpb_input),
+
+};
+
+static const struct snd_soc_dapm_route cs42l56_audio_map[] = {
+
+       {"HiFi Capture", "DSP", "Digital Output Mux"},
+       {"HiFi Capture", "ADC", "Digital Output Mux"},
+
+       {"Digital Output Mux", NULL, "ADCA"},
+       {"Digital Output Mux", NULL, "ADCB"},
+
+       {"ADCB", NULL, "ADCB Mux"},
+       {"ADCA", NULL, "ADCA Mux"},
+
+       {"ADCA Mux", NULL, "AIN3A"},
+       {"ADCA Mux", NULL, "AIN2A"},
+       {"ADCA Mux", NULL, "AIN1A"},
+       {"ADCA Mux", NULL, "PGAA"},
+       {"ADCB Mux", NULL, "AIN3B"},
+       {"ADCB Mux", NULL, "AIN2B"},
+       {"ADCB Mux", NULL, "AIN1B"},
+       {"ADCB Mux", NULL, "PGAB"},
+
+       {"PGAA", "AIN1A", "PGAA Input Mux"},
+       {"PGAA", "AIN2A", "PGAA Input Mux"},
+       {"PGAA", "AIN3A", "PGAA Input Mux"},
+       {"PGAB", "AIN1B", "PGAB Input Mux"},
+       {"PGAB", "AIN2B", "PGAB Input Mux"},
+       {"PGAB", "AIN3B", "PGAB Input Mux"},
+
+       {"PGAA Input Mux", NULL, "AIN1A"},
+       {"PGAA Input Mux", NULL, "AIN2A"},
+       {"PGAA Input Mux", NULL, "AIN3A"},
+       {"PGAB Input Mux", NULL, "AIN1B"},
+       {"PGAB Input Mux", NULL, "AIN2B"},
+       {"PGAB Input Mux", NULL, "AIN3B"},
+
+       {"LOB", NULL, "Lineout Right"},
+       {"LOA", NULL, "Lineout Left"},
+
+       {"Lineout Right", "Switch", "LINEOUTB Input Mux"},
+       {"Lineout Left", "Switch", "LINEOUTA Input Mux"},
+
+       {"LINEOUTA Input Mux", "PGAA", "PGAA"},
+       {"LINEOUTB Input Mux", "PGAB", "PGAB"},
+       {"LINEOUTA Input Mux", "DACA", "DACA"},
+       {"LINEOUTB Input Mux", "DACB", "DACB"},
+
+       {"HPA", NULL, "Headphone Left"},
+       {"HPB", NULL, "Headphone Right"},
+
+       {"Headphone Right", "Switch", "HPB Input Mux"},
+       {"Headphone Left", "Switch", "HPA Input Mux"},
+
+       {"HPA Input Mux", "PGAA", "PGAA"},
+       {"HPB Input Mux", "PGAB", "PGAB"},
+       {"HPA Input Mux", "DACA", "DACA"},
+       {"HPB Input Mux", "DACB", "DACB"},
+
+       {"DACB", NULL, "HiFi Playback"},
+       {"DACA", NULL, "HiFi Playback"},
+
+};
+
+struct cs42l56_clk_para {
+       u32 mclk;
+       u32 srate;
+       u8 ratio;
+};
+
+static const struct cs42l56_clk_para clk_ratio_table[] = {
+       /* 8k */
+       { 6000000, 8000, CS42L56_MCLK_LRCLK_768 },
+       { 6144000, 8000, CS42L56_MCLK_LRCLK_750 },
+       { 12000000, 8000, CS42L56_MCLK_LRCLK_768 },
+       { 12288000, 8000, CS42L56_MCLK_LRCLK_750 },
+       { 24000000, 8000, CS42L56_MCLK_LRCLK_768 },
+       { 24576000, 8000, CS42L56_MCLK_LRCLK_750 },
+       /* 11.025k */
+       { 5644800, 11025, CS42L56_MCLK_LRCLK_512},
+       { 11289600, 11025, CS42L56_MCLK_LRCLK_512},
+       { 22579200, 11025, CS42L56_MCLK_LRCLK_512 },
+       /* 11.0294k */
+       { 6000000, 110294, CS42L56_MCLK_LRCLK_544 },
+       { 12000000, 110294, CS42L56_MCLK_LRCLK_544 },
+       { 24000000, 110294, CS42L56_MCLK_LRCLK_544 },
+       /* 12k */
+       { 6000000, 12000, CS42L56_MCLK_LRCLK_500 },
+       { 6144000, 12000, CS42L56_MCLK_LRCLK_512 },
+       { 12000000, 12000, CS42L56_MCLK_LRCLK_500 },
+       { 12288000, 12000, CS42L56_MCLK_LRCLK_512 },
+       { 24000000, 12000, CS42L56_MCLK_LRCLK_500 },
+       { 24576000, 12000, CS42L56_MCLK_LRCLK_512 },
+       /* 16k */
+       { 6000000, 16000, CS42L56_MCLK_LRCLK_375 },
+       { 6144000, 16000, CS42L56_MCLK_LRCLK_384 },
+       { 12000000, 16000, CS42L56_MCLK_LRCLK_375 },
+       { 12288000, 16000, CS42L56_MCLK_LRCLK_384 },
+       { 24000000, 16000, CS42L56_MCLK_LRCLK_375 },
+       { 24576000, 16000, CS42L56_MCLK_LRCLK_384 },
+       /* 22.050k */
+       { 5644800, 22050, CS42L56_MCLK_LRCLK_256 },
+       { 11289600, 22050, CS42L56_MCLK_LRCLK_256 },
+       { 22579200, 22050, CS42L56_MCLK_LRCLK_256 },
+       /* 22.0588k */
+       { 6000000, 220588, CS42L56_MCLK_LRCLK_272 },
+       { 12000000, 220588, CS42L56_MCLK_LRCLK_272 },
+       { 24000000, 220588, CS42L56_MCLK_LRCLK_272 },
+       /* 24k */
+       { 6000000, 24000, CS42L56_MCLK_LRCLK_250 },
+       { 6144000, 24000, CS42L56_MCLK_LRCLK_256 },
+       { 12000000, 24000, CS42L56_MCLK_LRCLK_250 },
+       { 12288000, 24000, CS42L56_MCLK_LRCLK_256 },
+       { 24000000, 24000, CS42L56_MCLK_LRCLK_250 },
+       { 24576000, 24000, CS42L56_MCLK_LRCLK_256 },
+       /* 32k */
+       { 6000000, 32000, CS42L56_MCLK_LRCLK_187P5 },
+       { 6144000, 32000, CS42L56_MCLK_LRCLK_192 },
+       { 12000000, 32000, CS42L56_MCLK_LRCLK_187P5 },
+       { 12288000, 32000, CS42L56_MCLK_LRCLK_192 },
+       { 24000000, 32000, CS42L56_MCLK_LRCLK_187P5 },
+       { 24576000, 32000, CS42L56_MCLK_LRCLK_192 },
+       /* 44.118k */
+       { 6000000, 44118, CS42L56_MCLK_LRCLK_136 },
+       { 12000000, 44118, CS42L56_MCLK_LRCLK_136 },
+       { 24000000, 44118, CS42L56_MCLK_LRCLK_136 },
+       /* 44.1k */
+       { 5644800, 44100, CS42L56_MCLK_LRCLK_128 },
+       { 11289600, 44100, CS42L56_MCLK_LRCLK_128 },
+       { 22579200, 44100, CS42L56_MCLK_LRCLK_128 },
+       /* 48k */
+       { 6000000, 48000, CS42L56_MCLK_LRCLK_125 },
+       { 6144000, 48000, CS42L56_MCLK_LRCLK_128 },
+       { 12000000, 48000, CS42L56_MCLK_LRCLK_125 },
+       { 12288000, 48000, CS42L56_MCLK_LRCLK_128 },
+       { 24000000, 48000, CS42L56_MCLK_LRCLK_125 },
+       { 24576000, 48000, CS42L56_MCLK_LRCLK_128 },
+};
+
+static int cs42l56_get_mclk_ratio(int mclk, int rate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(clk_ratio_table); i++) {
+               if (clk_ratio_table[i].mclk == mclk &&
+                   clk_ratio_table[i].srate == rate)
+                       return clk_ratio_table[i].ratio;
+       }
+       return -EINVAL;
+}
+
+static int cs42l56_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 cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+
+       switch (freq) {
+       case CS42L56_MCLK_5P6448MHZ:
+       case CS42L56_MCLK_6MHZ:
+       case CS42L56_MCLK_6P144MHZ:
+               cs42l56->mclk_div2 = 0;
+               cs42l56->mclk_prediv = 0;
+               break;
+       case CS42L56_MCLK_11P2896MHZ:
+       case CS42L56_MCLK_12MHZ:
+       case CS42L56_MCLK_12P288MHZ:
+               cs42l56->mclk_div2 = CS42L56_MCLK_DIV2;
+               cs42l56->mclk_prediv = 0;
+               break;
+       case CS42L56_MCLK_22P5792MHZ:
+       case CS42L56_MCLK_24MHZ:
+       case CS42L56_MCLK_24P576MHZ:
+               cs42l56->mclk_div2 = CS42L56_MCLK_DIV2;
+               cs42l56->mclk_prediv = CS42L56_MCLK_PREDIV;
+               break;
+       default:
+               return -EINVAL;
+       }
+       cs42l56->mclk = freq;
+
+       snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+                           CS42L56_MCLK_PREDIV_MASK,
+                               cs42l56->mclk_prediv);
+       snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+                           CS42L56_MCLK_DIV2_MASK,
+                               cs42l56->mclk_div2);
+
+       return 0;
+}
+
+static int cs42l56_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               cs42l56->iface = CS42L56_MASTER_MODE;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               cs42l56->iface = CS42L56_SLAVE_MODE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+        /* interface format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               cs42l56->iface_fmt = CS42L56_DIG_FMT_I2S;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               cs42l56->iface_fmt = CS42L56_DIG_FMT_LEFT_J;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* sclk inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               cs42l56->iface_inv = 0;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               cs42l56->iface_inv = CS42L56_SCLK_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+                           CS42L56_MS_MODE_MASK, cs42l56->iface);
+       snd_soc_update_bits(codec, CS42L56_SERIAL_FMT,
+                           CS42L56_DIG_FMT_MASK, cs42l56->iface_fmt);
+       snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+                           CS42L56_SCLK_INV_MASK, cs42l56->iface_inv);
+       return 0;
+}
+
+static int cs42l56_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       if (mute) {
+               /* Hit the DSP Mixer first */
+               snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL,
+                                   CS42L56_ADCAMIX_MUTE_MASK |
+                                   CS42L56_ADCBMIX_MUTE_MASK |
+                                   CS42L56_PCMAMIX_MUTE_MASK |
+                                   CS42L56_PCMBMIX_MUTE_MASK |
+                                   CS42L56_MSTB_MUTE_MASK |
+                                   CS42L56_MSTA_MUTE_MASK,
+                                   CS42L56_MUTE_ALL);
+               /* Mute ADC's */
+               snd_soc_update_bits(codec, CS42L56_MISC_ADC_CTL,
+                                   CS42L56_ADCA_MUTE_MASK |
+                                   CS42L56_ADCB_MUTE_MASK,
+                                   CS42L56_MUTE_ALL);
+               /* HP And LO */
+               snd_soc_update_bits(codec, CS42L56_HPA_VOLUME,
+                                   CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
+               snd_soc_update_bits(codec, CS42L56_HPB_VOLUME,
+                                   CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
+               snd_soc_update_bits(codec, CS42L56_LOA_VOLUME,
+                                   CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
+               snd_soc_update_bits(codec, CS42L56_LOB_VOLUME,
+                                   CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
+       } else {
+               snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL,
+                                   CS42L56_ADCAMIX_MUTE_MASK |
+                                   CS42L56_ADCBMIX_MUTE_MASK |
+                                   CS42L56_PCMAMIX_MUTE_MASK |
+                                   CS42L56_PCMBMIX_MUTE_MASK |
+                                   CS42L56_MSTB_MUTE_MASK |
+                                   CS42L56_MSTA_MUTE_MASK,
+                                   CS42L56_UNMUTE);
+
+               snd_soc_update_bits(codec, CS42L56_MISC_ADC_CTL,
+                                   CS42L56_ADCA_MUTE_MASK |
+                                   CS42L56_ADCB_MUTE_MASK,
+                                   CS42L56_UNMUTE);
+
+               snd_soc_update_bits(codec, CS42L56_HPA_VOLUME,
+                                   CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
+               snd_soc_update_bits(codec, CS42L56_HPB_VOLUME,
+                                   CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
+               snd_soc_update_bits(codec, CS42L56_LOA_VOLUME,
+                                   CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
+               snd_soc_update_bits(codec, CS42L56_LOB_VOLUME,
+                                   CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
+       }
+       return 0;
+}
+
+static int cs42l56_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;
+       struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+       int ratio;
+
+       ratio = cs42l56_get_mclk_ratio(cs42l56->mclk, params_rate(params));
+       if (ratio >= 0) {
+               snd_soc_update_bits(codec, CS42L56_CLKCTL_2,
+                                   CS42L56_CLK_RATIO_MASK, ratio);
+       } else {
+               dev_err(codec->dev, "unsupported mclk/sclk/lrclk ratio\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
+                                       enum snd_soc_bias_level level)
+{
+       struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+                                   CS42L56_MCLK_DIS_MASK, 0);
+               snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+                                   CS42L56_PDN_ALL_MASK, 0);
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       regcache_cache_only(cs42l56->regmap, false);
+                       regcache_sync(cs42l56->regmap);
+                       ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies),
+                                                   cs42l56->supplies);
+                       if (ret != 0) {
+                               dev_err(cs42l56->dev,
+                                       "Failed to enable regulators: %d\n",
+                                       ret);
+                               return ret;
+                       }
+               }
+               snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+                                   CS42L56_PDN_ALL_MASK, 1);
+               break;
+       case SND_SOC_BIAS_OFF:
+               snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+                                   CS42L56_PDN_ALL_MASK, 1);
+               snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+                                   CS42L56_MCLK_DIS_MASK, 1);
+               regcache_cache_only(cs42l56->regmap, true);
+               regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies),
+                                                   cs42l56->supplies);
+               break;
+       }
+       codec->dapm.bias_level = level;
+
+       return 0;
+}
+
+#define CS42L56_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define CS42L56_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+                       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
+                       SNDRV_PCM_FMTBIT_S32_LE)
+
+
+static struct snd_soc_dai_ops cs42l56_ops = {
+       .hw_params      = cs42l56_pcm_hw_params,
+       .digital_mute   = cs42l56_digital_mute,
+       .set_fmt        = cs42l56_set_dai_fmt,
+       .set_sysclk     = cs42l56_set_sysclk,
+};
+
+static struct snd_soc_dai_driver cs42l56_dai = {
+               .name = "cs42l56",
+               .playback = {
+                       .stream_name = "HiFi Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = CS42L56_RATES,
+                       .formats = CS42L56_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "HiFi Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = CS42L56_RATES,
+                       .formats = CS42L56_FORMATS,
+               },
+               .ops = &cs42l56_ops,
+};
+
+static int cs42l56_suspend(struct snd_soc_codec *codec)
+{
+       cs42l56_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int cs42l56_resume(struct snd_soc_codec *codec)
+{
+       cs42l56_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+
+static int beep_freq[] = {
+       261, 522, 585, 667, 706, 774, 889, 1000,
+       1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182
+};
+
+static void cs42l56_beep_work(struct work_struct *work)
+{
+       struct cs42l56_private *cs42l56 =
+               container_of(work, struct cs42l56_private, beep_work);
+       struct snd_soc_codec *codec = cs42l56->codec;
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       int i;
+       int val = 0;
+       int best = 0;
+
+       if (cs42l56->beep_rate) {
+               for (i = 0; i < ARRAY_SIZE(beep_freq); i++) {
+                       if (abs(cs42l56->beep_rate - beep_freq[i]) <
+                           abs(cs42l56->beep_rate - beep_freq[best]))
+                               best = i;
+               }
+
+               dev_dbg(codec->dev, "Set beep rate %dHz for requested %dHz\n",
+                       beep_freq[best], cs42l56->beep_rate);
+
+               val = (best << CS42L56_BEEP_RATE_SHIFT);
+
+               snd_soc_dapm_enable_pin(dapm, "Beep");
+       } else {
+               dev_dbg(codec->dev, "Disabling beep\n");
+               snd_soc_dapm_disable_pin(dapm, "Beep");
+       }
+
+       snd_soc_update_bits(codec, CS42L56_BEEP_FREQ_ONTIME,
+                           CS42L56_BEEP_FREQ_MASK, val);
+
+       snd_soc_dapm_sync(dapm);
+}
+
+/* For usability define a way of injecting beep events for the device -
+ * many systems will not have a keyboard.
+ */
+static int cs42l56_beep_event(struct input_dev *dev, unsigned int type,
+                            unsigned int code, int hz)
+{
+       struct snd_soc_codec *codec = input_get_drvdata(dev);
+       struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+
+       dev_dbg(codec->dev, "Beep event %x %x\n", code, hz);
+
+       switch (code) {
+       case SND_BELL:
+               if (hz)
+                       hz = 261;
+       case SND_TONE:
+               break;
+       default:
+               return -1;
+       }
+
+       /* Kick the beep from a workqueue */
+       cs42l56->beep_rate = hz;
+       schedule_work(&cs42l56->beep_work);
+       return 0;
+}
+
+static ssize_t cs42l56_beep_set(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct cs42l56_private *cs42l56 = dev_get_drvdata(dev);
+       long int time;
+       int ret;
+
+       ret = kstrtol(buf, 10, &time);
+       if (ret != 0)
+               return ret;
+
+       input_event(cs42l56->beep, EV_SND, SND_TONE, time);
+
+       return count;
+}
+
+static DEVICE_ATTR(beep, 0200, NULL, cs42l56_beep_set);
+
+static void cs42l56_init_beep(struct snd_soc_codec *codec)
+{
+       struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       cs42l56->beep = devm_input_allocate_device(codec->dev);
+       if (!cs42l56->beep) {
+               dev_err(codec->dev, "Failed to allocate beep device\n");
+               return;
+       }
+
+       INIT_WORK(&cs42l56->beep_work, cs42l56_beep_work);
+       cs42l56->beep_rate = 0;
+
+       cs42l56->beep->name = "CS42L56 Beep Generator";
+       cs42l56->beep->phys = dev_name(codec->dev);
+       cs42l56->beep->id.bustype = BUS_I2C;
+
+       cs42l56->beep->evbit[0] = BIT_MASK(EV_SND);
+       cs42l56->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+       cs42l56->beep->event = cs42l56_beep_event;
+       cs42l56->beep->dev.parent = codec->dev;
+       input_set_drvdata(cs42l56->beep, codec);
+
+       ret = input_register_device(cs42l56->beep);
+       if (ret != 0) {
+               cs42l56->beep = NULL;
+               dev_err(codec->dev, "Failed to register beep device\n");
+       }
+
+       ret = device_create_file(codec->dev, &dev_attr_beep);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to create keyclick file: %d\n",
+                       ret);
+       }
+}
+
+static void cs42l56_free_beep(struct snd_soc_codec *codec)
+{
+       struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+
+       device_remove_file(codec->dev, &dev_attr_beep);
+       cancel_work_sync(&cs42l56->beep_work);
+       cs42l56->beep = NULL;
+
+       snd_soc_update_bits(codec, CS42L56_BEEP_TONE_CFG,
+                           CS42L56_BEEP_EN_MASK, 0);
+}
+
+static int cs42l56_probe(struct snd_soc_codec *codec)
+{
+       cs42l56_init_beep(codec);
+
+       cs42l56_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+
+static int cs42l56_remove(struct snd_soc_codec *codec)
+{
+       struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+
+       cs42l56_free_beep(codec);
+       cs42l56_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       regulator_bulk_free(ARRAY_SIZE(cs42l56->supplies), cs42l56->supplies);
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs42l56 = {
+       .probe = cs42l56_probe,
+       .remove = cs42l56_remove,
+       .suspend = cs42l56_suspend,
+       .resume = cs42l56_resume,
+       .set_bias_level = cs42l56_set_bias_level,
+
+       .dapm_widgets = cs42l56_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(cs42l56_dapm_widgets),
+       .dapm_routes = cs42l56_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(cs42l56_audio_map),
+
+       .controls = cs42l56_snd_controls,
+       .num_controls = ARRAY_SIZE(cs42l56_snd_controls),
+};
+
+static struct regmap_config cs42l56_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = CS42L56_MAX_REGISTER,
+       .reg_defaults = cs42l56_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(cs42l56_reg_defaults),
+       .readable_reg = cs42l56_readable_register,
+       .volatile_reg = cs42l56_volatile_register,
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
+                                   struct cs42l56_platform_data *pdata)
+{
+       struct device_node *np = i2c_client->dev.of_node;
+       u32 val32;
+
+       if (of_property_read_bool(np, "cirrus,ain1a-reference-cfg"))
+               pdata->ain1a_ref_cfg = true;
+
+       if (of_property_read_bool(np, "cirrus,ain2a-reference-cfg"))
+               pdata->ain2a_ref_cfg = true;
+
+       if (of_property_read_bool(np, "cirrus,ain1b-reference-cfg"))
+               pdata->ain1b_ref_cfg = true;
+
+       if (of_property_read_bool(np, "cirrus,ain2b-reference-cfg"))
+               pdata->ain2b_ref_cfg = true;
+
+       if (of_property_read_u32(np, "cirrus,micbias-lvl", &val32) >= 0)
+               pdata->micbias_lvl = val32;
+
+       if (of_property_read_u32(np, "cirrus,chgfreq-divisor", &val32) >= 0)
+               pdata->chgfreq = val32;
+
+       if (of_property_read_u32(np, "cirrus,adaptive-pwr-cfg", &val32) >= 0)
+               pdata->adaptive_pwr = val32;
+
+       if (of_property_read_u32(np, "cirrus,hpf-left-freq", &val32) >= 0)
+               pdata->hpfa_freq = val32;
+
+       if (of_property_read_u32(np, "cirrus,hpf-left-freq", &val32) >= 0)
+               pdata->hpfb_freq = val32;
+
+       pdata->gpio_nreset = of_get_named_gpio(np, "cirrus,gpio-nreset", 0);
+
+       return 0;
+}
+
+static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
+                            const struct i2c_device_id *id)
+{
+       struct cs42l56_private *cs42l56;
+       struct cs42l56_platform_data *pdata =
+               dev_get_platdata(&i2c_client->dev);
+       int ret, i;
+       unsigned int devid = 0;
+       unsigned int alpha_rev, metal_rev;
+       unsigned int reg;
+
+       cs42l56 = devm_kzalloc(&i2c_client->dev,
+                              sizeof(struct cs42l56_private),
+                              GFP_KERNEL);
+       if (cs42l56 == NULL)
+               return -ENOMEM;
+       cs42l56->dev = &i2c_client->dev;
+
+       cs42l56->regmap = devm_regmap_init_i2c(i2c_client, &cs42l56_regmap);
+       if (IS_ERR(cs42l56->regmap)) {
+               ret = PTR_ERR(cs42l56->regmap);
+               dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+               return ret;
+       }
+
+       if (pdata) {
+               cs42l56->pdata = *pdata;
+       } else {
+               pdata = devm_kzalloc(&i2c_client->dev,
+                                    sizeof(struct cs42l56_platform_data),
+                                    GFP_KERNEL);
+               if (!pdata) {
+                       dev_err(&i2c_client->dev,
+                               "could not allocate pdata\n");
+                       return -ENOMEM;
+               }
+               if (i2c_client->dev.of_node) {
+                       ret = cs42l56_handle_of_data(i2c_client,
+                                                    &cs42l56->pdata);
+                       if (ret != 0)
+                               return ret;
+               }
+               cs42l56->pdata = *pdata;
+       }
+
+       if (cs42l56->pdata.gpio_nreset) {
+               ret = gpio_request_one(cs42l56->pdata.gpio_nreset,
+                                      GPIOF_OUT_INIT_HIGH, "CS42L56 /RST");
+               if (ret < 0) {
+                       dev_err(&i2c_client->dev,
+                               "Failed to request /RST %d: %d\n",
+                               cs42l56->pdata.gpio_nreset, ret);
+                       return ret;
+               }
+               gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 0);
+               gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 1);
+       }
+
+
+       i2c_set_clientdata(i2c_client, cs42l56);
+
+       for (i = 0; i < ARRAY_SIZE(cs42l56->supplies); i++)
+               cs42l56->supplies[i].supply = cs42l56_supply_names[i];
+
+       ret = devm_regulator_bulk_get(&i2c_client->dev,
+                                     ARRAY_SIZE(cs42l56->supplies),
+                                     cs42l56->supplies);
+       if (ret != 0) {
+               dev_err(&i2c_client->dev,
+                       "Failed to request supplies: %d\n", ret);
+               return ret;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies),
+                                   cs42l56->supplies);
+       if (ret != 0) {
+               dev_err(&i2c_client->dev,
+                       "Failed to enable supplies: %d\n", ret);
+               return ret;
+       }
+
+       regcache_cache_bypass(cs42l56->regmap, true);
+
+       ret = regmap_read(cs42l56->regmap, CS42L56_CHIP_ID_1, &reg);
+       devid = reg & CS42L56_CHIP_ID_MASK;
+       if (devid != CS42L56_DEVID) {
+               dev_err(&i2c_client->dev,
+                       "CS42L56 Device ID (%X). Expected %X\n",
+                       devid, CS42L56_DEVID);
+               goto err_enable;
+       }
+       alpha_rev = reg & CS42L56_AREV_MASK;
+       metal_rev = reg & CS42L56_MTLREV_MASK;
+
+       dev_info(&i2c_client->dev, "Cirrus Logic CS42L56 ");
+       dev_info(&i2c_client->dev, "Alpha Rev %X Metal Rev %X\n",
+                alpha_rev, metal_rev);
+
+       regcache_cache_bypass(cs42l56->regmap, false);
+
+       if (cs42l56->pdata.ain1a_ref_cfg)
+               regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
+                                  CS42L56_AIN1A_REF_MASK, 1);
+
+       if (cs42l56->pdata.ain1b_ref_cfg)
+               regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
+                                  CS42L56_AIN1B_REF_MASK, 1);
+
+       if (cs42l56->pdata.ain2a_ref_cfg)
+               regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
+                                  CS42L56_AIN2A_REF_MASK, 1);
+
+       if (cs42l56->pdata.ain2b_ref_cfg)
+               regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
+                                  CS42L56_AIN2B_REF_MASK, 1);
+
+       if (cs42l56->pdata.micbias_lvl)
+               regmap_update_bits(cs42l56->regmap, CS42L56_GAIN_BIAS_CTL,
+                                  CS42L56_MIC_BIAS_MASK,
+                               cs42l56->pdata.micbias_lvl);
+
+       if (cs42l56->pdata.chgfreq)
+               regmap_update_bits(cs42l56->regmap, CS42L56_CLASSH_CTL,
+                                  CS42L56_CHRG_FREQ_MASK,
+                               cs42l56->pdata.chgfreq);
+
+       if (cs42l56->pdata.hpfb_freq)
+               regmap_update_bits(cs42l56->regmap, CS42L56_HPF_CTL,
+                                  CS42L56_HPFB_FREQ_MASK,
+                               cs42l56->pdata.hpfb_freq);
+
+       if (cs42l56->pdata.hpfa_freq)
+               regmap_update_bits(cs42l56->regmap, CS42L56_HPF_CTL,
+                                  CS42L56_HPFA_FREQ_MASK,
+                               cs42l56->pdata.hpfa_freq);
+
+       if (cs42l56->pdata.adaptive_pwr)
+               regmap_update_bits(cs42l56->regmap, CS42L56_CLASSH_CTL,
+                                  CS42L56_ADAPT_PWR_MASK,
+                               cs42l56->pdata.adaptive_pwr);
+
+       ret =  snd_soc_register_codec(&i2c_client->dev,
+                       &soc_codec_dev_cs42l56, &cs42l56_dai, 1);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+
+err_enable:
+       regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies),
+                              cs42l56->supplies);
+       return ret;
+}
+
+static int cs42l56_i2c_remove(struct i2c_client *client)
+{
+       struct cs42l56_private *cs42l56 = i2c_get_clientdata(client);
+
+       snd_soc_unregister_codec(&client->dev);
+       regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies),
+                              cs42l56->supplies);
+       return 0;
+}
+
+static const struct of_device_id cs42l56_of_match[] = {
+       { .compatible = "cirrus,cs42l56", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, cs42l56_of_match);
+
+
+static const struct i2c_device_id cs42l56_id[] = {
+       { "cs42l56", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, cs42l56_id);
+
+static struct i2c_driver cs42l56_i2c_driver = {
+       .driver = {
+               .name = "cs42l56",
+               .owner = THIS_MODULE,
+               .of_match_table = cs42l56_of_match,
+       },
+       .id_table = cs42l56_id,
+       .probe =    cs42l56_i2c_probe,
+       .remove =   cs42l56_i2c_remove,
+};
+
+module_i2c_driver(cs42l56_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L56 driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l56.h b/sound/soc/codecs/cs42l56.h
new file mode 100644 (file)
index 0000000..5025ec9
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * cs42l52.h -- CS42L56 ALSA SoC audio driver
+ *
+ * Copyright 2014 CirrusLogic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.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 __CS42L56_H__
+#define __CS42L56_H__
+
+#define CS42L56_CHIP_ID_1              0x01
+#define CS42L56_CHIP_ID_2              0x02
+#define CS42L56_PWRCTL_1               0x03
+#define CS42L56_PWRCTL_2               0x04
+#define CS42L56_CLKCTL_1               0x05
+#define CS42L56_CLKCTL_2               0x06
+#define CS42L56_SERIAL_FMT             0x07
+#define CS42L56_CLASSH_CTL             0x08
+#define CS42L56_MISC_CTL               0x09
+#define CS42L56_INT_STATUS             0x0a
+#define CS42L56_PLAYBACK_CTL           0x0b
+#define CS42L56_DSP_MUTE_CTL           0x0c
+#define CS42L56_ADCA_MIX_VOLUME                0x0d
+#define CS42L56_ADCB_MIX_VOLUME                0x0e
+#define CS42L56_PCMA_MIX_VOLUME                0x0f
+#define CS42L56_PCMB_MIX_VOLUME                0x10
+#define CS42L56_ANAINPUT_ADV_VOLUME    0x11
+#define CS42L56_DIGINPUT_ADV_VOLUME    0x12
+#define CS42L56_MASTER_A_VOLUME                0x13
+#define CS42L56_MASTER_B_VOLUME                0x14
+#define CS42L56_BEEP_FREQ_ONTIME       0x15
+#define CS42L56_BEEP_FREQ_OFFTIME      0x16
+#define CS42L56_BEEP_TONE_CFG          0x17
+#define CS42L56_TONE_CTL               0x18
+#define CS42L56_CHAN_MIX_SWAP          0x19
+#define CS42L56_AIN_REFCFG_ADC_MUX     0x1a
+#define CS42L56_HPF_CTL                        0x1b
+#define CS42L56_MISC_ADC_CTL           0x1c
+#define CS42L56_GAIN_BIAS_CTL          0x1d
+#define CS42L56_PGAA_MUX_VOLUME                0x1e
+#define CS42L56_PGAB_MUX_VOLUME                0x1f
+#define CS42L56_ADCA_ATTENUATOR                0x20
+#define CS42L56_ADCB_ATTENUATOR                0x21
+#define CS42L56_ALC_EN_ATTACK_RATE     0x22
+#define CS42L56_ALC_RELEASE_RATE       0x23
+#define CS42L56_ALC_THRESHOLD          0x24
+#define CS42L56_NOISE_GATE_CTL         0x25
+#define CS42L56_ALC_LIM_SFT_ZC         0x26
+#define CS42L56_AMUTE_HPLO_MUX         0x27
+#define CS42L56_HPA_VOLUME             0x28
+#define CS42L56_HPB_VOLUME             0x29
+#define CS42L56_LOA_VOLUME             0x2a
+#define CS42L56_LOB_VOLUME             0x2b
+#define CS42L56_LIM_THRESHOLD_CTL      0x2c
+#define CS42L56_LIM_CTL_RELEASE_RATE   0x2d
+#define CS42L56_LIM_ATTACK_RATE                0x2e
+
+/* Device ID and Rev ID Masks */
+#define CS42L56_DEVID                  0x56
+#define CS42L56_CHIP_ID_MASK           0xff
+#define CS42L56_AREV_MASK              0x1c
+#define CS42L56_MTLREV_MASK            0x03
+
+/* Power bit masks */
+#define CS42L56_PDN_ALL_MASK           0x01
+#define CS42L56_PDN_ADCA_MASK          0x02
+#define CS42L56_PDN_ADCB_MASK          0x04
+#define CS42L56_PDN_CHRG_MASK          0x08
+#define CS42L56_PDN_BIAS_MASK          0x10
+#define CS42L56_PDN_VBUF_MASK          0x20
+#define CS42L56_PDN_LOA_MASK           0x03
+#define CS42L56_PDN_LOB_MASK           0x0c
+#define CS42L56_PDN_HPA_MASK           0x30
+#define CS42L56_PDN_HPB_MASK           0xc0
+
+/* serial port and clk masks */
+#define CS42L56_MASTER_MODE            0x40
+#define CS42L56_SLAVE_MODE             0
+#define CS42L56_MS_MODE_MASK           0x40
+#define CS42L56_SCLK_INV               0x20
+#define CS42L56_SCLK_INV_MASK          0x20
+#define CS42L56_SCLK_MCLK_MASK         0x18
+#define CS42L56_MCLK_PREDIV            0x04
+#define CS42L56_MCLK_PREDIV_MASK       0x04
+#define CS42L56_MCLK_DIV2              0x02
+#define CS42L56_MCLK_DIV2_MASK         0x02
+#define CS42L56_MCLK_DIS_MASK          0x01
+#define CS42L56_CLK_AUTO_MASK          0x20
+#define CS42L56_CLK_RATIO_MASK         0x1f
+#define CS42L56_DIG_FMT_I2S            0
+#define CS42L56_DIG_FMT_LEFT_J         0x08
+#define CS42L56_DIG_FMT_MASK           0x08
+
+/* Class H and misc ctl masks */
+#define CS42L56_ADAPT_PWR_MASK         0xc0
+#define CS42L56_CHRG_FREQ_MASK         0x0f
+#define CS42L56_DIG_MUX_MASK           0x80
+#define CS42L56_ANLGSFT_MASK           0x10
+#define CS42L56_ANLGZC_MASK            0x08
+#define CS42L56_DIGSFT_MASK            0x04
+#define CS42L56_FREEZE_MASK            0x01
+#define CS42L56_MIC_BIAS_MASK          0x03
+#define CS42L56_HPFA_FREQ_MASK         0x03
+#define CS42L56_HPFB_FREQ_MASK         0xc0
+#define CS42L56_AIN1A_REF_MASK         0x10
+#define CS42L56_AIN2A_REF_MASK         0x40
+#define CS42L56_AIN1B_REF_MASK         0x20
+#define CS42L56_AIN2B_REF_MASK         0x80
+
+/* Playback Capture ctl masks */
+#define CS42L56_PDN_DSP_MASK           0x80
+#define CS42L56_DEEMPH_MASK            0x40
+#define CS42L56_PLYBCK_GANG_MASK       0x10
+#define CS42L56_PCM_INV_MASK           0x0c
+#define CS42L56_MUTE_ALL               0xff
+#define CS42L56_UNMUTE                 0
+#define CS42L56_ADCAMIX_MUTE_MASK      0x40
+#define CS42L56_ADCBMIX_MUTE_MASK      0x80
+#define CS42L56_PCMAMIX_MUTE_MASK      0x10
+#define CS42L56_PCMBMIX_MUTE_MASK      0x20
+#define CS42L56_MSTB_MUTE_MASK         0x02
+#define CS42L56_MSTA_MUTE_MASK         0x01
+#define CS42L56_ADCA_MUTE_MASK         0x01
+#define CS42L56_ADCB_MUTE_MASK         0x02
+#define CS42L56_HP_MUTE_MASK           0x80
+#define CS42L56_LO_MUTE_MASK           0x80
+
+/* Beep masks */
+#define CS42L56_BEEP_FREQ_MASK         0xf0
+#define CS42L56_BEEP_ONTIME_MASK       0x0f
+#define CS42L56_BEEP_OFFTIME_MASK      0xe0
+#define CS42L56_BEEP_CFG_MASK          0xc0
+#define CS42L56_BEEP_TREBCF_MASK       0x18
+#define CS42L56_BEEP_BASSCF_MASK       0x06
+#define CS42L56_BEEP_TCEN_MASK         0x01
+#define CS42L56_BEEP_RATE_SHIFT                4
+#define CS42L56_BEEP_EN_MASK           0x3f
+
+
+/* Supported MCLKS */
+#define CS42L56_MCLK_5P6448MHZ         5644800
+#define CS42L56_MCLK_6MHZ              6000000
+#define CS42L56_MCLK_6P144MHZ          6144000
+#define CS42L56_MCLK_11P2896MHZ                11289600
+#define CS42L56_MCLK_12MHZ             12000000
+#define CS42L56_MCLK_12P288MHZ         12288000
+#define CS42L56_MCLK_22P5792MHZ                22579200
+#define CS42L56_MCLK_24MHZ             24000000
+#define CS42L56_MCLK_24P576MHZ         24576000
+
+/* Clock ratios */
+#define CS42L56_MCLK_LRCLK_128         0x08
+#define CS42L56_MCLK_LRCLK_125         0x09
+#define CS42L56_MCLK_LRCLK_136         0x0b
+#define CS42L56_MCLK_LRCLK_192         0x0c
+#define CS42L56_MCLK_LRCLK_187P5       0x0d
+#define CS42L56_MCLK_LRCLK_256         0x10
+#define CS42L56_MCLK_LRCLK_250         0x11
+#define CS42L56_MCLK_LRCLK_272         0x13
+#define CS42L56_MCLK_LRCLK_384         0x14
+#define CS42L56_MCLK_LRCLK_375         0x15
+#define CS42L56_MCLK_LRCLK_512         0x18
+#define CS42L56_MCLK_LRCLK_500         0x19
+#define CS42L56_MCLK_LRCLK_544         0x1b
+#define CS42L56_MCLK_LRCLK_750         0x1c
+#define CS42L56_MCLK_LRCLK_768         0x1d
+
+
+#define CS42L56_MAX_REGISTER           0x34
+
+#endif
index 8502032..a25bc60 100644 (file)
@@ -248,8 +248,7 @@ static int cs42xx8_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_codec *codec = rtd->codec;
+       struct snd_soc_codec *codec = dai->codec;
        struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
        bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        u32 ratio = cs42xx8->sysclk / params_rate(params);
index 137e8eb..21810e5 100644 (file)
@@ -335,7 +335,7 @@ static SOC_ENUM_SINGLE_DECL(da7210_hp_mode_sel,
 static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
 
        if (ucontrol->value.integer.value[0]) {
                /* Check if noise suppression is enabled */
@@ -358,7 +358,7 @@ static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol,
 static int da7210_put_noise_sup_sw(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        u8 val;
 
        if (ucontrol->value.integer.value[0]) {
index 738fa18..9ec577f 100644 (file)
@@ -345,7 +345,7 @@ static void da7213_alc_calib(struct snd_soc_codec *codec)
 static int da7213_put_mixin_gain(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
        int ret;
 
@@ -361,7 +361,7 @@ static int da7213_put_mixin_gain(struct snd_kcontrol *kcontrol,
 static int da7213_put_alc_sw(struct snd_kcontrol *kcontrol,
                            struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
 
        /* Force ALC offset calibration if enabling ALC */
index 48f3fef..2fae31c 100644 (file)
@@ -332,7 +332,7 @@ static SOC_ENUM_SINGLE_DECL(da732x_adc2_voice_filter_enum,
 static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value;
        unsigned int reg = enum_ctrl->reg;
        unsigned int sel = ucontrol->value.integer.value[0];
@@ -360,7 +360,7 @@ static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
 static int da732x_hpf_get(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value;
        unsigned int reg = enum_ctrl->reg;
        int val;
index 4ff06b5..ad19cc5 100644 (file)
@@ -484,7 +484,7 @@ static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
 static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        u8 reg_val, adc_left, adc_right, mic_left, mic_right;
        int avg_left_data, avg_right_data, offset_l, offset_r;
 
index 9cb1c7d..1087fd5 100644 (file)
@@ -20,6 +20,7 @@
  */
 #include <linux/module.h>
 #include <sound/soc.h>
+#include <linux/of.h>
 #include <linux/of_device.h>
 
 #define DRV_NAME "hdmi-audio-codec"
index 4f048db..a924bb9 100644 (file)
@@ -49,7 +49,7 @@ static const struct reg_default lm4857_default_regs[] = {
 static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.integer.value[0] = lm4857->mode;
@@ -60,7 +60,7 @@ static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
 static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
        uint8_t value = ucontrol->value.integer.value[0];
 
index ec481fc..e1c196a 100644 (file)
@@ -43,7 +43,7 @@ static struct reg_default max9768_default_regs[] = {
 static int max9768_get_gpio(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec);
        int val = gpio_get_value_cansleep(max9768->mute_gpio);
 
@@ -55,7 +55,7 @@ static int max9768_get_gpio(struct snd_kcontrol *kcontrol,
 static int max9768_set_gpio(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec);
 
        gpio_set_value_cansleep(max9768->mute_gpio, !ucontrol->value.integer.value[0]);
index ef7cf89..9134982 100644 (file)
@@ -635,7 +635,7 @@ static SOC_ENUM_SINGLE_DECL(max98088_dai1_adc_filter_enum,
 static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
        unsigned int sel = ucontrol->value.integer.value[0];
 
@@ -649,7 +649,7 @@ static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
 static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.integer.value[0] = max98088->mic1pre;
@@ -659,7 +659,7 @@ static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
 static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
        unsigned int sel = ucontrol->value.integer.value[0];
 
@@ -673,7 +673,7 @@ static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
 static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.integer.value[0] = max98088->mic2pre;
@@ -1750,7 +1750,7 @@ static void max98088_setup_eq2(struct snd_soc_codec *codec)
 static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
        struct max98088_pdata *pdata = max98088->pdata;
        int channel = max98088_get_channel(codec, kcontrol->id.name);
@@ -1782,7 +1782,7 @@ static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol,
 static int max98088_get_eq_enum(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
        int channel = max98088_get_channel(codec, kcontrol->id.name);
        struct max98088_cdata *cdata;
index f7b0b37..f5fccc7 100644 (file)
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/clk.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -255,6 +258,7 @@ static struct reg_default max98090_reg[] = {
 static bool max98090_volatile_register(struct device *dev, unsigned int reg)
 {
        switch (reg) {
+       case M98090_REG_SOFTWARE_RESET:
        case M98090_REG_DEVICE_STATUS:
        case M98090_REG_JACK_STATUS:
        case M98090_REG_REVISION_ID:
@@ -389,6 +393,7 @@ static const DECLARE_TLV_DB_SCALE(max98090_alc_tlv, -1500, 100, 0);
 static const DECLARE_TLV_DB_SCALE(max98090_alcmakeup_tlv, 0, 100, 0);
 static const DECLARE_TLV_DB_SCALE(max98090_alccomp_tlv, -3100, 100, 0);
 static const DECLARE_TLV_DB_SCALE(max98090_drcexp_tlv, -6600, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98090_sdg_tlv, 50, 200, 0);
 
 static const unsigned int max98090_mixout_tlv[] = {
        TLV_DB_RANGE_HEAD(2),
@@ -426,7 +431,7 @@ static const unsigned int max98090_rcv_lout_tlv[] = {
 static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
@@ -466,7 +471,7 @@ static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol,
 static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
@@ -665,7 +670,7 @@ static const struct snd_kcontrol_new max98090_snd_controls[] = {
        SOC_SINGLE_EXT_TLV("Digital Sidetone Volume",
                M98090_REG_ADC_SIDETONE, M98090_DVST_SHIFT,
                M98090_DVST_NUM - 1, 1, max98090_get_enab_tlv,
-               max98090_put_enab_tlv, max98090_micboost_tlv),
+               max98090_put_enab_tlv, max98090_sdg_tlv),
        SOC_SINGLE_TLV("Digital Coarse Volume", M98090_REG_DAI_PLAYBACK_LEVEL,
                M98090_DVG_SHIFT, M98090_DVG_NUM - 1, 0,
                max98090_dvg_tlv),
@@ -875,7 +880,7 @@ static const char *dmic_mux_text[] = { "ADC", "DMIC" };
 static SOC_ENUM_SINGLE_VIRT_DECL(dmic_mux_enum, dmic_mux_text);
 
 static const struct snd_kcontrol_new max98090_dmic_mux =
-       SOC_DAPM_ENUM_VIRT("DMIC Mux", dmic_mux_enum);
+       SOC_DAPM_ENUM("DMIC Mux", dmic_mux_enum);
 
 static const char *max98090_micpre_text[] = { "Off", "On" };
 
@@ -1175,8 +1180,7 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
        SND_SOC_DAPM_MUX("MIC2 Mux", SND_SOC_NOPM,
                0, 0, &max98090_mic2_mux),
 
-       SND_SOC_DAPM_VIRT_MUX("DMIC Mux", SND_SOC_NOPM,
-               0, 0, &max98090_dmic_mux),
+       SND_SOC_DAPM_MUX("DMIC Mux", SND_SOC_NOPM, 0, 0, &max98090_dmic_mux),
 
        SND_SOC_DAPM_PGA_E("MIC1 Input", M98090_REG_MIC1_INPUT_LEVEL,
                M98090_MIC_PA1EN_SHIFT, 0, NULL, 0, max98090_micinput_event,
@@ -1544,19 +1548,19 @@ static const int lrclk_rates[] = {
 };
 
 static const int user_pclk_rates[] = {
-       13000000, 13000000
+       13000000, 13000000, 19200000, 19200000,
 };
 
 static const int user_lrclk_rates[] = {
-       44100, 48000
+       44100, 48000, 44100, 48000,
 };
 
 static const unsigned long long ni_value[] = {
-       3528, 768
+       3528, 768, 441, 8
 };
 
 static const unsigned long long mi_value[] = {
-       8125, 1625
+       8125, 1625, 1500, 25
 };
 
 static void max98090_configure_bclk(struct snd_soc_codec *codec)
@@ -1673,6 +1677,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
                                M98090_REG_CLOCK_RATIO_NI_LSB, 0x00);
                        snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
                                M98090_USE_M1_MASK, 0);
+                       max98090->master = false;
                        break;
                case SND_SOC_DAIFMT_CBM_CFM:
                        /* Set to master mode */
@@ -1689,6 +1694,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
                                regval |= M98090_MAS_MASK |
                                        M98090_BSEL_32;
                        }
+                       max98090->master = true;
                        break;
                case SND_SOC_DAIFMT_CBS_CFM:
                case SND_SOC_DAIFMT_CBM_CFS:
@@ -1792,16 +1798,22 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
 
        switch (level) {
        case SND_SOC_BIAS_ON:
-               if (max98090->jack_state == M98090_JACK_STATE_HEADSET) {
-                       /*
-                        * Set to normal bias level.
-                        */
-                       snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
-                               M98090_MBVSEL_MASK, M98090_MBVSEL_2V8);
-               }
                break;
 
        case SND_SOC_BIAS_PREPARE:
+               /*
+                * SND_SOC_BIAS_PREPARE is called while preparing for a
+                * transition to ON or away from ON. If current bias_level
+                * is SND_SOC_BIAS_ON, then it is preparing for a transition
+                * away from ON. Disable the clock in that case, otherwise
+                * enable it.
+                */
+               if (!IS_ERR(max98090->mclk)) {
+                       if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
+                               clk_disable_unprepare(max98090->mclk);
+                       else
+                               clk_prepare_enable(max98090->mclk);
+               }
                break;
 
        case SND_SOC_BIAS_STANDBY:
@@ -1872,7 +1884,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       max98090_configure_bclk(codec);
+       if (max98090->master)
+               max98090_configure_bclk(codec);
 
        cdata->rate = max98090->lrclk;
 
@@ -1930,6 +1943,11 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
        if (freq == max98090->sysclk)
                return 0;
 
+       if (!IS_ERR(max98090->mclk)) {
+               freq = clk_round_rate(max98090->mclk, freq);
+               clk_set_rate(max98090->mclk, freq);
+       }
+
        /* Setup clocks for slave mode, and using the PLL
         * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
         *               0x02 (when master clk is 20MHz to 40MHz)..
@@ -1951,8 +1969,6 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
 
        max98090->sysclk = freq;
 
-       max98090_configure_bclk(codec);
-
        return 0;
 }
 
@@ -2216,6 +2232,10 @@ static int max98090_probe(struct snd_soc_codec *codec)
 
        dev_dbg(codec->dev, "max98090_probe\n");
 
+       max98090->mclk = devm_clk_get(codec->dev, "mclk");
+       if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
        max98090->codec = codec;
 
        /* Reset the codec, the DSP core, and disable all interrupts */
@@ -2224,6 +2244,7 @@ static int max98090_probe(struct snd_soc_codec *codec)
        /* Initialize private data */
 
        max98090->sysclk = (unsigned)-1;
+       max98090->master = false;
 
        cdata = &max98090->dai[0];
        cdata->rate = (unsigned)-1;
@@ -2293,6 +2314,9 @@ static int max98090_probe(struct snd_soc_codec *codec)
        snd_soc_write(codec, M98090_REG_BIAS_CONTROL,
                M98090_VCM_MODE_MASK);
 
+       snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
+               M98090_MBVSEL_MASK, M98090_MBVSEL_2V8);
+
        max98090_handle_pdata(codec);
 
        max98090_add_widgets(codec);
@@ -2329,9 +2353,11 @@ static const struct regmap_config max98090_regmap = {
 };
 
 static int max98090_i2c_probe(struct i2c_client *i2c,
-                                const struct i2c_device_id *id)
+                                const struct i2c_device_id *i2c_id)
 {
        struct max98090_priv *max98090;
+       const struct acpi_device_id *acpi_id;
+       kernel_ulong_t driver_data = 0;
        int ret;
 
        pr_debug("max98090_i2c_probe\n");
@@ -2341,7 +2367,19 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
        if (max98090 == NULL)
                return -ENOMEM;
 
-       max98090->devtype = id->driver_data;
+       if (ACPI_HANDLE(&i2c->dev)) {
+               acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table,
+                                           &i2c->dev);
+               if (!acpi_id) {
+                       dev_err(&i2c->dev, "No driver data\n");
+                       return -EINVAL;
+               }
+               driver_data = acpi_id->driver_data;
+       } else if (i2c_id) {
+               driver_data = i2c_id->driver_data;
+       }
+
+       max98090->devtype = driver_data;
        i2c_set_clientdata(i2c, max98090);
        max98090->pdata = i2c->dev.platform_data;
        max98090->irq = i2c->irq;
@@ -2373,6 +2411,8 @@ static int max98090_runtime_resume(struct device *dev)
 
        regcache_cache_only(max98090->regmap, false);
 
+       max98090_reset(max98090);
+
        regcache_sync(max98090->regmap);
 
        return 0;
@@ -2388,9 +2428,34 @@ static int max98090_runtime_suspend(struct device *dev)
 }
 #endif
 
+#ifdef CONFIG_PM
+static int max98090_resume(struct device *dev)
+{
+       struct max98090_priv *max98090 = dev_get_drvdata(dev);
+       unsigned int status;
+
+       regcache_mark_dirty(max98090->regmap);
+
+       max98090_reset(max98090);
+
+       /* clear IRQ status */
+       regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &status);
+
+       regcache_sync(max98090->regmap);
+
+       return 0;
+}
+
+static int max98090_suspend(struct device *dev)
+{
+       return 0;
+}
+#endif
+
 static const struct dev_pm_ops max98090_pm = {
        SET_RUNTIME_PM_OPS(max98090_runtime_suspend,
                max98090_runtime_resume, NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(max98090_suspend, max98090_resume)
 };
 
 static const struct i2c_device_id max98090_i2c_id[] = {
@@ -2405,12 +2470,21 @@ static const struct of_device_id max98090_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, max98090_of_match);
 
+#ifdef CONFIG_ACPI
+static struct acpi_device_id max98090_acpi_match[] = {
+       { "193C9890", MAX98090 },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, max98090_acpi_match);
+#endif
+
 static struct i2c_driver max98090_i2c_driver = {
        .driver = {
                .name = "max98090",
                .owner = THIS_MODULE,
                .pm = &max98090_pm,
                .of_match_table = of_match_ptr(max98090_of_match),
+               .acpi_match_table = ACPI_PTR(max98090_acpi_match),
        },
        .probe  = max98090_i2c_probe,
        .remove = max98090_i2c_remove,
index 1a4e233..cf1b606 100644 (file)
@@ -1524,6 +1524,7 @@ struct max98090_priv {
        struct snd_soc_codec *codec;
        enum max98090_type devtype;
        struct max98090_pdata *pdata;
+       struct clk *mclk;
        unsigned int sysclk;
        unsigned int bclk;
        unsigned int lrclk;
@@ -1540,6 +1541,7 @@ struct max98090_priv {
        unsigned int pa2en;
        unsigned int extmic_mux;
        unsigned int sidetone;
+       bool master;
 };
 
 int max98090_mic_detect(struct snd_soc_codec *codec,
index 03f0536..89ec004 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
+#include <linux/clk.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -42,6 +43,7 @@ struct max98095_priv {
        struct regmap *regmap;
        enum max98095_type devtype;
        struct max98095_pdata *pdata;
+       struct clk *mclk;
        unsigned int sysclk;
        struct max98095_cdata dai[3];
        const char **eq_texts;
@@ -612,7 +614,7 @@ static SOC_ENUM_SINGLE_DECL(max98095_dai3_dac_filter_enum,
 static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
        unsigned int sel = ucontrol->value.integer.value[0];
 
@@ -626,7 +628,7 @@ static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol,
 static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.integer.value[0] = max98095->mic1pre;
@@ -636,7 +638,7 @@ static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol,
 static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
        unsigned int sel = ucontrol->value.integer.value[0];
 
@@ -650,7 +652,7 @@ static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol,
 static int max98095_mic2pre_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.integer.value[0] = max98095->mic2pre;
@@ -1395,6 +1397,11 @@ static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
        if (freq == max98095->sysclk)
                return 0;
 
+       if (!IS_ERR(max98095->mclk)) {
+               freq = clk_round_rate(max98095->mclk, freq);
+               clk_set_rate(max98095->mclk, freq);
+       }
+
        /* Setup clocks for slave mode, and using the PLL
         * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
         *         0x02 (when master clk is 20MHz to 40MHz)..
@@ -1634,6 +1641,19 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec,
                break;
 
        case SND_SOC_BIAS_PREPARE:
+               /*
+                * SND_SOC_BIAS_PREPARE is called while preparing for a
+                * transition to ON or away from ON. If current bias_level
+                * is SND_SOC_BIAS_ON, then it is preparing for a transition
+                * away from ON. Disable the clock in that case, otherwise
+                * enable it.
+                */
+               if (!IS_ERR(max98095->mclk)) {
+                       if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
+                               clk_disable_unprepare(max98095->mclk);
+                       else
+                               clk_prepare_enable(max98095->mclk);
+               }
                break;
 
        case SND_SOC_BIAS_STANDBY:
@@ -1737,7 +1757,7 @@ static int max98095_get_eq_channel(const char *name)
 static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
        struct max98095_pdata *pdata = max98095->pdata;
        int channel = max98095_get_eq_channel(kcontrol->id.name);
@@ -1801,7 +1821,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
 static int max98095_get_eq_enum(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
        int channel = max98095_get_eq_channel(kcontrol->id.name);
        struct max98095_cdata *cdata;
@@ -1891,7 +1911,7 @@ static int max98095_get_bq_channel(struct snd_soc_codec *codec,
 static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
        struct max98095_pdata *pdata = max98095->pdata;
        int channel = max98095_get_bq_channel(codec, kcontrol->id.name);
@@ -1952,7 +1972,7 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
 static int max98095_get_bq_enum(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
        int channel = max98095_get_bq_channel(codec, kcontrol->id.name);
        struct max98095_cdata *cdata;
@@ -2238,6 +2258,10 @@ static int max98095_probe(struct snd_soc_codec *codec)
        struct i2c_client *client;
        int ret = 0;
 
+       max98095->mclk = devm_clk_get(codec->dev, "mclk");
+       if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
        /* reset the codec, the DSP core, and disable all interrupts */
        max98095_reset(codec);
 
@@ -2399,10 +2423,17 @@ static const struct i2c_device_id max98095_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
 
+static const struct of_device_id max98095_of_match[] = {
+       { .compatible = "maxim,max98095", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, max98095_of_match);
+
 static struct i2c_driver max98095_i2c_driver = {
        .driver = {
                .name = "max98095",
                .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(max98095_of_match),
        },
        .probe  = max98095_i2c_probe,
        .remove = max98095_i2c_remove,
index 2c59b1f..9965277 100644 (file)
@@ -22,6 +22,7 @@
  */
 #include <linux/module.h>
 #include <linux/device.h>
+#include <linux/of.h>
 #include <linux/mfd/mc13xxx.h>
 #include <linux/slab.h>
 #include <sound/core.h>
@@ -409,7 +410,7 @@ static const char * const adcl_enum_text[] = {
 static SOC_ENUM_SINGLE_VIRT_DECL(adcl_enum, adcl_enum_text);
 
 static const struct snd_kcontrol_new left_input_mux =
-       SOC_DAPM_ENUM_VIRT("Route", adcl_enum);
+       SOC_DAPM_ENUM("Route", adcl_enum);
 
 static const char * const adcr_enum_text[] = {
        "MC1R", "MC2", "RXINR", "TXIN",
@@ -418,7 +419,7 @@ static const char * const adcr_enum_text[] = {
 static SOC_ENUM_SINGLE_VIRT_DECL(adcr_enum, adcr_enum_text);
 
 static const struct snd_kcontrol_new right_input_mux =
-       SOC_DAPM_ENUM_VIRT("Route", adcr_enum);
+       SOC_DAPM_ENUM("Route", adcr_enum);
 
 static const struct snd_kcontrol_new samp_ctl =
        SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 3, 1, 0);
@@ -478,9 +479,9 @@ static const struct snd_soc_dapm_widget mc13783_dapm_widgets[] = {
        SND_SOC_DAPM_SWITCH("MC2 Amp", MC13783_AUDIO_TX, 9, 0, &mc2_amp_ctl),
        SND_SOC_DAPM_SWITCH("TXIN Amp", MC13783_AUDIO_TX, 11, 0, &atx_amp_ctl),
 
-       SND_SOC_DAPM_VIRT_MUX("PGA Left Input Mux", SND_SOC_NOPM, 0, 0,
+       SND_SOC_DAPM_MUX("PGA Left Input Mux", SND_SOC_NOPM, 0, 0,
                              &left_input_mux),
-       SND_SOC_DAPM_VIRT_MUX("PGA Right Input Mux", SND_SOC_NOPM, 0, 0,
+       SND_SOC_DAPM_MUX("PGA Right Input Mux", SND_SOC_NOPM, 0, 0,
                              &right_input_mux),
 
        SND_SOC_DAPM_MUX("Speaker Amp Source MUX", SND_SOC_NOPM, 0, 0,
@@ -608,14 +609,6 @@ static struct snd_kcontrol_new mc13783_control_list[] = {
 static int mc13783_probe(struct snd_soc_codec *codec)
 {
        struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
-       int ret;
-
-       ret = snd_soc_codec_set_cache_io(codec,
-                       dev_get_regmap(codec->dev->parent, NULL));
-       if (ret != 0) {
-               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
-               return ret;
-       }
 
        /* these are the reset values */
        mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX0, 0x25893);
@@ -735,9 +728,15 @@ static struct snd_soc_dai_driver mc13783_dai_sync[] = {
        }
 };
 
+static struct regmap *mc13783_get_regmap(struct device *dev)
+{
+       return dev_get_regmap(dev->parent, NULL);
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_mc13783 = {
        .probe          = mc13783_probe,
        .remove         = mc13783_remove,
+       .get_regmap     = mc13783_get_regmap,
        .controls       = mc13783_control_list,
        .num_controls   = ARRAY_SIZE(mc13783_control_list),
        .dapm_widgets   = mc13783_dapm_widgets,
@@ -750,6 +749,7 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
 {
        struct mc13783_priv *priv;
        struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data;
+       struct device_node *np;
        int ret;
 
        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
@@ -760,7 +760,17 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
                priv->adc_ssi_port = pdata->adc_ssi_port;
                priv->dac_ssi_port = pdata->dac_ssi_port;
        } else {
-               return -ENOSYS;
+               np = of_get_child_by_name(pdev->dev.parent->of_node, "codec");
+               if (!np)
+                       return -ENOSYS;
+
+               ret = of_property_read_u32(np, "adc-port", &priv->adc_ssi_port);
+               if (ret)
+                       return ret;
+
+               ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port);
+               if (ret)
+                       return ret;
        }
 
        dev_set_drvdata(&pdev->dev, priv);
index e427544..a722a02 100644 (file)
@@ -115,7 +115,7 @@ static int pcm1681_set_deemph(struct snd_soc_codec *codec)
 static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = priv->deemph;
@@ -126,7 +126,7 @@ static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
 static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
 
        priv->deemph = ucontrol->value.enumerated.item[0];
index 4b4c0c7..163ec38 100644 (file)
@@ -269,7 +269,7 @@ SOC_DOUBLE("Playback Digital Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
           PCM512x_RQMR_SHIFT, 1, 1),
 
 SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1),
-SOC_VALUE_ENUM("DSP Program", pcm512x_dsp_program),
+SOC_ENUM("DSP Program", pcm512x_dsp_program),
 
 SOC_ENUM("Clock Missing Period", pcm512x_clk_missing),
 SOC_ENUM("Auto Mute Time Left", pcm512x_autom_l),
@@ -517,6 +517,7 @@ void pcm512x_remove(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(pcm512x_remove);
 
+#ifdef CONFIG_PM_RUNTIME
 static int pcm512x_suspend(struct device *dev)
 {
        struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
@@ -578,6 +579,7 @@ static int pcm512x_resume(struct device *dev)
 
        return 0;
 }
+#endif
 
 const struct dev_pm_ops pcm512x_pm_ops = {
        SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c
new file mode 100644 (file)
index 0000000..7b82fbe
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * rl6231.c - RL6231 class device shared support
+ *
+ * Copyright 2014 Realtek Semiconductor Corp.
+ *
+ * Author: Oder Chiou <oder_chiou@realtek.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/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.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 "rl6231.h"
+
+/**
+ * rl6231_calc_dmic_clk - Calculate the parameter of dmic.
+ *
+ * @rate: base clock rate.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+int rl6231_calc_dmic_clk(int rate)
+{
+       int div[] = {2, 3, 4, 6, 8, 12}, idx = -EINVAL;
+       int i, red, bound, temp;
+
+       red = 3000000 * 12;
+       for (i = 0; i < ARRAY_SIZE(div); i++) {
+               bound = div[i] * 3000000;
+               if (rate > bound)
+                       continue;
+               temp = bound - rate;
+               if (temp < red) {
+                       red = temp;
+                       idx = i;
+               }
+       }
+
+       return idx;
+}
+EXPORT_SYMBOL_GPL(rl6231_calc_dmic_clk);
+
+/**
+ * rl6231_pll_calc - Calcualte PLL M/N/K code.
+ * @freq_in: external clock provided to codec.
+ * @freq_out: target clock which codec works on.
+ * @pll_code: Pointer to structure with M, N, K and bypass flag.
+ *
+ * Calcualte M/N/K code to configure PLL for codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+int rl6231_pll_calc(const unsigned int freq_in,
+       const unsigned int freq_out, struct rl6231_pll_code *pll_code)
+{
+       int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX;
+       int k, red, n_t, pll_out, in_t, out_t;
+       int n = 0, m = 0, m_t = 0;
+       int red_t = abs(freq_out - freq_in);
+       bool bypass = false;
+
+       if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in)
+               return -EINVAL;
+
+       k = 100000000 / freq_out - 2;
+       if (k > RL6231_PLL_K_MAX)
+               k = RL6231_PLL_K_MAX;
+       for (n_t = 0; n_t <= max_n; n_t++) {
+               in_t = freq_in / (k + 2);
+               pll_out = freq_out / (n_t + 2);
+               if (in_t < 0)
+                       continue;
+               if (in_t == pll_out) {
+                       bypass = true;
+                       n = n_t;
+                       goto code_find;
+               }
+               red = abs(in_t - pll_out);
+               if (red < red_t) {
+                       bypass = true;
+                       n = n_t;
+                       m = m_t;
+                       if (red == 0)
+                               goto code_find;
+                       red_t = red;
+               }
+               for (m_t = 0; m_t <= max_m; m_t++) {
+                       out_t = in_t / (m_t + 2);
+                       red = abs(out_t - pll_out);
+                       if (red < red_t) {
+                               bypass = false;
+                               n = n_t;
+                               m = m_t;
+                               if (red == 0)
+                                       goto code_find;
+                               red_t = red;
+                       }
+               }
+       }
+       pr_debug("Only get approximation about PLL\n");
+
+code_find:
+
+       pll_code->m_bp = bypass;
+       pll_code->m_code = m;
+       pll_code->n_code = n;
+       pll_code->k_code = k;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rl6231_pll_calc);
+
+int rl6231_get_clk_info(int sclk, int rate)
+{
+       int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+       if (sclk <= 0 || rate <= 0)
+               return -EINVAL;
+
+       rate = rate << 8;
+       for (i = 0; i < ARRAY_SIZE(pd); i++)
+               if (sclk == rate * pd[i])
+                       return i;
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(rl6231_get_clk_info);
+
+MODULE_DESCRIPTION("RL6231 class device shared support");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rl6231.h b/sound/soc/codecs/rl6231.h
new file mode 100644 (file)
index 0000000..0f7b057
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * rl6231.h - RL6231 class device shared support
+ *
+ * Copyright 2014 Realtek Semiconductor Corp.
+ *
+ * Author: Oder Chiou <oder_chiou@realtek.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 __RL6231_H__
+#define __RL6231_H__
+
+#define RL6231_PLL_INP_MAX     40000000
+#define RL6231_PLL_INP_MIN     256000
+#define RL6231_PLL_N_MAX       0x1ff
+#define RL6231_PLL_K_MAX       0x1f
+#define RL6231_PLL_M_MAX       0xf
+
+struct rl6231_pll_code {
+       bool m_bp; /* Indicates bypass m code or not. */
+       int m_code;
+       int n_code;
+       int k_code;
+};
+
+int rl6231_calc_dmic_clk(int rate);
+int rl6231_pll_calc(const unsigned int freq_in,
+       const unsigned int freq_out, struct rl6231_pll_code *pll_code);
+int rl6231_get_clk_info(int sclk, int rate);
+
+#endif /* __RL6231_H__ */
index d4c229f..30e2347 100644 (file)
@@ -188,7 +188,7 @@ static unsigned int mic_bst_tlv[] = {
 static int rt5631_dmic_get(struct snd_kcontrol *kcontrol,
                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.integer.value[0] = rt5631->dmic_used_flag;
@@ -199,7 +199,7 @@ static int rt5631_dmic_get(struct snd_kcontrol *kcontrol,
 static int rt5631_dmic_put(struct snd_kcontrol *kcontrol,
                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
 
        rt5631->dmic_used_flag = ucontrol->value.integer.value[0];
index 68b4dd6..de80e89 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * rt5640.c  --  RT5640 ALSA SoC audio codec driver
+ * rt5640.c  --  RT5640/RT5639 ALSA SoC audio codec driver
  *
  * Copyright 2011 Realtek Semiconductor Corp.
  * Author: Johnny Hsu <johnnyhsu@realtek.com>
@@ -18,6 +18,7 @@
 #include <linux/gpio.h>
 #include <linux/i2c.h>
 #include <linux/regmap.h>
+#include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
@@ -30,6 +31,7 @@
 #include <sound/initval.h>
 #include <sound/tlv.h>
 
+#include "rl6231.h"
 #include "rt5640.h"
 
 #define RT5640_DEVICE_ID 0x6231
@@ -59,7 +61,7 @@ static struct reg_default init_list[] = {
 };
 #define RT5640_INIT_REG_LEN ARRAY_SIZE(init_list)
 
-static const struct reg_default rt5640_reg[RT5640_VENDOR_ID2 + 1] = {
+static const struct reg_default rt5640_reg[] = {
        { 0x00, 0x000e },
        { 0x01, 0xc8c8 },
        { 0x02, 0xc8c8 },
@@ -398,18 +400,13 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
                RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
        SOC_DOUBLE_TLV("OUT Playback Volume", RT5640_OUTPUT,
                RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
-       /* MONO Output Control */
-       SOC_SINGLE("Mono Playback Switch", RT5640_MONO_OUT,
-                               RT5640_L_MUTE_SFT, 1, 1),
+
        /* DAC Digital Volume */
        SOC_DOUBLE("DAC2 Playback Switch", RT5640_DAC2_CTRL,
                RT5640_M_DAC_L2_VOL_SFT, RT5640_M_DAC_R2_VOL_SFT, 1, 1),
        SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL,
                        RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
                        175, 0, dac_vol_tlv),
-       SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5640_DAC2_DIG_VOL,
-                       RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
-                       175, 0, dac_vol_tlv),
        /* IN1/IN2 Control */
        SOC_SINGLE_TLV("IN1 Boost", RT5640_IN1_IN2,
                RT5640_BST_SFT1, 8, 0, bst_tlv),
@@ -441,6 +438,15 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
        SOC_ENUM("DAC IF2 Data Switch", rt5640_if2_dac_enum),
 };
 
+static const struct snd_kcontrol_new rt5640_specific_snd_controls[] = {
+       /* MONO Output Control */
+       SOC_SINGLE("Mono Playback Switch", RT5640_MONO_OUT, RT5640_L_MUTE_SFT,
+               1, 1),
+
+       SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5640_DAC2_DIG_VOL,
+               RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 175, 0, dac_vol_tlv),
+};
+
 /**
  * set_dmic_clk - Set parameter of dmic.
  *
@@ -448,30 +454,16 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
  * @kcontrol: The kcontrol of this widget.
  * @event: Event id.
  *
- * Choose dmic clock between 1MHz and 3MHz.
- * It is better for clock to approximate 3MHz.
  */
 static int set_dmic_clk(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
        struct snd_soc_codec *codec = w->codec;
        struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
-       int div[] = {2, 3, 4, 6, 8, 12};
-       int idx = -EINVAL, i;
-       int rate, red, bound, temp;
-
-       rate = rt5640->sysclk;
-       red = 3000000 * 12;
-       for (i = 0; i < ARRAY_SIZE(div); i++) {
-               bound = div[i] * 3000000;
-               if (rate > bound)
-                       continue;
-               temp = bound - rate;
-               if (temp < red) {
-                       red = temp;
-                       idx = i;
-               }
-       }
+       int idx = -EINVAL;
+
+       idx = rl6231_calc_dmic_clk(rt5640->sysclk);
+
        if (idx < 0)
                dev_err(codec->dev, "Failed to set DMIC clock\n");
        else
@@ -480,14 +472,14 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
        return idx;
 }
 
-static int check_sysclk1_source(struct snd_soc_dapm_widget *source,
+static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
                         struct snd_soc_dapm_widget *sink)
 {
        unsigned int val;
 
        val = snd_soc_read(source->codec, RT5640_GLB_CLK);
        val &= RT5640_SCLK_SRC_MASK;
-       if (val == RT5640_SCLK_SRC_PLL1 || val == RT5640_SCLK_SRC_PLL1T)
+       if (val == RT5640_SCLK_SRC_PLL1)
                return 1;
        else
                return 0;
@@ -554,6 +546,20 @@ static const struct snd_kcontrol_new rt5640_sto_dac_r_mix[] = {
                        RT5640_M_ANC_DAC_R_SFT, 1, 1),
 };
 
+static const struct snd_kcontrol_new rt5639_sto_dac_l_mix[] = {
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_STO_DAC_MIXER,
+                       RT5640_M_DAC_L1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_STO_DAC_MIXER,
+                       RT5640_M_DAC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5639_sto_dac_r_mix[] = {
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_STO_DAC_MIXER,
+                       RT5640_M_DAC_R1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_STO_DAC_MIXER,
+                       RT5640_M_DAC_R2_SFT, 1, 1),
+};
+
 static const struct snd_kcontrol_new rt5640_mono_dac_l_mix[] = {
        SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_MONO_DAC_MIXER,
                        RT5640_M_DAC_L1_MONO_L_SFT, 1, 1),
@@ -676,6 +682,30 @@ static const struct snd_kcontrol_new rt5640_out_r_mix[] = {
                        RT5640_M_DAC_R1_OM_R_SFT, 1, 1),
 };
 
+static const struct snd_kcontrol_new rt5639_out_l_mix[] = {
+       SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_L3_MIXER,
+                       RT5640_M_BST1_OM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INL Switch", RT5640_OUT_L3_MIXER,
+                       RT5640_M_IN_L_OM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_OUT_L3_MIXER,
+                       RT5640_M_RM_L_OM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_OUT_L3_MIXER,
+                       RT5640_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5639_out_r_mix[] = {
+       SOC_DAPM_SINGLE("BST2 Switch", RT5640_OUT_R3_MIXER,
+                       RT5640_M_BST4_OM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_R3_MIXER,
+                       RT5640_M_BST1_OM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INR Switch", RT5640_OUT_R3_MIXER,
+                       RT5640_M_IN_R_OM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_OUT_R3_MIXER,
+                       RT5640_M_RM_R_OM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_OUT_R3_MIXER,
+                       RT5640_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
 static const struct snd_kcontrol_new rt5640_spo_l_mix[] = {
        SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPO_L_MIXER,
                        RT5640_M_DAC_R1_SPM_L_SFT, 1, 1),
@@ -707,6 +737,13 @@ static const struct snd_kcontrol_new rt5640_hpo_mix[] = {
                        RT5640_M_HPVOL_HM_SFT, 1, 1),
 };
 
+static const struct snd_kcontrol_new rt5639_hpo_mix[] = {
+       SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5640_HPO_MIXER,
+                       RT5640_M_DAC1_HM_SFT, 1, 1),
+       SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5640_HPO_MIXER,
+                       RT5640_M_HPVOL_HM_SFT, 1, 1),
+};
+
 static const struct snd_kcontrol_new rt5640_lout_mix[] = {
        SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_LOUT_MIXER,
                        RT5640_M_DAC_L1_LM_SFT, 1, 1),
@@ -824,7 +861,7 @@ static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dac_l2_enum,
                                  0x3, rt5640_dac_l2_src, rt5640_dac_l2_values);
 
 static const struct snd_kcontrol_new rt5640_dac_l2_mux =
-       SOC_DAPM_VALUE_ENUM("DAC2 left channel source", rt5640_dac_l2_enum);
+       SOC_DAPM_ENUM("DAC2 left channel source", rt5640_dac_l2_enum);
 
 static const char * const rt5640_dac_r2_src[] = {
        "IF2",
@@ -859,7 +896,7 @@ static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dai_iis_map_enum,
                                  rt5640_dai_iis_map_values);
 
 static const struct snd_kcontrol_new rt5640_dai_mux =
-       SOC_DAPM_VALUE_ENUM("DAI select", rt5640_dai_iis_map_enum);
+       SOC_DAPM_ENUM("DAI select", rt5640_dai_iis_map_enum);
 
 /* SDI select */
 static const char * const rt5640_sdi_sel[] = {
@@ -872,54 +909,6 @@ static SOC_ENUM_SINGLE_DECL(rt5640_sdi_sel_enum, RT5640_I2S2_SDP,
 static const struct snd_kcontrol_new rt5640_sdi_mux =
        SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum);
 
-static int rt5640_set_dmic1_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:
-               snd_soc_update_bits(codec, RT5640_GPIO_CTRL1,
-                       RT5640_GP2_PIN_MASK | RT5640_GP3_PIN_MASK,
-                       RT5640_GP2_PIN_DMIC1_SCL | RT5640_GP3_PIN_DMIC1_SDA);
-               snd_soc_update_bits(codec, RT5640_DMIC,
-                       RT5640_DMIC_1L_LH_MASK | RT5640_DMIC_1R_LH_MASK |
-                       RT5640_DMIC_1_DP_MASK,
-                       RT5640_DMIC_1L_LH_FALLING | RT5640_DMIC_1R_LH_RISING |
-                       RT5640_DMIC_1_DP_IN1P);
-               break;
-
-       default:
-               return 0;
-       }
-
-       return 0;
-}
-
-static int rt5640_set_dmic2_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:
-               snd_soc_update_bits(codec, RT5640_GPIO_CTRL1,
-                       RT5640_GP2_PIN_MASK | RT5640_GP4_PIN_MASK,
-                       RT5640_GP2_PIN_DMIC1_SCL | RT5640_GP4_PIN_DMIC2_SDA);
-               snd_soc_update_bits(codec, RT5640_DMIC,
-                       RT5640_DMIC_2L_LH_MASK | RT5640_DMIC_2R_LH_MASK |
-                       RT5640_DMIC_2_DP_MASK,
-                       RT5640_DMIC_2L_LH_FALLING | RT5640_DMIC_2R_LH_RISING |
-                       RT5640_DMIC_2_DP_IN1N);
-               break;
-
-       default:
-               return 0;
-       }
-
-       return 0;
-}
-
 static void hp_amp_power_on(struct snd_soc_codec *codec)
 {
        struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
@@ -1054,12 +1043,10 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
 
        SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
                set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
-       SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5640_DMIC,
-               RT5640_DMIC_1_EN_SFT, 0, rt5640_set_dmic1_event,
-               SND_SOC_DAPM_PRE_PMU),
-       SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5640_DMIC,
-               RT5640_DMIC_2_EN_SFT, 0, rt5640_set_dmic2_event,
-               SND_SOC_DAPM_PRE_PMU),
+       SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5640_DMIC, RT5640_DMIC_1_EN_SFT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5640_DMIC, RT5640_DMIC_2_EN_SFT, 0,
+               NULL, 0),
        /* Boost */
        SND_SOC_DAPM_PGA("BST1", RT5640_PWR_ANLG2,
                RT5640_PWR_BST1_BIT, 0, NULL, 0),
@@ -1146,26 +1133,15 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
        SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
        SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
        SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
-       /* Audio DSP */
-       SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
-       /* ANC */
-       SND_SOC_DAPM_PGA("ANC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
        /* Output Side */
        /* DAC mixer before sound effect  */
        SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
                rt5640_dac_l_mix, ARRAY_SIZE(rt5640_dac_l_mix)),
        SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
                rt5640_dac_r_mix, ARRAY_SIZE(rt5640_dac_r_mix)),
-       /* DAC2 channel Mux */
-       SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0,
-                               &rt5640_dac_l2_mux),
-       SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0,
-                               &rt5640_dac_r2_mux),
+
        /* DAC Mixer */
-       SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
-               rt5640_sto_dac_l_mix, ARRAY_SIZE(rt5640_sto_dac_l_mix)),
-       SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
-               rt5640_sto_dac_r_mix, ARRAY_SIZE(rt5640_sto_dac_r_mix)),
        SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0,
                rt5640_mono_dac_l_mix, ARRAY_SIZE(rt5640_mono_dac_l_mix)),
        SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0,
@@ -1177,21 +1153,14 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
        /* DACs */
        SND_SOC_DAPM_DAC("DAC L1", NULL, RT5640_PWR_DIG1,
                        RT5640_PWR_DAC_L1_BIT, 0),
-       SND_SOC_DAPM_DAC("DAC L2", NULL, RT5640_PWR_DIG1,
-                       RT5640_PWR_DAC_L2_BIT, 0),
        SND_SOC_DAPM_DAC("DAC R1", NULL, RT5640_PWR_DIG1,
                        RT5640_PWR_DAC_R1_BIT, 0),
-       SND_SOC_DAPM_DAC("DAC R2", NULL, RT5640_PWR_DIG1,
-                       RT5640_PWR_DAC_R2_BIT, 0),
+
        /* SPK/OUT Mixer */
        SND_SOC_DAPM_MIXER("SPK MIXL", RT5640_PWR_MIXER, RT5640_PWR_SM_L_BIT,
                0, rt5640_spk_l_mix, ARRAY_SIZE(rt5640_spk_l_mix)),
        SND_SOC_DAPM_MIXER("SPK MIXR", RT5640_PWR_MIXER, RT5640_PWR_SM_R_BIT,
                0, rt5640_spk_r_mix, ARRAY_SIZE(rt5640_spk_r_mix)),
-       SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
-               0, rt5640_out_l_mix, ARRAY_SIZE(rt5640_out_l_mix)),
-       SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT,
-               0, rt5640_out_r_mix, ARRAY_SIZE(rt5640_out_r_mix)),
        /* Ouput Volume */
        SND_SOC_DAPM_PGA("SPKVOL L", RT5640_PWR_VOL,
                RT5640_PWR_SV_L_BIT, 0, NULL, 0),
@@ -1210,16 +1179,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
                0, rt5640_spo_l_mix, ARRAY_SIZE(rt5640_spo_l_mix)),
        SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0,
                0, rt5640_spo_r_mix, ARRAY_SIZE(rt5640_spo_r_mix)),
-       SND_SOC_DAPM_MIXER("HPO MIX L", SND_SOC_NOPM, 0, 0,
-               rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)),
-       SND_SOC_DAPM_MIXER("HPO MIX R", SND_SOC_NOPM, 0, 0,
-               rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)),
        SND_SOC_DAPM_MIXER("LOUT MIX", RT5640_PWR_ANLG1, RT5640_PWR_LM_BIT, 0,
                rt5640_lout_mix, ARRAY_SIZE(rt5640_lout_mix)),
-       SND_SOC_DAPM_MIXER("Mono MIX", RT5640_PWR_ANLG1, RT5640_PWR_MM_BIT, 0,
-               rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)),
-       SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1,
-               RT5640_PWR_MA_BIT, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY_S("Improve HP Amp Drv", 1, SND_SOC_NOPM,
                0, 0, rt5640_hp_power_event, SND_SOC_DAPM_POST_PMU),
        SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0,
@@ -1251,10 +1212,69 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
        SND_SOC_DAPM_OUTPUT("HPOR"),
        SND_SOC_DAPM_OUTPUT("LOUTL"),
        SND_SOC_DAPM_OUTPUT("LOUTR"),
+};
+
+static const struct snd_soc_dapm_widget rt5640_specific_dapm_widgets[] = {
+       /* Audio DSP */
+       SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
+       /* ANC */
+       SND_SOC_DAPM_PGA("ANC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       /* DAC2 channel Mux */
+       SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dac_l2_mux),
+       SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dac_r2_mux),
+
+       SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5640_sto_dac_l_mix, ARRAY_SIZE(rt5640_sto_dac_l_mix)),
+       SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5640_sto_dac_r_mix, ARRAY_SIZE(rt5640_sto_dac_r_mix)),
+
+       SND_SOC_DAPM_DAC("DAC R2", NULL, RT5640_PWR_DIG1, RT5640_PWR_DAC_R2_BIT,
+               0),
+       SND_SOC_DAPM_DAC("DAC L2", NULL, RT5640_PWR_DIG1, RT5640_PWR_DAC_L2_BIT,
+               0),
+
+       SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
+               0, rt5640_out_l_mix, ARRAY_SIZE(rt5640_out_l_mix)),
+       SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT,
+               0, rt5640_out_r_mix, ARRAY_SIZE(rt5640_out_r_mix)),
+
+       SND_SOC_DAPM_MIXER("HPO MIX L", SND_SOC_NOPM, 0, 0,
+               rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)),
+       SND_SOC_DAPM_MIXER("HPO MIX R", SND_SOC_NOPM, 0, 0,
+               rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)),
+
+       SND_SOC_DAPM_MIXER("Mono MIX", RT5640_PWR_ANLG1, RT5640_PWR_MM_BIT, 0,
+               rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)),
+       SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1,
+               RT5640_PWR_MA_BIT, 0, NULL, 0),
+
        SND_SOC_DAPM_OUTPUT("MONOP"),
        SND_SOC_DAPM_OUTPUT("MONON"),
 };
 
+static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
+       SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5639_sto_dac_l_mix, ARRAY_SIZE(rt5639_sto_dac_l_mix)),
+       SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5639_sto_dac_r_mix, ARRAY_SIZE(rt5639_sto_dac_r_mix)),
+
+       SND_SOC_DAPM_SUPPLY("DAC L2 Filter", RT5640_PWR_DIG1,
+               RT5640_PWR_DAC_L2_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DAC R2 Filter", RT5640_PWR_DIG1,
+               RT5640_PWR_DAC_R2_BIT, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
+               0, rt5639_out_l_mix, ARRAY_SIZE(rt5639_out_l_mix)),
+       SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT,
+               0, rt5639_out_r_mix, ARRAY_SIZE(rt5639_out_r_mix)),
+
+       SND_SOC_DAPM_MIXER("HPO MIX L", SND_SOC_NOPM, 0, 0,
+               rt5639_hpo_mix, ARRAY_SIZE(rt5639_hpo_mix)),
+       SND_SOC_DAPM_MIXER("HPO MIX R", SND_SOC_NOPM, 0, 0,
+               rt5639_hpo_mix, ARRAY_SIZE(rt5639_hpo_mix)),
+};
+
 static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
        {"IN1P", NULL, "LDO2"},
        {"IN2P", NULL, "LDO2"},
@@ -1323,22 +1343,22 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
        {"Stereo ADC MIXL", "ADC1 Switch", "Stereo ADC L1 Mux"},
        {"Stereo ADC MIXL", "ADC2 Switch", "Stereo ADC L2 Mux"},
        {"Stereo ADC MIXL", NULL, "Stereo Filter"},
-       {"Stereo Filter", NULL, "PLL1", check_sysclk1_source},
+       {"Stereo Filter", NULL, "PLL1", is_sys_clk_from_pll},
 
        {"Stereo ADC MIXR", "ADC1 Switch", "Stereo ADC R1 Mux"},
        {"Stereo ADC MIXR", "ADC2 Switch", "Stereo ADC R2 Mux"},
        {"Stereo ADC MIXR", NULL, "Stereo Filter"},
-       {"Stereo Filter", NULL, "PLL1", check_sysclk1_source},
+       {"Stereo Filter", NULL, "PLL1", is_sys_clk_from_pll},
 
        {"Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux"},
        {"Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux"},
        {"Mono ADC MIXL", NULL, "Mono Left Filter"},
-       {"Mono Left Filter", NULL, "PLL1", check_sysclk1_source},
+       {"Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll},
 
        {"Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux"},
        {"Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux"},
        {"Mono ADC MIXR", NULL, "Mono Right Filter"},
-       {"Mono Right Filter", NULL, "PLL1", check_sysclk1_source},
+       {"Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll},
 
        {"IF2 ADC L", NULL, "Mono ADC MIXL"},
        {"IF2 ADC R", NULL, "Mono ADC MIXR"},
@@ -1396,71 +1416,38 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
        {"DAC MIXR", "Stereo ADC Switch", "Stereo ADC MIXR"},
        {"DAC MIXR", "INF1 Switch", "IF1 DAC R"},
 
-       {"ANC", NULL, "Stereo ADC MIXL"},
-       {"ANC", NULL, "Stereo ADC MIXR"},
-
-       {"Audio DSP", NULL, "DAC MIXL"},
-       {"Audio DSP", NULL, "DAC MIXR"},
-
-       {"DAC L2 Mux", "IF2", "IF2 DAC L"},
-       {"DAC L2 Mux", "Base L/R", "Audio DSP"},
-
-       {"DAC R2 Mux", "IF2", "IF2 DAC R"},
-
        {"Stereo DAC MIXL", "DAC L1 Switch", "DAC MIXL"},
-       {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
-       {"Stereo DAC MIXL", "ANC Switch", "ANC"},
        {"Stereo DAC MIXR", "DAC R1 Switch", "DAC MIXR"},
-       {"Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
-       {"Stereo DAC MIXR", "ANC Switch", "ANC"},
 
        {"Mono DAC MIXL", "DAC L1 Switch", "DAC MIXL"},
-       {"Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
-       {"Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"},
        {"Mono DAC MIXR", "DAC R1 Switch", "DAC MIXR"},
-       {"Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
-       {"Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"},
 
        {"DIG MIXL", "DAC L1 Switch", "DAC MIXL"},
-       {"DIG MIXL", "DAC L2 Switch", "DAC L2 Mux"},
        {"DIG MIXR", "DAC R1 Switch", "DAC MIXR"},
-       {"DIG MIXR", "DAC R2 Switch", "DAC R2 Mux"},
 
        {"DAC L1", NULL, "Stereo DAC MIXL"},
-       {"DAC L1", NULL, "PLL1", check_sysclk1_source},
+       {"DAC L1", NULL, "PLL1", is_sys_clk_from_pll},
        {"DAC R1", NULL, "Stereo DAC MIXR"},
-       {"DAC R1", NULL, "PLL1", check_sysclk1_source},
-       {"DAC L2", NULL, "Mono DAC MIXL"},
-       {"DAC L2", NULL, "PLL1", check_sysclk1_source},
-       {"DAC R2", NULL, "Mono DAC MIXR"},
-       {"DAC R2", NULL, "PLL1", check_sysclk1_source},
+       {"DAC R1", NULL, "PLL1", is_sys_clk_from_pll},
 
        {"SPK MIXL", "REC MIXL Switch", "RECMIXL"},
        {"SPK MIXL", "INL Switch", "INL VOL"},
        {"SPK MIXL", "DAC L1 Switch", "DAC L1"},
-       {"SPK MIXL", "DAC L2 Switch", "DAC L2"},
        {"SPK MIXL", "OUT MIXL Switch", "OUT MIXL"},
        {"SPK MIXR", "REC MIXR Switch", "RECMIXR"},
        {"SPK MIXR", "INR Switch", "INR VOL"},
        {"SPK MIXR", "DAC R1 Switch", "DAC R1"},
-       {"SPK MIXR", "DAC R2 Switch", "DAC R2"},
        {"SPK MIXR", "OUT MIXR Switch", "OUT MIXR"},
 
-       {"OUT MIXL", "SPK MIXL Switch", "SPK MIXL"},
        {"OUT MIXL", "BST1 Switch", "BST1"},
        {"OUT MIXL", "INL Switch", "INL VOL"},
        {"OUT MIXL", "REC MIXL Switch", "RECMIXL"},
-       {"OUT MIXL", "DAC R2 Switch", "DAC R2"},
-       {"OUT MIXL", "DAC L2 Switch", "DAC L2"},
        {"OUT MIXL", "DAC L1 Switch", "DAC L1"},
 
-       {"OUT MIXR", "SPK MIXR Switch", "SPK MIXR"},
        {"OUT MIXR", "BST2 Switch", "BST2"},
        {"OUT MIXR", "BST1 Switch", "BST1"},
        {"OUT MIXR", "INR Switch", "INR VOL"},
        {"OUT MIXR", "REC MIXR Switch", "RECMIXR"},
-       {"OUT MIXR", "DAC L2 Switch", "DAC L2"},
-       {"OUT MIXR", "DAC R2 Switch", "DAC R2"},
        {"OUT MIXR", "DAC R1 Switch", "DAC R1"},
 
        {"SPKVOL L", NULL, "SPK MIXL"},
@@ -1479,11 +1466,9 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
        {"SPOR MIX", "SPKVOL R Switch", "SPKVOL R"},
        {"SPOR MIX", "BST1 Switch", "BST1"},
 
-       {"HPO MIX L", "HPO MIX DAC2 Switch", "DAC L2"},
        {"HPO MIX L", "HPO MIX DAC1 Switch", "DAC L1"},
        {"HPO MIX L", "HPO MIX HPVOL Switch", "HPOVOL L"},
        {"HPO MIX L", NULL, "HP L Amp"},
-       {"HPO MIX R", "HPO MIX DAC2 Switch", "DAC R2"},
        {"HPO MIX R", "HPO MIX DAC1 Switch", "DAC R1"},
        {"HPO MIX R", "HPO MIX HPVOL Switch", "HPOVOL R"},
        {"HPO MIX R", NULL, "HP R Amp"},
@@ -1493,12 +1478,6 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
        {"LOUT MIX", "OUTVOL L Switch", "OUTVOL L"},
        {"LOUT MIX", "OUTVOL R Switch", "OUTVOL R"},
 
-       {"Mono MIX", "DAC R2 Switch", "DAC R2"},
-       {"Mono MIX", "DAC L2 Switch", "DAC L2"},
-       {"Mono MIX", "OUTVOL R Switch", "OUTVOL R"},
-       {"Mono MIX", "OUTVOL L Switch", "OUTVOL L"},
-       {"Mono MIX", "BST1 Switch", "BST1"},
-
        {"HP Amp", NULL, "HPO MIX L"},
        {"HP Amp", NULL, "HPO MIX R"},
 
@@ -1523,11 +1502,82 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
        {"HPOR", NULL, "HP R Playback"},
        {"LOUTL", NULL, "LOUT MIX"},
        {"LOUTR", NULL, "LOUT MIX"},
+};
+
+static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = {
+       {"ANC", NULL, "Stereo ADC MIXL"},
+       {"ANC", NULL, "Stereo ADC MIXR"},
+
+       {"Audio DSP", NULL, "DAC MIXL"},
+       {"Audio DSP", NULL, "DAC MIXR"},
+
+       {"DAC L2 Mux", "IF2", "IF2 DAC L"},
+       {"DAC L2 Mux", "Base L/R", "Audio DSP"},
+
+       {"DAC R2 Mux", "IF2", "IF2 DAC R"},
+
+       {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+       {"Stereo DAC MIXL", "ANC Switch", "ANC"},
+       {"Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+       {"Stereo DAC MIXR", "ANC Switch", "ANC"},
+
+       {"Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+       {"Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"},
+
+       {"Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+       {"Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"},
+
+       {"DIG MIXR", "DAC R2 Switch", "DAC R2 Mux"},
+       {"DIG MIXL", "DAC L2 Switch", "DAC L2 Mux"},
+
+       {"DAC L2", NULL, "Mono DAC MIXL"},
+       {"DAC L2", NULL, "PLL1", is_sys_clk_from_pll},
+       {"DAC R2", NULL, "Mono DAC MIXR"},
+       {"DAC R2", NULL, "PLL1", is_sys_clk_from_pll},
+
+       {"SPK MIXL", "DAC L2 Switch", "DAC L2"},
+       {"SPK MIXR", "DAC R2 Switch", "DAC R2"},
+
+       {"OUT MIXL", "SPK MIXL Switch", "SPK MIXL"},
+       {"OUT MIXR", "SPK MIXR Switch", "SPK MIXR"},
+
+       {"OUT MIXL", "DAC R2 Switch", "DAC R2"},
+       {"OUT MIXL", "DAC L2 Switch", "DAC L2"},
+
+       {"OUT MIXR", "DAC L2 Switch", "DAC L2"},
+       {"OUT MIXR", "DAC R2 Switch", "DAC R2"},
+
+       {"HPO MIX L", "HPO MIX DAC2 Switch", "DAC L2"},
+       {"HPO MIX R", "HPO MIX DAC2 Switch", "DAC R2"},
+
+       {"Mono MIX", "DAC R2 Switch", "DAC R2"},
+       {"Mono MIX", "DAC L2 Switch", "DAC L2"},
+       {"Mono MIX", "OUTVOL R Switch", "OUTVOL R"},
+       {"Mono MIX", "OUTVOL L Switch", "OUTVOL L"},
+       {"Mono MIX", "BST1 Switch", "BST1"},
+
        {"MONOP", NULL, "Mono MIX"},
        {"MONON", NULL, "Mono MIX"},
        {"MONOP", NULL, "Improve MONO Amp Drv"},
 };
 
+static const struct snd_soc_dapm_route rt5639_specific_dapm_routes[] = {
+       {"Stereo DAC MIXL", "DAC L2 Switch", "IF2 DAC L"},
+       {"Stereo DAC MIXR", "DAC R2 Switch", "IF2 DAC R"},
+
+       {"Mono DAC MIXL", "DAC L2 Switch", "IF2 DAC L"},
+       {"Mono DAC MIXL", "DAC R2 Switch", "IF2 DAC R"},
+
+       {"Mono DAC MIXR", "DAC R2 Switch", "IF2 DAC R"},
+       {"Mono DAC MIXR", "DAC L2 Switch", "IF2 DAC L"},
+
+       {"DIG MIXL", "DAC L2 Switch", "IF2 DAC L"},
+       {"DIG MIXR", "DAC R2 Switch", "IF2 DAC R"},
+
+       {"IF2 DAC L", NULL, "DAC L2 Filter"},
+       {"IF2 DAC R", NULL, "DAC R2 Filter"},
+};
+
 static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
 {
        int ret = 0, val;
@@ -1576,21 +1626,6 @@ static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
        return ret;
 }
 
-static int get_clk_info(int sclk, int rate)
-{
-       int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
-
-       if (sclk <= 0 || rate <= 0)
-               return -EINVAL;
-
-       rate = rate << 8;
-       for (i = 0; i < ARRAY_SIZE(pd); i++)
-               if (sclk == rate * pd[i])
-                       return i;
-
-       return -EINVAL;
-}
-
 static int rt5640_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
@@ -1600,7 +1635,7 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream,
        int dai_sel, pre_div, bclk_ms, frame_size;
 
        rt5640->lrck[dai->id] = params_rate(params);
-       pre_div = get_clk_info(rt5640->sysclk, rt5640->lrck[dai->id]);
+       pre_div = rl6231_get_clk_info(rt5640->sysclk, rt5640->lrck[dai->id]);
        if (pre_div < 0) {
                dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n",
                        rt5640->lrck[dai->id], dai->id);
@@ -1622,16 +1657,16 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream,
        dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
                                bclk_ms, pre_div, dai->id);
 
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S16_LE:
+       switch (params_width(params)) {
+       case 16:
                break;
-       case SNDRV_PCM_FORMAT_S20_3LE:
+       case 20:
                val_len |= RT5640_I2S_DL_20;
                break;
-       case SNDRV_PCM_FORMAT_S24_LE:
+       case 24:
                val_len |= RT5640_I2S_DL_24;
                break;
-       case SNDRV_PCM_FORMAT_S8:
+       case 8:
                val_len |= RT5640_I2S_DL_8;
                break;
        default:
@@ -1744,12 +1779,6 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
        case RT5640_SCLK_S_PLL1:
                reg_val |= RT5640_SCLK_SRC_PLL1;
                break;
-       case RT5640_SCLK_S_PLL1_TK:
-               reg_val |= RT5640_SCLK_SRC_PLL1T;
-               break;
-       case RT5640_SCLK_S_RCCLK:
-               reg_val |= RT5640_SCLK_SRC_RCCLK;
-               break;
        default:
                dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
                return -EINVAL;
@@ -1763,65 +1792,12 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
        return 0;
 }
 
-/**
- * rt5640_pll_calc - Calculate PLL M/N/K code.
- * @freq_in: external clock provided to codec.
- * @freq_out: target clock which codec works on.
- * @pll_code: Pointer to structure with M, N, K and bypass flag.
- *
- * Calculate M/N/K code to configure PLL for codec. And K is assigned to 2
- * which make calculation more efficiently.
- *
- * Returns 0 for success or negative error code.
- */
-static int rt5640_pll_calc(const unsigned int freq_in,
-       const unsigned int freq_out, struct rt5640_pll_code *pll_code)
-{
-       int max_n = RT5640_PLL_N_MAX, max_m = RT5640_PLL_M_MAX;
-       int n = 0, m = 0, red, n_t, m_t, in_t, out_t;
-       int red_t = abs(freq_out - freq_in);
-       bool bypass = false;
-
-       if (RT5640_PLL_INP_MAX < freq_in || RT5640_PLL_INP_MIN > freq_in)
-               return -EINVAL;
-
-       for (n_t = 0; n_t <= max_n; n_t++) {
-               in_t = (freq_in >> 1) + (freq_in >> 2) * n_t;
-               if (in_t < 0)
-                       continue;
-               if (in_t == freq_out) {
-                       bypass = true;
-                       n = n_t;
-                       goto code_find;
-               }
-               for (m_t = 0; m_t <= max_m; m_t++) {
-                       out_t = in_t / (m_t + 2);
-                       red = abs(out_t - freq_out);
-                       if (red < red_t) {
-                               n = n_t;
-                               m = m_t;
-                               if (red == 0)
-                                       goto code_find;
-                               red_t = red;
-                       }
-               }
-       }
-       pr_debug("Only get approximation about PLL\n");
-
-code_find:
-       pll_code->m_bp = bypass;
-       pll_code->m_code = m;
-       pll_code->n_code = n;
-       pll_code->k_code = 2;
-       return 0;
-}
-
 static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
                        unsigned int freq_in, unsigned int freq_out)
 {
        struct snd_soc_codec *codec = dai->codec;
        struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
-       struct rt5640_pll_code *pll_code = &rt5640->pll_code;
+       struct rl6231_pll_code pll_code;
        int ret, dai_sel;
 
        if (source == rt5640->pll_src && freq_in == rt5640->pll_in &&
@@ -1865,20 +1841,21 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
                return -EINVAL;
        }
 
-       ret = rt5640_pll_calc(freq_in, freq_out, pll_code);
+       ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
        if (ret < 0) {
                dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
                return ret;
        }
 
-       dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=2\n", pll_code->m_bp,
-               (pll_code->m_bp ? 0 : pll_code->m_code), pll_code->n_code);
+       dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+               pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+               pll_code.n_code, pll_code.k_code);
 
        snd_soc_write(codec, RT5640_PLL_CTRL1,
-               pll_code->n_code << RT5640_PLL_N_SFT | pll_code->k_code);
+               pll_code.n_code << RT5640_PLL_N_SFT | pll_code.k_code);
        snd_soc_write(codec, RT5640_PLL_CTRL2,
-               (pll_code->m_bp ? 0 : pll_code->m_code) << RT5640_PLL_M_SFT |
-               pll_code->m_bp << RT5640_PLL_M_BP_SFT);
+               (pll_code.m_bp ? 0 : pll_code.m_code) << RT5640_PLL_M_SFT |
+               pll_code.m_bp << RT5640_PLL_M_BP_SFT);
 
        rt5640->pll_in = freq_in;
        rt5640->pll_out = freq_out;
@@ -1890,11 +1867,9 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
 static int rt5640_set_bias_level(struct snd_soc_codec *codec,
                        enum snd_soc_bias_level level)
 {
-       struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
        switch (level) {
        case SND_SOC_BIAS_STANDBY:
                if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
-                       regcache_cache_only(rt5640->regmap, false);
                        snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
                                RT5640_PWR_VREF1 | RT5640_PWR_MB |
                                RT5640_PWR_BG | RT5640_PWR_VREF2,
@@ -1904,7 +1879,6 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
                        snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
                                RT5640_PWR_FV1 | RT5640_PWR_FV2,
                                RT5640_PWR_FV1 | RT5640_PWR_FV2);
-                       regcache_sync(rt5640->regmap);
                        snd_soc_update_bits(codec, RT5640_DUMMY1,
                                                0x0301, 0x0301);
                        snd_soc_update_bits(codec, RT5640_MICBIAS,
@@ -1938,13 +1912,39 @@ static int rt5640_probe(struct snd_soc_codec *codec)
 
        rt5640->codec = codec;
 
-       codec->dapm.idle_bias_off = 1;
        rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
        snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301);
        snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030);
        snd_soc_update_bits(codec, RT5640_DSP_PATH2, 0xfc00, 0x0c00);
 
+       switch (snd_soc_read(codec, RT5640_RESET) & RT5640_ID_MASK) {
+       case RT5640_ID_5640:
+       case RT5640_ID_5642:
+               snd_soc_add_codec_controls(codec,
+                       rt5640_specific_snd_controls,
+                       ARRAY_SIZE(rt5640_specific_snd_controls));
+               snd_soc_dapm_new_controls(&codec->dapm,
+                       rt5640_specific_dapm_widgets,
+                       ARRAY_SIZE(rt5640_specific_dapm_widgets));
+               snd_soc_dapm_add_routes(&codec->dapm,
+                       rt5640_specific_dapm_routes,
+                       ARRAY_SIZE(rt5640_specific_dapm_routes));
+               break;
+       case RT5640_ID_5639:
+               snd_soc_dapm_new_controls(&codec->dapm,
+                       rt5639_specific_dapm_widgets,
+                       ARRAY_SIZE(rt5639_specific_dapm_widgets));
+               snd_soc_dapm_add_routes(&codec->dapm,
+                       rt5639_specific_dapm_routes,
+                       ARRAY_SIZE(rt5639_specific_dapm_routes));
+               break;
+       default:
+               dev_err(codec->dev,
+                       "The driver is for RT5639 RT5640 or RT5642 only\n");
+               return -ENODEV;
+       }
+
        return 0;
 }
 
@@ -1979,6 +1979,9 @@ static int rt5640_resume(struct snd_soc_codec *codec)
                msleep(400);
        }
 
+       regcache_cache_only(rt5640->regmap, false);
+       regcache_sync(rt5640->regmap);
+
        return 0;
 }
 #else
@@ -2044,6 +2047,7 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5640 = {
        .suspend = rt5640_suspend,
        .resume = rt5640_resume,
        .set_bias_level = rt5640_set_bias_level,
+       .idle_bias_off = true,
        .controls = rt5640_snd_controls,
        .num_controls = ARRAY_SIZE(rt5640_snd_controls),
        .dapm_widgets = rt5640_dapm_widgets,
@@ -2070,12 +2074,15 @@ static const struct regmap_config rt5640_regmap = {
 
 static const struct i2c_device_id rt5640_i2c_id[] = {
        { "rt5640", 0 },
+       { "rt5639", 0 },
+       { "rt5642", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id);
 
 #if defined(CONFIG_OF)
 static const struct of_device_id rt5640_of_match[] = {
+       { .compatible = "realtek,rt5639", },
        { .compatible = "realtek,rt5640", },
        {},
 };
@@ -2166,7 +2173,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
        }
 
        regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
-       if ((val != RT5640_DEVICE_ID)) {
+       if (val != RT5640_DEVICE_ID) {
                dev_err(&i2c->dev,
                        "Device with ID register %x is not rt5640/39\n", val);
                return -ENODEV;
@@ -2187,6 +2194,25 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
                regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
                                        RT5640_IN_DF2, RT5640_IN_DF2);
 
+       if (rt5640->pdata.dmic_en) {
+               regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
+                       RT5640_GP2_PIN_MASK, RT5640_GP2_PIN_DMIC1_SCL);
+
+               if (rt5640->pdata.dmic1_data_pin) {
+                       regmap_update_bits(rt5640->regmap, RT5640_DMIC,
+                               RT5640_DMIC_1_DP_MASK, RT5640_DMIC_1_DP_GPIO3);
+                       regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
+                               RT5640_GP3_PIN_MASK, RT5640_GP3_PIN_DMIC1_SDA);
+               }
+
+               if (rt5640->pdata.dmic2_data_pin) {
+                       regmap_update_bits(rt5640->regmap, RT5640_DMIC,
+                               RT5640_DMIC_2_DP_MASK, RT5640_DMIC_2_DP_GPIO4);
+                       regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1,
+                               RT5640_GP4_PIN_MASK, RT5640_GP4_PIN_DMIC2_SDA);
+               }
+       }
+
        rt5640->hp_mute = 1;
 
        ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
@@ -2219,6 +2245,6 @@ static struct i2c_driver rt5640_i2c_driver = {
 };
 module_i2c_driver(rt5640_i2c_driver);
 
-MODULE_DESCRIPTION("ASoC RT5640 driver");
+MODULE_DESCRIPTION("ASoC RT5640/RT5639 driver");
 MODULE_AUTHOR("Johnny Hsu <johnnyhsu@realtek.com>");
 MODULE_LICENSE("GPL v2");
index 5e8df25..58ebe96 100644 (file)
 #define RT5640_R_VOL_MASK                      (0x3f)
 #define RT5640_R_VOL_SFT                       0
 
+/* SW Reset & Device ID (0x00) */
+#define RT5640_ID_MASK                         (0x3 << 1)
+#define RT5640_ID_5639                         (0x0 << 1)
+#define RT5640_ID_5640                         (0x2 << 1)
+#define RT5640_ID_5642                         (0x3 << 1)
+
+
 /* IN1 and IN2 Control (0x0d) */
 /* IN3 and IN4 Control (0x0e) */
 #define RT5640_BST_SFT1                                12
 #define RT5640_SCLK_SRC_SFT                    14
 #define RT5640_SCLK_SRC_MCLK                   (0x0 << 14)
 #define RT5640_SCLK_SRC_PLL1                   (0x1 << 14)
-#define RT5640_SCLK_SRC_PLL1T                  (0x2 << 14)
-#define RT5640_SCLK_SRC_RCCLK                  (0x3 << 14) /* 15MHz */
 #define RT5640_PLL1_SRC_MASK                   (0x3 << 12)
 #define RT5640_PLL1_SRC_SFT                    12
 #define RT5640_PLL1_SRC_MCLK                   (0x0 << 12)
@@ -2074,13 +2079,6 @@ enum {
        RT5640_DMIC2,
 };
 
-struct rt5640_pll_code {
-       bool m_bp; /* Indicates bypass m code or not. */
-       int m_code;
-       int n_code;
-       int k_code;
-};
-
 struct rt5640_priv {
        struct snd_soc_codec *codec;
        struct rt5640_platform_data pdata;
@@ -2092,12 +2090,10 @@ struct rt5640_priv {
        int bclk[RT5640_AIFS];
        int master[RT5640_AIFS];
 
-       struct rt5640_pll_code pll_code;
        int pll_src;
        int pll_in;
        int pll_out;
 
-       int dmic_en;
        bool hp_mute;
 };
 
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
new file mode 100644 (file)
index 0000000..02147be
--- /dev/null
@@ -0,0 +1,2378 @@
+/*
+ * rt5645.c  --  RT5645 ALSA SoC audio codec driver
+ *
+ * Copyright 2013 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.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/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt5645.h"
+
+#define RT5645_DEVICE_ID 0x6308
+
+#define RT5645_PR_RANGE_BASE (0xff + 1)
+#define RT5645_PR_SPACING 0x100
+
+#define RT5645_PR_BASE (RT5645_PR_RANGE_BASE + (0 * RT5645_PR_SPACING))
+
+static const struct regmap_range_cfg rt5645_ranges[] = {
+       {
+               .name = "PR",
+               .range_min = RT5645_PR_BASE,
+               .range_max = RT5645_PR_BASE + 0xf8,
+               .selector_reg = RT5645_PRIV_INDEX,
+               .selector_mask = 0xff,
+               .selector_shift = 0x0,
+               .window_start = RT5645_PRIV_DATA,
+               .window_len = 0x1,
+       },
+};
+
+static const struct reg_default init_list[] = {
+       {RT5645_PR_BASE + 0x3d, 0x3600},
+       {RT5645_PR_BASE + 0x1c, 0xfd20},
+       {RT5645_PR_BASE + 0x20, 0x611f},
+       {RT5645_PR_BASE + 0x21, 0x4040},
+       {RT5645_PR_BASE + 0x23, 0x0004},
+};
+#define RT5645_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+static const struct reg_default rt5645_reg[] = {
+       { 0x00, 0x0000 },
+       { 0x01, 0xc8c8 },
+       { 0x02, 0xc8c8 },
+       { 0x03, 0xc8c8 },
+       { 0x0a, 0x0002 },
+       { 0x0b, 0x2827 },
+       { 0x0c, 0xe000 },
+       { 0x0d, 0x0000 },
+       { 0x0e, 0x0000 },
+       { 0x0f, 0x0808 },
+       { 0x14, 0x3333 },
+       { 0x16, 0x4b00 },
+       { 0x18, 0x018b },
+       { 0x19, 0xafaf },
+       { 0x1a, 0xafaf },
+       { 0x1b, 0x0001 },
+       { 0x1c, 0x2f2f },
+       { 0x1d, 0x2f2f },
+       { 0x1e, 0x0000 },
+       { 0x20, 0x0000 },
+       { 0x27, 0x7060 },
+       { 0x28, 0x7070 },
+       { 0x29, 0x8080 },
+       { 0x2a, 0x5656 },
+       { 0x2b, 0x5454 },
+       { 0x2c, 0xaaa0 },
+       { 0x2f, 0x1002 },
+       { 0x31, 0x5000 },
+       { 0x32, 0x0000 },
+       { 0x33, 0x0000 },
+       { 0x34, 0x0000 },
+       { 0x35, 0x0000 },
+       { 0x3b, 0x0000 },
+       { 0x3c, 0x007f },
+       { 0x3d, 0x0000 },
+       { 0x3e, 0x007f },
+       { 0x3f, 0x0000 },
+       { 0x40, 0x001f },
+       { 0x41, 0x0000 },
+       { 0x42, 0x001f },
+       { 0x45, 0x6000 },
+       { 0x46, 0x003e },
+       { 0x47, 0x003e },
+       { 0x48, 0xf807 },
+       { 0x4a, 0x0004 },
+       { 0x4d, 0x0000 },
+       { 0x4e, 0x0000 },
+       { 0x4f, 0x01ff },
+       { 0x50, 0x0000 },
+       { 0x51, 0x0000 },
+       { 0x52, 0x01ff },
+       { 0x53, 0xf000 },
+       { 0x56, 0x0111 },
+       { 0x57, 0x0064 },
+       { 0x58, 0xef0e },
+       { 0x59, 0xf0f0 },
+       { 0x5a, 0xef0e },
+       { 0x5b, 0xf0f0 },
+       { 0x5c, 0xef0e },
+       { 0x5d, 0xf0f0 },
+       { 0x5e, 0xf000 },
+       { 0x5f, 0x0000 },
+       { 0x61, 0x0300 },
+       { 0x62, 0x0000 },
+       { 0x63, 0x00c2 },
+       { 0x64, 0x0000 },
+       { 0x65, 0x0000 },
+       { 0x66, 0x0000 },
+       { 0x6a, 0x0000 },
+       { 0x6c, 0x0aaa },
+       { 0x70, 0x8000 },
+       { 0x71, 0x8000 },
+       { 0x72, 0x8000 },
+       { 0x73, 0x7770 },
+       { 0x74, 0x3e00 },
+       { 0x75, 0x2409 },
+       { 0x76, 0x000a },
+       { 0x77, 0x0c00 },
+       { 0x78, 0x0000 },
+       { 0x80, 0x0000 },
+       { 0x81, 0x0000 },
+       { 0x82, 0x0000 },
+       { 0x83, 0x0000 },
+       { 0x84, 0x0000 },
+       { 0x85, 0x0000 },
+       { 0x8a, 0x0000 },
+       { 0x8e, 0x0004 },
+       { 0x8f, 0x1100 },
+       { 0x90, 0x0646 },
+       { 0x91, 0x0c06 },
+       { 0x93, 0x0000 },
+       { 0x94, 0x0200 },
+       { 0x95, 0x0000 },
+       { 0x9a, 0x2184 },
+       { 0x9b, 0x010a },
+       { 0x9c, 0x0aea },
+       { 0x9d, 0x000c },
+       { 0x9e, 0x0400 },
+       { 0xa0, 0xa0a8 },
+       { 0xa1, 0x0059 },
+       { 0xa2, 0x0001 },
+       { 0xae, 0x6000 },
+       { 0xaf, 0x0000 },
+       { 0xb0, 0x6000 },
+       { 0xb1, 0x0000 },
+       { 0xb2, 0x0000 },
+       { 0xb3, 0x001f },
+       { 0xb4, 0x020c },
+       { 0xb5, 0x1f00 },
+       { 0xb6, 0x0000 },
+       { 0xbb, 0x0000 },
+       { 0xbc, 0x0000 },
+       { 0xbd, 0x0000 },
+       { 0xbe, 0x0000 },
+       { 0xbf, 0x3100 },
+       { 0xc0, 0x0000 },
+       { 0xc1, 0x0000 },
+       { 0xc2, 0x0000 },
+       { 0xc3, 0x2000 },
+       { 0xcd, 0x0000 },
+       { 0xce, 0x0000 },
+       { 0xcf, 0x1813 },
+       { 0xd0, 0x0690 },
+       { 0xd1, 0x1c17 },
+       { 0xd3, 0xb320 },
+       { 0xd4, 0x0000 },
+       { 0xd6, 0x0400 },
+       { 0xd9, 0x0809 },
+       { 0xda, 0x0000 },
+       { 0xdb, 0x0003 },
+       { 0xdc, 0x0049 },
+       { 0xdd, 0x001b },
+       { 0xe6, 0x8000 },
+       { 0xe7, 0x0200 },
+       { 0xec, 0xb300 },
+       { 0xed, 0x0000 },
+       { 0xf0, 0x001f },
+       { 0xf1, 0x020c },
+       { 0xf2, 0x1f00 },
+       { 0xf3, 0x0000 },
+       { 0xf4, 0x4000 },
+       { 0xf8, 0x0000 },
+       { 0xf9, 0x0000 },
+       { 0xfa, 0x2060 },
+       { 0xfb, 0x4040 },
+       { 0xfc, 0x0000 },
+       { 0xfd, 0x0002 },
+       { 0xfe, 0x10ec },
+       { 0xff, 0x6308 },
+};
+
+static int rt5645_reset(struct snd_soc_codec *codec)
+{
+       return snd_soc_write(codec, RT5645_RESET, 0);
+}
+
+static bool rt5645_volatile_register(struct device *dev, unsigned int reg)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rt5645_ranges); i++) {
+               if (reg >= rt5645_ranges[i].range_min &&
+                       reg <= rt5645_ranges[i].range_max) {
+                       return true;
+               }
+       }
+
+       switch (reg) {
+       case RT5645_RESET:
+       case RT5645_PRIV_DATA:
+       case RT5645_IN1_CTRL1:
+       case RT5645_IN1_CTRL2:
+       case RT5645_IN1_CTRL3:
+       case RT5645_A_JD_CTRL1:
+       case RT5645_ADC_EQ_CTRL1:
+       case RT5645_EQ_CTRL1:
+       case RT5645_ALC_CTRL_1:
+       case RT5645_IRQ_CTRL2:
+       case RT5645_IRQ_CTRL3:
+       case RT5645_INT_IRQ_ST:
+       case RT5645_IL_CMD:
+       case RT5645_VENDOR_ID:
+       case RT5645_VENDOR_ID1:
+       case RT5645_VENDOR_ID2:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool rt5645_readable_register(struct device *dev, unsigned int reg)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rt5645_ranges); i++) {
+               if (reg >= rt5645_ranges[i].range_min &&
+                       reg <= rt5645_ranges[i].range_max) {
+                       return true;
+               }
+       }
+
+       switch (reg) {
+       case RT5645_RESET:
+       case RT5645_SPK_VOL:
+       case RT5645_HP_VOL:
+       case RT5645_LOUT1:
+       case RT5645_IN1_CTRL1:
+       case RT5645_IN1_CTRL2:
+       case RT5645_IN1_CTRL3:
+       case RT5645_IN2_CTRL:
+       case RT5645_INL1_INR1_VOL:
+       case RT5645_SPK_FUNC_LIM:
+       case RT5645_ADJ_HPF_CTRL:
+       case RT5645_DAC1_DIG_VOL:
+       case RT5645_DAC2_DIG_VOL:
+       case RT5645_DAC_CTRL:
+       case RT5645_STO1_ADC_DIG_VOL:
+       case RT5645_MONO_ADC_DIG_VOL:
+       case RT5645_ADC_BST_VOL1:
+       case RT5645_ADC_BST_VOL2:
+       case RT5645_STO1_ADC_MIXER:
+       case RT5645_MONO_ADC_MIXER:
+       case RT5645_AD_DA_MIXER:
+       case RT5645_STO_DAC_MIXER:
+       case RT5645_MONO_DAC_MIXER:
+       case RT5645_DIG_MIXER:
+       case RT5645_DIG_INF1_DATA:
+       case RT5645_PDM_OUT_CTRL:
+       case RT5645_REC_L1_MIXER:
+       case RT5645_REC_L2_MIXER:
+       case RT5645_REC_R1_MIXER:
+       case RT5645_REC_R2_MIXER:
+       case RT5645_HPMIXL_CTRL:
+       case RT5645_HPOMIXL_CTRL:
+       case RT5645_HPMIXR_CTRL:
+       case RT5645_HPOMIXR_CTRL:
+       case RT5645_HPO_MIXER:
+       case RT5645_SPK_L_MIXER:
+       case RT5645_SPK_R_MIXER:
+       case RT5645_SPO_MIXER:
+       case RT5645_SPO_CLSD_RATIO:
+       case RT5645_OUT_L1_MIXER:
+       case RT5645_OUT_R1_MIXER:
+       case RT5645_OUT_L_GAIN1:
+       case RT5645_OUT_L_GAIN2:
+       case RT5645_OUT_R_GAIN1:
+       case RT5645_OUT_R_GAIN2:
+       case RT5645_LOUT_MIXER:
+       case RT5645_HAPTIC_CTRL1:
+       case RT5645_HAPTIC_CTRL2:
+       case RT5645_HAPTIC_CTRL3:
+       case RT5645_HAPTIC_CTRL4:
+       case RT5645_HAPTIC_CTRL5:
+       case RT5645_HAPTIC_CTRL6:
+       case RT5645_HAPTIC_CTRL7:
+       case RT5645_HAPTIC_CTRL8:
+       case RT5645_HAPTIC_CTRL9:
+       case RT5645_HAPTIC_CTRL10:
+       case RT5645_PWR_DIG1:
+       case RT5645_PWR_DIG2:
+       case RT5645_PWR_ANLG1:
+       case RT5645_PWR_ANLG2:
+       case RT5645_PWR_MIXER:
+       case RT5645_PWR_VOL:
+       case RT5645_PRIV_INDEX:
+       case RT5645_PRIV_DATA:
+       case RT5645_I2S1_SDP:
+       case RT5645_I2S2_SDP:
+       case RT5645_ADDA_CLK1:
+       case RT5645_ADDA_CLK2:
+       case RT5645_DMIC_CTRL1:
+       case RT5645_DMIC_CTRL2:
+       case RT5645_TDM_CTRL_1:
+       case RT5645_TDM_CTRL_2:
+       case RT5645_GLB_CLK:
+       case RT5645_PLL_CTRL1:
+       case RT5645_PLL_CTRL2:
+       case RT5645_ASRC_1:
+       case RT5645_ASRC_2:
+       case RT5645_ASRC_3:
+       case RT5645_ASRC_4:
+       case RT5645_DEPOP_M1:
+       case RT5645_DEPOP_M2:
+       case RT5645_DEPOP_M3:
+       case RT5645_MICBIAS:
+       case RT5645_A_JD_CTRL1:
+       case RT5645_VAD_CTRL4:
+       case RT5645_CLSD_OUT_CTRL:
+       case RT5645_ADC_EQ_CTRL1:
+       case RT5645_ADC_EQ_CTRL2:
+       case RT5645_EQ_CTRL1:
+       case RT5645_EQ_CTRL2:
+       case RT5645_ALC_CTRL_1:
+       case RT5645_ALC_CTRL_2:
+       case RT5645_ALC_CTRL_3:
+       case RT5645_ALC_CTRL_4:
+       case RT5645_ALC_CTRL_5:
+       case RT5645_JD_CTRL:
+       case RT5645_IRQ_CTRL1:
+       case RT5645_IRQ_CTRL2:
+       case RT5645_IRQ_CTRL3:
+       case RT5645_INT_IRQ_ST:
+       case RT5645_GPIO_CTRL1:
+       case RT5645_GPIO_CTRL2:
+       case RT5645_GPIO_CTRL3:
+       case RT5645_BASS_BACK:
+       case RT5645_MP3_PLUS1:
+       case RT5645_MP3_PLUS2:
+       case RT5645_ADJ_HPF1:
+       case RT5645_ADJ_HPF2:
+       case RT5645_HP_CALIB_AMP_DET:
+       case RT5645_SV_ZCD1:
+       case RT5645_SV_ZCD2:
+       case RT5645_IL_CMD:
+       case RT5645_IL_CMD2:
+       case RT5645_IL_CMD3:
+       case RT5645_DRC1_HL_CTRL1:
+       case RT5645_DRC2_HL_CTRL1:
+       case RT5645_ADC_MONO_HP_CTRL1:
+       case RT5645_ADC_MONO_HP_CTRL2:
+       case RT5645_DRC2_CTRL1:
+       case RT5645_DRC2_CTRL2:
+       case RT5645_DRC2_CTRL3:
+       case RT5645_DRC2_CTRL4:
+       case RT5645_DRC2_CTRL5:
+       case RT5645_JD_CTRL3:
+       case RT5645_JD_CTRL4:
+       case RT5645_GEN_CTRL1:
+       case RT5645_GEN_CTRL2:
+       case RT5645_GEN_CTRL3:
+       case RT5645_VENDOR_ID:
+       case RT5645_VENDOR_ID1:
+       case RT5645_VENDOR_ID2:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+
+/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
+static unsigned int bst_tlv[] = {
+       TLV_DB_RANGE_HEAD(7),
+       0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+       1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+       2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+       3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+       6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+       7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+       8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
+};
+
+static const char * const rt5645_tdm_data_swap_select[] = {
+       "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot0_1_enum,
+       RT5645_TDM_CTRL_1, 6, rt5645_tdm_data_swap_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot2_3_enum,
+       RT5645_TDM_CTRL_1, 4, rt5645_tdm_data_swap_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot4_5_enum,
+       RT5645_TDM_CTRL_1, 2, rt5645_tdm_data_swap_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot6_7_enum,
+       RT5645_TDM_CTRL_1, 0, rt5645_tdm_data_swap_select);
+
+static const char * const rt5645_tdm_adc_data_select[] = {
+       "1/2/R", "2/1/R", "R/1/2", "R/2/1"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_sel_enum,
+                               RT5645_TDM_CTRL_1, 8,
+                               rt5645_tdm_adc_data_select);
+
+static const struct snd_kcontrol_new rt5645_snd_controls[] = {
+       /* Speaker Output Volume */
+       SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
+               RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
+       SOC_DOUBLE_TLV("Speaker Playback Volume", RT5645_SPK_VOL,
+               RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
+
+       /* Headphone Output Volume */
+       SOC_DOUBLE("HP Channel Switch", RT5645_HP_VOL,
+               RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
+       SOC_DOUBLE_TLV("HP Playback Volume", RT5645_HP_VOL,
+               RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
+
+       /* OUTPUT Control */
+       SOC_DOUBLE("OUT Playback Switch", RT5645_LOUT1,
+               RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1),
+       SOC_DOUBLE("OUT Channel Switch", RT5645_LOUT1,
+               RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
+       SOC_DOUBLE_TLV("OUT Playback Volume", RT5645_LOUT1,
+               RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
+
+       /* DAC Digital Volume */
+       SOC_DOUBLE("DAC2 Playback Switch", RT5645_DAC_CTRL,
+               RT5645_M_DAC_L2_VOL_SFT, RT5645_M_DAC_R2_VOL_SFT, 1, 1),
+       SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5645_DAC1_DIG_VOL,
+               RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 175, 0, dac_vol_tlv),
+       SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5645_DAC2_DIG_VOL,
+               RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 175, 0, dac_vol_tlv),
+
+       /* IN1/IN2 Control */
+       SOC_SINGLE_TLV("IN1 Boost", RT5645_IN1_CTRL1,
+               RT5645_BST_SFT1, 8, 0, bst_tlv),
+       SOC_SINGLE_TLV("IN2 Boost", RT5645_IN2_CTRL,
+               RT5645_BST_SFT2, 8, 0, bst_tlv),
+
+       /* INL/INR Volume Control */
+       SOC_DOUBLE_TLV("IN Capture Volume", RT5645_INL1_INR1_VOL,
+               RT5645_INL_VOL_SFT, RT5645_INR_VOL_SFT, 31, 1, in_vol_tlv),
+
+       /* ADC Digital Volume Control */
+       SOC_DOUBLE("ADC Capture Switch", RT5645_STO1_ADC_DIG_VOL,
+               RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1),
+       SOC_DOUBLE_TLV("ADC Capture Volume", RT5645_STO1_ADC_DIG_VOL,
+               RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 127, 0, adc_vol_tlv),
+       SOC_DOUBLE("Mono ADC Capture Switch", RT5645_MONO_ADC_DIG_VOL,
+               RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1),
+       SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5645_MONO_ADC_DIG_VOL,
+               RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 127, 0, adc_vol_tlv),
+
+       /* ADC Boost Volume Control */
+       SOC_DOUBLE_TLV("STO1 ADC Boost Gain", RT5645_ADC_BST_VOL1,
+               RT5645_STO1_ADC_L_BST_SFT, RT5645_STO1_ADC_R_BST_SFT, 3, 0,
+               adc_bst_tlv),
+       SOC_DOUBLE_TLV("STO2 ADC Boost Gain", RT5645_ADC_BST_VOL1,
+               RT5645_STO2_ADC_L_BST_SFT, RT5645_STO2_ADC_R_BST_SFT, 3, 0,
+               adc_bst_tlv),
+
+       /* I2S2 function select */
+       SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT,
+               1, 1),
+
+       /* TDM */
+       SOC_ENUM("TDM Adc Slot0 1 Data", rt5645_tdm_adc_slot0_1_enum),
+       SOC_ENUM("TDM Adc Slot2 3 Data", rt5645_tdm_adc_slot2_3_enum),
+       SOC_ENUM("TDM Adc Slot4 5 Data", rt5645_tdm_adc_slot4_5_enum),
+       SOC_ENUM("TDM Adc Slot6 7 Data", rt5645_tdm_adc_slot6_7_enum),
+       SOC_ENUM("TDM IF1 ADC DATA Sel", rt5645_tdm_adc_sel_enum),
+       SOC_SINGLE("TDM IF1_DAC1_L Sel", RT5645_TDM_CTRL_3, 12, 7, 0),
+       SOC_SINGLE("TDM IF1_DAC1_R Sel", RT5645_TDM_CTRL_3, 8, 7, 0),
+       SOC_SINGLE("TDM IF1_DAC2_L Sel", RT5645_TDM_CTRL_3, 4, 7, 0),
+       SOC_SINGLE("TDM IF1_DAC2_R Sel", RT5645_TDM_CTRL_3, 0, 7, 0),
+};
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+       int idx = -EINVAL;
+
+       idx = rl6231_calc_dmic_clk(rt5645->sysclk);
+
+       if (idx < 0)
+               dev_err(codec->dev, "Failed to set DMIC clock\n");
+       else
+               snd_soc_update_bits(codec, RT5645_DMIC_CTRL1,
+                       RT5645_DMIC_CLK_MASK, idx << RT5645_DMIC_CLK_SFT);
+       return idx;
+}
+
+static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink)
+{
+       unsigned int val;
+
+       val = snd_soc_read(source->codec, RT5645_GLB_CLK);
+       val &= RT5645_SCLK_SRC_MASK;
+       if (val == RT5645_SCLK_SRC_PLL1)
+               return 1;
+       else
+               return 0;
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5645_sto1_adc_l_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER,
+                       RT5645_M_ADC_L1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5645_STO1_ADC_MIXER,
+                       RT5645_M_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_sto1_adc_r_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER,
+                       RT5645_M_ADC_R1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5645_STO1_ADC_MIXER,
+                       RT5645_M_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_mono_adc_l_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5645_MONO_ADC_MIXER,
+                       RT5645_M_MONO_ADC_L1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5645_MONO_ADC_MIXER,
+                       RT5645_M_MONO_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_mono_adc_r_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5645_MONO_ADC_MIXER,
+                       RT5645_M_MONO_ADC_R1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5645_MONO_ADC_MIXER,
+                       RT5645_M_MONO_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_dac_l_mix[] = {
+       SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER,
+                       RT5645_M_ADCMIX_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC1 Switch", RT5645_AD_DA_MIXER,
+                       RT5645_M_DAC1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_dac_r_mix[] = {
+       SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER,
+                       RT5645_M_ADCMIX_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC1 Switch", RT5645_AD_DA_MIXER,
+                       RT5645_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_sto_dac_l_mix[] = {
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_STO_DAC_MIXER,
+                       RT5645_M_DAC_L1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_STO_DAC_MIXER,
+                       RT5645_M_DAC_L2_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_STO_DAC_MIXER,
+                       RT5645_M_DAC_R1_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_sto_dac_r_mix[] = {
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_STO_DAC_MIXER,
+                       RT5645_M_DAC_R1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_STO_DAC_MIXER,
+                       RT5645_M_DAC_R2_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_STO_DAC_MIXER,
+                       RT5645_M_DAC_L1_STO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_mono_dac_l_mix[] = {
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_MONO_DAC_MIXER,
+                       RT5645_M_DAC_L1_MONO_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_MONO_DAC_MIXER,
+                       RT5645_M_DAC_L2_MONO_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_MONO_DAC_MIXER,
+                       RT5645_M_DAC_R2_MONO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_mono_dac_r_mix[] = {
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_MONO_DAC_MIXER,
+                       RT5645_M_DAC_R1_MONO_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_MONO_DAC_MIXER,
+                       RT5645_M_DAC_R2_MONO_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_MONO_DAC_MIXER,
+                       RT5645_M_DAC_L2_MONO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_dig_l_mix[] = {
+       SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5645_DIG_MIXER,
+                       RT5645_M_STO_L_DAC_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_DIG_MIXER,
+                       RT5645_M_DAC_L2_DAC_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_DIG_MIXER,
+                       RT5645_M_DAC_R2_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_dig_r_mix[] = {
+       SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5645_DIG_MIXER,
+                       RT5645_M_STO_R_DAC_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_DIG_MIXER,
+                       RT5645_M_DAC_R2_DAC_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_DIG_MIXER,
+                       RT5645_M_DAC_L2_DAC_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5645_rec_l_mix[] = {
+       SOC_DAPM_SINGLE("HPOL Switch", RT5645_REC_L2_MIXER,
+                       RT5645_M_HP_L_RM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INL Switch", RT5645_REC_L2_MIXER,
+                       RT5645_M_IN_L_RM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST2 Switch", RT5645_REC_L2_MIXER,
+                       RT5645_M_BST2_RM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST1 Switch", RT5645_REC_L2_MIXER,
+                       RT5645_M_BST1_RM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("OUT MIXL Switch", RT5645_REC_L2_MIXER,
+                       RT5645_M_OM_L_RM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_rec_r_mix[] = {
+       SOC_DAPM_SINGLE("HPOR Switch", RT5645_REC_R2_MIXER,
+                       RT5645_M_HP_R_RM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INR Switch", RT5645_REC_R2_MIXER,
+                       RT5645_M_IN_R_RM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST2 Switch", RT5645_REC_R2_MIXER,
+                       RT5645_M_BST2_RM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST1 Switch", RT5645_REC_R2_MIXER,
+                       RT5645_M_BST1_RM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("OUT MIXR Switch", RT5645_REC_R2_MIXER,
+                       RT5645_M_OM_R_RM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_spk_l_mix[] = {
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_SPK_L_MIXER,
+                       RT5645_M_DAC_L1_SM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_SPK_L_MIXER,
+                       RT5645_M_DAC_L2_SM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INL Switch", RT5645_SPK_L_MIXER,
+                       RT5645_M_IN_L_SM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST1 Switch", RT5645_SPK_L_MIXER,
+                       RT5645_M_BST1_L_SM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_spk_r_mix[] = {
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPK_R_MIXER,
+                       RT5645_M_DAC_R1_SM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_SPK_R_MIXER,
+                       RT5645_M_DAC_R2_SM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INR Switch", RT5645_SPK_R_MIXER,
+                       RT5645_M_IN_R_SM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST2 Switch", RT5645_SPK_R_MIXER,
+                       RT5645_M_BST2_R_SM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_out_l_mix[] = {
+       SOC_DAPM_SINGLE("BST1 Switch", RT5645_OUT_L1_MIXER,
+                       RT5645_M_BST1_OM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INL Switch", RT5645_OUT_L1_MIXER,
+                       RT5645_M_IN_L_OM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_OUT_L1_MIXER,
+                       RT5645_M_DAC_L2_OM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_OUT_L1_MIXER,
+                       RT5645_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_out_r_mix[] = {
+       SOC_DAPM_SINGLE("BST2 Switch", RT5645_OUT_R1_MIXER,
+                       RT5645_M_BST2_OM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INR Switch", RT5645_OUT_R1_MIXER,
+                       RT5645_M_IN_R_OM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_OUT_R1_MIXER,
+                       RT5645_M_DAC_R2_OM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_OUT_R1_MIXER,
+                       RT5645_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_spo_l_mix[] = {
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPO_MIXER,
+                       RT5645_M_DAC_R1_SPM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_SPO_MIXER,
+                       RT5645_M_DAC_L1_SPM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("SPKVOL R Switch", RT5645_SPO_MIXER,
+                       RT5645_M_SV_R_SPM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("SPKVOL L Switch", RT5645_SPO_MIXER,
+                       RT5645_M_SV_L_SPM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_spo_r_mix[] = {
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPO_MIXER,
+                       RT5645_M_DAC_R1_SPM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("SPKVOL R Switch", RT5645_SPO_MIXER,
+                       RT5645_M_SV_R_SPM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_hpo_mix[] = {
+       SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPO_MIXER,
+                       RT5645_M_DAC1_HM_SFT, 1, 1),
+       SOC_DAPM_SINGLE("HPVOL Switch", RT5645_HPO_MIXER,
+                       RT5645_M_HPVOL_HM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_hpvoll_mix[] = {
+       SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPOMIXL_CTRL,
+                       RT5645_M_DAC1_HV_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC2 Switch", RT5645_HPOMIXL_CTRL,
+                       RT5645_M_DAC2_HV_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INL Switch", RT5645_HPOMIXL_CTRL,
+                       RT5645_M_IN_HV_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST1 Switch", RT5645_HPOMIXL_CTRL,
+                       RT5645_M_BST1_HV_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_hpvolr_mix[] = {
+       SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPOMIXR_CTRL,
+                       RT5645_M_DAC1_HV_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC2 Switch", RT5645_HPOMIXR_CTRL,
+                       RT5645_M_DAC2_HV_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INR Switch", RT5645_HPOMIXR_CTRL,
+                       RT5645_M_IN_HV_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST2 Switch", RT5645_HPOMIXR_CTRL,
+                       RT5645_M_BST2_HV_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5645_lout_mix[] = {
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_LOUT_MIXER,
+                       RT5645_M_DAC_L1_LM_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_LOUT_MIXER,
+                       RT5645_M_DAC_R1_LM_SFT, 1, 1),
+       SOC_DAPM_SINGLE("OUTMIX L Switch", RT5645_LOUT_MIXER,
+                       RT5645_M_OV_L_LM_SFT, 1, 1),
+       SOC_DAPM_SINGLE("OUTMIX R Switch", RT5645_LOUT_MIXER,
+                       RT5645_M_OV_R_LM_SFT, 1, 1),
+};
+
+/*DAC1 L/R source*/ /* MX-29 [9:8] [11:10] */
+static const char * const rt5645_dac1_src[] = {
+       "IF1 DAC", "IF2 DAC", "IF3 DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_dac1l_enum, RT5645_AD_DA_MIXER,
+       RT5645_DAC1_L_SEL_SFT, rt5645_dac1_src);
+
+static const struct snd_kcontrol_new rt5645_dac1l_mux =
+       SOC_DAPM_ENUM("DAC1 L source", rt5645_dac1l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_dac1r_enum, RT5645_AD_DA_MIXER,
+       RT5645_DAC1_R_SEL_SFT, rt5645_dac1_src);
+
+static const struct snd_kcontrol_new rt5645_dac1r_mux =
+       SOC_DAPM_ENUM("DAC1 R source", rt5645_dac1r_enum);
+
+/*DAC2 L/R source*/ /* MX-1B [6:4] [2:0] */
+static const char * const rt5645_dac12_src[] = {
+       "IF1 DAC", "IF2 DAC", "IF3 DAC", "Mono ADC", "VAD_ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_dac2l_enum, RT5645_DAC_CTRL,
+       RT5645_DAC2_L_SEL_SFT, rt5645_dac12_src);
+
+static const struct snd_kcontrol_new rt5645_dac_l2_mux =
+       SOC_DAPM_ENUM("DAC2 L source", rt5645_dac2l_enum);
+
+static const char * const rt5645_dacr2_src[] = {
+       "IF1 DAC", "IF2 DAC", "IF3 DAC", "Mono ADC", "Haptic"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_dac2r_enum, RT5645_DAC_CTRL,
+       RT5645_DAC2_R_SEL_SFT, rt5645_dacr2_src);
+
+static const struct snd_kcontrol_new rt5645_dac_r2_mux =
+       SOC_DAPM_ENUM("DAC2 R source", rt5645_dac2r_enum);
+
+
+/* INL/R source */
+static const char * const rt5645_inl_src[] = {
+       "IN2P", "MonoP"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_inl_enum, RT5645_INL1_INR1_VOL,
+       RT5645_INL_SEL_SFT, rt5645_inl_src);
+
+static const struct snd_kcontrol_new rt5645_inl_mux =
+       SOC_DAPM_ENUM("INL source", rt5645_inl_enum);
+
+static const char * const rt5645_inr_src[] = {
+       "IN2N", "MonoN"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_inr_enum, RT5645_INL1_INR1_VOL,
+       RT5645_INR_SEL_SFT, rt5645_inr_src);
+
+static const struct snd_kcontrol_new rt5645_inr_mux =
+       SOC_DAPM_ENUM("INR source", rt5645_inr_enum);
+
+/* Stereo1 ADC source */
+/* MX-27 [12] */
+static const char * const rt5645_stereo_adc1_src[] = {
+       "DAC MIX", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_stereo1_adc1_enum, RT5645_STO1_ADC_MIXER,
+       RT5645_ADC_1_SRC_SFT, rt5645_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5645_sto_adc1_mux =
+       SOC_DAPM_ENUM("Stereo1 ADC1 Mux", rt5645_stereo1_adc1_enum);
+
+/* MX-27 [11] */
+static const char * const rt5645_stereo_adc2_src[] = {
+       "DAC MIX", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_stereo1_adc2_enum, RT5645_STO1_ADC_MIXER,
+       RT5645_ADC_2_SRC_SFT, rt5645_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5645_sto_adc2_mux =
+       SOC_DAPM_ENUM("Stereo1 ADC2 Mux", rt5645_stereo1_adc2_enum);
+
+/* MX-27 [8] */
+static const char * const rt5645_stereo_dmic_src[] = {
+       "DMIC1", "DMIC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_stereo1_dmic_enum, RT5645_STO1_ADC_MIXER,
+       RT5645_DMIC_SRC_SFT, rt5645_stereo_dmic_src);
+
+static const struct snd_kcontrol_new rt5645_sto1_dmic_mux =
+       SOC_DAPM_ENUM("Stereo1 DMIC source", rt5645_stereo1_dmic_enum);
+
+/* Mono ADC source */
+/* MX-28 [12] */
+static const char * const rt5645_mono_adc_l1_src[] = {
+       "Mono DAC MIXL", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_mono_adc_l1_enum, RT5645_MONO_ADC_MIXER,
+       RT5645_MONO_ADC_L1_SRC_SFT, rt5645_mono_adc_l1_src);
+
+static const struct snd_kcontrol_new rt5645_mono_adc_l1_mux =
+       SOC_DAPM_ENUM("Mono ADC1 left source", rt5645_mono_adc_l1_enum);
+/* MX-28 [11] */
+static const char * const rt5645_mono_adc_l2_src[] = {
+       "Mono DAC MIXL", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_mono_adc_l2_enum, RT5645_MONO_ADC_MIXER,
+       RT5645_MONO_ADC_L2_SRC_SFT, rt5645_mono_adc_l2_src);
+
+static const struct snd_kcontrol_new rt5645_mono_adc_l2_mux =
+       SOC_DAPM_ENUM("Mono ADC2 left source", rt5645_mono_adc_l2_enum);
+
+/* MX-28 [8] */
+static const char * const rt5645_mono_dmic_src[] = {
+       "DMIC1", "DMIC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_mono_dmic_l_enum, RT5645_MONO_ADC_MIXER,
+       RT5645_MONO_DMIC_L_SRC_SFT, rt5645_mono_dmic_src);
+
+static const struct snd_kcontrol_new rt5645_mono_dmic_l_mux =
+       SOC_DAPM_ENUM("Mono DMIC left source", rt5645_mono_dmic_l_enum);
+/* MX-28 [1:0] */
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_mono_dmic_r_enum, RT5645_MONO_ADC_MIXER,
+       RT5645_MONO_DMIC_R_SRC_SFT, rt5645_mono_dmic_src);
+
+static const struct snd_kcontrol_new rt5645_mono_dmic_r_mux =
+       SOC_DAPM_ENUM("Mono DMIC Right source", rt5645_mono_dmic_r_enum);
+/* MX-28 [4] */
+static const char * const rt5645_mono_adc_r1_src[] = {
+       "Mono DAC MIXR", "ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_mono_adc_r1_enum, RT5645_MONO_ADC_MIXER,
+       RT5645_MONO_ADC_R1_SRC_SFT, rt5645_mono_adc_r1_src);
+
+static const struct snd_kcontrol_new rt5645_mono_adc_r1_mux =
+       SOC_DAPM_ENUM("Mono ADC1 right source", rt5645_mono_adc_r1_enum);
+/* MX-28 [3] */
+static const char * const rt5645_mono_adc_r2_src[] = {
+       "Mono DAC MIXR", "DMIC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_mono_adc_r2_enum, RT5645_MONO_ADC_MIXER,
+       RT5645_MONO_ADC_R2_SRC_SFT, rt5645_mono_adc_r2_src);
+
+static const struct snd_kcontrol_new rt5645_mono_adc_r2_mux =
+       SOC_DAPM_ENUM("Mono ADC2 right source", rt5645_mono_adc_r2_enum);
+
+/* MX-77 [9:8] */
+static const char * const rt5645_if1_adc_in_src[] = {
+       "IF_ADC1", "IF_ADC2", "VAD_ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_if1_adc_in_enum, RT5645_TDM_CTRL_1,
+       RT5645_IF1_ADC_IN_SFT, rt5645_if1_adc_in_src);
+
+static const struct snd_kcontrol_new rt5645_if1_adc_in_mux =
+       SOC_DAPM_ENUM("IF1 ADC IN source", rt5645_if1_adc_in_enum);
+
+/* MX-2F [13:12] */
+static const char * const rt5645_if2_adc_in_src[] = {
+       "IF_ADC1", "IF_ADC2", "VAD_ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_if2_adc_in_enum, RT5645_DIG_INF1_DATA,
+       RT5645_IF2_ADC_IN_SFT, rt5645_if2_adc_in_src);
+
+static const struct snd_kcontrol_new rt5645_if2_adc_in_mux =
+       SOC_DAPM_ENUM("IF2 ADC IN source", rt5645_if2_adc_in_enum);
+
+/* MX-2F [1:0] */
+static const char * const rt5645_if3_adc_in_src[] = {
+       "IF_ADC1", "IF_ADC2", "VAD_ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_if3_adc_in_enum, RT5645_DIG_INF1_DATA,
+       RT5645_IF3_ADC_IN_SFT, rt5645_if3_adc_in_src);
+
+static const struct snd_kcontrol_new rt5645_if3_adc_in_mux =
+       SOC_DAPM_ENUM("IF3 ADC IN source", rt5645_if3_adc_in_enum);
+
+/* MX-31 [15] [13] [11] [9] */
+static const char * const rt5645_pdm_src[] = {
+       "Mono DAC", "Stereo DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_pdm1_l_enum, RT5645_PDM_OUT_CTRL,
+       RT5645_PDM1_L_SFT, rt5645_pdm_src);
+
+static const struct snd_kcontrol_new rt5645_pdm1_l_mux =
+       SOC_DAPM_ENUM("PDM1 L source", rt5645_pdm1_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_pdm1_r_enum, RT5645_PDM_OUT_CTRL,
+       RT5645_PDM1_R_SFT, rt5645_pdm_src);
+
+static const struct snd_kcontrol_new rt5645_pdm1_r_mux =
+       SOC_DAPM_ENUM("PDM1 R source", rt5645_pdm1_r_enum);
+
+/* MX-9D [9:8] */
+static const char * const rt5645_vad_adc_src[] = {
+       "Sto1 ADC L", "Mono ADC L", "Mono ADC R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_vad_adc_enum, RT5645_VAD_CTRL4,
+       RT5645_VAD_SEL_SFT, rt5645_vad_adc_src);
+
+static const struct snd_kcontrol_new rt5645_vad_adc_mux =
+       SOC_DAPM_ENUM("VAD ADC source", rt5645_vad_adc_enum);
+
+static const struct snd_kcontrol_new spk_l_vol_control =
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_SPK_VOL,
+               RT5645_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new spk_r_vol_control =
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_SPK_VOL,
+               RT5645_R_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hp_l_vol_control =
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_HP_VOL,
+               RT5645_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hp_r_vol_control =
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_HP_VOL,
+               RT5645_R_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new pdm1_l_vol_control =
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_PDM_OUT_CTRL,
+               RT5645_M_PDM1_L, 1, 1);
+
+static const struct snd_kcontrol_new pdm1_r_vol_control =
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_PDM_OUT_CTRL,
+               RT5645_M_PDM1_R, 1, 1);
+
+static void hp_amp_power(struct snd_soc_codec *codec, int on)
+{
+       static int hp_amp_power_count;
+       struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
+       if (on) {
+               if (hp_amp_power_count <= 0) {
+                       /* depop parameters */
+                       snd_soc_update_bits(codec, RT5645_DEPOP_M2,
+                               RT5645_DEPOP_MASK, RT5645_DEPOP_MAN);
+                       snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d);
+                       regmap_write(rt5645->regmap, RT5645_PR_BASE +
+                               RT5645_HP_DCC_INT1, 0x9f01);
+                       mdelay(150);
+                       /* headphone amp power on */
+                       snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                               RT5645_PWR_FV1 | RT5645_PWR_FV2 , 0);
+                       snd_soc_update_bits(codec, RT5645_PWR_VOL,
+                               RT5645_PWR_HV_L | RT5645_PWR_HV_R,
+                               RT5645_PWR_HV_L | RT5645_PWR_HV_R);
+                       snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                               RT5645_PWR_HP_L | RT5645_PWR_HP_R |
+                               RT5645_PWR_HA,
+                               RT5645_PWR_HP_L | RT5645_PWR_HP_R |
+                               RT5645_PWR_HA);
+                       mdelay(5);
+                       snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                               RT5645_PWR_FV1 | RT5645_PWR_FV2,
+                               RT5645_PWR_FV1 | RT5645_PWR_FV2);
+
+                       snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+                               RT5645_HP_CO_MASK | RT5645_HP_SG_MASK,
+                               RT5645_HP_CO_EN | RT5645_HP_SG_EN);
+                       regmap_write(rt5645->regmap, RT5645_PR_BASE +
+                               0x14, 0x1aaa);
+                       regmap_write(rt5645->regmap, RT5645_PR_BASE +
+                               0x24, 0x0430);
+               }
+               hp_amp_power_count++;
+       } else {
+               hp_amp_power_count--;
+               if (hp_amp_power_count <= 0) {
+                       snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+                               RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK |
+                               RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS |
+                               RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS);
+                       /* headphone amp power down */
+                       snd_soc_write(codec, RT5645_DEPOP_M1, 0x0000);
+                       snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                               RT5645_PWR_HP_L | RT5645_PWR_HP_R |
+                               RT5645_PWR_HA, 0);
+               }
+       }
+}
+
+static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               hp_amp_power(codec, 1);
+               /* headphone unmute sequence */
+               snd_soc_update_bits(codec, RT5645_DEPOP_M3, RT5645_CP_FQ1_MASK |
+                       RT5645_CP_FQ2_MASK | RT5645_CP_FQ3_MASK,
+                       (RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ1_SFT) |
+                       (RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) |
+                       (RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ3_SFT));
+               regmap_write(rt5645->regmap,
+                       RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00);
+               snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+                       RT5645_SMT_TRIG_MASK, RT5645_SMT_TRIG_EN);
+               snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+                       RT5645_RSTN_MASK, RT5645_RSTN_EN);
+               snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+                       RT5645_RSTN_MASK | RT5645_HP_L_SMT_MASK |
+                       RT5645_HP_R_SMT_MASK, RT5645_RSTN_DIS |
+                       RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
+               msleep(40);
+               snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+                       RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK |
+                       RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS |
+                       RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               /* headphone mute sequence */
+               snd_soc_update_bits(codec, RT5645_DEPOP_M3,
+                       RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK |
+                       RT5645_CP_FQ3_MASK,
+                       (RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ1_SFT) |
+                       (RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) |
+                       (RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ3_SFT));
+               regmap_write(rt5645->regmap,
+                       RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00);
+               snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+                       RT5645_HP_SG_MASK, RT5645_HP_SG_EN);
+               snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+                       RT5645_RSTP_MASK, RT5645_RSTP_EN);
+               snd_soc_update_bits(codec, RT5645_DEPOP_M1,
+                       RT5645_RSTP_MASK | RT5645_HP_L_SMT_MASK |
+                       RT5645_HP_R_SMT_MASK, RT5645_RSTP_DIS |
+                       RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN);
+               msleep(30);
+               hp_amp_power(codec, 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5645_spk_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_POST_PMU:
+               snd_soc_update_bits(codec, RT5645_PWR_DIG1,
+                       RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
+                       RT5645_PWR_CLS_D_L,
+                       RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
+                       RT5645_PWR_CLS_D_L);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, RT5645_PWR_DIG1,
+                       RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
+                       RT5645_PWR_CLS_D_L, 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5645_lout_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_POST_PMU:
+               hp_amp_power(codec, 1);
+               snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                       RT5645_PWR_LM, RT5645_PWR_LM);
+               snd_soc_update_bits(codec, RT5645_LOUT1,
+                       RT5645_L_MUTE | RT5645_R_MUTE, 0);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, RT5645_LOUT1,
+                       RT5645_L_MUTE | RT5645_R_MUTE,
+                       RT5645_L_MUTE | RT5645_R_MUTE);
+               snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                       RT5645_PWR_LM, 0);
+               hp_amp_power(codec, 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5645_bst2_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_POST_PMU:
+               snd_soc_update_bits(codec, RT5645_PWR_ANLG2,
+                       RT5645_PWR_BST2_P, RT5645_PWR_BST2_P);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, RT5645_PWR_ANLG2,
+                       RT5645_PWR_BST2_P, 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY("LDO2", RT5645_PWR_MIXER,
+               RT5645_PWR_LDO2_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("PLL1", RT5645_PWR_ANLG2,
+               RT5645_PWR_PLL_BIT, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("JD Power", RT5645_PWR_ANLG2,
+               RT5645_PWR_JD1_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5645_PWR_VOL,
+               RT5645_PWR_MIC_DET_BIT, 0, NULL, 0),
+
+       /* Input Side */
+       /* micbias */
+       SND_SOC_DAPM_MICBIAS("micbias1", RT5645_PWR_ANLG2,
+                       RT5645_PWR_MB1_BIT, 0),
+       SND_SOC_DAPM_MICBIAS("micbias2", RT5645_PWR_ANLG2,
+                       RT5645_PWR_MB2_BIT, 0),
+       /* Input Lines */
+       SND_SOC_DAPM_INPUT("DMIC L1"),
+       SND_SOC_DAPM_INPUT("DMIC R1"),
+       SND_SOC_DAPM_INPUT("DMIC L2"),
+       SND_SOC_DAPM_INPUT("DMIC R2"),
+
+       SND_SOC_DAPM_INPUT("IN1P"),
+       SND_SOC_DAPM_INPUT("IN1N"),
+       SND_SOC_DAPM_INPUT("IN2P"),
+       SND_SOC_DAPM_INPUT("IN2N"),
+
+       SND_SOC_DAPM_INPUT("Haptic Generator"),
+
+       SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+               set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+       SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5645_DMIC_CTRL1,
+               RT5645_DMIC_1_EN_SFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5645_DMIC_CTRL1,
+               RT5645_DMIC_2_EN_SFT, 0, NULL, 0),
+       /* Boost */
+       SND_SOC_DAPM_PGA("BST1", RT5645_PWR_ANLG2,
+               RT5645_PWR_BST1_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA_E("BST2", RT5645_PWR_ANLG2,
+               RT5645_PWR_BST2_BIT, 0, NULL, 0, rt5645_bst2_event,
+               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+       /* Input Volume */
+       SND_SOC_DAPM_PGA("INL VOL", RT5645_PWR_VOL,
+               RT5645_PWR_IN_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("INR VOL", RT5645_PWR_VOL,
+               RT5645_PWR_IN_R_BIT, 0, NULL, 0),
+       /* REC Mixer */
+       SND_SOC_DAPM_MIXER("RECMIXL", RT5645_PWR_MIXER, RT5645_PWR_RM_L_BIT,
+                       0, rt5645_rec_l_mix, ARRAY_SIZE(rt5645_rec_l_mix)),
+       SND_SOC_DAPM_MIXER("RECMIXR", RT5645_PWR_MIXER, RT5645_PWR_RM_R_BIT,
+                       0, rt5645_rec_r_mix, ARRAY_SIZE(rt5645_rec_r_mix)),
+       /* ADCs */
+       SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0),
+
+       SND_SOC_DAPM_SUPPLY("ADC L power", RT5645_PWR_DIG1,
+               RT5645_PWR_ADC_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC R power", RT5645_PWR_DIG1,
+               RT5645_PWR_ADC_R_BIT, 0, NULL, 0),
+
+       /* ADC Mux */
+       SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0,
+               &rt5645_sto1_dmic_mux),
+       SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+               &rt5645_sto_adc2_mux),
+       SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+               &rt5645_sto_adc2_mux),
+       SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+               &rt5645_sto_adc1_mux),
+       SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+               &rt5645_sto_adc1_mux),
+       SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0,
+               &rt5645_mono_dmic_l_mux),
+       SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0,
+               &rt5645_mono_dmic_r_mux),
+       SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+               &rt5645_mono_adc_l2_mux),
+       SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+               &rt5645_mono_adc_l1_mux),
+       SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+               &rt5645_mono_adc_r1_mux),
+       SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+               &rt5645_mono_adc_r2_mux),
+       /* ADC Mixer */
+
+       SND_SOC_DAPM_SUPPLY_S("adc stereo1 filter", 1, RT5645_PWR_DIG2,
+               RT5645_PWR_ADC_S1F_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("adc stereo2 filter", 1, RT5645_PWR_DIG2,
+               RT5645_PWR_ADC_S2F_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER_E("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5645_sto1_adc_l_mix, ARRAY_SIZE(rt5645_sto1_adc_l_mix),
+               NULL, 0),
+       SND_SOC_DAPM_MIXER_E("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5645_sto1_adc_r_mix, ARRAY_SIZE(rt5645_sto1_adc_r_mix),
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("adc mono left filter", 1, RT5645_PWR_DIG2,
+               RT5645_PWR_ADC_MF_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER_E("Mono ADC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5645_mono_adc_l_mix, ARRAY_SIZE(rt5645_mono_adc_l_mix),
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("adc mono right filter", 1, RT5645_PWR_DIG2,
+               RT5645_PWR_ADC_MF_R_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER_E("Mono ADC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5645_mono_adc_r_mix, ARRAY_SIZE(rt5645_mono_adc_r_mix),
+               NULL, 0),
+
+       /* ADC PGA */
+       SND_SOC_DAPM_PGA("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("VAD_ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       /* IF1 2 Mux */
+       SND_SOC_DAPM_MUX("IF1 ADC Mux", SND_SOC_NOPM,
+               0, 0, &rt5645_if1_adc_in_mux),
+       SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM,
+               0, 0, &rt5645_if2_adc_in_mux),
+
+       /* Digital Interface */
+       SND_SOC_DAPM_SUPPLY("I2S1", RT5645_PWR_DIG1,
+               RT5645_PWR_I2S1_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("I2S2", RT5645_PWR_DIG1,
+               RT5645_PWR_I2S2_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       /* Digital Interface Select */
+       SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM,
+               0, 0, &rt5645_vad_adc_mux),
+
+       /* Audio Interface */
+       SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+       /* Output Side */
+       /* DAC mixer before sound effect  */
+       SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
+               rt5645_dac_l_mix, ARRAY_SIZE(rt5645_dac_l_mix)),
+       SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+               rt5645_dac_r_mix, ARRAY_SIZE(rt5645_dac_r_mix)),
+
+       /* DAC2 channel Mux */
+       SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac_l2_mux),
+       SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac_r2_mux),
+       SND_SOC_DAPM_PGA("DAC L2 Volume", RT5645_PWR_DIG1,
+               RT5645_PWR_DAC_L2_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("DAC R2 Volume", RT5645_PWR_DIG1,
+               RT5645_PWR_DAC_R2_BIT, 0, NULL, 0),
+
+       SND_SOC_DAPM_MUX("DAC1 L Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac1l_mux),
+       SND_SOC_DAPM_MUX("DAC1 R Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac1r_mux),
+
+       /* DAC Mixer */
+       SND_SOC_DAPM_SUPPLY_S("dac stereo1 filter", 1, RT5645_PWR_DIG2,
+               RT5645_PWR_DAC_S1F_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("dac mono left filter", 1, RT5645_PWR_DIG2,
+               RT5645_PWR_DAC_MF_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("dac mono right filter", 1, RT5645_PWR_DIG2,
+               RT5645_PWR_DAC_MF_R_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5645_sto_dac_l_mix, ARRAY_SIZE(rt5645_sto_dac_l_mix)),
+       SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5645_sto_dac_r_mix, ARRAY_SIZE(rt5645_sto_dac_r_mix)),
+       SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5645_mono_dac_l_mix, ARRAY_SIZE(rt5645_mono_dac_l_mix)),
+       SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5645_mono_dac_r_mix, ARRAY_SIZE(rt5645_mono_dac_r_mix)),
+       SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5645_dig_l_mix, ARRAY_SIZE(rt5645_dig_l_mix)),
+       SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5645_dig_r_mix, ARRAY_SIZE(rt5645_dig_r_mix)),
+
+       /* DACs */
+       SND_SOC_DAPM_DAC("DAC L1", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_L1_BIT,
+               0),
+       SND_SOC_DAPM_DAC("DAC L2", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_L2_BIT,
+               0),
+       SND_SOC_DAPM_DAC("DAC R1", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_R1_BIT,
+               0),
+       SND_SOC_DAPM_DAC("DAC R2", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_R2_BIT,
+               0),
+       /* OUT Mixer */
+       SND_SOC_DAPM_MIXER("SPK MIXL", RT5645_PWR_MIXER, RT5645_PWR_SM_L_BIT,
+               0, rt5645_spk_l_mix, ARRAY_SIZE(rt5645_spk_l_mix)),
+       SND_SOC_DAPM_MIXER("SPK MIXR", RT5645_PWR_MIXER, RT5645_PWR_SM_R_BIT,
+               0, rt5645_spk_r_mix, ARRAY_SIZE(rt5645_spk_r_mix)),
+       SND_SOC_DAPM_MIXER("OUT MIXL", RT5645_PWR_MIXER, RT5645_PWR_OM_L_BIT,
+               0, rt5645_out_l_mix, ARRAY_SIZE(rt5645_out_l_mix)),
+       SND_SOC_DAPM_MIXER("OUT MIXR", RT5645_PWR_MIXER, RT5645_PWR_OM_R_BIT,
+               0, rt5645_out_r_mix, ARRAY_SIZE(rt5645_out_r_mix)),
+       /* Ouput Volume */
+       SND_SOC_DAPM_SWITCH("SPKVOL L", RT5645_PWR_VOL, RT5645_PWR_SV_L_BIT, 0,
+               &spk_l_vol_control),
+       SND_SOC_DAPM_SWITCH("SPKVOL R", RT5645_PWR_VOL, RT5645_PWR_SV_R_BIT, 0,
+               &spk_r_vol_control),
+       SND_SOC_DAPM_MIXER("HPOVOL MIXL", RT5645_PWR_VOL, RT5645_PWR_HV_L_BIT,
+               0, rt5645_hpvoll_mix, ARRAY_SIZE(rt5645_hpvoll_mix)),
+       SND_SOC_DAPM_MIXER("HPOVOL MIXR", RT5645_PWR_VOL, RT5645_PWR_HV_R_BIT,
+               0, rt5645_hpvolr_mix, ARRAY_SIZE(rt5645_hpvolr_mix)),
+       SND_SOC_DAPM_SUPPLY("HPOVOL MIXL Power", RT5645_PWR_MIXER,
+               RT5645_PWR_HM_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("HPOVOL MIXR Power", RT5645_PWR_MIXER,
+               RT5645_PWR_HM_R_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_SWITCH("HPOVOL L", SND_SOC_NOPM, 0, 0, &hp_l_vol_control),
+       SND_SOC_DAPM_SWITCH("HPOVOL R", SND_SOC_NOPM, 0, 0, &hp_r_vol_control),
+
+       /* HPO/LOUT/Mono Mixer */
+       SND_SOC_DAPM_MIXER("SPOL MIX", SND_SOC_NOPM, 0, 0, rt5645_spo_l_mix,
+               ARRAY_SIZE(rt5645_spo_l_mix)),
+       SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0, 0, rt5645_spo_r_mix,
+               ARRAY_SIZE(rt5645_spo_r_mix)),
+       SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0, rt5645_hpo_mix,
+               ARRAY_SIZE(rt5645_hpo_mix)),
+       SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, rt5645_lout_mix,
+               ARRAY_SIZE(rt5645_lout_mix)),
+
+       SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0, rt5645_hp_event,
+               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0, rt5645_lout_event,
+               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_S("SPK amp", 2, SND_SOC_NOPM, 0, 0, rt5645_spk_event,
+               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+       /* PDM */
+       SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5645_PWR_DIG2, RT5645_PWR_PDM1_BIT,
+               0, NULL, 0),
+       SND_SOC_DAPM_MUX("PDM1 L Mux", SND_SOC_NOPM, 0, 0, &rt5645_pdm1_l_mux),
+       SND_SOC_DAPM_MUX("PDM1 R Mux", SND_SOC_NOPM, 0, 0, &rt5645_pdm1_r_mux),
+
+       SND_SOC_DAPM_SWITCH("PDM1 L", SND_SOC_NOPM, 0, 0, &pdm1_l_vol_control),
+       SND_SOC_DAPM_SWITCH("PDM1 R", SND_SOC_NOPM, 0, 0, &pdm1_r_vol_control),
+
+       /* Output Lines */
+       SND_SOC_DAPM_OUTPUT("HPOL"),
+       SND_SOC_DAPM_OUTPUT("HPOR"),
+       SND_SOC_DAPM_OUTPUT("LOUTL"),
+       SND_SOC_DAPM_OUTPUT("LOUTR"),
+       SND_SOC_DAPM_OUTPUT("PDM1L"),
+       SND_SOC_DAPM_OUTPUT("PDM1R"),
+       SND_SOC_DAPM_OUTPUT("SPOL"),
+       SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
+       { "IN1P", NULL, "LDO2" },
+       { "IN2P", NULL, "LDO2" },
+
+       { "DMIC1", NULL, "DMIC L1" },
+       { "DMIC1", NULL, "DMIC R1" },
+       { "DMIC2", NULL, "DMIC L2" },
+       { "DMIC2", NULL, "DMIC R2" },
+
+       { "BST1", NULL, "IN1P" },
+       { "BST1", NULL, "IN1N" },
+       { "BST1", NULL, "JD Power" },
+       { "BST1", NULL, "Mic Det Power" },
+       { "BST2", NULL, "IN2P" },
+       { "BST2", NULL, "IN2N" },
+
+       { "INL VOL", NULL, "IN2P" },
+       { "INR VOL", NULL, "IN2N" },
+
+       { "RECMIXL", "HPOL Switch", "HPOL" },
+       { "RECMIXL", "INL Switch", "INL VOL" },
+       { "RECMIXL", "BST2 Switch", "BST2" },
+       { "RECMIXL", "BST1 Switch", "BST1" },
+       { "RECMIXL", "OUT MIXL Switch", "OUT MIXL" },
+
+       { "RECMIXR", "HPOR Switch", "HPOR" },
+       { "RECMIXR", "INR Switch", "INR VOL" },
+       { "RECMIXR", "BST2 Switch", "BST2" },
+       { "RECMIXR", "BST1 Switch", "BST1" },
+       { "RECMIXR", "OUT MIXR Switch", "OUT MIXR" },
+
+       { "ADC L", NULL, "RECMIXL" },
+       { "ADC L", NULL, "ADC L power" },
+       { "ADC R", NULL, "RECMIXR" },
+       { "ADC R", NULL, "ADC R power" },
+
+       {"DMIC L1", NULL, "DMIC CLK"},
+       {"DMIC L1", NULL, "DMIC1 Power"},
+       {"DMIC R1", NULL, "DMIC CLK"},
+       {"DMIC R1", NULL, "DMIC1 Power"},
+       {"DMIC L2", NULL, "DMIC CLK"},
+       {"DMIC L2", NULL, "DMIC2 Power"},
+       {"DMIC R2", NULL, "DMIC CLK"},
+       {"DMIC R2", NULL, "DMIC2 Power"},
+
+       { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
+       { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
+
+       { "Mono DMIC L Mux", "DMIC1", "DMIC L1" },
+       { "Mono DMIC L Mux", "DMIC2", "DMIC L2" },
+
+       { "Mono DMIC R Mux", "DMIC1", "DMIC R1" },
+       { "Mono DMIC R Mux", "DMIC2", "DMIC R2" },
+
+       { "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC Mux" },
+       { "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" },
+       { "Stereo1 ADC L1 Mux", "ADC", "ADC L" },
+       { "Stereo1 ADC L1 Mux", "DAC MIX", "DAC MIXL" },
+
+       { "Stereo1 ADC R1 Mux", "ADC", "ADC R" },
+       { "Stereo1 ADC R1 Mux", "DAC MIX", "DAC MIXR" },
+       { "Stereo1 ADC R2 Mux", "DMIC", "Stereo1 DMIC Mux" },
+       { "Stereo1 ADC R2 Mux", "DAC MIX", "DAC MIXR" },
+
+       { "Mono ADC L2 Mux", "DMIC", "Mono DMIC L Mux" },
+       { "Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL" },
+       { "Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL" },
+       { "Mono ADC L1 Mux", "ADC", "ADC L" },
+
+       { "Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR" },
+       { "Mono ADC R1 Mux", "ADC", "ADC R" },
+       { "Mono ADC R2 Mux", "DMIC", "Mono DMIC R Mux" },
+       { "Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR" },
+
+       { "Sto1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux" },
+       { "Sto1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux" },
+       { "Sto1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux" },
+       { "Sto1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux" },
+
+       { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
+       { "Stereo1 ADC MIXL", NULL, "adc stereo1 filter" },
+       { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
+       { "Stereo1 ADC MIXR", NULL, "adc stereo1 filter" },
+       { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux" },
+       { "Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux" },
+       { "Mono ADC MIXL", NULL, "adc mono left filter" },
+       { "adc mono left filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux" },
+       { "Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux" },
+       { "Mono ADC MIXR", NULL, "adc mono right filter" },
+       { "adc mono right filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "VAD ADC Mux", "Sto1 ADC L", "Stereo1 ADC MIXL" },
+       { "VAD ADC Mux", "Mono ADC L", "Mono ADC MIXL" },
+       { "VAD ADC Mux", "Mono ADC R", "Mono ADC MIXR" },
+
+       { "IF_ADC1", NULL, "Stereo1 ADC MIXL" },
+       { "IF_ADC1", NULL, "Stereo1 ADC MIXR" },
+       { "IF_ADC2", NULL, "Mono ADC MIXL" },
+       { "IF_ADC2", NULL, "Mono ADC MIXR" },
+       { "VAD_ADC", NULL, "VAD ADC Mux" },
+
+       { "IF1 ADC Mux", "IF_ADC1", "IF_ADC1" },
+       { "IF1 ADC Mux", "IF_ADC2", "IF_ADC2" },
+       { "IF1 ADC Mux", "VAD_ADC", "VAD_ADC" },
+
+       { "IF2 ADC Mux", "IF_ADC1", "IF_ADC1" },
+       { "IF2 ADC Mux", "IF_ADC2", "IF_ADC2" },
+       { "IF2 ADC Mux", "VAD_ADC", "VAD_ADC" },
+
+       { "IF1 ADC", NULL, "I2S1" },
+       { "IF1 ADC", NULL, "IF1 ADC Mux" },
+       { "IF2 ADC", NULL, "I2S2" },
+       { "IF2 ADC", NULL, "IF2 ADC Mux" },
+
+       { "AIF1TX", NULL, "IF1 ADC" },
+       { "AIF1TX", NULL, "IF2 ADC" },
+       { "AIF2TX", NULL, "IF2 ADC" },
+
+       { "IF1 DAC1", NULL, "AIF1RX" },
+       { "IF1 DAC2", NULL, "AIF1RX" },
+       { "IF2 DAC", NULL, "AIF2RX" },
+
+       { "IF1 DAC1", NULL, "I2S1" },
+       { "IF1 DAC2", NULL, "I2S1" },
+       { "IF2 DAC", NULL, "I2S2" },
+
+       { "IF1 DAC2 L", NULL, "IF1 DAC2" },
+       { "IF1 DAC2 R", NULL, "IF1 DAC2" },
+       { "IF1 DAC1 L", NULL, "IF1 DAC1" },
+       { "IF1 DAC1 R", NULL, "IF1 DAC1" },
+       { "IF2 DAC L", NULL, "IF2 DAC" },
+       { "IF2 DAC R", NULL, "IF2 DAC" },
+
+       { "DAC1 L Mux", "IF1 DAC", "IF1 DAC1 L" },
+       { "DAC1 L Mux", "IF2 DAC", "IF2 DAC L" },
+
+       { "DAC1 R Mux", "IF1 DAC", "IF1 DAC1 R" },
+       { "DAC1 R Mux", "IF2 DAC", "IF2 DAC R" },
+
+       { "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" },
+       { "DAC1 MIXL", "DAC1 Switch", "DAC1 L Mux" },
+       { "DAC1 MIXL", NULL, "dac stereo1 filter" },
+       { "DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR" },
+       { "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" },
+       { "DAC1 MIXR", NULL, "dac stereo1 filter" },
+
+       { "DAC L2 Mux", "IF1 DAC", "IF1 DAC2 L" },
+       { "DAC L2 Mux", "IF2 DAC", "IF2 DAC L" },
+       { "DAC L2 Mux", "Mono ADC", "Mono ADC MIXL" },
+       { "DAC L2 Mux", "VAD_ADC", "VAD_ADC" },
+       { "DAC L2 Volume", NULL, "DAC L2 Mux" },
+       { "DAC L2 Volume", NULL, "dac mono left filter" },
+
+       { "DAC R2 Mux", "IF1 DAC", "IF1 DAC2 R" },
+       { "DAC R2 Mux", "IF2 DAC", "IF2 DAC R" },
+       { "DAC R2 Mux", "Mono ADC", "Mono ADC MIXR" },
+       { "DAC R2 Mux", "Haptic", "Haptic Generator" },
+       { "DAC R2 Volume", NULL, "DAC R2 Mux" },
+       { "DAC R2 Volume", NULL, "dac mono right filter" },
+
+       { "Stereo DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" },
+       { "Stereo DAC MIXL", "DAC R1 Switch", "DAC1 MIXR" },
+       { "Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" },
+       { "Stereo DAC MIXL", NULL, "dac stereo1 filter" },
+       { "Stereo DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" },
+       { "Stereo DAC MIXR", "DAC L1 Switch", "DAC1 MIXL" },
+       { "Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" },
+       { "Stereo DAC MIXR", NULL, "dac stereo1 filter" },
+
+       { "Mono DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" },
+       { "Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" },
+       { "Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" },
+       { "Mono DAC MIXL", NULL, "dac mono left filter" },
+       { "Mono DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" },
+       { "Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" },
+       { "Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" },
+       { "Mono DAC MIXR", NULL, "dac mono right filter" },
+
+       { "DAC MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" },
+       { "DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" },
+       { "DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" },
+       { "DAC MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" },
+       { "DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" },
+       { "DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" },
+
+       { "DAC L1", NULL, "Stereo DAC MIXL" },
+       { "DAC L1", NULL, "PLL1", is_sys_clk_from_pll },
+       { "DAC R1", NULL, "Stereo DAC MIXR" },
+       { "DAC R1", NULL, "PLL1", is_sys_clk_from_pll },
+       { "DAC L2", NULL, "Mono DAC MIXL" },
+       { "DAC L2", NULL, "PLL1", is_sys_clk_from_pll },
+       { "DAC R2", NULL, "Mono DAC MIXR" },
+       { "DAC R2", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "SPK MIXL", "BST1 Switch", "BST1" },
+       { "SPK MIXL", "INL Switch", "INL VOL" },
+       { "SPK MIXL", "DAC L1 Switch", "DAC L1" },
+       { "SPK MIXL", "DAC L2 Switch", "DAC L2" },
+       { "SPK MIXR", "BST2 Switch", "BST2" },
+       { "SPK MIXR", "INR Switch", "INR VOL" },
+       { "SPK MIXR", "DAC R1 Switch", "DAC R1" },
+       { "SPK MIXR", "DAC R2 Switch", "DAC R2" },
+
+       { "OUT MIXL", "BST1 Switch", "BST1" },
+       { "OUT MIXL", "INL Switch", "INL VOL" },
+       { "OUT MIXL", "DAC L2 Switch", "DAC L2" },
+       { "OUT MIXL", "DAC L1 Switch", "DAC L1" },
+
+       { "OUT MIXR", "BST2 Switch", "BST2" },
+       { "OUT MIXR", "INR Switch", "INR VOL" },
+       { "OUT MIXR", "DAC R2 Switch", "DAC R2" },
+       { "OUT MIXR", "DAC R1 Switch", "DAC R1" },
+
+       { "HPOVOL MIXL", "DAC1 Switch", "DAC L1" },
+       { "HPOVOL MIXL", "DAC2 Switch", "DAC L2" },
+       { "HPOVOL MIXL", "INL Switch", "INL VOL" },
+       { "HPOVOL MIXL", "BST1 Switch", "BST1" },
+       { "HPOVOL MIXL", NULL, "HPOVOL MIXL Power" },
+       { "HPOVOL MIXR", "DAC1 Switch", "DAC R1" },
+       { "HPOVOL MIXR", "DAC2 Switch", "DAC R2" },
+       { "HPOVOL MIXR", "INR Switch", "INR VOL" },
+       { "HPOVOL MIXR", "BST2 Switch", "BST2" },
+       { "HPOVOL MIXR", NULL, "HPOVOL MIXR Power" },
+
+       { "DAC 2", NULL, "DAC L2" },
+       { "DAC 2", NULL, "DAC R2" },
+       { "DAC 1", NULL, "DAC L1" },
+       { "DAC 1", NULL, "DAC R1" },
+       { "HPOVOL L", "Switch", "HPOVOL MIXL" },
+       { "HPOVOL R", "Switch", "HPOVOL MIXR" },
+       { "HPOVOL", NULL, "HPOVOL L" },
+       { "HPOVOL", NULL, "HPOVOL R" },
+       { "HPO MIX", "DAC1 Switch", "DAC 1" },
+       { "HPO MIX", "HPVOL Switch", "HPOVOL" },
+
+       { "SPKVOL L", "Switch", "SPK MIXL" },
+       { "SPKVOL R", "Switch", "SPK MIXR" },
+
+       { "SPOL MIX", "DAC R1 Switch", "DAC R1" },
+       { "SPOL MIX", "DAC L1 Switch", "DAC L1" },
+       { "SPOL MIX", "SPKVOL R Switch", "SPKVOL R" },
+       { "SPOL MIX", "SPKVOL L Switch", "SPKVOL L" },
+       { "SPOR MIX", "DAC R1 Switch", "DAC R1" },
+       { "SPOR MIX", "SPKVOL R Switch", "SPKVOL R" },
+
+       { "LOUT MIX", "DAC L1 Switch", "DAC L1" },
+       { "LOUT MIX", "DAC R1 Switch", "DAC R1" },
+       { "LOUT MIX", "OUTMIX L Switch", "OUT MIXL" },
+       { "LOUT MIX", "OUTMIX R Switch", "OUT MIXR" },
+
+       { "PDM1 L Mux", "Stereo DAC", "Stereo DAC MIXL" },
+       { "PDM1 L Mux", "Mono DAC", "Mono DAC MIXL" },
+       { "PDM1 L Mux", NULL, "PDM1 Power" },
+       { "PDM1 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
+       { "PDM1 R Mux", "Mono DAC", "Mono DAC MIXR" },
+       { "PDM1 R Mux", NULL, "PDM1 Power" },
+
+       { "HP amp", NULL, "HPO MIX" },
+       { "HP amp", NULL, "JD Power" },
+       { "HP amp", NULL, "Mic Det Power" },
+       { "HP amp", NULL, "LDO2" },
+       { "HPOL", NULL, "HP amp" },
+       { "HPOR", NULL, "HP amp" },
+
+       { "LOUT amp", NULL, "LOUT MIX" },
+       { "LOUTL", NULL, "LOUT amp" },
+       { "LOUTR", NULL, "LOUT amp" },
+
+       { "PDM1 L", "Switch", "PDM1 L Mux" },
+       { "PDM1 R", "Switch", "PDM1 R Mux" },
+
+       { "PDM1L", NULL, "PDM1 L" },
+       { "PDM1R", NULL, "PDM1 R" },
+
+       { "SPK amp", NULL, "SPOL MIX" },
+       { "SPK amp", NULL, "SPOR MIX" },
+       { "SPOL", NULL, "SPK amp" },
+       { "SPOR", NULL, "SPK amp" },
+};
+
+static int rt5645_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 rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val_len = 0, val_clk, mask_clk;
+       int pre_div, bclk_ms, frame_size;
+
+       rt5645->lrck[dai->id] = params_rate(params);
+       pre_div = rl6231_get_clk_info(rt5645->sysclk, rt5645->lrck[dai->id]);
+       if (pre_div < 0) {
+               dev_err(codec->dev, "Unsupported clock setting\n");
+               return -EINVAL;
+       }
+       frame_size = snd_soc_params_to_frame_size(params);
+       if (frame_size < 0) {
+               dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+               return -EINVAL;
+       }
+       bclk_ms = frame_size > 32;
+       rt5645->bclk[dai->id] = rt5645->lrck[dai->id] * (32 << bclk_ms);
+
+       dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+               rt5645->bclk[dai->id], rt5645->lrck[dai->id]);
+       dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+                               bclk_ms, pre_div, dai->id);
+
+       switch (params_width(params)) {
+       case 16:
+               break;
+       case 20:
+               val_len |= RT5645_I2S_DL_20;
+               break;
+       case 24:
+               val_len |= RT5645_I2S_DL_24;
+               break;
+       case 8:
+               val_len |= RT5645_I2S_DL_8;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (dai->id) {
+       case RT5645_AIF1:
+               mask_clk = RT5645_I2S_BCLK_MS1_MASK | RT5645_I2S_PD1_MASK;
+               val_clk = bclk_ms << RT5645_I2S_BCLK_MS1_SFT |
+                       pre_div << RT5645_I2S_PD1_SFT;
+               snd_soc_update_bits(codec, RT5645_I2S1_SDP,
+                       RT5645_I2S_DL_MASK, val_len);
+               snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk);
+               break;
+       case  RT5645_AIF2:
+               mask_clk = RT5645_I2S_BCLK_MS2_MASK | RT5645_I2S_PD2_MASK;
+               val_clk = bclk_ms << RT5645_I2S_BCLK_MS2_SFT |
+                       pre_div << RT5645_I2S_PD2_SFT;
+               snd_soc_update_bits(codec, RT5645_I2S2_SDP,
+                       RT5645_I2S_DL_MASK, val_len);
+               snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk);
+               break;
+       default:
+               dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+       unsigned int reg_val = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               rt5645->master[dai->id] = 1;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               reg_val |= RT5645_I2S_MS_S;
+               rt5645->master[dai->id] = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               reg_val |= RT5645_I2S_BP_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               reg_val |= RT5645_I2S_DF_LEFT;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               reg_val |= RT5645_I2S_DF_PCM_A;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               reg_val |= RT5645_I2S_DF_PCM_B;
+               break;
+       default:
+               return -EINVAL;
+       }
+       switch (dai->id) {
+       case RT5645_AIF1:
+               snd_soc_update_bits(codec, RT5645_I2S1_SDP,
+                       RT5645_I2S_MS_MASK | RT5645_I2S_BP_MASK |
+                       RT5645_I2S_DF_MASK, reg_val);
+               break;
+       case RT5645_AIF2:
+               snd_soc_update_bits(codec, RT5645_I2S2_SDP,
+                       RT5645_I2S_MS_MASK | RT5645_I2S_BP_MASK |
+                       RT5645_I2S_DF_MASK, reg_val);
+               break;
+       default:
+               dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+       unsigned int reg_val = 0;
+
+       if (freq == rt5645->sysclk && clk_id == rt5645->sysclk_src)
+               return 0;
+
+       switch (clk_id) {
+       case RT5645_SCLK_S_MCLK:
+               reg_val |= RT5645_SCLK_SRC_MCLK;
+               break;
+       case RT5645_SCLK_S_PLL1:
+               reg_val |= RT5645_SCLK_SRC_PLL1;
+               break;
+       case RT5645_SCLK_S_RCCLK:
+               reg_val |= RT5645_SCLK_SRC_RCCLK;
+               break;
+       default:
+               dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+               return -EINVAL;
+       }
+       snd_soc_update_bits(codec, RT5645_GLB_CLK,
+               RT5645_SCLK_SRC_MASK, reg_val);
+       rt5645->sysclk = freq;
+       rt5645->sysclk_src = clk_id;
+
+       dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+       return 0;
+}
+
+static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+                       unsigned int freq_in, unsigned int freq_out)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+       struct rl6231_pll_code pll_code;
+       int ret;
+
+       if (source == rt5645->pll_src && freq_in == rt5645->pll_in &&
+           freq_out == rt5645->pll_out)
+               return 0;
+
+       if (!freq_in || !freq_out) {
+               dev_dbg(codec->dev, "PLL disabled\n");
+
+               rt5645->pll_in = 0;
+               rt5645->pll_out = 0;
+               snd_soc_update_bits(codec, RT5645_GLB_CLK,
+                       RT5645_SCLK_SRC_MASK, RT5645_SCLK_SRC_MCLK);
+               return 0;
+       }
+
+       switch (source) {
+       case RT5645_PLL1_S_MCLK:
+               snd_soc_update_bits(codec, RT5645_GLB_CLK,
+                       RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_MCLK);
+               break;
+       case RT5645_PLL1_S_BCLK1:
+       case RT5645_PLL1_S_BCLK2:
+               switch (dai->id) {
+               case RT5645_AIF1:
+                       snd_soc_update_bits(codec, RT5645_GLB_CLK,
+                               RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK1);
+                       break;
+               case  RT5645_AIF2:
+                       snd_soc_update_bits(codec, RT5645_GLB_CLK,
+                               RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK2);
+                       break;
+               default:
+                       dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+                       return -EINVAL;
+               }
+               break;
+       default:
+               dev_err(codec->dev, "Unknown PLL source %d\n", source);
+               return -EINVAL;
+       }
+
+       ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+       if (ret < 0) {
+               dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+               return ret;
+       }
+
+       dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+               pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+               pll_code.n_code, pll_code.k_code);
+
+       snd_soc_write(codec, RT5645_PLL_CTRL1,
+               pll_code.n_code << RT5645_PLL_N_SFT | pll_code.k_code);
+       snd_soc_write(codec, RT5645_PLL_CTRL2,
+               (pll_code.m_bp ? 0 : pll_code.m_code) << RT5645_PLL_M_SFT |
+               pll_code.m_bp << RT5645_PLL_M_BP_SFT);
+
+       rt5645->pll_in = freq_in;
+       rt5645->pll_out = freq_out;
+       rt5645->pll_src = source;
+
+       return 0;
+}
+
+static int rt5645_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;
+       unsigned int val = 0;
+
+       if (rx_mask || tx_mask)
+               val |= (1 << 14);
+
+       switch (slots) {
+       case 4:
+               val |= (1 << 12);
+               break;
+       case 6:
+               val |= (2 << 12);
+               break;
+       case 8:
+               val |= (3 << 12);
+               break;
+       case 2:
+       default:
+               break;
+       }
+
+       switch (slot_width) {
+       case 20:
+               val |= (1 << 10);
+               break;
+       case 24:
+               val |= (2 << 10);
+               break;
+       case 32:
+               val |= (3 << 10);
+               break;
+       case 16:
+       default:
+               break;
+       }
+
+       snd_soc_update_bits(codec, RT5645_TDM_CTRL_1, 0x7c00, val);
+
+       return 0;
+}
+
+static int rt5645_set_bias_level(struct snd_soc_codec *codec,
+                       enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_STANDBY:
+               if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+                       snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                               RT5645_PWR_VREF1 | RT5645_PWR_MB |
+                               RT5645_PWR_BG | RT5645_PWR_VREF2,
+                               RT5645_PWR_VREF1 | RT5645_PWR_MB |
+                               RT5645_PWR_BG | RT5645_PWR_VREF2);
+                       mdelay(10);
+                       snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                               RT5645_PWR_FV1 | RT5645_PWR_FV2,
+                               RT5645_PWR_FV1 | RT5645_PWR_FV2);
+                       snd_soc_update_bits(codec, RT5645_GEN_CTRL1,
+                               RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL);
+               }
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100);
+               snd_soc_write(codec, RT5645_GEN_CTRL1, 0x0128);
+               snd_soc_write(codec, RT5645_PWR_DIG1, 0x0000);
+               snd_soc_write(codec, RT5645_PWR_DIG2, 0x0000);
+               snd_soc_write(codec, RT5645_PWR_VOL, 0x0000);
+               snd_soc_write(codec, RT5645_PWR_MIXER, 0x0000);
+               snd_soc_write(codec, RT5645_PWR_ANLG1, 0x0000);
+               snd_soc_write(codec, RT5645_PWR_ANLG2, 0x0000);
+               break;
+
+       default:
+               break;
+       }
+       codec->dapm.bias_level = level;
+
+       return 0;
+}
+
+static int rt5645_probe(struct snd_soc_codec *codec)
+{
+       struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
+       rt5645->codec = codec;
+
+       rt5645_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200);
+
+       return 0;
+}
+
+static int rt5645_remove(struct snd_soc_codec *codec)
+{
+       rt5645_reset(codec);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt5645_suspend(struct snd_soc_codec *codec)
+{
+       struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
+       regcache_cache_only(rt5645->regmap, true);
+       regcache_mark_dirty(rt5645->regmap);
+
+       return 0;
+}
+
+static int rt5645_resume(struct snd_soc_codec *codec)
+{
+       struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+
+       regcache_cache_only(rt5645->regmap, false);
+       regcache_sync(rt5645->regmap);
+
+       return 0;
+}
+#else
+#define rt5645_suspend NULL
+#define rt5645_resume NULL
+#endif
+
+#define RT5645_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5645_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static struct snd_soc_dai_ops rt5645_aif_dai_ops = {
+       .hw_params = rt5645_hw_params,
+       .set_fmt = rt5645_set_dai_fmt,
+       .set_sysclk = rt5645_set_dai_sysclk,
+       .set_tdm_slot = rt5645_set_tdm_slot,
+       .set_pll = rt5645_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver rt5645_dai[] = {
+       {
+               .name = "rt5645-aif1",
+               .id = RT5645_AIF1,
+               .playback = {
+                       .stream_name = "AIF1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5645_STEREO_RATES,
+                       .formats = RT5645_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AIF1 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5645_STEREO_RATES,
+                       .formats = RT5645_FORMATS,
+               },
+               .ops = &rt5645_aif_dai_ops,
+       },
+       {
+               .name = "rt5645-aif2",
+               .id = RT5645_AIF2,
+               .playback = {
+                       .stream_name = "AIF2 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5645_STEREO_RATES,
+                       .formats = RT5645_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AIF2 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5645_STEREO_RATES,
+                       .formats = RT5645_FORMATS,
+               },
+               .ops = &rt5645_aif_dai_ops,
+       },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_rt5645 = {
+       .probe = rt5645_probe,
+       .remove = rt5645_remove,
+       .suspend = rt5645_suspend,
+       .resume = rt5645_resume,
+       .set_bias_level = rt5645_set_bias_level,
+       .idle_bias_off = true,
+       .controls = rt5645_snd_controls,
+       .num_controls = ARRAY_SIZE(rt5645_snd_controls),
+       .dapm_widgets = rt5645_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(rt5645_dapm_widgets),
+       .dapm_routes = rt5645_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(rt5645_dapm_routes),
+};
+
+static const struct regmap_config rt5645_regmap = {
+       .reg_bits = 8,
+       .val_bits = 16,
+
+       .max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) *
+                                              RT5645_PR_SPACING),
+       .volatile_reg = rt5645_volatile_register,
+       .readable_reg = rt5645_readable_register,
+
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = rt5645_reg,
+       .num_reg_defaults = ARRAY_SIZE(rt5645_reg),
+       .ranges = rt5645_ranges,
+       .num_ranges = ARRAY_SIZE(rt5645_ranges),
+};
+
+static const struct i2c_device_id rt5645_i2c_id[] = {
+       { "rt5645", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
+
+static int rt5645_i2c_probe(struct i2c_client *i2c,
+                   const struct i2c_device_id *id)
+{
+       struct rt5645_platform_data *pdata = dev_get_platdata(&i2c->dev);
+       struct rt5645_priv *rt5645;
+       int ret;
+       unsigned int val;
+
+       rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv),
+                               GFP_KERNEL);
+       if (rt5645 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, rt5645);
+
+       if (pdata)
+               rt5645->pdata = *pdata;
+
+       rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap);
+       if (IS_ERR(rt5645->regmap)) {
+               ret = PTR_ERR(rt5645->regmap);
+               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       regmap_read(rt5645->regmap, RT5645_VENDOR_ID2, &val);
+       if (val != RT5645_DEVICE_ID) {
+               dev_err(&i2c->dev,
+                       "Device with ID register %x is not rt5645\n", val);
+               return -ENODEV;
+       }
+
+       regmap_write(rt5645->regmap, RT5645_RESET, 0);
+
+       ret = regmap_register_patch(rt5645->regmap, init_list,
+                                   ARRAY_SIZE(init_list));
+       if (ret != 0)
+               dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+       if (rt5645->pdata.in2_diff)
+               regmap_update_bits(rt5645->regmap, RT5645_IN2_CTRL,
+                                       RT5645_IN_DF2, RT5645_IN_DF2);
+
+       if (rt5645->pdata.dmic_en) {
+               regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+                       RT5645_GP2_PIN_MASK, RT5645_GP2_PIN_DMIC1_SCL);
+
+               switch (rt5645->pdata.dmic1_data_pin) {
+               case RT5645_DMIC_DATA_IN2N:
+                       regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+                               RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_IN2N);
+                       break;
+
+               case RT5645_DMIC_DATA_GPIO5:
+                       regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+                               RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO5);
+                       regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+                               RT5645_GP5_PIN_MASK, RT5645_GP5_PIN_DMIC1_SDA);
+                       break;
+
+               case RT5645_DMIC_DATA_GPIO11:
+                       regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+                               RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO11);
+                       regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+                               RT5645_GP11_PIN_MASK,
+                               RT5645_GP11_PIN_DMIC1_SDA);
+                       break;
+
+               default:
+                       break;
+               }
+
+               switch (rt5645->pdata.dmic2_data_pin) {
+               case RT5645_DMIC_DATA_IN2P:
+                       regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+                               RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_IN2P);
+                       break;
+
+               case RT5645_DMIC_DATA_GPIO6:
+                       regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+                               RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO6);
+                       regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+                               RT5645_GP6_PIN_MASK, RT5645_GP6_PIN_DMIC2_SDA);
+                       break;
+
+               case RT5645_DMIC_DATA_GPIO10:
+                       regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+                               RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO10);
+                       regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+                               RT5645_GP10_PIN_MASK,
+                               RT5645_GP10_PIN_DMIC2_SDA);
+                       break;
+
+               case RT5645_DMIC_DATA_GPIO12:
+                       regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
+                               RT5645_DMIC_1_DP_MASK, RT5645_DMIC_2_DP_GPIO12);
+                       regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+                               RT5645_GP12_PIN_MASK,
+                               RT5645_GP12_PIN_DMIC2_SDA);
+                       break;
+
+               default:
+                       break;
+               }
+
+       }
+
+       ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
+                       rt5645_dai, ARRAY_SIZE(rt5645_dai));
+       if (ret < 0)
+               goto err;
+
+       return 0;
+err:
+       return ret;
+}
+
+static int rt5645_i2c_remove(struct i2c_client *i2c)
+{
+       snd_soc_unregister_codec(&i2c->dev);
+
+       return 0;
+}
+
+static struct i2c_driver rt5645_i2c_driver = {
+       .driver = {
+               .name = "rt5645",
+               .owner = THIS_MODULE,
+       },
+       .probe = rt5645_i2c_probe,
+       .remove   = rt5645_i2c_remove,
+       .id_table = rt5645_i2c_id,
+};
+module_i2c_driver(rt5645_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5645 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
new file mode 100644 (file)
index 0000000..355b7e9
--- /dev/null
@@ -0,0 +1,2181 @@
+/*
+ * rt5645.h  --  RT5645 ALSA SoC audio driver
+ *
+ * Copyright 2013 Realtek Microelectronics
+ * Author: Bard Liao <bardliao@realtek.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 __RT5645_H__
+#define __RT5645_H__
+
+#include <sound/rt5645.h>
+
+/* Info */
+#define RT5645_RESET                           0x00
+#define RT5645_VENDOR_ID                       0xfd
+#define RT5645_VENDOR_ID1                      0xfe
+#define RT5645_VENDOR_ID2                      0xff
+/*  I/O - Output */
+#define RT5645_SPK_VOL                         0x01
+#define RT5645_HP_VOL                          0x02
+#define RT5645_LOUT1                           0x03
+#define RT5645_LOUT_CTRL                       0x05
+/* I/O - Input */
+#define RT5645_IN1_CTRL1                       0x0a
+#define RT5645_IN1_CTRL2                       0x0b
+#define RT5645_IN1_CTRL3                       0x0c
+#define RT5645_IN2_CTRL                                0x0d
+#define RT5645_INL1_INR1_VOL                   0x0f
+#define RT5645_SPK_FUNC_LIM                    0x14
+#define RT5645_ADJ_HPF_CTRL                    0x16
+/* I/O - ADC/DAC/DMIC */
+#define RT5645_DAC1_DIG_VOL                    0x19
+#define RT5645_DAC2_DIG_VOL                    0x1a
+#define RT5645_DAC_CTRL                                0x1b
+#define RT5645_STO1_ADC_DIG_VOL                        0x1c
+#define RT5645_MONO_ADC_DIG_VOL                        0x1d
+#define RT5645_ADC_BST_VOL1                    0x1e
+/* Mixer - D-D */
+#define RT5645_ADC_BST_VOL2                    0x20
+#define RT5645_STO1_ADC_MIXER                  0x27
+#define RT5645_MONO_ADC_MIXER                  0x28
+#define RT5645_AD_DA_MIXER                     0x29
+#define RT5645_STO_DAC_MIXER                   0x2a
+#define RT5645_MONO_DAC_MIXER                  0x2b
+#define RT5645_DIG_MIXER                       0x2c
+#define RT5645_DIG_INF1_DATA                   0x2f
+/* Mixer - PDM */
+#define RT5645_PDM_OUT_CTRL                    0x31
+/* Mixer - ADC */
+#define RT5645_REC_L1_MIXER                    0x3b
+#define RT5645_REC_L2_MIXER                    0x3c
+#define RT5645_REC_R1_MIXER                    0x3d
+#define RT5645_REC_R2_MIXER                    0x3e
+/* Mixer - DAC */
+#define RT5645_HPMIXL_CTRL                     0x3f
+#define RT5645_HPOMIXL_CTRL                    0x40
+#define RT5645_HPMIXR_CTRL                     0x41
+#define RT5645_HPOMIXR_CTRL                    0x42
+#define RT5645_HPO_MIXER                       0x45
+#define RT5645_SPK_L_MIXER                     0x46
+#define RT5645_SPK_R_MIXER                     0x47
+#define RT5645_SPO_MIXER                       0x48
+#define RT5645_SPO_CLSD_RATIO                  0x4a
+#define RT5645_OUT_L_GAIN1                     0x4d
+#define RT5645_OUT_L_GAIN2                     0x4e
+#define RT5645_OUT_L1_MIXER                    0x4f
+#define RT5645_OUT_R_GAIN1                     0x50
+#define RT5645_OUT_R_GAIN2                     0x51
+#define RT5645_OUT_R1_MIXER                    0x52
+#define RT5645_LOUT_MIXER                      0x53
+/* Haptic */
+#define RT5645_HAPTIC_CTRL1                    0x56
+#define RT5645_HAPTIC_CTRL2                    0x57
+#define RT5645_HAPTIC_CTRL3                    0x58
+#define RT5645_HAPTIC_CTRL4                    0x59
+#define RT5645_HAPTIC_CTRL5                    0x5a
+#define RT5645_HAPTIC_CTRL6                    0x5b
+#define RT5645_HAPTIC_CTRL7                    0x5c
+#define RT5645_HAPTIC_CTRL8                    0x5d
+#define RT5645_HAPTIC_CTRL9                    0x5e
+#define RT5645_HAPTIC_CTRL10                   0x5f
+/* Power */
+#define RT5645_PWR_DIG1                                0x61
+#define RT5645_PWR_DIG2                                0x62
+#define RT5645_PWR_ANLG1                       0x63
+#define RT5645_PWR_ANLG2                       0x64
+#define RT5645_PWR_MIXER                       0x65
+#define RT5645_PWR_VOL                         0x66
+/* Private Register Control */
+#define RT5645_PRIV_INDEX                      0x6a
+#define RT5645_PRIV_DATA                       0x6c
+/* Format - ADC/DAC */
+#define RT5645_I2S1_SDP                                0x70
+#define RT5645_I2S2_SDP                                0x71
+#define RT5645_ADDA_CLK1                       0x73
+#define RT5645_ADDA_CLK2                       0x74
+#define RT5645_DMIC_CTRL1                      0x75
+#define RT5645_DMIC_CTRL2                      0x76
+/* Format - TDM Control */
+#define RT5645_TDM_CTRL_1                      0x77
+#define RT5645_TDM_CTRL_2                      0x78
+#define RT5645_TDM_CTRL_3                      0x79
+
+/* Function - Analog */
+#define RT5645_GLB_CLK                         0x80
+#define RT5645_PLL_CTRL1                       0x81
+#define RT5645_PLL_CTRL2                       0x82
+#define RT5645_ASRC_1                          0x83
+#define RT5645_ASRC_2                          0x84
+#define RT5645_ASRC_3                          0x85
+#define RT5645_ASRC_4                          0x8a
+#define RT5645_DEPOP_M1                                0x8e
+#define RT5645_DEPOP_M2                                0x8f
+#define RT5645_DEPOP_M3                                0x90
+#define RT5645_CHARGE_PUMP                     0x91
+#define RT5645_MICBIAS                         0x93
+#define RT5645_A_JD_CTRL1                      0x94
+#define RT5645_VAD_CTRL4                       0x9d
+#define RT5645_CLSD_OUT_CTRL                   0xa0
+/* Function - Digital */
+#define RT5645_ADC_EQ_CTRL1                    0xae
+#define RT5645_ADC_EQ_CTRL2                    0xaf
+#define RT5645_EQ_CTRL1                                0xb0
+#define RT5645_EQ_CTRL2                                0xb1
+#define RT5645_ALC_CTRL_1                      0xb3
+#define RT5645_ALC_CTRL_2                      0xb4
+#define RT5645_ALC_CTRL_3                      0xb5
+#define RT5645_ALC_CTRL_4                      0xb6
+#define RT5645_ALC_CTRL_5                      0xb7
+#define RT5645_JD_CTRL                         0xbb
+#define RT5645_IRQ_CTRL1                       0xbc
+#define RT5645_IRQ_CTRL2                       0xbd
+#define RT5645_IRQ_CTRL3                       0xbe
+#define RT5645_INT_IRQ_ST                      0xbf
+#define RT5645_GPIO_CTRL1                      0xc0
+#define RT5645_GPIO_CTRL2                      0xc1
+#define RT5645_GPIO_CTRL3                      0xc2
+#define RT5645_BASS_BACK                       0xcf
+#define RT5645_MP3_PLUS1                       0xd0
+#define RT5645_MP3_PLUS2                       0xd1
+#define RT5645_ADJ_HPF1                                0xd3
+#define RT5645_ADJ_HPF2                                0xd4
+#define RT5645_HP_CALIB_AMP_DET                        0xd6
+#define RT5645_SV_ZCD1                         0xd9
+#define RT5645_SV_ZCD2                         0xda
+#define RT5645_IL_CMD                          0xdb
+#define RT5645_IL_CMD2                         0xdc
+#define RT5645_IL_CMD3                         0xdd
+#define RT5645_DRC1_HL_CTRL1                   0xe7
+#define RT5645_DRC2_HL_CTRL1                   0xe9
+#define RT5645_MUTI_DRC_CTRL1                  0xea
+#define RT5645_ADC_MONO_HP_CTRL1               0xec
+#define RT5645_ADC_MONO_HP_CTRL2               0xed
+#define RT5645_DRC2_CTRL1                      0xf0
+#define RT5645_DRC2_CTRL2                      0xf1
+#define RT5645_DRC2_CTRL3                      0xf2
+#define RT5645_DRC2_CTRL4                      0xf3
+#define RT5645_DRC2_CTRL5                      0xf4
+#define RT5645_JD_CTRL3                                0xf8
+#define RT5645_JD_CTRL4                                0xf9
+/* General Control */
+#define RT5645_GEN_CTRL1                       0xfa
+#define RT5645_GEN_CTRL2                       0xfb
+#define RT5645_GEN_CTRL3                       0xfc
+
+
+/* Index of Codec Private Register definition */
+#define RT5645_DIG_VOL                         0x00
+#define RT5645_PR_ALC_CTRL_1                   0x01
+#define RT5645_PR_ALC_CTRL_2                   0x02
+#define RT5645_PR_ALC_CTRL_3                   0x03
+#define RT5645_PR_ALC_CTRL_4                   0x04
+#define RT5645_PR_ALC_CTRL_5                   0x05
+#define RT5645_PR_ALC_CTRL_6                   0x06
+#define RT5645_BIAS_CUR1                       0x12
+#define RT5645_BIAS_CUR3                       0x14
+#define RT5645_CLSD_INT_REG1                   0x1c
+#define RT5645_MAMP_INT_REG2                   0x37
+#define RT5645_CHOP_DAC_ADC                    0x3d
+#define RT5645_MIXER_INT_REG                   0x3f
+#define RT5645_3D_SPK                          0x63
+#define RT5645_WND_1                           0x6c
+#define RT5645_WND_2                           0x6d
+#define RT5645_WND_3                           0x6e
+#define RT5645_WND_4                           0x6f
+#define RT5645_WND_5                           0x70
+#define RT5645_WND_8                           0x73
+#define RT5645_DIP_SPK_INF                     0x75
+#define RT5645_HP_DCC_INT1                     0x77
+#define RT5645_EQ_BW_LOP                       0xa0
+#define RT5645_EQ_GN_LOP                       0xa1
+#define RT5645_EQ_FC_BP1                       0xa2
+#define RT5645_EQ_BW_BP1                       0xa3
+#define RT5645_EQ_GN_BP1                       0xa4
+#define RT5645_EQ_FC_BP2                       0xa5
+#define RT5645_EQ_BW_BP2                       0xa6
+#define RT5645_EQ_GN_BP2                       0xa7
+#define RT5645_EQ_FC_BP3                       0xa8
+#define RT5645_EQ_BW_BP3                       0xa9
+#define RT5645_EQ_GN_BP3                       0xaa
+#define RT5645_EQ_FC_BP4                       0xab
+#define RT5645_EQ_BW_BP4                       0xac
+#define RT5645_EQ_GN_BP4                       0xad
+#define RT5645_EQ_FC_HIP1                      0xae
+#define RT5645_EQ_GN_HIP1                      0xaf
+#define RT5645_EQ_FC_HIP2                      0xb0
+#define RT5645_EQ_BW_HIP2                      0xb1
+#define RT5645_EQ_GN_HIP2                      0xb2
+#define RT5645_EQ_PRE_VOL                      0xb3
+#define RT5645_EQ_PST_VOL                      0xb4
+
+
+/* global definition */
+#define RT5645_L_MUTE                          (0x1 << 15)
+#define RT5645_L_MUTE_SFT                      15
+#define RT5645_VOL_L_MUTE                      (0x1 << 14)
+#define RT5645_VOL_L_SFT                       14
+#define RT5645_R_MUTE                          (0x1 << 7)
+#define RT5645_R_MUTE_SFT                      7
+#define RT5645_VOL_R_MUTE                      (0x1 << 6)
+#define RT5645_VOL_R_SFT                       6
+#define RT5645_L_VOL_MASK                      (0x3f << 8)
+#define RT5645_L_VOL_SFT                       8
+#define RT5645_R_VOL_MASK                      (0x3f)
+#define RT5645_R_VOL_SFT                       0
+
+/* IN1 Control 1 (0x0a) */
+#define RT5645_CBJ_BST1_MASK                   (0xf << 12)
+#define RT5645_CBJ_BST1_SFT                    (12)
+#define RT5645_CBJ_JD_HP_EN                    (0x1 << 9)
+#define RT5645_CBJ_JD_MIC_EN                   (0x1 << 8)
+#define RT5645_CBJ_JD_MIC_SW_EN                        (0x1 << 7)
+#define RT5645_CBJ_MIC_SEL_R                   (0x1 << 6)
+#define RT5645_CBJ_MIC_SEL_L                   (0x1 << 5)
+#define RT5645_CBJ_MIC_SW                      (0x1 << 4)
+#define RT5645_CBJ_BST1_EN                     (0x1 << 2)
+
+/* IN1 Control 2 (0x0b) */
+#define RT5645_CBJ_MN_JD                       (0x1 << 12)
+#define RT5645_CAPLESS_EN                      (0x1 << 11)
+#define RT5645_CBJ_DET_MODE                    (0x1 << 7)
+
+/* IN1 Control 3 (0x0c) */
+#define RT5645_CBJ_TIE_G_L                     (0x1 << 15)
+#define RT5645_CBJ_TIE_G_R                     (0x1 << 14)
+
+/* IN2 Control (0x0d) */
+#define RT5645_BST_MASK1                       (0xf<<12)
+#define RT5645_BST_SFT1                                12
+#define RT5645_BST_MASK2                       (0xf<<8)
+#define RT5645_BST_SFT2                                8
+#define RT5645_IN_DF2                          (0x1 << 6)
+#define RT5645_IN_SFT2                         6
+
+/* INL and INR Volume Control (0x0f) */
+#define RT5645_INL_SEL_MASK                    (0x1 << 15)
+#define RT5645_INL_SEL_SFT                     15
+#define RT5645_INL_SEL_IN4P                    (0x0 << 15)
+#define RT5645_INL_SEL_MONOP                   (0x1 << 15)
+#define RT5645_INL_VOL_MASK                    (0x1f << 8)
+#define RT5645_INL_VOL_SFT                     8
+#define RT5645_INR_SEL_MASK                    (0x1 << 7)
+#define RT5645_INR_SEL_SFT                     7
+#define RT5645_INR_SEL_IN4N                    (0x0 << 7)
+#define RT5645_INR_SEL_MONON                   (0x1 << 7)
+#define RT5645_INR_VOL_MASK                    (0x1f)
+#define RT5645_INR_VOL_SFT                     0
+
+/* DAC1 Digital Volume (0x19) */
+#define RT5645_DAC_L1_VOL_MASK                 (0xff << 8)
+#define RT5645_DAC_L1_VOL_SFT                  8
+#define RT5645_DAC_R1_VOL_MASK                 (0xff)
+#define RT5645_DAC_R1_VOL_SFT                  0
+
+/* DAC2 Digital Volume (0x1a) */
+#define RT5645_DAC_L2_VOL_MASK                 (0xff << 8)
+#define RT5645_DAC_L2_VOL_SFT                  8
+#define RT5645_DAC_R2_VOL_MASK                 (0xff)
+#define RT5645_DAC_R2_VOL_SFT                  0
+
+/* DAC2 Control (0x1b) */
+#define RT5645_M_DAC_L2_VOL                    (0x1 << 13)
+#define RT5645_M_DAC_L2_VOL_SFT                        13
+#define RT5645_M_DAC_R2_VOL                    (0x1 << 12)
+#define RT5645_M_DAC_R2_VOL_SFT                        12
+#define RT5645_DAC2_L_SEL_MASK                 (0x7 << 4)
+#define RT5645_DAC2_L_SEL_SFT                  4
+#define RT5645_DAC2_R_SEL_MASK                 (0x7 << 0)
+#define RT5645_DAC2_R_SEL_SFT                  0
+
+/* ADC Digital Volume Control (0x1c) */
+#define RT5645_ADC_L_VOL_MASK                  (0x7f << 8)
+#define RT5645_ADC_L_VOL_SFT                   8
+#define RT5645_ADC_R_VOL_MASK                  (0x7f)
+#define RT5645_ADC_R_VOL_SFT                   0
+
+/* Mono ADC Digital Volume Control (0x1d) */
+#define RT5645_MONO_ADC_L_VOL_MASK             (0x7f << 8)
+#define RT5645_MONO_ADC_L_VOL_SFT              8
+#define RT5645_MONO_ADC_R_VOL_MASK             (0x7f)
+#define RT5645_MONO_ADC_R_VOL_SFT              0
+
+/* ADC Boost Volume Control (0x1e) */
+#define RT5645_STO1_ADC_L_BST_MASK             (0x3 << 14)
+#define RT5645_STO1_ADC_L_BST_SFT              14
+#define RT5645_STO1_ADC_R_BST_MASK             (0x3 << 12)
+#define RT5645_STO1_ADC_R_BST_SFT              12
+#define RT5645_STO1_ADC_COMP_MASK              (0x3 << 10)
+#define RT5645_STO1_ADC_COMP_SFT               10
+#define RT5645_STO2_ADC_L_BST_MASK             (0x3 << 8)
+#define RT5645_STO2_ADC_L_BST_SFT              8
+#define RT5645_STO2_ADC_R_BST_MASK             (0x3 << 6)
+#define RT5645_STO2_ADC_R_BST_SFT              6
+#define RT5645_STO2_ADC_COMP_MASK              (0x3 << 4)
+#define RT5645_STO2_ADC_COMP_SFT               4
+
+/* Stereo2 ADC Mixer Control (0x26) */
+#define RT5645_STO2_ADC_SRC_MASK               (0x1 << 15)
+#define RT5645_STO2_ADC_SRC_SFT                        15
+
+/* Stereo ADC Mixer Control (0x27) */
+#define RT5645_M_ADC_L1                                (0x1 << 14)
+#define RT5645_M_ADC_L1_SFT                    14
+#define RT5645_M_ADC_L2                                (0x1 << 13)
+#define RT5645_M_ADC_L2_SFT                    13
+#define RT5645_ADC_1_SRC_MASK                  (0x1 << 12)
+#define RT5645_ADC_1_SRC_SFT                   12
+#define RT5645_ADC_1_SRC_ADC                   (0x1 << 12)
+#define RT5645_ADC_1_SRC_DACMIX                        (0x0 << 12)
+#define RT5645_ADC_2_SRC_MASK                  (0x1 << 11)
+#define RT5645_ADC_2_SRC_SFT                   11
+#define RT5645_DMIC_SRC_MASK                   (0x1 << 8)
+#define RT5645_DMIC_SRC_SFT                    8
+#define RT5645_M_ADC_R1                                (0x1 << 6)
+#define RT5645_M_ADC_R1_SFT                    6
+#define RT5645_M_ADC_R2                                (0x1 << 5)
+#define RT5645_M_ADC_R2_SFT                    5
+#define RT5645_DMIC3_SRC_MASK                  (0x1 << 1)
+#define RT5645_DMIC3_SRC_SFT                   0
+
+/* Mono ADC Mixer Control (0x28) */
+#define RT5645_M_MONO_ADC_L1                   (0x1 << 14)
+#define RT5645_M_MONO_ADC_L1_SFT               14
+#define RT5645_M_MONO_ADC_L2                   (0x1 << 13)
+#define RT5645_M_MONO_ADC_L2_SFT               13
+#define RT5645_MONO_ADC_L1_SRC_MASK            (0x1 << 12)
+#define RT5645_MONO_ADC_L1_SRC_SFT             12
+#define RT5645_MONO_ADC_L1_SRC_DACMIXL         (0x0 << 12)
+#define RT5645_MONO_ADC_L1_SRC_ADCL            (0x1 << 12)
+#define RT5645_MONO_ADC_L2_SRC_MASK            (0x1 << 11)
+#define RT5645_MONO_ADC_L2_SRC_SFT             11
+#define RT5645_MONO_DMIC_L_SRC_MASK            (0x1 << 8)
+#define RT5645_MONO_DMIC_L_SRC_SFT             8
+#define RT5645_M_MONO_ADC_R1                   (0x1 << 6)
+#define RT5645_M_MONO_ADC_R1_SFT               6
+#define RT5645_M_MONO_ADC_R2                   (0x1 << 5)
+#define RT5645_M_MONO_ADC_R2_SFT               5
+#define RT5645_MONO_ADC_R1_SRC_MASK            (0x1 << 4)
+#define RT5645_MONO_ADC_R1_SRC_SFT             4
+#define RT5645_MONO_ADC_R1_SRC_ADCR            (0x1 << 4)
+#define RT5645_MONO_ADC_R1_SRC_DACMIXR         (0x0 << 4)
+#define RT5645_MONO_ADC_R2_SRC_MASK            (0x1 << 3)
+#define RT5645_MONO_ADC_R2_SRC_SFT             3
+#define RT5645_MONO_DMIC_R_SRC_MASK            (0x3)
+#define RT5645_MONO_DMIC_R_SRC_SFT             0
+
+/* ADC Mixer to DAC Mixer Control (0x29) */
+#define RT5645_M_ADCMIX_L                      (0x1 << 15)
+#define RT5645_M_ADCMIX_L_SFT                  15
+#define RT5645_M_DAC1_L                                (0x1 << 14)
+#define RT5645_M_DAC1_L_SFT                    14
+#define RT5645_DAC1_R_SEL_MASK                 (0x3 << 10)
+#define RT5645_DAC1_R_SEL_SFT                  10
+#define RT5645_DAC1_R_SEL_IF1                  (0x0 << 10)
+#define RT5645_DAC1_R_SEL_IF2                  (0x1 << 10)
+#define RT5645_DAC1_R_SEL_IF3                  (0x2 << 10)
+#define RT5645_DAC1_R_SEL_IF4                  (0x3 << 10)
+#define RT5645_DAC1_L_SEL_MASK                 (0x3 << 8)
+#define RT5645_DAC1_L_SEL_SFT                  8
+#define RT5645_DAC1_L_SEL_IF1                  (0x0 << 8)
+#define RT5645_DAC1_L_SEL_IF2                  (0x1 << 8)
+#define RT5645_DAC1_L_SEL_IF3                  (0x2 << 8)
+#define RT5645_DAC1_L_SEL_IF4                  (0x3 << 8)
+#define RT5645_M_ADCMIX_R                      (0x1 << 7)
+#define RT5645_M_ADCMIX_R_SFT                  7
+#define RT5645_M_DAC1_R                                (0x1 << 6)
+#define RT5645_M_DAC1_R_SFT                    6
+
+/* Stereo DAC Mixer Control (0x2a) */
+#define RT5645_M_DAC_L1                                (0x1 << 14)
+#define RT5645_M_DAC_L1_SFT                    14
+#define RT5645_DAC_L1_STO_L_VOL_MASK           (0x1 << 13)
+#define RT5645_DAC_L1_STO_L_VOL_SFT            13
+#define RT5645_M_DAC_L2                                (0x1 << 12)
+#define RT5645_M_DAC_L2_SFT                    12
+#define RT5645_DAC_L2_STO_L_VOL_MASK           (0x1 << 11)
+#define RT5645_DAC_L2_STO_L_VOL_SFT            11
+#define RT5645_M_ANC_DAC_L                     (0x1 << 10)
+#define RT5645_M_ANC_DAC_L_SFT                 10
+#define RT5645_M_DAC_R1_STO_L                  (0x1 << 9)
+#define RT5645_M_DAC_R1_STO_L_SFT                      9
+#define RT5645_DAC_R1_STO_L_VOL_MASK           (0x1 << 8)
+#define RT5645_DAC_R1_STO_L_VOL_SFT            8
+#define RT5645_M_DAC_R1                                (0x1 << 6)
+#define RT5645_M_DAC_R1_SFT                    6
+#define RT5645_DAC_R1_STO_R_VOL_MASK           (0x1 << 5)
+#define RT5645_DAC_R1_STO_R_VOL_SFT            5
+#define RT5645_M_DAC_R2                                (0x1 << 4)
+#define RT5645_M_DAC_R2_SFT                    4
+#define RT5645_DAC_R2_STO_R_VOL_MASK           (0x1 << 3)
+#define RT5645_DAC_R2_STO_R_VOL_SFT            3
+#define RT5645_M_ANC_DAC_R                     (0x1 << 2)
+#define RT5645_M_ANC_DAC_R_SFT                 2
+#define RT5645_M_DAC_L1_STO_R                  (0x1 << 1)
+#define RT5645_M_DAC_L1_STO_R_SFT                      1
+#define RT5645_DAC_L1_STO_R_VOL_MASK           (0x1)
+#define RT5645_DAC_L1_STO_R_VOL_SFT            0
+
+/* Mono DAC Mixer Control (0x2b) */
+#define RT5645_M_DAC_L1_MONO_L                 (0x1 << 14)
+#define RT5645_M_DAC_L1_MONO_L_SFT             14
+#define RT5645_DAC_L1_MONO_L_VOL_MASK          (0x1 << 13)
+#define RT5645_DAC_L1_MONO_L_VOL_SFT           13
+#define RT5645_M_DAC_L2_MONO_L                 (0x1 << 12)
+#define RT5645_M_DAC_L2_MONO_L_SFT             12
+#define RT5645_DAC_L2_MONO_L_VOL_MASK          (0x1 << 11)
+#define RT5645_DAC_L2_MONO_L_VOL_SFT           11
+#define RT5645_M_DAC_R2_MONO_L                 (0x1 << 10)
+#define RT5645_M_DAC_R2_MONO_L_SFT             10
+#define RT5645_DAC_R2_MONO_L_VOL_MASK          (0x1 << 9)
+#define RT5645_DAC_R2_MONO_L_VOL_SFT           9
+#define RT5645_M_DAC_R1_MONO_R                 (0x1 << 6)
+#define RT5645_M_DAC_R1_MONO_R_SFT             6
+#define RT5645_DAC_R1_MONO_R_VOL_MASK          (0x1 << 5)
+#define RT5645_DAC_R1_MONO_R_VOL_SFT           5
+#define RT5645_M_DAC_R2_MONO_R                 (0x1 << 4)
+#define RT5645_M_DAC_R2_MONO_R_SFT             4
+#define RT5645_DAC_R2_MONO_R_VOL_MASK          (0x1 << 3)
+#define RT5645_DAC_R2_MONO_R_VOL_SFT           3
+#define RT5645_M_DAC_L2_MONO_R                 (0x1 << 2)
+#define RT5645_M_DAC_L2_MONO_R_SFT             2
+#define RT5645_DAC_L2_MONO_R_VOL_MASK          (0x1 << 1)
+#define RT5645_DAC_L2_MONO_R_VOL_SFT           1
+
+/* Digital Mixer Control (0x2c) */
+#define RT5645_M_STO_L_DAC_L                   (0x1 << 15)
+#define RT5645_M_STO_L_DAC_L_SFT               15
+#define RT5645_STO_L_DAC_L_VOL_MASK            (0x1 << 14)
+#define RT5645_STO_L_DAC_L_VOL_SFT             14
+#define RT5645_M_DAC_L2_DAC_L                  (0x1 << 13)
+#define RT5645_M_DAC_L2_DAC_L_SFT              13
+#define RT5645_DAC_L2_DAC_L_VOL_MASK           (0x1 << 12)
+#define RT5645_DAC_L2_DAC_L_VOL_SFT            12
+#define RT5645_M_STO_R_DAC_R                   (0x1 << 11)
+#define RT5645_M_STO_R_DAC_R_SFT               11
+#define RT5645_STO_R_DAC_R_VOL_MASK            (0x1 << 10)
+#define RT5645_STO_R_DAC_R_VOL_SFT             10
+#define RT5645_M_DAC_R2_DAC_R                  (0x1 << 9)
+#define RT5645_M_DAC_R2_DAC_R_SFT              9
+#define RT5645_DAC_R2_DAC_R_VOL_MASK           (0x1 << 8)
+#define RT5645_DAC_R2_DAC_R_VOL_SFT            8
+#define RT5645_M_DAC_R2_DAC_L                  (0x1 << 7)
+#define RT5645_M_DAC_R2_DAC_L_SFT              7
+#define RT5645_DAC_R2_DAC_L_VOL_MASK           (0x1 << 6)
+#define RT5645_DAC_R2_DAC_L_VOL_SFT            6
+#define RT5645_M_DAC_L2_DAC_R                  (0x1 << 5)
+#define RT5645_M_DAC_L2_DAC_R_SFT              5
+#define RT5645_DAC_L2_DAC_R_VOL_MASK           (0x1 << 4)
+#define RT5645_DAC_L2_DAC_R_VOL_SFT            4
+
+/* Digital Interface Data Control (0x2f) */
+#define RT5645_IF1_ADC2_IN_SEL                 (0x1 << 15)
+#define RT5645_IF1_ADC2_IN_SFT                 15
+#define RT5645_IF2_ADC_IN_MASK                 (0x7 << 12)
+#define RT5645_IF2_ADC_IN_SFT                  12
+#define RT5645_IF2_DAC_SEL_MASK                        (0x3 << 10)
+#define RT5645_IF2_DAC_SEL_SFT                 10
+#define RT5645_IF2_ADC_SEL_MASK                        (0x3 << 8)
+#define RT5645_IF2_ADC_SEL_SFT                 8
+#define RT5645_IF3_DAC_SEL_MASK                        (0x3 << 6)
+#define RT5645_IF3_DAC_SEL_SFT                 6
+#define RT5645_IF3_ADC_SEL_MASK                        (0x3 << 4)
+#define RT5645_IF3_ADC_SEL_SFT                 4
+#define RT5645_IF3_ADC_IN_MASK                 (0x7)
+#define RT5645_IF3_ADC_IN_SFT                  0
+
+/* PDM Output Control (0x31) */
+#define RT5645_PDM1_L_MASK                     (0x1 << 15)
+#define RT5645_PDM1_L_SFT                      15
+#define RT5645_M_PDM1_L                                (0x1 << 14)
+#define RT5645_M_PDM1_L_SFT                    14
+#define RT5645_PDM1_R_MASK                     (0x1 << 13)
+#define RT5645_PDM1_R_SFT                      13
+#define RT5645_M_PDM1_R                                (0x1 << 12)
+#define RT5645_M_PDM1_R_SFT                    12
+#define RT5645_PDM2_L_MASK                     (0x1 << 11)
+#define RT5645_PDM2_L_SFT                      11
+#define RT5645_M_PDM2_L                                (0x1 << 10)
+#define RT5645_M_PDM2_L_SFT                    10
+#define RT5645_PDM2_R_MASK                     (0x1 << 9)
+#define RT5645_PDM2_R_SFT                      9
+#define RT5645_M_PDM2_R                                (0x1 << 8)
+#define RT5645_M_PDM2_R_SFT                    8
+#define RT5645_PDM2_BUSY                       (0x1 << 7)
+#define RT5645_PDM1_BUSY                       (0x1 << 6)
+#define RT5645_PDM_PATTERN                     (0x1 << 5)
+#define RT5645_PDM_GAIN                                (0x1 << 4)
+#define RT5645_PDM_DIV_MASK                    (0x3)
+
+/* REC Left Mixer Control 1 (0x3b) */
+#define RT5645_G_HP_L_RM_L_MASK                        (0x7 << 13)
+#define RT5645_G_HP_L_RM_L_SFT                 13
+#define RT5645_G_IN_L_RM_L_MASK                        (0x7 << 10)
+#define RT5645_G_IN_L_RM_L_SFT                 10
+#define RT5645_G_BST4_RM_L_MASK                        (0x7 << 7)
+#define RT5645_G_BST4_RM_L_SFT                 7
+#define RT5645_G_BST3_RM_L_MASK                        (0x7 << 4)
+#define RT5645_G_BST3_RM_L_SFT                 4
+#define RT5645_G_BST2_RM_L_MASK                        (0x7 << 1)
+#define RT5645_G_BST2_RM_L_SFT                 1
+
+/* REC Left Mixer Control 2 (0x3c) */
+#define RT5645_G_BST1_RM_L_MASK                        (0x7 << 13)
+#define RT5645_G_BST1_RM_L_SFT                 13
+#define RT5645_G_OM_L_RM_L_MASK                        (0x7 << 10)
+#define RT5645_G_OM_L_RM_L_SFT                 10
+#define RT5645_M_MM_L_RM_L                     (0x1 << 6)
+#define RT5645_M_MM_L_RM_L_SFT                 6
+#define RT5645_M_IN_L_RM_L                     (0x1 << 5)
+#define RT5645_M_IN_L_RM_L_SFT                 5
+#define RT5645_M_HP_L_RM_L                     (0x1 << 4)
+#define RT5645_M_HP_L_RM_L_SFT                 4
+#define RT5645_M_BST3_RM_L                     (0x1 << 3)
+#define RT5645_M_BST3_RM_L_SFT                 3
+#define RT5645_M_BST2_RM_L                     (0x1 << 2)
+#define RT5645_M_BST2_RM_L_SFT                 2
+#define RT5645_M_BST1_RM_L                     (0x1 << 1)
+#define RT5645_M_BST1_RM_L_SFT                 1
+#define RT5645_M_OM_L_RM_L                     (0x1)
+#define RT5645_M_OM_L_RM_L_SFT                 0
+
+/* REC Right Mixer Control 1 (0x3d) */
+#define RT5645_G_HP_R_RM_R_MASK                        (0x7 << 13)
+#define RT5645_G_HP_R_RM_R_SFT                 13
+#define RT5645_G_IN_R_RM_R_MASK                        (0x7 << 10)
+#define RT5645_G_IN_R_RM_R_SFT                 10
+#define RT5645_G_BST4_RM_R_MASK                        (0x7 << 7)
+#define RT5645_G_BST4_RM_R_SFT                 7
+#define RT5645_G_BST3_RM_R_MASK                        (0x7 << 4)
+#define RT5645_G_BST3_RM_R_SFT                 4
+#define RT5645_G_BST2_RM_R_MASK                        (0x7 << 1)
+#define RT5645_G_BST2_RM_R_SFT                 1
+
+/* REC Right Mixer Control 2 (0x3e) */
+#define RT5645_G_BST1_RM_R_MASK                        (0x7 << 13)
+#define RT5645_G_BST1_RM_R_SFT                 13
+#define RT5645_G_OM_R_RM_R_MASK                        (0x7 << 10)
+#define RT5645_G_OM_R_RM_R_SFT                 10
+#define RT5645_M_MM_R_RM_R                     (0x1 << 6)
+#define RT5645_M_MM_R_RM_R_SFT                 6
+#define RT5645_M_IN_R_RM_R                     (0x1 << 5)
+#define RT5645_M_IN_R_RM_R_SFT                 5
+#define RT5645_M_HP_R_RM_R                     (0x1 << 4)
+#define RT5645_M_HP_R_RM_R_SFT                 4
+#define RT5645_M_BST3_RM_R                     (0x1 << 3)
+#define RT5645_M_BST3_RM_R_SFT                 3
+#define RT5645_M_BST2_RM_R                     (0x1 << 2)
+#define RT5645_M_BST2_RM_R_SFT                 2
+#define RT5645_M_BST1_RM_R                     (0x1 << 1)
+#define RT5645_M_BST1_RM_R_SFT                 1
+#define RT5645_M_OM_R_RM_R                     (0x1)
+#define RT5645_M_OM_R_RM_R_SFT                 0
+
+/* HPOMIX Control (0x40) (0x42) */
+#define RT5645_M_BST1_HV                       (0x1 << 4)
+#define RT5645_M_BST1_HV_SFT                   4
+#define RT5645_M_BST2_HV                       (0x1 << 4)
+#define RT5645_M_BST2_HV_SFT                   4
+#define RT5645_M_BST3_HV                       (0x1 << 3)
+#define RT5645_M_BST3_HV_SFT                   3
+#define RT5645_M_IN_HV                         (0x1 << 2)
+#define RT5645_M_IN_HV_SFT                     2
+#define RT5645_M_DAC2_HV                       (0x1 << 1)
+#define RT5645_M_DAC2_HV_SFT                   1
+#define RT5645_M_DAC1_HV                       (0x1 << 0)
+#define RT5645_M_DAC1_HV_SFT                   0
+
+/* HPMIX Control (0x45) */
+#define RT5645_M_DAC1_HM                       (0x1 << 14)
+#define RT5645_M_DAC1_HM_SFT                   14
+#define RT5645_M_HPVOL_HM                      (0x1 << 13)
+#define RT5645_M_HPVOL_HM_SFT                  13
+
+/* SPK Left Mixer Control (0x46) */
+#define RT5645_G_RM_L_SM_L_MASK                        (0x3 << 14)
+#define RT5645_G_RM_L_SM_L_SFT                 14
+#define RT5645_G_IN_L_SM_L_MASK                        (0x3 << 12)
+#define RT5645_G_IN_L_SM_L_SFT                 12
+#define RT5645_G_DAC_L1_SM_L_MASK              (0x3 << 10)
+#define RT5645_G_DAC_L1_SM_L_SFT               10
+#define RT5645_G_DAC_L2_SM_L_MASK              (0x3 << 8)
+#define RT5645_G_DAC_L2_SM_L_SFT               8
+#define RT5645_G_OM_L_SM_L_MASK                        (0x3 << 6)
+#define RT5645_G_OM_L_SM_L_SFT                 6
+#define RT5645_M_BST1_L_SM_L                   (0x1 << 5)
+#define RT5645_M_BST1_L_SM_L_SFT               5
+#define RT5645_M_IN_L_SM_L                     (0x1 << 3)
+#define RT5645_M_IN_L_SM_L_SFT                 3
+#define RT5645_M_DAC_L1_SM_L                   (0x1 << 1)
+#define RT5645_M_DAC_L1_SM_L_SFT               1
+#define RT5645_M_DAC_L2_SM_L                   (0x1 << 2)
+#define RT5645_M_DAC_L2_SM_L_SFT               2
+#define RT5645_M_BST3_L_SM_L                   (0x1 << 4)
+#define RT5645_M_BST3_L_SM_L_SFT               4
+
+/* SPK Right Mixer Control (0x47) */
+#define RT5645_G_RM_R_SM_R_MASK                        (0x3 << 14)
+#define RT5645_G_RM_R_SM_R_SFT                 14
+#define RT5645_G_IN_R_SM_R_MASK                        (0x3 << 12)
+#define RT5645_G_IN_R_SM_R_SFT                 12
+#define RT5645_G_DAC_R1_SM_R_MASK              (0x3 << 10)
+#define RT5645_G_DAC_R1_SM_R_SFT               10
+#define RT5645_G_DAC_R2_SM_R_MASK              (0x3 << 8)
+#define RT5645_G_DAC_R2_SM_R_SFT               8
+#define RT5645_G_OM_R_SM_R_MASK                        (0x3 << 6)
+#define RT5645_G_OM_R_SM_R_SFT                 6
+#define RT5645_M_BST2_R_SM_R                   (0x1 << 5)
+#define RT5645_M_BST2_R_SM_R_SFT               5
+#define RT5645_M_IN_R_SM_R                     (0x1 << 3)
+#define RT5645_M_IN_R_SM_R_SFT                 3
+#define RT5645_M_DAC_R1_SM_R                   (0x1 << 1)
+#define RT5645_M_DAC_R1_SM_R_SFT               1
+#define RT5645_M_DAC_R2_SM_R                   (0x1 << 2)
+#define RT5645_M_DAC_R2_SM_R_SFT               2
+#define RT5645_M_BST3_R_SM_R                   (0x1 << 4)
+#define RT5645_M_BST3_R_SM_R_SFT               4
+
+/* SPOLMIX Control (0x48) */
+#define RT5645_M_DAC_L1_SPM_L                  (0x1 << 15)
+#define RT5645_M_DAC_L1_SPM_L_SFT              15
+#define RT5645_M_DAC_R1_SPM_L                  (0x1 << 14)
+#define RT5645_M_DAC_R1_SPM_L_SFT              14
+#define RT5645_M_SV_L_SPM_L                    (0x1 << 13)
+#define RT5645_M_SV_L_SPM_L_SFT                        13
+#define RT5645_M_SV_R_SPM_L                    (0x1 << 12)
+#define RT5645_M_SV_R_SPM_L_SFT                        12
+#define RT5645_M_BST3_SPM_L                    (0x1 << 11)
+#define RT5645_M_BST3_SPM_L_SFT                        11
+#define RT5645_M_DAC_R1_SPM_R                  (0x1 << 2)
+#define RT5645_M_DAC_R1_SPM_R_SFT              2
+#define RT5645_M_BST3_SPM_R                    (0x1 << 1)
+#define RT5645_M_BST3_SPM_R_SFT                        1
+#define RT5645_M_SV_R_SPM_R                    (0x1 << 0)
+#define RT5645_M_SV_R_SPM_R_SFT                        0
+
+/* Mono Output Mixer Control (0x4c) */
+#define RT5645_M_OV_L_MM                       (0x1 << 9)
+#define RT5645_M_OV_L_MM_SFT                   9
+#define RT5645_M_DAC_L2_MA                     (0x1 << 8)
+#define RT5645_M_DAC_L2_MA_SFT                 8
+#define RT5645_G_MONOMIX_MASK                  (0x1 << 10)
+#define RT5645_G_MONOMIX_SFT                   10
+#define RT5645_M_BST2_MM                       (0x1 << 4)
+#define RT5645_M_BST2_MM_SFT                   4
+#define RT5645_M_DAC_R1_MM                     (0x1 << 3)
+#define RT5645_M_DAC_R1_MM_SFT                 3
+#define RT5645_M_DAC_R2_MM                     (0x1 << 2)
+#define RT5645_M_DAC_R2_MM_SFT                 2
+#define RT5645_M_DAC_L2_MM                     (0x1 << 1)
+#define RT5645_M_DAC_L2_MM_SFT                 1
+#define RT5645_M_BST3_MM                       (0x1 << 0)
+#define RT5645_M_BST3_MM_SFT                   0
+
+/* Output Left Mixer Control 1 (0x4d) */
+#define RT5645_G_BST3_OM_L_MASK                        (0x7 << 13)
+#define RT5645_G_BST3_OM_L_SFT                 13
+#define RT5645_G_BST2_OM_L_MASK                        (0x7 << 10)
+#define RT5645_G_BST2_OM_L_SFT                 10
+#define RT5645_G_BST1_OM_L_MASK                        (0x7 << 7)
+#define RT5645_G_BST1_OM_L_SFT                 7
+#define RT5645_G_IN_L_OM_L_MASK                        (0x7 << 4)
+#define RT5645_G_IN_L_OM_L_SFT                 4
+#define RT5645_G_RM_L_OM_L_MASK                        (0x7 << 1)
+#define RT5645_G_RM_L_OM_L_SFT                 1
+
+/* Output Left Mixer Control 2 (0x4e) */
+#define RT5645_G_DAC_R2_OM_L_MASK              (0x7 << 13)
+#define RT5645_G_DAC_R2_OM_L_SFT               13
+#define RT5645_G_DAC_L2_OM_L_MASK              (0x7 << 10)
+#define RT5645_G_DAC_L2_OM_L_SFT               10
+#define RT5645_G_DAC_L1_OM_L_MASK              (0x7 << 7)
+#define RT5645_G_DAC_L1_OM_L_SFT               7
+
+/* Output Left Mixer Control 3 (0x4f) */
+#define RT5645_M_BST3_OM_L                     (0x1 << 4)
+#define RT5645_M_BST3_OM_L_SFT                 4
+#define RT5645_M_BST1_OM_L                     (0x1 << 3)
+#define RT5645_M_BST1_OM_L_SFT                 3
+#define RT5645_M_IN_L_OM_L                     (0x1 << 2)
+#define RT5645_M_IN_L_OM_L_SFT                 2
+#define RT5645_M_DAC_L2_OM_L                   (0x1 << 1)
+#define RT5645_M_DAC_L2_OM_L_SFT               1
+#define RT5645_M_DAC_L1_OM_L                   (0x1)
+#define RT5645_M_DAC_L1_OM_L_SFT               0
+
+/* Output Right Mixer Control 1 (0x50) */
+#define RT5645_G_BST4_OM_R_MASK                        (0x7 << 13)
+#define RT5645_G_BST4_OM_R_SFT                 13
+#define RT5645_G_BST2_OM_R_MASK                        (0x7 << 10)
+#define RT5645_G_BST2_OM_R_SFT                 10
+#define RT5645_G_BST1_OM_R_MASK                        (0x7 << 7)
+#define RT5645_G_BST1_OM_R_SFT                 7
+#define RT5645_G_IN_R_OM_R_MASK                        (0x7 << 4)
+#define RT5645_G_IN_R_OM_R_SFT                 4
+#define RT5645_G_RM_R_OM_R_MASK                        (0x7 << 1)
+#define RT5645_G_RM_R_OM_R_SFT                 1
+
+/* Output Right Mixer Control 2 (0x51) */
+#define RT5645_G_DAC_L2_OM_R_MASK              (0x7 << 13)
+#define RT5645_G_DAC_L2_OM_R_SFT               13
+#define RT5645_G_DAC_R2_OM_R_MASK              (0x7 << 10)
+#define RT5645_G_DAC_R2_OM_R_SFT               10
+#define RT5645_G_DAC_R1_OM_R_MASK              (0x7 << 7)
+#define RT5645_G_DAC_R1_OM_R_SFT               7
+
+/* Output Right Mixer Control 3 (0x52) */
+#define RT5645_M_BST3_OM_R                     (0x1 << 4)
+#define RT5645_M_BST3_OM_R_SFT                 4
+#define RT5645_M_BST2_OM_R                     (0x1 << 3)
+#define RT5645_M_BST2_OM_R_SFT                 3
+#define RT5645_M_IN_R_OM_R                     (0x1 << 2)
+#define RT5645_M_IN_R_OM_R_SFT                 2
+#define RT5645_M_DAC_R2_OM_R                   (0x1 << 1)
+#define RT5645_M_DAC_R2_OM_R_SFT               1
+#define RT5645_M_DAC_R1_OM_R                   (0x1)
+#define RT5645_M_DAC_R1_OM_R_SFT               0
+
+/* LOUT Mixer Control (0x53) */
+#define RT5645_M_DAC_L1_LM                     (0x1 << 15)
+#define RT5645_M_DAC_L1_LM_SFT                 15
+#define RT5645_M_DAC_R1_LM                     (0x1 << 14)
+#define RT5645_M_DAC_R1_LM_SFT                 14
+#define RT5645_M_OV_L_LM                       (0x1 << 13)
+#define RT5645_M_OV_L_LM_SFT                   13
+#define RT5645_M_OV_R_LM                       (0x1 << 12)
+#define RT5645_M_OV_R_LM_SFT                   12
+#define RT5645_G_LOUTMIX_MASK                  (0x1 << 11)
+#define RT5645_G_LOUTMIX_SFT                   11
+
+/* Power Management for Digital 1 (0x61) */
+#define RT5645_PWR_I2S1                                (0x1 << 15)
+#define RT5645_PWR_I2S1_BIT                    15
+#define RT5645_PWR_I2S2                                (0x1 << 14)
+#define RT5645_PWR_I2S2_BIT                    14
+#define RT5645_PWR_I2S3                                (0x1 << 13)
+#define RT5645_PWR_I2S3_BIT                    13
+#define RT5645_PWR_DAC_L1                      (0x1 << 12)
+#define RT5645_PWR_DAC_L1_BIT                  12
+#define RT5645_PWR_DAC_R1                      (0x1 << 11)
+#define RT5645_PWR_DAC_R1_BIT                  11
+#define RT5645_PWR_CLS_D_R                     (0x1 << 9)
+#define RT5645_PWR_CLS_D_R_BIT                 9
+#define RT5645_PWR_CLS_D_L                     (0x1 << 8)
+#define RT5645_PWR_CLS_D_L_BIT                 8
+#define RT5645_PWR_ADC_R                       (0x1 << 1)
+#define RT5645_PWR_ADC_R_BIT                   1
+#define RT5645_PWR_DAC_L2                      (0x1 << 7)
+#define RT5645_PWR_DAC_L2_BIT                  7
+#define RT5645_PWR_DAC_R2                      (0x1 << 6)
+#define RT5645_PWR_DAC_R2_BIT                  6
+#define RT5645_PWR_ADC_L                       (0x1 << 2)
+#define RT5645_PWR_ADC_L_BIT                   2
+#define RT5645_PWR_ADC_R                       (0x1 << 1)
+#define RT5645_PWR_ADC_R_BIT                   1
+#define RT5645_PWR_CLS_D                       (0x1)
+#define RT5645_PWR_CLS_D_BIT                   0
+
+/* Power Management for Digital 2 (0x62) */
+#define RT5645_PWR_ADC_S1F                     (0x1 << 15)
+#define RT5645_PWR_ADC_S1F_BIT                 15
+#define RT5645_PWR_ADC_MF_L                    (0x1 << 14)
+#define RT5645_PWR_ADC_MF_L_BIT                        14
+#define RT5645_PWR_ADC_MF_R                    (0x1 << 13)
+#define RT5645_PWR_ADC_MF_R_BIT                        13
+#define RT5645_PWR_I2S_DSP                     (0x1 << 12)
+#define RT5645_PWR_I2S_DSP_BIT                 12
+#define RT5645_PWR_DAC_S1F                     (0x1 << 11)
+#define RT5645_PWR_DAC_S1F_BIT                 11
+#define RT5645_PWR_DAC_MF_L                    (0x1 << 10)
+#define RT5645_PWR_DAC_MF_L_BIT                        10
+#define RT5645_PWR_DAC_MF_R                    (0x1 << 9)
+#define RT5645_PWR_DAC_MF_R_BIT                        9
+#define RT5645_PWR_ADC_S2F                     (0x1 << 8)
+#define RT5645_PWR_ADC_S2F_BIT                 8
+#define RT5645_PWR_PDM1                                (0x1 << 7)
+#define RT5645_PWR_PDM1_BIT                    7
+#define RT5645_PWR_PDM2                                (0x1 << 6)
+#define RT5645_PWR_PDM2_BIT                    6
+#define RT5645_PWR_IPTV                                (0x1 << 1)
+#define RT5645_PWR_IPTV_BIT                    1
+#define RT5645_PWR_PAD                         (0x1)
+#define RT5645_PWR_PAD_BIT                     0
+
+/* Power Management for Analog 1 (0x63) */
+#define RT5645_PWR_VREF1                       (0x1 << 15)
+#define RT5645_PWR_VREF1_BIT                   15
+#define RT5645_PWR_FV1                         (0x1 << 14)
+#define RT5645_PWR_FV1_BIT                     14
+#define RT5645_PWR_MB                          (0x1 << 13)
+#define RT5645_PWR_MB_BIT                      13
+#define RT5645_PWR_LM                          (0x1 << 12)
+#define RT5645_PWR_LM_BIT                      12
+#define RT5645_PWR_BG                          (0x1 << 11)
+#define RT5645_PWR_BG_BIT                      11
+#define RT5645_PWR_MA                          (0x1 << 10)
+#define RT5645_PWR_MA_BIT                      10
+#define RT5645_PWR_HP_L                                (0x1 << 7)
+#define RT5645_PWR_HP_L_BIT                    7
+#define RT5645_PWR_HP_R                                (0x1 << 6)
+#define RT5645_PWR_HP_R_BIT                    6
+#define RT5645_PWR_HA                          (0x1 << 5)
+#define RT5645_PWR_HA_BIT                      5
+#define RT5645_PWR_VREF2                       (0x1 << 4)
+#define RT5645_PWR_VREF2_BIT                   4
+#define RT5645_PWR_FV2                         (0x1 << 3)
+#define RT5645_PWR_FV2_BIT                     3
+#define RT5645_LDO_SEL_MASK                    (0x3)
+#define RT5645_LDO_SEL_SFT                     0
+
+/* Power Management for Analog 2 (0x64) */
+#define RT5645_PWR_BST1                                (0x1 << 15)
+#define RT5645_PWR_BST1_BIT                    15
+#define RT5645_PWR_BST2                                (0x1 << 14)
+#define RT5645_PWR_BST2_BIT                    14
+#define RT5645_PWR_BST3                                (0x1 << 13)
+#define RT5645_PWR_BST3_BIT                    13
+#define RT5645_PWR_BST4                                (0x1 << 12)
+#define RT5645_PWR_BST4_BIT                    12
+#define RT5645_PWR_MB1                         (0x1 << 11)
+#define RT5645_PWR_MB1_BIT                     11
+#define RT5645_PWR_MB2                         (0x1 << 10)
+#define RT5645_PWR_MB2_BIT                     10
+#define RT5645_PWR_PLL                         (0x1 << 9)
+#define RT5645_PWR_PLL_BIT                     9
+#define RT5645_PWR_BST2_P                      (0x1 << 5)
+#define RT5645_PWR_BST2_P_BIT                  5
+#define RT5645_PWR_BST3_P                      (0x1 << 4)
+#define RT5645_PWR_BST3_P_BIT                  4
+#define RT5645_PWR_BST4_P                      (0x1 << 3)
+#define RT5645_PWR_BST4_P_BIT                  3
+#define RT5645_PWR_JD1                         (0x1 << 2)
+#define RT5645_PWR_JD1_BIT                     2
+#define RT5645_PWR_JD                          (0x1 << 1)
+#define RT5645_PWR_JD_BIT                      1
+
+/* Power Management for Mixer (0x65) */
+#define RT5645_PWR_OM_L                                (0x1 << 15)
+#define RT5645_PWR_OM_L_BIT                    15
+#define RT5645_PWR_OM_R                                (0x1 << 14)
+#define RT5645_PWR_OM_R_BIT                    14
+#define RT5645_PWR_SM_L                                (0x1 << 13)
+#define RT5645_PWR_SM_L_BIT                    13
+#define RT5645_PWR_SM_R                                (0x1 << 12)
+#define RT5645_PWR_SM_R_BIT                    12
+#define RT5645_PWR_RM_L                                (0x1 << 11)
+#define RT5645_PWR_RM_L_BIT                    11
+#define RT5645_PWR_RM_R                                (0x1 << 10)
+#define RT5645_PWR_RM_R_BIT                    10
+#define RT5645_PWR_MM                          (0x1 << 8)
+#define RT5645_PWR_MM_BIT                      8
+#define RT5645_PWR_HM_L                                (0x1 << 7)
+#define RT5645_PWR_HM_L_BIT                    7
+#define RT5645_PWR_HM_R                                (0x1 << 6)
+#define RT5645_PWR_HM_R_BIT                    6
+#define RT5645_PWR_LDO2                                (0x1 << 1)
+#define RT5645_PWR_LDO2_BIT                    1
+
+/* Power Management for Volume (0x66) */
+#define RT5645_PWR_SV_L                                (0x1 << 15)
+#define RT5645_PWR_SV_L_BIT                    15
+#define RT5645_PWR_SV_R                                (0x1 << 14)
+#define RT5645_PWR_SV_R_BIT                    14
+#define RT5645_PWR_HV_L                                (0x1 << 11)
+#define RT5645_PWR_HV_L_BIT                    11
+#define RT5645_PWR_HV_R                                (0x1 << 10)
+#define RT5645_PWR_HV_R_BIT                    10
+#define RT5645_PWR_IN_L                                (0x1 << 9)
+#define RT5645_PWR_IN_L_BIT                    9
+#define RT5645_PWR_IN_R                                (0x1 << 8)
+#define RT5645_PWR_IN_R_BIT                    8
+#define RT5645_PWR_MIC_DET                     (0x1 << 5)
+#define RT5645_PWR_MIC_DET_BIT                 5
+
+/* I2S1/2 Audio Serial Data Port Control (0x70 0x71) */
+#define RT5645_I2S_MS_MASK                     (0x1 << 15)
+#define RT5645_I2S_MS_SFT                      15
+#define RT5645_I2S_MS_M                                (0x0 << 15)
+#define RT5645_I2S_MS_S                                (0x1 << 15)
+#define RT5645_I2S_O_CP_MASK                   (0x3 << 10)
+#define RT5645_I2S_O_CP_SFT                    10
+#define RT5645_I2S_O_CP_OFF                    (0x0 << 10)
+#define RT5645_I2S_O_CP_U_LAW                  (0x1 << 10)
+#define RT5645_I2S_O_CP_A_LAW                  (0x2 << 10)
+#define RT5645_I2S_I_CP_MASK                   (0x3 << 8)
+#define RT5645_I2S_I_CP_SFT                    8
+#define RT5645_I2S_I_CP_OFF                    (0x0 << 8)
+#define RT5645_I2S_I_CP_U_LAW                  (0x1 << 8)
+#define RT5645_I2S_I_CP_A_LAW                  (0x2 << 8)
+#define RT5645_I2S_BP_MASK                     (0x1 << 7)
+#define RT5645_I2S_BP_SFT                      7
+#define RT5645_I2S_BP_NOR                      (0x0 << 7)
+#define RT5645_I2S_BP_INV                      (0x1 << 7)
+#define RT5645_I2S_DL_MASK                     (0x3 << 2)
+#define RT5645_I2S_DL_SFT                      2
+#define RT5645_I2S_DL_16                       (0x0 << 2)
+#define RT5645_I2S_DL_20                       (0x1 << 2)
+#define RT5645_I2S_DL_24                       (0x2 << 2)
+#define RT5645_I2S_DL_8                                (0x3 << 2)
+#define RT5645_I2S_DF_MASK                     (0x3)
+#define RT5645_I2S_DF_SFT                      0
+#define RT5645_I2S_DF_I2S                      (0x0)
+#define RT5645_I2S_DF_LEFT                     (0x1)
+#define RT5645_I2S_DF_PCM_A                    (0x2)
+#define RT5645_I2S_DF_PCM_B                    (0x3)
+
+/* I2S2 Audio Serial Data Port Control (0x71) */
+#define RT5645_I2S2_SDI_MASK                   (0x1 << 6)
+#define RT5645_I2S2_SDI_SFT                    6
+#define RT5645_I2S2_SDI_I2S1                   (0x0 << 6)
+#define RT5645_I2S2_SDI_I2S2                   (0x1 << 6)
+
+/* ADC/DAC Clock Control 1 (0x73) */
+#define RT5645_I2S_BCLK_MS1_MASK               (0x1 << 15)
+#define RT5645_I2S_BCLK_MS1_SFT                        15
+#define RT5645_I2S_BCLK_MS1_32                 (0x0 << 15)
+#define RT5645_I2S_BCLK_MS1_64                 (0x1 << 15)
+#define RT5645_I2S_PD1_MASK                    (0x7 << 12)
+#define RT5645_I2S_PD1_SFT                     12
+#define RT5645_I2S_PD1_1                       (0x0 << 12)
+#define RT5645_I2S_PD1_2                       (0x1 << 12)
+#define RT5645_I2S_PD1_3                       (0x2 << 12)
+#define RT5645_I2S_PD1_4                       (0x3 << 12)
+#define RT5645_I2S_PD1_6                       (0x4 << 12)
+#define RT5645_I2S_PD1_8                       (0x5 << 12)
+#define RT5645_I2S_PD1_12                      (0x6 << 12)
+#define RT5645_I2S_PD1_16                      (0x7 << 12)
+#define RT5645_I2S_BCLK_MS2_MASK               (0x1 << 11)
+#define RT5645_I2S_BCLK_MS2_SFT                        11
+#define RT5645_I2S_BCLK_MS2_32                 (0x0 << 11)
+#define RT5645_I2S_BCLK_MS2_64                 (0x1 << 11)
+#define RT5645_I2S_PD2_MASK                    (0x7 << 8)
+#define RT5645_I2S_PD2_SFT                     8
+#define RT5645_I2S_PD2_1                       (0x0 << 8)
+#define RT5645_I2S_PD2_2                       (0x1 << 8)
+#define RT5645_I2S_PD2_3                       (0x2 << 8)
+#define RT5645_I2S_PD2_4                       (0x3 << 8)
+#define RT5645_I2S_PD2_6                       (0x4 << 8)
+#define RT5645_I2S_PD2_8                       (0x5 << 8)
+#define RT5645_I2S_PD2_12                      (0x6 << 8)
+#define RT5645_I2S_PD2_16                      (0x7 << 8)
+#define RT5645_I2S_BCLK_MS3_MASK               (0x1 << 7)
+#define RT5645_I2S_BCLK_MS3_SFT                        7
+#define RT5645_I2S_BCLK_MS3_32                 (0x0 << 7)
+#define RT5645_I2S_BCLK_MS3_64                 (0x1 << 7)
+#define RT5645_I2S_PD3_MASK                    (0x7 << 4)
+#define RT5645_I2S_PD3_SFT                     4
+#define RT5645_I2S_PD3_1                       (0x0 << 4)
+#define RT5645_I2S_PD3_2                       (0x1 << 4)
+#define RT5645_I2S_PD3_3                       (0x2 << 4)
+#define RT5645_I2S_PD3_4                       (0x3 << 4)
+#define RT5645_I2S_PD3_6                       (0x4 << 4)
+#define RT5645_I2S_PD3_8                       (0x5 << 4)
+#define RT5645_I2S_PD3_12                      (0x6 << 4)
+#define RT5645_I2S_PD3_16                      (0x7 << 4)
+#define RT5645_DAC_OSR_MASK                    (0x3 << 2)
+#define RT5645_DAC_OSR_SFT                     2
+#define RT5645_DAC_OSR_128                     (0x0 << 2)
+#define RT5645_DAC_OSR_64                      (0x1 << 2)
+#define RT5645_DAC_OSR_32                      (0x2 << 2)
+#define RT5645_DAC_OSR_16                      (0x3 << 2)
+#define RT5645_ADC_OSR_MASK                    (0x3)
+#define RT5645_ADC_OSR_SFT                     0
+#define RT5645_ADC_OSR_128                     (0x0)
+#define RT5645_ADC_OSR_64                      (0x1)
+#define RT5645_ADC_OSR_32                      (0x2)
+#define RT5645_ADC_OSR_16                      (0x3)
+
+/* ADC/DAC Clock Control 2 (0x74) */
+#define RT5645_DAC_L_OSR_MASK                  (0x3 << 14)
+#define RT5645_DAC_L_OSR_SFT                   14
+#define RT5645_DAC_L_OSR_128                   (0x0 << 14)
+#define RT5645_DAC_L_OSR_64                    (0x1 << 14)
+#define RT5645_DAC_L_OSR_32                    (0x2 << 14)
+#define RT5645_DAC_L_OSR_16                    (0x3 << 14)
+#define RT5645_ADC_R_OSR_MASK                  (0x3 << 12)
+#define RT5645_ADC_R_OSR_SFT                   12
+#define RT5645_ADC_R_OSR_128                   (0x0 << 12)
+#define RT5645_ADC_R_OSR_64                    (0x1 << 12)
+#define RT5645_ADC_R_OSR_32                    (0x2 << 12)
+#define RT5645_ADC_R_OSR_16                    (0x3 << 12)
+#define RT5645_DAHPF_EN                                (0x1 << 11)
+#define RT5645_DAHPF_EN_SFT                    11
+#define RT5645_ADHPF_EN                                (0x1 << 10)
+#define RT5645_ADHPF_EN_SFT                    10
+
+/* Digital Microphone Control (0x75) */
+#define RT5645_DMIC_1_EN_MASK                  (0x1 << 15)
+#define RT5645_DMIC_1_EN_SFT                   15
+#define RT5645_DMIC_1_DIS                      (0x0 << 15)
+#define RT5645_DMIC_1_EN                       (0x1 << 15)
+#define RT5645_DMIC_2_EN_MASK                  (0x1 << 14)
+#define RT5645_DMIC_2_EN_SFT                   14
+#define RT5645_DMIC_2_DIS                      (0x0 << 14)
+#define RT5645_DMIC_2_EN                       (0x1 << 14)
+#define RT5645_DMIC_1L_LH_MASK                 (0x1 << 13)
+#define RT5645_DMIC_1L_LH_SFT                  13
+#define RT5645_DMIC_1L_LH_FALLING              (0x0 << 13)
+#define RT5645_DMIC_1L_LH_RISING               (0x1 << 13)
+#define RT5645_DMIC_1R_LH_MASK                 (0x1 << 12)
+#define RT5645_DMIC_1R_LH_SFT                  12
+#define RT5645_DMIC_1R_LH_FALLING              (0x0 << 12)
+#define RT5645_DMIC_1R_LH_RISING               (0x1 << 12)
+#define RT5645_DMIC_2_DP_MASK                  (0x3 << 10)
+#define RT5645_DMIC_2_DP_SFT                   10
+#define RT5645_DMIC_2_DP_GPIO6                 (0x0 << 10)
+#define RT5645_DMIC_2_DP_GPIO10                        (0x1 << 10)
+#define RT5645_DMIC_2_DP_GPIO12                        (0x2 << 10)
+#define RT5645_DMIC_2_DP_IN2P                  (0x3 << 10)
+#define RT5645_DMIC_2L_LH_MASK                 (0x1 << 9)
+#define RT5645_DMIC_2L_LH_SFT                  9
+#define RT5645_DMIC_2L_LH_FALLING              (0x0 << 9)
+#define RT5645_DMIC_2L_LH_RISING               (0x1 << 9)
+#define RT5645_DMIC_2R_LH_MASK                 (0x1 << 8)
+#define RT5645_DMIC_2R_LH_SFT                  8
+#define RT5645_DMIC_2R_LH_FALLING              (0x0 << 8)
+#define RT5645_DMIC_2R_LH_RISING               (0x1 << 8)
+#define RT5645_DMIC_CLK_MASK                   (0x7 << 5)
+#define RT5645_DMIC_CLK_SFT                    5
+#define RT5645_DMIC_3_EN_MASK                  (0x1 << 4)
+#define RT5645_DMIC_3_EN_SFT                   4
+#define RT5645_DMIC_3_DIS                      (0x0 << 4)
+#define RT5645_DMIC_3_EN                       (0x1 << 4)
+#define RT5645_DMIC_1_DP_MASK                  (0x3 << 0)
+#define RT5645_DMIC_1_DP_SFT                   0
+#define RT5645_DMIC_1_DP_GPIO5                 (0x0 << 0)
+#define RT5645_DMIC_1_DP_IN2N                  (0x1 << 0)
+#define RT5645_DMIC_1_DP_GPIO11                        (0x2 << 0)
+
+/* TDM Control 1 (0x77) */
+#define RT5645_IF1_ADC_IN_MASK                 (0x3 << 8)
+#define RT5645_IF1_ADC_IN_SFT                  8
+
+/* Global Clock Control (0x80) */
+#define RT5645_SCLK_SRC_MASK                   (0x3 << 14)
+#define RT5645_SCLK_SRC_SFT                    14
+#define RT5645_SCLK_SRC_MCLK                   (0x0 << 14)
+#define RT5645_SCLK_SRC_PLL1                   (0x1 << 14)
+#define RT5645_SCLK_SRC_RCCLK                  (0x2 << 14) /* 15MHz */
+#define RT5645_PLL1_SRC_MASK                   (0x3 << 12)
+#define RT5645_PLL1_SRC_SFT                    12
+#define RT5645_PLL1_SRC_MCLK                   (0x0 << 12)
+#define RT5645_PLL1_SRC_BCLK1                  (0x1 << 12)
+#define RT5645_PLL1_SRC_BCLK2                  (0x2 << 12)
+#define RT5645_PLL1_SRC_BCLK3                  (0x3 << 12)
+#define RT5645_PLL1_PD_MASK                    (0x1 << 3)
+#define RT5645_PLL1_PD_SFT                     3
+#define RT5645_PLL1_PD_1                       (0x0 << 3)
+#define RT5645_PLL1_PD_2                       (0x1 << 3)
+
+#define RT5645_PLL_INP_MAX                     40000000
+#define RT5645_PLL_INP_MIN                     256000
+/* PLL M/N/K Code Control 1 (0x81) */
+#define RT5645_PLL_N_MAX                       0x1ff
+#define RT5645_PLL_N_MASK                      (RT5645_PLL_N_MAX << 7)
+#define RT5645_PLL_N_SFT                       7
+#define RT5645_PLL_K_MAX                       0x1f
+#define RT5645_PLL_K_MASK                      (RT5645_PLL_K_MAX)
+#define RT5645_PLL_K_SFT                       0
+
+/* PLL M/N/K Code Control 2 (0x82) */
+#define RT5645_PLL_M_MAX                       0xf
+#define RT5645_PLL_M_MASK                      (RT5645_PLL_M_MAX << 12)
+#define RT5645_PLL_M_SFT                       12
+#define RT5645_PLL_M_BP                                (0x1 << 11)
+#define RT5645_PLL_M_BP_SFT                    11
+
+/* ASRC Control 1 (0x83) */
+#define RT5645_STO_T_MASK                      (0x1 << 15)
+#define RT5645_STO_T_SFT                       15
+#define RT5645_STO_T_SCLK                      (0x0 << 15)
+#define RT5645_STO_T_LRCK1                     (0x1 << 15)
+#define RT5645_M1_T_MASK                       (0x1 << 14)
+#define RT5645_M1_T_SFT                                14
+#define RT5645_M1_T_I2S2                       (0x0 << 14)
+#define RT5645_M1_T_I2S2_D3                    (0x1 << 14)
+#define RT5645_I2S2_F_MASK                     (0x1 << 12)
+#define RT5645_I2S2_F_SFT                      12
+#define RT5645_I2S2_F_I2S2_D2                  (0x0 << 12)
+#define RT5645_I2S2_F_I2S1_TCLK                        (0x1 << 12)
+#define RT5645_DMIC_1_M_MASK                   (0x1 << 9)
+#define RT5645_DMIC_1_M_SFT                    9
+#define RT5645_DMIC_1_M_NOR                    (0x0 << 9)
+#define RT5645_DMIC_1_M_ASYN                   (0x1 << 9)
+#define RT5645_DMIC_2_M_MASK                   (0x1 << 8)
+#define RT5645_DMIC_2_M_SFT                    8
+#define RT5645_DMIC_2_M_NOR                    (0x0 << 8)
+#define RT5645_DMIC_2_M_ASYN                   (0x1 << 8)
+
+/* ASRC Control 2 (0x84) */
+#define RT5645_MDA_L_M_MASK                    (0x1 << 15)
+#define RT5645_MDA_L_M_SFT                     15
+#define RT5645_MDA_L_M_NOR                     (0x0 << 15)
+#define RT5645_MDA_L_M_ASYN                    (0x1 << 15)
+#define RT5645_MDA_R_M_MASK                    (0x1 << 14)
+#define RT5645_MDA_R_M_SFT                     14
+#define RT5645_MDA_R_M_NOR                     (0x0 << 14)
+#define RT5645_MDA_R_M_ASYN                    (0x1 << 14)
+#define RT5645_MAD_L_M_MASK                    (0x1 << 13)
+#define RT5645_MAD_L_M_SFT                     13
+#define RT5645_MAD_L_M_NOR                     (0x0 << 13)
+#define RT5645_MAD_L_M_ASYN                    (0x1 << 13)
+#define RT5645_MAD_R_M_MASK                    (0x1 << 12)
+#define RT5645_MAD_R_M_SFT                     12
+#define RT5645_MAD_R_M_NOR                     (0x0 << 12)
+#define RT5645_MAD_R_M_ASYN                    (0x1 << 12)
+#define RT5645_ADC_M_MASK                      (0x1 << 11)
+#define RT5645_ADC_M_SFT                       11
+#define RT5645_ADC_M_NOR                       (0x0 << 11)
+#define RT5645_ADC_M_ASYN                      (0x1 << 11)
+#define RT5645_STO_DAC_M_MASK                  (0x1 << 5)
+#define RT5645_STO_DAC_M_SFT                   5
+#define RT5645_STO_DAC_M_NOR                   (0x0 << 5)
+#define RT5645_STO_DAC_M_ASYN                  (0x1 << 5)
+#define RT5645_I2S1_R_D_MASK                   (0x1 << 4)
+#define RT5645_I2S1_R_D_SFT                    4
+#define RT5645_I2S1_R_D_DIS                    (0x0 << 4)
+#define RT5645_I2S1_R_D_EN                     (0x1 << 4)
+#define RT5645_I2S2_R_D_MASK                   (0x1 << 3)
+#define RT5645_I2S2_R_D_SFT                    3
+#define RT5645_I2S2_R_D_DIS                    (0x0 << 3)
+#define RT5645_I2S2_R_D_EN                     (0x1 << 3)
+#define RT5645_PRE_SCLK_MASK                   (0x3)
+#define RT5645_PRE_SCLK_SFT                    0
+#define RT5645_PRE_SCLK_512                    (0x0)
+#define RT5645_PRE_SCLK_1024                   (0x1)
+#define RT5645_PRE_SCLK_2048                   (0x2)
+
+/* ASRC Control 3 (0x85) */
+#define RT5645_I2S1_RATE_MASK                  (0xf << 12)
+#define RT5645_I2S1_RATE_SFT                   12
+#define RT5645_I2S2_RATE_MASK                  (0xf << 8)
+#define RT5645_I2S2_RATE_SFT                   8
+
+/* ASRC Control 4 (0x89) */
+#define RT5645_I2S1_PD_MASK                    (0x7 << 12)
+#define RT5645_I2S1_PD_SFT                     12
+#define RT5645_I2S2_PD_MASK                    (0x7 << 8)
+#define RT5645_I2S2_PD_SFT                     8
+
+/* HPOUT Over Current Detection (0x8b) */
+#define RT5645_HP_OVCD_MASK                    (0x1 << 10)
+#define RT5645_HP_OVCD_SFT                     10
+#define RT5645_HP_OVCD_DIS                     (0x0 << 10)
+#define RT5645_HP_OVCD_EN                      (0x1 << 10)
+#define RT5645_HP_OC_TH_MASK                   (0x3 << 8)
+#define RT5645_HP_OC_TH_SFT                    8
+#define RT5645_HP_OC_TH_90                     (0x0 << 8)
+#define RT5645_HP_OC_TH_105                    (0x1 << 8)
+#define RT5645_HP_OC_TH_120                    (0x2 << 8)
+#define RT5645_HP_OC_TH_135                    (0x3 << 8)
+
+/* Class D Over Current Control (0x8c) */
+#define RT5645_CLSD_OC_MASK                    (0x1 << 9)
+#define RT5645_CLSD_OC_SFT                     9
+#define RT5645_CLSD_OC_PU                      (0x0 << 9)
+#define RT5645_CLSD_OC_PD                      (0x1 << 9)
+#define RT5645_AUTO_PD_MASK                    (0x1 << 8)
+#define RT5645_AUTO_PD_SFT                     8
+#define RT5645_AUTO_PD_DIS                     (0x0 << 8)
+#define RT5645_AUTO_PD_EN                      (0x1 << 8)
+#define RT5645_CLSD_OC_TH_MASK                 (0x3f)
+#define RT5645_CLSD_OC_TH_SFT                  0
+
+/* Class D Output Control (0x8d) */
+#define RT5645_CLSD_RATIO_MASK                 (0xf << 12)
+#define RT5645_CLSD_RATIO_SFT                  12
+#define RT5645_CLSD_OM_MASK                    (0x1 << 11)
+#define RT5645_CLSD_OM_SFT                     11
+#define RT5645_CLSD_OM_MONO                    (0x0 << 11)
+#define RT5645_CLSD_OM_STO                     (0x1 << 11)
+#define RT5645_CLSD_SCH_MASK                   (0x1 << 10)
+#define RT5645_CLSD_SCH_SFT                    10
+#define RT5645_CLSD_SCH_L                      (0x0 << 10)
+#define RT5645_CLSD_SCH_S                      (0x1 << 10)
+
+/* Depop Mode Control 1 (0x8e) */
+#define RT5645_SMT_TRIG_MASK                   (0x1 << 15)
+#define RT5645_SMT_TRIG_SFT                    15
+#define RT5645_SMT_TRIG_DIS                    (0x0 << 15)
+#define RT5645_SMT_TRIG_EN                     (0x1 << 15)
+#define RT5645_HP_L_SMT_MASK                   (0x1 << 9)
+#define RT5645_HP_L_SMT_SFT                    9
+#define RT5645_HP_L_SMT_DIS                    (0x0 << 9)
+#define RT5645_HP_L_SMT_EN                     (0x1 << 9)
+#define RT5645_HP_R_SMT_MASK                   (0x1 << 8)
+#define RT5645_HP_R_SMT_SFT                    8
+#define RT5645_HP_R_SMT_DIS                    (0x0 << 8)
+#define RT5645_HP_R_SMT_EN                     (0x1 << 8)
+#define RT5645_HP_CD_PD_MASK                   (0x1 << 7)
+#define RT5645_HP_CD_PD_SFT                    7
+#define RT5645_HP_CD_PD_DIS                    (0x0 << 7)
+#define RT5645_HP_CD_PD_EN                     (0x1 << 7)
+#define RT5645_RSTN_MASK                       (0x1 << 6)
+#define RT5645_RSTN_SFT                                6
+#define RT5645_RSTN_DIS                                (0x0 << 6)
+#define RT5645_RSTN_EN                         (0x1 << 6)
+#define RT5645_RSTP_MASK                       (0x1 << 5)
+#define RT5645_RSTP_SFT                                5
+#define RT5645_RSTP_DIS                                (0x0 << 5)
+#define RT5645_RSTP_EN                         (0x1 << 5)
+#define RT5645_HP_CO_MASK                      (0x1 << 4)
+#define RT5645_HP_CO_SFT                       4
+#define RT5645_HP_CO_DIS                       (0x0 << 4)
+#define RT5645_HP_CO_EN                                (0x1 << 4)
+#define RT5645_HP_CP_MASK                      (0x1 << 3)
+#define RT5645_HP_CP_SFT                       3
+#define RT5645_HP_CP_PD                                (0x0 << 3)
+#define RT5645_HP_CP_PU                                (0x1 << 3)
+#define RT5645_HP_SG_MASK                      (0x1 << 2)
+#define RT5645_HP_SG_SFT                       2
+#define RT5645_HP_SG_DIS                       (0x0 << 2)
+#define RT5645_HP_SG_EN                                (0x1 << 2)
+#define RT5645_HP_DP_MASK                      (0x1 << 1)
+#define RT5645_HP_DP_SFT                       1
+#define RT5645_HP_DP_PD                                (0x0 << 1)
+#define RT5645_HP_DP_PU                                (0x1 << 1)
+#define RT5645_HP_CB_MASK                      (0x1)
+#define RT5645_HP_CB_SFT                       0
+#define RT5645_HP_CB_PD                                (0x0)
+#define RT5645_HP_CB_PU                                (0x1)
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5645_DEPOP_MASK                      (0x1 << 13)
+#define RT5645_DEPOP_SFT                       13
+#define RT5645_DEPOP_AUTO                      (0x0 << 13)
+#define RT5645_DEPOP_MAN                       (0x1 << 13)
+#define RT5645_RAMP_MASK                       (0x1 << 12)
+#define RT5645_RAMP_SFT                                12
+#define RT5645_RAMP_DIS                                (0x0 << 12)
+#define RT5645_RAMP_EN                         (0x1 << 12)
+#define RT5645_BPS_MASK                                (0x1 << 11)
+#define RT5645_BPS_SFT                         11
+#define RT5645_BPS_DIS                         (0x0 << 11)
+#define RT5645_BPS_EN                          (0x1 << 11)
+#define RT5645_FAST_UPDN_MASK                  (0x1 << 10)
+#define RT5645_FAST_UPDN_SFT                   10
+#define RT5645_FAST_UPDN_DIS                   (0x0 << 10)
+#define RT5645_FAST_UPDN_EN                    (0x1 << 10)
+#define RT5645_MRES_MASK                       (0x3 << 8)
+#define RT5645_MRES_SFT                                8
+#define RT5645_MRES_15MO                       (0x0 << 8)
+#define RT5645_MRES_25MO                       (0x1 << 8)
+#define RT5645_MRES_35MO                       (0x2 << 8)
+#define RT5645_MRES_45MO                       (0x3 << 8)
+#define RT5645_VLO_MASK                                (0x1 << 7)
+#define RT5645_VLO_SFT                         7
+#define RT5645_VLO_3V                          (0x0 << 7)
+#define RT5645_VLO_32V                         (0x1 << 7)
+#define RT5645_DIG_DP_MASK                     (0x1 << 6)
+#define RT5645_DIG_DP_SFT                      6
+#define RT5645_DIG_DP_DIS                      (0x0 << 6)
+#define RT5645_DIG_DP_EN                       (0x1 << 6)
+#define RT5645_DP_TH_MASK                      (0x3 << 4)
+#define RT5645_DP_TH_SFT                       4
+
+/* Depop Mode Control 3 (0x90) */
+#define RT5645_CP_SYS_MASK                     (0x7 << 12)
+#define RT5645_CP_SYS_SFT                      12
+#define RT5645_CP_FQ1_MASK                     (0x7 << 8)
+#define RT5645_CP_FQ1_SFT                      8
+#define RT5645_CP_FQ2_MASK                     (0x7 << 4)
+#define RT5645_CP_FQ2_SFT                      4
+#define RT5645_CP_FQ3_MASK                     (0x7)
+#define RT5645_CP_FQ3_SFT                      0
+#define RT5645_CP_FQ_1_5_KHZ                   0
+#define RT5645_CP_FQ_3_KHZ                     1
+#define RT5645_CP_FQ_6_KHZ                     2
+#define RT5645_CP_FQ_12_KHZ                    3
+#define RT5645_CP_FQ_24_KHZ                    4
+#define RT5645_CP_FQ_48_KHZ                    5
+#define RT5645_CP_FQ_96_KHZ                    6
+#define RT5645_CP_FQ_192_KHZ                   7
+
+/* PV detection and SPK gain control (0x92) */
+#define RT5645_PVDD_DET_MASK                   (0x1 << 15)
+#define RT5645_PVDD_DET_SFT                    15
+#define RT5645_PVDD_DET_DIS                    (0x0 << 15)
+#define RT5645_PVDD_DET_EN                     (0x1 << 15)
+#define RT5645_SPK_AG_MASK                     (0x1 << 14)
+#define RT5645_SPK_AG_SFT                      14
+#define RT5645_SPK_AG_DIS                      (0x0 << 14)
+#define RT5645_SPK_AG_EN                       (0x1 << 14)
+
+/* Micbias Control (0x93) */
+#define RT5645_MIC1_BS_MASK                    (0x1 << 15)
+#define RT5645_MIC1_BS_SFT                     15
+#define RT5645_MIC1_BS_9AV                     (0x0 << 15)
+#define RT5645_MIC1_BS_75AV                    (0x1 << 15)
+#define RT5645_MIC2_BS_MASK                    (0x1 << 14)
+#define RT5645_MIC2_BS_SFT                     14
+#define RT5645_MIC2_BS_9AV                     (0x0 << 14)
+#define RT5645_MIC2_BS_75AV                    (0x1 << 14)
+#define RT5645_MIC1_CLK_MASK                   (0x1 << 13)
+#define RT5645_MIC1_CLK_SFT                    13
+#define RT5645_MIC1_CLK_DIS                    (0x0 << 13)
+#define RT5645_MIC1_CLK_EN                     (0x1 << 13)
+#define RT5645_MIC2_CLK_MASK                   (0x1 << 12)
+#define RT5645_MIC2_CLK_SFT                    12
+#define RT5645_MIC2_CLK_DIS                    (0x0 << 12)
+#define RT5645_MIC2_CLK_EN                     (0x1 << 12)
+#define RT5645_MIC1_OVCD_MASK                  (0x1 << 11)
+#define RT5645_MIC1_OVCD_SFT                   11
+#define RT5645_MIC1_OVCD_DIS                   (0x0 << 11)
+#define RT5645_MIC1_OVCD_EN                    (0x1 << 11)
+#define RT5645_MIC1_OVTH_MASK                  (0x3 << 9)
+#define RT5645_MIC1_OVTH_SFT                   9
+#define RT5645_MIC1_OVTH_600UA                 (0x0 << 9)
+#define RT5645_MIC1_OVTH_1500UA                        (0x1 << 9)
+#define RT5645_MIC1_OVTH_2000UA                        (0x2 << 9)
+#define RT5645_MIC2_OVCD_MASK                  (0x1 << 8)
+#define RT5645_MIC2_OVCD_SFT                   8
+#define RT5645_MIC2_OVCD_DIS                   (0x0 << 8)
+#define RT5645_MIC2_OVCD_EN                    (0x1 << 8)
+#define RT5645_MIC2_OVTH_MASK                  (0x3 << 6)
+#define RT5645_MIC2_OVTH_SFT                   6
+#define RT5645_MIC2_OVTH_600UA                 (0x0 << 6)
+#define RT5645_MIC2_OVTH_1500UA                        (0x1 << 6)
+#define RT5645_MIC2_OVTH_2000UA                        (0x2 << 6)
+#define RT5645_PWR_MB_MASK                     (0x1 << 5)
+#define RT5645_PWR_MB_SFT                      5
+#define RT5645_PWR_MB_PD                       (0x0 << 5)
+#define RT5645_PWR_MB_PU                       (0x1 << 5)
+#define RT5645_PWR_CLK25M_MASK                 (0x1 << 4)
+#define RT5645_PWR_CLK25M_SFT                  4
+#define RT5645_PWR_CLK25M_PD                   (0x0 << 4)
+#define RT5645_PWR_CLK25M_PU                   (0x1 << 4)
+
+/* VAD Control 4 (0x9d) */
+#define RT5645_VAD_SEL_MASK                    (0x3 << 8)
+#define RT5645_VAD_SEL_SFT                     8
+
+/* EQ Control 1 (0xb0) */
+#define RT5645_EQ_SRC_MASK                     (0x1 << 15)
+#define RT5645_EQ_SRC_SFT                      15
+#define RT5645_EQ_SRC_DAC                      (0x0 << 15)
+#define RT5645_EQ_SRC_ADC                      (0x1 << 15)
+#define RT5645_EQ_UPD                          (0x1 << 14)
+#define RT5645_EQ_UPD_BIT                      14
+#define RT5645_EQ_CD_MASK                      (0x1 << 13)
+#define RT5645_EQ_CD_SFT                       13
+#define RT5645_EQ_CD_DIS                       (0x0 << 13)
+#define RT5645_EQ_CD_EN                                (0x1 << 13)
+#define RT5645_EQ_DITH_MASK                    (0x3 << 8)
+#define RT5645_EQ_DITH_SFT                     8
+#define RT5645_EQ_DITH_NOR                     (0x0 << 8)
+#define RT5645_EQ_DITH_LSB                     (0x1 << 8)
+#define RT5645_EQ_DITH_LSB_1                   (0x2 << 8)
+#define RT5645_EQ_DITH_LSB_2                   (0x3 << 8)
+
+/* EQ Control 2 (0xb1) */
+#define RT5645_EQ_HPF1_M_MASK                  (0x1 << 8)
+#define RT5645_EQ_HPF1_M_SFT                   8
+#define RT5645_EQ_HPF1_M_HI                    (0x0 << 8)
+#define RT5645_EQ_HPF1_M_1ST                   (0x1 << 8)
+#define RT5645_EQ_LPF1_M_MASK                  (0x1 << 7)
+#define RT5645_EQ_LPF1_M_SFT                   7
+#define RT5645_EQ_LPF1_M_LO                    (0x0 << 7)
+#define RT5645_EQ_LPF1_M_1ST                   (0x1 << 7)
+#define RT5645_EQ_HPF2_MASK                    (0x1 << 6)
+#define RT5645_EQ_HPF2_SFT                     6
+#define RT5645_EQ_HPF2_DIS                     (0x0 << 6)
+#define RT5645_EQ_HPF2_EN                      (0x1 << 6)
+#define RT5645_EQ_HPF1_MASK                    (0x1 << 5)
+#define RT5645_EQ_HPF1_SFT                     5
+#define RT5645_EQ_HPF1_DIS                     (0x0 << 5)
+#define RT5645_EQ_HPF1_EN                      (0x1 << 5)
+#define RT5645_EQ_BPF4_MASK                    (0x1 << 4)
+#define RT5645_EQ_BPF4_SFT                     4
+#define RT5645_EQ_BPF4_DIS                     (0x0 << 4)
+#define RT5645_EQ_BPF4_EN                      (0x1 << 4)
+#define RT5645_EQ_BPF3_MASK                    (0x1 << 3)
+#define RT5645_EQ_BPF3_SFT                     3
+#define RT5645_EQ_BPF3_DIS                     (0x0 << 3)
+#define RT5645_EQ_BPF3_EN                      (0x1 << 3)
+#define RT5645_EQ_BPF2_MASK                    (0x1 << 2)
+#define RT5645_EQ_BPF2_SFT                     2
+#define RT5645_EQ_BPF2_DIS                     (0x0 << 2)
+#define RT5645_EQ_BPF2_EN                      (0x1 << 2)
+#define RT5645_EQ_BPF1_MASK                    (0x1 << 1)
+#define RT5645_EQ_BPF1_SFT                     1
+#define RT5645_EQ_BPF1_DIS                     (0x0 << 1)
+#define RT5645_EQ_BPF1_EN                      (0x1 << 1)
+#define RT5645_EQ_LPF_MASK                     (0x1)
+#define RT5645_EQ_LPF_SFT                      0
+#define RT5645_EQ_LPF_DIS                      (0x0)
+#define RT5645_EQ_LPF_EN                       (0x1)
+#define RT5645_EQ_CTRL_MASK                    (0x7f)
+
+/* Memory Test (0xb2) */
+#define RT5645_MT_MASK                         (0x1 << 15)
+#define RT5645_MT_SFT                          15
+#define RT5645_MT_DIS                          (0x0 << 15)
+#define RT5645_MT_EN                           (0x1 << 15)
+
+/* DRC/AGC Control 1 (0xb4) */
+#define RT5645_DRC_AGC_P_MASK                  (0x1 << 15)
+#define RT5645_DRC_AGC_P_SFT                   15
+#define RT5645_DRC_AGC_P_DAC                   (0x0 << 15)
+#define RT5645_DRC_AGC_P_ADC                   (0x1 << 15)
+#define RT5645_DRC_AGC_MASK                    (0x1 << 14)
+#define RT5645_DRC_AGC_SFT                     14
+#define RT5645_DRC_AGC_DIS                     (0x0 << 14)
+#define RT5645_DRC_AGC_EN                      (0x1 << 14)
+#define RT5645_DRC_AGC_UPD                     (0x1 << 13)
+#define RT5645_DRC_AGC_UPD_BIT                 13
+#define RT5645_DRC_AGC_AR_MASK                 (0x1f << 8)
+#define RT5645_DRC_AGC_AR_SFT                  8
+#define RT5645_DRC_AGC_R_MASK                  (0x7 << 5)
+#define RT5645_DRC_AGC_R_SFT                   5
+#define RT5645_DRC_AGC_R_48K                   (0x1 << 5)
+#define RT5645_DRC_AGC_R_96K                   (0x2 << 5)
+#define RT5645_DRC_AGC_R_192K                  (0x3 << 5)
+#define RT5645_DRC_AGC_R_441K                  (0x5 << 5)
+#define RT5645_DRC_AGC_R_882K                  (0x6 << 5)
+#define RT5645_DRC_AGC_R_1764K                 (0x7 << 5)
+#define RT5645_DRC_AGC_RC_MASK                 (0x1f)
+#define RT5645_DRC_AGC_RC_SFT                  0
+
+/* DRC/AGC Control 2 (0xb5) */
+#define RT5645_DRC_AGC_POB_MASK                        (0x3f << 8)
+#define RT5645_DRC_AGC_POB_SFT                 8
+#define RT5645_DRC_AGC_CP_MASK                 (0x1 << 7)
+#define RT5645_DRC_AGC_CP_SFT                  7
+#define RT5645_DRC_AGC_CP_DIS                  (0x0 << 7)
+#define RT5645_DRC_AGC_CP_EN                   (0x1 << 7)
+#define RT5645_DRC_AGC_CPR_MASK                        (0x3 << 5)
+#define RT5645_DRC_AGC_CPR_SFT                 5
+#define RT5645_DRC_AGC_CPR_1_1                 (0x0 << 5)
+#define RT5645_DRC_AGC_CPR_1_2                 (0x1 << 5)
+#define RT5645_DRC_AGC_CPR_1_3                 (0x2 << 5)
+#define RT5645_DRC_AGC_CPR_1_4                 (0x3 << 5)
+#define RT5645_DRC_AGC_PRB_MASK                        (0x1f)
+#define RT5645_DRC_AGC_PRB_SFT                 0
+
+/* DRC/AGC Control 3 (0xb6) */
+#define RT5645_DRC_AGC_NGB_MASK                        (0xf << 12)
+#define RT5645_DRC_AGC_NGB_SFT                 12
+#define RT5645_DRC_AGC_TAR_MASK                        (0x1f << 7)
+#define RT5645_DRC_AGC_TAR_SFT                 7
+#define RT5645_DRC_AGC_NG_MASK                 (0x1 << 6)
+#define RT5645_DRC_AGC_NG_SFT                  6
+#define RT5645_DRC_AGC_NG_DIS                  (0x0 << 6)
+#define RT5645_DRC_AGC_NG_EN                   (0x1 << 6)
+#define RT5645_DRC_AGC_NGH_MASK                        (0x1 << 5)
+#define RT5645_DRC_AGC_NGH_SFT                 5
+#define RT5645_DRC_AGC_NGH_DIS                 (0x0 << 5)
+#define RT5645_DRC_AGC_NGH_EN                  (0x1 << 5)
+#define RT5645_DRC_AGC_NGT_MASK                        (0x1f)
+#define RT5645_DRC_AGC_NGT_SFT                 0
+
+/* ANC Control 1 (0xb8) */
+#define RT5645_ANC_M_MASK                      (0x1 << 15)
+#define RT5645_ANC_M_SFT                       15
+#define RT5645_ANC_M_NOR                       (0x0 << 15)
+#define RT5645_ANC_M_REV                       (0x1 << 15)
+#define RT5645_ANC_MASK                                (0x1 << 14)
+#define RT5645_ANC_SFT                         14
+#define RT5645_ANC_DIS                         (0x0 << 14)
+#define RT5645_ANC_EN                          (0x1 << 14)
+#define RT5645_ANC_MD_MASK                     (0x3 << 12)
+#define RT5645_ANC_MD_SFT                      12
+#define RT5645_ANC_MD_DIS                      (0x0 << 12)
+#define RT5645_ANC_MD_67MS                     (0x1 << 12)
+#define RT5645_ANC_MD_267MS                    (0x2 << 12)
+#define RT5645_ANC_MD_1067MS                   (0x3 << 12)
+#define RT5645_ANC_SN_MASK                     (0x1 << 11)
+#define RT5645_ANC_SN_SFT                      11
+#define RT5645_ANC_SN_DIS                      (0x0 << 11)
+#define RT5645_ANC_SN_EN                       (0x1 << 11)
+#define RT5645_ANC_CLK_MASK                    (0x1 << 10)
+#define RT5645_ANC_CLK_SFT                     10
+#define RT5645_ANC_CLK_ANC                     (0x0 << 10)
+#define RT5645_ANC_CLK_REG                     (0x1 << 10)
+#define RT5645_ANC_ZCD_MASK                    (0x3 << 8)
+#define RT5645_ANC_ZCD_SFT                     8
+#define RT5645_ANC_ZCD_DIS                     (0x0 << 8)
+#define RT5645_ANC_ZCD_T1                      (0x1 << 8)
+#define RT5645_ANC_ZCD_T2                      (0x2 << 8)
+#define RT5645_ANC_ZCD_WT                      (0x3 << 8)
+#define RT5645_ANC_CS_MASK                     (0x1 << 7)
+#define RT5645_ANC_CS_SFT                      7
+#define RT5645_ANC_CS_DIS                      (0x0 << 7)
+#define RT5645_ANC_CS_EN                       (0x1 << 7)
+#define RT5645_ANC_SW_MASK                     (0x1 << 6)
+#define RT5645_ANC_SW_SFT                      6
+#define RT5645_ANC_SW_NOR                      (0x0 << 6)
+#define RT5645_ANC_SW_AUTO                     (0x1 << 6)
+#define RT5645_ANC_CO_L_MASK                   (0x3f)
+#define RT5645_ANC_CO_L_SFT                    0
+
+/* ANC Control 2 (0xb6) */
+#define RT5645_ANC_FG_R_MASK                   (0xf << 12)
+#define RT5645_ANC_FG_R_SFT                    12
+#define RT5645_ANC_FG_L_MASK                   (0xf << 8)
+#define RT5645_ANC_FG_L_SFT                    8
+#define RT5645_ANC_CG_R_MASK                   (0xf << 4)
+#define RT5645_ANC_CG_R_SFT                    4
+#define RT5645_ANC_CG_L_MASK                   (0xf)
+#define RT5645_ANC_CG_L_SFT                    0
+
+/* ANC Control 3 (0xb6) */
+#define RT5645_ANC_CD_MASK                     (0x1 << 6)
+#define RT5645_ANC_CD_SFT                      6
+#define RT5645_ANC_CD_BOTH                     (0x0 << 6)
+#define RT5645_ANC_CD_IND                      (0x1 << 6)
+#define RT5645_ANC_CO_R_MASK                   (0x3f)
+#define RT5645_ANC_CO_R_SFT                    0
+
+/* Jack Detect Control (0xbb) */
+#define RT5645_JD_MASK                         (0x7 << 13)
+#define RT5645_JD_SFT                          13
+#define RT5645_JD_DIS                          (0x0 << 13)
+#define RT5645_JD_GPIO1                                (0x1 << 13)
+#define RT5645_JD_JD1_IN4P                     (0x2 << 13)
+#define RT5645_JD_JD2_IN4N                     (0x3 << 13)
+#define RT5645_JD_GPIO2                                (0x4 << 13)
+#define RT5645_JD_GPIO3                                (0x5 << 13)
+#define RT5645_JD_GPIO4                                (0x6 << 13)
+#define RT5645_JD_HP_MASK                      (0x1 << 11)
+#define RT5645_JD_HP_SFT                       11
+#define RT5645_JD_HP_DIS                       (0x0 << 11)
+#define RT5645_JD_HP_EN                                (0x1 << 11)
+#define RT5645_JD_HP_TRG_MASK                  (0x1 << 10)
+#define RT5645_JD_HP_TRG_SFT                   10
+#define RT5645_JD_HP_TRG_LO                    (0x0 << 10)
+#define RT5645_JD_HP_TRG_HI                    (0x1 << 10)
+#define RT5645_JD_SPL_MASK                     (0x1 << 9)
+#define RT5645_JD_SPL_SFT                      9
+#define RT5645_JD_SPL_DIS                      (0x0 << 9)
+#define RT5645_JD_SPL_EN                       (0x1 << 9)
+#define RT5645_JD_SPL_TRG_MASK                 (0x1 << 8)
+#define RT5645_JD_SPL_TRG_SFT                  8
+#define RT5645_JD_SPL_TRG_LO                   (0x0 << 8)
+#define RT5645_JD_SPL_TRG_HI                   (0x1 << 8)
+#define RT5645_JD_SPR_MASK                     (0x1 << 7)
+#define RT5645_JD_SPR_SFT                      7
+#define RT5645_JD_SPR_DIS                      (0x0 << 7)
+#define RT5645_JD_SPR_EN                       (0x1 << 7)
+#define RT5645_JD_SPR_TRG_MASK                 (0x1 << 6)
+#define RT5645_JD_SPR_TRG_SFT                  6
+#define RT5645_JD_SPR_TRG_LO                   (0x0 << 6)
+#define RT5645_JD_SPR_TRG_HI                   (0x1 << 6)
+#define RT5645_JD_MO_MASK                      (0x1 << 5)
+#define RT5645_JD_MO_SFT                       5
+#define RT5645_JD_MO_DIS                       (0x0 << 5)
+#define RT5645_JD_MO_EN                                (0x1 << 5)
+#define RT5645_JD_MO_TRG_MASK                  (0x1 << 4)
+#define RT5645_JD_MO_TRG_SFT                   4
+#define RT5645_JD_MO_TRG_LO                    (0x0 << 4)
+#define RT5645_JD_MO_TRG_HI                    (0x1 << 4)
+#define RT5645_JD_LO_MASK                      (0x1 << 3)
+#define RT5645_JD_LO_SFT                       3
+#define RT5645_JD_LO_DIS                       (0x0 << 3)
+#define RT5645_JD_LO_EN                                (0x1 << 3)
+#define RT5645_JD_LO_TRG_MASK                  (0x1 << 2)
+#define RT5645_JD_LO_TRG_SFT                   2
+#define RT5645_JD_LO_TRG_LO                    (0x0 << 2)
+#define RT5645_JD_LO_TRG_HI                    (0x1 << 2)
+#define RT5645_JD1_IN4P_MASK                   (0x1 << 1)
+#define RT5645_JD1_IN4P_SFT                    1
+#define RT5645_JD1_IN4P_DIS                    (0x0 << 1)
+#define RT5645_JD1_IN4P_EN                     (0x1 << 1)
+#define RT5645_JD2_IN4N_MASK                   (0x1)
+#define RT5645_JD2_IN4N_SFT                    0
+#define RT5645_JD2_IN4N_DIS                    (0x0)
+#define RT5645_JD2_IN4N_EN                     (0x1)
+
+/* Jack detect for ANC (0xbc) */
+#define RT5645_ANC_DET_MASK                    (0x3 << 4)
+#define RT5645_ANC_DET_SFT                     4
+#define RT5645_ANC_DET_DIS                     (0x0 << 4)
+#define RT5645_ANC_DET_MB1                     (0x1 << 4)
+#define RT5645_ANC_DET_MB2                     (0x2 << 4)
+#define RT5645_ANC_DET_JD                      (0x3 << 4)
+#define RT5645_AD_TRG_MASK                     (0x1 << 3)
+#define RT5645_AD_TRG_SFT                      3
+#define RT5645_AD_TRG_LO                       (0x0 << 3)
+#define RT5645_AD_TRG_HI                       (0x1 << 3)
+#define RT5645_ANCM_DET_MASK                   (0x3 << 4)
+#define RT5645_ANCM_DET_SFT                    4
+#define RT5645_ANCM_DET_DIS                    (0x0 << 4)
+#define RT5645_ANCM_DET_MB1                    (0x1 << 4)
+#define RT5645_ANCM_DET_MB2                    (0x2 << 4)
+#define RT5645_ANCM_DET_JD                     (0x3 << 4)
+#define RT5645_AMD_TRG_MASK                    (0x1 << 3)
+#define RT5645_AMD_TRG_SFT                     3
+#define RT5645_AMD_TRG_LO                      (0x0 << 3)
+#define RT5645_AMD_TRG_HI                      (0x1 << 3)
+
+/* IRQ Control 1 (0xbd) */
+#define RT5645_IRQ_JD_MASK                     (0x1 << 15)
+#define RT5645_IRQ_JD_SFT                      15
+#define RT5645_IRQ_JD_BP                       (0x0 << 15)
+#define RT5645_IRQ_JD_NOR                      (0x1 << 15)
+#define RT5645_IRQ_OT_MASK                     (0x1 << 14)
+#define RT5645_IRQ_OT_SFT                      14
+#define RT5645_IRQ_OT_BP                       (0x0 << 14)
+#define RT5645_IRQ_OT_NOR                      (0x1 << 14)
+#define RT5645_JD_STKY_MASK                    (0x1 << 13)
+#define RT5645_JD_STKY_SFT                     13
+#define RT5645_JD_STKY_DIS                     (0x0 << 13)
+#define RT5645_JD_STKY_EN                      (0x1 << 13)
+#define RT5645_OT_STKY_MASK                    (0x1 << 12)
+#define RT5645_OT_STKY_SFT                     12
+#define RT5645_OT_STKY_DIS                     (0x0 << 12)
+#define RT5645_OT_STKY_EN                      (0x1 << 12)
+#define RT5645_JD_P_MASK                       (0x1 << 11)
+#define RT5645_JD_P_SFT                                11
+#define RT5645_JD_P_NOR                                (0x0 << 11)
+#define RT5645_JD_P_INV                                (0x1 << 11)
+#define RT5645_OT_P_MASK                       (0x1 << 10)
+#define RT5645_OT_P_SFT                                10
+#define RT5645_OT_P_NOR                                (0x0 << 10)
+#define RT5645_OT_P_INV                                (0x1 << 10)
+
+/* IRQ Control 2 (0xbe) */
+#define RT5645_IRQ_MB1_OC_MASK                 (0x1 << 15)
+#define RT5645_IRQ_MB1_OC_SFT                  15
+#define RT5645_IRQ_MB1_OC_BP                   (0x0 << 15)
+#define RT5645_IRQ_MB1_OC_NOR                  (0x1 << 15)
+#define RT5645_IRQ_MB2_OC_MASK                 (0x1 << 14)
+#define RT5645_IRQ_MB2_OC_SFT                  14
+#define RT5645_IRQ_MB2_OC_BP                   (0x0 << 14)
+#define RT5645_IRQ_MB2_OC_NOR                  (0x1 << 14)
+#define RT5645_MB1_OC_STKY_MASK                        (0x1 << 13)
+#define RT5645_MB1_OC_STKY_SFT                 13
+#define RT5645_MB1_OC_STKY_DIS                 (0x0 << 13)
+#define RT5645_MB1_OC_STKY_EN                  (0x1 << 13)
+#define RT5645_MB2_OC_STKY_MASK                        (0x1 << 12)
+#define RT5645_MB2_OC_STKY_SFT                 12
+#define RT5645_MB2_OC_STKY_DIS                 (0x0 << 12)
+#define RT5645_MB2_OC_STKY_EN                  (0x1 << 12)
+#define RT5645_MB1_OC_P_MASK                   (0x1 << 7)
+#define RT5645_MB1_OC_P_SFT                    7
+#define RT5645_MB1_OC_P_NOR                    (0x0 << 7)
+#define RT5645_MB1_OC_P_INV                    (0x1 << 7)
+#define RT5645_MB2_OC_P_MASK                   (0x1 << 6)
+#define RT5645_MB2_OC_P_SFT                    6
+#define RT5645_MB2_OC_P_NOR                    (0x0 << 6)
+#define RT5645_MB2_OC_P_INV                    (0x1 << 6)
+#define RT5645_MB1_OC_CLR                      (0x1 << 3)
+#define RT5645_MB1_OC_CLR_SFT                  3
+#define RT5645_MB2_OC_CLR                      (0x1 << 2)
+#define RT5645_MB2_OC_CLR_SFT                  2
+
+/* GPIO Control 1 (0xc0) */
+#define RT5645_GP1_PIN_MASK                    (0x1 << 15)
+#define RT5645_GP1_PIN_SFT                     15
+#define RT5645_GP1_PIN_GPIO1                   (0x0 << 15)
+#define RT5645_GP1_PIN_IRQ                     (0x1 << 15)
+#define RT5645_GP2_PIN_MASK                    (0x1 << 14)
+#define RT5645_GP2_PIN_SFT                     14
+#define RT5645_GP2_PIN_GPIO2                   (0x0 << 14)
+#define RT5645_GP2_PIN_DMIC1_SCL               (0x1 << 14)
+#define RT5645_GP3_PIN_MASK                    (0x3 << 12)
+#define RT5645_GP3_PIN_SFT                     12
+#define RT5645_GP3_PIN_GPIO3                   (0x0 << 12)
+#define RT5645_GP3_PIN_DMIC1_SDA               (0x1 << 12)
+#define RT5645_GP3_PIN_IRQ                     (0x2 << 12)
+#define RT5645_GP4_PIN_MASK                    (0x1 << 11)
+#define RT5645_GP4_PIN_SFT                     11
+#define RT5645_GP4_PIN_GPIO4                   (0x0 << 11)
+#define RT5645_GP4_PIN_DMIC2_SDA               (0x1 << 11)
+#define RT5645_DP_SIG_MASK                     (0x1 << 10)
+#define RT5645_DP_SIG_SFT                      10
+#define RT5645_DP_SIG_TEST                     (0x0 << 10)
+#define RT5645_DP_SIG_AP                       (0x1 << 10)
+#define RT5645_GPIO_M_MASK                     (0x1 << 9)
+#define RT5645_GPIO_M_SFT                      9
+#define RT5645_GPIO_M_FLT                      (0x0 << 9)
+#define RT5645_GPIO_M_PH                       (0x1 << 9)
+#define RT5645_I2S2_SEL                                (0x1 << 8)
+#define RT5645_I2S2_SEL_SFT                    8
+#define RT5645_GP5_PIN_MASK                    (0x1 << 7)
+#define RT5645_GP5_PIN_SFT                     7
+#define RT5645_GP5_PIN_GPIO5                   (0x0 << 7)
+#define RT5645_GP5_PIN_DMIC1_SDA               (0x1 << 7)
+#define RT5645_GP6_PIN_MASK                    (0x1 << 6)
+#define RT5645_GP6_PIN_SFT                     6
+#define RT5645_GP6_PIN_GPIO6                   (0x0 << 6)
+#define RT5645_GP6_PIN_DMIC2_SDA               (0x1 << 6)
+#define RT5645_GP8_PIN_MASK                    (0x1 << 3)
+#define RT5645_GP8_PIN_SFT                     3
+#define RT5645_GP8_PIN_GPIO8                   (0x0 << 3)
+#define RT5645_GP8_PIN_DMIC2_SDA               (0x1 << 3)
+#define RT5645_GP12_PIN_MASK                   (0x1 << 2)
+#define RT5645_GP12_PIN_SFT                    2
+#define RT5645_GP12_PIN_GPIO12                 (0x0 << 2)
+#define RT5645_GP12_PIN_DMIC2_SDA              (0x1 << 2)
+#define RT5645_GP11_PIN_MASK                   (0x1 << 1)
+#define RT5645_GP11_PIN_SFT                    1
+#define RT5645_GP11_PIN_GPIO11                 (0x0 << 1)
+#define RT5645_GP11_PIN_DMIC1_SDA              (0x1 << 1)
+#define RT5645_GP10_PIN_MASK                   (0x1)
+#define RT5645_GP10_PIN_SFT                    0
+#define RT5645_GP10_PIN_GPIO10                 (0x0)
+#define RT5645_GP10_PIN_DMIC2_SDA              (0x1)
+
+/* GPIO Control 3 (0xc2) */
+#define RT5645_GP4_PF_MASK                     (0x1 << 11)
+#define RT5645_GP4_PF_SFT                      11
+#define RT5645_GP4_PF_IN                       (0x0 << 11)
+#define RT5645_GP4_PF_OUT                      (0x1 << 11)
+#define RT5645_GP4_OUT_MASK                    (0x1 << 10)
+#define RT5645_GP4_OUT_SFT                     10
+#define RT5645_GP4_OUT_LO                      (0x0 << 10)
+#define RT5645_GP4_OUT_HI                      (0x1 << 10)
+#define RT5645_GP4_P_MASK                      (0x1 << 9)
+#define RT5645_GP4_P_SFT                       9
+#define RT5645_GP4_P_NOR                       (0x0 << 9)
+#define RT5645_GP4_P_INV                       (0x1 << 9)
+#define RT5645_GP3_PF_MASK                     (0x1 << 8)
+#define RT5645_GP3_PF_SFT                      8
+#define RT5645_GP3_PF_IN                       (0x0 << 8)
+#define RT5645_GP3_PF_OUT                      (0x1 << 8)
+#define RT5645_GP3_OUT_MASK                    (0x1 << 7)
+#define RT5645_GP3_OUT_SFT                     7
+#define RT5645_GP3_OUT_LO                      (0x0 << 7)
+#define RT5645_GP3_OUT_HI                      (0x1 << 7)
+#define RT5645_GP3_P_MASK                      (0x1 << 6)
+#define RT5645_GP3_P_SFT                       6
+#define RT5645_GP3_P_NOR                       (0x0 << 6)
+#define RT5645_GP3_P_INV                       (0x1 << 6)
+#define RT5645_GP2_PF_MASK                     (0x1 << 5)
+#define RT5645_GP2_PF_SFT                      5
+#define RT5645_GP2_PF_IN                       (0x0 << 5)
+#define RT5645_GP2_PF_OUT                      (0x1 << 5)
+#define RT5645_GP2_OUT_MASK                    (0x1 << 4)
+#define RT5645_GP2_OUT_SFT                     4
+#define RT5645_GP2_OUT_LO                      (0x0 << 4)
+#define RT5645_GP2_OUT_HI                      (0x1 << 4)
+#define RT5645_GP2_P_MASK                      (0x1 << 3)
+#define RT5645_GP2_P_SFT                       3
+#define RT5645_GP2_P_NOR                       (0x0 << 3)
+#define RT5645_GP2_P_INV                       (0x1 << 3)
+#define RT5645_GP1_PF_MASK                     (0x1 << 2)
+#define RT5645_GP1_PF_SFT                      2
+#define RT5645_GP1_PF_IN                       (0x0 << 2)
+#define RT5645_GP1_PF_OUT                      (0x1 << 2)
+#define RT5645_GP1_OUT_MASK                    (0x1 << 1)
+#define RT5645_GP1_OUT_SFT                     1
+#define RT5645_GP1_OUT_LO                      (0x0 << 1)
+#define RT5645_GP1_OUT_HI                      (0x1 << 1)
+#define RT5645_GP1_P_MASK                      (0x1)
+#define RT5645_GP1_P_SFT                       0
+#define RT5645_GP1_P_NOR                       (0x0)
+#define RT5645_GP1_P_INV                       (0x1)
+
+/* Programmable Register Array Control 1 (0xc8) */
+#define RT5645_REG_SEQ_MASK                    (0xf << 12)
+#define RT5645_REG_SEQ_SFT                     12
+#define RT5645_SEQ1_ST_MASK                    (0x1 << 11) /*RO*/
+#define RT5645_SEQ1_ST_SFT                     11
+#define RT5645_SEQ1_ST_RUN                     (0x0 << 11)
+#define RT5645_SEQ1_ST_FIN                     (0x1 << 11)
+#define RT5645_SEQ2_ST_MASK                    (0x1 << 10) /*RO*/
+#define RT5645_SEQ2_ST_SFT                     10
+#define RT5645_SEQ2_ST_RUN                     (0x0 << 10)
+#define RT5645_SEQ2_ST_FIN                     (0x1 << 10)
+#define RT5645_REG_LV_MASK                     (0x1 << 9)
+#define RT5645_REG_LV_SFT                      9
+#define RT5645_REG_LV_MX                       (0x0 << 9)
+#define RT5645_REG_LV_PR                       (0x1 << 9)
+#define RT5645_SEQ_2_PT_MASK                   (0x1 << 8)
+#define RT5645_SEQ_2_PT_BIT                    8
+#define RT5645_REG_IDX_MASK                    (0xff)
+#define RT5645_REG_IDX_SFT                     0
+
+/* Programmable Register Array Control 2 (0xc9) */
+#define RT5645_REG_DAT_MASK                    (0xffff)
+#define RT5645_REG_DAT_SFT                     0
+
+/* Programmable Register Array Control 3 (0xca) */
+#define RT5645_SEQ_DLY_MASK                    (0xff << 8)
+#define RT5645_SEQ_DLY_SFT                     8
+#define RT5645_PROG_MASK                       (0x1 << 7)
+#define RT5645_PROG_SFT                                7
+#define RT5645_PROG_DIS                                (0x0 << 7)
+#define RT5645_PROG_EN                         (0x1 << 7)
+#define RT5645_SEQ1_PT_RUN                     (0x1 << 6)
+#define RT5645_SEQ1_PT_RUN_BIT                 6
+#define RT5645_SEQ2_PT_RUN                     (0x1 << 5)
+#define RT5645_SEQ2_PT_RUN_BIT                 5
+
+/* Programmable Register Array Control 4 (0xcb) */
+#define RT5645_SEQ1_START_MASK                 (0xf << 8)
+#define RT5645_SEQ1_START_SFT                  8
+#define RT5645_SEQ1_END_MASK                   (0xf)
+#define RT5645_SEQ1_END_SFT                    0
+
+/* Programmable Register Array Control 5 (0xcc) */
+#define RT5645_SEQ2_START_MASK                 (0xf << 8)
+#define RT5645_SEQ2_START_SFT                  8
+#define RT5645_SEQ2_END_MASK                   (0xf)
+#define RT5645_SEQ2_END_SFT                    0
+
+/* Scramble Function (0xcd) */
+#define RT5645_SCB_KEY_MASK                    (0xff)
+#define RT5645_SCB_KEY_SFT                     0
+
+/* Scramble Control (0xce) */
+#define RT5645_SCB_SWAP_MASK                   (0x1 << 15)
+#define RT5645_SCB_SWAP_SFT                    15
+#define RT5645_SCB_SWAP_DIS                    (0x0 << 15)
+#define RT5645_SCB_SWAP_EN                     (0x1 << 15)
+#define RT5645_SCB_MASK                                (0x1 << 14)
+#define RT5645_SCB_SFT                         14
+#define RT5645_SCB_DIS                         (0x0 << 14)
+#define RT5645_SCB_EN                          (0x1 << 14)
+
+/* Baseback Control (0xcf) */
+#define RT5645_BB_MASK                         (0x1 << 15)
+#define RT5645_BB_SFT                          15
+#define RT5645_BB_DIS                          (0x0 << 15)
+#define RT5645_BB_EN                           (0x1 << 15)
+#define RT5645_BB_CT_MASK                      (0x7 << 12)
+#define RT5645_BB_CT_SFT                       12
+#define RT5645_BB_CT_A                         (0x0 << 12)
+#define RT5645_BB_CT_B                         (0x1 << 12)
+#define RT5645_BB_CT_C                         (0x2 << 12)
+#define RT5645_BB_CT_D                         (0x3 << 12)
+#define RT5645_M_BB_L_MASK                     (0x1 << 9)
+#define RT5645_M_BB_L_SFT                      9
+#define RT5645_M_BB_R_MASK                     (0x1 << 8)
+#define RT5645_M_BB_R_SFT                      8
+#define RT5645_M_BB_HPF_L_MASK                 (0x1 << 7)
+#define RT5645_M_BB_HPF_L_SFT                  7
+#define RT5645_M_BB_HPF_R_MASK                 (0x1 << 6)
+#define RT5645_M_BB_HPF_R_SFT                  6
+#define RT5645_G_BB_BST_MASK                   (0x3f)
+#define RT5645_G_BB_BST_SFT                    0
+
+/* MP3 Plus Control 1 (0xd0) */
+#define RT5645_M_MP3_L_MASK                    (0x1 << 15)
+#define RT5645_M_MP3_L_SFT                     15
+#define RT5645_M_MP3_R_MASK                    (0x1 << 14)
+#define RT5645_M_MP3_R_SFT                     14
+#define RT5645_M_MP3_MASK                      (0x1 << 13)
+#define RT5645_M_MP3_SFT                       13
+#define RT5645_M_MP3_DIS                       (0x0 << 13)
+#define RT5645_M_MP3_EN                                (0x1 << 13)
+#define RT5645_EG_MP3_MASK                     (0x1f << 8)
+#define RT5645_EG_MP3_SFT                      8
+#define RT5645_MP3_HLP_MASK                    (0x1 << 7)
+#define RT5645_MP3_HLP_SFT                     7
+#define RT5645_MP3_HLP_DIS                     (0x0 << 7)
+#define RT5645_MP3_HLP_EN                      (0x1 << 7)
+#define RT5645_M_MP3_ORG_L_MASK                        (0x1 << 6)
+#define RT5645_M_MP3_ORG_L_SFT                 6
+#define RT5645_M_MP3_ORG_R_MASK                        (0x1 << 5)
+#define RT5645_M_MP3_ORG_R_SFT                 5
+
+/* MP3 Plus Control 2 (0xd1) */
+#define RT5645_MP3_WT_MASK                     (0x1 << 13)
+#define RT5645_MP3_WT_SFT                      13
+#define RT5645_MP3_WT_1_4                      (0x0 << 13)
+#define RT5645_MP3_WT_1_2                      (0x1 << 13)
+#define RT5645_OG_MP3_MASK                     (0x1f << 8)
+#define RT5645_OG_MP3_SFT                      8
+#define RT5645_HG_MP3_MASK                     (0x3f)
+#define RT5645_HG_MP3_SFT                      0
+
+/* 3D HP Control 1 (0xd2) */
+#define RT5645_3D_CF_MASK                      (0x1 << 15)
+#define RT5645_3D_CF_SFT                       15
+#define RT5645_3D_CF_DIS                       (0x0 << 15)
+#define RT5645_3D_CF_EN                                (0x1 << 15)
+#define RT5645_3D_HP_MASK                      (0x1 << 14)
+#define RT5645_3D_HP_SFT                       14
+#define RT5645_3D_HP_DIS                       (0x0 << 14)
+#define RT5645_3D_HP_EN                                (0x1 << 14)
+#define RT5645_3D_BT_MASK                      (0x1 << 13)
+#define RT5645_3D_BT_SFT                       13
+#define RT5645_3D_BT_DIS                       (0x0 << 13)
+#define RT5645_3D_BT_EN                                (0x1 << 13)
+#define RT5645_3D_1F_MIX_MASK                  (0x3 << 11)
+#define RT5645_3D_1F_MIX_SFT                   11
+#define RT5645_3D_HP_M_MASK                    (0x1 << 10)
+#define RT5645_3D_HP_M_SFT                     10
+#define RT5645_3D_HP_M_SUR                     (0x0 << 10)
+#define RT5645_3D_HP_M_FRO                     (0x1 << 10)
+#define RT5645_M_3D_HRTF_MASK                  (0x1 << 9)
+#define RT5645_M_3D_HRTF_SFT                   9
+#define RT5645_M_3D_D2H_MASK                   (0x1 << 8)
+#define RT5645_M_3D_D2H_SFT                    8
+#define RT5645_M_3D_D2R_MASK                   (0x1 << 7)
+#define RT5645_M_3D_D2R_SFT                    7
+#define RT5645_M_3D_REVB_MASK                  (0x1 << 6)
+#define RT5645_M_3D_REVB_SFT                   6
+
+/* Adjustable high pass filter control 1 (0xd3) */
+#define RT5645_2ND_HPF_MASK                    (0x1 << 15)
+#define RT5645_2ND_HPF_SFT                     15
+#define RT5645_2ND_HPF_DIS                     (0x0 << 15)
+#define RT5645_2ND_HPF_EN                      (0x1 << 15)
+#define RT5645_HPF_CF_L_MASK                   (0x7 << 12)
+#define RT5645_HPF_CF_L_SFT                    12
+#define RT5645_1ST_HPF_MASK                    (0x1 << 11)
+#define RT5645_1ST_HPF_SFT                     11
+#define RT5645_1ST_HPF_DIS                     (0x0 << 11)
+#define RT5645_1ST_HPF_EN                      (0x1 << 11)
+#define RT5645_HPF_CF_R_MASK                   (0x7 << 8)
+#define RT5645_HPF_CF_R_SFT                    8
+#define RT5645_ZD_T_MASK                       (0x3 << 6)
+#define RT5645_ZD_T_SFT                                6
+#define RT5645_ZD_F_MASK                       (0x3 << 4)
+#define RT5645_ZD_F_SFT                                4
+#define RT5645_ZD_F_IM                         (0x0 << 4)
+#define RT5645_ZD_F_ZC_IM                      (0x1 << 4)
+#define RT5645_ZD_F_ZC_IOD                     (0x2 << 4)
+#define RT5645_ZD_F_UN                         (0x3 << 4)
+
+/* HP calibration control and Amp detection (0xd6) */
+#define RT5645_SI_DAC_MASK                     (0x1 << 11)
+#define RT5645_SI_DAC_SFT                      11
+#define RT5645_SI_DAC_AUTO                     (0x0 << 11)
+#define RT5645_SI_DAC_TEST                     (0x1 << 11)
+#define RT5645_DC_CAL_M_MASK                   (0x1 << 10)
+#define RT5645_DC_CAL_M_SFT                    10
+#define RT5645_DC_CAL_M_CAL                    (0x0 << 10)
+#define RT5645_DC_CAL_M_NOR                    (0x1 << 10)
+#define RT5645_DC_CAL_MASK                     (0x1 << 9)
+#define RT5645_DC_CAL_SFT                      9
+#define RT5645_DC_CAL_DIS                      (0x0 << 9)
+#define RT5645_DC_CAL_EN                       (0x1 << 9)
+#define RT5645_HPD_RCV_MASK                    (0x7 << 6)
+#define RT5645_HPD_RCV_SFT                     6
+#define RT5645_HPD_PS_MASK                     (0x1 << 5)
+#define RT5645_HPD_PS_SFT                      5
+#define RT5645_HPD_PS_DIS                      (0x0 << 5)
+#define RT5645_HPD_PS_EN                       (0x1 << 5)
+#define RT5645_CAL_M_MASK                      (0x1 << 4)
+#define RT5645_CAL_M_SFT                       4
+#define RT5645_CAL_M_DEP                       (0x0 << 4)
+#define RT5645_CAL_M_CAL                       (0x1 << 4)
+#define RT5645_CAL_MASK                                (0x1 << 3)
+#define RT5645_CAL_SFT                         3
+#define RT5645_CAL_DIS                         (0x0 << 3)
+#define RT5645_CAL_EN                          (0x1 << 3)
+#define RT5645_CAL_TEST_MASK                   (0x1 << 2)
+#define RT5645_CAL_TEST_SFT                    2
+#define RT5645_CAL_TEST_DIS                    (0x0 << 2)
+#define RT5645_CAL_TEST_EN                     (0x1 << 2)
+#define RT5645_CAL_P_MASK                      (0x3)
+#define RT5645_CAL_P_SFT                       0
+#define RT5645_CAL_P_NONE                      (0x0)
+#define RT5645_CAL_P_CAL                       (0x1)
+#define RT5645_CAL_P_DAC_CAL                   (0x2)
+
+/* Soft volume and zero cross control 1 (0xd9) */
+#define RT5645_SV_MASK                         (0x1 << 15)
+#define RT5645_SV_SFT                          15
+#define RT5645_SV_DIS                          (0x0 << 15)
+#define RT5645_SV_EN                           (0x1 << 15)
+#define RT5645_SPO_SV_MASK                     (0x1 << 14)
+#define RT5645_SPO_SV_SFT                      14
+#define RT5645_SPO_SV_DIS                      (0x0 << 14)
+#define RT5645_SPO_SV_EN                       (0x1 << 14)
+#define RT5645_OUT_SV_MASK                     (0x1 << 13)
+#define RT5645_OUT_SV_SFT                      13
+#define RT5645_OUT_SV_DIS                      (0x0 << 13)
+#define RT5645_OUT_SV_EN                       (0x1 << 13)
+#define RT5645_HP_SV_MASK                      (0x1 << 12)
+#define RT5645_HP_SV_SFT                       12
+#define RT5645_HP_SV_DIS                       (0x0 << 12)
+#define RT5645_HP_SV_EN                                (0x1 << 12)
+#define RT5645_ZCD_DIG_MASK                    (0x1 << 11)
+#define RT5645_ZCD_DIG_SFT                     11
+#define RT5645_ZCD_DIG_DIS                     (0x0 << 11)
+#define RT5645_ZCD_DIG_EN                      (0x1 << 11)
+#define RT5645_ZCD_MASK                                (0x1 << 10)
+#define RT5645_ZCD_SFT                         10
+#define RT5645_ZCD_PD                          (0x0 << 10)
+#define RT5645_ZCD_PU                          (0x1 << 10)
+#define RT5645_M_ZCD_MASK                      (0x3f << 4)
+#define RT5645_M_ZCD_SFT                       4
+#define RT5645_M_ZCD_RM_L                      (0x1 << 9)
+#define RT5645_M_ZCD_RM_R                      (0x1 << 8)
+#define RT5645_M_ZCD_SM_L                      (0x1 << 7)
+#define RT5645_M_ZCD_SM_R                      (0x1 << 6)
+#define RT5645_M_ZCD_OM_L                      (0x1 << 5)
+#define RT5645_M_ZCD_OM_R                      (0x1 << 4)
+#define RT5645_SV_DLY_MASK                     (0xf)
+#define RT5645_SV_DLY_SFT                      0
+
+/* Soft volume and zero cross control 2 (0xda) */
+#define RT5645_ZCD_HP_MASK                     (0x1 << 15)
+#define RT5645_ZCD_HP_SFT                      15
+#define RT5645_ZCD_HP_DIS                      (0x0 << 15)
+#define RT5645_ZCD_HP_EN                       (0x1 << 15)
+
+
+/* Codec Private Register definition */
+/* 3D Speaker Control (0x63) */
+#define RT5645_3D_SPK_MASK                     (0x1 << 15)
+#define RT5645_3D_SPK_SFT                      15
+#define RT5645_3D_SPK_DIS                      (0x0 << 15)
+#define RT5645_3D_SPK_EN                       (0x1 << 15)
+#define RT5645_3D_SPK_M_MASK                   (0x3 << 13)
+#define RT5645_3D_SPK_M_SFT                    13
+#define RT5645_3D_SPK_CG_MASK                  (0x1f << 8)
+#define RT5645_3D_SPK_CG_SFT                   8
+#define RT5645_3D_SPK_SG_MASK                  (0x1f)
+#define RT5645_3D_SPK_SG_SFT                   0
+
+/* Wind Noise Detection Control 1 (0x6c) */
+#define RT5645_WND_MASK                                (0x1 << 15)
+#define RT5645_WND_SFT                         15
+#define RT5645_WND_DIS                         (0x0 << 15)
+#define RT5645_WND_EN                          (0x1 << 15)
+
+/* Wind Noise Detection Control 2 (0x6d) */
+#define RT5645_WND_FC_NW_MASK                  (0x3f << 10)
+#define RT5645_WND_FC_NW_SFT                   10
+#define RT5645_WND_FC_WK_MASK                  (0x3f << 4)
+#define RT5645_WND_FC_WK_SFT                   4
+
+/* Wind Noise Detection Control 3 (0x6e) */
+#define RT5645_HPF_FC_MASK                     (0x3f << 6)
+#define RT5645_HPF_FC_SFT                      6
+#define RT5645_WND_FC_ST_MASK                  (0x3f)
+#define RT5645_WND_FC_ST_SFT                   0
+
+/* Wind Noise Detection Control 4 (0x6f) */
+#define RT5645_WND_TH_LO_MASK                  (0x3ff)
+#define RT5645_WND_TH_LO_SFT                   0
+
+/* Wind Noise Detection Control 5 (0x70) */
+#define RT5645_WND_TH_HI_MASK                  (0x3ff)
+#define RT5645_WND_TH_HI_SFT                   0
+
+/* Wind Noise Detection Control 8 (0x73) */
+#define RT5645_WND_WIND_MASK                   (0x1 << 13) /* Read-Only */
+#define RT5645_WND_WIND_SFT                    13
+#define RT5645_WND_STRONG_MASK                 (0x1 << 12) /* Read-Only */
+#define RT5645_WND_STRONG_SFT                  12
+enum {
+       RT5645_NO_WIND,
+       RT5645_BREEZE,
+       RT5645_STORM,
+};
+
+/* Dipole Speaker Interface (0x75) */
+#define RT5645_DP_ATT_MASK                     (0x3 << 14)
+#define RT5645_DP_ATT_SFT                      14
+#define RT5645_DP_SPK_MASK                     (0x1 << 10)
+#define RT5645_DP_SPK_SFT                      10
+#define RT5645_DP_SPK_DIS                      (0x0 << 10)
+#define RT5645_DP_SPK_EN                       (0x1 << 10)
+
+/* EQ Pre Volume Control (0xb3) */
+#define RT5645_EQ_PRE_VOL_MASK                 (0xffff)
+#define RT5645_EQ_PRE_VOL_SFT                  0
+
+/* EQ Post Volume Control (0xb4) */
+#define RT5645_EQ_PST_VOL_MASK                 (0xffff)
+#define RT5645_EQ_PST_VOL_SFT                  0
+
+/* Jack Detect Control 3 (0xf8) */
+#define RT5645_CMP_MIC_IN_DET_MASK             (0x7 << 12)
+#define RT5645_JD_CBJ_EN                       (0x1 << 7)
+#define RT5645_JD_CBJ_POL                      (0x1 << 6)
+#define RT5645_JD_TRI_CBJ_SEL_MASK             (0x7 << 3)
+#define RT5645_JD_TRI_CBJ_SEL_SFT              (3)
+#define RT5645_JD_TRI_HPO_SEL_MASK             (0x7)
+#define RT5645_JD_TRI_HPO_SEL_SFT              (0)
+#define RT5645_JD_F_GPIO_JD1                   (0x0)
+#define RT5645_JD_F_JD1_1                      (0x1)
+#define RT5645_JD_F_JD1_2                      (0x2)
+#define RT5645_JD_F_JD2                                (0x3)
+#define RT5645_JD_F_JD3                                (0x4)
+#define RT5645_JD_F_GPIO_JD2                   (0x5)
+#define RT5645_JD_F_MX0B_12                    (0x6)
+
+/* Digital Misc Control (0xfa) */
+#define RT5645_RST_DSP                         (0x1 << 13)
+#define RT5645_IF1_ADC1_IN1_SEL                        (0x1 << 12)
+#define RT5645_IF1_ADC1_IN1_SFT                        12
+#define RT5645_IF1_ADC1_IN2_SEL                        (0x1 << 11)
+#define RT5645_IF1_ADC1_IN2_SFT                        11
+#define RT5645_IF1_ADC2_IN1_SEL                        (0x1 << 10)
+#define RT5645_IF1_ADC2_IN1_SFT                        10
+#define RT5645_DIG_GATE_CTRL                   0x1
+
+/* General Control2 (0xfb) */
+#define RT5645_RXDC_SRC_MASK                   (0x1 << 7)
+#define RT5645_RXDC_SRC_STO                    (0x0 << 7)
+#define RT5645_RXDC_SRC_MONO                   (0x1 << 7)
+#define RT5645_RXDC_SRC_SFT                    (7)
+#define RT5645_RXDP2_SEL_MASK                  (0x1 << 3)
+#define RT5645_RXDP2_SEL_IF2                   (0x0 << 3)
+#define RT5645_RXDP2_SEL_ADC                   (0x1 << 3)
+#define RT5645_RXDP2_SEL_SFT                   (3)
+
+
+/* Vendor ID (0xfd) */
+#define RT5645_VER_C                           0x2
+#define RT5645_VER_D                           0x3
+
+
+/* Volume Rescale */
+#define RT5645_VOL_RSCL_MAX 0x27
+#define RT5645_VOL_RSCL_RANGE 0x1F
+/* Debug String Length */
+#define RT5645_REG_DISP_LEN 23
+
+
+/* System Clock Source */
+enum {
+       RT5645_SCLK_S_MCLK,
+       RT5645_SCLK_S_PLL1,
+       RT5645_SCLK_S_RCCLK,
+};
+
+/* PLL1 Source */
+enum {
+       RT5645_PLL1_S_MCLK,
+       RT5645_PLL1_S_BCLK1,
+       RT5645_PLL1_S_BCLK2,
+};
+
+enum {
+       RT5645_AIF1,
+       RT5645_AIF2,
+       RT5645_AIFS,
+};
+
+enum {
+       RT5645_DMIC_DATA_IN2P,
+       RT5645_DMIC_DATA_GPIO6,
+       RT5645_DMIC_DATA_GPIO10,
+       RT5645_DMIC_DATA_GPIO12,
+};
+
+enum {
+       RT5645_DMIC_DATA_IN2N,
+       RT5645_DMIC_DATA_GPIO5,
+       RT5645_DMIC_DATA_GPIO11,
+};
+
+struct rt5645_priv {
+       struct snd_soc_codec *codec;
+       struct rt5645_platform_data pdata;
+       struct regmap *regmap;
+
+       int sysclk;
+       int sysclk_src;
+       int lrck[RT5645_AIFS];
+       int bclk[RT5645_AIFS];
+       int master[RT5645_AIFS];
+
+       int pll_src;
+       int pll_in;
+       int pll_out;
+};
+
+#endif /* __RT5645_H__ */
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
new file mode 100644 (file)
index 0000000..ea4b1c6
--- /dev/null
@@ -0,0 +1,1818 @@
+/*
+ * rt5651.c  --  RT5651 ALSA SoC audio codec driver
+ *
+ * Copyright 2014 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.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/regmap.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 "rl6231.h"
+#include "rt5651.h"
+
+#define RT5651_DEVICE_ID_VALUE 0x6281
+
+#define RT5651_PR_RANGE_BASE (0xff + 1)
+#define RT5651_PR_SPACING 0x100
+
+#define RT5651_PR_BASE (RT5651_PR_RANGE_BASE + (0 * RT5651_PR_SPACING))
+
+static const struct regmap_range_cfg rt5651_ranges[] = {
+       { .name = "PR", .range_min = RT5651_PR_BASE,
+         .range_max = RT5651_PR_BASE + 0xb4,
+         .selector_reg = RT5651_PRIV_INDEX,
+         .selector_mask = 0xff,
+         .selector_shift = 0x0,
+         .window_start = RT5651_PRIV_DATA,
+         .window_len = 0x1, },
+};
+
+static struct reg_default init_list[] = {
+       {RT5651_PR_BASE + 0x3d, 0x3e00},
+};
+
+static const struct reg_default rt5651_reg[] = {
+       { 0x00, 0x0000 },
+       { 0x02, 0xc8c8 },
+       { 0x03, 0xc8c8 },
+       { 0x05, 0x0000 },
+       { 0x0d, 0x0000 },
+       { 0x0e, 0x0000 },
+       { 0x0f, 0x0808 },
+       { 0x10, 0x0808 },
+       { 0x19, 0xafaf },
+       { 0x1a, 0xafaf },
+       { 0x1b, 0x0c00 },
+       { 0x1c, 0x2f2f },
+       { 0x1d, 0x2f2f },
+       { 0x1e, 0x0000 },
+       { 0x27, 0x7860 },
+       { 0x28, 0x7070 },
+       { 0x29, 0x8080 },
+       { 0x2a, 0x5252 },
+       { 0x2b, 0x5454 },
+       { 0x2f, 0x0000 },
+       { 0x30, 0x5000 },
+       { 0x3b, 0x0000 },
+       { 0x3c, 0x006f },
+       { 0x3d, 0x0000 },
+       { 0x3e, 0x006f },
+       { 0x45, 0x6000 },
+       { 0x4d, 0x0000 },
+       { 0x4e, 0x0000 },
+       { 0x4f, 0x0279 },
+       { 0x50, 0x0000 },
+       { 0x51, 0x0000 },
+       { 0x52, 0x0279 },
+       { 0x53, 0xf000 },
+       { 0x61, 0x0000 },
+       { 0x62, 0x0000 },
+       { 0x63, 0x00c0 },
+       { 0x64, 0x0000 },
+       { 0x65, 0x0000 },
+       { 0x66, 0x0000 },
+       { 0x70, 0x8000 },
+       { 0x71, 0x8000 },
+       { 0x73, 0x1104 },
+       { 0x74, 0x0c00 },
+       { 0x75, 0x1400 },
+       { 0x77, 0x0c00 },
+       { 0x78, 0x4000 },
+       { 0x79, 0x0123 },
+       { 0x80, 0x0000 },
+       { 0x81, 0x0000 },
+       { 0x82, 0x0000 },
+       { 0x83, 0x0800 },
+       { 0x84, 0x0000 },
+       { 0x85, 0x0008 },
+       { 0x89, 0x0000 },
+       { 0x8e, 0x0004 },
+       { 0x8f, 0x1100 },
+       { 0x90, 0x0000 },
+       { 0x93, 0x2000 },
+       { 0x94, 0x0200 },
+       { 0xb0, 0x2080 },
+       { 0xb1, 0x0000 },
+       { 0xb4, 0x2206 },
+       { 0xb5, 0x1f00 },
+       { 0xb6, 0x0000 },
+       { 0xbb, 0x0000 },
+       { 0xbc, 0x0000 },
+       { 0xbd, 0x0000 },
+       { 0xbe, 0x0000 },
+       { 0xbf, 0x0000 },
+       { 0xc0, 0x0400 },
+       { 0xc1, 0x0000 },
+       { 0xc2, 0x0000 },
+       { 0xcf, 0x0013 },
+       { 0xd0, 0x0680 },
+       { 0xd1, 0x1c17 },
+       { 0xd3, 0xb320 },
+       { 0xd9, 0x0809 },
+       { 0xfa, 0x0010 },
+       { 0xfe, 0x10ec },
+       { 0xff, 0x6281 },
+};
+
+static bool rt5651_volatile_register(struct device *dev,  unsigned int reg)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rt5651_ranges); i++) {
+               if ((reg >= rt5651_ranges[i].window_start &&
+                    reg <= rt5651_ranges[i].window_start +
+                    rt5651_ranges[i].window_len) ||
+                   (reg >= rt5651_ranges[i].range_min &&
+                    reg <= rt5651_ranges[i].range_max)) {
+                       return true;
+               }
+       }
+
+       switch (reg) {
+       case RT5651_RESET:
+       case RT5651_PRIV_DATA:
+       case RT5651_EQ_CTRL1:
+       case RT5651_ALC_1:
+       case RT5651_IRQ_CTRL2:
+       case RT5651_INT_IRQ_ST:
+       case RT5651_PGM_REG_ARR1:
+       case RT5651_PGM_REG_ARR3:
+       case RT5651_VENDOR_ID:
+       case RT5651_DEVICE_ID:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool rt5651_readable_register(struct device *dev, unsigned int reg)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rt5651_ranges); i++) {
+               if ((reg >= rt5651_ranges[i].window_start &&
+                    reg <= rt5651_ranges[i].window_start +
+                    rt5651_ranges[i].window_len) ||
+                   (reg >= rt5651_ranges[i].range_min &&
+                    reg <= rt5651_ranges[i].range_max)) {
+                       return true;
+               }
+       }
+
+       switch (reg) {
+       case RT5651_RESET:
+       case RT5651_VERSION_ID:
+       case RT5651_VENDOR_ID:
+       case RT5651_DEVICE_ID:
+       case RT5651_HP_VOL:
+       case RT5651_LOUT_CTRL1:
+       case RT5651_LOUT_CTRL2:
+       case RT5651_IN1_IN2:
+       case RT5651_IN3:
+       case RT5651_INL1_INR1_VOL:
+       case RT5651_INL2_INR2_VOL:
+       case RT5651_DAC1_DIG_VOL:
+       case RT5651_DAC2_DIG_VOL:
+       case RT5651_DAC2_CTRL:
+       case RT5651_ADC_DIG_VOL:
+       case RT5651_ADC_DATA:
+       case RT5651_ADC_BST_VOL:
+       case RT5651_STO1_ADC_MIXER:
+       case RT5651_STO2_ADC_MIXER:
+       case RT5651_AD_DA_MIXER:
+       case RT5651_STO_DAC_MIXER:
+       case RT5651_DD_MIXER:
+       case RT5651_DIG_INF_DATA:
+       case RT5651_PDM_CTL:
+       case RT5651_REC_L1_MIXER:
+       case RT5651_REC_L2_MIXER:
+       case RT5651_REC_R1_MIXER:
+       case RT5651_REC_R2_MIXER:
+       case RT5651_HPO_MIXER:
+       case RT5651_OUT_L1_MIXER:
+       case RT5651_OUT_L2_MIXER:
+       case RT5651_OUT_L3_MIXER:
+       case RT5651_OUT_R1_MIXER:
+       case RT5651_OUT_R2_MIXER:
+       case RT5651_OUT_R3_MIXER:
+       case RT5651_LOUT_MIXER:
+       case RT5651_PWR_DIG1:
+       case RT5651_PWR_DIG2:
+       case RT5651_PWR_ANLG1:
+       case RT5651_PWR_ANLG2:
+       case RT5651_PWR_MIXER:
+       case RT5651_PWR_VOL:
+       case RT5651_PRIV_INDEX:
+       case RT5651_PRIV_DATA:
+       case RT5651_I2S1_SDP:
+       case RT5651_I2S2_SDP:
+       case RT5651_ADDA_CLK1:
+       case RT5651_ADDA_CLK2:
+       case RT5651_DMIC:
+       case RT5651_TDM_CTL_1:
+       case RT5651_TDM_CTL_2:
+       case RT5651_TDM_CTL_3:
+       case RT5651_GLB_CLK:
+       case RT5651_PLL_CTRL1:
+       case RT5651_PLL_CTRL2:
+       case RT5651_PLL_MODE_1:
+       case RT5651_PLL_MODE_2:
+       case RT5651_PLL_MODE_3:
+       case RT5651_PLL_MODE_4:
+       case RT5651_PLL_MODE_5:
+       case RT5651_PLL_MODE_6:
+       case RT5651_PLL_MODE_7:
+       case RT5651_DEPOP_M1:
+       case RT5651_DEPOP_M2:
+       case RT5651_DEPOP_M3:
+       case RT5651_CHARGE_PUMP:
+       case RT5651_MICBIAS:
+       case RT5651_A_JD_CTL1:
+       case RT5651_EQ_CTRL1:
+       case RT5651_EQ_CTRL2:
+       case RT5651_ALC_1:
+       case RT5651_ALC_2:
+       case RT5651_ALC_3:
+       case RT5651_JD_CTRL1:
+       case RT5651_JD_CTRL2:
+       case RT5651_IRQ_CTRL1:
+       case RT5651_IRQ_CTRL2:
+       case RT5651_INT_IRQ_ST:
+       case RT5651_GPIO_CTRL1:
+       case RT5651_GPIO_CTRL2:
+       case RT5651_GPIO_CTRL3:
+       case RT5651_PGM_REG_ARR1:
+       case RT5651_PGM_REG_ARR2:
+       case RT5651_PGM_REG_ARR3:
+       case RT5651_PGM_REG_ARR4:
+       case RT5651_PGM_REG_ARR5:
+       case RT5651_SCB_FUNC:
+       case RT5651_SCB_CTRL:
+       case RT5651_BASE_BACK:
+       case RT5651_MP3_PLUS1:
+       case RT5651_MP3_PLUS2:
+       case RT5651_ADJ_HPF_CTRL1:
+       case RT5651_ADJ_HPF_CTRL2:
+       case RT5651_HP_CALIB_AMP_DET:
+       case RT5651_HP_CALIB2:
+       case RT5651_SV_ZCD1:
+       case RT5651_SV_ZCD2:
+       case RT5651_D_MISC:
+       case RT5651_DUMMY2:
+       case RT5651_DUMMY3:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+
+/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
+static unsigned int bst_tlv[] = {
+       TLV_DB_RANGE_HEAD(7),
+       0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+       1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+       2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+       3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+       6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+       7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+       8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
+};
+
+/* Interface data select */
+static const char * const rt5651_data_select[] = {
+       "Normal", "Swap", "left copy to right", "right copy to left"};
+
+static SOC_ENUM_SINGLE_DECL(rt5651_if2_dac_enum, RT5651_DIG_INF_DATA,
+                               RT5651_IF2_DAC_SEL_SFT, rt5651_data_select);
+
+static SOC_ENUM_SINGLE_DECL(rt5651_if2_adc_enum, RT5651_DIG_INF_DATA,
+                               RT5651_IF2_ADC_SEL_SFT, rt5651_data_select);
+
+static const struct snd_kcontrol_new rt5651_snd_controls[] = {
+       /* Headphone Output Volume */
+       SOC_DOUBLE_TLV("HP Playback Volume", RT5651_HP_VOL,
+               RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 39, 1, out_vol_tlv),
+       /* OUTPUT Control */
+       SOC_DOUBLE_TLV("OUT Playback Volume", RT5651_LOUT_CTRL1,
+               RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 39, 1, out_vol_tlv),
+
+       /* DAC Digital Volume */
+       SOC_DOUBLE("DAC2 Playback Switch", RT5651_DAC2_CTRL,
+               RT5651_M_DAC_L2_VOL_SFT, RT5651_M_DAC_R2_VOL_SFT, 1, 1),
+       SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5651_DAC1_DIG_VOL,
+                       RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
+                       175, 0, dac_vol_tlv),
+       SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5651_DAC2_DIG_VOL,
+                       RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
+                       175, 0, dac_vol_tlv),
+       /* IN1/IN2 Control */
+       SOC_SINGLE_TLV("IN1 Boost", RT5651_IN1_IN2,
+               RT5651_BST_SFT1, 8, 0, bst_tlv),
+       SOC_SINGLE_TLV("IN2 Boost", RT5651_IN1_IN2,
+               RT5651_BST_SFT2, 8, 0, bst_tlv),
+       /* INL/INR Volume Control */
+       SOC_DOUBLE_TLV("IN Capture Volume", RT5651_INL1_INR1_VOL,
+                       RT5651_INL_VOL_SFT, RT5651_INR_VOL_SFT,
+                       31, 1, in_vol_tlv),
+       /* ADC Digital Volume Control */
+       SOC_DOUBLE("ADC Capture Switch", RT5651_ADC_DIG_VOL,
+               RT5651_L_MUTE_SFT, RT5651_R_MUTE_SFT, 1, 1),
+       SOC_DOUBLE_TLV("ADC Capture Volume", RT5651_ADC_DIG_VOL,
+                       RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
+                       127, 0, adc_vol_tlv),
+       SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5651_ADC_DATA,
+                       RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
+                       127, 0, adc_vol_tlv),
+       /* ADC Boost Volume Control */
+       SOC_DOUBLE_TLV("ADC Boost Gain", RT5651_ADC_BST_VOL,
+                       RT5651_ADC_L_BST_SFT, RT5651_ADC_R_BST_SFT,
+                       3, 0, adc_bst_tlv),
+
+       /* ASRC */
+       SOC_SINGLE("IF1 ASRC Switch", RT5651_PLL_MODE_1,
+               RT5651_STO1_T_SFT, 1, 0),
+       SOC_SINGLE("IF2 ASRC Switch", RT5651_PLL_MODE_1,
+               RT5651_STO2_T_SFT, 1, 0),
+       SOC_SINGLE("DMIC ASRC Switch", RT5651_PLL_MODE_1,
+               RT5651_DMIC_1_M_SFT, 1, 0),
+
+       SOC_ENUM("ADC IF2 Data Switch", rt5651_if2_adc_enum),
+       SOC_ENUM("DAC IF2 Data Switch", rt5651_if2_dac_enum),
+};
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+       int idx = -EINVAL;
+
+       idx = rl6231_calc_dmic_clk(rt5651->sysclk);
+
+       if (idx < 0)
+               dev_err(codec->dev, "Failed to set DMIC clock\n");
+       else
+               snd_soc_update_bits(codec, RT5651_DMIC, RT5651_DMIC_CLK_MASK,
+                                       idx << RT5651_DMIC_CLK_SFT);
+
+       return idx;
+}
+
+static int is_sysclk_from_pll(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink)
+{
+       unsigned int val;
+
+       val = snd_soc_read(source->codec, RT5651_GLB_CLK);
+       val &= RT5651_SCLK_SRC_MASK;
+       if (val == RT5651_SCLK_SRC_PLL1)
+               return 1;
+       else
+               return 0;
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5651_sto1_adc_l_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO1_ADC_MIXER,
+                       RT5651_M_STO1_ADC_L1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO1_ADC_MIXER,
+                       RT5651_M_STO1_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_sto1_adc_r_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO1_ADC_MIXER,
+                       RT5651_M_STO1_ADC_R1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO1_ADC_MIXER,
+                       RT5651_M_STO1_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_sto2_adc_l_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO2_ADC_MIXER,
+                       RT5651_M_STO2_ADC_L1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO2_ADC_MIXER,
+                       RT5651_M_STO2_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_sto2_adc_r_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO2_ADC_MIXER,
+                       RT5651_M_STO2_ADC_R1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO2_ADC_MIXER,
+                       RT5651_M_STO2_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_dac_l_mix[] = {
+       SOC_DAPM_SINGLE("Stereo ADC Switch", RT5651_AD_DA_MIXER,
+                       RT5651_M_ADCMIX_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INF1 Switch", RT5651_AD_DA_MIXER,
+                       RT5651_M_IF1_DAC_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_dac_r_mix[] = {
+       SOC_DAPM_SINGLE("Stereo ADC Switch", RT5651_AD_DA_MIXER,
+                       RT5651_M_ADCMIX_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INF1 Switch", RT5651_AD_DA_MIXER,
+                       RT5651_M_IF1_DAC_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_sto_dac_l_mix[] = {
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_STO_DAC_MIXER,
+                       RT5651_M_DAC_L1_MIXL_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L2 Switch", RT5651_STO_DAC_MIXER,
+                       RT5651_M_DAC_L2_MIXL_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_STO_DAC_MIXER,
+                       RT5651_M_DAC_R1_MIXL_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_sto_dac_r_mix[] = {
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_STO_DAC_MIXER,
+                       RT5651_M_DAC_R1_MIXR_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R2 Switch", RT5651_STO_DAC_MIXER,
+                       RT5651_M_DAC_R2_MIXR_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_STO_DAC_MIXER,
+                       RT5651_M_DAC_L1_MIXR_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_dd_dac_l_mix[] = {
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_DD_MIXER,
+                       RT5651_M_STO_DD_L1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L2 Switch", RT5651_DD_MIXER,
+                       RT5651_M_STO_DD_L2_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R2 Switch", RT5651_DD_MIXER,
+                       RT5651_M_STO_DD_R2_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_dd_dac_r_mix[] = {
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_DD_MIXER,
+                       RT5651_M_STO_DD_R1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R2 Switch", RT5651_DD_MIXER,
+                       RT5651_M_STO_DD_R2_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L2 Switch", RT5651_DD_MIXER,
+                       RT5651_M_STO_DD_L2_R_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt5651_rec_l_mix[] = {
+       SOC_DAPM_SINGLE("INL1 Switch", RT5651_REC_L2_MIXER,
+                       RT5651_M_IN1_L_RM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST3 Switch", RT5651_REC_L2_MIXER,
+                       RT5651_M_BST3_RM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST2 Switch", RT5651_REC_L2_MIXER,
+                       RT5651_M_BST2_RM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST1 Switch", RT5651_REC_L2_MIXER,
+                       RT5651_M_BST1_RM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_rec_r_mix[] = {
+       SOC_DAPM_SINGLE("INR1 Switch", RT5651_REC_R2_MIXER,
+                       RT5651_M_IN1_R_RM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST3 Switch", RT5651_REC_R2_MIXER,
+                       RT5651_M_BST3_RM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST2 Switch", RT5651_REC_R2_MIXER,
+                       RT5651_M_BST2_RM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST1 Switch", RT5651_REC_R2_MIXER,
+                       RT5651_M_BST1_RM_R_SFT, 1, 1),
+};
+
+/* Analog Output Mixer */
+
+static const struct snd_kcontrol_new rt5651_out_l_mix[] = {
+       SOC_DAPM_SINGLE("BST1 Switch", RT5651_OUT_L3_MIXER,
+                       RT5651_M_BST1_OM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST2 Switch", RT5651_OUT_L3_MIXER,
+                       RT5651_M_BST2_OM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INL1 Switch", RT5651_OUT_L3_MIXER,
+                       RT5651_M_IN1_L_OM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("REC MIXL Switch", RT5651_OUT_L3_MIXER,
+                       RT5651_M_RM_L_OM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_OUT_L3_MIXER,
+                       RT5651_M_DAC_L1_OM_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_out_r_mix[] = {
+       SOC_DAPM_SINGLE("BST2 Switch", RT5651_OUT_R3_MIXER,
+                       RT5651_M_BST2_OM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST1 Switch", RT5651_OUT_R3_MIXER,
+                       RT5651_M_BST1_OM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("INR1 Switch", RT5651_OUT_R3_MIXER,
+                       RT5651_M_IN1_R_OM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("REC MIXR Switch", RT5651_OUT_R3_MIXER,
+                       RT5651_M_RM_R_OM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_OUT_R3_MIXER,
+                       RT5651_M_DAC_R1_OM_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_hpo_mix[] = {
+       SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5651_HPO_MIXER,
+                       RT5651_M_DAC1_HM_SFT, 1, 1),
+       SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5651_HPO_MIXER,
+                       RT5651_M_HPVOL_HM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5651_lout_mix[] = {
+       SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_LOUT_MIXER,
+                       RT5651_M_DAC_L1_LM_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_LOUT_MIXER,
+                       RT5651_M_DAC_R1_LM_SFT, 1, 1),
+       SOC_DAPM_SINGLE("OUTVOL L Switch", RT5651_LOUT_MIXER,
+                       RT5651_M_OV_L_LM_SFT, 1, 1),
+       SOC_DAPM_SINGLE("OUTVOL R Switch", RT5651_LOUT_MIXER,
+                       RT5651_M_OV_R_LM_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new outvol_l_control =
+       SOC_DAPM_SINGLE("Switch", RT5651_LOUT_CTRL1,
+                       RT5651_VOL_L_SFT, 1, 1);
+
+static const struct snd_kcontrol_new outvol_r_control =
+       SOC_DAPM_SINGLE("Switch", RT5651_LOUT_CTRL1,
+                       RT5651_VOL_R_SFT, 1, 1);
+
+static const struct snd_kcontrol_new lout_l_mute_control =
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_LOUT_CTRL1,
+                                   RT5651_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new lout_r_mute_control =
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_LOUT_CTRL1,
+                                   RT5651_R_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpovol_l_control =
+       SOC_DAPM_SINGLE("Switch", RT5651_HP_VOL,
+                       RT5651_VOL_L_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpovol_r_control =
+       SOC_DAPM_SINGLE("Switch", RT5651_HP_VOL,
+                       RT5651_VOL_R_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpo_l_mute_control =
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_HP_VOL,
+                                   RT5651_L_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpo_r_mute_control =
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_HP_VOL,
+                                   RT5651_R_MUTE_SFT, 1, 1);
+
+/* INL/R source */
+static const char * const rt5651_inl_src[] = {"IN2P", "HPOVOLLP"};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5651_inl_enum, RT5651_INL1_INR1_VOL,
+       RT5651_INL_SEL_SFT, rt5651_inl_src);
+
+static const struct snd_kcontrol_new rt5651_inl1_mux =
+       SOC_DAPM_ENUM("INL1 source", rt5651_inl_enum);
+
+static const char * const rt5651_inr1_src[] = {"IN2N", "HPOVOLRP"};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5651_inr1_enum, RT5651_INL1_INR1_VOL,
+       RT5651_INR_SEL_SFT, rt5651_inr1_src);
+
+static const struct snd_kcontrol_new rt5651_inr1_mux =
+       SOC_DAPM_ENUM("INR1 source", rt5651_inr1_enum);
+
+static const char * const rt5651_inl2_src[] = {"IN3P", "OUTVOLLP"};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5651_inl2_enum, RT5651_INL2_INR2_VOL,
+       RT5651_INL_SEL_SFT, rt5651_inl2_src);
+
+static const struct snd_kcontrol_new rt5651_inl2_mux =
+       SOC_DAPM_ENUM("INL2 source", rt5651_inl2_enum);
+
+static const char * const rt5651_inr2_src[] = {"IN3N", "OUTVOLRP"};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5651_inr2_enum, RT5651_INL2_INR2_VOL,
+       RT5651_INR_SEL_SFT, rt5651_inr2_src);
+
+static const struct snd_kcontrol_new rt5651_inr2_mux =
+       SOC_DAPM_ENUM("INR2 source", rt5651_inr2_enum);
+
+
+/* Stereo ADC source */
+static const char * const rt5651_stereo1_adc1_src[] = {"DD MIX", "ADC"};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5651_stereo1_adc1_enum, RT5651_STO1_ADC_MIXER,
+       RT5651_STO1_ADC_1_SRC_SFT, rt5651_stereo1_adc1_src);
+
+static const struct snd_kcontrol_new rt5651_sto1_adc_l1_mux =
+       SOC_DAPM_ENUM("Stereo1 ADC L1 source", rt5651_stereo1_adc1_enum);
+
+static const struct snd_kcontrol_new rt5651_sto1_adc_r1_mux =
+       SOC_DAPM_ENUM("Stereo1 ADC R1 source", rt5651_stereo1_adc1_enum);
+
+static const char * const rt5651_stereo1_adc2_src[] = {"DMIC", "DD MIX"};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5651_stereo1_adc2_enum, RT5651_STO1_ADC_MIXER,
+       RT5651_STO1_ADC_2_SRC_SFT, rt5651_stereo1_adc2_src);
+
+static const struct snd_kcontrol_new rt5651_sto1_adc_l2_mux =
+       SOC_DAPM_ENUM("Stereo1 ADC L2 source", rt5651_stereo1_adc2_enum);
+
+static const struct snd_kcontrol_new rt5651_sto1_adc_r2_mux =
+       SOC_DAPM_ENUM("Stereo1 ADC R2 source", rt5651_stereo1_adc2_enum);
+
+/* Mono ADC source */
+static const char * const rt5651_sto2_adc_l1_src[] = {"DD MIXL", "ADCL"};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5651_sto2_adc_l1_enum, RT5651_STO1_ADC_MIXER,
+       RT5651_STO2_ADC_L1_SRC_SFT, rt5651_sto2_adc_l1_src);
+
+static const struct snd_kcontrol_new rt5651_sto2_adc_l1_mux =
+       SOC_DAPM_ENUM("Stereo2 ADC1 left source", rt5651_sto2_adc_l1_enum);
+
+static const char * const rt5651_sto2_adc_l2_src[] = {"DMIC L", "DD MIXL"};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5651_sto2_adc_l2_enum, RT5651_STO1_ADC_MIXER,
+       RT5651_STO2_ADC_L2_SRC_SFT, rt5651_sto2_adc_l2_src);
+
+static const struct snd_kcontrol_new rt5651_sto2_adc_l2_mux =
+       SOC_DAPM_ENUM("Stereo2 ADC2 left source", rt5651_sto2_adc_l2_enum);
+
+static const char * const rt5651_sto2_adc_r1_src[] = {"DD MIXR", "ADCR"};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5651_sto2_adc_r1_enum, RT5651_STO1_ADC_MIXER,
+       RT5651_STO2_ADC_R1_SRC_SFT, rt5651_sto2_adc_r1_src);
+
+static const struct snd_kcontrol_new rt5651_sto2_adc_r1_mux =
+       SOC_DAPM_ENUM("Stereo2 ADC1 right source", rt5651_sto2_adc_r1_enum);
+
+static const char * const rt5651_sto2_adc_r2_src[] = {"DMIC R", "DD MIXR"};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5651_sto2_adc_r2_enum, RT5651_STO1_ADC_MIXER,
+       RT5651_STO2_ADC_R2_SRC_SFT, rt5651_sto2_adc_r2_src);
+
+static const struct snd_kcontrol_new rt5651_sto2_adc_r2_mux =
+       SOC_DAPM_ENUM("Stereo2 ADC2 right source", rt5651_sto2_adc_r2_enum);
+
+/* DAC2 channel source */
+
+static const char * const rt5651_dac_src[] = {"IF1", "IF2"};
+
+static SOC_ENUM_SINGLE_DECL(rt5651_dac_l2_enum, RT5651_DAC2_CTRL,
+                               RT5651_SEL_DAC_L2_SFT, rt5651_dac_src);
+
+static const struct snd_kcontrol_new rt5651_dac_l2_mux =
+       SOC_DAPM_ENUM("DAC2 left channel source", rt5651_dac_l2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5651_dac_r2_enum, RT5651_DAC2_CTRL,
+       RT5651_SEL_DAC_R2_SFT, rt5651_dac_src);
+
+static const struct snd_kcontrol_new rt5651_dac_r2_mux =
+       SOC_DAPM_ENUM("DAC2 right channel source", rt5651_dac_r2_enum);
+
+/* IF2_ADC channel source */
+
+static const char * const rt5651_adc_src[] = {"IF1 ADC1", "IF1 ADC2"};
+
+static SOC_ENUM_SINGLE_DECL(rt5651_if2_adc_src_enum, RT5651_DIG_INF_DATA,
+                               RT5651_IF2_ADC_SRC_SFT, rt5651_adc_src);
+
+static const struct snd_kcontrol_new rt5651_if2_adc_src_mux =
+       SOC_DAPM_ENUM("IF2 ADC channel source", rt5651_if2_adc_src_enum);
+
+/* PDM select */
+static const char * const rt5651_pdm_sel[] = {"DD MIX", "Stereo DAC MIX"};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5651_pdm_l_sel_enum, RT5651_PDM_CTL,
+       RT5651_PDM_L_SEL_SFT, rt5651_pdm_sel);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5651_pdm_r_sel_enum, RT5651_PDM_CTL,
+       RT5651_PDM_R_SEL_SFT, rt5651_pdm_sel);
+
+static const struct snd_kcontrol_new rt5651_pdm_l_mux =
+       SOC_DAPM_ENUM("PDM L select", rt5651_pdm_l_sel_enum);
+
+static const struct snd_kcontrol_new rt5651_pdm_r_mux =
+       SOC_DAPM_ENUM("PDM R select", rt5651_pdm_r_sel_enum);
+
+static int rt5651_amp_power_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               /* depop parameters */
+               regmap_update_bits(rt5651->regmap, RT5651_PR_BASE +
+                       RT5651_CHPUMP_INT_REG1, 0x0700, 0x0200);
+               regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M2,
+                       RT5651_DEPOP_MASK, RT5651_DEPOP_MAN);
+               regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M1,
+                       RT5651_HP_CP_MASK | RT5651_HP_SG_MASK |
+                       RT5651_HP_CB_MASK, RT5651_HP_CP_PU |
+                       RT5651_HP_SG_DIS | RT5651_HP_CB_PU);
+               regmap_write(rt5651->regmap, RT5651_PR_BASE +
+                               RT5651_HP_DCC_INT1, 0x9f00);
+               /* headphone amp power on */
+               regmap_update_bits(rt5651->regmap, RT5651_PWR_ANLG1,
+                       RT5651_PWR_FV1 | RT5651_PWR_FV2, 0);
+               regmap_update_bits(rt5651->regmap, RT5651_PWR_ANLG1,
+                       RT5651_PWR_HA,
+                       RT5651_PWR_HA);
+               usleep_range(10000, 15000);
+               regmap_update_bits(rt5651->regmap, RT5651_PWR_ANLG1,
+                       RT5651_PWR_FV1 | RT5651_PWR_FV2 ,
+                       RT5651_PWR_FV1 | RT5651_PWR_FV2);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5651_hp_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               /* headphone unmute sequence */
+               regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M2,
+                       RT5651_DEPOP_MASK | RT5651_DIG_DP_MASK,
+                       RT5651_DEPOP_AUTO | RT5651_DIG_DP_EN);
+               regmap_update_bits(rt5651->regmap, RT5651_CHARGE_PUMP,
+                       RT5651_PM_HP_MASK, RT5651_PM_HP_HV);
+
+               regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M3,
+                       RT5651_CP_FQ1_MASK | RT5651_CP_FQ2_MASK |
+                       RT5651_CP_FQ3_MASK,
+                       (RT5651_CP_FQ_192_KHZ << RT5651_CP_FQ1_SFT) |
+                       (RT5651_CP_FQ_12_KHZ << RT5651_CP_FQ2_SFT) |
+                       (RT5651_CP_FQ_192_KHZ << RT5651_CP_FQ3_SFT));
+
+               regmap_write(rt5651->regmap, RT5651_PR_BASE +
+                       RT5651_MAMP_INT_REG2, 0x1c00);
+               regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M1,
+                       RT5651_HP_CP_MASK | RT5651_HP_SG_MASK,
+                       RT5651_HP_CP_PD | RT5651_HP_SG_EN);
+               regmap_update_bits(rt5651->regmap, RT5651_PR_BASE +
+                       RT5651_CHPUMP_INT_REG1, 0x0700, 0x0400);
+               rt5651->hp_mute = 0;
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               rt5651->hp_mute = 1;
+               usleep_range(70000, 75000);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5651_hp_post_event(struct snd_soc_dapm_widget *w,
+                          struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (!rt5651->hp_mute)
+                       usleep_range(80000, 85000);
+
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5651_bst1_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_POST_PMU:
+               snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+                       RT5651_PWR_BST1_OP2, RT5651_PWR_BST1_OP2);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+                       RT5651_PWR_BST1_OP2, 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5651_bst2_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_POST_PMU:
+               snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+                       RT5651_PWR_BST2_OP2, RT5651_PWR_BST2_OP2);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+                       RT5651_PWR_BST2_OP2, 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5651_bst3_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_POST_PMU:
+               snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+                       RT5651_PWR_BST3_OP2, RT5651_PWR_BST3_OP2);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+                       RT5651_PWR_BST3_OP2, 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = {
+       /* ASRC */
+       SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5651_PLL_MODE_2,
+                             15, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5651_PLL_MODE_2,
+                             14, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("STO1 DAC ASRC", 1, RT5651_PLL_MODE_2,
+                             13, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("STO2 DAC ASRC", 1, RT5651_PLL_MODE_2,
+                             12, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("ADC ASRC", 1, RT5651_PLL_MODE_2,
+                             11, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("PLL1", RT5651_PWR_ANLG2,
+                       RT5651_PWR_PLL_BIT, 0, NULL, 0),
+       /* Input Side */
+       /* micbias */
+       SND_SOC_DAPM_SUPPLY("LDO", RT5651_PWR_ANLG1,
+                       RT5651_PWR_LDO_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_MICBIAS("micbias1", RT5651_PWR_ANLG2,
+                       RT5651_PWR_MB1_BIT, 0),
+       /* Input Lines */
+       SND_SOC_DAPM_INPUT("MIC1"),
+       SND_SOC_DAPM_INPUT("MIC2"),
+       SND_SOC_DAPM_INPUT("MIC3"),
+
+       SND_SOC_DAPM_INPUT("IN1P"),
+       SND_SOC_DAPM_INPUT("IN2P"),
+       SND_SOC_DAPM_INPUT("IN2N"),
+       SND_SOC_DAPM_INPUT("IN3P"),
+       SND_SOC_DAPM_INPUT("DMIC L1"),
+       SND_SOC_DAPM_INPUT("DMIC R1"),
+       SND_SOC_DAPM_SUPPLY("DMIC CLK", RT5651_DMIC, RT5651_DMIC_1_EN_SFT,
+                           0, set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+       /* Boost */
+       SND_SOC_DAPM_PGA_E("BST1", RT5651_PWR_ANLG2,
+               RT5651_PWR_BST1_BIT, 0, NULL, 0, rt5651_bst1_event,
+               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_E("BST2", RT5651_PWR_ANLG2,
+               RT5651_PWR_BST2_BIT, 0, NULL, 0, rt5651_bst2_event,
+               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_E("BST3", RT5651_PWR_ANLG2,
+               RT5651_PWR_BST3_BIT, 0, NULL, 0, rt5651_bst3_event,
+               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+       /* Input Volume */
+       SND_SOC_DAPM_PGA("INL1 VOL", RT5651_PWR_VOL,
+                        RT5651_PWR_IN1_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("INR1 VOL", RT5651_PWR_VOL,
+                        RT5651_PWR_IN1_R_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("INL2 VOL", RT5651_PWR_VOL,
+                        RT5651_PWR_IN2_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("INR2 VOL", RT5651_PWR_VOL,
+                        RT5651_PWR_IN2_R_BIT, 0, NULL, 0),
+       /* IN Mux */
+       SND_SOC_DAPM_MUX("INL1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl1_mux),
+       SND_SOC_DAPM_MUX("INR1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inr1_mux),
+       SND_SOC_DAPM_MUX("INL2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl2_mux),
+       SND_SOC_DAPM_MUX("INR2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inr2_mux),
+       /* REC Mixer */
+       SND_SOC_DAPM_MIXER("RECMIXL", RT5651_PWR_MIXER, RT5651_PWR_RM_L_BIT, 0,
+                          rt5651_rec_l_mix, ARRAY_SIZE(rt5651_rec_l_mix)),
+       SND_SOC_DAPM_MIXER("RECMIXR", RT5651_PWR_MIXER, RT5651_PWR_RM_R_BIT, 0,
+                          rt5651_rec_r_mix, ARRAY_SIZE(rt5651_rec_r_mix)),
+       /* ADCs */
+       SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_SUPPLY("ADC L Power", RT5651_PWR_DIG1,
+                           RT5651_PWR_ADC_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC R Power", RT5651_PWR_DIG1,
+                           RT5651_PWR_ADC_R_BIT, 0, NULL, 0),
+       /* ADC Mux */
+       SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+                        &rt5651_sto1_adc_l2_mux),
+       SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+                        &rt5651_sto1_adc_r2_mux),
+       SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+                        &rt5651_sto1_adc_l1_mux),
+       SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+                        &rt5651_sto1_adc_r1_mux),
+       SND_SOC_DAPM_MUX("Stereo2 ADC L2 Mux", SND_SOC_NOPM, 0, 0,
+                        &rt5651_sto2_adc_l2_mux),
+       SND_SOC_DAPM_MUX("Stereo2 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
+                        &rt5651_sto2_adc_l1_mux),
+       SND_SOC_DAPM_MUX("Stereo2 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
+                        &rt5651_sto2_adc_r1_mux),
+       SND_SOC_DAPM_MUX("Stereo2 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
+                        &rt5651_sto2_adc_r2_mux),
+       /* ADC Mixer */
+       SND_SOC_DAPM_SUPPLY("Stereo1 Filter", RT5651_PWR_DIG2,
+                           RT5651_PWR_ADC_STO1_F_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Stereo2 Filter", RT5651_PWR_DIG2,
+                           RT5651_PWR_ADC_STO2_F_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+                          rt5651_sto1_adc_l_mix,
+                          ARRAY_SIZE(rt5651_sto1_adc_l_mix)),
+       SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+                          rt5651_sto1_adc_r_mix,
+                          ARRAY_SIZE(rt5651_sto1_adc_r_mix)),
+       SND_SOC_DAPM_MIXER("Stereo2 ADC MIXL", SND_SOC_NOPM, 0, 0,
+                          rt5651_sto2_adc_l_mix,
+                          ARRAY_SIZE(rt5651_sto2_adc_l_mix)),
+       SND_SOC_DAPM_MIXER("Stereo2 ADC MIXR", SND_SOC_NOPM, 0, 0,
+                          rt5651_sto2_adc_r_mix,
+                          ARRAY_SIZE(rt5651_sto2_adc_r_mix)),
+
+       /* Digital Interface */
+       SND_SOC_DAPM_SUPPLY("I2S1", RT5651_PWR_DIG1,
+                           RT5651_PWR_I2S1_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("I2S2", RT5651_PWR_DIG1,
+                           RT5651_PWR_I2S2_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MUX("IF2 ADC", SND_SOC_NOPM, 0, 0,
+                        &rt5651_if2_adc_src_mux),
+
+       /* Digital Interface Select */
+
+       SND_SOC_DAPM_MUX("PDM L Mux", RT5651_PDM_CTL,
+                        RT5651_M_PDM_L_SFT, 1, &rt5651_pdm_l_mux),
+       SND_SOC_DAPM_MUX("PDM R Mux", RT5651_PDM_CTL,
+                        RT5651_M_PDM_R_SFT, 1, &rt5651_pdm_r_mux),
+       /* Audio Interface */
+       SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+       /* Audio DSP */
+       SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       /* Output Side */
+       /* DAC mixer before sound effect  */
+       SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
+                          rt5651_dac_l_mix, ARRAY_SIZE(rt5651_dac_l_mix)),
+       SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
+                          rt5651_dac_r_mix, ARRAY_SIZE(rt5651_dac_r_mix)),
+
+       /* DAC2 channel Mux */
+       SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_dac_l2_mux),
+       SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_dac_r2_mux),
+       SND_SOC_DAPM_PGA("DAC L2 Volume", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("DAC R2 Volume", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5651_PWR_DIG2,
+                           RT5651_PWR_DAC_STO1_F_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Stero2 DAC Power", RT5651_PWR_DIG2,
+                           RT5651_PWR_DAC_STO2_F_BIT, 0, NULL, 0),
+       /* DAC Mixer */
+       SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+                          rt5651_sto_dac_l_mix,
+                          ARRAY_SIZE(rt5651_sto_dac_l_mix)),
+       SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+                          rt5651_sto_dac_r_mix,
+                          ARRAY_SIZE(rt5651_sto_dac_r_mix)),
+       SND_SOC_DAPM_MIXER("DD MIXL", SND_SOC_NOPM, 0, 0,
+                          rt5651_dd_dac_l_mix,
+                          ARRAY_SIZE(rt5651_dd_dac_l_mix)),
+       SND_SOC_DAPM_MIXER("DD MIXR", SND_SOC_NOPM, 0, 0,
+                          rt5651_dd_dac_r_mix,
+                          ARRAY_SIZE(rt5651_dd_dac_r_mix)),
+
+       /* DACs */
+       SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_SUPPLY("DAC L1 Power", RT5651_PWR_DIG1,
+                           RT5651_PWR_DAC_L1_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5651_PWR_DIG1,
+                           RT5651_PWR_DAC_R1_BIT, 0, NULL, 0),
+       /* OUT Mixer */
+       SND_SOC_DAPM_MIXER("OUT MIXL", RT5651_PWR_MIXER, RT5651_PWR_OM_L_BIT,
+                          0, rt5651_out_l_mix, ARRAY_SIZE(rt5651_out_l_mix)),
+       SND_SOC_DAPM_MIXER("OUT MIXR", RT5651_PWR_MIXER, RT5651_PWR_OM_R_BIT,
+                          0, rt5651_out_r_mix, ARRAY_SIZE(rt5651_out_r_mix)),
+       /* Ouput Volume */
+       SND_SOC_DAPM_SWITCH("OUTVOL L", RT5651_PWR_VOL,
+                           RT5651_PWR_OV_L_BIT, 0, &outvol_l_control),
+       SND_SOC_DAPM_SWITCH("OUTVOL R", RT5651_PWR_VOL,
+                           RT5651_PWR_OV_R_BIT, 0, &outvol_r_control),
+       SND_SOC_DAPM_SWITCH("HPOVOL L", RT5651_PWR_VOL,
+                           RT5651_PWR_HV_L_BIT, 0, &hpovol_l_control),
+       SND_SOC_DAPM_SWITCH("HPOVOL R", RT5651_PWR_VOL,
+                           RT5651_PWR_HV_R_BIT, 0, &hpovol_r_control),
+       SND_SOC_DAPM_PGA("INL1", RT5651_PWR_VOL,
+                        RT5651_PWR_IN1_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("INR1", RT5651_PWR_VOL,
+                        RT5651_PWR_IN1_R_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("INL2", RT5651_PWR_VOL,
+                        RT5651_PWR_IN2_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("INR2", RT5651_PWR_VOL,
+                        RT5651_PWR_IN2_R_BIT, 0, NULL, 0),
+       /* HPO/LOUT/Mono Mixer */
+       SND_SOC_DAPM_MIXER("HPOL MIX", SND_SOC_NOPM, 0, 0,
+                          rt5651_hpo_mix, ARRAY_SIZE(rt5651_hpo_mix)),
+       SND_SOC_DAPM_MIXER("HPOR MIX", SND_SOC_NOPM, 0, 0,
+                          rt5651_hpo_mix, ARRAY_SIZE(rt5651_hpo_mix)),
+       SND_SOC_DAPM_SUPPLY("HP L Amp", RT5651_PWR_ANLG1,
+                           RT5651_PWR_HP_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("HP R Amp", RT5651_PWR_ANLG1,
+                           RT5651_PWR_HP_R_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("LOUT MIX", RT5651_PWR_ANLG1, RT5651_PWR_LM_BIT, 0,
+                          rt5651_lout_mix, ARRAY_SIZE(rt5651_lout_mix)),
+
+       SND_SOC_DAPM_SUPPLY("Amp Power", RT5651_PWR_ANLG1,
+                           RT5651_PWR_HA_BIT, 0, rt5651_amp_power_event,
+                           SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5651_hp_event,
+                          SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_SWITCH("HPO L Playback", SND_SOC_NOPM, 0, 0,
+                           &hpo_l_mute_control),
+       SND_SOC_DAPM_SWITCH("HPO R Playback", SND_SOC_NOPM, 0, 0,
+                           &hpo_r_mute_control),
+       SND_SOC_DAPM_SWITCH("LOUT L Playback", SND_SOC_NOPM, 0, 0,
+                           &lout_l_mute_control),
+       SND_SOC_DAPM_SWITCH("LOUT R Playback", SND_SOC_NOPM, 0, 0,
+                           &lout_r_mute_control),
+       SND_SOC_DAPM_POST("HP Post", rt5651_hp_post_event),
+
+       /* Output Lines */
+       SND_SOC_DAPM_OUTPUT("HPOL"),
+       SND_SOC_DAPM_OUTPUT("HPOR"),
+       SND_SOC_DAPM_OUTPUT("LOUTL"),
+       SND_SOC_DAPM_OUTPUT("LOUTR"),
+       SND_SOC_DAPM_OUTPUT("PDML"),
+       SND_SOC_DAPM_OUTPUT("PDMR"),
+};
+
+static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
+       {"Stero1 DAC Power", NULL, "STO1 DAC ASRC"},
+       {"Stero2 DAC Power", NULL, "STO2 DAC ASRC"},
+       {"I2S1", NULL, "I2S1 ASRC"},
+       {"I2S2", NULL, "I2S2 ASRC"},
+
+       {"IN1P", NULL, "LDO"},
+       {"IN2P", NULL, "LDO"},
+       {"IN3P", NULL, "LDO"},
+
+       {"IN1P", NULL, "MIC1"},
+       {"IN2P", NULL, "MIC2"},
+       {"IN2N", NULL, "MIC2"},
+       {"IN3P", NULL, "MIC3"},
+
+       {"BST1", NULL, "IN1P"},
+       {"BST2", NULL, "IN2P"},
+       {"BST2", NULL, "IN2N"},
+       {"BST3", NULL, "IN3P"},
+
+       {"INL1 VOL", NULL, "IN2P"},
+       {"INR1 VOL", NULL, "IN2N"},
+
+       {"RECMIXL", "INL1 Switch", "INL1 VOL"},
+       {"RECMIXL", "BST3 Switch", "BST3"},
+       {"RECMIXL", "BST2 Switch", "BST2"},
+       {"RECMIXL", "BST1 Switch", "BST1"},
+
+       {"RECMIXR", "INR1 Switch", "INR1 VOL"},
+       {"RECMIXR", "BST3 Switch", "BST3"},
+       {"RECMIXR", "BST2 Switch", "BST2"},
+       {"RECMIXR", "BST1 Switch", "BST1"},
+
+       {"ADC L", NULL, "RECMIXL"},
+       {"ADC L", NULL, "ADC L Power"},
+       {"ADC R", NULL, "RECMIXR"},
+       {"ADC R", NULL, "ADC R Power"},
+
+       {"DMIC L1", NULL, "DMIC CLK"},
+       {"DMIC R1", NULL, "DMIC CLK"},
+
+       {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"},
+       {"Stereo1 ADC L2 Mux", "DD MIX", "DD MIXL"},
+       {"Stereo1 ADC L1 Mux", "ADC", "ADC L"},
+       {"Stereo1 ADC L1 Mux", "DD MIX", "DD MIXL"},
+
+       {"Stereo1 ADC R1 Mux", "ADC", "ADC R"},
+       {"Stereo1 ADC R1 Mux", "DD MIX", "DD MIXR"},
+       {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"},
+       {"Stereo1 ADC R2 Mux", "DD MIX", "DD MIXR"},
+
+       {"Stereo2 ADC L2 Mux", "DMIC L", "DMIC L1"},
+       {"Stereo2 ADC L2 Mux", "DD MIXL", "DD MIXL"},
+       {"Stereo2 ADC L1 Mux", "DD MIXL", "DD MIXL"},
+       {"Stereo2 ADC L1 Mux", "ADCL", "ADC L"},
+
+       {"Stereo2 ADC R1 Mux", "DD MIXR", "DD MIXR"},
+       {"Stereo2 ADC R1 Mux", "ADCR", "ADC R"},
+       {"Stereo2 ADC R2 Mux", "DMIC R", "DMIC R1"},
+       {"Stereo2 ADC R2 Mux", "DD MIXR", "DD MIXR"},
+
+       {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"},
+       {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"},
+       {"Stereo1 ADC MIXL", NULL, "Stereo1 Filter"},
+       {"Stereo1 Filter", NULL, "PLL1", is_sysclk_from_pll},
+       {"Stereo1 Filter", NULL, "ADC ASRC"},
+
+       {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"},
+       {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"},
+       {"Stereo1 ADC MIXR", NULL, "Stereo1 Filter"},
+
+       {"Stereo2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC L1 Mux"},
+       {"Stereo2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC L2 Mux"},
+       {"Stereo2 ADC MIXL", NULL, "Stereo2 Filter"},
+       {"Stereo2 Filter", NULL, "PLL1", is_sysclk_from_pll},
+       {"Stereo2 Filter", NULL, "ADC ASRC"},
+
+       {"Stereo2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC R1 Mux"},
+       {"Stereo2 ADC MIXR", "ADC2 Switch", "Stereo2 ADC R2 Mux"},
+       {"Stereo2 ADC MIXR", NULL, "Stereo2 Filter"},
+
+       {"IF1 ADC2", NULL, "Stereo2 ADC MIXL"},
+       {"IF1 ADC2", NULL, "Stereo2 ADC MIXR"},
+       {"IF1 ADC1", NULL, "Stereo1 ADC MIXL"},
+       {"IF1 ADC1", NULL, "Stereo1 ADC MIXR"},
+
+       {"IF1 ADC1", NULL, "I2S1"},
+
+       {"IF2 ADC", "IF1 ADC1", "IF1 ADC1"},
+       {"IF2 ADC", "IF1 ADC2", "IF1 ADC2"},
+       {"IF2 ADC", NULL, "I2S2"},
+
+       {"AIF1TX", NULL, "IF1 ADC1"},
+       {"AIF1TX", NULL, "IF1 ADC2"},
+       {"AIF2TX", NULL, "IF2 ADC"},
+
+       {"IF1 DAC", NULL, "AIF1RX"},
+       {"IF1 DAC", NULL, "I2S1"},
+       {"IF2 DAC", NULL, "AIF2RX"},
+       {"IF2 DAC", NULL, "I2S2"},
+
+       {"IF1 DAC1 L", NULL, "IF1 DAC"},
+       {"IF1 DAC1 R", NULL, "IF1 DAC"},
+       {"IF1 DAC2 L", NULL, "IF1 DAC"},
+       {"IF1 DAC2 R", NULL, "IF1 DAC"},
+       {"IF2 DAC L", NULL, "IF2 DAC"},
+       {"IF2 DAC R", NULL, "IF2 DAC"},
+
+       {"DAC MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"},
+       {"DAC MIXL", "INF1 Switch", "IF1 DAC1 L"},
+       {"DAC MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"},
+       {"DAC MIXR", "INF1 Switch", "IF1 DAC1 R"},
+
+       {"Audio DSP", NULL, "DAC MIXL"},
+       {"Audio DSP", NULL, "DAC MIXR"},
+
+       {"DAC L2 Mux", "IF1", "IF1 DAC2 L"},
+       {"DAC L2 Mux", "IF2", "IF2 DAC L"},
+       {"DAC L2 Volume", NULL, "DAC L2 Mux"},
+
+       {"DAC R2 Mux", "IF1", "IF1 DAC2 R"},
+       {"DAC R2 Mux", "IF2", "IF2 DAC R"},
+       {"DAC R2 Volume", NULL, "DAC R2 Mux"},
+
+       {"Stereo DAC MIXL", "DAC L1 Switch", "Audio DSP"},
+       {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Volume"},
+       {"Stereo DAC MIXL", "DAC R1 Switch", "DAC MIXR"},
+       {"Stereo DAC MIXL", NULL, "Stero1 DAC Power"},
+       {"Stereo DAC MIXL", NULL, "Stero2 DAC Power"},
+       {"Stereo DAC MIXR", "DAC R1 Switch", "Audio DSP"},
+       {"Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Volume"},
+       {"Stereo DAC MIXR", "DAC L1 Switch", "DAC MIXL"},
+       {"Stereo DAC MIXR", NULL, "Stero1 DAC Power"},
+       {"Stereo DAC MIXR", NULL, "Stero2 DAC Power"},
+
+       {"PDM L Mux", "Stereo DAC MIX", "Stereo DAC MIXL"},
+       {"PDM L Mux", "DD MIX", "DAC MIXL"},
+       {"PDM R Mux", "Stereo DAC MIX", "Stereo DAC MIXR"},
+       {"PDM R Mux", "DD MIX", "DAC MIXR"},
+
+       {"DAC L1", NULL, "Stereo DAC MIXL"},
+       {"DAC L1", NULL, "PLL1", is_sysclk_from_pll},
+       {"DAC L1", NULL, "DAC L1 Power"},
+       {"DAC R1", NULL, "Stereo DAC MIXR"},
+       {"DAC R1", NULL, "PLL1", is_sysclk_from_pll},
+       {"DAC R1", NULL, "DAC R1 Power"},
+
+       {"DD MIXL", "DAC L1 Switch", "DAC MIXL"},
+       {"DD MIXL", "DAC L2 Switch", "DAC L2 Volume"},
+       {"DD MIXL", "DAC R2 Switch", "DAC R2 Volume"},
+       {"DD MIXL", NULL, "Stero2 DAC Power"},
+
+       {"DD MIXR", "DAC R1 Switch", "DAC MIXR"},
+       {"DD MIXR", "DAC R2 Switch", "DAC R2 Volume"},
+       {"DD MIXR", "DAC L2 Switch", "DAC L2 Volume"},
+       {"DD MIXR", NULL, "Stero2 DAC Power"},
+
+       {"OUT MIXL", "BST1 Switch", "BST1"},
+       {"OUT MIXL", "BST2 Switch", "BST2"},
+       {"OUT MIXL", "INL1 Switch", "INL1 VOL"},
+       {"OUT MIXL", "REC MIXL Switch", "RECMIXL"},
+       {"OUT MIXL", "DAC L1 Switch", "DAC L1"},
+
+       {"OUT MIXR", "BST2 Switch", "BST2"},
+       {"OUT MIXR", "BST1 Switch", "BST1"},
+       {"OUT MIXR", "INR1 Switch", "INR1 VOL"},
+       {"OUT MIXR", "REC MIXR Switch", "RECMIXR"},
+       {"OUT MIXR", "DAC R1 Switch", "DAC R1"},
+
+       {"HPOVOL L", "Switch", "OUT MIXL"},
+       {"HPOVOL R", "Switch", "OUT MIXR"},
+       {"OUTVOL L", "Switch", "OUT MIXL"},
+       {"OUTVOL R", "Switch", "OUT MIXR"},
+
+       {"HPOL MIX", "HPO MIX DAC1 Switch", "DAC L1"},
+       {"HPOL MIX", "HPO MIX HPVOL Switch", "HPOVOL L"},
+       {"HPOL MIX", NULL, "HP L Amp"},
+       {"HPOR MIX", "HPO MIX DAC1 Switch", "DAC R1"},
+       {"HPOR MIX", "HPO MIX HPVOL Switch", "HPOVOL R"},
+       {"HPOR MIX", NULL, "HP R Amp"},
+
+       {"LOUT MIX", "DAC L1 Switch", "DAC L1"},
+       {"LOUT MIX", "DAC R1 Switch", "DAC R1"},
+       {"LOUT MIX", "OUTVOL L Switch", "OUTVOL L"},
+       {"LOUT MIX", "OUTVOL R Switch", "OUTVOL R"},
+
+       {"HP Amp", NULL, "HPOL MIX"},
+       {"HP Amp", NULL, "HPOR MIX"},
+       {"HP Amp", NULL, "Amp Power"},
+       {"HPO L Playback", "Switch", "HP Amp"},
+       {"HPO R Playback", "Switch", "HP Amp"},
+       {"HPOL", NULL, "HPO L Playback"},
+       {"HPOR", NULL, "HPO R Playback"},
+
+       {"LOUT L Playback", "Switch", "LOUT MIX"},
+       {"LOUT R Playback", "Switch", "LOUT MIX"},
+       {"LOUTL", NULL, "LOUT L Playback"},
+       {"LOUTL", NULL, "Amp Power"},
+       {"LOUTR", NULL, "LOUT R Playback"},
+       {"LOUTR", NULL, "Amp Power"},
+
+       {"PDML", NULL, "PDM L Mux"},
+       {"PDMR", NULL, "PDM R Mux"},
+};
+
+static int rt5651_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 rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val_len = 0, val_clk, mask_clk;
+       int pre_div, bclk_ms, frame_size;
+
+       rt5651->lrck[dai->id] = params_rate(params);
+       pre_div = rl6231_get_clk_info(rt5651->sysclk, rt5651->lrck[dai->id]);
+
+       if (pre_div < 0) {
+               dev_err(codec->dev, "Unsupported clock setting\n");
+               return -EINVAL;
+       }
+       frame_size = snd_soc_params_to_frame_size(params);
+       if (frame_size < 0) {
+               dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+               return -EINVAL;
+       }
+       bclk_ms = frame_size > 32 ? 1 : 0;
+       rt5651->bclk[dai->id] = rt5651->lrck[dai->id] * (32 << bclk_ms);
+
+       dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+               rt5651->bclk[dai->id], rt5651->lrck[dai->id]);
+       dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+                               bclk_ms, pre_div, dai->id);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               val_len |= RT5651_I2S_DL_20;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               val_len |= RT5651_I2S_DL_24;
+               break;
+       case SNDRV_PCM_FORMAT_S8:
+               val_len |= RT5651_I2S_DL_8;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (dai->id) {
+       case RT5651_AIF1:
+               mask_clk = RT5651_I2S_PD1_MASK;
+               val_clk = pre_div << RT5651_I2S_PD1_SFT;
+               snd_soc_update_bits(codec, RT5651_I2S1_SDP,
+                       RT5651_I2S_DL_MASK, val_len);
+               snd_soc_update_bits(codec, RT5651_ADDA_CLK1, mask_clk, val_clk);
+               break;
+       case RT5651_AIF2:
+               mask_clk = RT5651_I2S_BCLK_MS2_MASK | RT5651_I2S_PD2_MASK;
+               val_clk = pre_div << RT5651_I2S_PD2_SFT;
+               snd_soc_update_bits(codec, RT5651_I2S2_SDP,
+                       RT5651_I2S_DL_MASK, val_len);
+               snd_soc_update_bits(codec, RT5651_ADDA_CLK1, mask_clk, val_clk);
+               break;
+       default:
+               dev_err(codec->dev, "Wrong dai->id: %d\n", dai->id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+       unsigned int reg_val = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               rt5651->master[dai->id] = 1;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               reg_val |= RT5651_I2S_MS_S;
+               rt5651->master[dai->id] = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               reg_val |= RT5651_I2S_BP_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               reg_val |= RT5651_I2S_DF_LEFT;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               reg_val |= RT5651_I2S_DF_PCM_A;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               reg_val |= RT5651_I2S_DF_PCM_B;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (dai->id) {
+       case RT5651_AIF1:
+               snd_soc_update_bits(codec, RT5651_I2S1_SDP,
+                       RT5651_I2S_MS_MASK | RT5651_I2S_BP_MASK |
+                       RT5651_I2S_DF_MASK, reg_val);
+               break;
+       case RT5651_AIF2:
+               snd_soc_update_bits(codec, RT5651_I2S2_SDP,
+                       RT5651_I2S_MS_MASK | RT5651_I2S_BP_MASK |
+                       RT5651_I2S_DF_MASK, reg_val);
+               break;
+       default:
+               dev_err(codec->dev, "Wrong dai->id: %d\n", dai->id);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+       unsigned int reg_val = 0;
+
+       if (freq == rt5651->sysclk && clk_id == rt5651->sysclk_src)
+               return 0;
+
+       switch (clk_id) {
+       case RT5651_SCLK_S_MCLK:
+               reg_val |= RT5651_SCLK_SRC_MCLK;
+               break;
+       case RT5651_SCLK_S_PLL1:
+               reg_val |= RT5651_SCLK_SRC_PLL1;
+               break;
+       case RT5651_SCLK_S_RCCLK:
+               reg_val |= RT5651_SCLK_SRC_RCCLK;
+               break;
+       default:
+               dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+               return -EINVAL;
+       }
+       snd_soc_update_bits(codec, RT5651_GLB_CLK,
+               RT5651_SCLK_SRC_MASK, reg_val);
+       rt5651->sysclk = freq;
+       rt5651->sysclk_src = clk_id;
+
+       dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+       return 0;
+}
+
+static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+                       unsigned int freq_in, unsigned int freq_out)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+       struct rl6231_pll_code pll_code;
+       int ret;
+
+       if (source == rt5651->pll_src && freq_in == rt5651->pll_in &&
+           freq_out == rt5651->pll_out)
+               return 0;
+
+       if (!freq_in || !freq_out) {
+               dev_dbg(codec->dev, "PLL disabled\n");
+
+               rt5651->pll_in = 0;
+               rt5651->pll_out = 0;
+               snd_soc_update_bits(codec, RT5651_GLB_CLK,
+                       RT5651_SCLK_SRC_MASK, RT5651_SCLK_SRC_MCLK);
+               return 0;
+       }
+
+       switch (source) {
+       case RT5651_PLL1_S_MCLK:
+               snd_soc_update_bits(codec, RT5651_GLB_CLK,
+                       RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_MCLK);
+               break;
+       case RT5651_PLL1_S_BCLK1:
+               snd_soc_update_bits(codec, RT5651_GLB_CLK,
+                               RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_BCLK1);
+               break;
+       case RT5651_PLL1_S_BCLK2:
+                       snd_soc_update_bits(codec, RT5651_GLB_CLK,
+                               RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_BCLK2);
+               break;
+       default:
+               dev_err(codec->dev, "Unknown PLL source %d\n", source);
+               return -EINVAL;
+       }
+
+       ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+       if (ret < 0) {
+               dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+               return ret;
+       }
+
+       dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+               pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+               pll_code.n_code, pll_code.k_code);
+
+       snd_soc_write(codec, RT5651_PLL_CTRL1,
+               pll_code.n_code << RT5651_PLL_N_SFT | pll_code.k_code);
+       snd_soc_write(codec, RT5651_PLL_CTRL2,
+               (pll_code.m_bp ? 0 : pll_code.m_code) << RT5651_PLL_M_SFT |
+               pll_code.m_bp << RT5651_PLL_M_BP_SFT);
+
+       rt5651->pll_in = freq_in;
+       rt5651->pll_out = freq_out;
+       rt5651->pll_src = source;
+
+       return 0;
+}
+
+static int rt5651_set_bias_level(struct snd_soc_codec *codec,
+                       enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_PREPARE:
+               if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
+                       snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
+                               RT5651_PWR_VREF1 | RT5651_PWR_MB |
+                               RT5651_PWR_BG | RT5651_PWR_VREF2,
+                               RT5651_PWR_VREF1 | RT5651_PWR_MB |
+                               RT5651_PWR_BG | RT5651_PWR_VREF2);
+                       usleep_range(10000, 15000);
+                       snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
+                               RT5651_PWR_FV1 | RT5651_PWR_FV2,
+                               RT5651_PWR_FV1 | RT5651_PWR_FV2);
+                       snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
+                               RT5651_PWR_LDO_DVO_MASK,
+                               RT5651_PWR_LDO_DVO_1_2V);
+                       snd_soc_update_bits(codec, RT5651_D_MISC, 0x1, 0x1);
+                       if (snd_soc_read(codec, RT5651_PLL_MODE_1) & 0x9200)
+                               snd_soc_update_bits(codec, RT5651_D_MISC,
+                                                   0xc00, 0xc00);
+               }
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               snd_soc_write(codec, RT5651_D_MISC, 0x0010);
+               snd_soc_write(codec, RT5651_PWR_DIG1, 0x0000);
+               snd_soc_write(codec, RT5651_PWR_DIG2, 0x0000);
+               snd_soc_write(codec, RT5651_PWR_VOL, 0x0000);
+               snd_soc_write(codec, RT5651_PWR_MIXER, 0x0000);
+               snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0000);
+               snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0000);
+               break;
+
+       default:
+               break;
+       }
+       codec->dapm.bias_level = level;
+
+       return 0;
+}
+
+static int rt5651_probe(struct snd_soc_codec *codec)
+{
+       struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+       rt5651->codec = codec;
+
+       snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
+               RT5651_PWR_VREF1 | RT5651_PWR_MB |
+               RT5651_PWR_BG | RT5651_PWR_VREF2,
+               RT5651_PWR_VREF1 | RT5651_PWR_MB |
+               RT5651_PWR_BG | RT5651_PWR_VREF2);
+       usleep_range(10000, 15000);
+       snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
+               RT5651_PWR_FV1 | RT5651_PWR_FV2,
+               RT5651_PWR_FV1 | RT5651_PWR_FV2);
+
+       rt5651_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt5651_suspend(struct snd_soc_codec *codec)
+{
+       struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+       regcache_cache_only(rt5651->regmap, true);
+       regcache_mark_dirty(rt5651->regmap);
+       return 0;
+}
+
+static int rt5651_resume(struct snd_soc_codec *codec)
+{
+       struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+
+       regcache_cache_only(rt5651->regmap, false);
+       snd_soc_cache_sync(codec);
+
+       return 0;
+}
+#else
+#define rt5651_suspend NULL
+#define rt5651_resume NULL
+#endif
+
+#define RT5651_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5651_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt5651_aif_dai_ops = {
+       .hw_params = rt5651_hw_params,
+       .set_fmt = rt5651_set_dai_fmt,
+       .set_sysclk = rt5651_set_dai_sysclk,
+       .set_pll = rt5651_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver rt5651_dai[] = {
+       {
+               .name = "rt5651-aif1",
+               .id = RT5651_AIF1,
+               .playback = {
+                       .stream_name = "AIF1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5651_STEREO_RATES,
+                       .formats = RT5651_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AIF1 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5651_STEREO_RATES,
+                       .formats = RT5651_FORMATS,
+               },
+               .ops = &rt5651_aif_dai_ops,
+       },
+       {
+               .name = "rt5651-aif2",
+               .id = RT5651_AIF2,
+               .playback = {
+                       .stream_name = "AIF2 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5651_STEREO_RATES,
+                       .formats = RT5651_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AIF2 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5651_STEREO_RATES,
+                       .formats = RT5651_FORMATS,
+               },
+               .ops = &rt5651_aif_dai_ops,
+       },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_rt5651 = {
+       .probe = rt5651_probe,
+       .suspend = rt5651_suspend,
+       .resume = rt5651_resume,
+       .set_bias_level = rt5651_set_bias_level,
+       .idle_bias_off = true,
+       .controls = rt5651_snd_controls,
+       .num_controls = ARRAY_SIZE(rt5651_snd_controls),
+       .dapm_widgets = rt5651_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(rt5651_dapm_widgets),
+       .dapm_routes = rt5651_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(rt5651_dapm_routes),
+};
+
+static const struct regmap_config rt5651_regmap = {
+       .reg_bits = 8,
+       .val_bits = 16,
+
+       .max_register = RT5651_DEVICE_ID + 1 + (ARRAY_SIZE(rt5651_ranges) *
+                                              RT5651_PR_SPACING),
+       .volatile_reg = rt5651_volatile_register,
+       .readable_reg = rt5651_readable_register,
+
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = rt5651_reg,
+       .num_reg_defaults = ARRAY_SIZE(rt5651_reg),
+       .ranges = rt5651_ranges,
+       .num_ranges = ARRAY_SIZE(rt5651_ranges),
+};
+
+static const struct i2c_device_id rt5651_i2c_id[] = {
+       { "rt5651", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id);
+
+static int rt5651_i2c_probe(struct i2c_client *i2c,
+                   const struct i2c_device_id *id)
+{
+       struct rt5651_platform_data *pdata = dev_get_platdata(&i2c->dev);
+       struct rt5651_priv *rt5651;
+       int ret;
+
+       rt5651 = devm_kzalloc(&i2c->dev, sizeof(*rt5651),
+                               GFP_KERNEL);
+       if (NULL == rt5651)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, rt5651);
+
+       if (pdata)
+               rt5651->pdata = *pdata;
+
+       rt5651->regmap = devm_regmap_init_i2c(i2c, &rt5651_regmap);
+       if (IS_ERR(rt5651->regmap)) {
+               ret = PTR_ERR(rt5651->regmap);
+               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       regmap_read(rt5651->regmap, RT5651_DEVICE_ID, &ret);
+       if (ret != RT5651_DEVICE_ID_VALUE) {
+               dev_err(&i2c->dev,
+                       "Device with ID register %x is not rt5651\n", ret);
+               return -ENODEV;
+       }
+
+       regmap_write(rt5651->regmap, RT5651_RESET, 0);
+
+       ret = regmap_register_patch(rt5651->regmap, init_list,
+                                   ARRAY_SIZE(init_list));
+       if (ret != 0)
+               dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+       if (rt5651->pdata.in2_diff)
+               regmap_update_bits(rt5651->regmap, RT5651_IN1_IN2,
+                                       RT5651_IN_DF2, RT5651_IN_DF2);
+
+       if (rt5651->pdata.dmic_en)
+               regmap_update_bits(rt5651->regmap, RT5651_GPIO_CTRL1,
+                               RT5651_GP2_PIN_MASK, RT5651_GP2_PIN_DMIC1_SCL);
+
+       rt5651->hp_mute = 1;
+
+       ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5651,
+                               rt5651_dai, ARRAY_SIZE(rt5651_dai));
+
+       return ret;
+}
+
+static int rt5651_i2c_remove(struct i2c_client *i2c)
+{
+       snd_soc_unregister_codec(&i2c->dev);
+
+       return 0;
+}
+
+static struct i2c_driver rt5651_i2c_driver = {
+       .driver = {
+               .name = "rt5651",
+               .owner = THIS_MODULE,
+       },
+       .probe = rt5651_i2c_probe,
+       .remove   = rt5651_i2c_remove,
+       .id_table = rt5651_i2c_id,
+};
+module_i2c_driver(rt5651_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5651 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h
new file mode 100644 (file)
index 0000000..1bd33cf
--- /dev/null
@@ -0,0 +1,2080 @@
+/*
+ * rt5651.h  --  RT5651 ALSA SoC audio driver
+ *
+ * Copyright 2011 Realtek Microelectronics
+ * Author: Johnny Hsu <johnnyhsu@realtek.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 __RT5651_H__
+#define __RT5651_H__
+
+#include <sound/rt5651.h>
+
+/* Info */
+#define RT5651_RESET                           0x00
+#define RT5651_VERSION_ID                      0xfd
+#define RT5651_VENDOR_ID                       0xfe
+#define RT5651_DEVICE_ID                       0xff
+/*  I/O - Output */
+#define RT5651_HP_VOL                          0x02
+#define RT5651_LOUT_CTRL1                      0x03
+#define RT5651_LOUT_CTRL2                      0x05
+/* I/O - Input */
+#define RT5651_IN1_IN2                         0x0d
+#define RT5651_IN3                             0x0e
+#define RT5651_INL1_INR1_VOL                   0x0f
+#define RT5651_INL2_INR2_VOL                   0x10
+/* I/O - ADC/DAC/DMIC */
+#define RT5651_DAC1_DIG_VOL                    0x19
+#define RT5651_DAC2_DIG_VOL                    0x1a
+#define RT5651_DAC2_CTRL                       0x1b
+#define RT5651_ADC_DIG_VOL                     0x1c
+#define RT5651_ADC_DATA                                0x1d
+#define RT5651_ADC_BST_VOL                     0x1e
+/* Mixer - D-D */
+#define RT5651_STO1_ADC_MIXER                  0x27
+#define RT5651_STO2_ADC_MIXER                  0x28
+#define RT5651_AD_DA_MIXER                     0x29
+#define RT5651_STO_DAC_MIXER                   0x2a
+#define RT5651_DD_MIXER                                0x2b
+#define RT5651_DIG_INF_DATA                    0x2f
+/* PDM */
+#define RT5651_PDM_CTL                         0x30
+#define RT5651_PDM_I2C_CTL1                    0x31
+#define RT5651_PDM_I2C_CTL2                    0x32
+#define RT5651_PDM_I2C_DATA_W                  0x33
+#define RT5651_PDM_I2C_DATA_R                  0x34
+/* Mixer - ADC */
+#define RT5651_REC_L1_MIXER                    0x3b
+#define RT5651_REC_L2_MIXER                    0x3c
+#define RT5651_REC_R1_MIXER                    0x3d
+#define RT5651_REC_R2_MIXER                    0x3e
+/* Mixer - DAC */
+#define RT5651_HPO_MIXER                       0x45
+#define RT5651_OUT_L1_MIXER                    0x4d
+#define RT5651_OUT_L2_MIXER                    0x4e
+#define RT5651_OUT_L3_MIXER                    0x4f
+#define RT5651_OUT_R1_MIXER                    0x50
+#define RT5651_OUT_R2_MIXER                    0x51
+#define RT5651_OUT_R3_MIXER                    0x52
+#define RT5651_LOUT_MIXER                      0x53
+/* Power */
+#define RT5651_PWR_DIG1                                0x61
+#define RT5651_PWR_DIG2                                0x62
+#define RT5651_PWR_ANLG1                       0x63
+#define RT5651_PWR_ANLG2                       0x64
+#define RT5651_PWR_MIXER                       0x65
+#define RT5651_PWR_VOL                         0x66
+/* Private Register Control */
+#define RT5651_PRIV_INDEX                      0x6a
+#define RT5651_PRIV_DATA                       0x6c
+/* Format - ADC/DAC */
+#define RT5651_I2S1_SDP                                0x70
+#define RT5651_I2S2_SDP                                0x71
+#define RT5651_ADDA_CLK1                       0x73
+#define RT5651_ADDA_CLK2                       0x74
+#define RT5651_DMIC                            0x75
+/* TDM Control */
+#define RT5651_TDM_CTL_1                       0x77
+#define RT5651_TDM_CTL_2                       0x78
+#define RT5651_TDM_CTL_3                       0x79
+/* Function - Analog */
+#define RT5651_GLB_CLK                         0x80
+#define RT5651_PLL_CTRL1                       0x81
+#define RT5651_PLL_CTRL2                       0x82
+#define RT5651_PLL_MODE_1                      0x83
+#define RT5651_PLL_MODE_2                      0x84
+#define RT5651_PLL_MODE_3                      0x85
+#define RT5651_PLL_MODE_4                      0x86
+#define RT5651_PLL_MODE_5                      0x87
+#define RT5651_PLL_MODE_6                      0x89
+#define RT5651_PLL_MODE_7                      0x8a
+#define RT5651_DEPOP_M1                                0x8e
+#define RT5651_DEPOP_M2                                0x8f
+#define RT5651_DEPOP_M3                                0x90
+#define RT5651_CHARGE_PUMP                     0x91
+#define RT5651_MICBIAS                         0x93
+#define RT5651_A_JD_CTL1                       0x94
+/* Function - Digital */
+#define RT5651_EQ_CTRL1                                0xb0
+#define RT5651_EQ_CTRL2                                0xb1
+#define RT5651_ALC_1                           0xb4
+#define RT5651_ALC_2                           0xb5
+#define RT5651_ALC_3                           0xb6
+#define RT5651_JD_CTRL1                                0xbb
+#define RT5651_JD_CTRL2                                0xbc
+#define RT5651_IRQ_CTRL1                       0xbd
+#define RT5651_IRQ_CTRL2                       0xbe
+#define RT5651_INT_IRQ_ST                      0xbf
+#define RT5651_GPIO_CTRL1                      0xc0
+#define RT5651_GPIO_CTRL2                      0xc1
+#define RT5651_GPIO_CTRL3                      0xc2
+#define RT5651_PGM_REG_ARR1                    0xc8
+#define RT5651_PGM_REG_ARR2                    0xc9
+#define RT5651_PGM_REG_ARR3                    0xca
+#define RT5651_PGM_REG_ARR4                    0xcb
+#define RT5651_PGM_REG_ARR5                    0xcc
+#define RT5651_SCB_FUNC                                0xcd
+#define RT5651_SCB_CTRL                                0xce
+#define RT5651_BASE_BACK                       0xcf
+#define RT5651_MP3_PLUS1                       0xd0
+#define RT5651_MP3_PLUS2                       0xd1
+#define RT5651_ADJ_HPF_CTRL1                   0xd3
+#define RT5651_ADJ_HPF_CTRL2                   0xd4
+#define RT5651_HP_CALIB_AMP_DET                        0xd6
+#define RT5651_HP_CALIB2                       0xd7
+#define RT5651_SV_ZCD1                         0xd9
+#define RT5651_SV_ZCD2                         0xda
+#define RT5651_D_MISC                          0xfa
+/* Dummy Register */
+#define RT5651_DUMMY2                          0xfb
+#define RT5651_DUMMY3                          0xfc
+
+
+/* Index of Codec Private Register definition */
+#define RT5651_BIAS_CUR1                       0x12
+#define RT5651_BIAS_CUR3                       0x14
+#define RT5651_CLSD_INT_REG1                   0x1c
+#define RT5651_CHPUMP_INT_REG1                 0x24
+#define RT5651_MAMP_INT_REG2                   0x37
+#define RT5651_CHOP_DAC_ADC                    0x3d
+#define RT5651_3D_SPK                          0x63
+#define RT5651_WND_1                           0x6c
+#define RT5651_WND_2                           0x6d
+#define RT5651_WND_3                           0x6e
+#define RT5651_WND_4                           0x6f
+#define RT5651_WND_5                           0x70
+#define RT5651_WND_8                           0x73
+#define RT5651_DIP_SPK_INF                     0x75
+#define RT5651_HP_DCC_INT1                     0x77
+#define RT5651_EQ_BW_LOP                       0xa0
+#define RT5651_EQ_GN_LOP                       0xa1
+#define RT5651_EQ_FC_BP1                       0xa2
+#define RT5651_EQ_BW_BP1                       0xa3
+#define RT5651_EQ_GN_BP1                       0xa4
+#define RT5651_EQ_FC_BP2                       0xa5
+#define RT5651_EQ_BW_BP2                       0xa6
+#define RT5651_EQ_GN_BP2                       0xa7
+#define RT5651_EQ_FC_BP3                       0xa8
+#define RT5651_EQ_BW_BP3                       0xa9
+#define RT5651_EQ_GN_BP3                       0xaa
+#define RT5651_EQ_FC_BP4                       0xab
+#define RT5651_EQ_BW_BP4                       0xac
+#define RT5651_EQ_GN_BP4                       0xad
+#define RT5651_EQ_FC_HIP1                      0xae
+#define RT5651_EQ_GN_HIP1                      0xaf
+#define RT5651_EQ_FC_HIP2                      0xb0
+#define RT5651_EQ_BW_HIP2                      0xb1
+#define RT5651_EQ_GN_HIP2                      0xb2
+#define RT5651_EQ_PRE_VOL                      0xb3
+#define RT5651_EQ_PST_VOL                      0xb4
+
+
+/* global definition */
+#define RT5651_L_MUTE                          (0x1 << 15)
+#define RT5651_L_MUTE_SFT                      15
+#define RT5651_VOL_L_MUTE                      (0x1 << 14)
+#define RT5651_VOL_L_SFT                       14
+#define RT5651_R_MUTE                          (0x1 << 7)
+#define RT5651_R_MUTE_SFT                      7
+#define RT5651_VOL_R_MUTE                      (0x1 << 6)
+#define RT5651_VOL_R_SFT                       6
+#define RT5651_L_VOL_MASK                      (0x3f << 8)
+#define RT5651_L_VOL_SFT                       8
+#define RT5651_R_VOL_MASK                      (0x3f)
+#define RT5651_R_VOL_SFT                       0
+
+/* LOUT Control 2(0x05) */
+#define RT5651_EN_DFO                          (0x1 << 15)
+
+/* IN1 and IN2 Control (0x0d) */
+/* IN3 and IN4 Control (0x0e) */
+#define RT5651_BST_MASK1                       (0xf<<12)
+#define RT5651_BST_SFT1                                12
+#define RT5651_BST_MASK2                       (0xf<<8)
+#define RT5651_BST_SFT2                                8
+#define RT5651_IN_DF1                          (0x1 << 7)
+#define RT5651_IN_SFT1                         7
+#define RT5651_IN_DF2                          (0x1 << 6)
+#define RT5651_IN_SFT2                         6
+
+/* INL1 and INR1 Volume Control (0x0f) */
+/* INL2 and INR2 Volume Control (0x10) */
+#define RT5651_INL_SEL_MASK                    (0x1 << 15)
+#define RT5651_INL_SEL_SFT                     15
+#define RT5651_INL_SEL_IN4P                    (0x0 << 15)
+#define RT5651_INL_SEL_MONOP                   (0x1 << 15)
+#define RT5651_INL_VOL_MASK                    (0x1f << 8)
+#define RT5651_INL_VOL_SFT                     8
+#define RT5651_INR_SEL_MASK                    (0x1 << 7)
+#define RT5651_INR_SEL_SFT                     7
+#define RT5651_INR_SEL_IN4N                    (0x0 << 7)
+#define RT5651_INR_SEL_MONON                   (0x1 << 7)
+#define RT5651_INR_VOL_MASK                    (0x1f)
+#define RT5651_INR_VOL_SFT                     0
+
+/* DAC1 Digital Volume (0x19) */
+#define RT5651_DAC_L1_VOL_MASK                 (0xff << 8)
+#define RT5651_DAC_L1_VOL_SFT                  8
+#define RT5651_DAC_R1_VOL_MASK                 (0xff)
+#define RT5651_DAC_R1_VOL_SFT                  0
+
+/* DAC2 Digital Volume (0x1a) */
+#define RT5651_DAC_L2_VOL_MASK                 (0xff << 8)
+#define RT5651_DAC_L2_VOL_SFT                  8
+#define RT5651_DAC_R2_VOL_MASK                 (0xff)
+#define RT5651_DAC_R2_VOL_SFT                  0
+
+/* DAC2 Control (0x1b) */
+#define RT5651_M_DAC_L2_VOL                    (0x1 << 13)
+#define RT5651_M_DAC_L2_VOL_SFT                        13
+#define RT5651_M_DAC_R2_VOL                    (0x1 << 12)
+#define RT5651_M_DAC_R2_VOL_SFT                        12
+#define RT5651_SEL_DAC_L2                      (0x1 << 11)
+#define RT5651_IF2_DAC_L2                      (0x1 << 11)
+#define RT5651_IF1_DAC_L2                      (0x0 << 11)
+#define RT5651_SEL_DAC_L2_SFT                  11
+#define RT5651_SEL_DAC_R2                      (0x1 << 10)
+#define RT5651_IF2_DAC_R2                      (0x1 << 11)
+#define RT5651_IF1_DAC_R2                      (0x0 << 11)
+#define RT5651_SEL_DAC_R2_SFT                  10
+
+/* ADC Digital Volume Control (0x1c) */
+#define RT5651_ADC_L_VOL_MASK                  (0x7f << 8)
+#define RT5651_ADC_L_VOL_SFT                   8
+#define RT5651_ADC_R_VOL_MASK                  (0x7f)
+#define RT5651_ADC_R_VOL_SFT                   0
+
+/* Mono ADC Digital Volume Control (0x1d) */
+#define RT5651_M_MONO_ADC_L                    (0x1 << 15)
+#define RT5651_M_MONO_ADC_L_SFT                        15
+#define RT5651_MONO_ADC_L_VOL_MASK             (0x7f << 8)
+#define RT5651_MONO_ADC_L_VOL_SFT              8
+#define RT5651_M_MONO_ADC_R                    (0x1 << 7)
+#define RT5651_M_MONO_ADC_R_SFT                        7
+#define RT5651_MONO_ADC_R_VOL_MASK             (0x7f)
+#define RT5651_MONO_ADC_R_VOL_SFT              0
+
+/* ADC Boost Volume Control (0x1e) */
+#define RT5651_ADC_L_BST_MASK                  (0x3 << 14)
+#define RT5651_ADC_L_BST_SFT                   14
+#define RT5651_ADC_R_BST_MASK                  (0x3 << 12)
+#define RT5651_ADC_R_BST_SFT                   12
+#define RT5651_ADC_COMP_MASK                   (0x3 << 10)
+#define RT5651_ADC_COMP_SFT                    10
+
+/* Stereo ADC1 Mixer Control (0x27) */
+#define RT5651_M_STO1_ADC_L1                   (0x1 << 14)
+#define RT5651_M_STO1_ADC_L1_SFT               14
+#define RT5651_M_STO1_ADC_L2                   (0x1 << 13)
+#define RT5651_M_STO1_ADC_L2_SFT               13
+#define RT5651_STO1_ADC_1_SRC_MASK             (0x1 << 12)
+#define RT5651_STO1_ADC_1_SRC_SFT              12
+#define RT5651_STO1_ADC_1_SRC_ADC              (0x1 << 12)
+#define RT5651_STO1_ADC_1_SRC_DACMIX           (0x0 << 12)
+#define RT5651_STO1_ADC_2_SRC_MASK             (0x1 << 11)
+#define RT5651_STO1_ADC_2_SRC_SFT              11
+#define RT5651_STO1_ADC_2_SRC_DMIC             (0x0 << 11)
+#define RT5651_STO1_ADC_2_SRC_DACMIXR  (0x1 << 11)
+#define RT5651_M_STO1_ADC_R1                   (0x1 << 6)
+#define RT5651_M_STO1_ADC_R1_SFT               6
+#define RT5651_M_STO1_ADC_R2                   (0x1 << 5)
+#define RT5651_M_STO1_ADC_R2_SFT               5
+
+/* Stereo ADC2 Mixer Control (0x28) */
+#define RT5651_M_STO2_ADC_L1                   (0x1 << 14)
+#define RT5651_M_STO2_ADC_L1_SFT               14
+#define RT5651_M_STO2_ADC_L2                   (0x1 << 13)
+#define RT5651_M_STO2_ADC_L2_SFT               13
+#define RT5651_STO2_ADC_L1_SRC_MASK            (0x1 << 12)
+#define RT5651_STO2_ADC_L1_SRC_SFT             12
+#define RT5651_STO2_ADC_L1_SRC_DACMIXL         (0x0 << 12)
+#define RT5651_STO2_ADC_L1_SRC_ADCL            (0x1 << 12)
+#define RT5651_STO2_ADC_L2_SRC_MASK            (0x1 << 11)
+#define RT5651_STO2_ADC_L2_SRC_SFT             11
+#define RT5651_STO2_ADC_L2_SRC_DMIC            (0x0 << 11)
+#define RT5651_STO2_ADC_L2_SRC_DACMIXR         (0x1 << 11)
+#define RT5651_M_STO2_ADC_R1                   (0x1 << 6)
+#define RT5651_M_STO2_ADC_R1_SFT               6
+#define RT5651_M_STO2_ADC_R2                   (0x1 << 5)
+#define RT5651_M_STO2_ADC_R2_SFT               5
+#define RT5651_STO2_ADC_R1_SRC_MASK            (0x1 << 4)
+#define RT5651_STO2_ADC_R1_SRC_SFT             4
+#define RT5651_STO2_ADC_R1_SRC_ADCR            (0x1 << 4)
+#define RT5651_STO2_ADC_R1_SRC_DACMIXR         (0x0 << 4)
+#define RT5651_STO2_ADC_R2_SRC_MASK            (0x1 << 3)
+#define RT5651_STO2_ADC_R2_SRC_SFT             3
+#define RT5651_STO2_ADC_R2_SRC_DMIC            (0x0 << 3)
+#define RT5651_STO2_ADC_R2_SRC_DACMIXR         (0x1 << 3)
+
+/* ADC Mixer to DAC Mixer Control (0x29) */
+#define RT5651_M_ADCMIX_L                      (0x1 << 15)
+#define RT5651_M_ADCMIX_L_SFT                  15
+#define RT5651_M_IF1_DAC_L                     (0x1 << 14)
+#define RT5651_M_IF1_DAC_L_SFT                 14
+#define RT5651_M_ADCMIX_R                      (0x1 << 7)
+#define RT5651_M_ADCMIX_R_SFT                  7
+#define RT5651_M_IF1_DAC_R                     (0x1 << 6)
+#define RT5651_M_IF1_DAC_R_SFT                 6
+
+/* Stereo DAC Mixer Control (0x2a) */
+#define RT5651_M_DAC_L1_MIXL                   (0x1 << 14)
+#define RT5651_M_DAC_L1_MIXL_SFT               14
+#define RT5651_DAC_L1_STO_L_VOL_MASK           (0x1 << 13)
+#define RT5651_DAC_L1_STO_L_VOL_SFT            13
+#define RT5651_M_DAC_L2_MIXL                   (0x1 << 12)
+#define RT5651_M_DAC_L2_MIXL_SFT               12
+#define RT5651_DAC_L2_STO_L_VOL_MASK           (0x1 << 11)
+#define RT5651_DAC_L2_STO_L_VOL_SFT            11
+#define RT5651_M_DAC_R1_MIXL                   (0x1 << 9)
+#define RT5651_M_DAC_R1_MIXL_SFT               9
+#define RT5651_DAC_R1_STO_L_VOL_MASK           (0x1 << 8)
+#define RT5651_DAC_R1_STO_L_VOL_SFT            8
+#define RT5651_M_DAC_R1_MIXR                   (0x1 << 6)
+#define RT5651_M_DAC_R1_MIXR_SFT               6
+#define RT5651_DAC_R1_STO_R_VOL_MASK           (0x1 << 5)
+#define RT5651_DAC_R1_STO_R_VOL_SFT            5
+#define RT5651_M_DAC_R2_MIXR                   (0x1 << 4)
+#define RT5651_M_DAC_R2_MIXR_SFT               4
+#define RT5651_DAC_R2_STO_R_VOL_MASK           (0x1 << 3)
+#define RT5651_DAC_R2_STO_R_VOL_SFT            3
+#define RT5651_M_DAC_L1_MIXR                   (0x1 << 1)
+#define RT5651_M_DAC_L1_MIXR_SFT               1
+#define RT5651_DAC_L1_STO_R_VOL_MASK           (0x1)
+#define RT5651_DAC_L1_STO_R_VOL_SFT            0
+
+/* DD Mixer Control (0x2b) */
+#define RT5651_M_STO_DD_L1                     (0x1 << 14)
+#define RT5651_M_STO_DD_L1_SFT                 14
+#define RT5651_STO_DD_L1_VOL_MASK              (0x1 << 13)
+#define RT5651_DAC_DD_L1_VOL_SFT               13
+#define RT5651_M_STO_DD_L2                     (0x1 << 12)
+#define RT5651_M_STO_DD_L2_SFT                 12
+#define RT5651_STO_DD_L2_VOL_MASK              (0x1 << 11)
+#define RT5651_STO_DD_L2_VOL_SFT               11
+#define RT5651_M_STO_DD_R2_L                   (0x1 << 10)
+#define RT5651_M_STO_DD_R2_L_SFT               10
+#define RT5651_STO_DD_R2_L_VOL_MASK            (0x1 << 9)
+#define RT5651_STO_DD_R2_L_VOL_SFT             9
+#define RT5651_M_STO_DD_R1                     (0x1 << 6)
+#define RT5651_M_STO_DD_R1_SFT                 6
+#define RT5651_STO_DD_R1_VOL_MASK              (0x1 << 5)
+#define RT5651_STO_DD_R1_VOL_SFT               5
+#define RT5651_M_STO_DD_R2                     (0x1 << 4)
+#define RT5651_M_STO_DD_R2_SFT                 4
+#define RT5651_STO_DD_R2_VOL_MASK              (0x1 << 3)
+#define RT5651_STO_DD_R2_VOL_SFT               3
+#define RT5651_M_STO_DD_L2_R                   (0x1 << 2)
+#define RT5651_M_STO_DD_L2_R_SFT               2
+#define RT5651_STO_DD_L2_R_VOL_MASK            (0x1 << 1)
+#define RT5651_STO_DD_L2_R_VOL_SFT             1
+
+/* Digital Mixer Control (0x2c) */
+#define RT5651_M_STO_L_DAC_L                   (0x1 << 15)
+#define RT5651_M_STO_L_DAC_L_SFT               15
+#define RT5651_STO_L_DAC_L_VOL_MASK            (0x1 << 14)
+#define RT5651_STO_L_DAC_L_VOL_SFT             14
+#define RT5651_M_DAC_L2_DAC_L                  (0x1 << 13)
+#define RT5651_M_DAC_L2_DAC_L_SFT              13
+#define RT5651_DAC_L2_DAC_L_VOL_MASK           (0x1 << 12)
+#define RT5651_DAC_L2_DAC_L_VOL_SFT            12
+#define RT5651_M_STO_R_DAC_R                   (0x1 << 11)
+#define RT5651_M_STO_R_DAC_R_SFT               11
+#define RT5651_STO_R_DAC_R_VOL_MASK            (0x1 << 10)
+#define RT5651_STO_R_DAC_R_VOL_SFT             10
+#define RT5651_M_DAC_R2_DAC_R                  (0x1 << 9)
+#define RT5651_M_DAC_R2_DAC_R_SFT              9
+#define RT5651_DAC_R2_DAC_R_VOL_MASK           (0x1 << 8)
+#define RT5651_DAC_R2_DAC_R_VOL_SFT            8
+
+/* DSP Path Control 1 (0x2d) */
+#define RT5651_RXDP_SRC_MASK                   (0x1 << 15)
+#define RT5651_RXDP_SRC_SFT                    15
+#define RT5651_RXDP_SRC_NOR                    (0x0 << 15)
+#define RT5651_RXDP_SRC_DIV3                   (0x1 << 15)
+#define RT5651_TXDP_SRC_MASK                   (0x1 << 14)
+#define RT5651_TXDP_SRC_SFT                    14
+#define RT5651_TXDP_SRC_NOR                    (0x0 << 14)
+#define RT5651_TXDP_SRC_DIV3                   (0x1 << 14)
+
+/* DSP Path Control 2 (0x2e) */
+#define RT5651_DAC_L2_SEL_MASK                 (0x3 << 14)
+#define RT5651_DAC_L2_SEL_SFT                  14
+#define RT5651_DAC_L2_SEL_IF2                  (0x0 << 14)
+#define RT5651_DAC_L2_SEL_IF3                  (0x1 << 14)
+#define RT5651_DAC_L2_SEL_TXDC                 (0x2 << 14)
+#define RT5651_DAC_L2_SEL_BASS                 (0x3 << 14)
+#define RT5651_DAC_R2_SEL_MASK                 (0x3 << 12)
+#define RT5651_DAC_R2_SEL_SFT                  12
+#define RT5651_DAC_R2_SEL_IF2                  (0x0 << 12)
+#define RT5651_DAC_R2_SEL_IF3                  (0x1 << 12)
+#define RT5651_DAC_R2_SEL_TXDC                 (0x2 << 12)
+#define RT5651_IF2_ADC_L_SEL_MASK              (0x1 << 11)
+#define RT5651_IF2_ADC_L_SEL_SFT               11
+#define RT5651_IF2_ADC_L_SEL_TXDP              (0x0 << 11)
+#define RT5651_IF2_ADC_L_SEL_PASS              (0x1 << 11)
+#define RT5651_IF2_ADC_R_SEL_MASK              (0x1 << 10)
+#define RT5651_IF2_ADC_R_SEL_SFT               10
+#define RT5651_IF2_ADC_R_SEL_TXDP              (0x0 << 10)
+#define RT5651_IF2_ADC_R_SEL_PASS              (0x1 << 10)
+#define RT5651_RXDC_SEL_MASK                   (0x3 << 8)
+#define RT5651_RXDC_SEL_SFT                    8
+#define RT5651_RXDC_SEL_NOR                    (0x0 << 8)
+#define RT5651_RXDC_SEL_L2R                    (0x1 << 8)
+#define RT5651_RXDC_SEL_R2L                    (0x2 << 8)
+#define RT5651_RXDC_SEL_SWAP                   (0x3 << 8)
+#define RT5651_RXDP_SEL_MASK                   (0x3 << 6)
+#define RT5651_RXDP_SEL_SFT                    6
+#define RT5651_RXDP_SEL_NOR                    (0x0 << 6)
+#define RT5651_RXDP_SEL_L2R                    (0x1 << 6)
+#define RT5651_RXDP_SEL_R2L                    (0x2 << 6)
+#define RT5651_RXDP_SEL_SWAP                   (0x3 << 6)
+#define RT5651_TXDC_SEL_MASK                   (0x3 << 4)
+#define RT5651_TXDC_SEL_SFT                    4
+#define RT5651_TXDC_SEL_NOR                    (0x0 << 4)
+#define RT5651_TXDC_SEL_L2R                    (0x1 << 4)
+#define RT5651_TXDC_SEL_R2L                    (0x2 << 4)
+#define RT5651_TXDC_SEL_SWAP                   (0x3 << 4)
+#define RT5651_TXDP_SEL_MASK                   (0x3 << 2)
+#define RT5651_TXDP_SEL_SFT                    2
+#define RT5651_TXDP_SEL_NOR                    (0x0 << 2)
+#define RT5651_TXDP_SEL_L2R                    (0x1 << 2)
+#define RT5651_TXDP_SEL_R2L                    (0x2 << 2)
+#define RT5651_TRXDP_SEL_SWAP                  (0x3 << 2)
+
+/* Digital Interface Data Control (0x2f) */
+#define RT5651_IF2_DAC_SEL_MASK                        (0x3 << 10)
+#define RT5651_IF2_DAC_SEL_SFT                 10
+#define RT5651_IF2_DAC_SEL_NOR                 (0x0 << 10)
+#define RT5651_IF2_DAC_SEL_SWAP                        (0x1 << 10)
+#define RT5651_IF2_DAC_SEL_L2R                 (0x2 << 10)
+#define RT5651_IF2_DAC_SEL_R2L                 (0x3 << 10)
+#define RT5651_IF2_ADC_SEL_MASK                        (0x3 << 8)
+#define RT5651_IF2_ADC_SEL_SFT                 8
+#define RT5651_IF2_ADC_SEL_NOR                 (0x0 << 8)
+#define RT5651_IF2_ADC_SEL_SWAP                        (0x1 << 8)
+#define RT5651_IF2_ADC_SEL_L2R                 (0x2 << 8)
+#define RT5651_IF2_ADC_SEL_R2L                 (0x3 << 8)
+#define RT5651_IF2_ADC_SRC_MASK                        (0x1 << 7)
+#define RT5651_IF2_ADC_SRC_SFT                 7
+#define RT5651_IF1_ADC1                                (0x0 << 7)
+#define RT5651_IF1_ADC2                                (0x1 << 7)
+
+/* PDM Output Control (0x30) */
+#define RT5651_PDM_L_SEL_MASK                  (0x1 << 15)
+#define RT5651_PDM_L_SEL_SFT                   15
+#define RT5651_PDM_L_SEL_DD_L                  (0x0 << 15)
+#define RT5651_PDM_L_SEL_STO_L                 (0x1 << 15)
+#define RT5651_M_PDM_L                         (0x1 << 14)
+#define RT5651_M_PDM_L_SFT                     14
+#define RT5651_PDM_R_SEL_MASK                  (0x1 << 13)
+#define RT5651_PDM_R_SEL_SFT                   13
+#define RT5651_PDM_R_SEL_DD_L                  (0x0 << 13)
+#define RT5651_PDM_R_SEL_STO_L                 (0x1 << 13)
+#define RT5651_M_PDM_R                         (0x1 << 12)
+#define RT5651_M_PDM_R_SFT                     12
+#define RT5651_PDM_BUSY                                (0x1 << 6)
+#define RT5651_PDM_BUSY_SFT                    6
+#define RT5651_PDM_PATTERN_SEL_MASK            (0x1 << 5)
+#define RT5651_PDM_PATTERN_SEL_64              (0x0 << 5)
+#define RT5651_PDM_PATTERN_SEL_128             (0x1 << 5)
+#define RT5651_PDM_VOL_MASK                    (0x1 << 4)
+#define RT5651_PDM_VOL_SFT                     4
+#define RT5651_PDM_DIV_MASK                    (0x3)
+#define RT5651_PDM_DIV_SFT                     0
+#define RT5651_PDM_DIV_1                       0
+#define RT5651_PDM_DIV_2                       1
+#define RT5651_PDM_DIV_3                       2
+#define RT5651_PDM_DIV_4                       3
+
+/* PDM I2C/Data Control 1 (0x31) */
+#define RT5651_PDM_I2C_ID_MASK                 (0xf << 12)
+#define PT5631_PDM_CMD_EXE                     (0x1 << 11)
+#define RT5651_PDM_I2C_CMD_MASK                        (0x1 << 10)
+#define RT5651_PDM_I2C_CMD_R                   (0x0 << 10)
+#define RT5651_PDM_I2C_CMD_W                   (0x1 << 10)
+#define RT5651_PDM_I2C_CMD_EXE                 (0x1 << 9)
+#define RT5651_PDM_I2C_NORMAL                  (0x0 << 8)
+#define RT5651_PDM_I2C_BUSY                    (0x1 << 8)
+
+/* PDM I2C/Data Control 2 (0x32) */
+#define RT5651_PDM_I2C_ADDR                    (0xff << 8)
+#define RT5651_PDM_I2C_CMD_PATTERN             (0xff)
+
+
+/* REC Left Mixer Control 1 (0x3b) */
+#define RT5651_G_LN_L2_RM_L_MASK               (0x7 << 13)
+#define RT5651_G_IN_L2_RM_L_SFT                        13
+#define RT5651_G_LN_L1_RM_L_MASK               (0x7 << 10)
+#define RT5651_G_IN_L1_RM_L_SFT                        10
+#define RT5651_G_BST3_RM_L_MASK                        (0x7 << 4)
+#define RT5651_G_BST3_RM_L_SFT                 4
+#define RT5651_G_BST2_RM_L_MASK                        (0x7 << 1)
+#define RT5651_G_BST2_RM_L_SFT                 1
+
+/* REC Left Mixer Control 2 (0x3c) */
+#define RT5651_G_BST1_RM_L_MASK                        (0x7 << 13)
+#define RT5651_G_BST1_RM_L_SFT                 13
+#define RT5651_G_OM_L_RM_L_MASK                        (0x7 << 10)
+#define RT5651_G_OM_L_RM_L_SFT                 10
+#define RT5651_M_IN2_L_RM_L                    (0x1 << 6)
+#define RT5651_M_IN2_L_RM_L_SFT                        6
+#define RT5651_M_IN1_L_RM_L                    (0x1 << 5)
+#define RT5651_M_IN1_L_RM_L_SFT                        5
+#define RT5651_M_BST3_RM_L                     (0x1 << 3)
+#define RT5651_M_BST3_RM_L_SFT                 3
+#define RT5651_M_BST2_RM_L                     (0x1 << 2)
+#define RT5651_M_BST2_RM_L_SFT                 2
+#define RT5651_M_BST1_RM_L                     (0x1 << 1)
+#define RT5651_M_BST1_RM_L_SFT                 1
+#define RT5651_M_OM_L_RM_L                     (0x1)
+#define RT5651_M_OM_L_RM_L_SFT                 0
+
+/* REC Right Mixer Control 1 (0x3d) */
+#define RT5651_G_IN2_R_RM_R_MASK               (0x7 << 13)
+#define RT5651_G_IN2_R_RM_R_SFT                        13
+#define RT5651_G_IN1_R_RM_R_MASK               (0x7 << 10)
+#define RT5651_G_IN1_R_RM_R_SFT                        10
+#define RT5651_G_BST3_RM_R_MASK                        (0x7 << 4)
+#define RT5651_G_BST3_RM_R_SFT                 4
+#define RT5651_G_BST2_RM_R_MASK                        (0x7 << 1)
+#define RT5651_G_BST2_RM_R_SFT                 1
+
+/* REC Right Mixer Control 2 (0x3e) */
+#define RT5651_G_BST1_RM_R_MASK                        (0x7 << 13)
+#define RT5651_G_BST1_RM_R_SFT                 13
+#define RT5651_G_OM_R_RM_R_MASK                        (0x7 << 10)
+#define RT5651_G_OM_R_RM_R_SFT                 10
+#define RT5651_M_IN2_R_RM_R                    (0x1 << 6)
+#define RT5651_M_IN2_R_RM_R_SFT                        6
+#define RT5651_M_IN1_R_RM_R                    (0x1 << 5)
+#define RT5651_M_IN1_R_RM_R_SFT                        5
+#define RT5651_M_BST3_RM_R                     (0x1 << 3)
+#define RT5651_M_BST3_RM_R_SFT                 3
+#define RT5651_M_BST2_RM_R                     (0x1 << 2)
+#define RT5651_M_BST2_RM_R_SFT                 2
+#define RT5651_M_BST1_RM_R                     (0x1 << 1)
+#define RT5651_M_BST1_RM_R_SFT                 1
+#define RT5651_M_OM_R_RM_R                     (0x1)
+#define RT5651_M_OM_R_RM_R_SFT                 0
+
+/* HPMIX Control (0x45) */
+#define RT5651_M_DAC1_HM                       (0x1 << 14)
+#define RT5651_M_DAC1_HM_SFT                   14
+#define RT5651_M_HPVOL_HM                      (0x1 << 13)
+#define RT5651_M_HPVOL_HM_SFT                  13
+#define RT5651_G_HPOMIX_MASK                   (0x1 << 12)
+#define RT5651_G_HPOMIX_SFT                    12
+
+/* SPK Left Mixer Control (0x46) */
+#define RT5651_G_RM_L_SM_L_MASK                        (0x3 << 14)
+#define RT5651_G_RM_L_SM_L_SFT                 14
+#define RT5651_G_IN_L_SM_L_MASK                        (0x3 << 12)
+#define RT5651_G_IN_L_SM_L_SFT                 12
+#define RT5651_G_DAC_L1_SM_L_MASK              (0x3 << 10)
+#define RT5651_G_DAC_L1_SM_L_SFT               10
+#define RT5651_G_DAC_L2_SM_L_MASK              (0x3 << 8)
+#define RT5651_G_DAC_L2_SM_L_SFT               8
+#define RT5651_G_OM_L_SM_L_MASK                        (0x3 << 6)
+#define RT5651_G_OM_L_SM_L_SFT                 6
+#define RT5651_M_RM_L_SM_L                     (0x1 << 5)
+#define RT5651_M_RM_L_SM_L_SFT                 5
+#define RT5651_M_IN_L_SM_L                     (0x1 << 4)
+#define RT5651_M_IN_L_SM_L_SFT                 4
+#define RT5651_M_DAC_L1_SM_L                   (0x1 << 3)
+#define RT5651_M_DAC_L1_SM_L_SFT               3
+#define RT5651_M_DAC_L2_SM_L                   (0x1 << 2)
+#define RT5651_M_DAC_L2_SM_L_SFT               2
+#define RT5651_M_OM_L_SM_L                     (0x1 << 1)
+#define RT5651_M_OM_L_SM_L_SFT                 1
+
+/* SPK Right Mixer Control (0x47) */
+#define RT5651_G_RM_R_SM_R_MASK                        (0x3 << 14)
+#define RT5651_G_RM_R_SM_R_SFT                 14
+#define RT5651_G_IN_R_SM_R_MASK                        (0x3 << 12)
+#define RT5651_G_IN_R_SM_R_SFT                 12
+#define RT5651_G_DAC_R1_SM_R_MASK              (0x3 << 10)
+#define RT5651_G_DAC_R1_SM_R_SFT               10
+#define RT5651_G_DAC_R2_SM_R_MASK              (0x3 << 8)
+#define RT5651_G_DAC_R2_SM_R_SFT               8
+#define RT5651_G_OM_R_SM_R_MASK                        (0x3 << 6)
+#define RT5651_G_OM_R_SM_R_SFT                 6
+#define RT5651_M_RM_R_SM_R                     (0x1 << 5)
+#define RT5651_M_RM_R_SM_R_SFT                 5
+#define RT5651_M_IN_R_SM_R                     (0x1 << 4)
+#define RT5651_M_IN_R_SM_R_SFT                 4
+#define RT5651_M_DAC_R1_SM_R                   (0x1 << 3)
+#define RT5651_M_DAC_R1_SM_R_SFT               3
+#define RT5651_M_DAC_R2_SM_R                   (0x1 << 2)
+#define RT5651_M_DAC_R2_SM_R_SFT               2
+#define RT5651_M_OM_R_SM_R                     (0x1 << 1)
+#define RT5651_M_OM_R_SM_R_SFT                 1
+
+/* SPOLMIX Control (0x48) */
+#define RT5651_M_DAC_R1_SPM_L                  (0x1 << 15)
+#define RT5651_M_DAC_R1_SPM_L_SFT              15
+#define RT5651_M_DAC_L1_SPM_L                  (0x1 << 14)
+#define RT5651_M_DAC_L1_SPM_L_SFT              14
+#define RT5651_M_SV_R_SPM_L                    (0x1 << 13)
+#define RT5651_M_SV_R_SPM_L_SFT                        13
+#define RT5651_M_SV_L_SPM_L                    (0x1 << 12)
+#define RT5651_M_SV_L_SPM_L_SFT                        12
+#define RT5651_M_BST1_SPM_L                    (0x1 << 11)
+#define RT5651_M_BST1_SPM_L_SFT                        11
+
+/* SPORMIX Control (0x49) */
+#define RT5651_M_DAC_R1_SPM_R                  (0x1 << 13)
+#define RT5651_M_DAC_R1_SPM_R_SFT              13
+#define RT5651_M_SV_R_SPM_R                    (0x1 << 12)
+#define RT5651_M_SV_R_SPM_R_SFT                        12
+#define RT5651_M_BST1_SPM_R                    (0x1 << 11)
+#define RT5651_M_BST1_SPM_R_SFT                        11
+
+/* SPOLMIX / SPORMIX Ratio Control (0x4a) */
+#define RT5651_SPO_CLSD_RATIO_MASK             (0x7)
+#define RT5651_SPO_CLSD_RATIO_SFT              0
+
+/* Mono Output Mixer Control (0x4c) */
+#define RT5651_M_DAC_R2_MM                     (0x1 << 15)
+#define RT5651_M_DAC_R2_MM_SFT                 15
+#define RT5651_M_DAC_L2_MM                     (0x1 << 14)
+#define RT5651_M_DAC_L2_MM_SFT                 14
+#define RT5651_M_OV_R_MM                       (0x1 << 13)
+#define RT5651_M_OV_R_MM_SFT                   13
+#define RT5651_M_OV_L_MM                       (0x1 << 12)
+#define RT5651_M_OV_L_MM_SFT                   12
+#define RT5651_M_BST1_MM                       (0x1 << 11)
+#define RT5651_M_BST1_MM_SFT                   11
+#define RT5651_G_MONOMIX_MASK                  (0x1 << 10)
+#define RT5651_G_MONOMIX_SFT                   10
+
+/* Output Left Mixer Control 1 (0x4d) */
+#define RT5651_G_BST2_OM_L_MASK                        (0x7 << 10)
+#define RT5651_G_BST2_OM_L_SFT                 10
+#define RT5651_G_BST1_OM_L_MASK                        (0x7 << 7)
+#define RT5651_G_BST1_OM_L_SFT                 7
+#define RT5651_G_IN1_L_OM_L_MASK               (0x7 << 4)
+#define RT5651_G_IN1_L_OM_L_SFT                        4
+#define RT5651_G_RM_L_OM_L_MASK                        (0x7 << 1)
+#define RT5651_G_RM_L_OM_L_SFT                 1
+
+/* Output Left Mixer Control 2 (0x4e) */
+#define RT5651_G_DAC_L1_OM_L_MASK              (0x7 << 7)
+#define RT5651_G_DAC_L1_OM_L_SFT               7
+#define RT5651_G_IN2_L_OM_L_MASK               (0x7 << 4)
+#define RT5651_G_IN2_L_OM_L_SFT                        4
+
+/* Output Left Mixer Control 3 (0x4f) */
+#define RT5651_M_IN2_L_OM_L                    (0x1 << 9)
+#define RT5651_M_IN2_L_OM_L_SFT                        9
+#define RT5651_M_BST2_OM_L                     (0x1 << 6)
+#define RT5651_M_BST2_OM_L_SFT                 6
+#define RT5651_M_BST1_OM_L                     (0x1 << 5)
+#define RT5651_M_BST1_OM_L_SFT                 5
+#define RT5651_M_IN1_L_OM_L                    (0x1 << 4)
+#define RT5651_M_IN1_L_OM_L_SFT                        4
+#define RT5651_M_RM_L_OM_L                     (0x1 << 3)
+#define RT5651_M_RM_L_OM_L_SFT                 3
+#define RT5651_M_DAC_L1_OM_L                   (0x1)
+#define RT5651_M_DAC_L1_OM_L_SFT               0
+
+/* Output Right Mixer Control 1 (0x50) */
+#define RT5651_G_BST2_OM_R_MASK                        (0x7 << 10)
+#define RT5651_G_BST2_OM_R_SFT                 10
+#define RT5651_G_BST1_OM_R_MASK                        (0x7 << 7)
+#define RT5651_G_BST1_OM_R_SFT                 7
+#define RT5651_G_IN1_R_OM_R_MASK               (0x7 << 4)
+#define RT5651_G_IN1_R_OM_R_SFT                        4
+#define RT5651_G_RM_R_OM_R_MASK                        (0x7 << 1)
+#define RT5651_G_RM_R_OM_R_SFT                 1
+
+/* Output Right Mixer Control 2 (0x51) */
+#define RT5651_G_DAC_R1_OM_R_MASK              (0x7 << 7)
+#define RT5651_G_DAC_R1_OM_R_SFT               7
+#define RT5651_G_IN2_R_OM_R_MASK               (0x7 << 4)
+#define RT5651_G_IN2_R_OM_R_SFT                        4
+
+/* Output Right Mixer Control 3 (0x52) */
+#define RT5651_M_IN2_R_OM_R                    (0x1 << 9)
+#define RT5651_M_IN2_R_OM_R_SFT                        9
+#define RT5651_M_BST2_OM_R                     (0x1 << 6)
+#define RT5651_M_BST2_OM_R_SFT                 6
+#define RT5651_M_BST1_OM_R                     (0x1 << 5)
+#define RT5651_M_BST1_OM_R_SFT                 5
+#define RT5651_M_IN1_R_OM_R                    (0x1 << 4)
+#define RT5651_M_IN1_R_OM_R_SFT                        4
+#define RT5651_M_RM_R_OM_R                     (0x1 << 3)
+#define RT5651_M_RM_R_OM_R_SFT                 3
+#define RT5651_M_DAC_R1_OM_R                   (0x1)
+#define RT5651_M_DAC_R1_OM_R_SFT               0
+
+/* LOUT Mixer Control (0x53) */
+#define RT5651_M_DAC_L1_LM                     (0x1 << 15)
+#define RT5651_M_DAC_L1_LM_SFT                 15
+#define RT5651_M_DAC_R1_LM                     (0x1 << 14)
+#define RT5651_M_DAC_R1_LM_SFT                 14
+#define RT5651_M_OV_L_LM                       (0x1 << 13)
+#define RT5651_M_OV_L_LM_SFT                   13
+#define RT5651_M_OV_R_LM                       (0x1 << 12)
+#define RT5651_M_OV_R_LM_SFT                   12
+#define RT5651_G_LOUTMIX_MASK                  (0x1 << 11)
+#define RT5651_G_LOUTMIX_SFT                   11
+
+/* Power Management for Digital 1 (0x61) */
+#define RT5651_PWR_I2S1                                (0x1 << 15)
+#define RT5651_PWR_I2S1_BIT                    15
+#define RT5651_PWR_I2S2                                (0x1 << 14)
+#define RT5651_PWR_I2S2_BIT                    14
+#define RT5651_PWR_DAC_L1                      (0x1 << 12)
+#define RT5651_PWR_DAC_L1_BIT                  12
+#define RT5651_PWR_DAC_R1                      (0x1 << 11)
+#define RT5651_PWR_DAC_R1_BIT                  11
+#define RT5651_PWR_ADC_L                       (0x1 << 2)
+#define RT5651_PWR_ADC_L_BIT                   2
+#define RT5651_PWR_ADC_R                       (0x1 << 1)
+#define RT5651_PWR_ADC_R_BIT                   1
+
+/* Power Management for Digital 2 (0x62) */
+#define RT5651_PWR_ADC_STO1_F                  (0x1 << 15)
+#define RT5651_PWR_ADC_STO1_F_BIT                      15
+#define RT5651_PWR_ADC_STO2_F                  (0x1 << 14)
+#define RT5651_PWR_ADC_STO2_F_BIT              14
+#define RT5651_PWR_DAC_STO1_F                  (0x1 << 11)
+#define RT5651_PWR_DAC_STO1_F_BIT                      11
+#define RT5651_PWR_DAC_STO2_F                  (0x1 << 10)
+#define RT5651_PWR_DAC_STO2_F_BIT              10
+#define RT5651_PWR_PDM                         (0x1 << 9)
+#define RT5651_PWR_PDM_BIT                     9
+
+/* Power Management for Analog 1 (0x63) */
+#define RT5651_PWR_VREF1                       (0x1 << 15)
+#define RT5651_PWR_VREF1_BIT                   15
+#define RT5651_PWR_FV1                         (0x1 << 14)
+#define RT5651_PWR_FV1_BIT                     14
+#define RT5651_PWR_MB                          (0x1 << 13)
+#define RT5651_PWR_MB_BIT                      13
+#define RT5651_PWR_LM                          (0x1 << 12)
+#define RT5651_PWR_LM_BIT                      12
+#define RT5651_PWR_BG                          (0x1 << 11)
+#define RT5651_PWR_BG_BIT                      11
+#define RT5651_PWR_HP_L                                (0x1 << 7)
+#define RT5651_PWR_HP_L_BIT                    7
+#define RT5651_PWR_HP_R                                (0x1 << 6)
+#define RT5651_PWR_HP_R_BIT                    6
+#define RT5651_PWR_HA                          (0x1 << 5)
+#define RT5651_PWR_HA_BIT                      5
+#define RT5651_PWR_VREF2                       (0x1 << 4)
+#define RT5651_PWR_VREF2_BIT                   4
+#define RT5651_PWR_FV2                         (0x1 << 3)
+#define RT5651_PWR_FV2_BIT                     3
+#define RT5651_PWR_LDO                         (0x1 << 2)
+#define RT5651_PWR_LDO_BIT                     2
+#define RT5651_PWR_LDO_DVO_MASK                        (0x3)
+#define RT5651_PWR_LDO_DVO_1_0V                        0
+#define RT5651_PWR_LDO_DVO_1_1V                        1
+#define RT5651_PWR_LDO_DVO_1_2V                        2
+#define RT5651_PWR_LDO_DVO_1_3V                        3
+
+/* Power Management for Analog 2 (0x64) */
+#define RT5651_PWR_BST1                                (0x1 << 15)
+#define RT5651_PWR_BST1_BIT                    15
+#define RT5651_PWR_BST2                                (0x1 << 14)
+#define RT5651_PWR_BST2_BIT                    14
+#define RT5651_PWR_BST3                                (0x1 << 13)
+#define RT5651_PWR_BST3_BIT                    13
+#define RT5651_PWR_MB1                         (0x1 << 11)
+#define RT5651_PWR_MB1_BIT                     11
+#define RT5651_PWR_PLL                         (0x1 << 9)
+#define RT5651_PWR_PLL_BIT                     9
+#define RT5651_PWR_BST1_OP2                    (0x1 << 5)
+#define RT5651_PWR_BST1_OP2_BIT                        5
+#define RT5651_PWR_BST2_OP2                    (0x1 << 4)
+#define RT5651_PWR_BST2_OP2_BIT                        4
+#define RT5651_PWR_BST3_OP2                    (0x1 << 3)
+#define RT5651_PWR_BST3_OP2_BIT                        3
+#define RT5651_PWR_JD_M                                (0x1 << 2)
+#define RT5651_PWM_JD_M_BIT                    2
+#define RT5651_PWR_JD2                         (0x1 << 1)
+#define RT5651_PWM_JD2_BIT                     1
+#define RT5651_PWR_JD3                         (0x1)
+#define RT5651_PWM_JD3_BIT                     0
+
+/* Power Management for Mixer (0x65) */
+#define RT5651_PWR_OM_L                                (0x1 << 15)
+#define RT5651_PWR_OM_L_BIT                    15
+#define RT5651_PWR_OM_R                                (0x1 << 14)
+#define RT5651_PWR_OM_R_BIT                    14
+#define RT5651_PWR_RM_L                                (0x1 << 11)
+#define RT5651_PWR_RM_L_BIT                    11
+#define RT5651_PWR_RM_R                                (0x1 << 10)
+#define RT5651_PWR_RM_R_BIT                    10
+
+/* Power Management for Volume (0x66) */
+#define RT5651_PWR_OV_L                                (0x1 << 13)
+#define RT5651_PWR_OV_L_BIT                    13
+#define RT5651_PWR_OV_R                                (0x1 << 12)
+#define RT5651_PWR_OV_R_BIT                    12
+#define RT5651_PWR_HV_L                                (0x1 << 11)
+#define RT5651_PWR_HV_L_BIT                    11
+#define RT5651_PWR_HV_R                                (0x1 << 10)
+#define RT5651_PWR_HV_R_BIT                    10
+#define RT5651_PWR_IN1_L                       (0x1 << 9)
+#define RT5651_PWR_IN1_L_BIT                   9
+#define RT5651_PWR_IN1_R                       (0x1 << 8)
+#define RT5651_PWR_IN1_R_BIT                   8
+#define RT5651_PWR_IN2_L                       (0x1 << 7)
+#define RT5651_PWR_IN2_L_BIT                   7
+#define RT5651_PWR_IN2_R                       (0x1 << 6)
+#define RT5651_PWR_IN2_R_BIT                   6
+
+/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71) */
+#define RT5651_I2S_MS_MASK                     (0x1 << 15)
+#define RT5651_I2S_MS_SFT                      15
+#define RT5651_I2S_MS_M                                (0x0 << 15)
+#define RT5651_I2S_MS_S                                (0x1 << 15)
+#define RT5651_I2S_O_CP_MASK                   (0x3 << 10)
+#define RT5651_I2S_O_CP_SFT                    10
+#define RT5651_I2S_O_CP_OFF                    (0x0 << 10)
+#define RT5651_I2S_O_CP_U_LAW                  (0x1 << 10)
+#define RT5651_I2S_O_CP_A_LAW                  (0x2 << 10)
+#define RT5651_I2S_I_CP_MASK                   (0x3 << 8)
+#define RT5651_I2S_I_CP_SFT                    8
+#define RT5651_I2S_I_CP_OFF                    (0x0 << 8)
+#define RT5651_I2S_I_CP_U_LAW                  (0x1 << 8)
+#define RT5651_I2S_I_CP_A_LAW                  (0x2 << 8)
+#define RT5651_I2S_BP_MASK                     (0x1 << 7)
+#define RT5651_I2S_BP_SFT                      7
+#define RT5651_I2S_BP_NOR                      (0x0 << 7)
+#define RT5651_I2S_BP_INV                      (0x1 << 7)
+#define RT5651_I2S_DL_MASK                     (0x3 << 2)
+#define RT5651_I2S_DL_SFT                      2
+#define RT5651_I2S_DL_16                       (0x0 << 2)
+#define RT5651_I2S_DL_20                       (0x1 << 2)
+#define RT5651_I2S_DL_24                       (0x2 << 2)
+#define RT5651_I2S_DL_8                                (0x3 << 2)
+#define RT5651_I2S_DF_MASK                     (0x3)
+#define RT5651_I2S_DF_SFT                      0
+#define RT5651_I2S_DF_I2S                      (0x0)
+#define RT5651_I2S_DF_LEFT                     (0x1)
+#define RT5651_I2S_DF_PCM_A                    (0x2)
+#define RT5651_I2S_DF_PCM_B                    (0x3)
+
+/* ADC/DAC Clock Control 1 (0x73) */
+#define RT5651_I2S_PD1_MASK                    (0x7 << 12)
+#define RT5651_I2S_PD1_SFT                     12
+#define RT5651_I2S_PD1_1                       (0x0 << 12)
+#define RT5651_I2S_PD1_2                       (0x1 << 12)
+#define RT5651_I2S_PD1_3                       (0x2 << 12)
+#define RT5651_I2S_PD1_4                       (0x3 << 12)
+#define RT5651_I2S_PD1_6                       (0x4 << 12)
+#define RT5651_I2S_PD1_8                       (0x5 << 12)
+#define RT5651_I2S_PD1_12                      (0x6 << 12)
+#define RT5651_I2S_PD1_16                      (0x7 << 12)
+#define RT5651_I2S_BCLK_MS2_MASK               (0x1 << 11)
+#define RT5651_I2S_BCLK_MS2_SFT                        11
+#define RT5651_I2S_BCLK_MS2_32                 (0x0 << 11)
+#define RT5651_I2S_BCLK_MS2_64                 (0x1 << 11)
+#define RT5651_I2S_PD2_MASK                    (0x7 << 8)
+#define RT5651_I2S_PD2_SFT                     8
+#define RT5651_I2S_PD2_1                       (0x0 << 8)
+#define RT5651_I2S_PD2_2                       (0x1 << 8)
+#define RT5651_I2S_PD2_3                       (0x2 << 8)
+#define RT5651_I2S_PD2_4                       (0x3 << 8)
+#define RT5651_I2S_PD2_6                       (0x4 << 8)
+#define RT5651_I2S_PD2_8                       (0x5 << 8)
+#define RT5651_I2S_PD2_12                      (0x6 << 8)
+#define RT5651_I2S_PD2_16                      (0x7 << 8)
+#define RT5651_DAC_OSR_MASK                    (0x3 << 2)
+#define RT5651_DAC_OSR_SFT                     2
+#define RT5651_DAC_OSR_128                     (0x0 << 2)
+#define RT5651_DAC_OSR_64                      (0x1 << 2)
+#define RT5651_DAC_OSR_32                      (0x2 << 2)
+#define RT5651_DAC_OSR_128_3                   (0x3 << 2)
+#define RT5651_ADC_OSR_MASK                    (0x3)
+#define RT5651_ADC_OSR_SFT                     0
+#define RT5651_ADC_OSR_128                     (0x0)
+#define RT5651_ADC_OSR_64                      (0x1)
+#define RT5651_ADC_OSR_32                      (0x2)
+#define RT5651_ADC_OSR_128_3                   (0x3)
+
+/* ADC/DAC Clock Control 2 (0x74) */
+#define RT5651_DAHPF_EN                                (0x1 << 11)
+#define RT5651_DAHPF_EN_SFT                    11
+#define RT5651_ADHPF_EN                                (0x1 << 10)
+#define RT5651_ADHPF_EN_SFT                    10
+
+/* Digital Microphone Control (0x75) */
+#define RT5651_DMIC_1_EN_MASK                  (0x1 << 15)
+#define RT5651_DMIC_1_EN_SFT                   15
+#define RT5651_DMIC_1_DIS                      (0x0 << 15)
+#define RT5651_DMIC_1_EN                       (0x1 << 15)
+#define RT5651_DMIC_1L_LH_MASK                 (0x1 << 13)
+#define RT5651_DMIC_1L_LH_SFT                  13
+#define RT5651_DMIC_1L_LH_FALLING              (0x0 << 13)
+#define RT5651_DMIC_1L_LH_RISING               (0x1 << 13)
+#define RT5651_DMIC_1R_LH_MASK                 (0x1 << 12)
+#define RT5651_DMIC_1R_LH_SFT                  12
+#define RT5651_DMIC_1R_LH_FALLING              (0x0 << 12)
+#define RT5651_DMIC_1R_LH_RISING               (0x1 << 12)
+#define RT5651_DMIC_1_DP_MASK                  (0x3 << 10)
+#define RT5651_DMIC_1_DP_SFT                   10
+#define RT5651_DMIC_1_DP_GPIO6                 (0x0 << 10)
+#define RT5651_DMIC_1_DP_IN1P                  (0x1 << 10)
+#define RT5651_DMIC_2_DP_GPIO8                 (0x2 << 10)
+#define RT5651_DMIC_CLK_MASK                   (0x7 << 5)
+#define RT5651_DMIC_CLK_SFT                    5
+
+/* TDM Control 1 (0x77) */
+#define RT5651_TDM_INTEL_SEL_MASK              (0x1 << 15)
+#define RT5651_TDM_INTEL_SEL_SFT               15
+#define RT5651_TDM_INTEL_SEL_64                        (0x0 << 15)
+#define RT5651_TDM_INTEL_SEL_50                        (0x1 << 15)
+#define RT5651_TDM_MODE_SEL_MASK               (0x1 << 14)
+#define RT5651_TDM_MODE_SEL_SFT                        14
+#define RT5651_TDM_MODE_SEL_NOR                        (0x0 << 14)
+#define RT5651_TDM_MODE_SEL_TDM                        (0x1 << 14)
+#define RT5651_TDM_CH_NUM_SEL_MASK             (0x3 << 12)
+#define RT5651_TDM_CH_NUM_SEL_SFT              12
+#define RT5651_TDM_CH_NUM_SEL_2                        (0x0 << 12)
+#define RT5651_TDM_CH_NUM_SEL_4                        (0x1 << 12)
+#define RT5651_TDM_CH_NUM_SEL_6                        (0x2 << 12)
+#define RT5651_TDM_CH_NUM_SEL_8                        (0x3 << 12)
+#define RT5651_TDM_CH_LEN_SEL_MASK             (0x3 << 10)
+#define RT5651_TDM_CH_LEN_SEL_SFT              10
+#define RT5651_TDM_CH_LEN_SEL_16               (0x0 << 10)
+#define RT5651_TDM_CH_LEN_SEL_20               (0x1 << 10)
+#define RT5651_TDM_CH_LEN_SEL_24               (0x2 << 10)
+#define RT5651_TDM_CH_LEN_SEL_32               (0x3 << 10)
+#define RT5651_TDM_ADC_SEL_MASK                        (0x1 << 9)
+#define RT5651_TDM_ADC_SEL_SFT                 9
+#define RT5651_TDM_ADC_SEL_NOR                 (0x0 << 9)
+#define RT5651_TDM_ADC_SEL_SWAP                        (0x1 << 9)
+#define RT5651_TDM_ADC_START_SEL_MASK          (0x1 << 8)
+#define RT5651_TDM_ADC_START_SEL_SFT           8
+#define RT5651_TDM_ADC_START_SEL_SL0           (0x0 << 8)
+#define RT5651_TDM_ADC_START_SEL_SL4           (0x1 << 8)
+#define RT5651_TDM_I2S_CH2_SEL_MASK            (0x3 << 6)
+#define RT5651_TDM_I2S_CH2_SEL_SFT             6
+#define RT5651_TDM_I2S_CH2_SEL_LR              (0x0 << 6)
+#define RT5651_TDM_I2S_CH2_SEL_RL              (0x1 << 6)
+#define RT5651_TDM_I2S_CH2_SEL_LL              (0x2 << 6)
+#define RT5651_TDM_I2S_CH2_SEL_RR              (0x3 << 6)
+#define RT5651_TDM_I2S_CH4_SEL_MASK            (0x3 << 4)
+#define RT5651_TDM_I2S_CH4_SEL_SFT             4
+#define RT5651_TDM_I2S_CH4_SEL_LR              (0x0 << 4)
+#define RT5651_TDM_I2S_CH4_SEL_RL              (0x1 << 4)
+#define RT5651_TDM_I2S_CH4_SEL_LL              (0x2 << 4)
+#define RT5651_TDM_I2S_CH4_SEL_RR              (0x3 << 4)
+#define RT5651_TDM_I2S_CH6_SEL_MASK            (0x3 << 2)
+#define RT5651_TDM_I2S_CH6_SEL_SFT             2
+#define RT5651_TDM_I2S_CH6_SEL_LR              (0x0 << 2)
+#define RT5651_TDM_I2S_CH6_SEL_RL              (0x1 << 2)
+#define RT5651_TDM_I2S_CH6_SEL_LL              (0x2 << 2)
+#define RT5651_TDM_I2S_CH6_SEL_RR              (0x3 << 2)
+#define RT5651_TDM_I2S_CH8_SEL_MASK            (0x3)
+#define RT5651_TDM_I2S_CH8_SEL_SFT             0
+#define RT5651_TDM_I2S_CH8_SEL_LR              (0x0)
+#define RT5651_TDM_I2S_CH8_SEL_RL              (0x1)
+#define RT5651_TDM_I2S_CH8_SEL_LL              (0x2)
+#define RT5651_TDM_I2S_CH8_SEL_RR              (0x3)
+
+/* TDM Control 2 (0x78) */
+#define RT5651_TDM_LRCK_POL_SEL_MASK           (0x1 << 15)
+#define RT5651_TDM_LRCK_POL_SEL_SFT            15
+#define RT5651_TDM_LRCK_POL_SEL_NOR            (0x0 << 15)
+#define RT5651_TDM_LRCK_POL_SEL_INV            (0x1 << 15)
+#define RT5651_TDM_CH_VAL_SEL_MASK             (0x1 << 14)
+#define RT5651_TDM_CH_VAL_SEL_SFT              14
+#define RT5651_TDM_CH_VAL_SEL_CH01             (0x0 << 14)
+#define RT5651_TDM_CH_VAL_SEL_CH0123           (0x1 << 14)
+#define RT5651_TDM_CH_VAL_EN                   (0x1 << 13)
+#define RT5651_TDM_CH_VAL_SFT                  13
+#define RT5651_TDM_LPBK_EN                     (0x1 << 12)
+#define RT5651_TDM_LPBK_SFT                    12
+#define RT5651_TDM_LRCK_PULSE_SEL_MASK         (0x1 << 11)
+#define RT5651_TDM_LRCK_PULSE_SEL_SFT          11
+#define RT5651_TDM_LRCK_PULSE_SEL_BCLK         (0x0 << 11)
+#define RT5651_TDM_LRCK_PULSE_SEL_CH           (0x1 << 11)
+#define RT5651_TDM_END_EDGE_SEL_MASK           (0x1 << 10)
+#define RT5651_TDM_END_EDGE_SEL_SFT            10
+#define RT5651_TDM_END_EDGE_SEL_POS            (0x0 << 10)
+#define RT5651_TDM_END_EDGE_SEL_NEG            (0x1 << 10)
+#define RT5651_TDM_END_EDGE_EN                 (0x1 << 9)
+#define RT5651_TDM_END_EDGE_EN_SFT             9
+#define RT5651_TDM_TRAN_EDGE_SEL_MASK          (0x1 << 8)
+#define RT5651_TDM_TRAN_EDGE_SEL_SFT           8
+#define RT5651_TDM_TRAN_EDGE_SEL_POS           (0x0 << 8)
+#define RT5651_TDM_TRAN_EDGE_SEL_NEG           (0x1 << 8)
+#define RT5651_M_TDM2_L                                (0x1 << 7)
+#define RT5651_M_TDM2_L_SFT                    7
+#define RT5651_M_TDM2_R                                (0x1 << 6)
+#define RT5651_M_TDM2_R_SFT                    6
+#define RT5651_M_TDM4_L                                (0x1 << 5)
+#define RT5651_M_TDM4_L_SFT                    5
+#define RT5651_M_TDM4_R                                (0x1 << 4)
+#define RT5651_M_TDM4_R_SFT                    4
+
+/* TDM Control 3 (0x79) */
+#define RT5651_CH2_L_SEL_MASK                  (0x7 << 12)
+#define RT5651_CH2_L_SEL_SFT                   12
+#define RT5651_CH2_L_SEL_SL0                   (0x0 << 12)
+#define RT5651_CH2_L_SEL_SL1                   (0x1 << 12)
+#define RT5651_CH2_L_SEL_SL2                   (0x2 << 12)
+#define RT5651_CH2_L_SEL_SL3                   (0x3 << 12)
+#define RT5651_CH2_L_SEL_SL4                   (0x4 << 12)
+#define RT5651_CH2_L_SEL_SL5                   (0x5 << 12)
+#define RT5651_CH2_L_SEL_SL6                   (0x6 << 12)
+#define RT5651_CH2_L_SEL_SL7                   (0x7 << 12)
+#define RT5651_CH2_R_SEL_MASK                  (0x7 << 8)
+#define RT5651_CH2_R_SEL_SFT                   8
+#define RT5651_CH2_R_SEL_SL0                   (0x0 << 8)
+#define RT5651_CH2_R_SEL_SL1                   (0x1 << 8)
+#define RT5651_CH2_R_SEL_SL2                   (0x2 << 8)
+#define RT5651_CH2_R_SEL_SL3                   (0x3 << 8)
+#define RT5651_CH2_R_SEL_SL4                   (0x4 << 8)
+#define RT5651_CH2_R_SEL_SL5                   (0x5 << 8)
+#define RT5651_CH2_R_SEL_SL6                   (0x6 << 8)
+#define RT5651_CH2_R_SEL_SL7                   (0x7 << 8)
+#define RT5651_CH4_L_SEL_MASK                  (0x7 << 4)
+#define RT5651_CH4_L_SEL_SFT                   4
+#define RT5651_CH4_L_SEL_SL0                   (0x0 << 4)
+#define RT5651_CH4_L_SEL_SL1                   (0x1 << 4)
+#define RT5651_CH4_L_SEL_SL2                   (0x2 << 4)
+#define RT5651_CH4_L_SEL_SL3                   (0x3 << 4)
+#define RT5651_CH4_L_SEL_SL4                   (0x4 << 4)
+#define RT5651_CH4_L_SEL_SL5                   (0x5 << 4)
+#define RT5651_CH4_L_SEL_SL6                   (0x6 << 4)
+#define RT5651_CH4_L_SEL_SL7                   (0x7 << 4)
+#define RT5651_CH4_R_SEL_MASK                  (0x7)
+#define RT5651_CH4_R_SEL_SFT                   0
+#define RT5651_CH4_R_SEL_SL0                   (0x0)
+#define RT5651_CH4_R_SEL_SL1                   (0x1)
+#define RT5651_CH4_R_SEL_SL2                   (0x2)
+#define RT5651_CH4_R_SEL_SL3                   (0x3)
+#define RT5651_CH4_R_SEL_SL4                   (0x4)
+#define RT5651_CH4_R_SEL_SL5                   (0x5)
+#define RT5651_CH4_R_SEL_SL6                   (0x6)
+#define RT5651_CH4_R_SEL_SL7                   (0x7)
+
+/* Global Clock Control (0x80) */
+#define RT5651_SCLK_SRC_MASK                   (0x3 << 14)
+#define RT5651_SCLK_SRC_SFT                    14
+#define RT5651_SCLK_SRC_MCLK                   (0x0 << 14)
+#define RT5651_SCLK_SRC_PLL1                   (0x1 << 14)
+#define RT5651_SCLK_SRC_RCCLK                  (0x2 << 14)
+#define RT5651_PLL1_SRC_MASK                   (0x3 << 12)
+#define RT5651_PLL1_SRC_SFT                    12
+#define RT5651_PLL1_SRC_MCLK                   (0x0 << 12)
+#define RT5651_PLL1_SRC_BCLK1                  (0x1 << 12)
+#define RT5651_PLL1_SRC_BCLK2                  (0x2 << 12)
+#define RT5651_PLL1_PD_MASK                    (0x1 << 3)
+#define RT5651_PLL1_PD_SFT                     3
+#define RT5651_PLL1_PD_1                       (0x0 << 3)
+#define RT5651_PLL1_PD_2                       (0x1 << 3)
+
+#define RT5651_PLL_INP_MAX                     40000000
+#define RT5651_PLL_INP_MIN                     256000
+/* PLL M/N/K Code Control 1 (0x81) */
+#define RT5651_PLL_N_MAX                       0x1ff
+#define RT5651_PLL_N_MASK                      (RT5651_PLL_N_MAX << 7)
+#define RT5651_PLL_N_SFT                       7
+#define RT5651_PLL_K_MAX                       0x1f
+#define RT5651_PLL_K_MASK                      (RT5651_PLL_K_MAX)
+#define RT5651_PLL_K_SFT                       0
+
+/* PLL M/N/K Code Control 2 (0x82) */
+#define RT5651_PLL_M_MAX                       0xf
+#define RT5651_PLL_M_MASK                      (RT5651_PLL_M_MAX << 12)
+#define RT5651_PLL_M_SFT                       12
+#define RT5651_PLL_M_BP                                (0x1 << 11)
+#define RT5651_PLL_M_BP_SFT                    11
+
+/* PLL tracking mode 1 (0x83) */
+#define RT5651_STO1_T_MASK                     (0x1 << 15)
+#define RT5651_STO1_T_SFT                      15
+#define RT5651_STO1_T_SCLK                     (0x0 << 15)
+#define RT5651_STO1_T_LRCK1                    (0x1 << 15)
+#define RT5651_STO2_T_MASK                     (0x1 << 12)
+#define RT5651_STO2_T_SFT                      12
+#define RT5651_STO2_T_I2S2                     (0x0 << 12)
+#define RT5651_STO2_T_LRCK2                    (0x1 << 12)
+#define RT5651_ASRC2_REF_MASK                  (0x1 << 11)
+#define RT5651_ASRC2_REF_SFT                   11
+#define RT5651_ASRC2_REF_LRCK2                 (0x0 << 11)
+#define RT5651_ASRC2_REF_LRCK1                 (0x1 << 11)
+#define RT5651_DMIC_1_M_MASK                   (0x1 << 9)
+#define RT5651_DMIC_1_M_SFT                    9
+#define RT5651_DMIC_1_M_NOR                    (0x0 << 9)
+#define RT5651_DMIC_1_M_ASYN                   (0x1 << 9)
+
+/* PLL tracking mode 2 (0x84) */
+#define RT5651_STO1_ASRC_EN                    (0x1 << 15)
+#define RT5651_STO1_ASRC_EN_SFT                        15
+#define RT5651_STO2_ASRC_EN                    (0x1 << 14)
+#define RT5651_STO2_ASRC_EN_SFT                        14
+#define RT5651_STO1_DAC_M_MASK                 (0x1 << 13)
+#define RT5651_STO1_DAC_M_SFT                  13
+#define RT5651_STO1_DAC_M_NOR                  (0x0 << 13)
+#define RT5651_STO1_DAC_M_ASRC                 (0x1 << 13)
+#define RT5651_STO2_DAC_M_MASK                 (0x1 << 12)
+#define RT5651_STO2_DAC_M_SFT                  12
+#define RT5651_STO2_DAC_M_NOR                  (0x0 << 12)
+#define RT5651_STO2_DAC_M_ASRC                 (0x1 << 12)
+#define RT5651_ADC_M_MASK                      (0x1 << 11)
+#define RT5651_ADC_M_SFT                       11
+#define RT5651_ADC_M_NOR                       (0x0 << 11)
+#define RT5651_ADC_M_ASRC                      (0x1 << 11)
+#define RT5651_I2S1_R_D_MASK                   (0x1 << 4)
+#define RT5651_I2S1_R_D_SFT                    4
+#define RT5651_I2S1_R_D_DIS                    (0x0 << 4)
+#define RT5651_I2S1_R_D_EN                     (0x1 << 4)
+#define RT5651_I2S2_R_D_MASK                   (0x1 << 3)
+#define RT5651_I2S2_R_D_SFT                    3
+#define RT5651_I2S2_R_D_DIS                    (0x0 << 3)
+#define RT5651_I2S2_R_D_EN                     (0x1 << 3)
+#define RT5651_PRE_SCLK_MASK                   (0x3)
+#define RT5651_PRE_SCLK_SFT                    0
+#define RT5651_PRE_SCLK_512                    (0x0)
+#define RT5651_PRE_SCLK_1024                   (0x1)
+#define RT5651_PRE_SCLK_2048                   (0x2)
+
+/* PLL tracking mode 3 (0x85) */
+#define RT5651_I2S1_RATE_MASK                  (0xf << 12)
+#define RT5651_I2S1_RATE_SFT                   12
+#define RT5651_I2S2_RATE_MASK                  (0xf << 8)
+#define RT5651_I2S2_RATE_SFT                   8
+#define RT5651_G_ASRC_LP_MASK                  (0x1 << 3)
+#define RT5651_G_ASRC_LP_SFT                   3
+#define RT5651_ASRC_LP_F_M                     (0x1 << 2)
+#define RT5651_ASRC_LP_F_SFT                   2
+#define RT5651_ASRC_LP_F_NOR                   (0x0 << 2)
+#define RT5651_ASRC_LP_F_SB                    (0x1 << 2)
+#define RT5651_FTK_PH_DET_MASK                 (0x3)
+#define RT5651_FTK_PH_DET_SFT                  0
+#define RT5651_FTK_PH_DET_DIV1                 (0x0)
+#define RT5651_FTK_PH_DET_DIV2                 (0x1)
+#define RT5651_FTK_PH_DET_DIV4                 (0x2)
+#define RT5651_FTK_PH_DET_DIV8                 (0x3)
+
+/*PLL tracking mode 6 (0x89) */
+#define RT5651_I2S1_PD_MASK                    (0x7 << 12)
+#define RT5651_I2S1_PD_SFT                     12
+#define RT5651_I2S2_PD_MASK                    (0x7 << 8)
+#define RT5651_I2S2_PD_SFT                     8
+
+/*PLL tracking mode 7 (0x8a) */
+#define RT5651_FSI1_RATE_MASK                  (0xf << 12)
+#define RT5651_FSI1_RATE_SFT                   12
+#define RT5651_FSI2_RATE_MASK                  (0xf << 8)
+#define RT5651_FSI2_RATE_SFT                   8
+
+/* HPOUT Over Current Detection (0x8b) */
+#define RT5651_HP_OVCD_MASK                    (0x1 << 10)
+#define RT5651_HP_OVCD_SFT                     10
+#define RT5651_HP_OVCD_DIS                     (0x0 << 10)
+#define RT5651_HP_OVCD_EN                      (0x1 << 10)
+#define RT5651_HP_OC_TH_MASK                   (0x3 << 8)
+#define RT5651_HP_OC_TH_SFT                    8
+#define RT5651_HP_OC_TH_90                     (0x0 << 8)
+#define RT5651_HP_OC_TH_105                    (0x1 << 8)
+#define RT5651_HP_OC_TH_120                    (0x2 << 8)
+#define RT5651_HP_OC_TH_135                    (0x3 << 8)
+
+/* Depop Mode Control 1 (0x8e) */
+#define RT5651_SMT_TRIG_MASK                   (0x1 << 15)
+#define RT5651_SMT_TRIG_SFT                    15
+#define RT5651_SMT_TRIG_DIS                    (0x0 << 15)
+#define RT5651_SMT_TRIG_EN                     (0x1 << 15)
+#define RT5651_HP_L_SMT_MASK                   (0x1 << 9)
+#define RT5651_HP_L_SMT_SFT                    9
+#define RT5651_HP_L_SMT_DIS                    (0x0 << 9)
+#define RT5651_HP_L_SMT_EN                     (0x1 << 9)
+#define RT5651_HP_R_SMT_MASK                   (0x1 << 8)
+#define RT5651_HP_R_SMT_SFT                    8
+#define RT5651_HP_R_SMT_DIS                    (0x0 << 8)
+#define RT5651_HP_R_SMT_EN                     (0x1 << 8)
+#define RT5651_HP_CD_PD_MASK                   (0x1 << 7)
+#define RT5651_HP_CD_PD_SFT                    7
+#define RT5651_HP_CD_PD_DIS                    (0x0 << 7)
+#define RT5651_HP_CD_PD_EN                     (0x1 << 7)
+#define RT5651_RSTN_MASK                       (0x1 << 6)
+#define RT5651_RSTN_SFT                                6
+#define RT5651_RSTN_DIS                                (0x0 << 6)
+#define RT5651_RSTN_EN                         (0x1 << 6)
+#define RT5651_RSTP_MASK                       (0x1 << 5)
+#define RT5651_RSTP_SFT                                5
+#define RT5651_RSTP_DIS                                (0x0 << 5)
+#define RT5651_RSTP_EN                         (0x1 << 5)
+#define RT5651_HP_CO_MASK                      (0x1 << 4)
+#define RT5651_HP_CO_SFT                       4
+#define RT5651_HP_CO_DIS                       (0x0 << 4)
+#define RT5651_HP_CO_EN                                (0x1 << 4)
+#define RT5651_HP_CP_MASK                      (0x1 << 3)
+#define RT5651_HP_CP_SFT                       3
+#define RT5651_HP_CP_PD                                (0x0 << 3)
+#define RT5651_HP_CP_PU                                (0x1 << 3)
+#define RT5651_HP_SG_MASK                      (0x1 << 2)
+#define RT5651_HP_SG_SFT                       2
+#define RT5651_HP_SG_DIS                       (0x0 << 2)
+#define RT5651_HP_SG_EN                                (0x1 << 2)
+#define RT5651_HP_DP_MASK                      (0x1 << 1)
+#define RT5651_HP_DP_SFT                       1
+#define RT5651_HP_DP_PD                                (0x0 << 1)
+#define RT5651_HP_DP_PU                                (0x1 << 1)
+#define RT5651_HP_CB_MASK                      (0x1)
+#define RT5651_HP_CB_SFT                       0
+#define RT5651_HP_CB_PD                                (0x0)
+#define RT5651_HP_CB_PU                                (0x1)
+
+/* Depop Mode Control 2 (0x8f) */
+#define RT5651_DEPOP_MASK                      (0x1 << 13)
+#define RT5651_DEPOP_SFT                       13
+#define RT5651_DEPOP_AUTO                      (0x0 << 13)
+#define RT5651_DEPOP_MAN                       (0x1 << 13)
+#define RT5651_RAMP_MASK                       (0x1 << 12)
+#define RT5651_RAMP_SFT                                12
+#define RT5651_RAMP_DIS                                (0x0 << 12)
+#define RT5651_RAMP_EN                         (0x1 << 12)
+#define RT5651_BPS_MASK                                (0x1 << 11)
+#define RT5651_BPS_SFT                         11
+#define RT5651_BPS_DIS                         (0x0 << 11)
+#define RT5651_BPS_EN                          (0x1 << 11)
+#define RT5651_FAST_UPDN_MASK                  (0x1 << 10)
+#define RT5651_FAST_UPDN_SFT                   10
+#define RT5651_FAST_UPDN_DIS                   (0x0 << 10)
+#define RT5651_FAST_UPDN_EN                    (0x1 << 10)
+#define RT5651_MRES_MASK                       (0x3 << 8)
+#define RT5651_MRES_SFT                                8
+#define RT5651_MRES_15MO                       (0x0 << 8)
+#define RT5651_MRES_25MO                       (0x1 << 8)
+#define RT5651_MRES_35MO                       (0x2 << 8)
+#define RT5651_MRES_45MO                       (0x3 << 8)
+#define RT5651_VLO_MASK                                (0x1 << 7)
+#define RT5651_VLO_SFT                         7
+#define RT5651_VLO_3V                          (0x0 << 7)
+#define RT5651_VLO_32V                         (0x1 << 7)
+#define RT5651_DIG_DP_MASK                     (0x1 << 6)
+#define RT5651_DIG_DP_SFT                      6
+#define RT5651_DIG_DP_DIS                      (0x0 << 6)
+#define RT5651_DIG_DP_EN                       (0x1 << 6)
+#define RT5651_DP_TH_MASK                      (0x3 << 4)
+#define RT5651_DP_TH_SFT                       4
+
+/* Depop Mode Control 3 (0x90) */
+#define RT5651_CP_SYS_MASK                     (0x7 << 12)
+#define RT5651_CP_SYS_SFT                      12
+#define RT5651_CP_FQ1_MASK                     (0x7 << 8)
+#define RT5651_CP_FQ1_SFT                      8
+#define RT5651_CP_FQ2_MASK                     (0x7 << 4)
+#define RT5651_CP_FQ2_SFT                      4
+#define RT5651_CP_FQ3_MASK                     (0x7)
+#define RT5651_CP_FQ3_SFT                      0
+#define RT5651_CP_FQ_1_5_KHZ                   0
+#define RT5651_CP_FQ_3_KHZ                     1
+#define RT5651_CP_FQ_6_KHZ                     2
+#define RT5651_CP_FQ_12_KHZ                    3
+#define RT5651_CP_FQ_24_KHZ                    4
+#define RT5651_CP_FQ_48_KHZ                    5
+#define RT5651_CP_FQ_96_KHZ                    6
+#define RT5651_CP_FQ_192_KHZ                   7
+
+/* HPOUT charge pump (0x91) */
+#define RT5651_OSW_L_MASK                      (0x1 << 11)
+#define RT5651_OSW_L_SFT                       11
+#define RT5651_OSW_L_DIS                       (0x0 << 11)
+#define RT5651_OSW_L_EN                                (0x1 << 11)
+#define RT5651_OSW_R_MASK                      (0x1 << 10)
+#define RT5651_OSW_R_SFT                       10
+#define RT5651_OSW_R_DIS                       (0x0 << 10)
+#define RT5651_OSW_R_EN                                (0x1 << 10)
+#define RT5651_PM_HP_MASK                      (0x3 << 8)
+#define RT5651_PM_HP_SFT                       8
+#define RT5651_PM_HP_LV                                (0x0 << 8)
+#define RT5651_PM_HP_MV                                (0x1 << 8)
+#define RT5651_PM_HP_HV                                (0x2 << 8)
+#define RT5651_IB_HP_MASK                      (0x3 << 6)
+#define RT5651_IB_HP_SFT                       6
+#define RT5651_IB_HP_125IL                     (0x0 << 6)
+#define RT5651_IB_HP_25IL                      (0x1 << 6)
+#define RT5651_IB_HP_5IL                       (0x2 << 6)
+#define RT5651_IB_HP_1IL                       (0x3 << 6)
+
+/* Micbias Control (0x93) */
+#define RT5651_MIC1_BS_MASK                    (0x1 << 15)
+#define RT5651_MIC1_BS_SFT                     15
+#define RT5651_MIC1_BS_9AV                     (0x0 << 15)
+#define RT5651_MIC1_BS_75AV                    (0x1 << 15)
+#define RT5651_MIC1_CLK_MASK                   (0x1 << 13)
+#define RT5651_MIC1_CLK_SFT                    13
+#define RT5651_MIC1_CLK_DIS                    (0x0 << 13)
+#define RT5651_MIC1_CLK_EN                     (0x1 << 13)
+#define RT5651_MIC1_OVCD_MASK                  (0x1 << 11)
+#define RT5651_MIC1_OVCD_SFT                   11
+#define RT5651_MIC1_OVCD_DIS                   (0x0 << 11)
+#define RT5651_MIC1_OVCD_EN                    (0x1 << 11)
+#define RT5651_MIC1_OVTH_MASK                  (0x3 << 9)
+#define RT5651_MIC1_OVTH_SFT                   9
+#define RT5651_MIC1_OVTH_600UA                 (0x0 << 9)
+#define RT5651_MIC1_OVTH_1500UA                        (0x1 << 9)
+#define RT5651_MIC1_OVTH_2000UA                        (0x2 << 9)
+#define RT5651_PWR_MB_MASK                     (0x1 << 5)
+#define RT5651_PWR_MB_SFT                      5
+#define RT5651_PWR_MB_PD                       (0x0 << 5)
+#define RT5651_PWR_MB_PU                       (0x1 << 5)
+#define RT5651_PWR_CLK12M_MASK                 (0x1 << 4)
+#define RT5651_PWR_CLK12M_SFT                  4
+#define RT5651_PWR_CLK12M_PD                   (0x0 << 4)
+#define RT5651_PWR_CLK12M_PU                   (0x1 << 4)
+
+/* Analog JD Control 1 (0x94) */
+#define RT5651_JD2_CMP_MASK                    (0x7 << 12)
+#define RT5651_JD2_CMP_SFT                     12
+#define RT5651_JD_PU                           (0x1 << 11)
+#define RT5651_JD_PU_SFT                       11
+#define RT5651_JD_PD                           (0x1 << 10)
+#define RT5651_JD_PD_SFT                       10
+#define RT5651_JD_MODE_SEL_MASK                        (0x3 << 8)
+#define RT5651_JD_MODE_SEL_SFT                 8
+#define RT5651_JD_MODE_SEL_M0                  (0x0 << 8)
+#define RT5651_JD_MODE_SEL_M1                  (0x1 << 8)
+#define RT5651_JD_MODE_SEL_M2                  (0x2 << 8)
+#define RT5651_JD_M_CMP                                (0x7 << 4)
+#define RT5651_JD_M_CMP_SFT                    4
+#define RT5651_JD_M_PU                         (0x1 << 3)
+#define RT5651_JD_M_PU_SFT                     3
+#define RT5651_JD_M_PD                         (0x1 << 2)
+#define RT5651_JD_M_PD_SFT                     2
+#define RT5651_JD_M_MODE_SEL_MASK              (0x3)
+#define RT5651_JD_M_MODE_SEL_SFT               0
+#define RT5651_JD_M_MODE_SEL_M0                        (0x0)
+#define RT5651_JD_M_MODE_SEL_M1                        (0x1)
+#define RT5651_JD_M_MODE_SEL_M2                        (0x2)
+
+/* Analog JD Control 2 (0x95) */
+#define RT5651_JD3_CMP_MASK                    (0x7 << 12)
+#define RT5651_JD3_CMP_SFT                     12
+
+/* EQ Control 1 (0xb0) */
+#define RT5651_EQ_SRC_MASK                     (0x1 << 15)
+#define RT5651_EQ_SRC_SFT                      15
+#define RT5651_EQ_SRC_DAC                      (0x0 << 15)
+#define RT5651_EQ_SRC_ADC                      (0x1 << 15)
+#define RT5651_EQ_UPD                          (0x1 << 14)
+#define RT5651_EQ_UPD_BIT                      14
+#define RT5651_EQ_CD_MASK                      (0x1 << 13)
+#define RT5651_EQ_CD_SFT                       13
+#define RT5651_EQ_CD_DIS                       (0x0 << 13)
+#define RT5651_EQ_CD_EN                                (0x1 << 13)
+#define RT5651_EQ_DITH_MASK                    (0x3 << 8)
+#define RT5651_EQ_DITH_SFT                     8
+#define RT5651_EQ_DITH_NOR                     (0x0 << 8)
+#define RT5651_EQ_DITH_LSB                     (0x1 << 8)
+#define RT5651_EQ_DITH_LSB_1                   (0x2 << 8)
+#define RT5651_EQ_DITH_LSB_2                   (0x3 << 8)
+#define RT5651_EQ_CD_F                         (0x1 << 7)
+#define RT5651_EQ_CD_F_BIT                     7
+#define RT5651_EQ_STA_HP2                      (0x1 << 6)
+#define RT5651_EQ_STA_HP2_BIT                  6
+#define RT5651_EQ_STA_HP1                      (0x1 << 5)
+#define RT5651_EQ_STA_HP1_BIT                  5
+#define RT5651_EQ_STA_BP4                      (0x1 << 4)
+#define RT5651_EQ_STA_BP4_BIT                  4
+#define RT5651_EQ_STA_BP3                      (0x1 << 3)
+#define RT5651_EQ_STA_BP3_BIT                  3
+#define RT5651_EQ_STA_BP2                      (0x1 << 2)
+#define RT5651_EQ_STA_BP2_BIT                  2
+#define RT5651_EQ_STA_BP1                      (0x1 << 1)
+#define RT5651_EQ_STA_BP1_BIT                  1
+#define RT5651_EQ_STA_LP                       (0x1)
+#define RT5651_EQ_STA_LP_BIT                   0
+
+/* EQ Control 2 (0xb1) */
+#define RT5651_EQ_HPF1_M_MASK                  (0x1 << 8)
+#define RT5651_EQ_HPF1_M_SFT                   8
+#define RT5651_EQ_HPF1_M_HI                    (0x0 << 8)
+#define RT5651_EQ_HPF1_M_1ST                   (0x1 << 8)
+#define RT5651_EQ_LPF1_M_MASK                  (0x1 << 7)
+#define RT5651_EQ_LPF1_M_SFT                   7
+#define RT5651_EQ_LPF1_M_LO                    (0x0 << 7)
+#define RT5651_EQ_LPF1_M_1ST                   (0x1 << 7)
+#define RT5651_EQ_HPF2_MASK                    (0x1 << 6)
+#define RT5651_EQ_HPF2_SFT                     6
+#define RT5651_EQ_HPF2_DIS                     (0x0 << 6)
+#define RT5651_EQ_HPF2_EN                      (0x1 << 6)
+#define RT5651_EQ_HPF1_MASK                    (0x1 << 5)
+#define RT5651_EQ_HPF1_SFT                     5
+#define RT5651_EQ_HPF1_DIS                     (0x0 << 5)
+#define RT5651_EQ_HPF1_EN                      (0x1 << 5)
+#define RT5651_EQ_BPF4_MASK                    (0x1 << 4)
+#define RT5651_EQ_BPF4_SFT                     4
+#define RT5651_EQ_BPF4_DIS                     (0x0 << 4)
+#define RT5651_EQ_BPF4_EN                      (0x1 << 4)
+#define RT5651_EQ_BPF3_MASK                    (0x1 << 3)
+#define RT5651_EQ_BPF3_SFT                     3
+#define RT5651_EQ_BPF3_DIS                     (0x0 << 3)
+#define RT5651_EQ_BPF3_EN                      (0x1 << 3)
+#define RT5651_EQ_BPF2_MASK                    (0x1 << 2)
+#define RT5651_EQ_BPF2_SFT                     2
+#define RT5651_EQ_BPF2_DIS                     (0x0 << 2)
+#define RT5651_EQ_BPF2_EN                      (0x1 << 2)
+#define RT5651_EQ_BPF1_MASK                    (0x1 << 1)
+#define RT5651_EQ_BPF1_SFT                     1
+#define RT5651_EQ_BPF1_DIS                     (0x0 << 1)
+#define RT5651_EQ_BPF1_EN                      (0x1 << 1)
+#define RT5651_EQ_LPF_MASK                     (0x1)
+#define RT5651_EQ_LPF_SFT                      0
+#define RT5651_EQ_LPF_DIS                      (0x0)
+#define RT5651_EQ_LPF_EN                       (0x1)
+#define RT5651_EQ_CTRL_MASK                    (0x7f)
+
+/* Memory Test (0xb2) */
+#define RT5651_MT_MASK                         (0x1 << 15)
+#define RT5651_MT_SFT                          15
+#define RT5651_MT_DIS                          (0x0 << 15)
+#define RT5651_MT_EN                           (0x1 << 15)
+
+/* ALC Control 1 (0xb4) */
+#define RT5651_ALC_P_MASK                      (0x1 << 15)
+#define RT5651_ALC_P_SFT                       15
+#define RT5651_ALC_P_DAC                       (0x0 << 15)
+#define RT5651_ALC_P_ADC                       (0x1 << 15)
+#define RT5651_ALC_MASK                                (0x1 << 14)
+#define RT5651_ALC_SFT                         14
+#define RT5651_ALC_DIS                         (0x0 << 14)
+#define RT5651_ALC_EN                          (0x1 << 14)
+#define RT5651_ALC_UPD                         (0x1 << 13)
+#define RT5651_ALC_UPD_BIT                     13
+#define RT5651_ALC_AR_MASK                     (0x1f << 8)
+#define RT5651_ALC_AR_SFT                      8
+#define RT5651_ALC_R_MASK                      (0x7 << 5)
+#define RT5651_ALC_R_SFT                       5
+#define RT5651_ALC_R_48K                       (0x1 << 5)
+#define RT5651_ALC_R_96K                       (0x2 << 5)
+#define RT5651_ALC_R_192K                      (0x3 << 5)
+#define RT5651_ALC_R_441K                      (0x5 << 5)
+#define RT5651_ALC_R_882K                      (0x6 << 5)
+#define RT5651_ALC_R_1764K                     (0x7 << 5)
+#define RT5651_ALC_RC_MASK                     (0x1f)
+#define RT5651_ALC_RC_SFT                      0
+
+/* ALC Control 2 (0xb5) */
+#define RT5651_ALC_POB_MASK                    (0x3f << 8)
+#define RT5651_ALC_POB_SFT                     8
+#define RT5651_ALC_DRC_MASK                    (0x1 << 7)
+#define RT5651_ALC_DRC_SFT                     7
+#define RT5651_ALC_DRC_DIS                     (0x0 << 7)
+#define RT5651_ALC_DRC_EN                      (0x1 << 7)
+#define RT5651_ALC_CPR_MASK                    (0x3 << 5)
+#define RT5651_ALC_CPR_SFT                     5
+#define RT5651_ALC_CPR_1_1                     (0x0 << 5)
+#define RT5651_ALC_CPR_1_2                     (0x1 << 5)
+#define RT5651_ALC_CPR_1_4                     (0x2 << 5)
+#define RT5651_ALC_CPR_1_8                     (0x3 << 5)
+#define RT5651_ALC_PRB_MASK                    (0x1f)
+#define RT5651_ALC_PRB_SFT                     0
+
+/* ALC Control 3 (0xb6) */
+#define RT5651_ALC_NGB_MASK                    (0xf << 12)
+#define RT5651_ALC_NGB_SFT                     12
+#define RT5651_ALC_TAR_MASK                    (0x1f << 7)
+#define RT5651_ALC_TAR_SFT                     7
+#define RT5651_ALC_NG_MASK                     (0x1 << 6)
+#define RT5651_ALC_NG_SFT                      6
+#define RT5651_ALC_NG_DIS                      (0x0 << 6)
+#define RT5651_ALC_NG_EN                       (0x1 << 6)
+#define RT5651_ALC_NGH_MASK                    (0x1 << 5)
+#define RT5651_ALC_NGH_SFT                     5
+#define RT5651_ALC_NGH_DIS                     (0x0 << 5)
+#define RT5651_ALC_NGH_EN                      (0x1 << 5)
+#define RT5651_ALC_NGT_MASK                    (0x1f)
+#define RT5651_ALC_NGT_SFT                     0
+
+/* Jack Detect Control 1 (0xbb) */
+#define RT5651_JD_MASK                         (0x7 << 13)
+#define RT5651_JD_SFT                          13
+#define RT5651_JD_DIS                          (0x0 << 13)
+#define RT5651_JD_GPIO1                                (0x1 << 13)
+#define RT5651_JD_GPIO2                                (0x2 << 13)
+#define RT5651_JD_GPIO3                                (0x3 << 13)
+#define RT5651_JD_GPIO4                                (0x4 << 13)
+#define RT5651_JD_GPIO5                                (0x5 << 13)
+#define RT5651_JD_GPIO6                                (0x6 << 13)
+#define RT5651_JD_HP_MASK                      (0x1 << 11)
+#define RT5651_JD_HP_SFT                       11
+#define RT5651_JD_HP_DIS                       (0x0 << 11)
+#define RT5651_JD_HP_EN                                (0x1 << 11)
+#define RT5651_JD_HP_TRG_MASK                  (0x1 << 10)
+#define RT5651_JD_HP_TRG_SFT                   10
+#define RT5651_JD_HP_TRG_LO                    (0x0 << 10)
+#define RT5651_JD_HP_TRG_HI                    (0x1 << 10)
+#define RT5651_JD_SPL_MASK                     (0x1 << 9)
+#define RT5651_JD_SPL_SFT                      9
+#define RT5651_JD_SPL_DIS                      (0x0 << 9)
+#define RT5651_JD_SPL_EN                       (0x1 << 9)
+#define RT5651_JD_SPL_TRG_MASK                 (0x1 << 8)
+#define RT5651_JD_SPL_TRG_SFT                  8
+#define RT5651_JD_SPL_TRG_LO                   (0x0 << 8)
+#define RT5651_JD_SPL_TRG_HI                   (0x1 << 8)
+#define RT5651_JD_SPR_MASK                     (0x1 << 7)
+#define RT5651_JD_SPR_SFT                      7
+#define RT5651_JD_SPR_DIS                      (0x0 << 7)
+#define RT5651_JD_SPR_EN                       (0x1 << 7)
+#define RT5651_JD_SPR_TRG_MASK                 (0x1 << 6)
+#define RT5651_JD_SPR_TRG_SFT                  6
+#define RT5651_JD_SPR_TRG_LO                   (0x0 << 6)
+#define RT5651_JD_SPR_TRG_HI                   (0x1 << 6)
+#define RT5651_JD_LO_MASK                      (0x1 << 3)
+#define RT5651_JD_LO_SFT                       3
+#define RT5651_JD_LO_DIS                       (0x0 << 3)
+#define RT5651_JD_LO_EN                                (0x1 << 3)
+#define RT5651_JD_LO_TRG_MASK                  (0x1 << 2)
+#define RT5651_JD_LO_TRG_SFT                   2
+#define RT5651_JD_LO_TRG_LO                    (0x0 << 2)
+#define RT5651_JD_LO_TRG_HI                    (0x1 << 2)
+
+/* Jack Detect Control 2 (0xbc) */
+#define RT5651_JD_TRG_SEL_MASK                 (0x7 << 9)
+#define RT5651_JD_TRG_SEL_SFT                  9
+#define RT5651_JD_TRG_SEL_GPIO                 (0x0 << 9)
+#define RT5651_JD_TRG_SEL_JD1_1                        (0x1 << 9)
+#define RT5651_JD_TRG_SEL_JD1_2                        (0x2 << 9)
+#define RT5651_JD_TRG_SEL_JD2                  (0x3 << 9)
+#define RT5651_JD_TRG_SEL_JD3                  (0x4 << 9)
+#define RT5651_JD3_IRQ_EN                      (0x1 << 8)
+#define RT5651_JD3_IRQ_EN_SFT                  8
+#define RT5651_JD3_EN_STKY                     (0x1 << 7)
+#define RT5651_JD3_EN_STKY_SFT                 7
+#define RT5651_JD3_INV                         (0x1 << 6)
+#define RT5651_JD3_INV_SFT                     6
+
+/* IRQ Control 1 (0xbd) */
+#define RT5651_IRQ_JD_MASK                     (0x1 << 15)
+#define RT5651_IRQ_JD_SFT                      15
+#define RT5651_IRQ_JD_BP                       (0x0 << 15)
+#define RT5651_IRQ_JD_NOR                      (0x1 << 15)
+#define RT5651_JD_STKY_MASK                    (0x1 << 13)
+#define RT5651_JD_STKY_SFT                     13
+#define RT5651_JD_STKY_DIS                     (0x0 << 13)
+#define RT5651_JD_STKY_EN                      (0x1 << 13)
+#define RT5651_JD_P_MASK                       (0x1 << 11)
+#define RT5651_JD_P_SFT                                11
+#define RT5651_JD_P_NOR                                (0x0 << 11)
+#define RT5651_JD_P_INV                                (0x1 << 11)
+#define RT5651_JD1_1_IRQ_EN                    (0x1 << 9)
+#define RT5651_JD1_1_IRQ_EN_SFT                        9
+#define RT5651_JD1_1_EN_STKY                   (0x1 << 8)
+#define RT5651_JD1_1_EN_STKY_SFT                       8
+#define RT5651_JD1_1_INV                       (0x1 << 7)
+#define RT5651_JD1_1_INV_SFT                   7
+#define RT5651_JD1_2_IRQ_EN                    (0x1 << 6)
+#define RT5651_JD1_2_IRQ_EN_SFT                        6
+#define RT5651_JD1_2_EN_STKY                   (0x1 << 5)
+#define RT5651_JD1_2_EN_STKY_SFT                       5
+#define RT5651_JD1_2_INV                       (0x1 << 4)
+#define RT5651_JD1_2_INV_SFT                   4
+#define RT5651_JD2_IRQ_EN                      (0x1 << 3)
+#define RT5651_JD2_IRQ_EN_SFT                  3
+#define RT5651_JD2_EN_STKY                     (0x1 << 2)
+#define RT5651_JD2_EN_STKY_SFT                 2
+#define RT5651_JD2_INV                         (0x1 << 1)
+#define RT5651_JD2_INV_SFT                     1
+
+/* IRQ Control 2 (0xbe) */
+#define RT5651_IRQ_MB1_OC_MASK                 (0x1 << 15)
+#define RT5651_IRQ_MB1_OC_SFT                  15
+#define RT5651_IRQ_MB1_OC_BP                   (0x0 << 15)
+#define RT5651_IRQ_MB1_OC_NOR                  (0x1 << 15)
+#define RT5651_MB1_OC_STKY_MASK                        (0x1 << 11)
+#define RT5651_MB1_OC_STKY_SFT                 11
+#define RT5651_MB1_OC_STKY_DIS                 (0x0 << 11)
+#define RT5651_MB1_OC_STKY_EN                  (0x1 << 11)
+#define RT5651_MB1_OC_P_MASK                   (0x1 << 7)
+#define RT5651_MB1_OC_P_SFT                    7
+#define RT5651_MB1_OC_P_NOR                    (0x0 << 7)
+#define RT5651_MB1_OC_P_INV                    (0x1 << 7)
+#define RT5651_MB2_OC_P_MASK                   (0x1 << 6)
+#define RT5651_MB1_OC_CLR                      (0x1 << 3)
+#define RT5651_MB1_OC_CLR_SFT                  3
+#define RT5651_STA_GPIO8                       (0x1)
+#define RT5651_STA_GPIO8_BIT                   0
+
+/* Internal Status and GPIO status (0xbf) */
+#define RT5651_STA_JD3                         (0x1 << 15)
+#define RT5651_STA_JD3_BIT                     15
+#define RT5651_STA_JD2                         (0x1 << 14)
+#define RT5651_STA_JD2_BIT                     14
+#define RT5651_STA_JD1_2                       (0x1 << 13)
+#define RT5651_STA_JD1_2_BIT                   13
+#define RT5651_STA_JD1_1                       (0x1 << 12)
+#define RT5651_STA_JD1_1_BIT                   12
+#define RT5651_STA_GP7                         (0x1 << 11)
+#define RT5651_STA_GP7_BIT                     11
+#define RT5651_STA_GP6                         (0x1 << 10)
+#define RT5651_STA_GP6_BIT                     10
+#define RT5651_STA_GP5                         (0x1 << 9)
+#define RT5651_STA_GP5_BIT                     9
+#define RT5651_STA_GP1                         (0x1 << 8)
+#define RT5651_STA_GP1_BIT                     8
+#define RT5651_STA_GP2                         (0x1 << 7)
+#define RT5651_STA_GP2_BIT                     7
+#define RT5651_STA_GP3                         (0x1 << 6)
+#define RT5651_STA_GP3_BIT                     6
+#define RT5651_STA_GP4                         (0x1 << 5)
+#define RT5651_STA_GP4_BIT                     5
+#define RT5651_STA_GP_JD                       (0x1 << 4)
+#define RT5651_STA_GP_JD_BIT                   4
+
+/* GPIO Control 1 (0xc0) */
+#define RT5651_GP1_PIN_MASK                    (0x1 << 15)
+#define RT5651_GP1_PIN_SFT                     15
+#define RT5651_GP1_PIN_GPIO1                   (0x0 << 15)
+#define RT5651_GP1_PIN_IRQ                     (0x1 << 15)
+#define RT5651_GP2_PIN_MASK                    (0x1 << 14)
+#define RT5651_GP2_PIN_SFT                     14
+#define RT5651_GP2_PIN_GPIO2                   (0x0 << 14)
+#define RT5651_GP2_PIN_DMIC1_SCL               (0x1 << 14)
+#define RT5651_GPIO_M_MASK                     (0x1 << 9)
+#define RT5651_GPIO_M_SFT                      9
+#define RT5651_GPIO_M_FLT                      (0x0 << 9)
+#define RT5651_GPIO_M_PH                       (0x1 << 9)
+#define RT5651_I2S2_SEL_MASK                   (0x1 << 8)
+#define RT5651_I2S2_SEL_SFT                    8
+#define RT5651_I2S2_SEL_I2S                    (0x0 << 8)
+#define RT5651_I2S2_SEL_GPIO                   (0x1 << 8)
+#define RT5651_GP5_PIN_MASK                    (0x1 << 7)
+#define RT5651_GP5_PIN_SFT                     7
+#define RT5651_GP5_PIN_GPIO5                   (0x0 << 7)
+#define RT5651_GP5_PIN_IRQ                     (0x1 << 7)
+#define RT5651_GP6_PIN_MASK                    (0x1 << 6)
+#define RT5651_GP6_PIN_SFT                     6
+#define RT5651_GP6_PIN_GPIO6                   (0x0 << 6)
+#define RT5651_GP6_PIN_DMIC_SDA                        (0x1 << 6)
+#define RT5651_GP7_PIN_MASK                    (0x1 << 5)
+#define RT5651_GP7_PIN_SFT                     5
+#define RT5651_GP7_PIN_GPIO7                   (0x0 << 5)
+#define RT5651_GP7_PIN_IRQ                     (0x1 << 5)
+#define RT5651_GP8_PIN_MASK                    (0x1 << 4)
+#define RT5651_GP8_PIN_SFT                     4
+#define RT5651_GP8_PIN_GPIO8                   (0x0 << 4)
+#define RT5651_GP8_PIN_DMIC_SDA                        (0x1 << 4)
+#define RT5651_GPIO_PDM_SEL_MASK               (0x1 << 3)
+#define RT5651_GPIO_PDM_SEL_SFT                        3
+#define RT5651_GPIO_PDM_SEL_GPIO               (0x0 << 3)
+#define RT5651_GPIO_PDM_SEL_PDM                        (0x1 << 3)
+
+/* GPIO Control 2 (0xc1) */
+#define RT5651_GP5_DR_MASK                     (0x1 << 14)
+#define RT5651_GP5_DR_SFT                      14
+#define RT5651_GP5_DR_IN                       (0x0 << 14)
+#define RT5651_GP5_DR_OUT                      (0x1 << 14)
+#define RT5651_GP5_OUT_MASK                    (0x1 << 13)
+#define RT5651_GP5_OUT_SFT                     13
+#define RT5651_GP5_OUT_LO                      (0x0 << 13)
+#define RT5651_GP5_OUT_HI                      (0x1 << 13)
+#define RT5651_GP5_P_MASK                      (0x1 << 12)
+#define RT5651_GP5_P_SFT                       12
+#define RT5651_GP5_P_NOR                       (0x0 << 12)
+#define RT5651_GP5_P_INV                       (0x1 << 12)
+#define RT5651_GP4_DR_MASK                     (0x1 << 11)
+#define RT5651_GP4_DR_SFT                      11
+#define RT5651_GP4_DR_IN                       (0x0 << 11)
+#define RT5651_GP4_DR_OUT                      (0x1 << 11)
+#define RT5651_GP4_OUT_MASK                    (0x1 << 10)
+#define RT5651_GP4_OUT_SFT                     10
+#define RT5651_GP4_OUT_LO                      (0x0 << 10)
+#define RT5651_GP4_OUT_HI                      (0x1 << 10)
+#define RT5651_GP4_P_MASK                      (0x1 << 9)
+#define RT5651_GP4_P_SFT                       9
+#define RT5651_GP4_P_NOR                       (0x0 << 9)
+#define RT5651_GP4_P_INV                       (0x1 << 9)
+#define RT5651_GP3_DR_MASK                     (0x1 << 8)
+#define RT5651_GP3_DR_SFT                      8
+#define RT5651_GP3_DR_IN                       (0x0 << 8)
+#define RT5651_GP3_DR_OUT                      (0x1 << 8)
+#define RT5651_GP3_OUT_MASK                    (0x1 << 7)
+#define RT5651_GP3_OUT_SFT                     7
+#define RT5651_GP3_OUT_LO                      (0x0 << 7)
+#define RT5651_GP3_OUT_HI                      (0x1 << 7)
+#define RT5651_GP3_P_MASK                      (0x1 << 6)
+#define RT5651_GP3_P_SFT                       6
+#define RT5651_GP3_P_NOR                       (0x0 << 6)
+#define RT5651_GP3_P_INV                       (0x1 << 6)
+#define RT5651_GP2_DR_MASK                     (0x1 << 5)
+#define RT5651_GP2_DR_SFT                      5
+#define RT5651_GP2_DR_IN                       (0x0 << 5)
+#define RT5651_GP2_DR_OUT                      (0x1 << 5)
+#define RT5651_GP2_OUT_MASK                    (0x1 << 4)
+#define RT5651_GP2_OUT_SFT                     4
+#define RT5651_GP2_OUT_LO                      (0x0 << 4)
+#define RT5651_GP2_OUT_HI                      (0x1 << 4)
+#define RT5651_GP2_P_MASK                      (0x1 << 3)
+#define RT5651_GP2_P_SFT                       3
+#define RT5651_GP2_P_NOR                       (0x0 << 3)
+#define RT5651_GP2_P_INV                       (0x1 << 3)
+#define RT5651_GP1_DR_MASK                     (0x1 << 2)
+#define RT5651_GP1_DR_SFT                      2
+#define RT5651_GP1_DR_IN                       (0x0 << 2)
+#define RT5651_GP1_DR_OUT                      (0x1 << 2)
+#define RT5651_GP1_OUT_MASK                    (0x1 << 1)
+#define RT5651_GP1_OUT_SFT                     1
+#define RT5651_GP1_OUT_LO                      (0x0 << 1)
+#define RT5651_GP1_OUT_HI                      (0x1 << 1)
+#define RT5651_GP1_P_MASK                      (0x1)
+#define RT5651_GP1_P_SFT                       0
+#define RT5651_GP1_P_NOR                       (0x0)
+#define RT5651_GP1_P_INV                       (0x1)
+
+/* GPIO Control 3 (0xc2) */
+#define RT5651_GP8_DR_MASK                     (0x1 << 8)
+#define RT5651_GP8_DR_SFT                      8
+#define RT5651_GP8_DR_IN                       (0x0 << 8)
+#define RT5651_GP8_DR_OUT                      (0x1 << 8)
+#define RT5651_GP8_OUT_MASK                    (0x1 << 7)
+#define RT5651_GP8_OUT_SFT                     7
+#define RT5651_GP8_OUT_LO                      (0x0 << 7)
+#define RT5651_GP8_OUT_HI                      (0x1 << 7)
+#define RT5651_GP8_P_MASK                      (0x1 << 6)
+#define RT5651_GP8_P_SFT                       6
+#define RT5651_GP8_P_NOR                       (0x0 << 6)
+#define RT5651_GP8_P_INV                       (0x1 << 6)
+#define RT5651_GP7_DR_MASK                     (0x1 << 5)
+#define RT5651_GP7_DR_SFT                      5
+#define RT5651_GP7_DR_IN                       (0x0 << 5)
+#define RT5651_GP7_DR_OUT                      (0x1 << 5)
+#define RT5651_GP7_OUT_MASK                    (0x1 << 4)
+#define RT5651_GP7_OUT_SFT                     4
+#define RT5651_GP7_OUT_LO                      (0x0 << 4)
+#define RT5651_GP7_OUT_HI                      (0x1 << 4)
+#define RT5651_GP7_P_MASK                      (0x1 << 3)
+#define RT5651_GP7_P_SFT                       3
+#define RT5651_GP7_P_NOR                       (0x0 << 3)
+#define RT5651_GP7_P_INV                       (0x1 << 3)
+#define RT5651_GP6_DR_MASK                     (0x1 << 2)
+#define RT5651_GP6_DR_SFT                      2
+#define RT5651_GP6_DR_IN                       (0x0 << 2)
+#define RT5651_GP6_DR_OUT                      (0x1 << 2)
+#define RT5651_GP6_OUT_MASK                    (0x1 << 1)
+#define RT5651_GP6_OUT_SFT                     1
+#define RT5651_GP6_OUT_LO                      (0x0 << 1)
+#define RT5651_GP6_OUT_HI                      (0x1 << 1)
+#define RT5651_GP6_P_MASK                      (0x1)
+#define RT5651_GP6_P_SFT                       0
+#define RT5651_GP6_P_NOR                       (0x0)
+#define RT5651_GP6_P_INV                       (0x1)
+
+/* Scramble Control (0xce) */
+#define RT5651_SCB_SWAP_MASK                   (0x1 << 15)
+#define RT5651_SCB_SWAP_SFT                    15
+#define RT5651_SCB_SWAP_DIS                    (0x0 << 15)
+#define RT5651_SCB_SWAP_EN                     (0x1 << 15)
+#define RT5651_SCB_MASK                                (0x1 << 14)
+#define RT5651_SCB_SFT                         14
+#define RT5651_SCB_DIS                         (0x0 << 14)
+#define RT5651_SCB_EN                          (0x1 << 14)
+
+/* Baseback Control (0xcf) */
+#define RT5651_BB_MASK                         (0x1 << 15)
+#define RT5651_BB_SFT                          15
+#define RT5651_BB_DIS                          (0x0 << 15)
+#define RT5651_BB_EN                           (0x1 << 15)
+#define RT5651_BB_CT_MASK                      (0x7 << 12)
+#define RT5651_BB_CT_SFT                       12
+#define RT5651_BB_CT_A                         (0x0 << 12)
+#define RT5651_BB_CT_B                         (0x1 << 12)
+#define RT5651_BB_CT_C                         (0x2 << 12)
+#define RT5651_BB_CT_D                         (0x3 << 12)
+#define RT5651_M_BB_L_MASK                     (0x1 << 9)
+#define RT5651_M_BB_L_SFT                      9
+#define RT5651_M_BB_R_MASK                     (0x1 << 8)
+#define RT5651_M_BB_R_SFT                      8
+#define RT5651_M_BB_HPF_L_MASK                 (0x1 << 7)
+#define RT5651_M_BB_HPF_L_SFT                  7
+#define RT5651_M_BB_HPF_R_MASK                 (0x1 << 6)
+#define RT5651_M_BB_HPF_R_SFT                  6
+#define RT5651_G_BB_BST_MASK                   (0x3f)
+#define RT5651_G_BB_BST_SFT                    0
+
+/* MP3 Plus Control 1 (0xd0) */
+#define RT5651_M_MP3_L_MASK                    (0x1 << 15)
+#define RT5651_M_MP3_L_SFT                     15
+#define RT5651_M_MP3_R_MASK                    (0x1 << 14)
+#define RT5651_M_MP3_R_SFT                     14
+#define RT5651_M_MP3_MASK                      (0x1 << 13)
+#define RT5651_M_MP3_SFT                       13
+#define RT5651_M_MP3_DIS                       (0x0 << 13)
+#define RT5651_M_MP3_EN                                (0x1 << 13)
+#define RT5651_EG_MP3_MASK                     (0x1f << 8)
+#define RT5651_EG_MP3_SFT                      8
+#define RT5651_MP3_HLP_MASK                    (0x1 << 7)
+#define RT5651_MP3_HLP_SFT                     7
+#define RT5651_MP3_HLP_DIS                     (0x0 << 7)
+#define RT5651_MP3_HLP_EN                      (0x1 << 7)
+#define RT5651_M_MP3_ORG_L_MASK                        (0x1 << 6)
+#define RT5651_M_MP3_ORG_L_SFT                 6
+#define RT5651_M_MP3_ORG_R_MASK                        (0x1 << 5)
+#define RT5651_M_MP3_ORG_R_SFT                 5
+
+/* MP3 Plus Control 2 (0xd1) */
+#define RT5651_MP3_WT_MASK                     (0x1 << 13)
+#define RT5651_MP3_WT_SFT                      13
+#define RT5651_MP3_WT_1_4                      (0x0 << 13)
+#define RT5651_MP3_WT_1_2                      (0x1 << 13)
+#define RT5651_OG_MP3_MASK                     (0x1f << 8)
+#define RT5651_OG_MP3_SFT                      8
+#define RT5651_HG_MP3_MASK                     (0x3f)
+#define RT5651_HG_MP3_SFT                      0
+
+/* 3D HP Control 1 (0xd2) */
+#define RT5651_3D_CF_MASK                      (0x1 << 15)
+#define RT5651_3D_CF_SFT                       15
+#define RT5651_3D_CF_DIS                       (0x0 << 15)
+#define RT5651_3D_CF_EN                                (0x1 << 15)
+#define RT5651_3D_HP_MASK                      (0x1 << 14)
+#define RT5651_3D_HP_SFT                       14
+#define RT5651_3D_HP_DIS                       (0x0 << 14)
+#define RT5651_3D_HP_EN                                (0x1 << 14)
+#define RT5651_3D_BT_MASK                      (0x1 << 13)
+#define RT5651_3D_BT_SFT                       13
+#define RT5651_3D_BT_DIS                       (0x0 << 13)
+#define RT5651_3D_BT_EN                                (0x1 << 13)
+#define RT5651_3D_1F_MIX_MASK                  (0x3 << 11)
+#define RT5651_3D_1F_MIX_SFT                   11
+#define RT5651_3D_HP_M_MASK                    (0x1 << 10)
+#define RT5651_3D_HP_M_SFT                     10
+#define RT5651_3D_HP_M_SUR                     (0x0 << 10)
+#define RT5651_3D_HP_M_FRO                     (0x1 << 10)
+#define RT5651_M_3D_HRTF_MASK                  (0x1 << 9)
+#define RT5651_M_3D_HRTF_SFT                   9
+#define RT5651_M_3D_D2H_MASK                   (0x1 << 8)
+#define RT5651_M_3D_D2H_SFT                    8
+#define RT5651_M_3D_D2R_MASK                   (0x1 << 7)
+#define RT5651_M_3D_D2R_SFT                    7
+#define RT5651_M_3D_REVB_MASK                  (0x1 << 6)
+#define RT5651_M_3D_REVB_SFT                   6
+
+/* Adjustable high pass filter control 1 (0xd3) */
+#define RT5651_2ND_HPF_MASK                    (0x1 << 15)
+#define RT5651_2ND_HPF_SFT                     15
+#define RT5651_2ND_HPF_DIS                     (0x0 << 15)
+#define RT5651_2ND_HPF_EN                      (0x1 << 15)
+#define RT5651_HPF_CF_L_MASK                   (0x7 << 12)
+#define RT5651_HPF_CF_L_SFT                    12
+#define RT5651_HPF_CF_R_MASK                   (0x7 << 8)
+#define RT5651_HPF_CF_R_SFT                    8
+#define RT5651_ZD_T_MASK                       (0x3 << 6)
+#define RT5651_ZD_T_SFT                                6
+#define RT5651_ZD_F_MASK                       (0x3 << 4)
+#define RT5651_ZD_F_SFT                                4
+#define RT5651_ZD_F_IM                         (0x0 << 4)
+#define RT5651_ZD_F_ZC_IM                      (0x1 << 4)
+#define RT5651_ZD_F_ZC_IOD                     (0x2 << 4)
+#define RT5651_ZD_F_UN                         (0x3 << 4)
+
+/* Adjustable high pass filter control 2 (0xd4) */
+#define RT5651_HPF_CF_L_NUM_MASK               (0x3f << 8)
+#define RT5651_HPF_CF_L_NUM_SFT                        8
+#define RT5651_HPF_CF_R_NUM_MASK               (0x3f)
+#define RT5651_HPF_CF_R_NUM_SFT                        0
+
+/* HP calibration control and Amp detection (0xd6) */
+#define RT5651_SI_DAC_MASK                     (0x1 << 11)
+#define RT5651_SI_DAC_SFT                      11
+#define RT5651_SI_DAC_AUTO                     (0x0 << 11)
+#define RT5651_SI_DAC_TEST                     (0x1 << 11)
+#define RT5651_DC_CAL_M_MASK                   (0x1 << 10)
+#define RT5651_DC_CAL_M_SFT                    10
+#define RT5651_DC_CAL_M_NOR                    (0x0 << 10)
+#define RT5651_DC_CAL_M_CAL                    (0x1 << 10)
+#define RT5651_DC_CAL_MASK                     (0x1 << 9)
+#define RT5651_DC_CAL_SFT                      9
+#define RT5651_DC_CAL_DIS                      (0x0 << 9)
+#define RT5651_DC_CAL_EN                       (0x1 << 9)
+#define RT5651_HPD_RCV_MASK                    (0x7 << 6)
+#define RT5651_HPD_RCV_SFT                     6
+#define RT5651_HPD_PS_MASK                     (0x1 << 5)
+#define RT5651_HPD_PS_SFT                      5
+#define RT5651_HPD_PS_DIS                      (0x0 << 5)
+#define RT5651_HPD_PS_EN                       (0x1 << 5)
+#define RT5651_CAL_M_MASK                      (0x1 << 4)
+#define RT5651_CAL_M_SFT                       4
+#define RT5651_CAL_M_DEP                       (0x0 << 4)
+#define RT5651_CAL_M_CAL                       (0x1 << 4)
+#define RT5651_CAL_MASK                                (0x1 << 3)
+#define RT5651_CAL_SFT                         3
+#define RT5651_CAL_DIS                         (0x0 << 3)
+#define RT5651_CAL_EN                          (0x1 << 3)
+#define RT5651_CAL_TEST_MASK                   (0x1 << 2)
+#define RT5651_CAL_TEST_SFT                    2
+#define RT5651_CAL_TEST_DIS                    (0x0 << 2)
+#define RT5651_CAL_TEST_EN                     (0x1 << 2)
+#define RT5651_CAL_P_MASK                      (0x3)
+#define RT5651_CAL_P_SFT                       0
+#define RT5651_CAL_P_NONE                      (0x0)
+#define RT5651_CAL_P_CAL                       (0x1)
+#define RT5651_CAL_P_DAC_CAL                   (0x2)
+
+/* Soft volume and zero cross control 1 (0xd9) */
+#define RT5651_SV_MASK                         (0x1 << 15)
+#define RT5651_SV_SFT                          15
+#define RT5651_SV_DIS                          (0x0 << 15)
+#define RT5651_SV_EN                           (0x1 << 15)
+#define RT5651_OUT_SV_MASK                     (0x1 << 13)
+#define RT5651_OUT_SV_SFT                      13
+#define RT5651_OUT_SV_DIS                      (0x0 << 13)
+#define RT5651_OUT_SV_EN                       (0x1 << 13)
+#define RT5651_HP_SV_MASK                      (0x1 << 12)
+#define RT5651_HP_SV_SFT                       12
+#define RT5651_HP_SV_DIS                       (0x0 << 12)
+#define RT5651_HP_SV_EN                                (0x1 << 12)
+#define RT5651_ZCD_DIG_MASK                    (0x1 << 11)
+#define RT5651_ZCD_DIG_SFT                     11
+#define RT5651_ZCD_DIG_DIS                     (0x0 << 11)
+#define RT5651_ZCD_DIG_EN                      (0x1 << 11)
+#define RT5651_ZCD_MASK                                (0x1 << 10)
+#define RT5651_ZCD_SFT                         10
+#define RT5651_ZCD_PD                          (0x0 << 10)
+#define RT5651_ZCD_PU                          (0x1 << 10)
+#define RT5651_M_ZCD_MASK                      (0x3f << 4)
+#define RT5651_M_ZCD_SFT                       4
+#define RT5651_M_ZCD_OM_L                      (0x1 << 7)
+#define RT5651_M_ZCD_OM_R                      (0x1 << 6)
+#define RT5651_M_ZCD_RM_L                      (0x1 << 5)
+#define RT5651_M_ZCD_RM_R                      (0x1 << 4)
+#define RT5651_SV_DLY_MASK                     (0xf)
+#define RT5651_SV_DLY_SFT                      0
+
+/* Soft volume and zero cross control 2 (0xda) */
+#define RT5651_ZCD_HP_MASK                     (0x1 << 15)
+#define RT5651_ZCD_HP_SFT                      15
+#define RT5651_ZCD_HP_DIS                      (0x0 << 15)
+#define RT5651_ZCD_HP_EN                       (0x1 << 15)
+
+/* Digital Misc Control (0xfa) */
+#define RT5651_I2S2_MS_SP_MASK                 (0x1 << 8)
+#define RT5651_I2S2_MS_SP_SEL                  8
+#define RT5651_I2S2_MS_SP_64                   (0x0 << 8)
+#define RT5651_I2S2_MS_SP_50                   (0x1 << 8)
+#define RT5651_CLK_DET_EN                      (0x1 << 3)
+#define RT5651_CLK_DET_EN_SFT                  3
+#define RT5651_AMP_DET_EN                      (0x1 << 1)
+#define RT5651_AMP_DET_EN_SFT                  1
+#define RT5651_D_GATE_EN                       (0x1)
+#define RT5651_D_GATE_EN_SFT                   0
+
+/* Codec Private Register definition */
+/* 3D Speaker Control (0x63) */
+#define RT5651_3D_SPK_MASK                     (0x1 << 15)
+#define RT5651_3D_SPK_SFT                      15
+#define RT5651_3D_SPK_DIS                      (0x0 << 15)
+#define RT5651_3D_SPK_EN                       (0x1 << 15)
+#define RT5651_3D_SPK_M_MASK                   (0x3 << 13)
+#define RT5651_3D_SPK_M_SFT                    13
+#define RT5651_3D_SPK_CG_MASK                  (0x1f << 8)
+#define RT5651_3D_SPK_CG_SFT                   8
+#define RT5651_3D_SPK_SG_MASK                  (0x1f)
+#define RT5651_3D_SPK_SG_SFT                   0
+
+/* Wind Noise Detection Control 1 (0x6c) */
+#define RT5651_WND_MASK                                (0x1 << 15)
+#define RT5651_WND_SFT                         15
+#define RT5651_WND_DIS                         (0x0 << 15)
+#define RT5651_WND_EN                          (0x1 << 15)
+
+/* Wind Noise Detection Control 2 (0x6d) */
+#define RT5651_WND_FC_NW_MASK                  (0x3f << 10)
+#define RT5651_WND_FC_NW_SFT                   10
+#define RT5651_WND_FC_WK_MASK                  (0x3f << 4)
+#define RT5651_WND_FC_WK_SFT                   4
+
+/* Wind Noise Detection Control 3 (0x6e) */
+#define RT5651_HPF_FC_MASK                     (0x3f << 6)
+#define RT5651_HPF_FC_SFT                      6
+#define RT5651_WND_FC_ST_MASK                  (0x3f)
+#define RT5651_WND_FC_ST_SFT                   0
+
+/* Wind Noise Detection Control 4 (0x6f) */
+#define RT5651_WND_TH_LO_MASK                  (0x3ff)
+#define RT5651_WND_TH_LO_SFT                   0
+
+/* Wind Noise Detection Control 5 (0x70) */
+#define RT5651_WND_TH_HI_MASK                  (0x3ff)
+#define RT5651_WND_TH_HI_SFT                   0
+
+/* Wind Noise Detection Control 8 (0x73) */
+#define RT5651_WND_WIND_MASK                   (0x1 << 13) /* Read-Only */
+#define RT5651_WND_WIND_SFT                    13
+#define RT5651_WND_STRONG_MASK                 (0x1 << 12) /* Read-Only */
+#define RT5651_WND_STRONG_SFT                  12
+enum {
+       RT5651_NO_WIND,
+       RT5651_BREEZE,
+       RT5651_STORM,
+};
+
+/* Dipole Speaker Interface (0x75) */
+#define RT5651_DP_ATT_MASK                     (0x3 << 14)
+#define RT5651_DP_ATT_SFT                      14
+#define RT5651_DP_SPK_MASK                     (0x1 << 10)
+#define RT5651_DP_SPK_SFT                      10
+#define RT5651_DP_SPK_DIS                      (0x0 << 10)
+#define RT5651_DP_SPK_EN                       (0x1 << 10)
+
+/* EQ Pre Volume Control (0xb3) */
+#define RT5651_EQ_PRE_VOL_MASK                 (0xffff)
+#define RT5651_EQ_PRE_VOL_SFT                  0
+
+/* EQ Post Volume Control (0xb4) */
+#define RT5651_EQ_PST_VOL_MASK                 (0xffff)
+#define RT5651_EQ_PST_VOL_SFT                  0
+
+/* System Clock Source */
+enum {
+       RT5651_SCLK_S_MCLK,
+       RT5651_SCLK_S_PLL1,
+       RT5651_SCLK_S_RCCLK,
+};
+
+/* PLL1 Source */
+enum {
+       RT5651_PLL1_S_MCLK,
+       RT5651_PLL1_S_BCLK1,
+       RT5651_PLL1_S_BCLK2,
+};
+
+enum {
+       RT5651_AIF1,
+       RT5651_AIF2,
+       RT5651_AIFS,
+};
+
+struct rt5651_pll_code {
+       bool m_bp; /* Indicates bypass m code or not. */
+       int m_code;
+       int n_code;
+       int k_code;
+};
+
+struct rt5651_priv {
+       struct snd_soc_codec *codec;
+       struct rt5651_platform_data pdata;
+       struct regmap *regmap;
+
+       int sysclk;
+       int sysclk_src;
+       int lrck[RT5651_AIFS];
+       int bclk[RT5651_AIFS];
+       int master[RT5651_AIFS];
+
+       int pll_src;
+       int pll_in;
+       int pll_out;
+
+       int dmic_en;
+       bool hp_mute;
+};
+
+#endif /* __RT5651_H__ */
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
new file mode 100644 (file)
index 0000000..833231e
--- /dev/null
@@ -0,0 +1,3498 @@
+/*
+ * rt5677.c  --  RT5677 ALSA SoC audio codec driver
+ *
+ * Copyright 2013 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.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/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.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 "rt5677.h"
+
+#define RT5677_DEVICE_ID 0x6327
+
+#define RT5677_PR_RANGE_BASE (0xff + 1)
+#define RT5677_PR_SPACING 0x100
+
+#define RT5677_PR_BASE (RT5677_PR_RANGE_BASE + (0 * RT5677_PR_SPACING))
+
+static const struct regmap_range_cfg rt5677_ranges[] = {
+       {
+               .name = "PR",
+               .range_min = RT5677_PR_BASE,
+               .range_max = RT5677_PR_BASE + 0xfd,
+               .selector_reg = RT5677_PRIV_INDEX,
+               .selector_mask = 0xff,
+               .selector_shift = 0x0,
+               .window_start = RT5677_PRIV_DATA,
+               .window_len = 0x1,
+       },
+};
+
+static const struct reg_default init_list[] = {
+       {RT5677_PR_BASE + 0x3d, 0x364d},
+       {RT5677_PR_BASE + 0x17, 0x4fc0},
+       {RT5677_PR_BASE + 0x13, 0x0312},
+       {RT5677_PR_BASE + 0x1e, 0x0000},
+       {RT5677_PR_BASE + 0x12, 0x0eaa},
+       {RT5677_PR_BASE + 0x14, 0x018a},
+};
+#define RT5677_INIT_REG_LEN ARRAY_SIZE(init_list)
+
+static const struct reg_default rt5677_reg[] = {
+       {RT5677_RESET                   , 0x0000},
+       {RT5677_LOUT1                   , 0xa800},
+       {RT5677_IN1                     , 0x0000},
+       {RT5677_MICBIAS                 , 0x0000},
+       {RT5677_SLIMBUS_PARAM           , 0x0000},
+       {RT5677_SLIMBUS_RX              , 0x0000},
+       {RT5677_SLIMBUS_CTRL            , 0x0000},
+       {RT5677_SIDETONE_CTRL           , 0x000b},
+       {RT5677_ANA_DAC1_2_3_SRC        , 0x0000},
+       {RT5677_IF_DSP_DAC3_4_MIXER     , 0x1111},
+       {RT5677_DAC4_DIG_VOL            , 0xafaf},
+       {RT5677_DAC3_DIG_VOL            , 0xafaf},
+       {RT5677_DAC1_DIG_VOL            , 0xafaf},
+       {RT5677_DAC2_DIG_VOL            , 0xafaf},
+       {RT5677_IF_DSP_DAC2_MIXER       , 0x0011},
+       {RT5677_STO1_ADC_DIG_VOL        , 0x2f2f},
+       {RT5677_MONO_ADC_DIG_VOL        , 0x2f2f},
+       {RT5677_STO1_2_ADC_BST          , 0x0000},
+       {RT5677_STO2_ADC_DIG_VOL        , 0x2f2f},
+       {RT5677_ADC_BST_CTRL2           , 0x0000},
+       {RT5677_STO3_4_ADC_BST          , 0x0000},
+       {RT5677_STO3_ADC_DIG_VOL        , 0x2f2f},
+       {RT5677_STO4_ADC_DIG_VOL        , 0x2f2f},
+       {RT5677_STO4_ADC_MIXER          , 0xd4c0},
+       {RT5677_STO3_ADC_MIXER          , 0xd4c0},
+       {RT5677_STO2_ADC_MIXER          , 0xd4c0},
+       {RT5677_STO1_ADC_MIXER          , 0xd4c0},
+       {RT5677_MONO_ADC_MIXER          , 0xd4d1},
+       {RT5677_ADC_IF_DSP_DAC1_MIXER   , 0x8080},
+       {RT5677_STO1_DAC_MIXER          , 0xaaaa},
+       {RT5677_MONO_DAC_MIXER          , 0xaaaa},
+       {RT5677_DD1_MIXER               , 0xaaaa},
+       {RT5677_DD2_MIXER               , 0xaaaa},
+       {RT5677_IF3_DATA                , 0x0000},
+       {RT5677_IF4_DATA                , 0x0000},
+       {RT5677_PDM_OUT_CTRL            , 0x8888},
+       {RT5677_PDM_DATA_CTRL1          , 0x0000},
+       {RT5677_PDM_DATA_CTRL2          , 0x0000},
+       {RT5677_PDM1_DATA_CTRL2         , 0x0000},
+       {RT5677_PDM1_DATA_CTRL3         , 0x0000},
+       {RT5677_PDM1_DATA_CTRL4         , 0x0000},
+       {RT5677_PDM2_DATA_CTRL2         , 0x0000},
+       {RT5677_PDM2_DATA_CTRL3         , 0x0000},
+       {RT5677_PDM2_DATA_CTRL4         , 0x0000},
+       {RT5677_TDM1_CTRL1              , 0x0300},
+       {RT5677_TDM1_CTRL2              , 0x0000},
+       {RT5677_TDM1_CTRL3              , 0x4000},
+       {RT5677_TDM1_CTRL4              , 0x0123},
+       {RT5677_TDM1_CTRL5              , 0x4567},
+       {RT5677_TDM2_CTRL1              , 0x0300},
+       {RT5677_TDM2_CTRL2              , 0x0000},
+       {RT5677_TDM2_CTRL3              , 0x4000},
+       {RT5677_TDM2_CTRL4              , 0x0123},
+       {RT5677_TDM2_CTRL5              , 0x4567},
+       {RT5677_I2C_MASTER_CTRL1        , 0x0001},
+       {RT5677_I2C_MASTER_CTRL2        , 0x0000},
+       {RT5677_I2C_MASTER_CTRL3        , 0x0000},
+       {RT5677_I2C_MASTER_CTRL4        , 0x0000},
+       {RT5677_I2C_MASTER_CTRL5        , 0x0000},
+       {RT5677_I2C_MASTER_CTRL6        , 0x0000},
+       {RT5677_I2C_MASTER_CTRL7        , 0x0000},
+       {RT5677_I2C_MASTER_CTRL8        , 0x0000},
+       {RT5677_DMIC_CTRL1              , 0x1505},
+       {RT5677_DMIC_CTRL2              , 0x0055},
+       {RT5677_HAP_GENE_CTRL1          , 0x0111},
+       {RT5677_HAP_GENE_CTRL2          , 0x0064},
+       {RT5677_HAP_GENE_CTRL3          , 0xef0e},
+       {RT5677_HAP_GENE_CTRL4          , 0xf0f0},
+       {RT5677_HAP_GENE_CTRL5          , 0xef0e},
+       {RT5677_HAP_GENE_CTRL6          , 0xf0f0},
+       {RT5677_HAP_GENE_CTRL7          , 0xef0e},
+       {RT5677_HAP_GENE_CTRL8          , 0xf0f0},
+       {RT5677_HAP_GENE_CTRL9          , 0xf000},
+       {RT5677_HAP_GENE_CTRL10         , 0x0000},
+       {RT5677_PWR_DIG1                , 0x0000},
+       {RT5677_PWR_DIG2                , 0x0000},
+       {RT5677_PWR_ANLG1               , 0x0055},
+       {RT5677_PWR_ANLG2               , 0x0000},
+       {RT5677_PWR_DSP1                , 0x0001},
+       {RT5677_PWR_DSP_ST              , 0x0000},
+       {RT5677_PWR_DSP2                , 0x0000},
+       {RT5677_ADC_DAC_HPF_CTRL1       , 0x0e00},
+       {RT5677_PRIV_INDEX              , 0x0000},
+       {RT5677_PRIV_DATA               , 0x0000},
+       {RT5677_I2S4_SDP                , 0x8000},
+       {RT5677_I2S1_SDP                , 0x8000},
+       {RT5677_I2S2_SDP                , 0x8000},
+       {RT5677_I2S3_SDP                , 0x8000},
+       {RT5677_CLK_TREE_CTRL1          , 0x1111},
+       {RT5677_CLK_TREE_CTRL2          , 0x1111},
+       {RT5677_CLK_TREE_CTRL3          , 0x0000},
+       {RT5677_PLL1_CTRL1              , 0x0000},
+       {RT5677_PLL1_CTRL2              , 0x0000},
+       {RT5677_PLL2_CTRL1              , 0x0c60},
+       {RT5677_PLL2_CTRL2              , 0x2000},
+       {RT5677_GLB_CLK1                , 0x0000},
+       {RT5677_GLB_CLK2                , 0x0000},
+       {RT5677_ASRC_1                  , 0x0000},
+       {RT5677_ASRC_2                  , 0x0000},
+       {RT5677_ASRC_3                  , 0x0000},
+       {RT5677_ASRC_4                  , 0x0000},
+       {RT5677_ASRC_5                  , 0x0000},
+       {RT5677_ASRC_6                  , 0x0000},
+       {RT5677_ASRC_7                  , 0x0000},
+       {RT5677_ASRC_8                  , 0x0000},
+       {RT5677_ASRC_9                  , 0x0000},
+       {RT5677_ASRC_10                 , 0x0000},
+       {RT5677_ASRC_11                 , 0x0000},
+       {RT5677_ASRC_12                 , 0x0008},
+       {RT5677_ASRC_13                 , 0x0000},
+       {RT5677_ASRC_14                 , 0x0000},
+       {RT5677_ASRC_15                 , 0x0000},
+       {RT5677_ASRC_16                 , 0x0000},
+       {RT5677_ASRC_17                 , 0x0000},
+       {RT5677_ASRC_18                 , 0x0000},
+       {RT5677_ASRC_19                 , 0x0000},
+       {RT5677_ASRC_20                 , 0x0000},
+       {RT5677_ASRC_21                 , 0x000c},
+       {RT5677_ASRC_22                 , 0x0000},
+       {RT5677_ASRC_23                 , 0x0000},
+       {RT5677_VAD_CTRL1               , 0x2184},
+       {RT5677_VAD_CTRL2               , 0x010a},
+       {RT5677_VAD_CTRL3               , 0x0aea},
+       {RT5677_VAD_CTRL4               , 0x000c},
+       {RT5677_VAD_CTRL5               , 0x0000},
+       {RT5677_DSP_INB_CTRL1           , 0x0000},
+       {RT5677_DSP_INB_CTRL2           , 0x0000},
+       {RT5677_DSP_IN_OUTB_CTRL        , 0x0000},
+       {RT5677_DSP_OUTB0_1_DIG_VOL     , 0x2f2f},
+       {RT5677_DSP_OUTB2_3_DIG_VOL     , 0x2f2f},
+       {RT5677_DSP_OUTB4_5_DIG_VOL     , 0x2f2f},
+       {RT5677_DSP_OUTB6_7_DIG_VOL     , 0x2f2f},
+       {RT5677_ADC_EQ_CTRL1            , 0x6000},
+       {RT5677_ADC_EQ_CTRL2            , 0x0000},
+       {RT5677_EQ_CTRL1                , 0xc000},
+       {RT5677_EQ_CTRL2                , 0x0000},
+       {RT5677_EQ_CTRL3                , 0x0000},
+       {RT5677_SOFT_VOL_ZERO_CROSS1    , 0x0009},
+       {RT5677_JD_CTRL1                , 0x0000},
+       {RT5677_JD_CTRL2                , 0x0000},
+       {RT5677_JD_CTRL3                , 0x0000},
+       {RT5677_IRQ_CTRL1               , 0x0000},
+       {RT5677_IRQ_CTRL2               , 0x0000},
+       {RT5677_GPIO_ST                 , 0x0000},
+       {RT5677_GPIO_CTRL1              , 0x0000},
+       {RT5677_GPIO_CTRL2              , 0x0000},
+       {RT5677_GPIO_CTRL3              , 0x0000},
+       {RT5677_STO1_ADC_HI_FILTER1     , 0xb320},
+       {RT5677_STO1_ADC_HI_FILTER2     , 0x0000},
+       {RT5677_MONO_ADC_HI_FILTER1     , 0xb300},
+       {RT5677_MONO_ADC_HI_FILTER2     , 0x0000},
+       {RT5677_STO2_ADC_HI_FILTER1     , 0xb300},
+       {RT5677_STO2_ADC_HI_FILTER2     , 0x0000},
+       {RT5677_STO3_ADC_HI_FILTER1     , 0xb300},
+       {RT5677_STO3_ADC_HI_FILTER2     , 0x0000},
+       {RT5677_STO4_ADC_HI_FILTER1     , 0xb300},
+       {RT5677_STO4_ADC_HI_FILTER2     , 0x0000},
+       {RT5677_MB_DRC_CTRL1            , 0x0f20},
+       {RT5677_DRC1_CTRL1              , 0x001f},
+       {RT5677_DRC1_CTRL2              , 0x020c},
+       {RT5677_DRC1_CTRL3              , 0x1f00},
+       {RT5677_DRC1_CTRL4              , 0x0000},
+       {RT5677_DRC1_CTRL5              , 0x0000},
+       {RT5677_DRC1_CTRL6              , 0x0029},
+       {RT5677_DRC2_CTRL1              , 0x001f},
+       {RT5677_DRC2_CTRL2              , 0x020c},
+       {RT5677_DRC2_CTRL3              , 0x1f00},
+       {RT5677_DRC2_CTRL4              , 0x0000},
+       {RT5677_DRC2_CTRL5              , 0x0000},
+       {RT5677_DRC2_CTRL6              , 0x0029},
+       {RT5677_DRC1_HL_CTRL1           , 0x8000},
+       {RT5677_DRC1_HL_CTRL2           , 0x0200},
+       {RT5677_DRC2_HL_CTRL1           , 0x8000},
+       {RT5677_DRC2_HL_CTRL2           , 0x0200},
+       {RT5677_DSP_INB1_SRC_CTRL1      , 0x5800},
+       {RT5677_DSP_INB1_SRC_CTRL2      , 0x0000},
+       {RT5677_DSP_INB1_SRC_CTRL3      , 0x0000},
+       {RT5677_DSP_INB1_SRC_CTRL4      , 0x0800},
+       {RT5677_DSP_INB2_SRC_CTRL1      , 0x5800},
+       {RT5677_DSP_INB2_SRC_CTRL2      , 0x0000},
+       {RT5677_DSP_INB2_SRC_CTRL3      , 0x0000},
+       {RT5677_DSP_INB2_SRC_CTRL4      , 0x0800},
+       {RT5677_DSP_INB3_SRC_CTRL1      , 0x5800},
+       {RT5677_DSP_INB3_SRC_CTRL2      , 0x0000},
+       {RT5677_DSP_INB3_SRC_CTRL3      , 0x0000},
+       {RT5677_DSP_INB3_SRC_CTRL4      , 0x0800},
+       {RT5677_DSP_OUTB1_SRC_CTRL1     , 0x5800},
+       {RT5677_DSP_OUTB1_SRC_CTRL2     , 0x0000},
+       {RT5677_DSP_OUTB1_SRC_CTRL3     , 0x0000},
+       {RT5677_DSP_OUTB1_SRC_CTRL4     , 0x0800},
+       {RT5677_DSP_OUTB2_SRC_CTRL1     , 0x5800},
+       {RT5677_DSP_OUTB2_SRC_CTRL2     , 0x0000},
+       {RT5677_DSP_OUTB2_SRC_CTRL3     , 0x0000},
+       {RT5677_DSP_OUTB2_SRC_CTRL4     , 0x0800},
+       {RT5677_DSP_OUTB_0123_MIXER_CTRL, 0xfefe},
+       {RT5677_DSP_OUTB_45_MIXER_CTRL  , 0xfefe},
+       {RT5677_DSP_OUTB_67_MIXER_CTRL  , 0xfefe},
+       {RT5677_DIG_MISC                , 0x0000},
+       {RT5677_GEN_CTRL1               , 0x0000},
+       {RT5677_GEN_CTRL2               , 0x0000},
+       {RT5677_VENDOR_ID               , 0x0000},
+       {RT5677_VENDOR_ID1              , 0x10ec},
+       {RT5677_VENDOR_ID2              , 0x6327},
+};
+
+static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rt5677_ranges); i++) {
+               if (reg >= rt5677_ranges[i].range_min &&
+                       reg <= rt5677_ranges[i].range_max) {
+                       return true;
+               }
+       }
+
+       switch (reg) {
+       case RT5677_RESET:
+       case RT5677_SLIMBUS_PARAM:
+       case RT5677_PDM_DATA_CTRL1:
+       case RT5677_PDM_DATA_CTRL2:
+       case RT5677_PDM1_DATA_CTRL4:
+       case RT5677_PDM2_DATA_CTRL4:
+       case RT5677_I2C_MASTER_CTRL1:
+       case RT5677_I2C_MASTER_CTRL7:
+       case RT5677_I2C_MASTER_CTRL8:
+       case RT5677_HAP_GENE_CTRL2:
+       case RT5677_PWR_DSP_ST:
+       case RT5677_PRIV_DATA:
+       case RT5677_PLL1_CTRL2:
+       case RT5677_PLL2_CTRL2:
+       case RT5677_ASRC_22:
+       case RT5677_ASRC_23:
+       case RT5677_VAD_CTRL5:
+       case RT5677_ADC_EQ_CTRL1:
+       case RT5677_EQ_CTRL1:
+       case RT5677_IRQ_CTRL1:
+       case RT5677_IRQ_CTRL2:
+       case RT5677_GPIO_ST:
+       case RT5677_DSP_INB1_SRC_CTRL4:
+       case RT5677_DSP_INB2_SRC_CTRL4:
+       case RT5677_DSP_INB3_SRC_CTRL4:
+       case RT5677_DSP_OUTB1_SRC_CTRL4:
+       case RT5677_DSP_OUTB2_SRC_CTRL4:
+       case RT5677_VENDOR_ID:
+       case RT5677_VENDOR_ID1:
+       case RT5677_VENDOR_ID2:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool rt5677_readable_register(struct device *dev, unsigned int reg)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rt5677_ranges); i++) {
+               if (reg >= rt5677_ranges[i].range_min &&
+                       reg <= rt5677_ranges[i].range_max) {
+                       return true;
+               }
+       }
+
+       switch (reg) {
+       case RT5677_RESET:
+       case RT5677_LOUT1:
+       case RT5677_IN1:
+       case RT5677_MICBIAS:
+       case RT5677_SLIMBUS_PARAM:
+       case RT5677_SLIMBUS_RX:
+       case RT5677_SLIMBUS_CTRL:
+       case RT5677_SIDETONE_CTRL:
+       case RT5677_ANA_DAC1_2_3_SRC:
+       case RT5677_IF_DSP_DAC3_4_MIXER:
+       case RT5677_DAC4_DIG_VOL:
+       case RT5677_DAC3_DIG_VOL:
+       case RT5677_DAC1_DIG_VOL:
+       case RT5677_DAC2_DIG_VOL:
+       case RT5677_IF_DSP_DAC2_MIXER:
+       case RT5677_STO1_ADC_DIG_VOL:
+       case RT5677_MONO_ADC_DIG_VOL:
+       case RT5677_STO1_2_ADC_BST:
+       case RT5677_STO2_ADC_DIG_VOL:
+       case RT5677_ADC_BST_CTRL2:
+       case RT5677_STO3_4_ADC_BST:
+       case RT5677_STO3_ADC_DIG_VOL:
+       case RT5677_STO4_ADC_DIG_VOL:
+       case RT5677_STO4_ADC_MIXER:
+       case RT5677_STO3_ADC_MIXER:
+       case RT5677_STO2_ADC_MIXER:
+       case RT5677_STO1_ADC_MIXER:
+       case RT5677_MONO_ADC_MIXER:
+       case RT5677_ADC_IF_DSP_DAC1_MIXER:
+       case RT5677_STO1_DAC_MIXER:
+       case RT5677_MONO_DAC_MIXER:
+       case RT5677_DD1_MIXER:
+       case RT5677_DD2_MIXER:
+       case RT5677_IF3_DATA:
+       case RT5677_IF4_DATA:
+       case RT5677_PDM_OUT_CTRL:
+       case RT5677_PDM_DATA_CTRL1:
+       case RT5677_PDM_DATA_CTRL2:
+       case RT5677_PDM1_DATA_CTRL2:
+       case RT5677_PDM1_DATA_CTRL3:
+       case RT5677_PDM1_DATA_CTRL4:
+       case RT5677_PDM2_DATA_CTRL2:
+       case RT5677_PDM2_DATA_CTRL3:
+       case RT5677_PDM2_DATA_CTRL4:
+       case RT5677_TDM1_CTRL1:
+       case RT5677_TDM1_CTRL2:
+       case RT5677_TDM1_CTRL3:
+       case RT5677_TDM1_CTRL4:
+       case RT5677_TDM1_CTRL5:
+       case RT5677_TDM2_CTRL1:
+       case RT5677_TDM2_CTRL2:
+       case RT5677_TDM2_CTRL3:
+       case RT5677_TDM2_CTRL4:
+       case RT5677_TDM2_CTRL5:
+       case RT5677_I2C_MASTER_CTRL1:
+       case RT5677_I2C_MASTER_CTRL2:
+       case RT5677_I2C_MASTER_CTRL3:
+       case RT5677_I2C_MASTER_CTRL4:
+       case RT5677_I2C_MASTER_CTRL5:
+       case RT5677_I2C_MASTER_CTRL6:
+       case RT5677_I2C_MASTER_CTRL7:
+       case RT5677_I2C_MASTER_CTRL8:
+       case RT5677_DMIC_CTRL1:
+       case RT5677_DMIC_CTRL2:
+       case RT5677_HAP_GENE_CTRL1:
+       case RT5677_HAP_GENE_CTRL2:
+       case RT5677_HAP_GENE_CTRL3:
+       case RT5677_HAP_GENE_CTRL4:
+       case RT5677_HAP_GENE_CTRL5:
+       case RT5677_HAP_GENE_CTRL6:
+       case RT5677_HAP_GENE_CTRL7:
+       case RT5677_HAP_GENE_CTRL8:
+       case RT5677_HAP_GENE_CTRL9:
+       case RT5677_HAP_GENE_CTRL10:
+       case RT5677_PWR_DIG1:
+       case RT5677_PWR_DIG2:
+       case RT5677_PWR_ANLG1:
+       case RT5677_PWR_ANLG2:
+       case RT5677_PWR_DSP1:
+       case RT5677_PWR_DSP_ST:
+       case RT5677_PWR_DSP2:
+       case RT5677_ADC_DAC_HPF_CTRL1:
+       case RT5677_PRIV_INDEX:
+       case RT5677_PRIV_DATA:
+       case RT5677_I2S4_SDP:
+       case RT5677_I2S1_SDP:
+       case RT5677_I2S2_SDP:
+       case RT5677_I2S3_SDP:
+       case RT5677_CLK_TREE_CTRL1:
+       case RT5677_CLK_TREE_CTRL2:
+       case RT5677_CLK_TREE_CTRL3:
+       case RT5677_PLL1_CTRL1:
+       case RT5677_PLL1_CTRL2:
+       case RT5677_PLL2_CTRL1:
+       case RT5677_PLL2_CTRL2:
+       case RT5677_GLB_CLK1:
+       case RT5677_GLB_CLK2:
+       case RT5677_ASRC_1:
+       case RT5677_ASRC_2:
+       case RT5677_ASRC_3:
+       case RT5677_ASRC_4:
+       case RT5677_ASRC_5:
+       case RT5677_ASRC_6:
+       case RT5677_ASRC_7:
+       case RT5677_ASRC_8:
+       case RT5677_ASRC_9:
+       case RT5677_ASRC_10:
+       case RT5677_ASRC_11:
+       case RT5677_ASRC_12:
+       case RT5677_ASRC_13:
+       case RT5677_ASRC_14:
+       case RT5677_ASRC_15:
+       case RT5677_ASRC_16:
+       case RT5677_ASRC_17:
+       case RT5677_ASRC_18:
+       case RT5677_ASRC_19:
+       case RT5677_ASRC_20:
+       case RT5677_ASRC_21:
+       case RT5677_ASRC_22:
+       case RT5677_ASRC_23:
+       case RT5677_VAD_CTRL1:
+       case RT5677_VAD_CTRL2:
+       case RT5677_VAD_CTRL3:
+       case RT5677_VAD_CTRL4:
+       case RT5677_VAD_CTRL5:
+       case RT5677_DSP_INB_CTRL1:
+       case RT5677_DSP_INB_CTRL2:
+       case RT5677_DSP_IN_OUTB_CTRL:
+       case RT5677_DSP_OUTB0_1_DIG_VOL:
+       case RT5677_DSP_OUTB2_3_DIG_VOL:
+       case RT5677_DSP_OUTB4_5_DIG_VOL:
+       case RT5677_DSP_OUTB6_7_DIG_VOL:
+       case RT5677_ADC_EQ_CTRL1:
+       case RT5677_ADC_EQ_CTRL2:
+       case RT5677_EQ_CTRL1:
+       case RT5677_EQ_CTRL2:
+       case RT5677_EQ_CTRL3:
+       case RT5677_SOFT_VOL_ZERO_CROSS1:
+       case RT5677_JD_CTRL1:
+       case RT5677_JD_CTRL2:
+       case RT5677_JD_CTRL3:
+       case RT5677_IRQ_CTRL1:
+       case RT5677_IRQ_CTRL2:
+       case RT5677_GPIO_ST:
+       case RT5677_GPIO_CTRL1:
+       case RT5677_GPIO_CTRL2:
+       case RT5677_GPIO_CTRL3:
+       case RT5677_STO1_ADC_HI_FILTER1:
+       case RT5677_STO1_ADC_HI_FILTER2:
+       case RT5677_MONO_ADC_HI_FILTER1:
+       case RT5677_MONO_ADC_HI_FILTER2:
+       case RT5677_STO2_ADC_HI_FILTER1:
+       case RT5677_STO2_ADC_HI_FILTER2:
+       case RT5677_STO3_ADC_HI_FILTER1:
+       case RT5677_STO3_ADC_HI_FILTER2:
+       case RT5677_STO4_ADC_HI_FILTER1:
+       case RT5677_STO4_ADC_HI_FILTER2:
+       case RT5677_MB_DRC_CTRL1:
+       case RT5677_DRC1_CTRL1:
+       case RT5677_DRC1_CTRL2:
+       case RT5677_DRC1_CTRL3:
+       case RT5677_DRC1_CTRL4:
+       case RT5677_DRC1_CTRL5:
+       case RT5677_DRC1_CTRL6:
+       case RT5677_DRC2_CTRL1:
+       case RT5677_DRC2_CTRL2:
+       case RT5677_DRC2_CTRL3:
+       case RT5677_DRC2_CTRL4:
+       case RT5677_DRC2_CTRL5:
+       case RT5677_DRC2_CTRL6:
+       case RT5677_DRC1_HL_CTRL1:
+       case RT5677_DRC1_HL_CTRL2:
+       case RT5677_DRC2_HL_CTRL1:
+       case RT5677_DRC2_HL_CTRL2:
+       case RT5677_DSP_INB1_SRC_CTRL1:
+       case RT5677_DSP_INB1_SRC_CTRL2:
+       case RT5677_DSP_INB1_SRC_CTRL3:
+       case RT5677_DSP_INB1_SRC_CTRL4:
+       case RT5677_DSP_INB2_SRC_CTRL1:
+       case RT5677_DSP_INB2_SRC_CTRL2:
+       case RT5677_DSP_INB2_SRC_CTRL3:
+       case RT5677_DSP_INB2_SRC_CTRL4:
+       case RT5677_DSP_INB3_SRC_CTRL1:
+       case RT5677_DSP_INB3_SRC_CTRL2:
+       case RT5677_DSP_INB3_SRC_CTRL3:
+       case RT5677_DSP_INB3_SRC_CTRL4:
+       case RT5677_DSP_OUTB1_SRC_CTRL1:
+       case RT5677_DSP_OUTB1_SRC_CTRL2:
+       case RT5677_DSP_OUTB1_SRC_CTRL3:
+       case RT5677_DSP_OUTB1_SRC_CTRL4:
+       case RT5677_DSP_OUTB2_SRC_CTRL1:
+       case RT5677_DSP_OUTB2_SRC_CTRL2:
+       case RT5677_DSP_OUTB2_SRC_CTRL3:
+       case RT5677_DSP_OUTB2_SRC_CTRL4:
+       case RT5677_DSP_OUTB_0123_MIXER_CTRL:
+       case RT5677_DSP_OUTB_45_MIXER_CTRL:
+       case RT5677_DSP_OUTB_67_MIXER_CTRL:
+       case RT5677_DIG_MISC:
+       case RT5677_GEN_CTRL1:
+       case RT5677_GEN_CTRL2:
+       case RT5677_VENDOR_ID:
+       case RT5677_VENDOR_ID1:
+       case RT5677_VENDOR_ID2:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
+
+/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
+static unsigned int bst_tlv[] = {
+       TLV_DB_RANGE_HEAD(7),
+       0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+       1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+       2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+       3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0),
+       6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0),
+       7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0),
+       8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
+};
+
+static const struct snd_kcontrol_new rt5677_snd_controls[] = {
+       /* OUTPUT Control */
+       SOC_SINGLE("OUT1 Playback Switch", RT5677_LOUT1,
+               RT5677_LOUT1_L_MUTE_SFT, 1, 1),
+       SOC_SINGLE("OUT2 Playback Switch", RT5677_LOUT1,
+               RT5677_LOUT2_L_MUTE_SFT, 1, 1),
+       SOC_SINGLE("OUT3 Playback Switch", RT5677_LOUT1,
+               RT5677_LOUT3_L_MUTE_SFT, 1, 1),
+
+       /* DAC Digital Volume */
+       SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5677_DAC1_DIG_VOL,
+               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+       SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5677_DAC2_DIG_VOL,
+               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+       SOC_DOUBLE_TLV("DAC3 Playback Volume", RT5677_DAC3_DIG_VOL,
+               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+       SOC_DOUBLE_TLV("DAC4 Playback Volume", RT5677_DAC4_DIG_VOL,
+               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+
+       /* IN1/IN2 Control */
+       SOC_SINGLE_TLV("IN1 Boost", RT5677_IN1, RT5677_BST_SFT1, 8, 0, bst_tlv),
+       SOC_SINGLE_TLV("IN2 Boost", RT5677_IN1, RT5677_BST_SFT2, 8, 0, bst_tlv),
+
+       /* ADC Digital Volume Control */
+       SOC_DOUBLE("ADC1 Capture Switch", RT5677_STO1_ADC_DIG_VOL,
+               RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1),
+       SOC_DOUBLE("ADC2 Capture Switch", RT5677_STO2_ADC_DIG_VOL,
+               RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1),
+       SOC_DOUBLE("ADC3 Capture Switch", RT5677_STO3_ADC_DIG_VOL,
+               RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1),
+       SOC_DOUBLE("ADC4 Capture Switch", RT5677_STO4_ADC_DIG_VOL,
+               RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1),
+       SOC_DOUBLE("Mono ADC Capture Switch", RT5677_MONO_ADC_DIG_VOL,
+               RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1),
+
+       SOC_DOUBLE_TLV("ADC1 Capture Volume", RT5677_STO1_ADC_DIG_VOL,
+               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+               adc_vol_tlv),
+       SOC_DOUBLE_TLV("ADC2 Capture Volume", RT5677_STO2_ADC_DIG_VOL,
+               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+               adc_vol_tlv),
+       SOC_DOUBLE_TLV("ADC3 Capture Volume", RT5677_STO3_ADC_DIG_VOL,
+               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+               adc_vol_tlv),
+       SOC_DOUBLE_TLV("ADC4 Capture Volume", RT5677_STO4_ADC_DIG_VOL,
+               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+               adc_vol_tlv),
+       SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5677_MONO_ADC_DIG_VOL,
+               RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 127, 0,
+               adc_vol_tlv),
+
+       /* ADC Boost Volume Control */
+       SOC_DOUBLE_TLV("STO1 ADC Boost Gain", RT5677_STO1_2_ADC_BST,
+               RT5677_STO1_ADC_L_BST_SFT, RT5677_STO1_ADC_R_BST_SFT, 3, 0,
+               adc_bst_tlv),
+       SOC_DOUBLE_TLV("STO2 ADC Boost Gain", RT5677_STO1_2_ADC_BST,
+               RT5677_STO2_ADC_L_BST_SFT, RT5677_STO2_ADC_R_BST_SFT, 3, 0,
+               adc_bst_tlv),
+       SOC_DOUBLE_TLV("STO3 ADC Boost Gain", RT5677_STO3_4_ADC_BST,
+               RT5677_STO3_ADC_L_BST_SFT, RT5677_STO3_ADC_R_BST_SFT, 3, 0,
+               adc_bst_tlv),
+       SOC_DOUBLE_TLV("STO4 ADC Boost Gain", RT5677_STO3_4_ADC_BST,
+               RT5677_STO4_ADC_L_BST_SFT, RT5677_STO4_ADC_R_BST_SFT, 3, 0,
+               adc_bst_tlv),
+       SOC_DOUBLE_TLV("Mono ADC Boost Gain", RT5677_ADC_BST_CTRL2,
+               RT5677_MONO_ADC_L_BST_SFT, RT5677_MONO_ADC_R_BST_SFT, 3, 0,
+               adc_bst_tlv),
+};
+
+/**
+ * set_dmic_clk - Set parameter of dmic.
+ *
+ * @w: DAPM widget.
+ * @kcontrol: The kcontrol of this widget.
+ * @event: Event id.
+ *
+ * Choose dmic clock between 1MHz and 3MHz.
+ * It is better for clock to approximate 3MHz.
+ */
+static int set_dmic_clk(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+       int div[] = {2, 3, 4, 6, 8, 12}, idx = -EINVAL, i;
+       int rate, red, bound, temp;
+
+       rate = rt5677->sysclk;
+       red = 3000000 * 12;
+       for (i = 0; i < ARRAY_SIZE(div); i++) {
+               bound = div[i] * 3000000;
+               if (rate > bound)
+                       continue;
+               temp = bound - rate;
+               if (temp < red) {
+                       red = temp;
+                       idx = i;
+               }
+       }
+
+       if (idx < 0)
+               dev_err(codec->dev, "Failed to set DMIC clock\n");
+       else
+               regmap_update_bits(rt5677->regmap, RT5677_DMIC_CTRL1,
+                       RT5677_DMIC_CLK_MASK, idx << RT5677_DMIC_CLK_SFT);
+       return idx;
+}
+
+static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink)
+{
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(source->codec);
+       unsigned int val;
+
+       regmap_read(rt5677->regmap, RT5677_GLB_CLK1, &val);
+       val &= RT5677_SCLK_SRC_MASK;
+       if (val == RT5677_SCLK_SRC_PLL1)
+               return 1;
+       else
+               return 0;
+}
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt5677_sto1_adc_l_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO1_ADC_MIXER,
+                       RT5677_M_STO1_ADC_L1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO1_ADC_MIXER,
+                       RT5677_M_STO1_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto1_adc_r_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO1_ADC_MIXER,
+                       RT5677_M_STO1_ADC_R1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO1_ADC_MIXER,
+                       RT5677_M_STO1_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto2_adc_l_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO2_ADC_MIXER,
+                       RT5677_M_STO2_ADC_L1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO2_ADC_MIXER,
+                       RT5677_M_STO2_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto2_adc_r_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO2_ADC_MIXER,
+                       RT5677_M_STO2_ADC_R1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO2_ADC_MIXER,
+                       RT5677_M_STO2_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto3_adc_l_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO3_ADC_MIXER,
+                       RT5677_M_STO3_ADC_L1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO3_ADC_MIXER,
+                       RT5677_M_STO3_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto3_adc_r_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO3_ADC_MIXER,
+                       RT5677_M_STO3_ADC_R1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO3_ADC_MIXER,
+                       RT5677_M_STO3_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto4_adc_l_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO4_ADC_MIXER,
+                       RT5677_M_STO4_ADC_L1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO4_ADC_MIXER,
+                       RT5677_M_STO4_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto4_adc_r_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO4_ADC_MIXER,
+                       RT5677_M_STO4_ADC_R1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO4_ADC_MIXER,
+                       RT5677_M_STO4_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_mono_adc_l_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5677_MONO_ADC_MIXER,
+                       RT5677_M_MONO_ADC_L1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5677_MONO_ADC_MIXER,
+                       RT5677_M_MONO_ADC_L2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_mono_adc_r_mix[] = {
+       SOC_DAPM_SINGLE("ADC1 Switch", RT5677_MONO_ADC_MIXER,
+                       RT5677_M_MONO_ADC_R1_SFT, 1, 1),
+       SOC_DAPM_SINGLE("ADC2 Switch", RT5677_MONO_ADC_MIXER,
+                       RT5677_M_MONO_ADC_R2_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_dac_l_mix[] = {
+       SOC_DAPM_SINGLE("Stereo ADC Switch", RT5677_ADC_IF_DSP_DAC1_MIXER,
+                       RT5677_M_ADDA_MIXER1_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC1 Switch", RT5677_ADC_IF_DSP_DAC1_MIXER,
+                       RT5677_M_DAC1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_dac_r_mix[] = {
+       SOC_DAPM_SINGLE("Stereo ADC Switch", RT5677_ADC_IF_DSP_DAC1_MIXER,
+                       RT5677_M_ADDA_MIXER1_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC1 Switch", RT5677_ADC_IF_DSP_DAC1_MIXER,
+                       RT5677_M_DAC1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto1_dac_l_mix[] = {
+       SOC_DAPM_SINGLE("ST L Switch", RT5677_STO1_DAC_MIXER,
+                       RT5677_M_ST_DAC1_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
+                       RT5677_M_DAC1_L_STO_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER,
+                       RT5677_M_DAC2_L_STO_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
+                       RT5677_M_DAC1_R_STO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_sto1_dac_r_mix[] = {
+       SOC_DAPM_SINGLE("ST R Switch", RT5677_STO1_DAC_MIXER,
+                       RT5677_M_ST_DAC1_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
+                       RT5677_M_DAC1_R_STO_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER,
+                       RT5677_M_DAC2_R_STO_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
+                       RT5677_M_DAC1_L_STO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_mono_dac_l_mix[] = {
+       SOC_DAPM_SINGLE("ST L Switch", RT5677_MONO_DAC_MIXER,
+                       RT5677_M_ST_DAC2_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER,
+                       RT5677_M_DAC1_L_MONO_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
+                       RT5677_M_DAC2_L_MONO_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
+                       RT5677_M_DAC2_R_MONO_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_mono_dac_r_mix[] = {
+       SOC_DAPM_SINGLE("ST R Switch", RT5677_MONO_DAC_MIXER,
+                       RT5677_M_ST_DAC2_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER,
+                       RT5677_M_DAC1_R_MONO_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
+                       RT5677_M_DAC2_R_MONO_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
+                       RT5677_M_DAC2_L_MONO_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_dd1_l_mix[] = {
+       SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5677_DD1_MIXER,
+                       RT5677_M_STO_L_DD1_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER,
+                       RT5677_M_MONO_L_DD1_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER,
+                       RT5677_M_DAC3_L_DD1_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER,
+                       RT5677_M_DAC3_R_DD1_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_dd1_r_mix[] = {
+       SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5677_DD1_MIXER,
+                       RT5677_M_STO_R_DD1_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER,
+                       RT5677_M_MONO_R_DD1_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER,
+                       RT5677_M_DAC3_R_DD1_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER,
+                       RT5677_M_DAC3_L_DD1_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_dd2_l_mix[] = {
+       SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5677_DD2_MIXER,
+                       RT5677_M_STO_L_DD2_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER,
+                       RT5677_M_MONO_L_DD2_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER,
+                       RT5677_M_DAC4_L_DD2_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER,
+                       RT5677_M_DAC4_R_DD2_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_dd2_r_mix[] = {
+       SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5677_DD2_MIXER,
+                       RT5677_M_STO_R_DD2_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER,
+                       RT5677_M_MONO_R_DD2_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER,
+                       RT5677_M_DAC4_R_DD2_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER,
+                       RT5677_M_DAC4_L_DD2_R_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_ob_01_mix[] = {
+       SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_01_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_23_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_45_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_6_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_7_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_8_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_9_H_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_ob_23_mix[] = {
+       SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_01_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_23_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_45_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_6_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_7_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_8_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL,
+                       RT5677_DSP_IB_9_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_ob_4_mix[] = {
+       SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_01_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_23_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_45_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_6_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_7_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_8_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_9_H_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_ob_5_mix[] = {
+       SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_01_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_23_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_45_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_6_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_7_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_8_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL,
+                       RT5677_DSP_IB_9_L_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_ob_6_mix[] = {
+       SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_01_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_23_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_45_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_6_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_7_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_8_H_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_9_H_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5677_ob_7_mix[] = {
+       SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_01_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_23_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_45_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_6_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_7_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_8_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL,
+                       RT5677_DSP_IB_9_L_SFT, 1, 1),
+};
+
+
+/* Mux */
+/* DAC1 L/R source */ /* MX-29 [10:8] */
+static const char * const rt5677_dac1_src[] = {
+       "IF1 DAC 01", "IF2 DAC 01", "IF3 DAC LR", "IF4 DAC LR", "SLB DAC 01",
+       "OB 01"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_dac1_enum, RT5677_ADC_IF_DSP_DAC1_MIXER,
+       RT5677_DAC1_L_SEL_SFT, rt5677_dac1_src);
+
+static const struct snd_kcontrol_new rt5677_dac1_mux =
+       SOC_DAPM_ENUM("DAC1 source", rt5677_dac1_enum);
+
+/* ADDA1 L/R source */ /* MX-29 [1:0] */
+static const char * const rt5677_adda1_src[] = {
+       "STO1 ADC MIX", "STO2 ADC MIX", "OB 67",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_adda1_enum, RT5677_ADC_IF_DSP_DAC1_MIXER,
+       RT5677_ADDA1_SEL_SFT, rt5677_adda1_src);
+
+static const struct snd_kcontrol_new rt5677_adda1_mux =
+       SOC_DAPM_ENUM("ADDA1 source", rt5677_adda1_enum);
+
+
+/*DAC2 L/R source*/ /* MX-1B [6:4] [2:0] */
+static const char * const rt5677_dac2l_src[] = {
+       "IF1 DAC 2", "IF2 DAC 2", "IF3 DAC L", "IF4 DAC L", "SLB DAC 2",
+       "OB 2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_dac2l_enum, RT5677_IF_DSP_DAC2_MIXER,
+       RT5677_SEL_DAC2_L_SRC_SFT, rt5677_dac2l_src);
+
+static const struct snd_kcontrol_new rt5677_dac2_l_mux =
+       SOC_DAPM_ENUM("DAC2 L source", rt5677_dac2l_enum);
+
+static const char * const rt5677_dac2r_src[] = {
+       "IF1 DAC 3", "IF2 DAC 3", "IF3 DAC R", "IF4 DAC R", "SLB DAC 3",
+       "OB 3", "Haptic Generator", "VAD ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_dac2r_enum, RT5677_IF_DSP_DAC2_MIXER,
+       RT5677_SEL_DAC2_R_SRC_SFT, rt5677_dac2r_src);
+
+static const struct snd_kcontrol_new rt5677_dac2_r_mux =
+       SOC_DAPM_ENUM("DAC2 R source", rt5677_dac2r_enum);
+
+/*DAC3 L/R source*/ /* MX-16 [6:4] [2:0] */
+static const char * const rt5677_dac3l_src[] = {
+       "IF1 DAC 4", "IF2 DAC 4", "IF3 DAC L", "IF4 DAC L",
+       "SLB DAC 4", "OB 4"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_dac3l_enum, RT5677_IF_DSP_DAC3_4_MIXER,
+       RT5677_SEL_DAC3_L_SRC_SFT, rt5677_dac3l_src);
+
+static const struct snd_kcontrol_new rt5677_dac3_l_mux =
+       SOC_DAPM_ENUM("DAC3 L source", rt5677_dac3l_enum);
+
+static const char * const rt5677_dac3r_src[] = {
+       "IF1 DAC 5", "IF2 DAC 5", "IF3 DAC R", "IF4 DAC R",
+       "SLB DAC 5", "OB 5"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_dac3r_enum, RT5677_IF_DSP_DAC3_4_MIXER,
+       RT5677_SEL_DAC3_R_SRC_SFT, rt5677_dac3r_src);
+
+static const struct snd_kcontrol_new rt5677_dac3_r_mux =
+       SOC_DAPM_ENUM("DAC3 R source", rt5677_dac3r_enum);
+
+/*DAC4 L/R source*/ /* MX-16 [14:12] [10:8] */
+static const char * const rt5677_dac4l_src[] = {
+       "IF1 DAC 6", "IF2 DAC 6", "IF3 DAC L", "IF4 DAC L",
+       "SLB DAC 6", "OB 6"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_dac4l_enum, RT5677_IF_DSP_DAC3_4_MIXER,
+       RT5677_SEL_DAC4_L_SRC_SFT, rt5677_dac4l_src);
+
+static const struct snd_kcontrol_new rt5677_dac4_l_mux =
+       SOC_DAPM_ENUM("DAC4 L source", rt5677_dac4l_enum);
+
+static const char * const rt5677_dac4r_src[] = {
+       "IF1 DAC 7", "IF2 DAC 7", "IF3 DAC R", "IF4 DAC R",
+       "SLB DAC 7", "OB 7"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_dac4r_enum, RT5677_IF_DSP_DAC3_4_MIXER,
+       RT5677_SEL_DAC4_R_SRC_SFT, rt5677_dac4r_src);
+
+static const struct snd_kcontrol_new rt5677_dac4_r_mux =
+       SOC_DAPM_ENUM("DAC4 R source", rt5677_dac4r_enum);
+
+/* In/OutBound Source Pass SRC */ /* MX-A5 [3] [4] [0] [1] [2] */
+static const char * const rt5677_iob_bypass_src[] = {
+       "Bypass", "Pass SRC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_ob01_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL,
+       RT5677_SEL_SRC_OB01_SFT, rt5677_iob_bypass_src);
+
+static const struct snd_kcontrol_new rt5677_ob01_bypass_src_mux =
+       SOC_DAPM_ENUM("OB01 Bypass source", rt5677_ob01_bypass_src_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_ob23_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL,
+       RT5677_SEL_SRC_OB23_SFT, rt5677_iob_bypass_src);
+
+static const struct snd_kcontrol_new rt5677_ob23_bypass_src_mux =
+       SOC_DAPM_ENUM("OB23 Bypass source", rt5677_ob23_bypass_src_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_ib01_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL,
+       RT5677_SEL_SRC_IB01_SFT, rt5677_iob_bypass_src);
+
+static const struct snd_kcontrol_new rt5677_ib01_bypass_src_mux =
+       SOC_DAPM_ENUM("IB01 Bypass source", rt5677_ib01_bypass_src_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_ib23_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL,
+       RT5677_SEL_SRC_IB23_SFT, rt5677_iob_bypass_src);
+
+static const struct snd_kcontrol_new rt5677_ib23_bypass_src_mux =
+       SOC_DAPM_ENUM("IB23 Bypass source", rt5677_ib23_bypass_src_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_ib45_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL,
+       RT5677_SEL_SRC_IB45_SFT, rt5677_iob_bypass_src);
+
+static const struct snd_kcontrol_new rt5677_ib45_bypass_src_mux =
+       SOC_DAPM_ENUM("IB45 Bypass source", rt5677_ib45_bypass_src_enum);
+
+/* Stereo ADC Source 2 */ /* MX-27 MX26  MX25 [11:10] */
+static const char * const rt5677_stereo_adc2_src[] = {
+       "DD MIX1", "DMIC", "Stereo DAC MIX"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_stereo1_adc2_enum, RT5677_STO1_ADC_MIXER,
+       RT5677_SEL_STO1_ADC2_SFT, rt5677_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_sto1_adc2_mux =
+       SOC_DAPM_ENUM("Stereo1 ADC2 source", rt5677_stereo1_adc2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_stereo2_adc2_enum, RT5677_STO2_ADC_MIXER,
+       RT5677_SEL_STO2_ADC2_SFT, rt5677_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_sto2_adc2_mux =
+       SOC_DAPM_ENUM("Stereo2 ADC2 source", rt5677_stereo2_adc2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_stereo3_adc2_enum, RT5677_STO3_ADC_MIXER,
+       RT5677_SEL_STO3_ADC2_SFT, rt5677_stereo_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_sto3_adc2_mux =
+       SOC_DAPM_ENUM("Stereo3 ADC2 source", rt5677_stereo3_adc2_enum);
+
+/* DMIC Source */ /* MX-28 [9:8][1:0] MX-27 MX-26 MX-25 MX-24 [9:8] */
+static const char * const rt5677_dmic_src[] = {
+       "DMIC1", "DMIC2", "DMIC3", "DMIC4"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_mono_dmic_l_enum, RT5677_MONO_ADC_MIXER,
+       RT5677_SEL_MONO_DMIC_L_SFT, rt5677_dmic_src);
+
+static const struct snd_kcontrol_new rt5677_mono_dmic_l_mux =
+       SOC_DAPM_ENUM("Mono DMIC L source", rt5677_mono_dmic_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_mono_dmic_r_enum, RT5677_MONO_ADC_MIXER,
+       RT5677_SEL_MONO_DMIC_R_SFT, rt5677_dmic_src);
+
+static const struct snd_kcontrol_new rt5677_mono_dmic_r_mux =
+       SOC_DAPM_ENUM("Mono DMIC R source", rt5677_mono_dmic_r_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_stereo1_dmic_enum, RT5677_STO1_ADC_MIXER,
+       RT5677_SEL_STO1_DMIC_SFT, rt5677_dmic_src);
+
+static const struct snd_kcontrol_new rt5677_sto1_dmic_mux =
+       SOC_DAPM_ENUM("Stereo1 DMIC source", rt5677_stereo1_dmic_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_stereo2_dmic_enum, RT5677_STO2_ADC_MIXER,
+       RT5677_SEL_STO2_DMIC_SFT, rt5677_dmic_src);
+
+static const struct snd_kcontrol_new rt5677_sto2_dmic_mux =
+       SOC_DAPM_ENUM("Stereo2 DMIC source", rt5677_stereo2_dmic_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_stereo3_dmic_enum, RT5677_STO3_ADC_MIXER,
+       RT5677_SEL_STO3_DMIC_SFT, rt5677_dmic_src);
+
+static const struct snd_kcontrol_new rt5677_sto3_dmic_mux =
+       SOC_DAPM_ENUM("Stereo3 DMIC source", rt5677_stereo3_dmic_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_stereo4_dmic_enum, RT5677_STO4_ADC_MIXER,
+       RT5677_SEL_STO4_DMIC_SFT, rt5677_dmic_src);
+
+static const struct snd_kcontrol_new rt5677_sto4_dmic_mux =
+       SOC_DAPM_ENUM("Stereo4 DMIC source", rt5677_stereo4_dmic_enum);
+
+/* Stereo2 ADC source */ /* MX-26 [0] */
+static const char * const rt5677_stereo2_adc_lr_src[] = {
+       "L", "LR"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_stereo2_adc_lr_enum, RT5677_STO2_ADC_MIXER,
+       RT5677_SEL_STO2_LR_MIX_SFT, rt5677_stereo2_adc_lr_src);
+
+static const struct snd_kcontrol_new rt5677_sto2_adc_lr_mux =
+       SOC_DAPM_ENUM("Stereo2 ADC LR source", rt5677_stereo2_adc_lr_enum);
+
+/* Stereo1 ADC Source 1 */ /* MX-27 MX26  MX25 [13:12] */
+static const char * const rt5677_stereo_adc1_src[] = {
+       "DD MIX1", "ADC1/2", "Stereo DAC MIX"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_stereo1_adc1_enum, RT5677_STO1_ADC_MIXER,
+       RT5677_SEL_STO1_ADC1_SFT, rt5677_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_sto1_adc1_mux =
+       SOC_DAPM_ENUM("Stereo1 ADC1 source", rt5677_stereo1_adc1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_stereo2_adc1_enum, RT5677_STO2_ADC_MIXER,
+       RT5677_SEL_STO2_ADC1_SFT, rt5677_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_sto2_adc1_mux =
+       SOC_DAPM_ENUM("Stereo2 ADC1 source", rt5677_stereo2_adc1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_stereo3_adc1_enum, RT5677_STO3_ADC_MIXER,
+       RT5677_SEL_STO3_ADC1_SFT, rt5677_stereo_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_sto3_adc1_mux =
+       SOC_DAPM_ENUM("Stereo3 ADC1 source", rt5677_stereo3_adc1_enum);
+
+/* Mono ADC Left source 2 */ /* MX-28 [11:10] */
+static const char * const rt5677_mono_adc2_l_src[] = {
+       "DD MIX1L", "DMIC", "MONO DAC MIXL"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_mono_adc2_l_enum, RT5677_MONO_ADC_MIXER,
+       RT5677_SEL_MONO_ADC_L2_SFT, rt5677_mono_adc2_l_src);
+
+static const struct snd_kcontrol_new rt5677_mono_adc2_l_mux =
+       SOC_DAPM_ENUM("Mono ADC2 L source", rt5677_mono_adc2_l_enum);
+
+/* Mono ADC Left source 1 */ /* MX-28 [13:12] */
+static const char * const rt5677_mono_adc1_l_src[] = {
+       "DD MIX1L", "ADC1", "MONO DAC MIXL"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_mono_adc1_l_enum, RT5677_MONO_ADC_MIXER,
+       RT5677_SEL_MONO_ADC_L1_SFT, rt5677_mono_adc1_l_src);
+
+static const struct snd_kcontrol_new rt5677_mono_adc1_l_mux =
+       SOC_DAPM_ENUM("Mono ADC1 L source", rt5677_mono_adc1_l_enum);
+
+/* Mono ADC Right source 2 */ /* MX-28 [3:2] */
+static const char * const rt5677_mono_adc2_r_src[] = {
+       "DD MIX1R", "DMIC", "MONO DAC MIXR"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_mono_adc2_r_enum, RT5677_MONO_ADC_MIXER,
+       RT5677_SEL_MONO_ADC_R2_SFT, rt5677_mono_adc2_r_src);
+
+static const struct snd_kcontrol_new rt5677_mono_adc2_r_mux =
+       SOC_DAPM_ENUM("Mono ADC2 R source", rt5677_mono_adc2_r_enum);
+
+/* Mono ADC Right source 1 */ /* MX-28 [5:4] */
+static const char * const rt5677_mono_adc1_r_src[] = {
+       "DD MIX1R", "ADC2", "MONO DAC MIXR"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_mono_adc1_r_enum, RT5677_MONO_ADC_MIXER,
+       RT5677_SEL_MONO_ADC_R1_SFT, rt5677_mono_adc1_r_src);
+
+static const struct snd_kcontrol_new rt5677_mono_adc1_r_mux =
+       SOC_DAPM_ENUM("Mono ADC1 R source", rt5677_mono_adc1_r_enum);
+
+/* Stereo4 ADC Source 2 */ /* MX-24 [11:10] */
+static const char * const rt5677_stereo4_adc2_src[] = {
+       "DD MIX1", "DMIC", "DD MIX2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_stereo4_adc2_enum, RT5677_STO4_ADC_MIXER,
+       RT5677_SEL_STO4_ADC2_SFT, rt5677_stereo4_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_sto4_adc2_mux =
+       SOC_DAPM_ENUM("Stereo4 ADC2 source", rt5677_stereo4_adc2_enum);
+
+
+/* Stereo4 ADC Source 1 */ /* MX-24 [13:12] */
+static const char * const rt5677_stereo4_adc1_src[] = {
+       "DD MIX1", "ADC1/2", "DD MIX2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_stereo4_adc1_enum, RT5677_STO4_ADC_MIXER,
+       RT5677_SEL_STO4_ADC1_SFT, rt5677_stereo4_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_sto4_adc1_mux =
+       SOC_DAPM_ENUM("Stereo4 ADC1 source", rt5677_stereo4_adc1_enum);
+
+/* InBound0/1 Source */ /* MX-A3 [14:12] */
+static const char * const rt5677_inbound01_src[] = {
+       "IF1 DAC 01", "IF2 DAC 01", "SLB DAC 01", "STO1 ADC MIX",
+       "VAD ADC/DAC1 FS"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_inbound01_enum, RT5677_DSP_INB_CTRL1,
+       RT5677_IB01_SRC_SFT, rt5677_inbound01_src);
+
+static const struct snd_kcontrol_new rt5677_ib01_src_mux =
+       SOC_DAPM_ENUM("InBound0/1 Source", rt5677_inbound01_enum);
+
+/* InBound2/3 Source */ /* MX-A3 [10:8] */
+static const char * const rt5677_inbound23_src[] = {
+       "IF1 DAC 23", "IF2 DAC 23", "SLB DAC 23", "STO2 ADC MIX",
+       "DAC1 FS", "IF4 DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_inbound23_enum, RT5677_DSP_INB_CTRL1,
+       RT5677_IB23_SRC_SFT, rt5677_inbound23_src);
+
+static const struct snd_kcontrol_new rt5677_ib23_src_mux =
+       SOC_DAPM_ENUM("InBound2/3 Source", rt5677_inbound23_enum);
+
+/* InBound4/5 Source */ /* MX-A3 [6:4] */
+static const char * const rt5677_inbound45_src[] = {
+       "IF1 DAC 45", "IF2 DAC 45", "SLB DAC 45", "STO3 ADC MIX",
+       "IF3 DAC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_inbound45_enum, RT5677_DSP_INB_CTRL1,
+       RT5677_IB45_SRC_SFT, rt5677_inbound45_src);
+
+static const struct snd_kcontrol_new rt5677_ib45_src_mux =
+       SOC_DAPM_ENUM("InBound4/5 Source", rt5677_inbound45_enum);
+
+/* InBound6 Source */ /* MX-A3 [2:0] */
+static const char * const rt5677_inbound6_src[] = {
+       "IF1 DAC 6", "IF2 DAC 6", "SLB DAC 6", "STO4 ADC MIX L",
+       "IF4 DAC L", "STO1 ADC MIX L", "STO2 ADC MIX L", "STO3 ADC MIX L"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_inbound6_enum, RT5677_DSP_INB_CTRL1,
+       RT5677_IB6_SRC_SFT, rt5677_inbound6_src);
+
+static const struct snd_kcontrol_new rt5677_ib6_src_mux =
+       SOC_DAPM_ENUM("InBound6 Source", rt5677_inbound6_enum);
+
+/* InBound7 Source */ /* MX-A4 [14:12] */
+static const char * const rt5677_inbound7_src[] = {
+       "IF1 DAC 7", "IF2 DAC 7", "SLB DAC 7", "STO4 ADC MIX R",
+       "IF4 DAC R", "STO1 ADC MIX R", "STO2 ADC MIX R", "STO3 ADC MIX R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_inbound7_enum, RT5677_DSP_INB_CTRL2,
+       RT5677_IB7_SRC_SFT, rt5677_inbound7_src);
+
+static const struct snd_kcontrol_new rt5677_ib7_src_mux =
+       SOC_DAPM_ENUM("InBound7 Source", rt5677_inbound7_enum);
+
+/* InBound8 Source */ /* MX-A4 [10:8] */
+static const char * const rt5677_inbound8_src[] = {
+       "STO1 ADC MIX L", "STO2 ADC MIX L", "STO3 ADC MIX L", "STO4 ADC MIX L",
+       "MONO ADC MIX L", "DACL1 FS"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_inbound8_enum, RT5677_DSP_INB_CTRL2,
+       RT5677_IB8_SRC_SFT, rt5677_inbound8_src);
+
+static const struct snd_kcontrol_new rt5677_ib8_src_mux =
+       SOC_DAPM_ENUM("InBound8 Source", rt5677_inbound8_enum);
+
+/* InBound9 Source */ /* MX-A4 [6:4] */
+static const char * const rt5677_inbound9_src[] = {
+       "STO1 ADC MIX R", "STO2 ADC MIX R", "STO3 ADC MIX R", "STO4 ADC MIX R",
+       "MONO ADC MIX R", "DACR1 FS", "DAC1 FS"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_inbound9_enum, RT5677_DSP_INB_CTRL2,
+       RT5677_IB9_SRC_SFT, rt5677_inbound9_src);
+
+static const struct snd_kcontrol_new rt5677_ib9_src_mux =
+       SOC_DAPM_ENUM("InBound9 Source", rt5677_inbound9_enum);
+
+/* VAD Source */ /* MX-9F [6:4] */
+static const char * const rt5677_vad_src[] = {
+       "STO1 ADC MIX L", "MONO ADC MIX L", "MONO ADC MIX R", "STO2 ADC MIX L",
+       "STO3 ADC MIX L"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_vad_enum, RT5677_VAD_CTRL4,
+       RT5677_VAD_SRC_SFT, rt5677_vad_src);
+
+static const struct snd_kcontrol_new rt5677_vad_src_mux =
+       SOC_DAPM_ENUM("VAD Source", rt5677_vad_enum);
+
+/* Sidetone Source */ /* MX-13 [11:9] */
+static const char * const rt5677_sidetone_src[] = {
+       "DMIC1 L", "DMIC2 L", "DMIC3 L", "DMIC4 L", "ADC1", "ADC2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_sidetone_enum, RT5677_SIDETONE_CTRL,
+       RT5677_ST_SEL_SFT, rt5677_sidetone_src);
+
+static const struct snd_kcontrol_new rt5677_sidetone_mux =
+       SOC_DAPM_ENUM("Sidetone Source", rt5677_sidetone_enum);
+
+/* DAC1/2 Source */ /* MX-15 [1:0] */
+static const char * const rt5677_dac12_src[] = {
+       "STO1 DAC MIX", "MONO DAC MIX", "DD MIX1", "DD MIX2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_dac12_enum, RT5677_ANA_DAC1_2_3_SRC,
+       RT5677_ANA_DAC1_2_SRC_SEL_SFT, rt5677_dac12_src);
+
+static const struct snd_kcontrol_new rt5677_dac12_mux =
+       SOC_DAPM_ENUM("Analog DAC1/2 Source", rt5677_dac12_enum);
+
+/* DAC3 Source */ /* MX-15 [5:4] */
+static const char * const rt5677_dac3_src[] = {
+       "MONO DAC MIXL", "MONO DAC MIXR", "DD MIX1L", "DD MIX2L"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_dac3_enum, RT5677_ANA_DAC1_2_3_SRC,
+       RT5677_ANA_DAC3_SRC_SEL_SFT, rt5677_dac3_src);
+
+static const struct snd_kcontrol_new rt5677_dac3_mux =
+       SOC_DAPM_ENUM("Analog DAC3 Source", rt5677_dac3_enum);
+
+/* PDM channel source */ /* MX-31 [13:12][9:8][5:4][1:0] */
+static const char * const rt5677_pdm_src[] = {
+       "STO1 DAC MIX", "MONO DAC MIX", "DD MIX1", "DD MIX2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_pdm1_l_enum, RT5677_PDM_OUT_CTRL,
+       RT5677_SEL_PDM1_L_SFT, rt5677_pdm_src);
+
+static const struct snd_kcontrol_new rt5677_pdm1_l_mux =
+       SOC_DAPM_ENUM("PDM1 source", rt5677_pdm1_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_pdm2_l_enum, RT5677_PDM_OUT_CTRL,
+       RT5677_SEL_PDM2_L_SFT, rt5677_pdm_src);
+
+static const struct snd_kcontrol_new rt5677_pdm2_l_mux =
+       SOC_DAPM_ENUM("PDM2 source", rt5677_pdm2_l_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_pdm1_r_enum, RT5677_PDM_OUT_CTRL,
+       RT5677_SEL_PDM1_R_SFT, rt5677_pdm_src);
+
+static const struct snd_kcontrol_new rt5677_pdm1_r_mux =
+       SOC_DAPM_ENUM("PDM1 source", rt5677_pdm1_r_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_pdm2_r_enum, RT5677_PDM_OUT_CTRL,
+       RT5677_SEL_PDM2_R_SFT, rt5677_pdm_src);
+
+static const struct snd_kcontrol_new rt5677_pdm2_r_mux =
+       SOC_DAPM_ENUM("PDM2 source", rt5677_pdm2_r_enum);
+
+/* TDM IF1/2 SLB ADC1 Data Selection */ /* MX-3C MX-41 [5:4] MX-08 [1:0]*/
+static const char * const rt5677_if12_adc1_src[] = {
+       "STO1 ADC MIX", "OB01", "VAD ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc1_enum, RT5677_TDM1_CTRL2,
+       RT5677_IF1_ADC1_SFT, rt5677_if12_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc1_mux =
+       SOC_DAPM_ENUM("IF1 ADC1 source", rt5677_if1_adc1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc1_enum, RT5677_TDM2_CTRL2,
+       RT5677_IF2_ADC1_SFT, rt5677_if12_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc1_mux =
+       SOC_DAPM_ENUM("IF2 ADC1 source", rt5677_if2_adc1_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_slb_adc1_enum, RT5677_SLIMBUS_RX,
+       RT5677_SLB_ADC1_SFT, rt5677_if12_adc1_src);
+
+static const struct snd_kcontrol_new rt5677_slb_adc1_mux =
+       SOC_DAPM_ENUM("SLB ADC1 source", rt5677_slb_adc1_enum);
+
+/* TDM IF1/2 SLB ADC2 Data Selection */ /* MX-3C MX-41 [7:6] MX-08 [3:2] */
+static const char * const rt5677_if12_adc2_src[] = {
+       "STO2 ADC MIX", "OB23"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc2_enum, RT5677_TDM1_CTRL2,
+       RT5677_IF1_ADC2_SFT, rt5677_if12_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc2_mux =
+       SOC_DAPM_ENUM("IF1 ADC2 source", rt5677_if1_adc2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc2_enum, RT5677_TDM2_CTRL2,
+       RT5677_IF2_ADC2_SFT, rt5677_if12_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc2_mux =
+       SOC_DAPM_ENUM("IF2 ADC2 source", rt5677_if2_adc2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_slb_adc2_enum, RT5677_SLIMBUS_RX,
+       RT5677_SLB_ADC2_SFT, rt5677_if12_adc2_src);
+
+static const struct snd_kcontrol_new rt5677_slb_adc2_mux =
+       SOC_DAPM_ENUM("SLB ADC2 source", rt5677_slb_adc2_enum);
+
+/* TDM IF1/2 SLB ADC3 Data Selection */ /* MX-3C MX-41 [9:8] MX-08 [5:4] */
+static const char * const rt5677_if12_adc3_src[] = {
+       "STO3 ADC MIX", "MONO ADC MIX", "OB45"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc3_enum, RT5677_TDM1_CTRL2,
+       RT5677_IF1_ADC3_SFT, rt5677_if12_adc3_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc3_mux =
+       SOC_DAPM_ENUM("IF1 ADC3 source", rt5677_if1_adc3_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc3_enum, RT5677_TDM2_CTRL2,
+       RT5677_IF2_ADC3_SFT, rt5677_if12_adc3_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc3_mux =
+       SOC_DAPM_ENUM("IF2 ADC3 source", rt5677_if2_adc3_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_slb_adc3_enum, RT5677_SLIMBUS_RX,
+       RT5677_SLB_ADC3_SFT, rt5677_if12_adc3_src);
+
+static const struct snd_kcontrol_new rt5677_slb_adc3_mux =
+       SOC_DAPM_ENUM("SLB ADC3 source", rt5677_slb_adc3_enum);
+
+/* TDM IF1/2 SLB ADC4 Data Selection */ /* MX-3C MX-41 [11:10]  MX-08 [7:6] */
+static const char * const rt5677_if12_adc4_src[] = {
+       "STO4 ADC MIX", "OB67", "OB01"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc4_enum, RT5677_TDM1_CTRL2,
+       RT5677_IF1_ADC4_SFT, rt5677_if12_adc4_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc4_mux =
+       SOC_DAPM_ENUM("IF1 ADC4 source", rt5677_if1_adc4_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc4_enum, RT5677_TDM2_CTRL2,
+       RT5677_IF2_ADC4_SFT, rt5677_if12_adc4_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc4_mux =
+       SOC_DAPM_ENUM("IF2 ADC4 source", rt5677_if2_adc4_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_slb_adc4_enum, RT5677_SLIMBUS_RX,
+       RT5677_SLB_ADC4_SFT, rt5677_if12_adc4_src);
+
+static const struct snd_kcontrol_new rt5677_slb_adc4_mux =
+       SOC_DAPM_ENUM("SLB ADC4 source", rt5677_slb_adc4_enum);
+
+/* Interface3/4 ADC Data Input */ /* MX-2F [3:0] MX-30 [7:4]*/
+static const char * const rt5677_if34_adc_src[] = {
+       "STO1 ADC MIX", "STO2 ADC MIX", "STO3 ADC MIX", "STO4 ADC MIX",
+       "MONO ADC MIX", "OB01", "OB23", "VAD ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if3_adc_enum, RT5677_IF3_DATA,
+       RT5677_IF3_ADC_IN_SFT, rt5677_if34_adc_src);
+
+static const struct snd_kcontrol_new rt5677_if3_adc_mux =
+       SOC_DAPM_ENUM("IF3 ADC source", rt5677_if3_adc_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if4_adc_enum, RT5677_IF4_DATA,
+       RT5677_IF4_ADC_IN_SFT, rt5677_if34_adc_src);
+
+static const struct snd_kcontrol_new rt5677_if4_adc_mux =
+       SOC_DAPM_ENUM("IF4 ADC source", rt5677_if4_adc_enum);
+
+static int rt5677_bst1_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+                       RT5677_PWR_BST1_P, RT5677_PWR_BST1_P);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+                       RT5677_PWR_BST1_P, 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5677_bst2_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+                       RT5677_PWR_BST2_P, RT5677_PWR_BST2_P);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+                       RT5677_PWR_BST2_P, 0);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5677_set_pll1_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               regmap_update_bits(rt5677->regmap, RT5677_PLL1_CTRL2, 0x2, 0x2);
+               regmap_update_bits(rt5677->regmap, RT5677_PLL1_CTRL2, 0x2, 0x0);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5677_set_pll2_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               regmap_update_bits(rt5677->regmap, RT5677_PLL2_CTRL2, 0x2, 0x2);
+               regmap_update_bits(rt5677->regmap, RT5677_PLL2_CTRL2, 0x2, 0x0);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5677_set_micbias1_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+                       RT5677_PWR_CLK_MB1 | RT5677_PWR_PP_MB1 |
+                       RT5677_PWR_CLK_MB, RT5677_PWR_CLK_MB1 |
+                       RT5677_PWR_PP_MB1 | RT5677_PWR_CLK_MB);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT,
+               0, rt5677_set_pll1_event, SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_SUPPLY("PLL2", RT5677_PWR_ANLG2, RT5677_PWR_PLL2_BIT,
+               0, rt5677_set_pll2_event, SND_SOC_DAPM_POST_PMU),
+
+       /* Input Side */
+       /* micbias */
+       SND_SOC_DAPM_SUPPLY("micbias1", RT5677_PWR_ANLG2, RT5677_PWR_MB1_BIT,
+               0, rt5677_set_micbias1_event, SND_SOC_DAPM_POST_PMU),
+
+       /* Input Lines */
+       SND_SOC_DAPM_INPUT("DMIC L1"),
+       SND_SOC_DAPM_INPUT("DMIC R1"),
+       SND_SOC_DAPM_INPUT("DMIC L2"),
+       SND_SOC_DAPM_INPUT("DMIC R2"),
+       SND_SOC_DAPM_INPUT("DMIC L3"),
+       SND_SOC_DAPM_INPUT("DMIC R3"),
+       SND_SOC_DAPM_INPUT("DMIC L4"),
+       SND_SOC_DAPM_INPUT("DMIC R4"),
+
+       SND_SOC_DAPM_INPUT("IN1P"),
+       SND_SOC_DAPM_INPUT("IN1N"),
+       SND_SOC_DAPM_INPUT("IN2P"),
+       SND_SOC_DAPM_INPUT("IN2N"),
+
+       SND_SOC_DAPM_INPUT("Haptic Generator"),
+
+       SND_SOC_DAPM_PGA("DMIC1", RT5677_DMIC_CTRL1, RT5677_DMIC_1_EN_SFT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_PGA("DMIC2", RT5677_DMIC_CTRL1, RT5677_DMIC_2_EN_SFT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_PGA("DMIC3", RT5677_DMIC_CTRL1, RT5677_DMIC_3_EN_SFT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_PGA("DMIC4", RT5677_DMIC_CTRL2, RT5677_DMIC_4_EN_SFT, 0,
+               NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+               set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+
+       /* Boost */
+       SND_SOC_DAPM_PGA_E("BST1", RT5677_PWR_ANLG2,
+               RT5677_PWR_BST1_BIT, 0, NULL, 0, rt5677_bst1_event,
+               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_E("BST2", RT5677_PWR_ANLG2,
+               RT5677_PWR_BST2_BIT, 0, NULL, 0, rt5677_bst2_event,
+               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+       /* ADCs */
+       SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM,
+               0, 0),
+       SND_SOC_DAPM_ADC("ADC 2", NULL, SND_SOC_NOPM,
+               0, 0),
+       SND_SOC_DAPM_PGA("ADC 1_2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("ADC 1 power", RT5677_PWR_DIG1,
+               RT5677_PWR_ADC_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC 2 power", RT5677_PWR_DIG1,
+               RT5677_PWR_ADC_R_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5677_PWR_DIG1,
+               RT5677_PWR_ADCFED1_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC2 clock", RT5677_PWR_DIG1,
+               RT5677_PWR_ADCFED2_BIT, 0, NULL, 0),
+
+       /* ADC Mux */
+       SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_sto1_dmic_mux),
+       SND_SOC_DAPM_MUX("Stereo1 ADC1 Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_sto1_adc1_mux),
+       SND_SOC_DAPM_MUX("Stereo1 ADC2 Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_sto1_adc2_mux),
+       SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_sto2_dmic_mux),
+       SND_SOC_DAPM_MUX("Stereo2 ADC1 Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_sto2_adc1_mux),
+       SND_SOC_DAPM_MUX("Stereo2 ADC2 Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_sto2_adc2_mux),
+       SND_SOC_DAPM_MUX("Stereo2 ADC LR Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_sto2_adc_lr_mux),
+       SND_SOC_DAPM_MUX("Stereo3 DMIC Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_sto3_dmic_mux),
+       SND_SOC_DAPM_MUX("Stereo3 ADC1 Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_sto3_adc1_mux),
+       SND_SOC_DAPM_MUX("Stereo3 ADC2 Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_sto3_adc2_mux),
+       SND_SOC_DAPM_MUX("Stereo4 DMIC Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_sto4_dmic_mux),
+       SND_SOC_DAPM_MUX("Stereo4 ADC1 Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_sto4_adc1_mux),
+       SND_SOC_DAPM_MUX("Stereo4 ADC2 Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_sto4_adc2_mux),
+       SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_mono_dmic_l_mux),
+       SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_mono_dmic_r_mux),
+       SND_SOC_DAPM_MUX("Mono ADC2 L Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_mono_adc2_l_mux),
+       SND_SOC_DAPM_MUX("Mono ADC1 L Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_mono_adc1_l_mux),
+       SND_SOC_DAPM_MUX("Mono ADC1 R Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_mono_adc1_r_mux),
+       SND_SOC_DAPM_MUX("Mono ADC2 R Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_mono_adc2_r_mux),
+
+       /* ADC Mixer */
+       SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5677_PWR_DIG2,
+               RT5677_PWR_ADC_S1F_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("adc stereo2 filter", RT5677_PWR_DIG2,
+               RT5677_PWR_ADC_S2F_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("adc stereo3 filter", RT5677_PWR_DIG2,
+               RT5677_PWR_ADC_S3F_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("adc stereo4 filter", RT5677_PWR_DIG2,
+               RT5677_PWR_ADC_S4F_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5677_sto1_adc_l_mix, ARRAY_SIZE(rt5677_sto1_adc_l_mix)),
+       SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5677_sto1_adc_r_mix, ARRAY_SIZE(rt5677_sto1_adc_r_mix)),
+       SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5677_sto2_adc_l_mix, ARRAY_SIZE(rt5677_sto2_adc_l_mix)),
+       SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5677_sto2_adc_r_mix, ARRAY_SIZE(rt5677_sto2_adc_r_mix)),
+       SND_SOC_DAPM_MIXER("Sto3 ADC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5677_sto3_adc_l_mix, ARRAY_SIZE(rt5677_sto3_adc_l_mix)),
+       SND_SOC_DAPM_MIXER("Sto3 ADC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5677_sto3_adc_r_mix, ARRAY_SIZE(rt5677_sto3_adc_r_mix)),
+       SND_SOC_DAPM_MIXER("Sto4 ADC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5677_sto4_adc_l_mix, ARRAY_SIZE(rt5677_sto4_adc_l_mix)),
+       SND_SOC_DAPM_MIXER("Sto4 ADC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5677_sto4_adc_r_mix, ARRAY_SIZE(rt5677_sto4_adc_r_mix)),
+       SND_SOC_DAPM_SUPPLY("adc mono left filter", RT5677_PWR_DIG2,
+               RT5677_PWR_ADC_MF_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Mono ADC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5677_mono_adc_l_mix, ARRAY_SIZE(rt5677_mono_adc_l_mix)),
+       SND_SOC_DAPM_SUPPLY("adc mono right filter", RT5677_PWR_DIG2,
+               RT5677_PWR_ADC_MF_R_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Mono ADC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5677_mono_adc_r_mix, ARRAY_SIZE(rt5677_mono_adc_r_mix)),
+
+       /* ADC PGA */
+       SND_SOC_DAPM_PGA("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Stereo2 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Stereo2 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Stereo3 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Stereo3 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Stereo3 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Stereo4 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Stereo4 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Stereo4 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Mono ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       /* DSP */
+       SND_SOC_DAPM_MUX("IB9 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_ib9_src_mux),
+       SND_SOC_DAPM_MUX("IB8 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_ib8_src_mux),
+       SND_SOC_DAPM_MUX("IB7 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_ib7_src_mux),
+       SND_SOC_DAPM_MUX("IB6 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_ib6_src_mux),
+       SND_SOC_DAPM_MUX("IB45 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_ib45_src_mux),
+       SND_SOC_DAPM_MUX("IB23 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_ib23_src_mux),
+       SND_SOC_DAPM_MUX("IB01 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_ib01_src_mux),
+       SND_SOC_DAPM_MUX("IB45 Bypass Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_ib45_bypass_src_mux),
+       SND_SOC_DAPM_MUX("IB23 Bypass Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_ib23_bypass_src_mux),
+       SND_SOC_DAPM_MUX("IB01 Bypass Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_ib01_bypass_src_mux),
+       SND_SOC_DAPM_MUX("OB23 Bypass Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_ob23_bypass_src_mux),
+       SND_SOC_DAPM_MUX("OB01 Bypass Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_ob01_bypass_src_mux),
+
+       SND_SOC_DAPM_PGA("OB45", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("OB67", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("OutBound2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("OutBound3", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("OutBound4", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("OutBound5", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("OutBound6", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("OutBound7", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       /* Digital Interface */
+       SND_SOC_DAPM_SUPPLY("I2S1", RT5677_PWR_DIG1,
+               RT5677_PWR_I2S1_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC0", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC5", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC6", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC7", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC01", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC23", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC45", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 DAC67", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("I2S2", RT5677_PWR_DIG1,
+               RT5677_PWR_I2S2_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC0", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC5", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC6", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC7", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC01", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC23", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC45", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 DAC67", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("I2S3", RT5677_PWR_DIG1,
+               RT5677_PWR_I2S3_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF3 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF3 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF3 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF3 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF3 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF3 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("I2S4", RT5677_PWR_DIG1,
+               RT5677_PWR_I2S4_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF4 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF4 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF4 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF4 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF4 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF4 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("SLB", RT5677_PWR_DIG1,
+               RT5677_PWR_SLB_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB DAC0", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB DAC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB DAC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB DAC5", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB DAC6", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB DAC7", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB DAC01", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB DAC23", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB DAC45", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB DAC67", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SLB ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       /* Digital Interface Select */
+       SND_SOC_DAPM_MUX("IF1 ADC1 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc1_mux),
+       SND_SOC_DAPM_MUX("IF1 ADC2 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc2_mux),
+       SND_SOC_DAPM_MUX("IF1 ADC3 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc3_mux),
+       SND_SOC_DAPM_MUX("IF1 ADC4 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc4_mux),
+       SND_SOC_DAPM_MUX("IF2 ADC1 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc1_mux),
+       SND_SOC_DAPM_MUX("IF2 ADC2 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc2_mux),
+       SND_SOC_DAPM_MUX("IF2 ADC3 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc3_mux),
+       SND_SOC_DAPM_MUX("IF2 ADC4 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc4_mux),
+       SND_SOC_DAPM_MUX("IF3 ADC Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if3_adc_mux),
+       SND_SOC_DAPM_MUX("IF4 ADC Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if4_adc_mux),
+       SND_SOC_DAPM_MUX("SLB ADC1 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_slb_adc1_mux),
+       SND_SOC_DAPM_MUX("SLB ADC2 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_slb_adc2_mux),
+       SND_SOC_DAPM_MUX("SLB ADC3 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_slb_adc3_mux),
+       SND_SOC_DAPM_MUX("SLB ADC4 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_slb_adc4_mux),
+
+       /* Audio Interface */
+       SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("AIF3RX", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF3TX", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("AIF4RX", "AIF4 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF4TX", "AIF4 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("SLBRX", "SLIMBus Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("SLBTX", "SLIMBus Capture", 0, SND_SOC_NOPM, 0, 0),
+
+       /* Sidetone Mux */
+       SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_sidetone_mux),
+       /* VAD Mux*/
+       SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_vad_src_mux),
+
+       /* Tensilica DSP */
+       SND_SOC_DAPM_PGA("Tensilica DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("OB01 MIX", SND_SOC_NOPM, 0, 0,
+               rt5677_ob_01_mix, ARRAY_SIZE(rt5677_ob_01_mix)),
+       SND_SOC_DAPM_MIXER("OB23 MIX", SND_SOC_NOPM, 0, 0,
+               rt5677_ob_23_mix, ARRAY_SIZE(rt5677_ob_23_mix)),
+       SND_SOC_DAPM_MIXER("OB4 MIX", SND_SOC_NOPM, 0, 0,
+               rt5677_ob_4_mix, ARRAY_SIZE(rt5677_ob_4_mix)),
+       SND_SOC_DAPM_MIXER("OB5 MIX", SND_SOC_NOPM, 0, 0,
+               rt5677_ob_5_mix, ARRAY_SIZE(rt5677_ob_5_mix)),
+       SND_SOC_DAPM_MIXER("OB6 MIX", SND_SOC_NOPM, 0, 0,
+               rt5677_ob_6_mix, ARRAY_SIZE(rt5677_ob_6_mix)),
+       SND_SOC_DAPM_MIXER("OB7 MIX", SND_SOC_NOPM, 0, 0,
+               rt5677_ob_7_mix, ARRAY_SIZE(rt5677_ob_7_mix)),
+
+       /* Output Side */
+       /* DAC mixer before sound effect  */
+       SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
+               rt5677_dac_l_mix, ARRAY_SIZE(rt5677_dac_l_mix)),
+       SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
+               rt5677_dac_r_mix, ARRAY_SIZE(rt5677_dac_r_mix)),
+       SND_SOC_DAPM_PGA("DAC1 FS", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       /* DAC Mux */
+       SND_SOC_DAPM_MUX("DAC1 Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_dac1_mux),
+       SND_SOC_DAPM_MUX("ADDA1 Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_adda1_mux),
+       SND_SOC_DAPM_MUX("DAC12 SRC Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_dac12_mux),
+       SND_SOC_DAPM_MUX("DAC3 SRC Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_dac3_mux),
+
+       /* DAC2 channel Mux */
+       SND_SOC_DAPM_MUX("DAC2 L Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_dac2_l_mux),
+       SND_SOC_DAPM_MUX("DAC2 R Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5677_dac2_r_mux),
+
+       /* DAC3 channel Mux */
+       SND_SOC_DAPM_MUX("DAC3 L Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_dac3_l_mux),
+       SND_SOC_DAPM_MUX("DAC3 R Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_dac3_r_mux),
+
+       /* DAC4 channel Mux */
+       SND_SOC_DAPM_MUX("DAC4 L Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_dac4_l_mux),
+       SND_SOC_DAPM_MUX("DAC4 R Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_dac4_r_mux),
+
+       /* DAC Mixer */
+       SND_SOC_DAPM_SUPPLY("dac stereo1 filter", RT5677_PWR_DIG2,
+               RT5677_PWR_DAC_S1F_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("dac mono left filter", RT5677_PWR_DIG2,
+               RT5677_PWR_DAC_M2F_L_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("dac mono right filter", RT5677_PWR_DIG2,
+               RT5677_PWR_DAC_M2F_R_BIT, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5677_sto1_dac_l_mix, ARRAY_SIZE(rt5677_sto1_dac_l_mix)),
+       SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5677_sto1_dac_r_mix, ARRAY_SIZE(rt5677_sto1_dac_r_mix)),
+       SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5677_mono_dac_l_mix, ARRAY_SIZE(rt5677_mono_dac_l_mix)),
+       SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5677_mono_dac_r_mix, ARRAY_SIZE(rt5677_mono_dac_r_mix)),
+       SND_SOC_DAPM_MIXER("DD1 MIXL", SND_SOC_NOPM, 0, 0,
+               rt5677_dd1_l_mix, ARRAY_SIZE(rt5677_dd1_l_mix)),
+       SND_SOC_DAPM_MIXER("DD1 MIXR", SND_SOC_NOPM, 0, 0,
+               rt5677_dd1_r_mix, ARRAY_SIZE(rt5677_dd1_r_mix)),
+       SND_SOC_DAPM_MIXER("DD2 MIXL", SND_SOC_NOPM, 0, 0,
+               rt5677_dd2_l_mix, ARRAY_SIZE(rt5677_dd2_l_mix)),
+       SND_SOC_DAPM_MIXER("DD2 MIXR", SND_SOC_NOPM, 0, 0,
+               rt5677_dd2_r_mix, ARRAY_SIZE(rt5677_dd2_r_mix)),
+       SND_SOC_DAPM_PGA("Stereo DAC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Mono DAC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("DD1 MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("DD2 MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       /* DACs */
+       SND_SOC_DAPM_DAC("DAC 1", NULL, RT5677_PWR_DIG1,
+               RT5677_PWR_DAC1_BIT, 0),
+       SND_SOC_DAPM_DAC("DAC 2", NULL, RT5677_PWR_DIG1,
+               RT5677_PWR_DAC2_BIT, 0),
+       SND_SOC_DAPM_DAC("DAC 3", NULL, RT5677_PWR_DIG1,
+               RT5677_PWR_DAC3_BIT, 0),
+
+       /* PDM */
+       SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5677_PWR_DIG2,
+               RT5677_PWR_PDM1_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5677_PWR_DIG2,
+               RT5677_PWR_PDM2_BIT, 0, NULL, 0),
+
+       SND_SOC_DAPM_MUX("PDM1 L Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM1_L_SFT,
+               1, &rt5677_pdm1_l_mux),
+       SND_SOC_DAPM_MUX("PDM1 R Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM1_R_SFT,
+               1, &rt5677_pdm1_r_mux),
+       SND_SOC_DAPM_MUX("PDM2 L Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM2_L_SFT,
+               1, &rt5677_pdm2_l_mux),
+       SND_SOC_DAPM_MUX("PDM2 R Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM2_R_SFT,
+               1, &rt5677_pdm2_r_mux),
+
+       SND_SOC_DAPM_PGA_S("LOUT1 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO1_BIT,
+               0, NULL, 0),
+       SND_SOC_DAPM_PGA_S("LOUT2 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO2_BIT,
+               0, NULL, 0),
+       SND_SOC_DAPM_PGA_S("LOUT3 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO3_BIT,
+               0, NULL, 0),
+
+       /* Output Lines */
+       SND_SOC_DAPM_OUTPUT("LOUT1"),
+       SND_SOC_DAPM_OUTPUT("LOUT2"),
+       SND_SOC_DAPM_OUTPUT("LOUT3"),
+       SND_SOC_DAPM_OUTPUT("PDM1L"),
+       SND_SOC_DAPM_OUTPUT("PDM1R"),
+       SND_SOC_DAPM_OUTPUT("PDM2L"),
+       SND_SOC_DAPM_OUTPUT("PDM2R"),
+};
+
+static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
+       { "DMIC1", NULL, "DMIC L1" },
+       { "DMIC1", NULL, "DMIC R1" },
+       { "DMIC2", NULL, "DMIC L2" },
+       { "DMIC2", NULL, "DMIC R2" },
+       { "DMIC3", NULL, "DMIC L3" },
+       { "DMIC3", NULL, "DMIC R3" },
+       { "DMIC4", NULL, "DMIC L4" },
+       { "DMIC4", NULL, "DMIC R4" },
+
+       { "DMIC L1", NULL, "DMIC CLK" },
+       { "DMIC R1", NULL, "DMIC CLK" },
+       { "DMIC L2", NULL, "DMIC CLK" },
+       { "DMIC R2", NULL, "DMIC CLK" },
+       { "DMIC L3", NULL, "DMIC CLK" },
+       { "DMIC R3", NULL, "DMIC CLK" },
+       { "DMIC L4", NULL, "DMIC CLK" },
+       { "DMIC R4", NULL, "DMIC CLK" },
+
+       { "BST1", NULL, "IN1P" },
+       { "BST1", NULL, "IN1N" },
+       { "BST2", NULL, "IN2P" },
+       { "BST2", NULL, "IN2N" },
+
+       { "IN1P", NULL, "micbias1" },
+       { "IN1N", NULL, "micbias1" },
+       { "IN2P", NULL, "micbias1" },
+       { "IN2N", NULL, "micbias1" },
+
+       { "ADC 1", NULL, "BST1" },
+       { "ADC 1", NULL, "ADC 1 power" },
+       { "ADC 1", NULL, "ADC1 clock" },
+       { "ADC 2", NULL, "BST2" },
+       { "ADC 2", NULL, "ADC 2 power" },
+       { "ADC 2", NULL, "ADC2 clock" },
+
+       { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
+       { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
+       { "Stereo1 DMIC Mux", "DMIC3", "DMIC3" },
+       { "Stereo1 DMIC Mux", "DMIC4", "DMIC4" },
+
+       { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" },
+       { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" },
+       { "Stereo2 DMIC Mux", "DMIC3", "DMIC3" },
+       { "Stereo2 DMIC Mux", "DMIC4", "DMIC4" },
+
+       { "Stereo3 DMIC Mux", "DMIC1", "DMIC1" },
+       { "Stereo3 DMIC Mux", "DMIC2", "DMIC2" },
+       { "Stereo3 DMIC Mux", "DMIC3", "DMIC3" },
+       { "Stereo3 DMIC Mux", "DMIC4", "DMIC4" },
+
+       { "Stereo4 DMIC Mux", "DMIC1", "DMIC1" },
+       { "Stereo4 DMIC Mux", "DMIC2", "DMIC2" },
+       { "Stereo4 DMIC Mux", "DMIC3", "DMIC3" },
+       { "Stereo4 DMIC Mux", "DMIC4", "DMIC4" },
+
+       { "Mono DMIC L Mux", "DMIC1", "DMIC1" },
+       { "Mono DMIC L Mux", "DMIC2", "DMIC2" },
+       { "Mono DMIC L Mux", "DMIC3", "DMIC3" },
+       { "Mono DMIC L Mux", "DMIC4", "DMIC4" },
+
+       { "Mono DMIC R Mux", "DMIC1", "DMIC1" },
+       { "Mono DMIC R Mux", "DMIC2", "DMIC2" },
+       { "Mono DMIC R Mux", "DMIC3", "DMIC3" },
+       { "Mono DMIC R Mux", "DMIC4", "DMIC4" },
+
+       { "ADC 1_2", NULL, "ADC 1" },
+       { "ADC 1_2", NULL, "ADC 2" },
+
+       { "Stereo1 ADC1 Mux", "DD MIX1", "DD1 MIX" },
+       { "Stereo1 ADC1 Mux", "ADC1/2", "ADC 1_2" },
+       { "Stereo1 ADC1 Mux", "Stereo DAC MIX", "Stereo DAC MIX" },
+
+       { "Stereo1 ADC2 Mux", "DD MIX1", "DD1 MIX" },
+       { "Stereo1 ADC2 Mux", "DMIC", "Stereo1 DMIC Mux" },
+       { "Stereo1 ADC2 Mux", "Stereo DAC MIX", "Stereo DAC MIX" },
+
+       { "Stereo2 ADC1 Mux", "DD MIX1", "DD1 MIX" },
+       { "Stereo2 ADC1 Mux", "ADC1/2", "ADC 1_2" },
+       { "Stereo2 ADC1 Mux", "Stereo DAC MIX", "Stereo DAC MIX" },
+
+       { "Stereo2 ADC2 Mux", "DD MIX1", "DD1 MIX" },
+       { "Stereo2 ADC2 Mux", "DMIC", "Stereo2 DMIC Mux" },
+       { "Stereo2 ADC2 Mux", "Stereo DAC MIX", "Stereo DAC MIX" },
+
+       { "Stereo3 ADC1 Mux", "DD MIX1", "DD1 MIX" },
+       { "Stereo3 ADC1 Mux", "ADC1/2", "ADC 1_2" },
+       { "Stereo3 ADC1 Mux", "Stereo DAC MIX", "Stereo DAC MIX" },
+
+       { "Stereo3 ADC2 Mux", "DD MIX1", "DD1 MIX" },
+       { "Stereo3 ADC2 Mux", "DMIC", "Stereo3 DMIC Mux" },
+       { "Stereo3 ADC2 Mux", "Stereo DAC MIX", "Stereo DAC MIX" },
+
+       { "Stereo4 ADC1 Mux", "DD MIX1", "DD1 MIX" },
+       { "Stereo4 ADC1 Mux", "ADC1/2", "ADC 1_2" },
+       { "Stereo4 ADC1 Mux", "DD MIX2", "DD2 MIX" },
+
+       { "Stereo4 ADC2 Mux", "DD MIX1", "DD1 MIX" },
+       { "Stereo4 ADC2 Mux", "DMIC", "Stereo3 DMIC Mux" },
+       { "Stereo4 ADC2 Mux", "DD MIX2", "DD2 MIX" },
+
+       { "Mono ADC2 L Mux", "DD MIX1L", "DD1 MIXL" },
+       { "Mono ADC2 L Mux", "DMIC", "Mono DMIC L Mux" },
+       { "Mono ADC2 L Mux", "MONO DAC MIXL", "Mono DAC MIXL" },
+
+       { "Mono ADC1 L Mux", "DD MIX1L", "DD1 MIXL" },
+       { "Mono ADC1 L Mux", "ADC1", "ADC 1" },
+       { "Mono ADC1 L Mux", "MONO DAC MIXL", "Mono DAC MIXL" },
+
+       { "Mono ADC1 R Mux", "DD MIX1R", "DD1 MIXR" },
+       { "Mono ADC1 R Mux", "ADC2", "ADC 2" },
+       { "Mono ADC1 R Mux", "MONO DAC MIXR", "Mono DAC MIXR" },
+
+       { "Mono ADC2 R Mux", "DD MIX1R", "DD1 MIXR" },
+       { "Mono ADC2 R Mux", "DMIC", "Mono DMIC R Mux" },
+       { "Mono ADC2 R Mux", "MONO DAC MIXR", "Mono DAC MIXR" },
+
+       { "Sto1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC1 Mux" },
+       { "Sto1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC2 Mux" },
+       { "Sto1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC1 Mux" },
+       { "Sto1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC2 Mux" },
+
+       { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
+       { "Stereo1 ADC MIXL", NULL, "adc stereo1 filter" },
+       { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
+       { "Stereo1 ADC MIXR", NULL, "adc stereo1 filter" },
+       { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" },
+       { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" },
+
+       { "Sto2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC1 Mux" },
+       { "Sto2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC2 Mux" },
+       { "Sto2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC1 Mux" },
+       { "Sto2 ADC MIXR", "ADC2 Switch", "Stereo2 ADC2 Mux" },
+
+       { "Sto2 ADC LR MIX", NULL, "Sto2 ADC MIXL" },
+       { "Sto2 ADC LR MIX", NULL, "Sto2 ADC MIXR" },
+
+       { "Stereo2 ADC LR Mux", "L", "Sto2 ADC MIXL" },
+       { "Stereo2 ADC LR Mux", "LR", "Sto2 ADC LR MIX" },
+
+       { "Stereo2 ADC MIXL", NULL, "Stereo2 ADC LR Mux" },
+       { "Stereo2 ADC MIXL", NULL, "adc stereo2 filter" },
+       { "adc stereo2 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" },
+       { "Stereo2 ADC MIXR", NULL, "adc stereo2 filter" },
+       { "adc stereo2 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL" },
+       { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR" },
+
+       { "Sto3 ADC MIXL", "ADC1 Switch", "Stereo3 ADC1 Mux" },
+       { "Sto3 ADC MIXL", "ADC2 Switch", "Stereo3 ADC2 Mux" },
+       { "Sto3 ADC MIXR", "ADC1 Switch", "Stereo3 ADC1 Mux" },
+       { "Sto3 ADC MIXR", "ADC2 Switch", "Stereo3 ADC2 Mux" },
+
+       { "Stereo3 ADC MIXL", NULL, "Sto3 ADC MIXL" },
+       { "Stereo3 ADC MIXL", NULL, "adc stereo3 filter" },
+       { "adc stereo3 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "Stereo3 ADC MIXR", NULL, "Sto3 ADC MIXR" },
+       { "Stereo3 ADC MIXR", NULL, "adc stereo3 filter" },
+       { "adc stereo3 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "Stereo3 ADC MIX", NULL, "Stereo3 ADC MIXL" },
+       { "Stereo3 ADC MIX", NULL, "Stereo3 ADC MIXR" },
+
+       { "Sto4 ADC MIXL", "ADC1 Switch", "Stereo4 ADC1 Mux" },
+       { "Sto4 ADC MIXL", "ADC2 Switch", "Stereo4 ADC2 Mux" },
+       { "Sto4 ADC MIXR", "ADC1 Switch", "Stereo4 ADC1 Mux" },
+       { "Sto4 ADC MIXR", "ADC2 Switch", "Stereo4 ADC2 Mux" },
+
+       { "Stereo4 ADC MIXL", NULL, "Sto4 ADC MIXL" },
+       { "Stereo4 ADC MIXL", NULL, "adc stereo4 filter" },
+       { "adc stereo4 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "Stereo4 ADC MIXR", NULL, "Sto4 ADC MIXR" },
+       { "Stereo4 ADC MIXR", NULL, "adc stereo4 filter" },
+       { "adc stereo4 filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "Stereo4 ADC MIX", NULL, "Stereo4 ADC MIXL" },
+       { "Stereo4 ADC MIX", NULL, "Stereo4 ADC MIXR" },
+
+       { "Mono ADC MIXL", "ADC1 Switch", "Mono ADC1 L Mux" },
+       { "Mono ADC MIXL", "ADC2 Switch", "Mono ADC2 L Mux" },
+       { "Mono ADC MIXL", NULL, "adc mono left filter" },
+       { "adc mono left filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "Mono ADC MIXR", "ADC1 Switch", "Mono ADC1 R Mux" },
+       { "Mono ADC MIXR", "ADC2 Switch", "Mono ADC2 R Mux" },
+       { "Mono ADC MIXR", NULL, "adc mono right filter" },
+       { "adc mono right filter", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "Mono ADC MIX", NULL, "Mono ADC MIXL" },
+       { "Mono ADC MIX", NULL, "Mono ADC MIXR" },
+
+       { "VAD ADC Mux", "STO1 ADC MIX L", "Stereo1 ADC MIXL" },
+       { "VAD ADC Mux", "MONO ADC MIX L", "Mono ADC MIXL" },
+       { "VAD ADC Mux", "MONO ADC MIX R", "Mono ADC MIXR" },
+       { "VAD ADC Mux", "STO2 ADC MIX L", "Stereo2 ADC MIXL" },
+       { "VAD ADC Mux", "STO3 ADC MIX L", "Stereo3 ADC MIXL" },
+
+       { "IF1 ADC1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
+       { "IF1 ADC1 Mux", "OB01", "OB01 Bypass Mux" },
+       { "IF1 ADC1 Mux", "VAD ADC", "VAD ADC Mux" },
+
+       { "IF1 ADC2 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+       { "IF1 ADC2 Mux", "OB23", "OB23 Bypass Mux" },
+
+       { "IF1 ADC3 Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" },
+       { "IF1 ADC3 Mux", "MONO ADC MIX", "Mono ADC MIX" },
+       { "IF1 ADC3 Mux", "OB45", "OB45" },
+
+       { "IF1 ADC4 Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" },
+       { "IF1 ADC4 Mux", "OB67", "OB67" },
+       { "IF1 ADC4 Mux", "OB01", "OB01 Bypass Mux" },
+
+       { "AIF1TX", NULL, "I2S1" },
+       { "AIF1TX", NULL, "IF1 ADC1 Mux" },
+       { "AIF1TX", NULL, "IF1 ADC2 Mux" },
+       { "AIF1TX", NULL, "IF1 ADC3 Mux" },
+       { "AIF1TX", NULL, "IF1 ADC4 Mux" },
+
+       { "IF2 ADC1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
+       { "IF2 ADC1 Mux", "OB01", "OB01 Bypass Mux" },
+       { "IF2 ADC1 Mux", "VAD ADC", "VAD ADC Mux" },
+
+       { "IF2 ADC2 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+       { "IF2 ADC2 Mux", "OB23", "OB23 Bypass Mux" },
+
+       { "IF2 ADC3 Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" },
+       { "IF2 ADC3 Mux", "MONO ADC MIX", "Mono ADC MIX" },
+       { "IF2 ADC3 Mux", "OB45", "OB45" },
+
+       { "IF2 ADC4 Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" },
+       { "IF2 ADC4 Mux", "OB67", "OB67" },
+       { "IF2 ADC4 Mux", "OB01", "OB01 Bypass Mux" },
+
+       { "AIF2TX", NULL, "I2S2" },
+       { "AIF2TX", NULL, "IF2 ADC1 Mux" },
+       { "AIF2TX", NULL, "IF2 ADC2 Mux" },
+       { "AIF2TX", NULL, "IF2 ADC3 Mux" },
+       { "AIF2TX", NULL, "IF2 ADC4 Mux" },
+
+       { "IF3 ADC Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
+       { "IF3 ADC Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+       { "IF3 ADC Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" },
+       { "IF3 ADC Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" },
+       { "IF3 ADC Mux", "MONO ADC MIX", "Mono ADC MIX" },
+       { "IF3 ADC Mux", "OB01", "OB01 Bypass Mux" },
+       { "IF3 ADC Mux", "OB23", "OB23 Bypass Mux" },
+       { "IF3 ADC Mux", "VAD ADC", "VAD ADC Mux" },
+
+       { "AIF3TX", NULL, "I2S3" },
+       { "AIF3TX", NULL, "IF3 ADC Mux" },
+
+       { "IF4 ADC Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
+       { "IF4 ADC Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+       { "IF4 ADC Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" },
+       { "IF4 ADC Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" },
+       { "IF4 ADC Mux", "MONO ADC MIX", "Mono ADC MIX" },
+       { "IF4 ADC Mux", "OB01", "OB01 Bypass Mux" },
+       { "IF4 ADC Mux", "OB23", "OB23 Bypass Mux" },
+       { "IF4 ADC Mux", "VAD ADC", "VAD ADC Mux" },
+
+       { "AIF4TX", NULL, "I2S4" },
+       { "AIF4TX", NULL, "IF4 ADC Mux" },
+
+       { "SLB ADC1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
+       { "SLB ADC1 Mux", "OB01", "OB01 Bypass Mux" },
+       { "SLB ADC1 Mux", "VAD ADC", "VAD ADC Mux" },
+
+       { "SLB ADC2 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+       { "SLB ADC2 Mux", "OB23", "OB23 Bypass Mux" },
+
+       { "SLB ADC3 Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" },
+       { "SLB ADC3 Mux", "MONO ADC MIX", "Mono ADC MIX" },
+       { "SLB ADC3 Mux", "OB45", "OB45" },
+
+       { "SLB ADC4 Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" },
+       { "SLB ADC4 Mux", "OB67", "OB67" },
+       { "SLB ADC4 Mux", "OB01", "OB01 Bypass Mux" },
+
+       { "SLBTX", NULL, "SLB" },
+       { "SLBTX", NULL, "SLB ADC1 Mux" },
+       { "SLBTX", NULL, "SLB ADC2 Mux" },
+       { "SLBTX", NULL, "SLB ADC3 Mux" },
+       { "SLBTX", NULL, "SLB ADC4 Mux" },
+
+       { "IB01 Mux", "IF1 DAC 01", "IF1 DAC01" },
+       { "IB01 Mux", "IF2 DAC 01", "IF2 DAC01" },
+       { "IB01 Mux", "SLB DAC 01", "SLB DAC01" },
+       { "IB01 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
+       { "IB01 Mux", "VAD ADC/DAC1 FS", "DAC1 FS" },
+
+       { "IB01 Bypass Mux", "Bypass", "IB01 Mux" },
+       { "IB01 Bypass Mux", "Pass SRC", "IB01 Mux" },
+
+       { "IB23 Mux", "IF1 DAC 23", "IF1 DAC23" },
+       { "IB23 Mux", "IF2 DAC 23", "IF2 DAC23" },
+       { "IB23 Mux", "SLB DAC 23", "SLB DAC23" },
+       { "IB23 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+       { "IB23 Mux", "DAC1 FS", "DAC1 FS" },
+       { "IB23 Mux", "IF4 DAC", "IF4 DAC" },
+
+       { "IB23 Bypass Mux", "Bypass", "IB23 Mux" },
+       { "IB23 Bypass Mux", "Pass SRC", "IB23 Mux" },
+
+       { "IB45 Mux", "IF1 DAC 45", "IF1 DAC45" },
+       { "IB45 Mux", "IF2 DAC 45", "IF2 DAC45" },
+       { "IB45 Mux", "SLB DAC 45", "SLB DAC45" },
+       { "IB45 Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" },
+       { "IB45 Mux", "IF3 DAC", "IF3 DAC" },
+
+       { "IB45 Bypass Mux", "Bypass", "IB45 Mux" },
+       { "IB45 Bypass Mux", "Pass SRC", "IB45 Mux" },
+
+       { "IB6 Mux", "IF1 DAC 6", "IF1 DAC6" },
+       { "IB6 Mux", "IF2 DAC 6", "IF2 DAC6" },
+       { "IB6 Mux", "SLB DAC 6", "SLB DAC6" },
+       { "IB6 Mux", "STO4 ADC MIX L", "Stereo4 ADC MIXL" },
+       { "IB6 Mux", "IF4 DAC L", "IF4 DAC L" },
+       { "IB6 Mux", "STO1 ADC MIX L", "Stereo1 ADC MIXL" },
+       { "IB6 Mux", "STO2 ADC MIX L", "Stereo2 ADC MIXL" },
+       { "IB6 Mux", "STO3 ADC MIX L", "Stereo3 ADC MIXL" },
+
+       { "IB7 Mux", "IF1 DAC 7", "IF1 DAC7" },
+       { "IB7 Mux", "IF2 DAC 7", "IF2 DAC7" },
+       { "IB7 Mux", "SLB DAC 7", "SLB DAC7" },
+       { "IB7 Mux", "STO4 ADC MIX R", "Stereo4 ADC MIXR" },
+       { "IB7 Mux", "IF4 DAC R", "IF4 DAC R" },
+       { "IB7 Mux", "STO1 ADC MIX R", "Stereo1 ADC MIXR" },
+       { "IB7 Mux", "STO2 ADC MIX R", "Stereo2 ADC MIXR" },
+       { "IB7 Mux", "STO3 ADC MIX R", "Stereo3 ADC MIXR" },
+
+       { "IB8 Mux", "STO1 ADC MIX L", "Stereo1 ADC MIXL" },
+       { "IB8 Mux", "STO2 ADC MIX L", "Stereo2 ADC MIXL" },
+       { "IB8 Mux", "STO3 ADC MIX L", "Stereo3 ADC MIXL" },
+       { "IB8 Mux", "STO4 ADC MIX L", "Stereo4 ADC MIXL" },
+       { "IB8 Mux", "MONO ADC MIX L", "Mono ADC MIXL" },
+       { "IB8 Mux", "DACL1 FS", "DAC1 MIXL" },
+
+       { "IB9 Mux", "STO1 ADC MIX R", "Stereo1 ADC MIXR" },
+       { "IB9 Mux", "STO2 ADC MIX R", "Stereo2 ADC MIXR" },
+       { "IB9 Mux", "STO3 ADC MIX R", "Stereo3 ADC MIXR" },
+       { "IB9 Mux", "STO4 ADC MIX R", "Stereo4 ADC MIXR" },
+       { "IB9 Mux", "MONO ADC MIX R", "Mono ADC MIXR" },
+       { "IB9 Mux", "DACR1 FS", "DAC1 MIXR" },
+       { "IB9 Mux", "DAC1 FS", "DAC1 FS" },
+
+       { "OB01 MIX", "IB01 Switch", "IB01 Bypass Mux" },
+       { "OB01 MIX", "IB23 Switch", "IB23 Bypass Mux" },
+       { "OB01 MIX", "IB45 Switch", "IB45 Bypass Mux" },
+       { "OB01 MIX", "IB6 Switch", "IB6 Mux" },
+       { "OB01 MIX", "IB7 Switch", "IB7 Mux" },
+       { "OB01 MIX", "IB8 Switch", "IB8 Mux" },
+       { "OB01 MIX", "IB9 Switch", "IB9 Mux" },
+
+       { "OB23 MIX", "IB01 Switch", "IB01 Bypass Mux" },
+       { "OB23 MIX", "IB23 Switch", "IB23 Bypass Mux" },
+       { "OB23 MIX", "IB45 Switch", "IB45 Bypass Mux" },
+       { "OB23 MIX", "IB6 Switch", "IB6 Mux" },
+       { "OB23 MIX", "IB7 Switch", "IB7 Mux" },
+       { "OB23 MIX", "IB8 Switch", "IB8 Mux" },
+       { "OB23 MIX", "IB9 Switch", "IB9 Mux" },
+
+       { "OB4 MIX", "IB01 Switch", "IB01 Bypass Mux" },
+       { "OB4 MIX", "IB23 Switch", "IB23 Bypass Mux" },
+       { "OB4 MIX", "IB45 Switch", "IB45 Bypass Mux" },
+       { "OB4 MIX", "IB6 Switch", "IB6 Mux" },
+       { "OB4 MIX", "IB7 Switch", "IB7 Mux" },
+       { "OB4 MIX", "IB8 Switch", "IB8 Mux" },
+       { "OB4 MIX", "IB9 Switch", "IB9 Mux" },
+
+       { "OB5 MIX", "IB01 Switch", "IB01 Bypass Mux" },
+       { "OB5 MIX", "IB23 Switch", "IB23 Bypass Mux" },
+       { "OB5 MIX", "IB45 Switch", "IB45 Bypass Mux" },
+       { "OB5 MIX", "IB6 Switch", "IB6 Mux" },
+       { "OB5 MIX", "IB7 Switch", "IB7 Mux" },
+       { "OB5 MIX", "IB8 Switch", "IB8 Mux" },
+       { "OB5 MIX", "IB9 Switch", "IB9 Mux" },
+
+       { "OB6 MIX", "IB01 Switch", "IB01 Bypass Mux" },
+       { "OB6 MIX", "IB23 Switch", "IB23 Bypass Mux" },
+       { "OB6 MIX", "IB45 Switch", "IB45 Bypass Mux" },
+       { "OB6 MIX", "IB6 Switch", "IB6 Mux" },
+       { "OB6 MIX", "IB7 Switch", "IB7 Mux" },
+       { "OB6 MIX", "IB8 Switch", "IB8 Mux" },
+       { "OB6 MIX", "IB9 Switch", "IB9 Mux" },
+
+       { "OB7 MIX", "IB01 Switch", "IB01 Bypass Mux" },
+       { "OB7 MIX", "IB23 Switch", "IB23 Bypass Mux" },
+       { "OB7 MIX", "IB45 Switch", "IB45 Bypass Mux" },
+       { "OB7 MIX", "IB6 Switch", "IB6 Mux" },
+       { "OB7 MIX", "IB7 Switch", "IB7 Mux" },
+       { "OB7 MIX", "IB8 Switch", "IB8 Mux" },
+       { "OB7 MIX", "IB9 Switch", "IB9 Mux" },
+
+       { "OB01 Bypass Mux", "Bypass", "OB01 MIX" },
+       { "OB01 Bypass Mux", "Pass SRC", "OB01 MIX" },
+       { "OB23 Bypass Mux", "Bypass", "OB23 MIX" },
+       { "OB23 Bypass Mux", "Pass SRC", "OB23 MIX" },
+
+       { "OutBound2", NULL, "OB23 Bypass Mux" },
+       { "OutBound3", NULL, "OB23 Bypass Mux" },
+       { "OutBound4", NULL, "OB4 MIX" },
+       { "OutBound5", NULL, "OB5 MIX" },
+       { "OutBound6", NULL, "OB6 MIX" },
+       { "OutBound7", NULL, "OB7 MIX" },
+
+       { "OB45", NULL, "OutBound4" },
+       { "OB45", NULL, "OutBound5" },
+       { "OB67", NULL, "OutBound6" },
+       { "OB67", NULL, "OutBound7" },
+
+       { "IF1 DAC0", NULL, "AIF1RX" },
+       { "IF1 DAC1", NULL, "AIF1RX" },
+       { "IF1 DAC2", NULL, "AIF1RX" },
+       { "IF1 DAC3", NULL, "AIF1RX" },
+       { "IF1 DAC4", NULL, "AIF1RX" },
+       { "IF1 DAC5", NULL, "AIF1RX" },
+       { "IF1 DAC6", NULL, "AIF1RX" },
+       { "IF1 DAC7", NULL, "AIF1RX" },
+       { "IF1 DAC0", NULL, "I2S1" },
+       { "IF1 DAC1", NULL, "I2S1" },
+       { "IF1 DAC2", NULL, "I2S1" },
+       { "IF1 DAC3", NULL, "I2S1" },
+       { "IF1 DAC4", NULL, "I2S1" },
+       { "IF1 DAC5", NULL, "I2S1" },
+       { "IF1 DAC6", NULL, "I2S1" },
+       { "IF1 DAC7", NULL, "I2S1" },
+
+       { "IF1 DAC01", NULL, "IF1 DAC0" },
+       { "IF1 DAC01", NULL, "IF1 DAC1" },
+       { "IF1 DAC23", NULL, "IF1 DAC2" },
+       { "IF1 DAC23", NULL, "IF1 DAC3" },
+       { "IF1 DAC45", NULL, "IF1 DAC4" },
+       { "IF1 DAC45", NULL, "IF1 DAC5" },
+       { "IF1 DAC67", NULL, "IF1 DAC6" },
+       { "IF1 DAC67", NULL, "IF1 DAC7" },
+
+       { "IF2 DAC0", NULL, "AIF2RX" },
+       { "IF2 DAC1", NULL, "AIF2RX" },
+       { "IF2 DAC2", NULL, "AIF2RX" },
+       { "IF2 DAC3", NULL, "AIF2RX" },
+       { "IF2 DAC4", NULL, "AIF2RX" },
+       { "IF2 DAC5", NULL, "AIF2RX" },
+       { "IF2 DAC6", NULL, "AIF2RX" },
+       { "IF2 DAC7", NULL, "AIF2RX" },
+       { "IF2 DAC0", NULL, "I2S2" },
+       { "IF2 DAC1", NULL, "I2S2" },
+       { "IF2 DAC2", NULL, "I2S2" },
+       { "IF2 DAC3", NULL, "I2S2" },
+       { "IF2 DAC4", NULL, "I2S2" },
+       { "IF2 DAC5", NULL, "I2S2" },
+       { "IF2 DAC6", NULL, "I2S2" },
+       { "IF2 DAC7", NULL, "I2S2" },
+
+       { "IF2 DAC01", NULL, "IF2 DAC0" },
+       { "IF2 DAC01", NULL, "IF2 DAC1" },
+       { "IF2 DAC23", NULL, "IF2 DAC2" },
+       { "IF2 DAC23", NULL, "IF2 DAC3" },
+       { "IF2 DAC45", NULL, "IF2 DAC4" },
+       { "IF2 DAC45", NULL, "IF2 DAC5" },
+       { "IF2 DAC67", NULL, "IF2 DAC6" },
+       { "IF2 DAC67", NULL, "IF2 DAC7" },
+
+       { "IF3 DAC", NULL, "AIF3RX" },
+       { "IF3 DAC", NULL, "I2S3" },
+
+       { "IF4 DAC", NULL, "AIF4RX" },
+       { "IF4 DAC", NULL, "I2S4" },
+
+       { "IF3 DAC L", NULL, "IF3 DAC" },
+       { "IF3 DAC R", NULL, "IF3 DAC" },
+
+       { "IF4 DAC L", NULL, "IF4 DAC" },
+       { "IF4 DAC R", NULL, "IF4 DAC" },
+
+       { "SLB DAC0", NULL, "SLBRX" },
+       { "SLB DAC1", NULL, "SLBRX" },
+       { "SLB DAC2", NULL, "SLBRX" },
+       { "SLB DAC3", NULL, "SLBRX" },
+       { "SLB DAC4", NULL, "SLBRX" },
+       { "SLB DAC5", NULL, "SLBRX" },
+       { "SLB DAC6", NULL, "SLBRX" },
+       { "SLB DAC7", NULL, "SLBRX" },
+       { "SLB DAC0", NULL, "SLB" },
+       { "SLB DAC1", NULL, "SLB" },
+       { "SLB DAC2", NULL, "SLB" },
+       { "SLB DAC3", NULL, "SLB" },
+       { "SLB DAC4", NULL, "SLB" },
+       { "SLB DAC5", NULL, "SLB" },
+       { "SLB DAC6", NULL, "SLB" },
+       { "SLB DAC7", NULL, "SLB" },
+
+       { "SLB DAC01", NULL, "SLB DAC0" },
+       { "SLB DAC01", NULL, "SLB DAC1" },
+       { "SLB DAC23", NULL, "SLB DAC2" },
+       { "SLB DAC23", NULL, "SLB DAC3" },
+       { "SLB DAC45", NULL, "SLB DAC4" },
+       { "SLB DAC45", NULL, "SLB DAC5" },
+       { "SLB DAC67", NULL, "SLB DAC6" },
+       { "SLB DAC67", NULL, "SLB DAC7" },
+
+       { "ADDA1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
+       { "ADDA1 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
+       { "ADDA1 Mux", "OB 67", "OB67" },
+
+       { "DAC1 Mux", "IF1 DAC 01", "IF1 DAC01" },
+       { "DAC1 Mux", "IF2 DAC 01", "IF2 DAC01" },
+       { "DAC1 Mux", "IF3 DAC LR", "IF3 DAC" },
+       { "DAC1 Mux", "IF4 DAC LR", "IF4 DAC" },
+       { "DAC1 Mux", "SLB DAC 01", "SLB DAC01" },
+       { "DAC1 Mux", "OB 01", "OB01 Bypass Mux" },
+
+       { "DAC1 MIXL", "Stereo ADC Switch", "ADDA1 Mux" },
+       { "DAC1 MIXL", "DAC1 Switch", "DAC1 Mux" },
+       { "DAC1 MIXL", NULL, "dac stereo1 filter" },
+       { "DAC1 MIXR", "Stereo ADC Switch", "ADDA1 Mux" },
+       { "DAC1 MIXR", "DAC1 Switch", "DAC1 Mux" },
+       { "DAC1 MIXR", NULL, "dac stereo1 filter" },
+
+       { "DAC1 FS", NULL, "DAC1 MIXL" },
+       { "DAC1 FS", NULL, "DAC1 MIXR" },
+
+       { "DAC2 L Mux", "IF1 DAC 2", "IF1 DAC2" },
+       { "DAC2 L Mux", "IF2 DAC 2", "IF2 DAC2" },
+       { "DAC2 L Mux", "IF3 DAC L", "IF3 DAC L" },
+       { "DAC2 L Mux", "IF4 DAC L", "IF4 DAC L" },
+       { "DAC2 L Mux", "SLB DAC 2", "SLB DAC2" },
+       { "DAC2 L Mux", "OB 2", "OutBound2" },
+
+       { "DAC2 R Mux", "IF1 DAC 3", "IF1 DAC3" },
+       { "DAC2 R Mux", "IF2 DAC 3", "IF2 DAC3" },
+       { "DAC2 R Mux", "IF3 DAC R", "IF3 DAC R" },
+       { "DAC2 R Mux", "IF4 DAC R", "IF4 DAC R" },
+       { "DAC2 R Mux", "SLB DAC 3", "SLB DAC3" },
+       { "DAC2 R Mux", "OB 3", "OutBound3" },
+       { "DAC2 R Mux", "Haptic Generator", "Haptic Generator" },
+       { "DAC2 R Mux", "VAD ADC", "VAD ADC Mux" },
+
+       { "DAC3 L Mux", "IF1 DAC 4", "IF1 DAC4" },
+       { "DAC3 L Mux", "IF2 DAC 4", "IF2 DAC4" },
+       { "DAC3 L Mux", "IF3 DAC L", "IF3 DAC L" },
+       { "DAC3 L Mux", "IF4 DAC L", "IF4 DAC L" },
+       { "DAC3 L Mux", "SLB DAC 4", "SLB DAC4" },
+       { "DAC3 L Mux", "OB 4", "OutBound4" },
+
+       { "DAC3 R Mux", "IF1 DAC 5", "IF1 DAC4" },
+       { "DAC3 R Mux", "IF2 DAC 5", "IF2 DAC4" },
+       { "DAC3 R Mux", "IF3 DAC R", "IF3 DAC R" },
+       { "DAC3 R Mux", "IF4 DAC R", "IF4 DAC R" },
+       { "DAC3 R Mux", "SLB DAC 5", "SLB DAC5" },
+       { "DAC3 R Mux", "OB 5", "OutBound5" },
+
+       { "DAC4 L Mux", "IF1 DAC 6", "IF1 DAC6" },
+       { "DAC4 L Mux", "IF2 DAC 6", "IF2 DAC6" },
+       { "DAC4 L Mux", "IF3 DAC L", "IF3 DAC L" },
+       { "DAC4 L Mux", "IF4 DAC L", "IF4 DAC L" },
+       { "DAC4 L Mux", "SLB DAC 6", "SLB DAC6" },
+       { "DAC4 L Mux", "OB 6", "OutBound6" },
+
+       { "DAC4 R Mux", "IF1 DAC 7", "IF1 DAC7" },
+       { "DAC4 R Mux", "IF2 DAC 7", "IF2 DAC7" },
+       { "DAC4 R Mux", "IF3 DAC R", "IF3 DAC R" },
+       { "DAC4 R Mux", "IF4 DAC R", "IF4 DAC R" },
+       { "DAC4 R Mux", "SLB DAC 7", "SLB DAC7" },
+       { "DAC4 R Mux", "OB 7", "OutBound7" },
+
+       { "Sidetone Mux", "DMIC1 L", "DMIC L1" },
+       { "Sidetone Mux", "DMIC2 L", "DMIC L2" },
+       { "Sidetone Mux", "DMIC3 L", "DMIC L3" },
+       { "Sidetone Mux", "DMIC4 L", "DMIC L4" },
+       { "Sidetone Mux", "ADC1", "ADC 1" },
+       { "Sidetone Mux", "ADC2", "ADC 2" },
+
+       { "Stereo DAC MIXL", "ST L Switch", "Sidetone Mux" },
+       { "Stereo DAC MIXL", "DAC1 L Switch", "DAC1 MIXL" },
+       { "Stereo DAC MIXL", "DAC2 L Switch", "DAC2 L Mux" },
+       { "Stereo DAC MIXL", "DAC1 R Switch", "DAC1 MIXR" },
+       { "Stereo DAC MIXL", NULL, "dac stereo1 filter" },
+       { "Stereo DAC MIXR", "ST R Switch", "Sidetone Mux" },
+       { "Stereo DAC MIXR", "DAC1 R Switch", "DAC1 MIXR" },
+       { "Stereo DAC MIXR", "DAC2 R Switch", "DAC2 R Mux" },
+       { "Stereo DAC MIXR", "DAC1 L Switch", "DAC1 MIXL" },
+       { "Stereo DAC MIXR", NULL, "dac stereo1 filter" },
+
+       { "Mono DAC MIXL", "ST L Switch", "Sidetone Mux" },
+       { "Mono DAC MIXL", "DAC1 L Switch", "DAC1 MIXL" },
+       { "Mono DAC MIXL", "DAC2 L Switch", "DAC2 L Mux" },
+       { "Mono DAC MIXL", "DAC2 R Switch", "DAC2 R Mux" },
+       { "Mono DAC MIXL", NULL, "dac mono left filter" },
+       { "Mono DAC MIXR", "ST R Switch", "Sidetone Mux" },
+       { "Mono DAC MIXR", "DAC1 R Switch", "DAC1 MIXR" },
+       { "Mono DAC MIXR", "DAC2 R Switch", "DAC2 R Mux" },
+       { "Mono DAC MIXR", "DAC2 L Switch", "DAC2 L Mux" },
+       { "Mono DAC MIXR", NULL, "dac mono right filter" },
+
+       { "DD1 MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" },
+       { "DD1 MIXL", "Mono DAC Mix L Switch", "Mono DAC MIXL" },
+       { "DD1 MIXL", "DAC3 L Switch", "DAC3 L Mux" },
+       { "DD1 MIXL", "DAC3 R Switch", "DAC3 R Mux" },
+       { "DD1 MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" },
+       { "DD1 MIXR", "Mono DAC Mix R Switch", "Mono DAC MIXR" },
+       { "DD1 MIXR", "DAC3 L Switch", "DAC3 L Mux" },
+       { "DD1 MIXR", "DAC3 R Switch", "DAC3 R Mux" },
+
+       { "DD2 MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" },
+       { "DD2 MIXL", "Mono DAC Mix L Switch", "Mono DAC MIXL" },
+       { "DD2 MIXL", "DAC4 L Switch", "DAC4 L Mux" },
+       { "DD2 MIXL", "DAC4 R Switch", "DAC4 R Mux" },
+       { "DD2 MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" },
+       { "DD2 MIXR", "Mono DAC Mix R Switch", "Mono DAC MIXR" },
+       { "DD2 MIXR", "DAC4 L Switch", "DAC4 L Mux" },
+       { "DD2 MIXR", "DAC4 R Switch", "DAC4 R Mux" },
+
+       { "Stereo DAC MIX", NULL, "Stereo DAC MIXL" },
+       { "Stereo DAC MIX", NULL, "Stereo DAC MIXR" },
+       { "Mono DAC MIX", NULL, "Mono DAC MIXL" },
+       { "Mono DAC MIX", NULL, "Mono DAC MIXR" },
+       { "DD1 MIX", NULL, "DD1 MIXL" },
+       { "DD1 MIX", NULL, "DD1 MIXR" },
+       { "DD2 MIX", NULL, "DD2 MIXL" },
+       { "DD2 MIX", NULL, "DD2 MIXR" },
+
+       { "DAC12 SRC Mux", "STO1 DAC MIX", "Stereo DAC MIX" },
+       { "DAC12 SRC Mux", "MONO DAC MIX", "Mono DAC MIX" },
+       { "DAC12 SRC Mux", "DD MIX1", "DD1 MIX" },
+       { "DAC12 SRC Mux", "DD MIX2", "DD2 MIX" },
+
+       { "DAC3 SRC Mux", "MONO DAC MIXL", "Mono DAC MIXL" },
+       { "DAC3 SRC Mux", "MONO DAC MIXR", "Mono DAC MIXR" },
+       { "DAC3 SRC Mux", "DD MIX1L", "DD1 MIXL" },
+       { "DAC3 SRC Mux", "DD MIX2L", "DD2 MIXL" },
+
+       { "DAC 1", NULL, "DAC12 SRC Mux" },
+       { "DAC 1", NULL, "PLL1", is_sys_clk_from_pll },
+       { "DAC 2", NULL, "DAC12 SRC Mux" },
+       { "DAC 2", NULL, "PLL1", is_sys_clk_from_pll },
+       { "DAC 3", NULL, "DAC3 SRC Mux" },
+       { "DAC 3", NULL, "PLL1", is_sys_clk_from_pll },
+
+       { "PDM1 L Mux", "STO1 DAC MIX", "Stereo DAC MIXL" },
+       { "PDM1 L Mux", "MONO DAC MIX", "Mono DAC MIXL" },
+       { "PDM1 L Mux", "DD MIX1", "DD1 MIXL" },
+       { "PDM1 L Mux", "DD MIX2", "DD2 MIXL" },
+       { "PDM1 L Mux", NULL, "PDM1 Power" },
+       { "PDM1 R Mux", "STO1 DAC MIX", "Stereo DAC MIXR" },
+       { "PDM1 R Mux", "MONO DAC MIX", "Mono DAC MIXR" },
+       { "PDM1 R Mux", "DD MIX1", "DD1 MIXR" },
+       { "PDM1 R Mux", "DD MIX2", "DD2 MIXR" },
+       { "PDM1 R Mux", NULL, "PDM1 Power" },
+       { "PDM2 L Mux", "STO1 DAC MIX", "Stereo DAC MIXL" },
+       { "PDM2 L Mux", "MONO DAC MIX", "Mono DAC MIXL" },
+       { "PDM2 L Mux", "DD MIX1", "DD1 MIXL" },
+       { "PDM2 L Mux", "DD MIX2", "DD2 MIXL" },
+       { "PDM2 L Mux", NULL, "PDM2 Power" },
+       { "PDM2 R Mux", "STO1 DAC MIX", "Stereo DAC MIXR" },
+       { "PDM2 R Mux", "MONO DAC MIX", "Mono DAC MIXR" },
+       { "PDM2 R Mux", "DD MIX1", "DD1 MIXR" },
+       { "PDM2 R Mux", "DD MIX1", "DD2 MIXR" },
+       { "PDM2 R Mux", NULL, "PDM2 Power" },
+
+       { "LOUT1 amp", NULL, "DAC 1" },
+       { "LOUT2 amp", NULL, "DAC 2" },
+       { "LOUT3 amp", NULL, "DAC 3" },
+
+       { "LOUT1", NULL, "LOUT1 amp" },
+       { "LOUT2", NULL, "LOUT2 amp" },
+       { "LOUT3", NULL, "LOUT3 amp" },
+
+       { "PDM1L", NULL, "PDM1 L Mux" },
+       { "PDM1R", NULL, "PDM1 R Mux" },
+       { "PDM2L", NULL, "PDM2 L Mux" },
+       { "PDM2R", NULL, "PDM2 R Mux" },
+};
+
+static int get_clk_info(int sclk, int rate)
+{
+       int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+       if (sclk <= 0 || rate <= 0)
+               return -EINVAL;
+
+       rate = rate << 8;
+       for (i = 0; i < ARRAY_SIZE(pd); i++)
+               if (sclk == rate * pd[i])
+                       return i;
+
+       return -EINVAL;
+}
+
+static int rt5677_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 rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val_len = 0, val_clk, mask_clk;
+       int pre_div, bclk_ms, frame_size;
+
+       rt5677->lrck[dai->id] = params_rate(params);
+       pre_div = get_clk_info(rt5677->sysclk, rt5677->lrck[dai->id]);
+       if (pre_div < 0) {
+               dev_err(codec->dev, "Unsupported clock setting\n");
+               return -EINVAL;
+       }
+       frame_size = snd_soc_params_to_frame_size(params);
+       if (frame_size < 0) {
+               dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+               return -EINVAL;
+       }
+       bclk_ms = frame_size > 32;
+       rt5677->bclk[dai->id] = rt5677->lrck[dai->id] * (32 << bclk_ms);
+
+       dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+               rt5677->bclk[dai->id], rt5677->lrck[dai->id]);
+       dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+                               bclk_ms, pre_div, dai->id);
+
+       switch (params_width(params)) {
+       case 16:
+               break;
+       case 20:
+               val_len |= RT5677_I2S_DL_20;
+               break;
+       case 24:
+               val_len |= RT5677_I2S_DL_24;
+               break;
+       case 8:
+               val_len |= RT5677_I2S_DL_8;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (dai->id) {
+       case RT5677_AIF1:
+               mask_clk = RT5677_I2S_PD1_MASK;
+               val_clk = pre_div << RT5677_I2S_PD1_SFT;
+               regmap_update_bits(rt5677->regmap, RT5677_I2S1_SDP,
+                       RT5677_I2S_DL_MASK, val_len);
+               regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1,
+                       mask_clk, val_clk);
+               break;
+       case RT5677_AIF2:
+               mask_clk = RT5677_I2S_PD2_MASK;
+               val_clk = pre_div << RT5677_I2S_PD2_SFT;
+               regmap_update_bits(rt5677->regmap, RT5677_I2S2_SDP,
+                       RT5677_I2S_DL_MASK, val_len);
+               regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1,
+                       mask_clk, val_clk);
+               break;
+       case RT5677_AIF3:
+               mask_clk = RT5677_I2S_BCLK_MS3_MASK | RT5677_I2S_PD3_MASK;
+               val_clk = bclk_ms << RT5677_I2S_BCLK_MS3_SFT |
+                       pre_div << RT5677_I2S_PD3_SFT;
+               regmap_update_bits(rt5677->regmap, RT5677_I2S3_SDP,
+                       RT5677_I2S_DL_MASK, val_len);
+               regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1,
+                       mask_clk, val_clk);
+               break;
+       case RT5677_AIF4:
+               mask_clk = RT5677_I2S_BCLK_MS4_MASK | RT5677_I2S_PD4_MASK;
+               val_clk = bclk_ms << RT5677_I2S_BCLK_MS4_SFT |
+                       pre_div << RT5677_I2S_PD4_SFT;
+               regmap_update_bits(rt5677->regmap, RT5677_I2S4_SDP,
+                       RT5677_I2S_DL_MASK, val_len);
+               regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1,
+                       mask_clk, val_clk);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int rt5677_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+       unsigned int reg_val = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               rt5677->master[dai->id] = 1;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               reg_val |= RT5677_I2S_MS_S;
+               rt5677->master[dai->id] = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               reg_val |= RT5677_I2S_BP_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               reg_val |= RT5677_I2S_DF_LEFT;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               reg_val |= RT5677_I2S_DF_PCM_A;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               reg_val |= RT5677_I2S_DF_PCM_B;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (dai->id) {
+       case RT5677_AIF1:
+               regmap_update_bits(rt5677->regmap, RT5677_I2S1_SDP,
+                       RT5677_I2S_MS_MASK | RT5677_I2S_BP_MASK |
+                       RT5677_I2S_DF_MASK, reg_val);
+               break;
+       case RT5677_AIF2:
+               regmap_update_bits(rt5677->regmap, RT5677_I2S2_SDP,
+                       RT5677_I2S_MS_MASK | RT5677_I2S_BP_MASK |
+                       RT5677_I2S_DF_MASK, reg_val);
+               break;
+       case RT5677_AIF3:
+               regmap_update_bits(rt5677->regmap, RT5677_I2S3_SDP,
+                       RT5677_I2S_MS_MASK | RT5677_I2S_BP_MASK |
+                       RT5677_I2S_DF_MASK, reg_val);
+               break;
+       case RT5677_AIF4:
+               regmap_update_bits(rt5677->regmap, RT5677_I2S4_SDP,
+                       RT5677_I2S_MS_MASK | RT5677_I2S_BP_MASK |
+                       RT5677_I2S_DF_MASK, reg_val);
+               break;
+       default:
+               break;
+       }
+
+
+       return 0;
+}
+
+static int rt5677_set_dai_sysclk(struct snd_soc_dai *dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+       unsigned int reg_val = 0;
+
+       if (freq == rt5677->sysclk && clk_id == rt5677->sysclk_src)
+               return 0;
+
+       switch (clk_id) {
+       case RT5677_SCLK_S_MCLK:
+               reg_val |= RT5677_SCLK_SRC_MCLK;
+               break;
+       case RT5677_SCLK_S_PLL1:
+               reg_val |= RT5677_SCLK_SRC_PLL1;
+               break;
+       case RT5677_SCLK_S_RCCLK:
+               reg_val |= RT5677_SCLK_SRC_RCCLK;
+               break;
+       default:
+               dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+               return -EINVAL;
+       }
+       regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+               RT5677_SCLK_SRC_MASK, reg_val);
+       rt5677->sysclk = freq;
+       rt5677->sysclk_src = clk_id;
+
+       dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+       return 0;
+}
+
+/**
+ * rt5677_pll_calc - Calcualte PLL M/N/K code.
+ * @freq_in: external clock provided to codec.
+ * @freq_out: target clock which codec works on.
+ * @pll_code: Pointer to structure with M, N, K, bypass K and bypass M flag.
+ *
+ * Calcualte M/N/K code and bypass K/M flag to configure PLL for codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_pll_calc(const unsigned int freq_in,
+       const unsigned int freq_out, struct rt5677_pll_code *pll_code)
+{
+       int max_n = RT5677_PLL_N_MAX, max_m = RT5677_PLL_M_MAX;
+       int k, red, n_t, pll_out, in_t;
+       int n = 0, m = 0, m_t = 0;
+       int out_t, red_t = abs(freq_out - freq_in);
+       bool m_bp = false, k_bp = false;
+
+       if (RT5677_PLL_INP_MAX < freq_in || RT5677_PLL_INP_MIN > freq_in)
+               return -EINVAL;
+
+       k = 100000000 / freq_out - 2;
+       if (k > RT5677_PLL_K_MAX)
+               k = RT5677_PLL_K_MAX;
+       for (n_t = 0; n_t <= max_n; n_t++) {
+               in_t = freq_in / (k + 2);
+               pll_out = freq_out / (n_t + 2);
+               if (in_t < 0)
+                       continue;
+               if (in_t == pll_out) {
+                       m_bp = true;
+                       n = n_t;
+                       goto code_find;
+               }
+               red = abs(in_t - pll_out);
+               if (red < red_t) {
+                       m_bp = true;
+                       n = n_t;
+                       m = m_t;
+                       if (red == 0)
+                               goto code_find;
+                       red_t = red;
+               }
+               for (m_t = 0; m_t <= max_m; m_t++) {
+                       out_t = in_t / (m_t + 2);
+                       red = abs(out_t - pll_out);
+                       if (red < red_t) {
+                               m_bp = false;
+                               n = n_t;
+                               m = m_t;
+                               if (red == 0)
+                                       goto code_find;
+                               red_t = red;
+                       }
+               }
+       }
+       pr_debug("Only get approximation about PLL\n");
+
+code_find:
+
+       pll_code->m_bp = m_bp;
+       pll_code->k_bp = k_bp;
+       pll_code->m_code = m;
+       pll_code->n_code = n;
+       pll_code->k_code = k;
+       return 0;
+}
+
+static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+                       unsigned int freq_in, unsigned int freq_out)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+       struct rt5677_pll_code pll_code;
+       int ret;
+
+       if (source == rt5677->pll_src && freq_in == rt5677->pll_in &&
+           freq_out == rt5677->pll_out)
+               return 0;
+
+       if (!freq_in || !freq_out) {
+               dev_dbg(codec->dev, "PLL disabled\n");
+
+               rt5677->pll_in = 0;
+               rt5677->pll_out = 0;
+               regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+                       RT5677_SCLK_SRC_MASK, RT5677_SCLK_SRC_MCLK);
+               return 0;
+       }
+
+       switch (source) {
+       case RT5677_PLL1_S_MCLK:
+               regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+                       RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_MCLK);
+               break;
+       case RT5677_PLL1_S_BCLK1:
+       case RT5677_PLL1_S_BCLK2:
+       case RT5677_PLL1_S_BCLK3:
+       case RT5677_PLL1_S_BCLK4:
+               switch (dai->id) {
+               case RT5677_AIF1:
+                       regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+                               RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_BCLK1);
+                       break;
+               case RT5677_AIF2:
+                       regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+                               RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_BCLK2);
+                       break;
+               case RT5677_AIF3:
+                       regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+                               RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_BCLK3);
+                       break;
+               case RT5677_AIF4:
+                       regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+                               RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_BCLK4);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       default:
+               dev_err(codec->dev, "Unknown PLL source %d\n", source);
+               return -EINVAL;
+       }
+
+       ret = rt5677_pll_calc(freq_in, freq_out, &pll_code);
+       if (ret < 0) {
+               dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+               return ret;
+       }
+
+       dev_dbg(codec->dev, "m_bypass=%d k_bypass=%d m=%d n=%d k=%d\n",
+               pll_code.m_bp, pll_code.k_bp,
+               (pll_code.m_bp ? 0 : pll_code.m_code), pll_code.n_code,
+               (pll_code.k_bp ? 0 : pll_code.k_code));
+
+       regmap_write(rt5677->regmap, RT5677_PLL1_CTRL1,
+               pll_code.n_code << RT5677_PLL_N_SFT |
+               pll_code.k_bp << RT5677_PLL_K_BP_SFT |
+               (pll_code.k_bp ? 0 : pll_code.k_code));
+       regmap_write(rt5677->regmap, RT5677_PLL1_CTRL2,
+               (pll_code.m_bp ? 0 : pll_code.m_code) << RT5677_PLL_M_SFT |
+               pll_code.m_bp << RT5677_PLL_M_BP_SFT);
+
+       rt5677->pll_in = freq_in;
+       rt5677->pll_out = freq_out;
+       rt5677->pll_src = source;
+
+       return 0;
+}
+
+static int rt5677_set_bias_level(struct snd_soc_codec *codec,
+                       enum snd_soc_bias_level level)
+{
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+                       regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+                               RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
+                               0x0055);
+                       regmap_update_bits(rt5677->regmap,
+                               RT5677_PR_BASE + RT5677_BIAS_CUR4,
+                               0x0f00, 0x0f00);
+                       regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+                               RT5677_PWR_VREF1 | RT5677_PWR_MB |
+                               RT5677_PWR_BG | RT5677_PWR_VREF2,
+                               RT5677_PWR_VREF1 | RT5677_PWR_MB |
+                               RT5677_PWR_BG | RT5677_PWR_VREF2);
+                       mdelay(20);
+                       regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+                               RT5677_PWR_FV1 | RT5677_PWR_FV2,
+                               RT5677_PWR_FV1 | RT5677_PWR_FV2);
+                       regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+                               RT5677_PWR_CORE, RT5677_PWR_CORE);
+                       regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
+                               0x1, 0x1);
+               }
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x0);
+               regmap_write(rt5677->regmap, RT5677_PWR_DIG1, 0x0000);
+               regmap_write(rt5677->regmap, RT5677_PWR_DIG2, 0x0000);
+               regmap_write(rt5677->regmap, RT5677_PWR_ANLG1, 0x0000);
+               regmap_write(rt5677->regmap, RT5677_PWR_ANLG2, 0x0000);
+               regmap_update_bits(rt5677->regmap,
+                       RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0000);
+               break;
+
+       default:
+               break;
+       }
+       codec->dapm.bias_level = level;
+
+       return 0;
+}
+
+static int rt5677_probe(struct snd_soc_codec *codec)
+{
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       rt5677->codec = codec;
+
+       rt5677_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
+       regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
+
+       return 0;
+}
+
+static int rt5677_remove(struct snd_soc_codec *codec)
+{
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt5677_suspend(struct snd_soc_codec *codec)
+{
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       regcache_cache_only(rt5677->regmap, true);
+       regcache_mark_dirty(rt5677->regmap);
+
+       return 0;
+}
+
+static int rt5677_resume(struct snd_soc_codec *codec)
+{
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       regcache_cache_only(rt5677->regmap, false);
+       regcache_sync(rt5677->regmap);
+
+       return 0;
+}
+#else
+#define rt5677_suspend NULL
+#define rt5677_resume NULL
+#endif
+
+#define RT5677_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5677_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static struct snd_soc_dai_ops rt5677_aif_dai_ops = {
+       .hw_params = rt5677_hw_params,
+       .set_fmt = rt5677_set_dai_fmt,
+       .set_sysclk = rt5677_set_dai_sysclk,
+       .set_pll = rt5677_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver rt5677_dai[] = {
+       {
+               .name = "rt5677-aif1",
+               .id = RT5677_AIF1,
+               .playback = {
+                       .stream_name = "AIF1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5677_STEREO_RATES,
+                       .formats = RT5677_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AIF1 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5677_STEREO_RATES,
+                       .formats = RT5677_FORMATS,
+               },
+               .ops = &rt5677_aif_dai_ops,
+       },
+       {
+               .name = "rt5677-aif2",
+               .id = RT5677_AIF2,
+               .playback = {
+                       .stream_name = "AIF2 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5677_STEREO_RATES,
+                       .formats = RT5677_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AIF2 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5677_STEREO_RATES,
+                       .formats = RT5677_FORMATS,
+               },
+               .ops = &rt5677_aif_dai_ops,
+       },
+       {
+               .name = "rt5677-aif3",
+               .id = RT5677_AIF3,
+               .playback = {
+                       .stream_name = "AIF3 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5677_STEREO_RATES,
+                       .formats = RT5677_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AIF3 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5677_STEREO_RATES,
+                       .formats = RT5677_FORMATS,
+               },
+               .ops = &rt5677_aif_dai_ops,
+       },
+       {
+               .name = "rt5677-aif4",
+               .id = RT5677_AIF4,
+               .playback = {
+                       .stream_name = "AIF4 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5677_STEREO_RATES,
+                       .formats = RT5677_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AIF4 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5677_STEREO_RATES,
+                       .formats = RT5677_FORMATS,
+               },
+               .ops = &rt5677_aif_dai_ops,
+       },
+       {
+               .name = "rt5677-slimbus",
+               .id = RT5677_AIF5,
+               .playback = {
+                       .stream_name = "SLIMBus Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5677_STEREO_RATES,
+                       .formats = RT5677_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "SLIMBus Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT5677_STEREO_RATES,
+                       .formats = RT5677_FORMATS,
+               },
+               .ops = &rt5677_aif_dai_ops,
+       },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_rt5677 = {
+       .probe = rt5677_probe,
+       .remove = rt5677_remove,
+       .suspend = rt5677_suspend,
+       .resume = rt5677_resume,
+       .set_bias_level = rt5677_set_bias_level,
+       .idle_bias_off = true,
+       .controls = rt5677_snd_controls,
+       .num_controls = ARRAY_SIZE(rt5677_snd_controls),
+       .dapm_widgets = rt5677_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(rt5677_dapm_widgets),
+       .dapm_routes = rt5677_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(rt5677_dapm_routes),
+};
+
+static const struct regmap_config rt5677_regmap = {
+       .reg_bits = 8,
+       .val_bits = 16,
+
+       .max_register = RT5677_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5677_ranges) *
+                                               RT5677_PR_SPACING),
+
+       .volatile_reg = rt5677_volatile_register,
+       .readable_reg = rt5677_readable_register,
+
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = rt5677_reg,
+       .num_reg_defaults = ARRAY_SIZE(rt5677_reg),
+       .ranges = rt5677_ranges,
+       .num_ranges = ARRAY_SIZE(rt5677_ranges),
+};
+
+static const struct i2c_device_id rt5677_i2c_id[] = {
+       { "rt5677", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
+
+static int rt5677_i2c_probe(struct i2c_client *i2c,
+                   const struct i2c_device_id *id)
+{
+       struct rt5677_platform_data *pdata = dev_get_platdata(&i2c->dev);
+       struct rt5677_priv *rt5677;
+       int ret;
+       unsigned int val;
+
+       rt5677 = devm_kzalloc(&i2c->dev, sizeof(struct rt5677_priv),
+                               GFP_KERNEL);
+       if (rt5677 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, rt5677);
+
+       if (pdata)
+               rt5677->pdata = *pdata;
+
+       rt5677->regmap = devm_regmap_init_i2c(i2c, &rt5677_regmap);
+       if (IS_ERR(rt5677->regmap)) {
+               ret = PTR_ERR(rt5677->regmap);
+               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       regmap_read(rt5677->regmap, RT5677_VENDOR_ID2, &val);
+       if (val != RT5677_DEVICE_ID) {
+               dev_err(&i2c->dev,
+                       "Device with ID register %x is not rt5677\n", val);
+               return -ENODEV;
+       }
+
+       regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+
+       ret = regmap_register_patch(rt5677->regmap, init_list,
+                                   ARRAY_SIZE(init_list));
+       if (ret != 0)
+               dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+       if (rt5677->pdata.in1_diff)
+               regmap_update_bits(rt5677->regmap, RT5677_IN1,
+                                       RT5677_IN_DF1, RT5677_IN_DF1);
+
+       if (rt5677->pdata.in2_diff)
+               regmap_update_bits(rt5677->regmap, RT5677_IN1,
+                                       RT5677_IN_DF2, RT5677_IN_DF2);
+
+       ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677,
+                       rt5677_dai, ARRAY_SIZE(rt5677_dai));
+       if (ret < 0)
+               goto err;
+
+       return 0;
+err:
+       return ret;
+}
+
+static int rt5677_i2c_remove(struct i2c_client *i2c)
+{
+       snd_soc_unregister_codec(&i2c->dev);
+
+       return 0;
+}
+
+static struct i2c_driver rt5677_i2c_driver = {
+       .driver = {
+               .name = "rt5677",
+               .owner = THIS_MODULE,
+       },
+       .probe = rt5677_i2c_probe,
+       .remove   = rt5677_i2c_remove,
+       .id_table = rt5677_i2c_id,
+};
+
+static int __init rt5677_modinit(void)
+{
+       return i2c_add_driver(&rt5677_i2c_driver);
+}
+module_init(rt5677_modinit);
+
+static void __exit rt5677_modexit(void)
+{
+       i2c_del_driver(&rt5677_i2c_driver);
+}
+module_exit(rt5677_modexit);
+
+MODULE_DESCRIPTION("ASoC RT5677 driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h
new file mode 100644 (file)
index 0000000..af4e9c7
--- /dev/null
@@ -0,0 +1,1451 @@
+/*
+ * rt5677.h  --  RT5677 ALSA SoC audio driver
+ *
+ * Copyright 2013 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.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 __RT5677_H__
+#define __RT5677_H__
+
+#include <sound/rt5677.h>
+
+/* Info */
+#define RT5677_RESET                           0x00
+#define RT5677_VENDOR_ID                       0xfd
+#define RT5677_VENDOR_ID1                      0xfe
+#define RT5677_VENDOR_ID2                      0xff
+/*  I/O - Output */
+#define RT5677_LOUT1                           0x01
+/* I/O - Input */
+#define RT5677_IN1                             0x03
+#define RT5677_MICBIAS                         0x04
+/* I/O - SLIMBus */
+#define RT5677_SLIMBUS_PARAM                   0x07
+#define RT5677_SLIMBUS_RX                      0x08
+#define RT5677_SLIMBUS_CTRL                    0x09
+/* I/O */
+#define RT5677_SIDETONE_CTRL                   0x13
+/* I/O - ADC/DAC */
+#define RT5677_ANA_DAC1_2_3_SRC                        0x15
+#define RT5677_IF_DSP_DAC3_4_MIXER             0x16
+#define RT5677_DAC4_DIG_VOL                    0x17
+#define RT5677_DAC3_DIG_VOL                    0x18
+#define RT5677_DAC1_DIG_VOL                    0x19
+#define RT5677_DAC2_DIG_VOL                    0x1a
+#define RT5677_IF_DSP_DAC2_MIXER               0x1b
+#define RT5677_STO1_ADC_DIG_VOL                        0x1c
+#define RT5677_MONO_ADC_DIG_VOL                        0x1d
+#define RT5677_STO1_2_ADC_BST                  0x1e
+#define RT5677_STO2_ADC_DIG_VOL                        0x1f
+/* Mixer - D-D */
+#define RT5677_ADC_BST_CTRL2                   0x20
+#define RT5677_STO3_4_ADC_BST                  0x21
+#define RT5677_STO3_ADC_DIG_VOL                        0x22
+#define RT5677_STO4_ADC_DIG_VOL                        0x23
+#define RT5677_STO4_ADC_MIXER                  0x24
+#define RT5677_STO3_ADC_MIXER                  0x25
+#define RT5677_STO2_ADC_MIXER                  0x26
+#define RT5677_STO1_ADC_MIXER                  0x27
+#define RT5677_MONO_ADC_MIXER                  0x28
+#define RT5677_ADC_IF_DSP_DAC1_MIXER           0x29
+#define RT5677_STO1_DAC_MIXER                  0x2a
+#define RT5677_MONO_DAC_MIXER                  0x2b
+#define RT5677_DD1_MIXER                       0x2c
+#define RT5677_DD2_MIXER                       0x2d
+#define RT5677_IF3_DATA                                0x2f
+#define RT5677_IF4_DATA                                0x30
+/* Mixer - PDM */
+#define RT5677_PDM_OUT_CTRL                    0x31
+#define RT5677_PDM_DATA_CTRL1                  0x32
+#define RT5677_PDM_DATA_CTRL2                  0x33
+#define RT5677_PDM1_DATA_CTRL2                 0x34
+#define RT5677_PDM1_DATA_CTRL3                 0x35
+#define RT5677_PDM1_DATA_CTRL4                 0x36
+#define RT5677_PDM2_DATA_CTRL2                 0x37
+#define RT5677_PDM2_DATA_CTRL3                 0x38
+#define RT5677_PDM2_DATA_CTRL4                 0x39
+/* TDM */
+#define RT5677_TDM1_CTRL1                      0x3b
+#define RT5677_TDM1_CTRL2                      0x3c
+#define RT5677_TDM1_CTRL3                      0x3d
+#define RT5677_TDM1_CTRL4                      0x3e
+#define RT5677_TDM1_CTRL5                      0x3f
+#define RT5677_TDM2_CTRL1                      0x40
+#define RT5677_TDM2_CTRL2                      0x41
+#define RT5677_TDM2_CTRL3                      0x42
+#define RT5677_TDM2_CTRL4                      0x43
+#define RT5677_TDM2_CTRL5                      0x44
+/* I2C_MASTER_CTRL */
+#define RT5677_I2C_MASTER_CTRL1                        0x47
+#define RT5677_I2C_MASTER_CTRL2                        0x48
+#define RT5677_I2C_MASTER_CTRL3                        0x49
+#define RT5677_I2C_MASTER_CTRL4                        0x4a
+#define RT5677_I2C_MASTER_CTRL5                        0x4b
+#define RT5677_I2C_MASTER_CTRL6                        0x4c
+#define RT5677_I2C_MASTER_CTRL7                        0x4d
+#define RT5677_I2C_MASTER_CTRL8                        0x4e
+/* DMIC */
+#define RT5677_DMIC_CTRL1                      0x50
+#define RT5677_DMIC_CTRL2                      0x51
+/* Haptic Generator */
+#define RT5677_HAP_GENE_CTRL1                  0x56
+#define RT5677_HAP_GENE_CTRL2                  0x57
+#define RT5677_HAP_GENE_CTRL3                  0x58
+#define RT5677_HAP_GENE_CTRL4                  0x59
+#define RT5677_HAP_GENE_CTRL5                  0x5a
+#define RT5677_HAP_GENE_CTRL6                  0x5b
+#define RT5677_HAP_GENE_CTRL7                  0x5c
+#define RT5677_HAP_GENE_CTRL8                  0x5d
+#define RT5677_HAP_GENE_CTRL9                  0x5e
+#define RT5677_HAP_GENE_CTRL10                 0x5f
+/* Power */
+#define RT5677_PWR_DIG1                                0x61
+#define RT5677_PWR_DIG2                                0x62
+#define RT5677_PWR_ANLG1                       0x63
+#define RT5677_PWR_ANLG2                       0x64
+#define RT5677_PWR_DSP1                                0x65
+#define RT5677_PWR_DSP_ST                      0x66
+#define RT5677_PWR_DSP2                                0x67
+#define RT5677_ADC_DAC_HPF_CTRL1               0x68
+/* Private Register Control */
+#define RT5677_PRIV_INDEX                      0x6a
+#define RT5677_PRIV_DATA                       0x6c
+/* Format - ADC/DAC */
+#define RT5677_I2S4_SDP                                0x6f
+#define RT5677_I2S1_SDP                                0x70
+#define RT5677_I2S2_SDP                                0x71
+#define RT5677_I2S3_SDP                                0x72
+#define RT5677_CLK_TREE_CTRL1                  0x73
+#define RT5677_CLK_TREE_CTRL2                  0x74
+#define RT5677_CLK_TREE_CTRL3                  0x75
+/* Function - Analog */
+#define RT5677_PLL1_CTRL1                      0x7a
+#define RT5677_PLL1_CTRL2                      0x7b
+#define RT5677_PLL2_CTRL1                      0x7c
+#define RT5677_PLL2_CTRL2                      0x7d
+#define RT5677_GLB_CLK1                                0x80
+#define RT5677_GLB_CLK2                                0x81
+#define RT5677_ASRC_1                          0x83
+#define RT5677_ASRC_2                          0x84
+#define RT5677_ASRC_3                          0x85
+#define RT5677_ASRC_4                          0x86
+#define RT5677_ASRC_5                          0x87
+#define RT5677_ASRC_6                          0x88
+#define RT5677_ASRC_7                          0x89
+#define RT5677_ASRC_8                          0x8a
+#define RT5677_ASRC_9                          0x8b
+#define RT5677_ASRC_10                         0x8c
+#define RT5677_ASRC_11                         0x8d
+#define RT5677_ASRC_12                         0x8e
+#define RT5677_ASRC_13                         0x8f
+#define RT5677_ASRC_14                         0x90
+#define RT5677_ASRC_15                         0x91
+#define RT5677_ASRC_16                         0x92
+#define RT5677_ASRC_17                         0x93
+#define RT5677_ASRC_18                         0x94
+#define RT5677_ASRC_19                         0x95
+#define RT5677_ASRC_20                         0x97
+#define RT5677_ASRC_21                         0x98
+#define RT5677_ASRC_22                         0x99
+#define RT5677_ASRC_23                         0x9a
+#define RT5677_VAD_CTRL1                       0x9c
+#define RT5677_VAD_CTRL2                       0x9d
+#define RT5677_VAD_CTRL3                       0x9e
+#define RT5677_VAD_CTRL4                       0x9f
+#define RT5677_VAD_CTRL5                       0xa0
+/* Function - Digital */
+#define RT5677_DSP_INB_CTRL1                   0xa3
+#define RT5677_DSP_INB_CTRL2                   0xa4
+#define RT5677_DSP_IN_OUTB_CTRL                        0xa5
+#define RT5677_DSP_OUTB0_1_DIG_VOL             0xa6
+#define RT5677_DSP_OUTB2_3_DIG_VOL             0xa7
+#define RT5677_DSP_OUTB4_5_DIG_VOL             0xa8
+#define RT5677_DSP_OUTB6_7_DIG_VOL             0xa9
+#define RT5677_ADC_EQ_CTRL1                    0xae
+#define RT5677_ADC_EQ_CTRL2                    0xaf
+#define RT5677_EQ_CTRL1                                0xb0
+#define RT5677_EQ_CTRL2                                0xb1
+#define RT5677_EQ_CTRL3                                0xb2
+#define RT5677_SOFT_VOL_ZERO_CROSS1            0xb3
+#define RT5677_JD_CTRL1                                0xb5
+#define RT5677_JD_CTRL2                                0xb6
+#define RT5677_JD_CTRL3                                0xb8
+#define RT5677_IRQ_CTRL1                       0xbd
+#define RT5677_IRQ_CTRL2                       0xbe
+#define RT5677_GPIO_ST                         0xbf
+#define RT5677_GPIO_CTRL1                      0xc0
+#define RT5677_GPIO_CTRL2                      0xc1
+#define RT5677_GPIO_CTRL3                      0xc2
+#define RT5677_STO1_ADC_HI_FILTER1             0xc5
+#define RT5677_STO1_ADC_HI_FILTER2             0xc6
+#define RT5677_MONO_ADC_HI_FILTER1             0xc7
+#define RT5677_MONO_ADC_HI_FILTER2             0xc8
+#define RT5677_STO2_ADC_HI_FILTER1             0xc9
+#define RT5677_STO2_ADC_HI_FILTER2             0xca
+#define RT5677_STO3_ADC_HI_FILTER1             0xcb
+#define RT5677_STO3_ADC_HI_FILTER2             0xcc
+#define RT5677_STO4_ADC_HI_FILTER1             0xcd
+#define RT5677_STO4_ADC_HI_FILTER2             0xce
+#define RT5677_MB_DRC_CTRL1                    0xd0
+#define RT5677_DRC1_CTRL1                      0xd2
+#define RT5677_DRC1_CTRL2                      0xd3
+#define RT5677_DRC1_CTRL3                      0xd4
+#define RT5677_DRC1_CTRL4                      0xd5
+#define RT5677_DRC1_CTRL5                      0xd6
+#define RT5677_DRC1_CTRL6                      0xd7
+#define RT5677_DRC2_CTRL1                      0xd8
+#define RT5677_DRC2_CTRL2                      0xd9
+#define RT5677_DRC2_CTRL3                      0xda
+#define RT5677_DRC2_CTRL4                      0xdb
+#define RT5677_DRC2_CTRL5                      0xdc
+#define RT5677_DRC2_CTRL6                      0xdd
+#define RT5677_DRC1_HL_CTRL1                   0xde
+#define RT5677_DRC1_HL_CTRL2                   0xdf
+#define RT5677_DRC2_HL_CTRL1                   0xe0
+#define RT5677_DRC2_HL_CTRL2                   0xe1
+#define RT5677_DSP_INB1_SRC_CTRL1              0xe3
+#define RT5677_DSP_INB1_SRC_CTRL2              0xe4
+#define RT5677_DSP_INB1_SRC_CTRL3              0xe5
+#define RT5677_DSP_INB1_SRC_CTRL4              0xe6
+#define RT5677_DSP_INB2_SRC_CTRL1              0xe7
+#define RT5677_DSP_INB2_SRC_CTRL2              0xe8
+#define RT5677_DSP_INB2_SRC_CTRL3              0xe9
+#define RT5677_DSP_INB2_SRC_CTRL4              0xea
+#define RT5677_DSP_INB3_SRC_CTRL1              0xeb
+#define RT5677_DSP_INB3_SRC_CTRL2              0xec
+#define RT5677_DSP_INB3_SRC_CTRL3              0xed
+#define RT5677_DSP_INB3_SRC_CTRL4              0xee
+#define RT5677_DSP_OUTB1_SRC_CTRL1             0xef
+#define RT5677_DSP_OUTB1_SRC_CTRL2             0xf0
+#define RT5677_DSP_OUTB1_SRC_CTRL3             0xf1
+#define RT5677_DSP_OUTB1_SRC_CTRL4             0xf2
+#define RT5677_DSP_OUTB2_SRC_CTRL1             0xf3
+#define RT5677_DSP_OUTB2_SRC_CTRL2             0xf4
+#define RT5677_DSP_OUTB2_SRC_CTRL3             0xf5
+#define RT5677_DSP_OUTB2_SRC_CTRL4             0xf6
+
+/* Virtual DSP Mixer Control */
+#define RT5677_DSP_OUTB_0123_MIXER_CTRL                0xf7
+#define RT5677_DSP_OUTB_45_MIXER_CTRL          0xf8
+#define RT5677_DSP_OUTB_67_MIXER_CTRL          0xf9
+
+/* General Control */
+#define RT5677_DIG_MISC                                0xfa
+#define RT5677_GEN_CTRL1                       0xfb
+#define RT5677_GEN_CTRL2                       0xfc
+
+/* DSP Mode I2C Control*/
+#define RT5677_DSP_I2C_OP_CODE                 0x00
+#define RT5677_DSP_I2C_ADDR_LSB                        0x01
+#define RT5677_DSP_I2C_ADDR_MSB                        0x02
+#define RT5677_DSP_I2C_DATA_LSB                        0x03
+#define RT5677_DSP_I2C_DATA_MSB                        0x04
+
+/* Index of Codec Private Register definition */
+#define RT5677_PR_DRC1_CTRL_1                  0x01
+#define RT5677_PR_DRC1_CTRL_2                  0x02
+#define RT5677_PR_DRC1_CTRL_3                  0x03
+#define RT5677_PR_DRC1_CTRL_4                  0x04
+#define RT5677_PR_DRC1_CTRL_5                  0x05
+#define RT5677_PR_DRC1_CTRL_6                  0x06
+#define RT5677_PR_DRC1_CTRL_7                  0x07
+#define RT5677_PR_DRC2_CTRL_1                  0x08
+#define RT5677_PR_DRC2_CTRL_2                  0x09
+#define RT5677_PR_DRC2_CTRL_3                  0x0a
+#define RT5677_PR_DRC2_CTRL_4                  0x0b
+#define RT5677_PR_DRC2_CTRL_5                  0x0c
+#define RT5677_PR_DRC2_CTRL_6                  0x0d
+#define RT5677_PR_DRC2_CTRL_7                  0x0e
+#define RT5677_BIAS_CUR1                       0x10
+#define RT5677_BIAS_CUR2                       0x12
+#define RT5677_BIAS_CUR3                       0x13
+#define RT5677_BIAS_CUR4                       0x14
+#define RT5677_BIAS_CUR5                       0x15
+#define RT5677_VREF_LOUT_CTRL                  0x17
+#define RT5677_DIG_VOL_CTRL1                   0x1a
+#define RT5677_DIG_VOL_CTRL2                   0x1b
+#define RT5677_ANA_ADC_GAIN_CTRL               0x1e
+#define RT5677_VAD_SRAM_TEST1                  0x20
+#define RT5677_VAD_SRAM_TEST2                  0x21
+#define RT5677_VAD_SRAM_TEST3                  0x22
+#define RT5677_VAD_SRAM_TEST4                  0x23
+#define RT5677_PAD_DRV_CTRL                    0x26
+#define RT5677_DIG_IN_PIN_ST_CTRL1             0x29
+#define RT5677_DIG_IN_PIN_ST_CTRL2             0x2a
+#define RT5677_DIG_IN_PIN_ST_CTRL3             0x2b
+#define RT5677_PLL1_INT                                0x38
+#define RT5677_PLL2_INT                                0x39
+#define RT5677_TEST_CTRL1                      0x3a
+#define RT5677_TEST_CTRL2                      0x3b
+#define RT5677_TEST_CTRL3                      0x3c
+#define RT5677_CHOP_DAC_ADC                    0x3d
+#define RT5677_SOFT_DEPOP_DAC_CLK_CTRL         0x3e
+#define RT5677_CROSS_OVER_FILTER1              0x90
+#define RT5677_CROSS_OVER_FILTER2              0x91
+#define RT5677_CROSS_OVER_FILTER3              0x92
+#define RT5677_CROSS_OVER_FILTER4              0x93
+#define RT5677_CROSS_OVER_FILTER5              0x94
+#define RT5677_CROSS_OVER_FILTER6              0x95
+#define RT5677_CROSS_OVER_FILTER7              0x96
+#define RT5677_CROSS_OVER_FILTER8              0x97
+#define RT5677_CROSS_OVER_FILTER9              0x98
+#define RT5677_CROSS_OVER_FILTER10             0x99
+
+/* global definition */
+#define RT5677_L_MUTE                          (0x1 << 15)
+#define RT5677_L_MUTE_SFT                      15
+#define RT5677_VOL_L_MUTE                      (0x1 << 14)
+#define RT5677_VOL_L_SFT                       14
+#define RT5677_R_MUTE                          (0x1 << 7)
+#define RT5677_R_MUTE_SFT                      7
+#define RT5677_VOL_R_MUTE                      (0x1 << 6)
+#define RT5677_VOL_R_SFT                       6
+#define RT5677_L_VOL_MASK                      (0x3f << 8)
+#define RT5677_L_VOL_SFT                       8
+#define RT5677_R_VOL_MASK                      (0x3f)
+#define RT5677_R_VOL_SFT                       0
+
+/* LOUT1 Control (0x01) */
+#define RT5677_LOUT1_L_MUTE                    (0x1 << 15)
+#define RT5677_LOUT1_L_MUTE_SFT                        (15)
+#define RT5677_LOUT1_L_DF                      (0x1 << 14)
+#define RT5677_LOUT1_L_DF_SFT                  (14)
+#define RT5677_LOUT2_L_MUTE                    (0x1 << 13)
+#define RT5677_LOUT2_L_MUTE_SFT                        (13)
+#define RT5677_LOUT2_L_DF                      (0x1 << 12)
+#define RT5677_LOUT2_L_DF_SFT                  (12)
+#define RT5677_LOUT3_L_MUTE                    (0x1 << 11)
+#define RT5677_LOUT3_L_MUTE_SFT                        (11)
+#define RT5677_LOUT3_L_DF                      (0x1 << 10)
+#define RT5677_LOUT3_L_DF_SFT                  (10)
+#define RT5677_LOUT1_ENH_DRV                   (0x1 << 9)
+#define RT5677_LOUT1_ENH_DRV_SFT               (9)
+#define RT5677_LOUT2_ENH_DRV                   (0x1 << 8)
+#define RT5677_LOUT2_ENH_DRV_SFT               (8)
+#define RT5677_LOUT3_ENH_DRV                   (0x1 << 7)
+#define RT5677_LOUT3_ENH_DRV_SFT               (7)
+
+/* IN1 Control (0x03) */
+#define RT5677_BST_MASK1                       (0xf << 12)
+#define RT5677_BST_SFT1                                12
+#define RT5677_BST_MASK2                       (0xf << 8)
+#define RT5677_BST_SFT2                                8
+#define RT5677_IN_DF1                          (0x1 << 7)
+#define RT5677_IN_DF1_SFT                      7
+#define RT5677_IN_DF2                          (0x1 << 6)
+#define RT5677_IN_DF2_SFT                      6
+
+/* Micbias Control (0x04) */
+#define RT5677_MICBIAS1_OUTVOLT_MASK           (0x1 << 15)
+#define RT5677_MICBIAS1_OUTVOLT_SFT            (15)
+#define RT5677_MICBIAS1_OUTVOLT_2_7V           (0x0 << 15)
+#define RT5677_MICBIAS1_OUTVOLT_2_25V          (0x1 << 15)
+#define RT5677_MICBIAS1_CTRL_VDD_MASK          (0x1 << 14)
+#define RT5677_MICBIAS1_CTRL_VDD_SFT           (14)
+#define RT5677_MICBIAS1_CTRL_VDD_1_8V          (0x0 << 14)
+#define RT5677_MICBIAS1_CTRL_VDD_3_3V          (0x1 << 14)
+#define RT5677_MICBIAS1_OVCD_MASK              (0x1 << 11)
+#define RT5677_MICBIAS1_OVCD_SHIFT             (11)
+#define RT5677_MICBIAS1_OVCD_DIS               (0x0 << 11)
+#define RT5677_MICBIAS1_OVCD_EN                        (0x1 << 11)
+#define RT5677_MICBIAS1_OVTH_MASK              (0x3 << 9)
+#define RT5677_MICBIAS1_OVTH_SFT               9
+#define RT5677_MICBIAS1_OVTH_640UA             (0x0 << 9)
+#define RT5677_MICBIAS1_OVTH_1280UA            (0x1 << 9)
+#define RT5677_MICBIAS1_OVTH_1920UA            (0x2 << 9)
+
+/* SLIMbus Parameter (0x07) */
+
+/* SLIMbus Rx (0x08) */
+#define RT5677_SLB_ADC4_MASK                   (0x3 << 6)
+#define RT5677_SLB_ADC4_SFT                    6
+#define RT5677_SLB_ADC3_MASK                   (0x3 << 4)
+#define RT5677_SLB_ADC3_SFT                    4
+#define RT5677_SLB_ADC2_MASK                   (0x3 << 2)
+#define RT5677_SLB_ADC2_SFT                    2
+#define RT5677_SLB_ADC1_MASK                   (0x3 << 0)
+#define RT5677_SLB_ADC1_SFT                    0
+
+/* SLIMBus control (0x09) */
+
+/* Sidetone Control (0x13) */
+#define RT5677_ST_HPF_SEL_MASK                 (0x7 << 13)
+#define RT5677_ST_HPF_SEL_SFT                  13
+#define RT5677_ST_HPF_PATH                     (0x1 << 12)
+#define RT5677_ST_HPF_PATH_SFT                 12
+#define RT5677_ST_SEL_MASK                     (0x7 << 9)
+#define RT5677_ST_SEL_SFT                      9
+#define RT5677_ST_EN                           (0x1 << 6)
+#define RT5677_ST_EN_SFT                       6
+
+/* Analog DAC1/2/3 Source Control (0x15) */
+#define RT5677_ANA_DAC3_SRC_SEL_MASK           (0x3 << 4)
+#define RT5677_ANA_DAC3_SRC_SEL_SFT            4
+#define RT5677_ANA_DAC1_2_SRC_SEL_MASK         (0x3 << 0)
+#define RT5677_ANA_DAC1_2_SRC_SEL_SFT          0
+
+/* IF/DSP to DAC3/4 Mixer Control (0x16) */
+#define RT5677_M_DAC4_L_VOL                    (0x1 << 15)
+#define RT5677_M_DAC4_L_VOL_SFT                        15
+#define RT5677_SEL_DAC4_L_SRC_MASK             (0x7 << 12)
+#define RT5677_SEL_DAC4_L_SRC_SFT              12
+#define RT5677_M_DAC4_R_VOL                    (0x1 << 11)
+#define RT5677_M_DAC4_R_VOL_SFT                        11
+#define RT5677_SEL_DAC4_R_SRC_MASK             (0x7 << 8)
+#define RT5677_SEL_DAC4_R_SRC_SFT              8
+#define RT5677_M_DAC3_L_VOL                    (0x1 << 7)
+#define RT5677_M_DAC3_L_VOL_SFT                        7
+#define RT5677_SEL_DAC3_L_SRC_MASK             (0x7 << 4)
+#define RT5677_SEL_DAC3_L_SRC_SFT              4
+#define RT5677_M_DAC3_R_VOL                    (0x1 << 3)
+#define RT5677_M_DAC3_R_VOL_SFT                        3
+#define RT5677_SEL_DAC3_R_SRC_MASK             (0x7 << 0)
+#define RT5677_SEL_DAC3_R_SRC_SFT              0
+
+/* DAC4 Digital Volume (0x17) */
+#define RT5677_DAC4_L_VOL_MASK                 (0xff << 8)
+#define RT5677_DAC4_L_VOL_SFT                  8
+#define RT5677_DAC4_R_VOL_MASK                 (0xff)
+#define RT5677_DAC4_R_VOL_SFT                  0
+
+/* DAC3 Digital Volume (0x18) */
+#define RT5677_DAC3_L_VOL_MASK                 (0xff << 8)
+#define RT5677_DAC3_L_VOL_SFT                  8
+#define RT5677_DAC3_R_VOL_MASK                 (0xff)
+#define RT5677_DAC3_R_VOL_SFT                  0
+
+/* DAC3 Digital Volume (0x19) */
+#define RT5677_DAC1_L_VOL_MASK                 (0xff << 8)
+#define RT5677_DAC1_L_VOL_SFT                  8
+#define RT5677_DAC1_R_VOL_MASK                 (0xff)
+#define RT5677_DAC1_R_VOL_SFT                  0
+
+/* DAC2 Digital Volume (0x1a) */
+#define RT5677_DAC2_L_VOL_MASK                 (0xff << 8)
+#define RT5677_DAC2_L_VOL_SFT                  8
+#define RT5677_DAC2_R_VOL_MASK                 (0xff)
+#define RT5677_DAC2_R_VOL_SFT                  0
+
+/* IF/DSP to DAC2 Mixer Control (0x1b) */
+#define RT5677_M_DAC2_L_VOL                    (0x1 << 7)
+#define RT5677_M_DAC2_L_VOL_SFT                        7
+#define RT5677_SEL_DAC2_L_SRC_MASK             (0x7 << 4)
+#define RT5677_SEL_DAC2_L_SRC_SFT              4
+#define RT5677_M_DAC2_R_VOL                    (0x1 << 3)
+#define RT5677_M_DAC2_R_VOL_SFT                        3
+#define RT5677_SEL_DAC2_R_SRC_MASK             (0x7 << 0)
+#define RT5677_SEL_DAC2_R_SRC_SFT              0
+
+/* Stereo1 ADC Digital Volume Control (0x1c) */
+#define RT5677_STO1_ADC_L_VOL_MASK             (0x7f << 8)
+#define RT5677_STO1_ADC_L_VOL_SFT              8
+#define RT5677_STO1_ADC_R_VOL_MASK             (0x7f)
+#define RT5677_STO1_ADC_R_VOL_SFT              0
+
+/* Mono ADC Digital Volume Control (0x1d) */
+#define RT5677_MONO_ADC_L_VOL_MASK             (0x7f << 8)
+#define RT5677_MONO_ADC_L_VOL_SFT              8
+#define RT5677_MONO_ADC_R_VOL_MASK             (0x7f)
+#define RT5677_MONO_ADC_R_VOL_SFT              0
+
+/* Stereo 1/2 ADC Boost Gain Control (0x1e) */
+#define RT5677_STO1_ADC_L_BST_MASK             (0x3 << 14)
+#define RT5677_STO1_ADC_L_BST_SFT              14
+#define RT5677_STO1_ADC_R_BST_MASK             (0x3 << 12)
+#define RT5677_STO1_ADC_R_BST_SFT              12
+#define RT5677_STO1_ADC_COMP_MASK              (0x3 << 10)
+#define RT5677_STO1_ADC_COMP_SFT               10
+#define RT5677_STO2_ADC_L_BST_MASK             (0x3 << 8)
+#define RT5677_STO2_ADC_L_BST_SFT              8
+#define RT5677_STO2_ADC_R_BST_MASK             (0x3 << 6)
+#define RT5677_STO2_ADC_R_BST_SFT              6
+#define RT5677_STO2_ADC_COMP_MASK              (0x3 << 4)
+#define RT5677_STO2_ADC_COMP_SFT               4
+
+/* Stereo2 ADC Digital Volume Control (0x1f) */
+#define RT5677_STO2_ADC_L_VOL_MASK             (0x7f << 8)
+#define RT5677_STO2_ADC_L_VOL_SFT              8
+#define RT5677_STO2_ADC_R_VOL_MASK             (0x7f)
+#define RT5677_STO2_ADC_R_VOL_SFT              0
+
+/* ADC Boost Gain Control 2 (0x20) */
+#define RT5677_MONO_ADC_L_BST_MASK             (0x3 << 14)
+#define RT5677_MONO_ADC_L_BST_SFT              14
+#define RT5677_MONO_ADC_R_BST_MASK             (0x3 << 12)
+#define RT5677_MONO_ADC_R_BST_SFT              12
+#define RT5677_MONO_ADC_COMP_MASK              (0x3 << 10)
+#define RT5677_MONO_ADC_COMP_SFT               10
+
+/* Stereo 3/4 ADC Boost Gain Control (0x21) */
+#define RT5677_STO3_ADC_L_BST_MASK             (0x3 << 14)
+#define RT5677_STO3_ADC_L_BST_SFT              14
+#define RT5677_STO3_ADC_R_BST_MASK             (0x3 << 12)
+#define RT5677_STO3_ADC_R_BST_SFT              12
+#define RT5677_STO3_ADC_COMP_MASK              (0x3 << 10)
+#define RT5677_STO3_ADC_COMP_SFT               10
+#define RT5677_STO4_ADC_L_BST_MASK             (0x3 << 8)
+#define RT5677_STO4_ADC_L_BST_SFT              8
+#define RT5677_STO4_ADC_R_BST_MASK             (0x3 << 6)
+#define RT5677_STO4_ADC_R_BST_SFT              6
+#define RT5677_STO4_ADC_COMP_MASK              (0x3 << 4)
+#define RT5677_STO4_ADC_COMP_SFT               4
+
+/* Stereo3 ADC Digital Volume Control (0x22) */
+#define RT5677_STO3_ADC_L_VOL_MASK             (0x7f << 8)
+#define RT5677_STO3_ADC_L_VOL_SFT              8
+#define RT5677_STO3_ADC_R_VOL_MASK             (0x7f)
+#define RT5677_STO3_ADC_R_VOL_SFT              0
+
+/* Stereo4 ADC Digital Volume Control (0x23) */
+#define RT5677_STO4_ADC_L_VOL_MASK             (0x7f << 8)
+#define RT5677_STO4_ADC_L_VOL_SFT              8
+#define RT5677_STO4_ADC_R_VOL_MASK             (0x7f)
+#define RT5677_STO4_ADC_R_VOL_SFT              0
+
+/* Stereo4 ADC Mixer control (0x24) */
+#define RT5677_M_STO4_ADC_L2                   (0x1 << 15)
+#define RT5677_M_STO4_ADC_L2_SFT               15
+#define RT5677_M_STO4_ADC_L1                   (0x1 << 14)
+#define RT5677_M_STO4_ADC_L1_SFT               14
+#define RT5677_SEL_STO4_ADC1_MASK              (0x3 << 12)
+#define RT5677_SEL_STO4_ADC1_SFT               12
+#define RT5677_SEL_STO4_ADC2_MASK              (0x3 << 10)
+#define RT5677_SEL_STO4_ADC2_SFT               10
+#define RT5677_SEL_STO4_DMIC_MASK              (0x3 << 8)
+#define RT5677_SEL_STO4_DMIC_SFT               8
+#define RT5677_M_STO4_ADC_R1                   (0x1 << 7)
+#define RT5677_M_STO4_ADC_R1_SFT               7
+#define RT5677_M_STO4_ADC_R2                   (0x1 << 6)
+#define RT5677_M_STO4_ADC_R2_SFT               6
+
+/* Stereo3 ADC Mixer control (0x25) */
+#define RT5677_M_STO3_ADC_L2                   (0x1 << 15)
+#define RT5677_M_STO3_ADC_L2_SFT               15
+#define RT5677_M_STO3_ADC_L1                   (0x1 << 14)
+#define RT5677_M_STO3_ADC_L1_SFT               14
+#define RT5677_SEL_STO3_ADC1_MASK              (0x3 << 12)
+#define RT5677_SEL_STO3_ADC1_SFT               12
+#define RT5677_SEL_STO3_ADC2_MASK              (0x3 << 10)
+#define RT5677_SEL_STO3_ADC2_SFT               10
+#define RT5677_SEL_STO3_DMIC_MASK              (0x3 << 8)
+#define RT5677_SEL_STO3_DMIC_SFT               8
+#define RT5677_M_STO3_ADC_R1                   (0x1 << 7)
+#define RT5677_M_STO3_ADC_R1_SFT               7
+#define RT5677_M_STO3_ADC_R2                   (0x1 << 6)
+#define RT5677_M_STO3_ADC_R2_SFT               6
+
+/* Stereo2 ADC Mixer Control (0x26) */
+#define RT5677_M_STO2_ADC_L2                   (0x1 << 15)
+#define RT5677_M_STO2_ADC_L2_SFT               15
+#define RT5677_M_STO2_ADC_L1                   (0x1 << 14)
+#define RT5677_M_STO2_ADC_L1_SFT               14
+#define RT5677_SEL_STO2_ADC1_MASK              (0x3 << 12)
+#define RT5677_SEL_STO2_ADC1_SFT               12
+#define RT5677_SEL_STO2_ADC2_MASK              (0x3 << 10)
+#define RT5677_SEL_STO2_ADC2_SFT               10
+#define RT5677_SEL_STO2_DMIC_MASK              (0x3 << 8)
+#define RT5677_SEL_STO2_DMIC_SFT               8
+#define RT5677_M_STO2_ADC_R1                   (0x1 << 7)
+#define RT5677_M_STO2_ADC_R1_SFT               7
+#define RT5677_M_STO2_ADC_R2                   (0x1 << 6)
+#define RT5677_M_STO2_ADC_R2_SFT               6
+#define RT5677_SEL_STO2_LR_MIX_MASK            (0x1 << 0)
+#define RT5677_SEL_STO2_LR_MIX_SFT             0
+#define RT5677_SEL_STO2_LR_MIX_L               (0x0 << 0)
+#define RT5677_SEL_STO2_LR_MIX_LR              (0x1 << 0)
+
+/* Stereo1 ADC Mixer control (0x27) */
+#define RT5677_M_STO1_ADC_L2                   (0x1 << 15)
+#define RT5677_M_STO1_ADC_L2_SFT               15
+#define RT5677_M_STO1_ADC_L1                   (0x1 << 14)
+#define RT5677_M_STO1_ADC_L1_SFT               14
+#define RT5677_SEL_STO1_ADC1_MASK              (0x3 << 12)
+#define RT5677_SEL_STO1_ADC1_SFT               12
+#define RT5677_SEL_STO1_ADC2_MASK              (0x3 << 10)
+#define RT5677_SEL_STO1_ADC2_SFT               10
+#define RT5677_SEL_STO1_DMIC_MASK              (0x3 << 8)
+#define RT5677_SEL_STO1_DMIC_SFT               8
+#define RT5677_M_STO1_ADC_R1                   (0x1 << 7)
+#define RT5677_M_STO1_ADC_R1_SFT               7
+#define RT5677_M_STO1_ADC_R2                   (0x1 << 6)
+#define RT5677_M_STO1_ADC_R2_SFT               6
+
+/* Mono ADC Mixer control (0x28) */
+#define RT5677_M_MONO_ADC_L2                   (0x1 << 15)
+#define RT5677_M_MONO_ADC_L2_SFT               15
+#define RT5677_M_MONO_ADC_L1                   (0x1 << 14)
+#define RT5677_M_MONO_ADC_L1_SFT               14
+#define RT5677_SEL_MONO_ADC_L1_MASK            (0x3 << 12)
+#define RT5677_SEL_MONO_ADC_L1_SFT             12
+#define RT5677_SEL_MONO_ADC_L2_MASK            (0x3 << 10)
+#define RT5677_SEL_MONO_ADC_L2_SFT             10
+#define RT5677_SEL_MONO_DMIC_L_MASK            (0x3 << 8)
+#define RT5677_SEL_MONO_DMIC_L_SFT             8
+#define RT5677_M_MONO_ADC_R1                   (0x1 << 7)
+#define RT5677_M_MONO_ADC_R1_SFT               7
+#define RT5677_M_MONO_ADC_R2                   (0x1 << 6)
+#define RT5677_M_MONO_ADC_R2_SFT               6
+#define RT5677_SEL_MONO_ADC_R1_MASK            (0x3 << 4)
+#define RT5677_SEL_MONO_ADC_R1_SFT             4
+#define RT5677_SEL_MONO_ADC_R2_MASK            (0x3 << 2)
+#define RT5677_SEL_MONO_ADC_R2_SFT             2
+#define RT5677_SEL_MONO_DMIC_R_MASK            (0x3 << 0)
+#define RT5677_SEL_MONO_DMIC_R_SFT             0
+
+/* ADC/IF/DSP to DAC1 Mixer control (0x29) */
+#define RT5677_M_ADDA_MIXER1_L                 (0x1 << 15)
+#define RT5677_M_ADDA_MIXER1_L_SFT             15
+#define RT5677_M_DAC1_L                                (0x1 << 14)
+#define RT5677_M_DAC1_L_SFT                    14
+#define RT5677_DAC1_L_SEL_MASK                 (0x7 << 8)
+#define RT5677_DAC1_L_SEL_SFT                  8
+#define RT5677_M_ADDA_MIXER1_R                 (0x1 << 7)
+#define RT5677_M_ADDA_MIXER1_R_SFT             7
+#define RT5677_M_DAC1_R                                (0x1 << 6)
+#define RT5677_M_DAC1_R_SFT                    6
+#define RT5677_ADDA1_SEL_MASK                  (0x3 << 0)
+#define RT5677_ADDA1_SEL_SFT                   0
+
+/* Stereo1 DAC Mixer L/R Control (0x2a) */
+#define RT5677_M_ST_DAC1_L                     (0x1 << 15)
+#define RT5677_M_ST_DAC1_L_SFT                 15
+#define RT5677_M_DAC1_L_STO_L                  (0x1 << 13)
+#define RT5677_M_DAC1_L_STO_L_SFT              13
+#define RT5677_DAC1_L_STO_L_VOL_MASK           (0x1 << 12)
+#define RT5677_DAC1_L_STO_L_VOL_SFT            12
+#define RT5677_M_DAC2_L_STO_L                  (0x1 << 11)
+#define RT5677_M_DAC2_L_STO_L_SFT              11
+#define RT5677_DAC2_L_STO_L_VOL_MASK           (0x1 << 10)
+#define RT5677_DAC2_L_STO_L_VOL_SFT            10
+#define RT5677_M_DAC1_R_STO_L                  (0x1 << 9)
+#define RT5677_M_DAC1_R_STO_L_SFT              9
+#define RT5677_DAC1_R_STO_L_VOL_MASK           (0x1 << 8)
+#define RT5677_DAC1_R_STO_L_VOL_SFT            8
+#define RT5677_M_ST_DAC1_R                     (0x1 << 7)
+#define RT5677_M_ST_DAC1_R_SFT                 7
+#define RT5677_M_DAC1_R_STO_R                  (0x1 << 5)
+#define RT5677_M_DAC1_R_STO_R_SFT              5
+#define RT5677_DAC1_R_STO_R_VOL_MASK           (0x1 << 4)
+#define RT5677_DAC1_R_STO_R_VOL_SFT            4
+#define RT5677_M_DAC2_R_STO_R                  (0x1 << 3)
+#define RT5677_M_DAC2_R_STO_R_SFT              3
+#define RT5677_DAC2_R_STO_R_VOL_MASK           (0x1 << 2)
+#define RT5677_DAC2_R_STO_R_VOL_SFT            2
+#define RT5677_M_DAC1_L_STO_R                  (0x1 << 1)
+#define RT5677_M_DAC1_L_STO_R_SFT              1
+#define RT5677_DAC1_L_STO_R_VOL_MASK           (0x1 << 0)
+#define RT5677_DAC1_L_STO_R_VOL_SFT            0
+
+/* Mono DAC Mixer L/R Control (0x2b) */
+#define RT5677_M_ST_DAC2_L                     (0x1 << 15)
+#define RT5677_M_ST_DAC2_L_SFT                 15
+#define RT5677_M_DAC2_L_MONO_L                 (0x1 << 13)
+#define RT5677_M_DAC2_L_MONO_L_SFT             13
+#define RT5677_DAC2_L_MONO_L_VOL_MASK          (0x1 << 12)
+#define RT5677_DAC2_L_MONO_L_VOL_SFT           12
+#define RT5677_M_DAC2_R_MONO_L                 (0x1 << 11)
+#define RT5677_M_DAC2_R_MONO_L_SFT             11
+#define RT5677_DAC2_R_MONO_L_VOL_MASK          (0x1 << 10)
+#define RT5677_DAC2_R_MONO_L_VOL_SFT           10
+#define RT5677_M_DAC1_L_MONO_L                 (0x1 << 9)
+#define RT5677_M_DAC1_L_MONO_L_SFT             9
+#define RT5677_DAC1_L_MONO_L_VOL_MASK          (0x1 << 8)
+#define RT5677_DAC1_L_MONO_L_VOL_SFT           8
+#define RT5677_M_ST_DAC2_R                     (0x1 << 7)
+#define RT5677_M_ST_DAC2_R_SFT                 7
+#define RT5677_M_DAC2_R_MONO_R                 (0x1 << 5)
+#define RT5677_M_DAC2_R_MONO_R_SFT             5
+#define RT5677_DAC2_R_MONO_R_VOL_MASK          (0x1 << 4)
+#define RT5677_DAC2_R_MONO_R_VOL_SFT           4
+#define RT5677_M_DAC1_R_MONO_R                 (0x1 << 3)
+#define RT5677_M_DAC1_R_MONO_R_SFT             3
+#define RT5677_DAC1_R_MONO_R_VOL_MASK          (0x1 << 2)
+#define RT5677_DAC1_R_MONO_R_VOL_SFT           2
+#define RT5677_M_DAC2_L_MONO_R                 (0x1 << 1)
+#define RT5677_M_DAC2_L_MONO_R_SFT             1
+#define RT5677_DAC2_L_MONO_R_VOL_MASK          (0x1 << 0)
+#define RT5677_DAC2_L_MONO_R_VOL_SFT           0
+
+/* DD Mixer 1 Control (0x2c) */
+#define RT5677_M_STO_L_DD1_L                   (0x1 << 15)
+#define RT5677_M_STO_L_DD1_L_SFT               15
+#define RT5677_STO_L_DD1_L_VOL_MASK            (0x1 << 14)
+#define RT5677_STO_L_DD1_L_VOL_SFT             14
+#define RT5677_M_MONO_L_DD1_L                  (0x1 << 13)
+#define RT5677_M_MONO_L_DD1_L_SFT              13
+#define RT5677_MONO_L_DD1_L_VOL_MASK           (0x1 << 12)
+#define RT5677_MONO_L_DD1_L_VOL_SFT            12
+#define RT5677_M_DAC3_L_DD1_L                  (0x1 << 11)
+#define RT5677_M_DAC3_L_DD1_L_SFT              11
+#define RT5677_DAC3_L_DD1_L_VOL_MASK           (0x1 << 10)
+#define RT5677_DAC3_L_DD1_L_VOL_SFT            10
+#define RT5677_M_DAC3_R_DD1_L                  (0x1 << 9)
+#define RT5677_M_DAC3_R_DD1_L_SFT              9
+#define RT5677_DAC3_R_DD1_L_VOL_MASK           (0x1 << 8)
+#define RT5677_DAC3_R_DD1_L_VOL_SFT            8
+#define RT5677_M_STO_R_DD1_R                   (0x1 << 7)
+#define RT5677_M_STO_R_DD1_R_SFT               7
+#define RT5677_STO_R_DD1_R_VOL_MASK            (0x1 << 6)
+#define RT5677_STO_R_DD1_R_VOL_SFT             6
+#define RT5677_M_MONO_R_DD1_R                  (0x1 << 5)
+#define RT5677_M_MONO_R_DD1_R_SFT              5
+#define RT5677_MONO_R_DD1_R_VOL_MASK           (0x1 << 4)
+#define RT5677_MONO_R_DD1_R_VOL_SFT            4
+#define RT5677_M_DAC3_R_DD1_R                  (0x1 << 3)
+#define RT5677_M_DAC3_R_DD1_R_SFT              3
+#define RT5677_DAC3_R_DD1_R_VOL_MASK           (0x1 << 2)
+#define RT5677_DAC3_R_DD1_R_VOL_SFT            2
+#define RT5677_M_DAC3_L_DD1_R                  (0x1 << 1)
+#define RT5677_M_DAC3_L_DD1_R_SFT              1
+#define RT5677_DAC3_L_DD1_R_VOL_MASK           (0x1 << 0)
+#define RT5677_DAC3_L_DD1_R_VOL_SFT            0
+
+/* DD Mixer 2 Control (0x2d) */
+#define RT5677_M_STO_L_DD2_L                   (0x1 << 15)
+#define RT5677_M_STO_L_DD2_L_SFT               15
+#define RT5677_STO_L_DD2_L_VOL_MASK            (0x1 << 14)
+#define RT5677_STO_L_DD2_L_VOL_SFT             14
+#define RT5677_M_MONO_L_DD2_L                  (0x1 << 13)
+#define RT5677_M_MONO_L_DD2_L_SFT              13
+#define RT5677_MONO_L_DD2_L_VOL_MASK           (0x1 << 12)
+#define RT5677_MONO_L_DD2_L_VOL_SFT            12
+#define RT5677_M_DAC4_L_DD2_L                  (0x1 << 11)
+#define RT5677_M_DAC4_L_DD2_L_SFT              11
+#define RT5677_DAC4_L_DD2_L_VOL_MASK           (0x1 << 10)
+#define RT5677_DAC4_L_DD2_L_VOL_SFT            10
+#define RT5677_M_DAC4_R_DD2_L                  (0x1 << 9)
+#define RT5677_M_DAC4_R_DD2_L_SFT              9
+#define RT5677_DAC4_R_DD2_L_VOL_MASK           (0x1 << 8)
+#define RT5677_DAC4_R_DD2_L_VOL_SFT            8
+#define RT5677_M_STO_R_DD2_R                   (0x1 << 7)
+#define RT5677_M_STO_R_DD2_R_SFT               7
+#define RT5677_STO_R_DD2_R_VOL_MASK            (0x1 << 6)
+#define RT5677_STO_R_DD2_R_VOL_SFT             6
+#define RT5677_M_MONO_R_DD2_R                  (0x1 << 5)
+#define RT5677_M_MONO_R_DD2_R_SFT              5
+#define RT5677_MONO_R_DD2_R_VOL_MASK           (0x1 << 4)
+#define RT5677_MONO_R_DD2_R_VOL_SFT            4
+#define RT5677_M_DAC4_R_DD2_R                  (0x1 << 3)
+#define RT5677_M_DAC4_R_DD2_R_SFT              3
+#define RT5677_DAC4_R_DD2_R_VOL_MASK           (0x1 << 2)
+#define RT5677_DAC4_R_DD2_R_VOL_SFT            2
+#define RT5677_M_DAC4_L_DD2_R                  (0x1 << 1)
+#define RT5677_M_DAC4_L_DD2_R_SFT              1
+#define RT5677_DAC4_L_DD2_R_VOL_MASK           (0x1 << 0)
+#define RT5677_DAC4_L_DD2_R_VOL_SFT            0
+
+/* IF3 data control (0x2f) */
+#define RT5677_IF3_DAC_SEL_MASK                        (0x3 << 6)
+#define RT5677_IF3_DAC_SEL_SFT                 6
+#define RT5677_IF3_ADC_SEL_MASK                        (0x3 << 4)
+#define RT5677_IF3_ADC_SEL_SFT                 4
+#define RT5677_IF3_ADC_IN_MASK                 (0xf << 0)
+#define RT5677_IF3_ADC_IN_SFT                  0
+
+/* IF4 data control (0x30) */
+#define RT5677_IF4_ADC_IN_MASK                 (0xf << 4)
+#define RT5677_IF4_ADC_IN_SFT                  4
+#define RT5677_IF4_DAC_SEL_MASK                        (0x3 << 2)
+#define RT5677_IF4_DAC_SEL_SFT                 2
+#define RT5677_IF4_ADC_SEL_MASK                        (0x3 << 0)
+#define RT5677_IF4_ADC_SEL_SFT                 0
+
+/* PDM Output Control (0x31) */
+#define RT5677_M_PDM1_L                                (0x1 << 15)
+#define RT5677_M_PDM1_L_SFT                    15
+#define RT5677_SEL_PDM1_L_MASK                 (0x3 << 12)
+#define RT5677_SEL_PDM1_L_SFT                  12
+#define RT5677_M_PDM1_R                                (0x1 << 11)
+#define RT5677_M_PDM1_R_SFT                    11
+#define RT5677_SEL_PDM1_R_MASK                 (0x3 << 8)
+#define RT5677_SEL_PDM1_R_SFT                  8
+#define RT5677_M_PDM2_L                                (0x1 << 7)
+#define RT5677_M_PDM2_L_SFT                    7
+#define RT5677_SEL_PDM2_L_MASK                 (0x3 << 4)
+#define RT5677_SEL_PDM2_L_SFT                  4
+#define RT5677_M_PDM2_R                                (0x1 << 3)
+#define RT5677_M_PDM2_R_SFT                    3
+#define RT5677_SEL_PDM2_R_MASK                 (0x3 << 0)
+#define RT5677_SEL_PDM2_R_SFT                  0
+
+/* PDM I2C / Data Control 1 (0x32) */
+#define RT5677_PDM2_PW_DOWN                    (0x1 << 7)
+#define RT5677_PDM1_PW_DOWN                    (0x1 << 6)
+#define RT5677_PDM2_BUSY                       (0x1 << 5)
+#define RT5677_PDM1_BUSY                       (0x1 << 4)
+#define RT5677_PDM_PATTERN                     (0x1 << 3)
+#define RT5677_PDM_GAIN                                (0x1 << 2)
+#define RT5677_PDM_DIV_MASK                    (0x3 << 0)
+
+/* PDM I2C / Data Control 2 (0x33) */
+#define RT5677_PDM1_I2C_ID                     (0xf << 12)
+#define RT5677_PDM1_EXE                                (0x1 << 11)
+#define RT5677_PDM1_I2C_CMD                    (0x1 << 10)
+#define RT5677_PDM1_I2C_EXE                    (0x1 << 9)
+#define RT5677_PDM1_I2C_BUSY                   (0x1 << 8)
+#define RT5677_PDM2_I2C_ID                     (0xf << 4)
+#define RT5677_PDM2_EXE                                (0x1 << 3)
+#define RT5677_PDM2_I2C_CMD                    (0x1 << 2)
+#define RT5677_PDM2_I2C_EXE                    (0x1 << 1)
+#define RT5677_PDM2_I2C_BUSY                   (0x1 << 0)
+
+/* MX3C TDM1 control 1 (0x3c) */
+#define RT5677_IF1_ADC4_MASK                   (0x3 << 10)
+#define RT5677_IF1_ADC4_SFT                    10
+#define RT5677_IF1_ADC3_MASK                   (0x3 << 8)
+#define RT5677_IF1_ADC3_SFT                    8
+#define RT5677_IF1_ADC2_MASK                   (0x3 << 6)
+#define RT5677_IF1_ADC2_SFT                    6
+#define RT5677_IF1_ADC1_MASK                   (0x3 << 4)
+#define RT5677_IF1_ADC1_SFT                    4
+
+/* MX41 TDM2 control 1 (0x41) */
+#define RT5677_IF2_ADC4_MASK                   (0x3 << 10)
+#define RT5677_IF2_ADC4_SFT                    10
+#define RT5677_IF2_ADC3_MASK                   (0x3 << 8)
+#define RT5677_IF2_ADC3_SFT                    8
+#define RT5677_IF2_ADC2_MASK                   (0x3 << 6)
+#define RT5677_IF2_ADC2_SFT                    6
+#define RT5677_IF2_ADC1_MASK                   (0x3 << 4)
+#define RT5677_IF2_ADC1_SFT                    4
+
+/* Digital Microphone Control 1 (0x50) */
+#define RT5677_DMIC_1_EN_MASK                  (0x1 << 15)
+#define RT5677_DMIC_1_EN_SFT                   15
+#define RT5677_DMIC_1_DIS                      (0x0 << 15)
+#define RT5677_DMIC_1_EN                       (0x1 << 15)
+#define RT5677_DMIC_2_EN_MASK                  (0x1 << 14)
+#define RT5677_DMIC_2_EN_SFT                   14
+#define RT5677_DMIC_2_DIS                      (0x0 << 14)
+#define RT5677_DMIC_2_EN                       (0x1 << 14)
+#define RT5677_DMIC_L_STO1_LH_MASK             (0x1 << 13)
+#define RT5677_DMIC_L_STO1_LH_SFT              13
+#define RT5677_DMIC_L_STO1_LH_FALLING          (0x0 << 13)
+#define RT5677_DMIC_L_STO1_LH_RISING           (0x1 << 13)
+#define RT5677_DMIC_R_STO1_LH_MASK             (0x1 << 12)
+#define RT5677_DMIC_R_STO1_LH_SFT              12
+#define RT5677_DMIC_R_STO1_LH_FALLING          (0x0 << 12)
+#define RT5677_DMIC_R_STO1_LH_RISING           (0x1 << 12)
+#define RT5677_DMIC_L_STO3_LH_MASK             (0x1 << 11)
+#define RT5677_DMIC_L_STO3_LH_SFT              11
+#define RT5677_DMIC_L_STO3_LH_FALLING          (0x0 << 11)
+#define RT5677_DMIC_L_STO3_LH_RISING           (0x1 << 11)
+#define RT5677_DMIC_R_STO3_LH_MASK             (0x1 << 10)
+#define RT5677_DMIC_R_STO3_LH_SFT              10
+#define RT5677_DMIC_R_STO3_LH_FALLING          (0x0 << 10)
+#define RT5677_DMIC_R_STO3_LH_RISING           (0x1 << 10)
+#define RT5677_DMIC_L_STO2_LH_MASK             (0x1 << 9)
+#define RT5677_DMIC_L_STO2_LH_SFT              9
+#define RT5677_DMIC_L_STO2_LH_FALLING          (0x0 << 9)
+#define RT5677_DMIC_L_STO2_LH_RISING           (0x1 << 9)
+#define RT5677_DMIC_R_STO2_LH_MASK             (0x1 << 8)
+#define RT5677_DMIC_R_STO2_LH_SFT              8
+#define RT5677_DMIC_R_STO2_LH_FALLING          (0x0 << 8)
+#define RT5677_DMIC_R_STO2_LH_RISING           (0x1 << 8)
+#define RT5677_DMIC_CLK_MASK                   (0x7 << 5)
+#define RT5677_DMIC_CLK_SFT                    5
+#define RT5677_DMIC_3_EN_MASK                  (0x1 << 4)
+#define RT5677_DMIC_3_EN_SFT                   4
+#define RT5677_DMIC_3_DIS                      (0x0 << 4)
+#define RT5677_DMIC_3_EN                       (0x1 << 4)
+#define RT5677_DMIC_R_MONO_LH_MASK             (0x1 << 2)
+#define RT5677_DMIC_R_MONO_LH_SFT              2
+#define RT5677_DMIC_R_MONO_LH_FALLING          (0x0 << 2)
+#define RT5677_DMIC_R_MONO_LH_RISING           (0x1 << 2)
+#define RT5677_DMIC_L_STO4_LH_MASK             (0x1 << 1)
+#define RT5677_DMIC_L_STO4_LH_SFT              1
+#define RT5677_DMIC_L_STO4_LH_FALLING          (0x0 << 1)
+#define RT5677_DMIC_L_STO4_LH_RISING           (0x1 << 1)
+#define RT5677_DMIC_R_STO4_LH_MASK             (0x1 << 0)
+#define RT5677_DMIC_R_STO4_LH_SFT              0
+#define RT5677_DMIC_R_STO4_LH_FALLING          (0x0 << 0)
+#define RT5677_DMIC_R_STO4_LH_RISING           (0x1 << 0)
+
+/* Digital Microphone Control 2 (0x51) */
+#define RT5677_DMIC_4_EN_MASK                  (0x1 << 15)
+#define RT5677_DMIC_4_EN_SFT                   15
+#define RT5677_DMIC_4_DIS                      (0x0 << 15)
+#define RT5677_DMIC_4_EN                       (0x1 << 15)
+#define RT5677_DMIC_4L_LH_MASK                 (0x1 << 7)
+#define RT5677_DMIC_4L_LH_SFT                  7
+#define RT5677_DMIC_4L_LH_FALLING              (0x0 << 7)
+#define RT5677_DMIC_4L_LH_RISING               (0x1 << 7)
+#define RT5677_DMIC_4R_LH_MASK                 (0x1 << 6)
+#define RT5677_DMIC_4R_LH_SFT                  6
+#define RT5677_DMIC_4R_LH_FALLING              (0x0 << 6)
+#define RT5677_DMIC_4R_LH_RISING               (0x1 << 6)
+#define RT5677_DMIC_3L_LH_MASK                 (0x1 << 5)
+#define RT5677_DMIC_3L_LH_SFT                  5
+#define RT5677_DMIC_3L_LH_FALLING              (0x0 << 5)
+#define RT5677_DMIC_3L_LH_RISING               (0x1 << 5)
+#define RT5677_DMIC_3R_LH_MASK                 (0x1 << 4)
+#define RT5677_DMIC_3R_LH_SFT                  4
+#define RT5677_DMIC_3R_LH_FALLING              (0x0 << 4)
+#define RT5677_DMIC_3R_LH_RISING               (0x1 << 4)
+#define RT5677_DMIC_2L_LH_MASK                 (0x1 << 3)
+#define RT5677_DMIC_2L_LH_SFT                  3
+#define RT5677_DMIC_2L_LH_FALLING              (0x0 << 3)
+#define RT5677_DMIC_2L_LH_RISING               (0x1 << 3)
+#define RT5677_DMIC_2R_LH_MASK                 (0x1 << 2)
+#define RT5677_DMIC_2R_LH_SFT                  2
+#define RT5677_DMIC_2R_LH_FALLING              (0x0 << 2)
+#define RT5677_DMIC_2R_LH_RISING               (0x1 << 2)
+#define RT5677_DMIC_1L_LH_MASK                 (0x1 << 1)
+#define RT5677_DMIC_1L_LH_SFT                  1
+#define RT5677_DMIC_1L_LH_FALLING              (0x0 << 1)
+#define RT5677_DMIC_1L_LH_RISING               (0x1 << 1)
+#define RT5677_DMIC_1R_LH_MASK                 (0x1 << 0)
+#define RT5677_DMIC_1R_LH_SFT                  0
+#define RT5677_DMIC_1R_LH_FALLING              (0x0 << 0)
+#define RT5677_DMIC_1R_LH_RISING               (0x1 << 0)
+
+/* Power Management for Digital 1 (0x61) */
+#define RT5677_PWR_I2S1                                (0x1 << 15)
+#define RT5677_PWR_I2S1_BIT                    15
+#define RT5677_PWR_I2S2                                (0x1 << 14)
+#define RT5677_PWR_I2S2_BIT                    14
+#define RT5677_PWR_I2S3                                (0x1 << 13)
+#define RT5677_PWR_I2S3_BIT                    13
+#define RT5677_PWR_DAC1                                (0x1 << 12)
+#define RT5677_PWR_DAC1_BIT                    12
+#define RT5677_PWR_DAC2                                (0x1 << 11)
+#define RT5677_PWR_DAC2_BIT                    11
+#define RT5677_PWR_I2S4                                (0x1 << 10)
+#define RT5677_PWR_I2S4_BIT                    10
+#define RT5677_PWR_SLB                         (0x1 << 9)
+#define RT5677_PWR_SLB_BIT                     9
+#define RT5677_PWR_DAC3                                (0x1 << 7)
+#define RT5677_PWR_DAC3_BIT                    7
+#define RT5677_PWR_ADCFED2                     (0x1 << 4)
+#define RT5677_PWR_ADCFED2_BIT                 4
+#define RT5677_PWR_ADCFED1                     (0x1 << 3)
+#define RT5677_PWR_ADCFED1_BIT                 3
+#define RT5677_PWR_ADC_L                       (0x1 << 2)
+#define RT5677_PWR_ADC_L_BIT                   2
+#define RT5677_PWR_ADC_R                       (0x1 << 1)
+#define RT5677_PWR_ADC_R_BIT                   1
+#define RT5677_PWR_I2C_MASTER                  (0x1 << 0)
+#define RT5677_PWR_I2C_MASTER_BIT              0
+
+/* Power Management for Digital 2 (0x62) */
+#define RT5677_PWR_ADC_S1F                     (0x1 << 15)
+#define RT5677_PWR_ADC_S1F_BIT                 15
+#define RT5677_PWR_ADC_MF_L                    (0x1 << 14)
+#define RT5677_PWR_ADC_MF_L_BIT                        14
+#define RT5677_PWR_ADC_MF_R                    (0x1 << 13)
+#define RT5677_PWR_ADC_MF_R_BIT                        13
+#define RT5677_PWR_DAC_S1F                     (0x1 << 12)
+#define RT5677_PWR_DAC_S1F_BIT                 12
+#define RT5677_PWR_DAC_M2F_L                   (0x1 << 11)
+#define RT5677_PWR_DAC_M2F_L_BIT               11
+#define RT5677_PWR_DAC_M2F_R                   (0x1 << 10)
+#define RT5677_PWR_DAC_M2F_R_BIT               10
+#define RT5677_PWR_DAC_M3F_L                   (0x1 << 9)
+#define RT5677_PWR_DAC_M3F_L_BIT               9
+#define RT5677_PWR_DAC_M3F_R                   (0x1 << 8)
+#define RT5677_PWR_DAC_M3F_R_BIT               8
+#define RT5677_PWR_DAC_M4F_L                   (0x1 << 7)
+#define RT5677_PWR_DAC_M4F_L_BIT               7
+#define RT5677_PWR_DAC_M4F_R                   (0x1 << 6)
+#define RT5677_PWR_DAC_M4F_R_BIT               6
+#define RT5677_PWR_ADC_S2F                     (0x1 << 5)
+#define RT5677_PWR_ADC_S2F_BIT                 5
+#define RT5677_PWR_ADC_S3F                     (0x1 << 4)
+#define RT5677_PWR_ADC_S3F_BIT                 4
+#define RT5677_PWR_ADC_S4F                     (0x1 << 3)
+#define RT5677_PWR_ADC_S4F_BIT                 3
+#define RT5677_PWR_PDM1                                (0x1 << 2)
+#define RT5677_PWR_PDM1_BIT                    2
+#define RT5677_PWR_PDM2                                (0x1 << 1)
+#define RT5677_PWR_PDM2_BIT                    1
+
+/* Power Management for Analog 1 (0x63) */
+#define RT5677_PWR_VREF1                       (0x1 << 15)
+#define RT5677_PWR_VREF1_BIT                   15
+#define RT5677_PWR_FV1                         (0x1 << 14)
+#define RT5677_PWR_FV1_BIT                     14
+#define RT5677_PWR_MB                          (0x1 << 13)
+#define RT5677_PWR_MB_BIT                      13
+#define RT5677_PWR_LO1                         (0x1 << 12)
+#define RT5677_PWR_LO1_BIT                     12
+#define RT5677_PWR_BG                          (0x1 << 11)
+#define RT5677_PWR_BG_BIT                      11
+#define RT5677_PWR_LO2                         (0x1 << 10)
+#define RT5677_PWR_LO2_BIT                     10
+#define RT5677_PWR_LO3                         (0x1 << 9)
+#define RT5677_PWR_LO3_BIT                     9
+#define RT5677_PWR_VREF2                       (0x1 << 8)
+#define RT5677_PWR_VREF2_BIT                   8
+#define RT5677_PWR_FV2                         (0x1 << 7)
+#define RT5677_PWR_FV2_BIT                     7
+#define RT5677_LDO2_SEL_MASK                   (0x7 << 4)
+#define RT5677_LDO2_SEL_SFT                    4
+#define RT5677_LDO1_SEL_MASK                   (0x7 << 0)
+#define RT5677_LDO1_SEL_SFT                    0
+
+/* Power Management for Analog 2 (0x64) */
+#define RT5677_PWR_BST1                                (0x1 << 15)
+#define RT5677_PWR_BST1_BIT                    15
+#define RT5677_PWR_BST2                                (0x1 << 14)
+#define RT5677_PWR_BST2_BIT                    14
+#define RT5677_PWR_CLK_MB1                     (0x1 << 13)
+#define RT5677_PWR_CLK_MB1_BIT                 13
+#define RT5677_PWR_SLIM                                (0x1 << 12)
+#define RT5677_PWR_SLIM_BIT                    12
+#define RT5677_PWR_MB1                         (0x1 << 11)
+#define RT5677_PWR_MB1_BIT                     11
+#define RT5677_PWR_PP_MB1                      (0x1 << 10)
+#define RT5677_PWR_PP_MB1_BIT                  10
+#define RT5677_PWR_PLL1                                (0x1 << 9)
+#define RT5677_PWR_PLL1_BIT                    9
+#define RT5677_PWR_PLL2                                (0x1 << 8)
+#define RT5677_PWR_PLL2_BIT                    8
+#define RT5677_PWR_CORE                                (0x1 << 7)
+#define RT5677_PWR_CORE_BIT                    7
+#define RT5677_PWR_CLK_MB                      (0x1 << 6)
+#define RT5677_PWR_CLK_MB_BIT                  6
+#define RT5677_PWR_BST1_P                      (0x1 << 5)
+#define RT5677_PWR_BST1_P_BIT                  5
+#define RT5677_PWR_BST2_P                      (0x1 << 4)
+#define RT5677_PWR_BST2_P_BIT                  4
+#define RT5677_PWR_IPTV                                (0x1 << 3)
+#define RT5677_PWR_IPTV_BIT                    3
+#define RT5677_PWR_25M_CLK                     (0x1 << 1)
+#define RT5677_PWR_25M_CLK_BIT                 1
+#define RT5677_PWR_LDO1                                (0x1 << 0)
+#define RT5677_PWR_LDO1_BIT                    0
+
+/* Power Management for DSP (0x65) */
+#define RT5677_PWR_SR7                         (0x1 << 10)
+#define RT5677_PWR_SR7_BIT                     10
+#define RT5677_PWR_SR6                         (0x1 << 9)
+#define RT5677_PWR_SR6_BIT                     9
+#define RT5677_PWR_SR5                         (0x1 << 8)
+#define RT5677_PWR_SR5_BIT                     8
+#define RT5677_PWR_SR4                         (0x1 << 7)
+#define RT5677_PWR_SR4_BIT                     7
+#define RT5677_PWR_SR3                         (0x1 << 6)
+#define RT5677_PWR_SR3_BIT                     6
+#define RT5677_PWR_SR2                         (0x1 << 5)
+#define RT5677_PWR_SR2_BIT                     5
+#define RT5677_PWR_SR1                         (0x1 << 4)
+#define RT5677_PWR_SR1_BIT                     4
+#define RT5677_PWR_SR0                         (0x1 << 3)
+#define RT5677_PWR_SR0_BIT                     3
+#define RT5677_PWR_MLT                         (0x1 << 2)
+#define RT5677_PWR_MLT_BIT                     2
+#define RT5677_PWR_DSP                         (0x1 << 1)
+#define RT5677_PWR_DSP_BIT                     1
+#define RT5677_PWR_DSP_CPU                     (0x1 << 0)
+#define RT5677_PWR_DSP_CPU_BIT                 0
+
+/* Power Status for DSP (0x66) */
+#define RT5677_PWR_SR7_RDY                     (0x1 << 9)
+#define RT5677_PWR_SR7_RDY_BIT                 9
+#define RT5677_PWR_SR6_RDY                     (0x1 << 8)
+#define RT5677_PWR_SR6_RDY_BIT                 8
+#define RT5677_PWR_SR5_RDY                     (0x1 << 7)
+#define RT5677_PWR_SR5_RDY_BIT                 7
+#define RT5677_PWR_SR4_RDY                     (0x1 << 6)
+#define RT5677_PWR_SR4_RDY_BIT                 6
+#define RT5677_PWR_SR3_RDY                     (0x1 << 5)
+#define RT5677_PWR_SR3_RDY_BIT                 5
+#define RT5677_PWR_SR2_RDY                     (0x1 << 4)
+#define RT5677_PWR_SR2_RDY_BIT                 4
+#define RT5677_PWR_SR1_RDY                     (0x1 << 3)
+#define RT5677_PWR_SR1_RDY_BIT                 3
+#define RT5677_PWR_SR0_RDY                     (0x1 << 2)
+#define RT5677_PWR_SR0_RDY_BIT                 2
+#define RT5677_PWR_MLT_RDY                     (0x1 << 1)
+#define RT5677_PWR_MLT_RDY_BIT                 1
+#define RT5677_PWR_DSP_RDY                     (0x1 << 0)
+#define RT5677_PWR_DSP_RDY_BIT                 0
+
+/* Power Management for DSP (0x67) */
+#define RT5677_PWR_SLIM_ISO                    (0x1 << 11)
+#define RT5677_PWR_SLIM_ISO_BIT                        11
+#define RT5677_PWR_CORE_ISO                    (0x1 << 10)
+#define RT5677_PWR_CORE_ISO_BIT                        10
+#define RT5677_PWR_DSP_ISO                     (0x1 << 9)
+#define RT5677_PWR_DSP_ISO_BIT                 9
+#define RT5677_PWR_SR7_ISO                     (0x1 << 8)
+#define RT5677_PWR_SR7_ISO_BIT                 8
+#define RT5677_PWR_SR6_ISO                     (0x1 << 7)
+#define RT5677_PWR_SR6_ISO_BIT                 7
+#define RT5677_PWR_SR5_ISO                     (0x1 << 6)
+#define RT5677_PWR_SR5_ISO_BIT                 6
+#define RT5677_PWR_SR4_ISO                     (0x1 << 5)
+#define RT5677_PWR_SR4_ISO_BIT                 5
+#define RT5677_PWR_SR3_ISO                     (0x1 << 4)
+#define RT5677_PWR_SR3_ISO_BIT                 4
+#define RT5677_PWR_SR2_ISO                     (0x1 << 3)
+#define RT5677_PWR_SR2_ISO_BIT                 3
+#define RT5677_PWR_SR1_ISO                     (0x1 << 2)
+#define RT5677_PWR_SR1_ISO_BIT                 2
+#define RT5677_PWR_SR0_ISO                     (0x1 << 1)
+#define RT5677_PWR_SR0_ISO_BIT                 1
+#define RT5677_PWR_MLT_ISO                     (0x1 << 0)
+#define RT5677_PWR_MLT_ISO_BIT                 0
+
+/* I2S1/2/3/4 Audio Serial Data Port Control (0x6f 0x70 0x71 0x72) */
+#define RT5677_I2S_MS_MASK                     (0x1 << 15)
+#define RT5677_I2S_MS_SFT                      15
+#define RT5677_I2S_MS_M                                (0x0 << 15)
+#define RT5677_I2S_MS_S                                (0x1 << 15)
+#define RT5677_I2S_O_CP_MASK                   (0x3 << 10)
+#define RT5677_I2S_O_CP_SFT                    10
+#define RT5677_I2S_O_CP_OFF                    (0x0 << 10)
+#define RT5677_I2S_O_CP_U_LAW                  (0x1 << 10)
+#define RT5677_I2S_O_CP_A_LAW                  (0x2 << 10)
+#define RT5677_I2S_I_CP_MASK                   (0x3 << 8)
+#define RT5677_I2S_I_CP_SFT                    8
+#define RT5677_I2S_I_CP_OFF                    (0x0 << 8)
+#define RT5677_I2S_I_CP_U_LAW                  (0x1 << 8)
+#define RT5677_I2S_I_CP_A_LAW                  (0x2 << 8)
+#define RT5677_I2S_BP_MASK                     (0x1 << 7)
+#define RT5677_I2S_BP_SFT                      7
+#define RT5677_I2S_BP_NOR                      (0x0 << 7)
+#define RT5677_I2S_BP_INV                      (0x1 << 7)
+#define RT5677_I2S_DL_MASK                     (0x3 << 2)
+#define RT5677_I2S_DL_SFT                      2
+#define RT5677_I2S_DL_16                       (0x0 << 2)
+#define RT5677_I2S_DL_20                       (0x1 << 2)
+#define RT5677_I2S_DL_24                       (0x2 << 2)
+#define RT5677_I2S_DL_8                                (0x3 << 2)
+#define RT5677_I2S_DF_MASK                     (0x3 << 0)
+#define RT5677_I2S_DF_SFT                      0
+#define RT5677_I2S_DF_I2S                      (0x0 << 0)
+#define RT5677_I2S_DF_LEFT                     (0x1 << 0)
+#define RT5677_I2S_DF_PCM_A                    (0x2 << 0)
+#define RT5677_I2S_DF_PCM_B                    (0x3 << 0)
+
+/* Clock Tree Control 1 (0x73) */
+#define RT5677_I2S_PD1_MASK                    (0x7 << 12)
+#define RT5677_I2S_PD1_SFT                     12
+#define RT5677_I2S_PD1_1                       (0x0 << 12)
+#define RT5677_I2S_PD1_2                       (0x1 << 12)
+#define RT5677_I2S_PD1_3                       (0x2 << 12)
+#define RT5677_I2S_PD1_4                       (0x3 << 12)
+#define RT5677_I2S_PD1_6                       (0x4 << 12)
+#define RT5677_I2S_PD1_8                       (0x5 << 12)
+#define RT5677_I2S_PD1_12                      (0x6 << 12)
+#define RT5677_I2S_PD1_16                      (0x7 << 12)
+#define RT5677_I2S_BCLK_MS2_MASK               (0x1 << 11)
+#define RT5677_I2S_BCLK_MS2_SFT                        11
+#define RT5677_I2S_BCLK_MS2_32                 (0x0 << 11)
+#define RT5677_I2S_BCLK_MS2_64                 (0x1 << 11)
+#define RT5677_I2S_PD2_MASK                    (0x7 << 8)
+#define RT5677_I2S_PD2_SFT                     8
+#define RT5677_I2S_PD2_1                       (0x0 << 8)
+#define RT5677_I2S_PD2_2                       (0x1 << 8)
+#define RT5677_I2S_PD2_3                       (0x2 << 8)
+#define RT5677_I2S_PD2_4                       (0x3 << 8)
+#define RT5677_I2S_PD2_6                       (0x4 << 8)
+#define RT5677_I2S_PD2_8                       (0x5 << 8)
+#define RT5677_I2S_PD2_12                      (0x6 << 8)
+#define RT5677_I2S_PD2_16                      (0x7 << 8)
+#define RT5677_I2S_BCLK_MS3_MASK               (0x1 << 7)
+#define RT5677_I2S_BCLK_MS3_SFT                        7
+#define RT5677_I2S_BCLK_MS3_32                 (0x0 << 7)
+#define RT5677_I2S_BCLK_MS3_64                 (0x1 << 7)
+#define RT5677_I2S_PD3_MASK                    (0x7 << 4)
+#define RT5677_I2S_PD3_SFT                     4
+#define RT5677_I2S_PD3_1                       (0x0 << 4)
+#define RT5677_I2S_PD3_2                       (0x1 << 4)
+#define RT5677_I2S_PD3_3                       (0x2 << 4)
+#define RT5677_I2S_PD3_4                       (0x3 << 4)
+#define RT5677_I2S_PD3_6                       (0x4 << 4)
+#define RT5677_I2S_PD3_8                       (0x5 << 4)
+#define RT5677_I2S_PD3_12                      (0x6 << 4)
+#define RT5677_I2S_PD3_16                      (0x7 << 4)
+#define RT5677_I2S_BCLK_MS4_MASK               (0x1 << 3)
+#define RT5677_I2S_BCLK_MS4_SFT                        3
+#define RT5677_I2S_BCLK_MS4_32                 (0x0 << 3)
+#define RT5677_I2S_BCLK_MS4_64                 (0x1 << 3)
+#define RT5677_I2S_PD4_MASK                    (0x7 << 0)
+#define RT5677_I2S_PD4_SFT                     0
+#define RT5677_I2S_PD4_1                       (0x0 << 0)
+#define RT5677_I2S_PD4_2                       (0x1 << 0)
+#define RT5677_I2S_PD4_3                       (0x2 << 0)
+#define RT5677_I2S_PD4_4                       (0x3 << 0)
+#define RT5677_I2S_PD4_6                       (0x4 << 0)
+#define RT5677_I2S_PD4_8                       (0x5 << 0)
+#define RT5677_I2S_PD4_12                      (0x6 << 0)
+#define RT5677_I2S_PD4_16                      (0x7 << 0)
+
+/* Clock Tree Control 2 (0x74) */
+#define RT5677_I2S_PD5_MASK                    (0x7 << 12)
+#define RT5677_I2S_PD5_SFT                     12
+#define RT5677_I2S_PD5_1                       (0x0 << 12)
+#define RT5677_I2S_PD5_2                       (0x1 << 12)
+#define RT5677_I2S_PD5_3                       (0x2 << 12)
+#define RT5677_I2S_PD5_4                       (0x3 << 12)
+#define RT5677_I2S_PD5_6                       (0x4 << 12)
+#define RT5677_I2S_PD5_8                       (0x5 << 12)
+#define RT5677_I2S_PD5_12                      (0x6 << 12)
+#define RT5677_I2S_PD5_16                      (0x7 << 12)
+#define RT5677_I2S_PD6_MASK                    (0x7 << 8)
+#define RT5677_I2S_PD6_SFT                     8
+#define RT5677_I2S_PD6_1                       (0x0 << 8)
+#define RT5677_I2S_PD6_2                       (0x1 << 8)
+#define RT5677_I2S_PD6_3                       (0x2 << 8)
+#define RT5677_I2S_PD6_4                       (0x3 << 8)
+#define RT5677_I2S_PD6_6                       (0x4 << 8)
+#define RT5677_I2S_PD6_8                       (0x5 << 8)
+#define RT5677_I2S_PD6_12                      (0x6 << 8)
+#define RT5677_I2S_PD6_16                      (0x7 << 8)
+#define RT5677_I2S_PD7_MASK                    (0x7 << 4)
+#define RT5677_I2S_PD7_SFT                     4
+#define RT5677_I2S_PD7_1                       (0x0 << 4)
+#define RT5677_I2S_PD7_2                       (0x1 << 4)
+#define RT5677_I2S_PD7_3                       (0x2 << 4)
+#define RT5677_I2S_PD7_4                       (0x3 << 4)
+#define RT5677_I2S_PD7_6                       (0x4 << 4)
+#define RT5677_I2S_PD7_8                       (0x5 << 4)
+#define RT5677_I2S_PD7_12                      (0x6 << 4)
+#define RT5677_I2S_PD7_16                      (0x7 << 4)
+#define RT5677_I2S_PD8_MASK                    (0x7 << 0)
+#define RT5677_I2S_PD8_SFT                     0
+#define RT5677_I2S_PD8_1                       (0x0 << 0)
+#define RT5677_I2S_PD8_2                       (0x1 << 0)
+#define RT5677_I2S_PD8_3                       (0x2 << 0)
+#define RT5677_I2S_PD8_4                       (0x3 << 0)
+#define RT5677_I2S_PD8_6                       (0x4 << 0)
+#define RT5677_I2S_PD8_8                       (0x5 << 0)
+#define RT5677_I2S_PD8_12                      (0x6 << 0)
+#define RT5677_I2S_PD8_16                      (0x7 << 0)
+
+/* Clock Tree Control 3 (0x75) */
+#define RT5677_DSP_ASRC_O_MASK                 (0x3 << 6)
+#define RT5677_DSP_ASRC_O_SFT                  6
+#define RT5677_DSP_ASRC_O_1_0                  (0x0 << 6)
+#define RT5677_DSP_ASRC_O_1_5                  (0x1 << 6)
+#define RT5677_DSP_ASRC_O_2_0                  (0x2 << 6)
+#define RT5677_DSP_ASRC_O_3_0                  (0x3 << 6)
+#define RT5677_DSP_ASRC_I_MASK                 (0x3 << 4)
+#define RT5677_DSP_ASRC_I_SFT                  4
+#define RT5677_DSP_ASRC_I_1_0                  (0x0 << 4)
+#define RT5677_DSP_ASRC_I_1_5                  (0x1 << 4)
+#define RT5677_DSP_ASRC_I_2_0                  (0x2 << 4)
+#define RT5677_DSP_ASRC_I_3_0                  (0x3 << 4)
+#define RT5677_DSP_BUS_PD_MASK                 (0x7 << 0)
+#define RT5677_DSP_BUS_PD_SFT                  0
+#define RT5677_DSP_BUS_PD_1                    (0x0 << 0)
+#define RT5677_DSP_BUS_PD_2                    (0x1 << 0)
+#define RT5677_DSP_BUS_PD_3                    (0x2 << 0)
+#define RT5677_DSP_BUS_PD_4                    (0x3 << 0)
+#define RT5677_DSP_BUS_PD_6                    (0x4 << 0)
+#define RT5677_DSP_BUS_PD_8                    (0x5 << 0)
+#define RT5677_DSP_BUS_PD_12                   (0x6 << 0)
+#define RT5677_DSP_BUS_PD_16                   (0x7 << 0)
+
+#define RT5677_PLL_INP_MAX                     40000000
+#define RT5677_PLL_INP_MIN                     2048000
+/* PLL M/N/K Code Control 1 (0x7a 0x7c) */
+#define RT5677_PLL_N_MAX                       0x1ff
+#define RT5677_PLL_N_MASK                      (RT5677_PLL_N_MAX << 7)
+#define RT5677_PLL_N_SFT                       7
+#define RT5677_PLL_K_BP                                (0x1 << 5)
+#define RT5677_PLL_K_BP_SFT                    5
+#define RT5677_PLL_K_MAX                       0x1f
+#define RT5677_PLL_K_MASK                      (RT5677_PLL_K_MAX)
+#define RT5677_PLL_K_SFT                       0
+
+/* PLL M/N/K Code Control 2 (0x7b 0x7d) */
+#define RT5677_PLL_M_MAX                       0xf
+#define RT5677_PLL_M_MASK                      (RT5677_PLL_M_MAX << 12)
+#define RT5677_PLL_M_SFT                       12
+#define RT5677_PLL_M_BP                                (0x1 << 11)
+#define RT5677_PLL_M_BP_SFT                    11
+
+/* Global Clock Control 1 (0x80) */
+#define RT5677_SCLK_SRC_MASK                   (0x3 << 14)
+#define RT5677_SCLK_SRC_SFT                    14
+#define RT5677_SCLK_SRC_MCLK                   (0x0 << 14)
+#define RT5677_SCLK_SRC_PLL1                   (0x1 << 14)
+#define RT5677_SCLK_SRC_RCCLK                  (0x2 << 14) /* 25MHz */
+#define RT5677_SCLK_SRC_SLIM                   (0x3 << 14)
+#define RT5677_PLL1_SRC_MASK                   (0x7 << 11)
+#define RT5677_PLL1_SRC_SFT                    11
+#define RT5677_PLL1_SRC_MCLK                   (0x0 << 11)
+#define RT5677_PLL1_SRC_BCLK1                  (0x1 << 11)
+#define RT5677_PLL1_SRC_BCLK2                  (0x2 << 11)
+#define RT5677_PLL1_SRC_BCLK3                  (0x3 << 11)
+#define RT5677_PLL1_SRC_BCLK4                  (0x4 << 11)
+#define RT5677_PLL1_SRC_RCCLK                  (0x5 << 11)
+#define RT5677_PLL1_SRC_SLIM                   (0x6 << 11)
+#define RT5677_MCLK_SRC_MASK                   (0x1 << 10)
+#define RT5677_MCLK_SRC_SFT                    10
+#define RT5677_MCLK1_SRC                       (0x0 << 10)
+#define RT5677_MCLK2_SRC                       (0x1 << 10)
+#define RT5677_PLL1_PD_MASK                    (0x1 << 8)
+#define RT5677_PLL1_PD_SFT                     8
+#define RT5677_PLL1_PD_1                       (0x0 << 8)
+#define RT5677_PLL1_PD_2                       (0x1 << 8)
+#define RT5671_DAC_OSR_MASK                    (0x3 << 6)
+#define RT5671_DAC_OSR_SFT                     6
+#define RT5671_DAC_OSR_128                     (0x0 << 6)
+#define RT5671_DAC_OSR_64                      (0x1 << 6)
+#define RT5671_DAC_OSR_32                      (0x2 << 6)
+#define RT5671_ADC_OSR_MASK                    (0x3 << 4)
+#define RT5671_ADC_OSR_SFT                     4
+#define RT5671_ADC_OSR_128                     (0x0 << 4)
+#define RT5671_ADC_OSR_64                      (0x1 << 4)
+#define RT5671_ADC_OSR_32                      (0x2 << 4)
+
+/* Global Clock Control 2 (0x81) */
+#define RT5677_PLL2_PR_SRC_MASK                        (0x1 << 15)
+#define RT5677_PLL2_PR_SRC_SFT                 15
+#define RT5677_PLL2_PR_SRC_MCLK1               (0x0 << 15)
+#define RT5677_PLL2_PR_SRC_MCLK2               (0x1 << 15)
+#define RT5677_PLL2_SRC_MASK                   (0x7 << 12)
+#define RT5677_PLL2_SRC_SFT                    12
+#define RT5677_PLL2_SRC_MCLK                   (0x0 << 12)
+#define RT5677_PLL2_SRC_BCLK1                  (0x1 << 12)
+#define RT5677_PLL2_SRC_BCLK2                  (0x2 << 12)
+#define RT5677_PLL2_SRC_BCLK3                  (0x3 << 12)
+#define RT5677_PLL2_SRC_BCLK4                  (0x4 << 12)
+#define RT5677_PLL2_SRC_RCCLK                  (0x5 << 12)
+#define RT5677_PLL2_SRC_SLIM                   (0x6 << 12)
+#define RT5671_DSP_ASRC_O_SRC                  (0x3 << 10)
+#define RT5671_DSP_ASRC_O_SRC_SFT              10
+#define RT5671_DSP_ASRC_O_MCLK                 (0x0 << 10)
+#define RT5671_DSP_ASRC_O_PLL1                 (0x1 << 10)
+#define RT5671_DSP_ASRC_O_SLIM                 (0x2 << 10)
+#define RT5671_DSP_ASRC_O_RCCLK                        (0x3 << 10)
+#define RT5671_DSP_ASRC_I_SRC                  (0x3 << 8)
+#define RT5671_DSP_ASRC_I_SRC_SFT              8
+#define RT5671_DSP_ASRC_I_MCLK                 (0x0 << 8)
+#define RT5671_DSP_ASRC_I_PLL1                 (0x1 << 8)
+#define RT5671_DSP_ASRC_I_SLIM                 (0x2 << 8)
+#define RT5671_DSP_ASRC_I_RCCLK                        (0x3 << 8)
+#define RT5677_DSP_CLK_SRC_MASK                        (0x1 << 7)
+#define RT5677_DSP_CLK_SRC_SFT                 7
+#define RT5677_DSP_CLK_SRC_PLL2                        (0x0 << 7)
+#define RT5677_DSP_CLK_SRC_BYPASS              (0x1 << 7)
+
+/* VAD Function Control 4 (0x9f) */
+#define RT5677_VAD_SRC_MASK                    (0x7 << 8)
+#define RT5677_VAD_SRC_SFT                     8
+
+/* DSP InBound Control (0xa3) */
+#define RT5677_IB01_SRC_MASK                   (0x7 << 12)
+#define RT5677_IB01_SRC_SFT                    12
+#define RT5677_IB23_SRC_MASK                   (0x7 << 8)
+#define RT5677_IB23_SRC_SFT                    8
+#define RT5677_IB45_SRC_MASK                   (0x7 << 4)
+#define RT5677_IB45_SRC_SFT                    4
+#define RT5677_IB6_SRC_MASK                    (0x7 << 0)
+#define RT5677_IB6_SRC_SFT                     0
+
+/* DSP InBound Control (0xa4) */
+#define RT5677_IB7_SRC_MASK                    (0x7 << 12)
+#define RT5677_IB7_SRC_SFT                     12
+#define RT5677_IB8_SRC_MASK                    (0x7 << 8)
+#define RT5677_IB8_SRC_SFT                     8
+#define RT5677_IB9_SRC_MASK                    (0x7 << 4)
+#define RT5677_IB9_SRC_SFT                     4
+
+/* DSP In/OutBound Control (0xa5) */
+#define RT5677_SEL_SRC_OB23                    (0x1 << 4)
+#define RT5677_SEL_SRC_OB23_SFT                        4
+#define RT5677_SEL_SRC_OB01                    (0x1 << 3)
+#define RT5677_SEL_SRC_OB01_SFT                        3
+#define RT5677_SEL_SRC_IB45                    (0x1 << 2)
+#define RT5677_SEL_SRC_IB45_SFT                        2
+#define RT5677_SEL_SRC_IB23                    (0x1 << 1)
+#define RT5677_SEL_SRC_IB23_SFT                        1
+#define RT5677_SEL_SRC_IB01                    (0x1 << 0)
+#define RT5677_SEL_SRC_IB01_SFT                        0
+
+/* Virtual DSP Mixer Control (0xf7 0xf8 0xf9) */
+#define RT5677_DSP_IB_01_H                     (0x1 << 15)
+#define RT5677_DSP_IB_01_H_SFT                 15
+#define RT5677_DSP_IB_23_H                     (0x1 << 14)
+#define RT5677_DSP_IB_23_H_SFT                 14
+#define RT5677_DSP_IB_45_H                     (0x1 << 13)
+#define RT5677_DSP_IB_45_H_SFT                 13
+#define RT5677_DSP_IB_6_H                      (0x1 << 12)
+#define RT5677_DSP_IB_6_H_SFT                  12
+#define RT5677_DSP_IB_7_H                      (0x1 << 11)
+#define RT5677_DSP_IB_7_H_SFT                  11
+#define RT5677_DSP_IB_8_H                      (0x1 << 10)
+#define RT5677_DSP_IB_8_H_SFT                  10
+#define RT5677_DSP_IB_9_H                      (0x1 << 9)
+#define RT5677_DSP_IB_9_H_SFT                  9
+#define RT5677_DSP_IB_01_L                     (0x1 << 7)
+#define RT5677_DSP_IB_01_L_SFT                 7
+#define RT5677_DSP_IB_23_L                     (0x1 << 6)
+#define RT5677_DSP_IB_23_L_SFT                 6
+#define RT5677_DSP_IB_45_L                     (0x1 << 5)
+#define RT5677_DSP_IB_45_L_SFT                 5
+#define RT5677_DSP_IB_6_L                      (0x1 << 4)
+#define RT5677_DSP_IB_6_L_SFT                  4
+#define RT5677_DSP_IB_7_L                      (0x1 << 3)
+#define RT5677_DSP_IB_7_L_SFT                  3
+#define RT5677_DSP_IB_8_L                      (0x1 << 2)
+#define RT5677_DSP_IB_8_L_SFT                  2
+#define RT5677_DSP_IB_9_L                      (0x1 << 1)
+#define RT5677_DSP_IB_9_L_SFT                  1
+
+/* Debug String Length */
+#define RT5677_REG_DISP_LEN 23
+
+#define RT5677_NO_JACK         BIT(0)
+#define RT5677_HEADSET_DET     BIT(1)
+#define RT5677_HEADPHO_DET     BIT(2)
+
+/* System Clock Source */
+enum {
+       RT5677_SCLK_S_MCLK,
+       RT5677_SCLK_S_PLL1,
+       RT5677_SCLK_S_RCCLK,
+};
+
+/* PLL1 Source */
+enum {
+       RT5677_PLL1_S_MCLK,
+       RT5677_PLL1_S_BCLK1,
+       RT5677_PLL1_S_BCLK2,
+       RT5677_PLL1_S_BCLK3,
+       RT5677_PLL1_S_BCLK4,
+};
+
+enum {
+       RT5677_AIF1,
+       RT5677_AIF2,
+       RT5677_AIF3,
+       RT5677_AIF4,
+       RT5677_AIF5,
+       RT5677_AIFS,
+};
+
+struct rt5677_pll_code {
+       bool m_bp; /* Indicates bypass m code or not. */
+       bool k_bp; /* Indicates bypass k code or not. */
+       int m_code;
+       int n_code;
+       int k_code;
+};
+
+struct rt5677_priv {
+       struct snd_soc_codec *codec;
+       struct rt5677_platform_data pdata;
+       struct regmap *regmap;
+
+       int sysclk;
+       int sysclk_src;
+       int lrck[RT5677_AIFS];
+       int bclk[RT5677_AIFS];
+       int master[RT5677_AIFS];
+       int pll_src;
+       int pll_in;
+       int pll_out;
+};
+
+#endif /* __RT5677_H__ */
index d3ed1be..3d39f0b 100644 (file)
 
 /* default value of sgtl5000 registers */
 static const struct reg_default sgtl5000_reg_defaults[] = {
+       { SGTL5000_CHIP_DIG_POWER,              0x0000 },
        { SGTL5000_CHIP_CLK_CTRL,               0x0008 },
        { SGTL5000_CHIP_I2S_CTRL,               0x0010 },
        { SGTL5000_CHIP_SSS_CTRL,               0x0010 },
+       { SGTL5000_CHIP_ADCDAC_CTRL,            0x020c },
        { SGTL5000_CHIP_DAC_VOL,                0x3c3c },
        { SGTL5000_CHIP_PAD_STRENGTH,           0x015f },
+       { SGTL5000_CHIP_ANA_ADC_CTRL,           0x0000 },
        { SGTL5000_CHIP_ANA_HP_CTRL,            0x1818 },
        { SGTL5000_CHIP_ANA_CTRL,               0x0111 },
+       { SGTL5000_CHIP_LINREG_CTRL,            0x0000 },
+       { SGTL5000_CHIP_REF_CTRL,               0x0000 },
+       { SGTL5000_CHIP_MIC_CTRL,               0x0000 },
+       { SGTL5000_CHIP_LINE_OUT_CTRL,          0x0000 },
        { SGTL5000_CHIP_LINE_OUT_VOL,           0x0404 },
        { SGTL5000_CHIP_ANA_POWER,              0x7060 },
        { SGTL5000_CHIP_PLL_CTRL,               0x5000 },
+       { SGTL5000_CHIP_CLK_TOP_CTRL,           0x0000 },
+       { SGTL5000_CHIP_ANA_STATUS,             0x0000 },
+       { SGTL5000_CHIP_SHORT_CTRL,             0x0000 },
+       { SGTL5000_CHIP_ANA_TEST2,              0x0000 },
+       { SGTL5000_DAP_CTRL,                    0x0000 },
+       { SGTL5000_DAP_PEQ,                     0x0000 },
        { SGTL5000_DAP_BASS_ENHANCE,            0x0040 },
        { SGTL5000_DAP_BASS_ENHANCE_CTRL,       0x051f },
+       { SGTL5000_DAP_AUDIO_EQ,                0x0000 },
        { SGTL5000_DAP_SURROUND,                0x0040 },
        { SGTL5000_DAP_EQ_BASS_BAND0,           0x002f },
        { SGTL5000_DAP_EQ_BASS_BAND1,           0x002f },
@@ -55,6 +69,7 @@ static const struct reg_default sgtl5000_reg_defaults[] = {
        { SGTL5000_DAP_EQ_BASS_BAND3,           0x002f },
        { SGTL5000_DAP_EQ_BASS_BAND4,           0x002f },
        { SGTL5000_DAP_MAIN_CHAN,               0x8000 },
+       { SGTL5000_DAP_MIX_CHAN,                0x0000 },
        { SGTL5000_DAP_AVC_CTRL,                0x0510 },
        { SGTL5000_DAP_AVC_THRESHOLD,           0x1473 },
        { SGTL5000_DAP_AVC_ATTACK,              0x0028 },
@@ -296,7 +311,7 @@ static int dac_info_volsw(struct snd_kcontrol *kcontrol,
 static int dac_get_volsw(struct snd_kcontrol *kcontrol,
                         struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        int reg;
        int l;
        int r;
@@ -349,7 +364,7 @@ static int dac_get_volsw(struct snd_kcontrol *kcontrol,
 static int dac_put_volsw(struct snd_kcontrol *kcontrol,
                         struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        int reg;
        int l;
        int r;
@@ -1068,71 +1083,11 @@ static int sgtl5000_suspend(struct snd_soc_codec *codec)
        return 0;
 }
 
-/*
- * restore all sgtl5000 registers,
- * since a big hole between dap and regular registers,
- * we will restore them respectively.
- */
-static int sgtl5000_restore_regs(struct snd_soc_codec *codec)
-{
-       u16 *cache = codec->reg_cache;
-       u16 reg;
-
-       /* restore regular registers */
-       for (reg = 0; reg <= SGTL5000_CHIP_SHORT_CTRL; reg += 2) {
-
-               /* These regs should restore in particular order */
-               if (reg == SGTL5000_CHIP_ANA_POWER ||
-                       reg == SGTL5000_CHIP_CLK_CTRL ||
-                       reg == SGTL5000_CHIP_LINREG_CTRL ||
-                       reg == SGTL5000_CHIP_LINE_OUT_CTRL ||
-                       reg == SGTL5000_CHIP_REF_CTRL)
-                       continue;
-
-               snd_soc_write(codec, reg, cache[reg]);
-       }
-
-       /* restore dap registers */
-       for (reg = SGTL5000_DAP_REG_OFFSET; reg < SGTL5000_MAX_REG_OFFSET; reg += 2)
-               snd_soc_write(codec, reg, cache[reg]);
-
-       /*
-        * restore these regs according to the power setting sequence in
-        * sgtl5000_set_power_regs() and clock setting sequence in
-        * sgtl5000_set_clock().
-        *
-        * The order of restore is:
-        * 1. SGTL5000_CHIP_CLK_CTRL MCLK_FREQ bits (1:0) should be restore after
-        *    SGTL5000_CHIP_ANA_POWER PLL bits set
-        * 2. SGTL5000_CHIP_LINREG_CTRL should be set before
-        *    SGTL5000_CHIP_ANA_POWER LINREG_D restored
-        * 3. SGTL5000_CHIP_REF_CTRL controls Analog Ground Voltage,
-        *    prefer to resotre it after SGTL5000_CHIP_ANA_POWER restored
-        */
-       snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL,
-                       cache[SGTL5000_CHIP_LINREG_CTRL]);
-
-       snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER,
-                       cache[SGTL5000_CHIP_ANA_POWER]);
-
-       snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL,
-                       cache[SGTL5000_CHIP_CLK_CTRL]);
-
-       snd_soc_write(codec, SGTL5000_CHIP_REF_CTRL,
-                       cache[SGTL5000_CHIP_REF_CTRL]);
-
-       snd_soc_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
-                       cache[SGTL5000_CHIP_LINE_OUT_CTRL]);
-       return 0;
-}
-
 static int sgtl5000_resume(struct snd_soc_codec *codec)
 {
        /* Bring the codec back up to standby to enable regulators */
        sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
-       /* Restore registers by cached in memory */
-       sgtl5000_restore_regs(codec);
        return 0;
 }
 #else
@@ -1322,7 +1277,7 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
                        return ret;
        }
 
-       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
+       ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
                                 sgtl5000->supplies);
        if (ret)
                goto err_ldo_remove;
@@ -1330,16 +1285,13 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
        ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
                                        sgtl5000->supplies);
        if (ret)
-               goto err_regulator_free;
+               goto err_ldo_remove;
 
        /* wait for all power rails bring up */
        udelay(10);
 
        return 0;
 
-err_regulator_free:
-       regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
-                               sgtl5000->supplies);
 err_ldo_remove:
        if (!external_vddd)
                ldo_regulator_remove(codec);
@@ -1409,8 +1361,6 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
 err:
        regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
                                                sgtl5000->supplies);
-       regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
-                               sgtl5000->supplies);
        ldo_regulator_remove(codec);
 
        return ret;
@@ -1424,8 +1374,6 @@ static int sgtl5000_remove(struct snd_soc_codec *codec)
 
        regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
                                                sgtl5000->supplies);
-       regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
-                               sgtl5000->supplies);
        ldo_regulator_remove(codec);
 
        return 0;
index 244c097..f26befb 100644 (file)
@@ -208,13 +208,6 @@ out:
        return err;
 }
 
-static int si476x_codec_probe(struct snd_soc_codec *codec)
-{
-       struct regmap *regmap = dev_get_regmap(codec->dev->parent, NULL);
-
-       return snd_soc_codec_set_cache_io(codec, regmap);
-}
-
 static struct snd_soc_dai_ops si476x_dai_ops = {
        .hw_params      = si476x_codec_hw_params,
        .set_fmt        = si476x_codec_set_dai_fmt,
@@ -238,8 +231,13 @@ static struct snd_soc_dai_driver si476x_dai = {
        .ops            = &si476x_dai_ops,
 };
 
+static struct regmap *si476x_get_regmap(struct device *dev)
+{
+       return dev_get_regmap(dev->parent, NULL);
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_si476x = {
-       .probe  = si476x_codec_probe,
+       .get_regmap = si476x_get_regmap,
        .dapm_widgets = si476x_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets),
        .dapm_routes = si476x_dapm_routes,
index 58e7c1f..d90cb0f 100644 (file)
@@ -109,7 +109,7 @@ static void enable_and_reset_codec(struct regmap *regmap,
 {
        regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1,
                        codec_enable_bits | codec_reset_bits,
-                       codec_enable_bits | ~codec_reset_bits);
+                       codec_enable_bits);
        msleep(20);
        regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1,
                        codec_reset_bits, codec_reset_bits);
@@ -128,8 +128,7 @@ static int atlas6_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
                break;
        case SND_SOC_DAPM_POST_PMD:
                regmap_update_bits(sirf_audio_codec->regmap,
-                       AUDIO_IC_CODEC_CTRL1, ATLAS6_CODEC_ENABLE_BITS,
-                       ~ATLAS6_CODEC_ENABLE_BITS);
+                       AUDIO_IC_CODEC_CTRL1, ATLAS6_CODEC_ENABLE_BITS, 0);
                break;
        default:
                break;
@@ -151,8 +150,7 @@ static int prima2_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
                break;
        case SND_SOC_DAPM_POST_PMD:
                regmap_update_bits(sirf_audio_codec->regmap,
-                       AUDIO_IC_CODEC_CTRL1, PRIMA2_CODEC_ENABLE_BITS,
-                       ~PRIMA2_CODEC_ENABLE_BITS);
+                       AUDIO_IC_CODEC_CTRL1, PRIMA2_CODEC_ENABLE_BITS, 0);
                break;
        default:
                break;
@@ -279,13 +277,63 @@ static const struct snd_soc_dapm_route sirf_audio_codec_map[] = {
        {"Mic input mode mux", "Differential", "MICIN1"},
 };
 
+static void sirf_audio_codec_tx_enable(struct sirf_audio_codec *sirf_audio_codec)
+{
+       regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP,
+               AUDIO_FIFO_RESET, AUDIO_FIFO_RESET);
+       regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP,
+               AUDIO_FIFO_RESET, ~AUDIO_FIFO_RESET);
+       regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_INT_MSK, 0);
+       regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0);
+       regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP,
+               AUDIO_FIFO_START, AUDIO_FIFO_START);
+       regmap_update_bits(sirf_audio_codec->regmap,
+               AUDIO_PORT_IC_CODEC_TX_CTRL, IC_TX_ENABLE, IC_TX_ENABLE);
+}
+
+static void sirf_audio_codec_tx_disable(struct sirf_audio_codec *sirf_audio_codec)
+{
+       regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0);
+       regmap_update_bits(sirf_audio_codec->regmap,
+               AUDIO_PORT_IC_CODEC_TX_CTRL, IC_TX_ENABLE, ~IC_TX_ENABLE);
+}
+
+static void sirf_audio_codec_rx_enable(struct sirf_audio_codec *sirf_audio_codec,
+       int channels)
+{
+       regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP,
+               AUDIO_FIFO_RESET, AUDIO_FIFO_RESET);
+       regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP,
+               AUDIO_FIFO_RESET, ~AUDIO_FIFO_RESET);
+       regmap_write(sirf_audio_codec->regmap,
+               AUDIO_PORT_IC_RXFIFO_INT_MSK, 0);
+       regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP, 0);
+       regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP,
+               AUDIO_FIFO_START, AUDIO_FIFO_START);
+       if (channels == 1)
+               regmap_update_bits(sirf_audio_codec->regmap,
+                       AUDIO_PORT_IC_CODEC_RX_CTRL,
+                       IC_RX_ENABLE_MONO, IC_RX_ENABLE_MONO);
+       else
+               regmap_update_bits(sirf_audio_codec->regmap,
+                       AUDIO_PORT_IC_CODEC_RX_CTRL,
+                       IC_RX_ENABLE_STEREO, IC_RX_ENABLE_STEREO);
+}
+
+static void sirf_audio_codec_rx_disable(struct sirf_audio_codec *sirf_audio_codec)
+{
+       regmap_update_bits(sirf_audio_codec->regmap,
+                       AUDIO_PORT_IC_CODEC_RX_CTRL,
+                       IC_RX_ENABLE_STEREO, ~IC_RX_ENABLE_STEREO);
+}
+
 static int sirf_audio_codec_trigger(struct snd_pcm_substream *substream,
                int cmd,
                struct snd_soc_dai *dai)
 {
-       int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        struct snd_soc_codec *codec = dai->codec;
-       u32 val = 0;
+       struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
+       int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 
        /*
         * This is a workaround, When stop playback,
@@ -295,20 +343,28 @@ static int sirf_audio_codec_trigger(struct snd_pcm_substream *substream,
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (playback) {
+                       snd_soc_update_bits(codec, AUDIO_IC_CODEC_CTRL0,
+                               IC_HSLEN | IC_HSREN, 0);
+                       sirf_audio_codec_tx_disable(sirf_audio_codec);
+               } else
+                       sirf_audio_codec_rx_disable(sirf_audio_codec);
                break;
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               if (playback)
-                       val = IC_HSLEN | IC_HSREN;
+               if (playback) {
+                       sirf_audio_codec_tx_enable(sirf_audio_codec);
+                       snd_soc_update_bits(codec, AUDIO_IC_CODEC_CTRL0,
+                               IC_HSLEN | IC_HSREN, IC_HSLEN | IC_HSREN);
+               } else
+                       sirf_audio_codec_rx_enable(sirf_audio_codec,
+                               substream->runtime->channels);
                break;
        default:
                return -EINVAL;
        }
 
-       if (playback)
-               snd_soc_update_bits(codec, AUDIO_IC_CODEC_CTRL0,
-                       IC_HSLEN | IC_HSREN, val);
        return 0;
 }
 
@@ -392,7 +448,7 @@ static const struct regmap_config sirf_audio_codec_regmap_config = {
        .reg_bits = 32,
        .reg_stride = 4,
        .val_bits = 32,
-       .max_register = AUDIO_IC_CODEC_CTRL3,
+       .max_register = AUDIO_PORT_IC_RXFIFO_INT_MSK,
        .cache_type = REGCACHE_NONE,
 };
 
index d4c187b..ba1adc0 100644 (file)
 #define IC_RXPGAR              0x7B
 #define IC_RXPGAL              0x7B
 
+#define AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK     0x3F
+#define AUDIO_PORT_TX_FIFO_SC_OFFSET    0
+#define AUDIO_PORT_TX_FIFO_LC_OFFSET    10
+#define AUDIO_PORT_TX_FIFO_HC_OFFSET    20
+
+#define TX_FIFO_SC(x)           (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
+                               << AUDIO_PORT_TX_FIFO_SC_OFFSET)
+#define TX_FIFO_LC(x)           (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
+                               << AUDIO_PORT_TX_FIFO_LC_OFFSET)
+#define TX_FIFO_HC(x)           (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
+                               << AUDIO_PORT_TX_FIFO_HC_OFFSET)
+
+#define AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK     0x0F
+#define AUDIO_PORT_RX_FIFO_SC_OFFSET    0
+#define AUDIO_PORT_RX_FIFO_LC_OFFSET    10
+#define AUDIO_PORT_RX_FIFO_HC_OFFSET    20
+
+#define RX_FIFO_SC(x)           (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
+                               << AUDIO_PORT_RX_FIFO_SC_OFFSET)
+#define RX_FIFO_LC(x)           (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
+                               << AUDIO_PORT_RX_FIFO_LC_OFFSET)
+#define RX_FIFO_HC(x)           (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
+                               << AUDIO_PORT_RX_FIFO_HC_OFFSET)
+#define AUDIO_PORT_IC_CODEC_TX_CTRL            (0x00F4)
+#define AUDIO_PORT_IC_CODEC_RX_CTRL            (0x00F8)
+
+#define AUDIO_PORT_IC_TXFIFO_OP                        (0x00FC)
+#define AUDIO_PORT_IC_TXFIFO_LEV_CHK           (0x0100)
+#define AUDIO_PORT_IC_TXFIFO_STS               (0x0104)
+#define AUDIO_PORT_IC_TXFIFO_INT               (0x0108)
+#define AUDIO_PORT_IC_TXFIFO_INT_MSK           (0x010C)
+
+#define AUDIO_PORT_IC_RXFIFO_OP                        (0x0110)
+#define AUDIO_PORT_IC_RXFIFO_LEV_CHK           (0x0114)
+#define AUDIO_PORT_IC_RXFIFO_STS               (0x0118)
+#define AUDIO_PORT_IC_RXFIFO_INT               (0x011C)
+#define AUDIO_PORT_IC_RXFIFO_INT_MSK           (0x0120)
+
+#define AUDIO_FIFO_START               (1 << 0)
+#define AUDIO_FIFO_RESET               (1 << 1)
+
+#define AUDIO_FIFO_FULL                        (1 << 0)
+#define AUDIO_FIFO_EMPTY               (1 << 1)
+#define AUDIO_FIFO_OFLOW               (1 << 2)
+#define AUDIO_FIFO_UFLOW               (1 << 3)
+
+#define IC_TX_ENABLE           (0x03)
+#define IC_RX_ENABLE_MONO      (0x01)
+#define IC_RX_ENABLE_STEREO    (0x03)
+
 #endif /*__SIRF_AUDIO_CODEC_H*/
index 1257774..0579d18 100644 (file)
@@ -243,7 +243,7 @@ static int sta32x_coefficient_info(struct snd_kcontrol *kcontrol,
 static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        int numcoef = kcontrol->private_value >> 16;
        int index = kcontrol->private_value & 0xffff;
        unsigned int cfud;
@@ -272,7 +272,7 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol,
 static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
        int numcoef = kcontrol->private_value >> 16;
        int index = kcontrol->private_value & 0xffff;
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c
new file mode 100644 (file)
index 0000000..cc97dd5
--- /dev/null
@@ -0,0 +1,1311 @@
+/*
+ * Codec driver for ST STA350 2.1-channel high-efficiency digital audio system
+ *
+ * Copyright: 2014 Raumfeld GmbH
+ * Author: Sven Brandau <info@brandau.biz>
+ *
+ * based on code from:
+ *     Raumfeld GmbH
+ *       Johannes Stezenbach <js@sig21.net>
+ *     Wolfson Microelectronics PLC.
+ *       Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *     Freescale Semiconductor, Inc.
+ *       Timur Tabi <timur@freescale.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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__
+
+#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/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/slab.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 <sound/sta350.h>
+#include "sta350.h"
+
+#define STA350_RATES (SNDRV_PCM_RATE_32000 | \
+                     SNDRV_PCM_RATE_44100 | \
+                     SNDRV_PCM_RATE_48000 | \
+                     SNDRV_PCM_RATE_88200 | \
+                     SNDRV_PCM_RATE_96000 | \
+                     SNDRV_PCM_RATE_176400 | \
+                     SNDRV_PCM_RATE_192000)
+
+#define STA350_FORMATS \
+       (SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
+        SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
+        SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
+        SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
+        SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE  | \
+        SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_S32_BE)
+
+/* Power-up register defaults */
+static const struct reg_default sta350_regs[] = {
+       {  0x0, 0x63 },
+       {  0x1, 0x80 },
+       {  0x2, 0xdf },
+       {  0x3, 0x40 },
+       {  0x4, 0xc2 },
+       {  0x5, 0x5c },
+       {  0x6, 0x00 },
+       {  0x7, 0xff },
+       {  0x8, 0x60 },
+       {  0x9, 0x60 },
+       {  0xa, 0x60 },
+       {  0xb, 0x00 },
+       {  0xc, 0x00 },
+       {  0xd, 0x00 },
+       {  0xe, 0x00 },
+       {  0xf, 0x40 },
+       { 0x10, 0x80 },
+       { 0x11, 0x77 },
+       { 0x12, 0x6a },
+       { 0x13, 0x69 },
+       { 0x14, 0x6a },
+       { 0x15, 0x69 },
+       { 0x16, 0x00 },
+       { 0x17, 0x00 },
+       { 0x18, 0x00 },
+       { 0x19, 0x00 },
+       { 0x1a, 0x00 },
+       { 0x1b, 0x00 },
+       { 0x1c, 0x00 },
+       { 0x1d, 0x00 },
+       { 0x1e, 0x00 },
+       { 0x1f, 0x00 },
+       { 0x20, 0x00 },
+       { 0x21, 0x00 },
+       { 0x22, 0x00 },
+       { 0x23, 0x00 },
+       { 0x24, 0x00 },
+       { 0x25, 0x00 },
+       { 0x26, 0x00 },
+       { 0x27, 0x2a },
+       { 0x28, 0xc0 },
+       { 0x29, 0xf3 },
+       { 0x2a, 0x33 },
+       { 0x2b, 0x00 },
+       { 0x2c, 0x0c },
+       { 0x31, 0x00 },
+       { 0x36, 0x00 },
+       { 0x37, 0x00 },
+       { 0x38, 0x00 },
+       { 0x39, 0x01 },
+       { 0x3a, 0xee },
+       { 0x3b, 0xff },
+       { 0x3c, 0x7e },
+       { 0x3d, 0xc0 },
+       { 0x3e, 0x26 },
+       { 0x3f, 0x00 },
+       { 0x48, 0x00 },
+       { 0x49, 0x00 },
+       { 0x4a, 0x00 },
+       { 0x4b, 0x04 },
+       { 0x4c, 0x00 },
+};
+
+static const struct regmap_range sta350_write_regs_range[] = {
+       regmap_reg_range(STA350_CONFA,  STA350_AUTO2),
+       regmap_reg_range(STA350_C1CFG,  STA350_FDRC2),
+       regmap_reg_range(STA350_EQCFG,  STA350_EVOLRES),
+       regmap_reg_range(STA350_NSHAPE, STA350_MISC2),
+};
+
+static const struct regmap_range sta350_read_regs_range[] = {
+       regmap_reg_range(STA350_CONFA,  STA350_AUTO2),
+       regmap_reg_range(STA350_C1CFG,  STA350_STATUS),
+       regmap_reg_range(STA350_EQCFG,  STA350_EVOLRES),
+       regmap_reg_range(STA350_NSHAPE, STA350_MISC2),
+};
+
+static const struct regmap_range sta350_volatile_regs_range[] = {
+       regmap_reg_range(STA350_CFADDR2, STA350_CFUD),
+       regmap_reg_range(STA350_STATUS,  STA350_STATUS),
+};
+
+static const struct regmap_access_table sta350_write_regs = {
+       .yes_ranges =   sta350_write_regs_range,
+       .n_yes_ranges = ARRAY_SIZE(sta350_write_regs_range),
+};
+
+static const struct regmap_access_table sta350_read_regs = {
+       .yes_ranges =   sta350_read_regs_range,
+       .n_yes_ranges = ARRAY_SIZE(sta350_read_regs_range),
+};
+
+static const struct regmap_access_table sta350_volatile_regs = {
+       .yes_ranges =   sta350_volatile_regs_range,
+       .n_yes_ranges = ARRAY_SIZE(sta350_volatile_regs_range),
+};
+
+/* regulator power supply names */
+static const char * const sta350_supply_names[] = {
+       "vdd-dig",      /* digital supply, 3.3V */
+       "vdd-pll",      /* pll supply, 3.3V */
+       "vcc"           /* power amp supply, 5V - 26V */
+};
+
+/* codec private data */
+struct sta350_priv {
+       struct regmap *regmap;
+       struct regulator_bulk_data supplies[ARRAY_SIZE(sta350_supply_names)];
+       struct sta350_platform_data *pdata;
+
+       unsigned int mclk;
+       unsigned int format;
+
+       u32 coef_shadow[STA350_COEF_COUNT];
+       int shutdown;
+
+       struct gpio_desc *gpiod_nreset;
+       struct gpio_desc *gpiod_power_down;
+
+       struct mutex coeff_lock;
+};
+
+static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(chvol_tlv, -7950, 50, 1);
+static const DECLARE_TLV_DB_SCALE(tone_tlv, -1200, 200, 0);
+
+static const char * const sta350_drc_ac[] = {
+       "Anti-Clipping", "Dynamic Range Compression"
+};
+static const char * const sta350_auto_gc_mode[] = {
+       "User", "AC no clipping", "AC limited clipping (10%)",
+       "DRC nighttime listening mode"
+};
+static const char * const sta350_auto_xo_mode[] = {
+       "User", "80Hz", "100Hz", "120Hz", "140Hz", "160Hz", "180Hz",
+       "200Hz", "220Hz", "240Hz", "260Hz", "280Hz", "300Hz", "320Hz",
+       "340Hz", "360Hz"
+};
+static const char * const sta350_binary_output[] = {
+       "FFX 3-state output - normal operation", "Binary output"
+};
+static const char * const sta350_limiter_select[] = {
+       "Limiter Disabled", "Limiter #1", "Limiter #2"
+};
+static const char * const sta350_limiter_attack_rate[] = {
+       "3.1584", "2.7072", "2.2560", "1.8048", "1.3536", "0.9024",
+       "0.4512", "0.2256", "0.1504", "0.1123", "0.0902", "0.0752",
+       "0.0645", "0.0564", "0.0501", "0.0451"
+};
+static const char * const sta350_limiter_release_rate[] = {
+       "0.5116", "0.1370", "0.0744", "0.0499", "0.0360", "0.0299",
+       "0.0264", "0.0208", "0.0198", "0.0172", "0.0147", "0.0137",
+       "0.0134", "0.0117", "0.0110", "0.0104"
+};
+static const char * const sta350_noise_shaper_type[] = {
+       "Third order", "Fourth order"
+};
+
+static DECLARE_TLV_DB_RANGE(sta350_limiter_ac_attack_tlv,
+       0, 7, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+       8, 16, TLV_DB_SCALE_ITEM(300, 100, 0),
+);
+
+static DECLARE_TLV_DB_RANGE(sta350_limiter_ac_release_tlv,
+       0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
+       1, 1, TLV_DB_SCALE_ITEM(-2900, 0, 0),
+       2, 2, TLV_DB_SCALE_ITEM(-2000, 0, 0),
+       3, 8, TLV_DB_SCALE_ITEM(-1400, 200, 0),
+       8, 16, TLV_DB_SCALE_ITEM(-700, 100, 0),
+);
+
+static DECLARE_TLV_DB_RANGE(sta350_limiter_drc_attack_tlv,
+       0, 7, TLV_DB_SCALE_ITEM(-3100, 200, 0),
+       8, 13, TLV_DB_SCALE_ITEM(-1600, 100, 0),
+       14, 16, TLV_DB_SCALE_ITEM(-1000, 300, 0),
+);
+
+static DECLARE_TLV_DB_RANGE(sta350_limiter_drc_release_tlv,
+       0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
+       1, 2, TLV_DB_SCALE_ITEM(-3800, 200, 0),
+       3, 4, TLV_DB_SCALE_ITEM(-3300, 200, 0),
+       5, 12, TLV_DB_SCALE_ITEM(-3000, 200, 0),
+       13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0),
+);
+
+static SOC_ENUM_SINGLE_DECL(sta350_drc_ac_enum,
+                           STA350_CONFD, STA350_CONFD_DRC_SHIFT,
+                           sta350_drc_ac);
+static SOC_ENUM_SINGLE_DECL(sta350_noise_shaper_enum,
+                           STA350_CONFE, STA350_CONFE_NSBW_SHIFT,
+                           sta350_noise_shaper_type);
+static SOC_ENUM_SINGLE_DECL(sta350_auto_gc_enum,
+                           STA350_AUTO1, STA350_AUTO1_AMGC_SHIFT,
+                           sta350_auto_gc_mode);
+static SOC_ENUM_SINGLE_DECL(sta350_auto_xo_enum,
+                           STA350_AUTO2, STA350_AUTO2_XO_SHIFT,
+                           sta350_auto_xo_mode);
+static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch1_enum,
+                           STA350_C1CFG, STA350_CxCFG_BO_SHIFT,
+                           sta350_binary_output);
+static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch2_enum,
+                           STA350_C2CFG, STA350_CxCFG_BO_SHIFT,
+                           sta350_binary_output);
+static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch3_enum,
+                           STA350_C3CFG, STA350_CxCFG_BO_SHIFT,
+                           sta350_binary_output);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch1_enum,
+                           STA350_C1CFG, STA350_CxCFG_LS_SHIFT,
+                           sta350_limiter_select);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch2_enum,
+                           STA350_C2CFG, STA350_CxCFG_LS_SHIFT,
+                           sta350_limiter_select);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch3_enum,
+                           STA350_C3CFG, STA350_CxCFG_LS_SHIFT,
+                           sta350_limiter_select);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter1_attack_rate_enum,
+                           STA350_L1AR, STA350_LxA_SHIFT,
+                           sta350_limiter_attack_rate);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter2_attack_rate_enum,
+                           STA350_L2AR, STA350_LxA_SHIFT,
+                           sta350_limiter_attack_rate);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter1_release_rate_enum,
+                           STA350_L1AR, STA350_LxR_SHIFT,
+                           sta350_limiter_release_rate);
+static SOC_ENUM_SINGLE_DECL(sta350_limiter2_release_rate_enum,
+                           STA350_L2AR, STA350_LxR_SHIFT,
+                           sta350_limiter_release_rate);
+
+/*
+ * byte array controls for setting biquad, mixer, scaling coefficients;
+ * for biquads all five coefficients need to be set in one go,
+ * mixer and pre/postscale coefs can be set individually;
+ * each coef is 24bit, the bytes are ordered in the same way
+ * as given in the STA350 data sheet (big endian; b1, b2, a1, a2, b0)
+ */
+
+static int sta350_coefficient_info(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_info *uinfo)
+{
+       int numcoef = kcontrol->private_value >> 16;
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       uinfo->count = 3 * numcoef;
+       return 0;
+}
+
+static int sta350_coefficient_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+       int numcoef = kcontrol->private_value >> 16;
+       int index = kcontrol->private_value & 0xffff;
+       unsigned int cfud, val;
+       int i, ret = 0;
+
+       mutex_lock(&sta350->coeff_lock);
+
+       /* preserve reserved bits in STA350_CFUD */
+       regmap_read(sta350->regmap, STA350_CFUD, &cfud);
+       cfud &= 0xf0;
+       /*
+        * chip documentation does not say if the bits are self clearing,
+        * so do it explicitly
+        */
+       regmap_write(sta350->regmap, STA350_CFUD, cfud);
+
+       regmap_write(sta350->regmap, STA350_CFADDR2, index);
+       if (numcoef == 1) {
+               regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x04);
+       } else if (numcoef == 5) {
+               regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x08);
+       } else {
+               ret = -EINVAL;
+               goto exit_unlock;
+       }
+
+       for (i = 0; i < 3 * numcoef; i++) {
+               regmap_read(sta350->regmap, STA350_B1CF1 + i, &val);
+               ucontrol->value.bytes.data[i] = val;
+       }
+
+exit_unlock:
+       mutex_unlock(&sta350->coeff_lock);
+
+       return ret;
+}
+
+static int sta350_coefficient_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+       int numcoef = kcontrol->private_value >> 16;
+       int index = kcontrol->private_value & 0xffff;
+       unsigned int cfud;
+       int i;
+
+       /* preserve reserved bits in STA350_CFUD */
+       regmap_read(sta350->regmap, STA350_CFUD, &cfud);
+       cfud &= 0xf0;
+       /*
+        * chip documentation does not say if the bits are self clearing,
+        * so do it explicitly
+        */
+       regmap_write(sta350->regmap, STA350_CFUD, cfud);
+
+       regmap_write(sta350->regmap, STA350_CFADDR2, index);
+       for (i = 0; i < numcoef && (index + i < STA350_COEF_COUNT); i++)
+               sta350->coef_shadow[index + i] =
+                         (ucontrol->value.bytes.data[3 * i] << 16)
+                       | (ucontrol->value.bytes.data[3 * i + 1] << 8)
+                       | (ucontrol->value.bytes.data[3 * i + 2]);
+       for (i = 0; i < 3 * numcoef; i++)
+               regmap_write(sta350->regmap, STA350_B1CF1 + i,
+                            ucontrol->value.bytes.data[i]);
+       if (numcoef == 1)
+               regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x01);
+       else if (numcoef == 5)
+               regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x02);
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int sta350_sync_coef_shadow(struct snd_soc_codec *codec)
+{
+       struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+       unsigned int cfud;
+       int i;
+
+       /* preserve reserved bits in STA350_CFUD */
+       regmap_read(sta350->regmap, STA350_CFUD, &cfud);
+       cfud &= 0xf0;
+
+       for (i = 0; i < STA350_COEF_COUNT; i++) {
+               regmap_write(sta350->regmap, STA350_CFADDR2, i);
+               regmap_write(sta350->regmap, STA350_B1CF1,
+                            (sta350->coef_shadow[i] >> 16) & 0xff);
+               regmap_write(sta350->regmap, STA350_B1CF2,
+                            (sta350->coef_shadow[i] >> 8) & 0xff);
+               regmap_write(sta350->regmap, STA350_B1CF3,
+                            (sta350->coef_shadow[i]) & 0xff);
+               /*
+                * chip documentation does not say if the bits are
+                * self-clearing, so do it explicitly
+                */
+               regmap_write(sta350->regmap, STA350_CFUD, cfud);
+               regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x01);
+       }
+       return 0;
+}
+
+static int sta350_cache_sync(struct snd_soc_codec *codec)
+{
+       struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+       unsigned int mute;
+       int rc;
+
+       /* mute during register sync */
+       regmap_read(sta350->regmap, STA350_CFUD, &mute);
+       regmap_write(sta350->regmap, STA350_MMUTE, mute | STA350_MMUTE_MMUTE);
+       sta350_sync_coef_shadow(codec);
+       rc = regcache_sync(sta350->regmap);
+       regmap_write(sta350->regmap, STA350_MMUTE, mute);
+       return rc;
+}
+
+#define SINGLE_COEF(xname, index) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = sta350_coefficient_info, \
+       .get = sta350_coefficient_get,\
+       .put = sta350_coefficient_put, \
+       .private_value = index | (1 << 16) }
+
+#define BIQUAD_COEFS(xname, index) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = sta350_coefficient_info, \
+       .get = sta350_coefficient_get,\
+       .put = sta350_coefficient_put, \
+       .private_value = index | (5 << 16) }
+
+static const struct snd_kcontrol_new sta350_snd_controls[] = {
+SOC_SINGLE_TLV("Master Volume", STA350_MVOL, 0, 0xff, 1, mvol_tlv),
+/* VOL */
+SOC_SINGLE_TLV("Ch1 Volume", STA350_C1VOL, 0, 0xff, 1, chvol_tlv),
+SOC_SINGLE_TLV("Ch2 Volume", STA350_C2VOL, 0, 0xff, 1, chvol_tlv),
+SOC_SINGLE_TLV("Ch3 Volume", STA350_C3VOL, 0, 0xff, 1, chvol_tlv),
+/* CONFD */
+SOC_SINGLE("High Pass Filter Bypass Switch",
+          STA350_CONFD, STA350_CONFD_HPB_SHIFT, 1, 1),
+SOC_SINGLE("De-emphasis Filter Switch",
+          STA350_CONFD, STA350_CONFD_DEMP_SHIFT, 1, 0),
+SOC_SINGLE("DSP Bypass Switch",
+          STA350_CONFD, STA350_CONFD_DSPB_SHIFT, 1, 0),
+SOC_SINGLE("Post-scale Link Switch",
+          STA350_CONFD, STA350_CONFD_PSL_SHIFT, 1, 0),
+SOC_SINGLE("Biquad Coefficient Link Switch",
+          STA350_CONFD, STA350_CONFD_BQL_SHIFT, 1, 0),
+SOC_ENUM("Compressor/Limiter Switch", sta350_drc_ac_enum),
+SOC_ENUM("Noise Shaper Bandwidth", sta350_noise_shaper_enum),
+SOC_SINGLE("Zero-detect Mute Enable Switch",
+          STA350_CONFD, STA350_CONFD_ZDE_SHIFT, 1, 0),
+SOC_SINGLE("Submix Mode Switch",
+          STA350_CONFD, STA350_CONFD_SME_SHIFT, 1, 0),
+/* CONFE */
+SOC_SINGLE("Zero Cross Switch", STA350_CONFE, STA350_CONFE_ZCE_SHIFT, 1, 0),
+SOC_SINGLE("Soft Ramp Switch", STA350_CONFE, STA350_CONFE_SVE_SHIFT, 1, 0),
+/* MUTE */
+SOC_SINGLE("Master Switch", STA350_MMUTE, STA350_MMUTE_MMUTE_SHIFT, 1, 1),
+SOC_SINGLE("Ch1 Switch", STA350_MMUTE, STA350_MMUTE_C1M_SHIFT, 1, 1),
+SOC_SINGLE("Ch2 Switch", STA350_MMUTE, STA350_MMUTE_C2M_SHIFT, 1, 1),
+SOC_SINGLE("Ch3 Switch", STA350_MMUTE, STA350_MMUTE_C3M_SHIFT, 1, 1),
+/* AUTOx */
+SOC_ENUM("Automode GC", sta350_auto_gc_enum),
+SOC_ENUM("Automode XO", sta350_auto_xo_enum),
+/* CxCFG */
+SOC_SINGLE("Ch1 Tone Control Bypass Switch",
+          STA350_C1CFG, STA350_CxCFG_TCB_SHIFT, 1, 0),
+SOC_SINGLE("Ch2 Tone Control Bypass Switch",
+          STA350_C2CFG, STA350_CxCFG_TCB_SHIFT, 1, 0),
+SOC_SINGLE("Ch1 EQ Bypass Switch",
+          STA350_C1CFG, STA350_CxCFG_EQBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch2 EQ Bypass Switch",
+          STA350_C2CFG, STA350_CxCFG_EQBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch1 Master Volume Bypass Switch",
+          STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch2 Master Volume Bypass Switch",
+          STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0),
+SOC_SINGLE("Ch3 Master Volume Bypass Switch",
+          STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0),
+SOC_ENUM("Ch1 Binary Output Select", sta350_binary_output_ch1_enum),
+SOC_ENUM("Ch2 Binary Output Select", sta350_binary_output_ch2_enum),
+SOC_ENUM("Ch3 Binary Output Select", sta350_binary_output_ch3_enum),
+SOC_ENUM("Ch1 Limiter Select", sta350_limiter_ch1_enum),
+SOC_ENUM("Ch2 Limiter Select", sta350_limiter_ch2_enum),
+SOC_ENUM("Ch3 Limiter Select", sta350_limiter_ch3_enum),
+/* TONE */
+SOC_SINGLE_RANGE_TLV("Bass Tone Control Volume",
+                    STA350_TONE, STA350_TONE_BTC_SHIFT, 1, 13, 0, tone_tlv),
+SOC_SINGLE_RANGE_TLV("Treble Tone Control Volume",
+                    STA350_TONE, STA350_TONE_TTC_SHIFT, 1, 13, 0, tone_tlv),
+SOC_ENUM("Limiter1 Attack Rate (dB/ms)", sta350_limiter1_attack_rate_enum),
+SOC_ENUM("Limiter2 Attack Rate (dB/ms)", sta350_limiter2_attack_rate_enum),
+SOC_ENUM("Limiter1 Release Rate (dB/ms)", sta350_limiter1_release_rate_enum),
+SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta350_limiter2_release_rate_enum),
+
+/*
+ * depending on mode, the attack/release thresholds have
+ * two different enum definitions; provide both
+ */
+SOC_SINGLE_TLV("Limiter1 Attack Threshold (AC Mode)",
+              STA350_L1ATRT, STA350_LxA_SHIFT,
+              16, 0, sta350_limiter_ac_attack_tlv),
+SOC_SINGLE_TLV("Limiter2 Attack Threshold (AC Mode)",
+              STA350_L2ATRT, STA350_LxA_SHIFT,
+              16, 0, sta350_limiter_ac_attack_tlv),
+SOC_SINGLE_TLV("Limiter1 Release Threshold (AC Mode)",
+              STA350_L1ATRT, STA350_LxR_SHIFT,
+              16, 0, sta350_limiter_ac_release_tlv),
+SOC_SINGLE_TLV("Limiter2 Release Threshold (AC Mode)",
+              STA350_L2ATRT, STA350_LxR_SHIFT,
+              16, 0, sta350_limiter_ac_release_tlv),
+SOC_SINGLE_TLV("Limiter1 Attack Threshold (DRC Mode)",
+              STA350_L1ATRT, STA350_LxA_SHIFT,
+              16, 0, sta350_limiter_drc_attack_tlv),
+SOC_SINGLE_TLV("Limiter2 Attack Threshold (DRC Mode)",
+              STA350_L2ATRT, STA350_LxA_SHIFT,
+              16, 0, sta350_limiter_drc_attack_tlv),
+SOC_SINGLE_TLV("Limiter1 Release Threshold (DRC Mode)",
+              STA350_L1ATRT, STA350_LxR_SHIFT,
+              16, 0, sta350_limiter_drc_release_tlv),
+SOC_SINGLE_TLV("Limiter2 Release Threshold (DRC Mode)",
+              STA350_L2ATRT, STA350_LxR_SHIFT,
+              16, 0, sta350_limiter_drc_release_tlv),
+
+BIQUAD_COEFS("Ch1 - Biquad 1", 0),
+BIQUAD_COEFS("Ch1 - Biquad 2", 5),
+BIQUAD_COEFS("Ch1 - Biquad 3", 10),
+BIQUAD_COEFS("Ch1 - Biquad 4", 15),
+BIQUAD_COEFS("Ch2 - Biquad 1", 20),
+BIQUAD_COEFS("Ch2 - Biquad 2", 25),
+BIQUAD_COEFS("Ch2 - Biquad 3", 30),
+BIQUAD_COEFS("Ch2 - Biquad 4", 35),
+BIQUAD_COEFS("High-pass", 40),
+BIQUAD_COEFS("Low-pass", 45),
+SINGLE_COEF("Ch1 - Prescale", 50),
+SINGLE_COEF("Ch2 - Prescale", 51),
+SINGLE_COEF("Ch1 - Postscale", 52),
+SINGLE_COEF("Ch2 - Postscale", 53),
+SINGLE_COEF("Ch3 - Postscale", 54),
+SINGLE_COEF("Thermal warning - Postscale", 55),
+SINGLE_COEF("Ch1 - Mix 1", 56),
+SINGLE_COEF("Ch1 - Mix 2", 57),
+SINGLE_COEF("Ch2 - Mix 1", 58),
+SINGLE_COEF("Ch2 - Mix 2", 59),
+SINGLE_COEF("Ch3 - Mix 1", 60),
+SINGLE_COEF("Ch3 - Mix 2", 61),
+};
+
+static const struct snd_soc_dapm_widget sta350_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("LEFT"),
+SND_SOC_DAPM_OUTPUT("RIGHT"),
+SND_SOC_DAPM_OUTPUT("SUB"),
+};
+
+static const struct snd_soc_dapm_route sta350_dapm_routes[] = {
+       { "LEFT", NULL, "DAC" },
+       { "RIGHT", NULL, "DAC" },
+       { "SUB", NULL, "DAC" },
+       { "DAC", NULL, "Playback" },
+};
+
+/* MCLK interpolation ratio per fs */
+static struct {
+       int fs;
+       int ir;
+} interpolation_ratios[] = {
+       { 32000, 0 },
+       { 44100, 0 },
+       { 48000, 0 },
+       { 88200, 1 },
+       { 96000, 1 },
+       { 176400, 2 },
+       { 192000, 2 },
+};
+
+/* MCLK to fs clock ratios */
+static int mcs_ratio_table[3][6] = {
+       { 768, 512, 384, 256, 128, 576 },
+       { 384, 256, 192, 128,  64,   0 },
+       { 192, 128,  96,  64,  32,   0 },
+};
+
+/**
+ * sta350_set_dai_sysclk - configure MCLK
+ * @codec_dai: the codec DAI
+ * @clk_id: the clock ID (ignored)
+ * @freq: the MCLK input frequency
+ * @dir: the clock direction (ignored)
+ *
+ * The value of MCLK is used to determine which sample rates are supported
+ * by the STA350, based on the mcs_ratio_table.
+ *
+ * This function must be called by the machine driver's 'startup' function,
+ * otherwise the list of supported sample rates will not be available in
+ * time for ALSA.
+ */
+static int sta350_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 sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+
+       dev_dbg(codec->dev, "mclk=%u\n", freq);
+       sta350->mclk = freq;
+
+       return 0;
+}
+
+/**
+ * sta350_set_dai_fmt - configure the codec for the selected audio format
+ * @codec_dai: the codec DAI
+ * @fmt: a SND_SOC_DAIFMT_x value indicating the data format
+ *
+ * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the
+ * codec accordingly.
+ */
+static int sta350_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                             unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+       unsigned int confb = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+       case SND_SOC_DAIFMT_RIGHT_J:
+       case SND_SOC_DAIFMT_LEFT_J:
+               sta350->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               confb |= STA350_CONFB_C2IM;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               confb |= STA350_CONFB_C1IM;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_update_bits(sta350->regmap, STA350_CONFB,
+                                 STA350_CONFB_C1IM | STA350_CONFB_C2IM, confb);
+}
+
+/**
+ * sta350_hw_params - program the STA350 with the given hardware parameters.
+ * @substream: the audio stream
+ * @params: the hardware parameters to set
+ * @dai: the SOC DAI (ignored)
+ *
+ * This function programs the hardware with the values provided.
+ * Specifically, the sample rate and the data format.
+ */
+static int sta350_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 sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+       int i, mcs = -EINVAL, ir = -EINVAL;
+       unsigned int confa, confb;
+       unsigned int rate, ratio;
+       int ret;
+
+       if (!sta350->mclk) {
+               dev_err(codec->dev,
+                       "sta350->mclk is unset. Unable to determine ratio\n");
+               return -EIO;
+       }
+
+       rate = params_rate(params);
+       ratio = sta350->mclk / rate;
+       dev_dbg(codec->dev, "rate: %u, ratio: %u\n", rate, ratio);
+
+       for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) {
+               if (interpolation_ratios[i].fs == rate) {
+                       ir = interpolation_ratios[i].ir;
+                       break;
+               }
+       }
+
+       if (ir < 0) {
+               dev_err(codec->dev, "Unsupported samplerate: %u\n", rate);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < 6; i++) {
+               if (mcs_ratio_table[ir][i] == ratio) {
+                       mcs = i;
+                       break;
+               }
+       }
+
+       if (mcs < 0) {
+               dev_err(codec->dev, "Unresolvable ratio: %u\n", ratio);
+               return -EINVAL;
+       }
+
+       confa = (ir << STA350_CONFA_IR_SHIFT) |
+               (mcs << STA350_CONFA_MCS_SHIFT);
+       confb = 0;
+
+       switch (params_width(params)) {
+       case 24:
+               dev_dbg(codec->dev, "24bit\n");
+               /* fall through */
+       case 32:
+               dev_dbg(codec->dev, "24bit or 32bit\n");
+               switch (sta350->format) {
+               case SND_SOC_DAIFMT_I2S:
+                       confb |= 0x0;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       confb |= 0x1;
+                       break;
+               case SND_SOC_DAIFMT_RIGHT_J:
+                       confb |= 0x2;
+                       break;
+               }
+
+               break;
+       case 20:
+               dev_dbg(codec->dev, "20bit\n");
+               switch (sta350->format) {
+               case SND_SOC_DAIFMT_I2S:
+                       confb |= 0x4;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       confb |= 0x5;
+                       break;
+               case SND_SOC_DAIFMT_RIGHT_J:
+                       confb |= 0x6;
+                       break;
+               }
+
+               break;
+       case 18:
+               dev_dbg(codec->dev, "18bit\n");
+               switch (sta350->format) {
+               case SND_SOC_DAIFMT_I2S:
+                       confb |= 0x8;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       confb |= 0x9;
+                       break;
+               case SND_SOC_DAIFMT_RIGHT_J:
+                       confb |= 0xa;
+                       break;
+               }
+
+               break;
+       case 16:
+               dev_dbg(codec->dev, "16bit\n");
+               switch (sta350->format) {
+               case SND_SOC_DAIFMT_I2S:
+                       confb |= 0x0;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       confb |= 0xd;
+                       break;
+               case SND_SOC_DAIFMT_RIGHT_J:
+                       confb |= 0xe;
+                       break;
+               }
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = regmap_update_bits(sta350->regmap, STA350_CONFA,
+                                STA350_CONFA_MCS_MASK | STA350_CONFA_IR_MASK,
+                                confa);
+       if (ret < 0)
+               return ret;
+
+       ret = regmap_update_bits(sta350->regmap, STA350_CONFB,
+                                STA350_CONFB_SAI_MASK | STA350_CONFB_SAIFB,
+                                confb);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int sta350_startup_sequence(struct sta350_priv *sta350)
+{
+       if (sta350->gpiod_power_down)
+               gpiod_set_value(sta350->gpiod_power_down, 1);
+
+       if (sta350->gpiod_nreset) {
+               gpiod_set_value(sta350->gpiod_nreset, 0);
+               mdelay(1);
+               gpiod_set_value(sta350->gpiod_nreset, 1);
+               mdelay(1);
+       }
+
+       return 0;
+}
+
+/**
+ * sta350_set_bias_level - DAPM callback
+ * @codec: the codec device
+ * @level: DAPM power level
+ *
+ * This is called by ALSA to put the codec into low power mode
+ * or to wake it up.  If the codec is powered off completely
+ * all registers must be restored after power on.
+ */
+static int sta350_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       dev_dbg(codec->dev, "level = %d\n", level);
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               /* Full power on */
+               regmap_update_bits(sta350->regmap, STA350_CONFF,
+                                  STA350_CONFF_PWDN | STA350_CONFF_EAPD,
+                                  STA350_CONFF_PWDN | STA350_CONFF_EAPD);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       ret = regulator_bulk_enable(
+                               ARRAY_SIZE(sta350->supplies),
+                               sta350->supplies);
+                       if (ret < 0) {
+                               dev_err(codec->dev,
+                                       "Failed to enable supplies: %d\n",
+                                       ret);
+                               return ret;
+                       }
+                       sta350_startup_sequence(sta350);
+                       sta350_cache_sync(codec);
+               }
+
+               /* Power down */
+               regmap_update_bits(sta350->regmap, STA350_CONFF,
+                                  STA350_CONFF_PWDN | STA350_CONFF_EAPD,
+                                  0);
+
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               /* The chip runs through the power down sequence for us */
+               regmap_update_bits(sta350->regmap, STA350_CONFF,
+                                  STA350_CONFF_PWDN | STA350_CONFF_EAPD, 0);
+
+               /* power down: low */
+               if (sta350->gpiod_power_down)
+                       gpiod_set_value(sta350->gpiod_power_down, 0);
+
+               if (sta350->gpiod_nreset)
+                       gpiod_set_value(sta350->gpiod_nreset, 0);
+
+               regulator_bulk_disable(ARRAY_SIZE(sta350->supplies),
+                                      sta350->supplies);
+               break;
+       }
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+static const struct snd_soc_dai_ops sta350_dai_ops = {
+       .hw_params      = sta350_hw_params,
+       .set_sysclk     = sta350_set_dai_sysclk,
+       .set_fmt        = sta350_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver sta350_dai = {
+       .name = "sta350-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = STA350_RATES,
+               .formats = STA350_FORMATS,
+       },
+       .ops = &sta350_dai_ops,
+};
+
+#ifdef CONFIG_PM
+static int sta350_suspend(struct snd_soc_codec *codec)
+{
+       sta350_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int sta350_resume(struct snd_soc_codec *codec)
+{
+       sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       return 0;
+}
+#else
+#define sta350_suspend NULL
+#define sta350_resume NULL
+#endif
+
+static int sta350_probe(struct snd_soc_codec *codec)
+{
+       struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+       struct sta350_platform_data *pdata = sta350->pdata;
+       int i, ret = 0, thermal = 0;
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(sta350->supplies),
+                                   sta350->supplies);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+               return ret;
+       }
+
+       ret = sta350_startup_sequence(sta350);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to startup device\n");
+               return ret;
+       }
+
+       /* CONFA */
+       if (!pdata->thermal_warning_recovery)
+               thermal |= STA350_CONFA_TWAB;
+       if (!pdata->thermal_warning_adjustment)
+               thermal |= STA350_CONFA_TWRB;
+       if (!pdata->fault_detect_recovery)
+               thermal |= STA350_CONFA_FDRB;
+       regmap_update_bits(sta350->regmap, STA350_CONFA,
+                          STA350_CONFA_TWAB | STA350_CONFA_TWRB |
+                          STA350_CONFA_FDRB,
+                          thermal);
+
+       /* CONFC */
+       regmap_update_bits(sta350->regmap, STA350_CONFC,
+                          STA350_CONFC_OM_MASK,
+                          pdata->ffx_power_output_mode
+                               << STA350_CONFC_OM_SHIFT);
+       regmap_update_bits(sta350->regmap, STA350_CONFC,
+                          STA350_CONFC_CSZ_MASK,
+                          pdata->drop_compensation_ns
+                               << STA350_CONFC_CSZ_SHIFT);
+       regmap_update_bits(sta350->regmap,
+                          STA350_CONFC,
+                          STA350_CONFC_OCRB,
+                          pdata->oc_warning_adjustment ?
+                               STA350_CONFC_OCRB : 0);
+
+       /* CONFE */
+       regmap_update_bits(sta350->regmap, STA350_CONFE,
+                          STA350_CONFE_MPCV,
+                          pdata->max_power_use_mpcc ?
+                               STA350_CONFE_MPCV : 0);
+       regmap_update_bits(sta350->regmap, STA350_CONFE,
+                          STA350_CONFE_MPC,
+                          pdata->max_power_correction ?
+                               STA350_CONFE_MPC : 0);
+       regmap_update_bits(sta350->regmap, STA350_CONFE,
+                          STA350_CONFE_AME,
+                          pdata->am_reduction_mode ?
+                               STA350_CONFE_AME : 0);
+       regmap_update_bits(sta350->regmap, STA350_CONFE,
+                          STA350_CONFE_PWMS,
+                          pdata->odd_pwm_speed_mode ?
+                               STA350_CONFE_PWMS : 0);
+       regmap_update_bits(sta350->regmap, STA350_CONFE,
+                          STA350_CONFE_DCCV,
+                          pdata->distortion_compensation ?
+                               STA350_CONFE_DCCV : 0);
+       /*  CONFF */
+       regmap_update_bits(sta350->regmap, STA350_CONFF,
+                          STA350_CONFF_IDE,
+                          pdata->invalid_input_detect_mute ?
+                               STA350_CONFF_IDE : 0);
+       regmap_update_bits(sta350->regmap, STA350_CONFF,
+                          STA350_CONFF_OCFG_MASK,
+                          pdata->output_conf
+                               << STA350_CONFF_OCFG_SHIFT);
+
+       /* channel to output mapping */
+       regmap_update_bits(sta350->regmap, STA350_C1CFG,
+                          STA350_CxCFG_OM_MASK,
+                          pdata->ch1_output_mapping
+                               << STA350_CxCFG_OM_SHIFT);
+       regmap_update_bits(sta350->regmap, STA350_C2CFG,
+                          STA350_CxCFG_OM_MASK,
+                          pdata->ch2_output_mapping
+                               << STA350_CxCFG_OM_SHIFT);
+       regmap_update_bits(sta350->regmap, STA350_C3CFG,
+                          STA350_CxCFG_OM_MASK,
+                          pdata->ch3_output_mapping
+                               << STA350_CxCFG_OM_SHIFT);
+
+       /* miscellaneous registers */
+       regmap_update_bits(sta350->regmap, STA350_MISC1,
+                          STA350_MISC1_CPWMEN,
+                          pdata->activate_mute_output ?
+                               STA350_MISC1_CPWMEN : 0);
+       regmap_update_bits(sta350->regmap, STA350_MISC1,
+                          STA350_MISC1_BRIDGOFF,
+                          pdata->bridge_immediate_off ?
+                               STA350_MISC1_BRIDGOFF : 0);
+       regmap_update_bits(sta350->regmap, STA350_MISC1,
+                          STA350_MISC1_NSHHPEN,
+                          pdata->noise_shape_dc_cut ?
+                               STA350_MISC1_NSHHPEN : 0);
+       regmap_update_bits(sta350->regmap, STA350_MISC1,
+                          STA350_MISC1_RPDNEN,
+                          pdata->powerdown_master_vol ?
+                               STA350_MISC1_RPDNEN: 0);
+
+       regmap_update_bits(sta350->regmap, STA350_MISC2,
+                          STA350_MISC2_PNDLSL_MASK,
+                          pdata->powerdown_delay_divider
+                               << STA350_MISC2_PNDLSL_SHIFT);
+
+       /* initialize coefficient shadow RAM with reset values */
+       for (i = 4; i <= 49; i += 5)
+               sta350->coef_shadow[i] = 0x400000;
+       for (i = 50; i <= 54; i++)
+               sta350->coef_shadow[i] = 0x7fffff;
+       sta350->coef_shadow[55] = 0x5a9df7;
+       sta350->coef_shadow[56] = 0x7fffff;
+       sta350->coef_shadow[59] = 0x7fffff;
+       sta350->coef_shadow[60] = 0x400000;
+       sta350->coef_shadow[61] = 0x400000;
+
+       sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       /* Bias level configuration will have done an extra enable */
+       regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies);
+
+       return 0;
+}
+
+static int sta350_remove(struct snd_soc_codec *codec)
+{
+       struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+
+       sta350_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies);
+
+       return 0;
+}
+
+static const struct snd_soc_codec_driver sta350_codec = {
+       .probe =                sta350_probe,
+       .remove =               sta350_remove,
+       .suspend =              sta350_suspend,
+       .resume =               sta350_resume,
+       .set_bias_level =       sta350_set_bias_level,
+       .controls =             sta350_snd_controls,
+       .num_controls =         ARRAY_SIZE(sta350_snd_controls),
+       .dapm_widgets =         sta350_dapm_widgets,
+       .num_dapm_widgets =     ARRAY_SIZE(sta350_dapm_widgets),
+       .dapm_routes =          sta350_dapm_routes,
+       .num_dapm_routes =      ARRAY_SIZE(sta350_dapm_routes),
+};
+
+static const struct regmap_config sta350_regmap = {
+       .reg_bits =             8,
+       .val_bits =             8,
+       .max_register =         STA350_MISC2,
+       .reg_defaults =         sta350_regs,
+       .num_reg_defaults =     ARRAY_SIZE(sta350_regs),
+       .cache_type =           REGCACHE_RBTREE,
+       .wr_table =             &sta350_write_regs,
+       .rd_table =             &sta350_read_regs,
+       .volatile_table =       &sta350_volatile_regs,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id st350_dt_ids[] = {
+       { .compatible = "st,sta350", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, st350_dt_ids);
+
+static const char * const sta350_ffx_modes[] = {
+       [STA350_FFX_PM_DROP_COMP]               = "drop-compensation",
+       [STA350_FFX_PM_TAPERED_COMP]            = "tapered-compensation",
+       [STA350_FFX_PM_FULL_POWER]              = "full-power-mode",
+       [STA350_FFX_PM_VARIABLE_DROP_COMP]      = "variable-drop-compensation",
+};
+
+static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
+{
+       struct device_node *np = dev->of_node;
+       struct sta350_platform_data *pdata;
+       const char *ffx_power_mode;
+       u16 tmp;
+       u8 tmp8;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+
+       of_property_read_u8(np, "st,output-conf",
+                           &pdata->output_conf);
+       of_property_read_u8(np, "st,ch1-output-mapping",
+                           &pdata->ch1_output_mapping);
+       of_property_read_u8(np, "st,ch2-output-mapping",
+                           &pdata->ch2_output_mapping);
+       of_property_read_u8(np, "st,ch3-output-mapping",
+                           &pdata->ch3_output_mapping);
+
+       if (of_get_property(np, "st,thermal-warning-recovery", NULL))
+               pdata->thermal_warning_recovery = 1;
+       if (of_get_property(np, "st,thermal-warning-adjustment", NULL))
+               pdata->thermal_warning_adjustment = 1;
+       if (of_get_property(np, "st,fault-detect-recovery", NULL))
+               pdata->fault_detect_recovery = 1;
+
+       pdata->ffx_power_output_mode = STA350_FFX_PM_VARIABLE_DROP_COMP;
+       if (!of_property_read_string(np, "st,ffx-power-output-mode",
+                                    &ffx_power_mode)) {
+               int i, mode = -EINVAL;
+
+               for (i = 0; i < ARRAY_SIZE(sta350_ffx_modes); i++)
+                       if (!strcasecmp(ffx_power_mode, sta350_ffx_modes[i]))
+                               mode = i;
+
+               if (mode < 0)
+                       dev_warn(dev, "Unsupported ffx output mode: %s\n",
+                                ffx_power_mode);
+               else
+                       pdata->ffx_power_output_mode = mode;
+       }
+
+       tmp = 140;
+       of_property_read_u16(np, "st,drop-compensation-ns", &tmp);
+       pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20;
+
+       if (of_get_property(np, "st,overcurrent-warning-adjustment", NULL))
+               pdata->oc_warning_adjustment = 1;
+
+       /* CONFE */
+       if (of_get_property(np, "st,max-power-use-mpcc", NULL))
+               pdata->max_power_use_mpcc = 1;
+
+       if (of_get_property(np, "st,max-power-correction", NULL))
+               pdata->max_power_correction = 1;
+
+       if (of_get_property(np, "st,am-reduction-mode", NULL))
+               pdata->am_reduction_mode = 1;
+
+       if (of_get_property(np, "st,odd-pwm-speed-mode", NULL))
+               pdata->odd_pwm_speed_mode = 1;
+
+       if (of_get_property(np, "st,distortion-compensation", NULL))
+               pdata->distortion_compensation = 1;
+
+       /* CONFF */
+       if (of_get_property(np, "st,invalid-input-detect-mute", NULL))
+               pdata->invalid_input_detect_mute = 1;
+
+       /* MISC */
+       if (of_get_property(np, "st,activate-mute-output", NULL))
+               pdata->activate_mute_output = 1;
+
+       if (of_get_property(np, "st,bridge-immediate-off", NULL))
+               pdata->bridge_immediate_off = 1;
+
+       if (of_get_property(np, "st,noise-shape-dc-cut", NULL))
+               pdata->noise_shape_dc_cut = 1;
+
+       if (of_get_property(np, "st,powerdown-master-volume", NULL))
+               pdata->powerdown_master_vol = 1;
+
+       if (!of_property_read_u8(np, "st,powerdown-delay-divider", &tmp8)) {
+               if (is_power_of_2(tmp8) && tmp8 >= 1 && tmp8 <= 128)
+                       pdata->powerdown_delay_divider = ilog2(tmp8);
+               else
+                       dev_warn(dev, "Unsupported powerdown delay divider %d\n",
+                                tmp8);
+       }
+
+       sta350->pdata = pdata;
+
+       return 0;
+}
+#endif
+
+static int sta350_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct device *dev = &i2c->dev;
+       struct sta350_priv *sta350;
+       int ret, i;
+
+       sta350 = devm_kzalloc(dev, sizeof(struct sta350_priv), GFP_KERNEL);
+       if (!sta350)
+               return -ENOMEM;
+
+       mutex_init(&sta350->coeff_lock);
+       sta350->pdata = dev_get_platdata(dev);
+
+#ifdef CONFIG_OF
+       if (dev->of_node) {
+               ret = sta350_probe_dt(dev, sta350);
+               if (ret < 0)
+                       return ret;
+       }
+#endif
+
+       /* GPIOs */
+       sta350->gpiod_nreset = devm_gpiod_get(dev, "reset");
+       if (IS_ERR(sta350->gpiod_nreset)) {
+               ret = PTR_ERR(sta350->gpiod_nreset);
+               if (ret != -ENOENT && ret != -ENOSYS)
+                       return ret;
+
+               sta350->gpiod_nreset = NULL;
+       } else {
+               gpiod_direction_output(sta350->gpiod_nreset, 0);
+       }
+
+       sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down");
+       if (IS_ERR(sta350->gpiod_power_down)) {
+               ret = PTR_ERR(sta350->gpiod_power_down);
+               if (ret != -ENOENT && ret != -ENOSYS)
+                       return ret;
+
+               sta350->gpiod_power_down = NULL;
+       } else {
+               gpiod_direction_output(sta350->gpiod_power_down, 0);
+       }
+
+       /* regulators */
+       for (i = 0; i < ARRAY_SIZE(sta350->supplies); i++)
+               sta350->supplies[i].supply = sta350_supply_names[i];
+
+       ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sta350->supplies),
+                                     sta350->supplies);
+       if (ret < 0) {
+               dev_err(dev, "Failed to request supplies: %d\n", ret);
+               return ret;
+       }
+
+       sta350->regmap = devm_regmap_init_i2c(i2c, &sta350_regmap);
+       if (IS_ERR(sta350->regmap)) {
+               ret = PTR_ERR(sta350->regmap);
+               dev_err(dev, "Failed to init regmap: %d\n", ret);
+               return ret;
+       }
+
+       i2c_set_clientdata(i2c, sta350);
+
+       ret = snd_soc_register_codec(dev, &sta350_codec, &sta350_dai, 1);
+       if (ret < 0)
+               dev_err(dev, "Failed to register codec (%d)\n", ret);
+
+       return ret;
+}
+
+static int sta350_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static const struct i2c_device_id sta350_i2c_id[] = {
+       { "sta350", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, sta350_i2c_id);
+
+static struct i2c_driver sta350_i2c_driver = {
+       .driver = {
+               .name = "sta350",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(st350_dt_ids),
+       },
+       .probe =    sta350_i2c_probe,
+       .remove =   sta350_i2c_remove,
+       .id_table = sta350_i2c_id,
+};
+
+module_i2c_driver(sta350_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC STA350 driver");
+MODULE_AUTHOR("Sven Brandau <info@brandau.biz>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sta350.h b/sound/soc/codecs/sta350.h
new file mode 100644 (file)
index 0000000..fb72852
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Codec driver for ST STA350 2.1-channel high-efficiency digital audio system
+ *
+ * Copyright: 2011 Raumfeld GmbH
+ * Author: Sven Brandau <info@brandau.biz>
+ *
+ * based on code from:
+ *      Raumfeld GmbH
+ *        Johannes Stezenbach <js@sig21.net>
+ *     Wolfson Microelectronics PLC.
+ *       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.
+ */
+#ifndef _ASOC_STA_350_H
+#define _ASOC_STA_350_H
+
+/* STA50 register addresses */
+
+#define STA350_REGISTER_COUNT  0x4D
+#define STA350_COEF_COUNT 62
+
+#define STA350_CONFA   0x00
+#define STA350_CONFB    0x01
+#define STA350_CONFC    0x02
+#define STA350_CONFD    0x03
+#define STA350_CONFE    0x04
+#define STA350_CONFF    0x05
+#define STA350_MMUTE    0x06
+#define STA350_MVOL     0x07
+#define STA350_C1VOL    0x08
+#define STA350_C2VOL    0x09
+#define STA350_C3VOL    0x0a
+#define STA350_AUTO1    0x0b
+#define STA350_AUTO2    0x0c
+#define STA350_AUTO3    0x0d
+#define STA350_C1CFG    0x0e
+#define STA350_C2CFG    0x0f
+#define STA350_C3CFG    0x10
+#define STA350_TONE     0x11
+#define STA350_L1AR     0x12
+#define STA350_L1ATRT   0x13
+#define STA350_L2AR     0x14
+#define STA350_L2ATRT   0x15
+#define STA350_CFADDR2  0x16
+#define STA350_B1CF1    0x17
+#define STA350_B1CF2    0x18
+#define STA350_B1CF3    0x19
+#define STA350_B2CF1    0x1a
+#define STA350_B2CF2    0x1b
+#define STA350_B2CF3    0x1c
+#define STA350_A1CF1    0x1d
+#define STA350_A1CF2    0x1e
+#define STA350_A1CF3    0x1f
+#define STA350_A2CF1    0x20
+#define STA350_A2CF2    0x21
+#define STA350_A2CF3    0x22
+#define STA350_B0CF1    0x23
+#define STA350_B0CF2    0x24
+#define STA350_B0CF3    0x25
+#define STA350_CFUD     0x26
+#define STA350_MPCC1    0x27
+#define STA350_MPCC2    0x28
+#define STA350_DCC1     0x29
+#define STA350_DCC2     0x2a
+#define STA350_FDRC1    0x2b
+#define STA350_FDRC2    0x2c
+#define STA350_STATUS   0x2d
+/* reserved: 0x2d - 0x30 */
+#define STA350_EQCFG    0x31
+#define STA350_EATH1    0x32
+#define STA350_ERTH1    0x33
+#define STA350_EATH2    0x34
+#define STA350_ERTH2    0x35
+#define STA350_CONFX    0x36
+#define STA350_SVCA     0x37
+#define STA350_SVCB     0x38
+#define STA350_RMS0A    0x39
+#define STA350_RMS0B    0x3a
+#define STA350_RMS0C    0x3b
+#define STA350_RMS1A    0x3c
+#define STA350_RMS1B    0x3d
+#define STA350_RMS1C    0x3e
+#define STA350_EVOLRES  0x3f
+/* reserved: 0x40 - 0x47 */
+#define STA350_NSHAPE   0x48
+#define STA350_CTXB4B1  0x49
+#define STA350_CTXB7B5  0x4a
+#define STA350_MISC1    0x4b
+#define STA350_MISC2    0x4c
+
+/* 0x00 CONFA */
+#define STA350_CONFA_MCS_MASK  0x03
+#define STA350_CONFA_MCS_SHIFT 0
+#define STA350_CONFA_IR_MASK   0x18
+#define STA350_CONFA_IR_SHIFT  3
+#define STA350_CONFA_TWRB      BIT(5)
+#define STA350_CONFA_TWAB      BIT(6)
+#define STA350_CONFA_FDRB      BIT(7)
+
+/* 0x01 CONFB */
+#define STA350_CONFB_SAI_MASK  0x0f
+#define STA350_CONFB_SAI_SHIFT 0
+#define STA350_CONFB_SAIFB     BIT(4)
+#define STA350_CONFB_DSCKE     BIT(5)
+#define STA350_CONFB_C1IM      BIT(6)
+#define STA350_CONFB_C2IM      BIT(7)
+
+/* 0x02 CONFC */
+#define STA350_CONFC_OM_MASK   0x03
+#define STA350_CONFC_OM_SHIFT  0
+#define STA350_CONFC_CSZ_MASK  0x3c
+#define STA350_CONFC_CSZ_SHIFT 2
+#define STA350_CONFC_OCRB      BIT(7)
+
+/* 0x03 CONFD */
+#define STA350_CONFD_HPB_SHIFT 0
+#define STA350_CONFD_DEMP_SHIFT        1
+#define STA350_CONFD_DSPB_SHIFT        2
+#define STA350_CONFD_PSL_SHIFT 3
+#define STA350_CONFD_BQL_SHIFT 4
+#define STA350_CONFD_DRC_SHIFT 5
+#define STA350_CONFD_ZDE_SHIFT 6
+#define STA350_CONFD_SME_SHIFT 7
+
+/* 0x04 CONFE */
+#define STA350_CONFE_MPCV      BIT(0)
+#define STA350_CONFE_MPCV_SHIFT        0
+#define STA350_CONFE_MPC       BIT(1)
+#define STA350_CONFE_MPC_SHIFT 1
+#define STA350_CONFE_NSBW      BIT(2)
+#define STA350_CONFE_NSBW_SHIFT        2
+#define STA350_CONFE_AME       BIT(3)
+#define STA350_CONFE_AME_SHIFT 3
+#define STA350_CONFE_PWMS      BIT(4)
+#define STA350_CONFE_PWMS_SHIFT        4
+#define STA350_CONFE_DCCV      BIT(5)
+#define STA350_CONFE_DCCV_SHIFT        5
+#define STA350_CONFE_ZCE       BIT(6)
+#define STA350_CONFE_ZCE_SHIFT 6
+#define STA350_CONFE_SVE       BIT(7)
+#define STA350_CONFE_SVE_SHIFT 7
+
+/* 0x05 CONFF */
+#define STA350_CONFF_OCFG_MASK 0x03
+#define STA350_CONFF_OCFG_SHIFT        0
+#define STA350_CONFF_IDE       BIT(2)
+#define STA350_CONFF_BCLE      BIT(3)
+#define STA350_CONFF_LDTE      BIT(4)
+#define STA350_CONFF_ECLE      BIT(5)
+#define STA350_CONFF_PWDN      BIT(6)
+#define STA350_CONFF_EAPD      BIT(7)
+
+/* 0x06 MMUTE */
+#define STA350_MMUTE_MMUTE             0x01
+#define STA350_MMUTE_MMUTE_SHIFT       0
+#define STA350_MMUTE_C1M               0x02
+#define STA350_MMUTE_C1M_SHIFT         1
+#define STA350_MMUTE_C2M               0x04
+#define STA350_MMUTE_C2M_SHIFT         2
+#define STA350_MMUTE_C3M               0x08
+#define STA350_MMUTE_C3M_SHIFT         3
+#define STA350_MMUTE_LOC_MASK          0xC0
+#define STA350_MMUTE_LOC_SHIFT         6
+
+/* 0x0b AUTO1 */
+#define STA350_AUTO1_AMGC_MASK 0x30
+#define STA350_AUTO1_AMGC_SHIFT        4
+
+/* 0x0c AUTO2 */
+#define STA350_AUTO2_AMAME     0x01
+#define STA350_AUTO2_AMAM_MASK 0x0e
+#define STA350_AUTO2_AMAM_SHIFT        1
+#define STA350_AUTO2_XO_MASK   0xf0
+#define STA350_AUTO2_XO_SHIFT  4
+
+/* 0x0d AUTO3 */
+#define STA350_AUTO3_PEQ_MASK  0x1f
+#define STA350_AUTO3_PEQ_SHIFT 0
+
+/* 0x0e 0x0f 0x10 CxCFG */
+#define STA350_CxCFG_TCB_SHIFT 0
+#define STA350_CxCFG_EQBP_SHIFT        1
+#define STA350_CxCFG_VBP_SHIFT 2
+#define STA350_CxCFG_BO_SHIFT  3
+#define STA350_CxCFG_LS_SHIFT  4
+#define STA350_CxCFG_OM_MASK   0xc0
+#define STA350_CxCFG_OM_SHIFT  6
+
+/* 0x11 TONE */
+#define STA350_TONE_BTC_SHIFT  0
+#define STA350_TONE_TTC_SHIFT  4
+
+/* 0x12 0x13 0x14 0x15 limiter attack/release */
+#define STA350_LxA_SHIFT       0
+#define STA350_LxR_SHIFT       4
+
+/* 0x26 CFUD */
+#define STA350_CFUD_W1         0x01
+#define STA350_CFUD_WA         0x02
+#define STA350_CFUD_R1         0x04
+#define STA350_CFUD_RA         0x08
+
+
+/* biquad filter coefficient table offsets */
+#define STA350_C1_BQ_BASE      0
+#define STA350_C2_BQ_BASE      20
+#define STA350_CH_BQ_NUM       4
+#define STA350_BQ_NUM_COEF     5
+#define STA350_XO_HP_BQ_BASE   40
+#define STA350_XO_LP_BQ_BASE   45
+#define STA350_C1_PRESCALE     50
+#define STA350_C2_PRESCALE     51
+#define STA350_C1_POSTSCALE    52
+#define STA350_C2_POSTSCALE    53
+#define STA350_C3_POSTSCALE    54
+#define STA350_TW_POSTSCALE    55
+#define STA350_C1_MIX1         56
+#define STA350_C1_MIX2         57
+#define STA350_C2_MIX1         58
+#define STA350_C2_MIX2         59
+#define STA350_C3_MIX1         60
+#define STA350_C3_MIX2         61
+
+/* miscellaneous register 1 */
+#define STA350_MISC1_CPWMEN    BIT(2)
+#define STA350_MISC1_BRIDGOFF  BIT(5)
+#define STA350_MISC1_NSHHPEN   BIT(6)
+#define STA350_MISC1_RPDNEN    BIT(7)
+
+/* miscellaneous register 2 */
+#define STA350_MISC2_PNDLSL_MASK       0x1c
+#define STA350_MISC2_PNDLSL_SHIFT      2
+
+#endif /* _ASOC_STA_350_H */
index a895a5e..d48491a 100644 (file)
@@ -272,7 +272,7 @@ static int tas5086_set_deemph(struct snd_soc_codec *codec)
 static int tas5086_get_deemph(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = priv->deemph;
@@ -283,7 +283,7 @@ static int tas5086_get_deemph(struct snd_kcontrol *kcontrol,
 static int tas5086_put_deemph(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
 
        priv->deemph = ucontrol->value.enumerated.item[0];
index b73c94e..f137019 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/i2c.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/regmap.h>
 #include <sound/soc.h>
 
index 20864ee..686b8b8 100644 (file)
@@ -82,7 +82,7 @@ static const DECLARE_TLV_DB_SCALE(sidetone_vol_tlv, -1800, 300, 0);
 static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        u16 val, reg;
 
        val = (ucontrol->value.integer.value[0] & 0x07);
@@ -105,7 +105,7 @@ static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol,
 static int snd_soc_tlv320aic23_get_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        u16 val;
 
        val = snd_soc_read(codec, TLV320AIC23_ANLG) & (0x1C0);
index d1929de..2341910 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/i2c.h>
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
+#include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/slab.h>
 #include <sound/core.h>
index d7349bc..e12fafb 100644 (file)
@@ -169,7 +169,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
        mask <<= shift;
        val <<= shift;
 
-       change = snd_soc_test_bits(codec, val, mask, reg);
+       change = snd_soc_test_bits(codec, reg, mask, val);
        if (change) {
                update.kcontrol = kcontrol;
                update.reg = reg;
index 6bfc8a1..df3a750 100644 (file)
@@ -442,7 +442,7 @@ static int dac33_playback_event(struct snd_soc_dapm_widget *w,
 static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol,
                         struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.integer.value[0] = dac33->fifo_mode;
@@ -453,7 +453,7 @@ static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol,
 static int dac33_set_fifo_mode(struct snd_kcontrol *kcontrol,
                         struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
        int ret = 0;
 
@@ -1540,7 +1540,7 @@ static int dac33_i2c_probe(struct i2c_client *client,
        for (i = 0; i < ARRAY_SIZE(dac33->supplies); i++)
                dac33->supplies[i].supply = dac33_supply_names[i];
 
-       ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(dac33->supplies),
+       ret = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(dac33->supplies),
                                 dac33->supplies);
 
        if (ret != 0) {
@@ -1551,11 +1551,9 @@ static int dac33_i2c_probe(struct i2c_client *client,
        ret = snd_soc_register_codec(&client->dev,
                        &soc_codec_dev_tlv320dac33, &dac33_dai, 1);
        if (ret < 0)
-               goto err_register;
+               goto err_get;
 
        return ret;
-err_register:
-       regulator_bulk_free(ARRAY_SIZE(dac33->supplies), dac33->supplies);
 err_get:
        if (dac33->power_gpio >= 0)
                gpio_free(dac33->power_gpio);
@@ -1573,8 +1571,6 @@ static int dac33_i2c_remove(struct i2c_client *client)
        if (dac33->power_gpio >= 0)
                gpio_free(dac33->power_gpio);
 
-       regulator_bulk_free(ARRAY_SIZE(dac33->supplies), dac33->supplies);
-
        snd_soc_unregister_codec(&client->dev);
        return 0;
 }
index b27c396..8fc5a64 100644 (file)
@@ -30,6 +30,7 @@
 #include <sound/tpa6130a2-plat.h>
 #include <sound/soc.h>
 #include <sound/tlv.h>
+#include <linux/of.h>
 #include <linux/of_gpio.h>
 
 #include "tpa6130a2.h"
index 975e0f7..69e12a3 100644 (file)
@@ -830,7 +830,7 @@ static int snd_soc_get_volsw_twl4030(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);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int shift = mc->shift;
        unsigned int rshift = mc->rshift;
@@ -859,7 +859,7 @@ static int snd_soc_put_volsw_twl4030(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);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int shift = mc->shift;
        unsigned int rshift = mc->rshift;
@@ -888,7 +888,7 @@ static int snd_soc_get_volsw_r2_twl4030(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);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int reg2 = mc->rreg;
        unsigned int shift = mc->shift;
@@ -915,7 +915,7 @@ static int snd_soc_put_volsw_r2_twl4030(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);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int reg2 = mc->rreg;
        unsigned int shift = mc->shift;
@@ -956,7 +956,7 @@ static SOC_ENUM_SINGLE_DECL(twl4030_op_modes_enum,
 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);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
 
        if (twl4030->configured) {
index bd3a206..0f6067f 100644 (file)
@@ -484,7 +484,7 @@ static SOC_ENUM_SINGLE_EXT_DECL(twl6040_power_mode_enum,
 static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = priv->hs_power_mode;
@@ -495,7 +495,7 @@ static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol,
 static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
        int high_perf = ucontrol->value.enumerated.item[0];
        int ret = 0;
@@ -512,7 +512,7 @@ static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol,
 static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = priv->pll_power_mode;
@@ -523,7 +523,7 @@ static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol,
 static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
 
        priv->pll_power_mode = ucontrol->value.enumerated.item[0];
index 6be5f80..4ead0dc 100644 (file)
@@ -172,7 +172,7 @@ out:
 static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol,
                                      struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.integer.value[0] = wl1273->mode;
@@ -190,7 +190,7 @@ static const char * const wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" };
 static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol,
                                      struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
 
        if (wl1273->mode == ucontrol->value.integer.value[0])
@@ -214,7 +214,7 @@ static SOC_ENUM_SINGLE_EXT_DECL(wl1273_enum, wl1273_audio_route);
 static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
 
        dev_dbg(codec->dev, "%s: enter.\n", __func__);
@@ -227,7 +227,7 @@ static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol,
 static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
        int val, r = 0;
 
@@ -251,7 +251,7 @@ static SOC_ENUM_SINGLE_EXT_DECL(wl1273_audio_enum, wl1273_audio_strings);
 static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol,
                                    struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
 
        dev_dbg(codec->dev, "%s: enter.\n", __func__);
@@ -264,7 +264,7 @@ static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol,
 static int snd_wl1273_fm_volume_put(struct snd_kcontrol *kcontrol,
                                    struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
        int r;
 
index 83a2c87..a4c352c 100644 (file)
@@ -607,7 +607,7 @@ static int wm2000_anc_set_mode(struct wm2000_priv *wm2000)
 static int wm2000_anc_mode_get(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
 
        ucontrol->value.enumerated.item[0] = wm2000->anc_active;
@@ -618,7 +618,7 @@ static int wm2000_anc_mode_get(struct snd_kcontrol *kcontrol,
 static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
        int anc_active = ucontrol->value.enumerated.item[0];
        int ret;
@@ -640,7 +640,7 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol,
 static int wm2000_speaker_get(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
 
        ucontrol->value.enumerated.item[0] = wm2000->spk_ena;
@@ -651,7 +651,7 @@ static int wm2000_speaker_get(struct snd_kcontrol *kcontrol,
 static int wm2000_speaker_put(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
        int val = ucontrol->value.enumerated.item[0];
        int ret;
index 2e721e0..cdea9d9 100644 (file)
@@ -1083,7 +1083,7 @@ static int wm2200_mixer_values[] = {
 
 #define WM2200_MUX_CTL_DECL(name) \
        const struct snd_kcontrol_new name##_mux =      \
-               SOC_DAPM_VALUE_ENUM("Route", name##_enum)
+               SOC_DAPM_ENUM("Route", name##_enum)
 
 #define WM2200_MIXER_ENUMS(name, base_reg) \
        static WM2200_MUX_ENUM_DECL(name##_in1_enum, base_reg);      \
@@ -1207,7 +1207,7 @@ WM2200_MIXER_ENUMS(LHPF1, WM2200_LHPF1MIX_INPUT_1_SOURCE);
 WM2200_MIXER_ENUMS(LHPF2, WM2200_LHPF2MIX_INPUT_1_SOURCE);
 
 #define WM2200_MUX(name, ctrl) \
-       SND_SOC_DAPM_VALUE_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+       SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
 
 #define WM2200_MIXER_WIDGETS(name, name_str)   \
        WM2200_MUX(name_str " Input 1", &name##_in1_mux), \
index eca983f..91a9ea2 100644 (file)
@@ -390,7 +390,7 @@ static int wm5100_mixer_values[] = {
 
 #define WM5100_MUX_CTL_DECL(name) \
        const struct snd_kcontrol_new name##_mux =      \
-               SOC_DAPM_VALUE_ENUM("Route", name##_enum)
+               SOC_DAPM_ENUM("Route", name##_enum)
 
 #define WM5100_MIXER_ENUMS(name, base_reg) \
        static WM5100_MUX_ENUM_DECL(name##_in1_enum, base_reg);      \
@@ -448,7 +448,7 @@ WM5100_MIXER_ENUMS(LHPF3, WM5100_HPLP3MIX_INPUT_1_SOURCE);
 WM5100_MIXER_ENUMS(LHPF4, WM5100_HPLP4MIX_INPUT_1_SOURCE);
 
 #define WM5100_MUX(name, ctrl) \
-       SND_SOC_DAPM_VALUE_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+       SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
 
 #define WM5100_MIXER_WIDGETS(name, name_str)   \
        WM5100_MUX(name_str " Input 1", &name##_in1_mux), \
index dcf1d12..289b64d 100644 (file)
@@ -764,8 +764,8 @@ SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
 SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
 SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
 
-SOC_VALUE_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
-SOC_VALUE_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
 
 ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE),
@@ -814,9 +814,9 @@ SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
                 ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
                 0xbf, 0, digital_tlv),
 
-SOC_VALUE_ENUM("HPOUT1 OSR", wm5102_hpout_osr[0]),
-SOC_VALUE_ENUM("HPOUT2 OSR", wm5102_hpout_osr[1]),
-SOC_VALUE_ENUM("EPOUT OSR", wm5102_hpout_osr[2]),
+SOC_ENUM("HPOUT1 OSR", wm5102_hpout_osr[0]),
+SOC_ENUM("HPOUT2 OSR", wm5102_hpout_osr[1]),
+SOC_ENUM("EPOUT OSR", wm5102_hpout_osr[2]),
 
 SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE,
           ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0),
@@ -970,7 +970,7 @@ static const struct soc_enum wm5102_aec_loopback =
                              wm5102_aec_loopback_values);
 
 static const struct snd_kcontrol_new wm5102_aec_loopback_mux =
-       SOC_DAPM_VALUE_ENUM("AEC Loopback", wm5102_aec_loopback);
+       SOC_DAPM_ENUM("AEC Loopback", wm5102_aec_loopback);
 
 static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = {
 SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
@@ -1204,7 +1204,7 @@ SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
 
 ARIZONA_DSP_WIDGETS(DSP1, "DSP1"),
 
-SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
                       ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
                       &wm5102_aec_loopback_mux),
 
@@ -1760,10 +1760,6 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
        struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
        int ret;
 
-       ret = snd_soc_codec_set_cache_io(codec, priv->core.arizona->regmap);
-       if (ret != 0)
-               return ret;
-
        ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 2);
        if (ret != 0)
                return ret;
@@ -1802,9 +1798,17 @@ static unsigned int wm5102_digital_vu[] = {
        ARIZONA_DAC_DIGITAL_VOLUME_5R,
 };
 
+static struct regmap *wm5102_get_regmap(struct device *dev)
+{
+       struct wm5102_priv *priv = dev_get_drvdata(dev);
+
+       return priv->core.arizona->regmap;
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_wm5102 = {
        .probe = wm5102_codec_probe,
        .remove = wm5102_codec_remove,
+       .get_regmap = wm5102_get_regmap,
 
        .idle_bias_off = true,
 
index df5a38d..2e5fcb5 100644 (file)
@@ -324,13 +324,13 @@ SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
 SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
 SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
 
-SOC_VALUE_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
-SOC_VALUE_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
-SOC_VALUE_ENUM("ISRC3 FSL", arizona_isrc_fsl[2]),
-SOC_VALUE_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]),
-SOC_VALUE_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),
-SOC_VALUE_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]),
-SOC_VALUE_ENUM("ASRC RATE 1", arizona_asrc_rate1),
+SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+SOC_ENUM("ISRC3 FSL", arizona_isrc_fsl[2]),
+SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]),
+SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),
+SOC_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]),
+SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1),
 
 ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),
@@ -367,6 +367,11 @@ SOC_SINGLE("HPOUT2 SC Protect Switch", ARIZONA_HP2_SHORT_CIRCUIT_CTRL,
 SOC_SINGLE("HPOUT3 SC Protect Switch", ARIZONA_HP3_SHORT_CIRCUIT_CTRL,
           ARIZONA_HP3_SC_ENA_SHIFT, 1, 0),
 
+SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
+          ARIZONA_OUT5_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_6L,
+          ARIZONA_OUT6_OSR_SHIFT, 1, 0),
+
 SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
             ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
 SOC_DOUBLE_R("HPOUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
@@ -592,7 +597,7 @@ static const struct soc_enum wm5110_aec_loopback =
                              wm5110_aec_loopback_values);
 
 static const struct snd_kcontrol_new wm5110_aec_loopback_mux =
-       SOC_DAPM_VALUE_ENUM("AEC Loopback", wm5110_aec_loopback);
+       SOC_DAPM_ENUM("AEC Loopback", wm5110_aec_loopback);
 
 static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
 SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
@@ -774,7 +779,7 @@ SND_SOC_DAPM_PGA("ISRC3DEC3", ARIZONA_ISRC_3_CTRL_3,
 SND_SOC_DAPM_PGA("ISRC3DEC4", ARIZONA_ISRC_3_CTRL_3,
                 ARIZONA_ISRC3_DEC3_ENA_SHIFT, 0, NULL, 0),
 
-SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
                       ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
                       &wm5110_aec_loopback_mux),
 
@@ -1589,10 +1594,6 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
 
        priv->core.arizona->dapm = &codec->dapm;
 
-       ret = snd_soc_codec_set_cache_io(codec, priv->core.arizona->regmap);
-       if (ret != 0)
-               return ret;
-
        arizona_init_spk(codec);
        arizona_init_gpio(codec);
 
@@ -1633,9 +1634,17 @@ static unsigned int wm5110_digital_vu[] = {
        ARIZONA_DAC_DIGITAL_VOLUME_6R,
 };
 
+static struct regmap *wm5110_get_regmap(struct device *dev)
+{
+       struct wm5110_priv *priv = dev_get_drvdata(dev);
+
+       return priv->core.arizona->regmap;
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
        .probe = wm5110_codec_probe,
        .remove = wm5110_codec_remove,
+       .get_regmap = wm5110_get_regmap,
 
        .idle_bias_off = true,
 
index 757256b..392285e 100644 (file)
@@ -302,7 +302,7 @@ static int pga_event(struct snd_soc_dapm_widget *w,
 static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8350_data *wm8350_priv = snd_soc_codec_get_drvdata(codec);
        struct wm8350_output *out = NULL;
        struct soc_mixer_control *mc =
@@ -345,7 +345,7 @@ static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol,
 static int wm8350_get_volsw_2r(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8350_data *wm8350_priv = snd_soc_codec_get_drvdata(codec);
        struct wm8350_output *out1 = &wm8350_priv->out1;
        struct wm8350_output *out2 = &wm8350_priv->out2;
@@ -1505,8 +1505,6 @@ static  int wm8350_codec_probe(struct snd_soc_codec *codec)
        if (ret != 0)
                return ret;
 
-       snd_soc_codec_set_cache_io(codec, wm8350->regmap);
-
        /* Put the codec into reset if it wasn't already */
        wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
 
@@ -1608,11 +1606,19 @@ static int  wm8350_codec_remove(struct snd_soc_codec *codec)
        return 0;
 }
 
+static struct regmap *wm8350_get_regmap(struct device *dev)
+{
+       struct wm8350 *wm8350 = dev_get_platdata(dev);
+
+       return wm8350->regmap;
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_wm8350 = {
        .probe =        wm8350_codec_probe,
        .remove =       wm8350_codec_remove,
        .suspend =      wm8350_suspend,
        .resume =       wm8350_resume,
+       .get_regmap =   wm8350_get_regmap,
        .set_bias_level = wm8350_set_bias_level,
 
        .controls = wm8350_snd_controls,
index 146564f..06e913d 100644 (file)
@@ -93,7 +93,7 @@ static const DECLARE_TLV_DB_SCALE(out_sidetone_tlv, -3600, 0, 0);
 static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
         struct snd_ctl_elem_value *ucontrol)
 {
-        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        int reg = mc->reg;
@@ -1318,8 +1318,6 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec)
        priv->wm8400 = wm8400;
        priv->codec = codec;
 
-       snd_soc_codec_set_cache_io(codec, wm8400->regmap);
-
        ret = devm_regulator_bulk_get(wm8400->dev,
                                 ARRAY_SIZE(power), &power[0]);
        if (ret != 0) {
@@ -1361,11 +1359,19 @@ static int  wm8400_codec_remove(struct snd_soc_codec *codec)
        return 0;
 }
 
+static struct regmap *wm8400_get_regmap(struct device *dev)
+{
+       struct wm8400 *wm8400 = dev_get_platdata(dev);
+
+       return wm8400->regmap;
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_wm8400 = {
        .probe =        wm8400_codec_probe,
        .remove =       wm8400_codec_remove,
        .suspend =      wm8400_suspend,
        .resume =       wm8400_resume,
+       .get_regmap =   wm8400_get_regmap,
        .set_bias_level = wm8400_set_bias_level,
 
        .controls = wm8400_snd_controls,
index af7ed8b..7665ff6 100644 (file)
@@ -252,7 +252,7 @@ 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);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
        unsigned int reg = mc->reg;
        unsigned int reg2 = mc->rreg;
index d74f439..5ada616 100644 (file)
@@ -119,7 +119,7 @@ static int wm8731_set_deemph(struct snd_soc_codec *codec)
 static int wm8731_get_deemph(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = wm8731->deemph;
@@ -130,7 +130,7 @@ static int wm8731_get_deemph(struct snd_kcontrol *kcontrol,
 static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
        int deemph = ucontrol->value.enumerated.item[0];
        int ret = 0;
@@ -586,7 +586,7 @@ static int wm8731_probe(struct snd_soc_codec *codec)
        for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
                wm8731->supplies[i].supply = wm8731_supply_names[i];
 
-       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies),
+       ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies),
                                 wm8731->supplies);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
@@ -597,7 +597,7 @@ static int wm8731_probe(struct snd_soc_codec *codec)
                                    wm8731->supplies);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
-               goto err_regulator_get;
+               return ret;
        }
 
        ret = wm8731_reset(codec);
@@ -624,8 +624,6 @@ static int wm8731_probe(struct snd_soc_codec *codec)
 
 err_regulator_enable:
        regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
-err_regulator_get:
-       regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
 
        return ret;
 }
@@ -638,7 +636,6 @@ static int wm8731_remove(struct snd_soc_codec *codec)
        wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
        regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
-       regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
 
        return 0;
 }
index cbb8d55..53e57b4 100644 (file)
@@ -234,7 +234,7 @@ SOC_ENUM_SINGLE(WM8753_OUTCTL, 2, 2, wm8753_rout2_phase),
 static int wm8753_get_dai(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.integer.value[0] = wm8753->dai_func;
@@ -244,7 +244,7 @@ static int wm8753_get_dai(struct snd_kcontrol *kcontrol,
 static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
        u16 ioctl;
 
index ee76f0f..d96e596 100644 (file)
@@ -63,6 +63,7 @@ struct wm8804_priv {
        struct regmap *regmap;
        struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES];
        struct notifier_block disable_nb[WM8804_NUM_SUPPLIES];
+       int mclk_div;
 };
 
 static int txsrc_get(struct snd_kcontrol *kcontrol,
@@ -106,7 +107,7 @@ static int txsrc_get(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec;
        unsigned int src;
 
-       codec = snd_kcontrol_chip(kcontrol);
+       codec = snd_soc_kcontrol_codec(kcontrol);
        src = snd_soc_read(codec, WM8804_SPDTX4);
        if (src & 0x40)
                ucontrol->value.integer.value[0] = 1;
@@ -122,7 +123,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec;
        unsigned int src, txpwr;
 
-       codec = snd_kcontrol_chip(kcontrol);
+       codec = snd_soc_kcontrol_codec(kcontrol);
 
        if (ucontrol->value.integer.value[0] != 0
                        && ucontrol->value.integer.value[0] != 1)
@@ -318,7 +319,7 @@ static struct {
 
 #define FIXED_PLL_SIZE ((1ULL << 22) * 10)
 static int pll_factors(struct pll_div *pll_div, unsigned int target,
-                      unsigned int source)
+                      unsigned int source, unsigned int mclk_div)
 {
        u64 Kpart;
        unsigned long int K, Ndiv, Nmod, tmp;
@@ -330,7 +331,8 @@ static int pll_factors(struct pll_div *pll_div, unsigned int target,
         */
        for (i = 0; i < ARRAY_SIZE(post_table); i++) {
                tmp = target * post_table[i].div;
-               if (tmp >= 90000000 && tmp <= 100000000) {
+               if ((tmp >= 90000000 && tmp <= 100000000) &&
+                   (mclk_div == post_table[i].mclkdiv)) {
                        pll_div->freqmode = post_table[i].freqmode;
                        pll_div->mclkdiv = post_table[i].mclkdiv;
                        target *= post_table[i].div;
@@ -387,8 +389,12 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
        } else {
                int ret;
                struct pll_div pll_div;
+               struct wm8804_priv *wm8804;
 
-               ret = pll_factors(&pll_div, freq_out, freq_in);
+               wm8804 = snd_soc_codec_get_drvdata(codec);
+
+               ret = pll_factors(&pll_div, freq_out, freq_in,
+                                 wm8804->mclk_div);
                if (ret)
                        return ret;
 
@@ -452,6 +458,7 @@ static int wm8804_set_clkdiv(struct snd_soc_dai *dai,
                             int div_id, int div)
 {
        struct snd_soc_codec *codec;
+       struct wm8804_priv *wm8804;
 
        codec = dai->codec;
        switch (div_id) {
@@ -459,6 +466,10 @@ static int wm8804_set_clkdiv(struct snd_soc_dai *dai,
                snd_soc_update_bits(codec, WM8804_PLL5, 0x30,
                                    (div & 0x3) << 4);
                break;
+       case WM8804_MCLK_DIV:
+               wm8804 = snd_soc_codec_get_drvdata(codec);
+               wm8804->mclk_div = div;
+               break;
        default:
                dev_err(dai->dev, "Unknown clock divider: %d\n", div_id);
                return -EINVAL;
@@ -535,7 +546,6 @@ static int wm8804_remove(struct snd_soc_codec *codec)
        for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i)
                regulator_unregister_notifier(wm8804->supplies[i].consumer,
                                              &wm8804->disable_nb[i]);
-       regulator_bulk_free(ARRAY_SIZE(wm8804->supplies), wm8804->supplies);
        return 0;
 }
 
@@ -549,7 +559,7 @@ static int wm8804_probe(struct snd_soc_codec *codec)
        for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++)
                wm8804->supplies[i].supply = wm8804_supply_names[i];
 
-       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8804->supplies),
+       ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8804->supplies),
                                 wm8804->supplies);
        if (ret) {
                dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
@@ -574,7 +584,7 @@ static int wm8804_probe(struct snd_soc_codec *codec)
                                    wm8804->supplies);
        if (ret) {
                dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
-               goto err_reg_get;
+               return ret;
        }
 
        id1 = snd_soc_read(codec, WM8804_RST_DEVID1);
@@ -619,8 +629,6 @@ static int wm8804_probe(struct snd_soc_codec *codec)
 
 err_reg_enable:
        regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies);
-err_reg_get:
-       regulator_bulk_free(ARRAY_SIZE(wm8804->supplies), wm8804->supplies);
        return ret;
 }
 
index 8ec14f5..e72d4f4 100644 (file)
@@ -57,5 +57,9 @@
 #define WM8804_CLKOUT_SRC_OSCCLK               4
 
 #define WM8804_CLKOUT_DIV                      1
+#define WM8804_MCLK_DIV                                2
+
+#define WM8804_MCLKDIV_256FS                   0
+#define WM8804_MCLKDIV_128FS                   1
 
 #endif  /* _WM8804_H */
index b0084a1..b84940c 100644 (file)
@@ -439,7 +439,7 @@ static int wm8903_set_deemph(struct snd_soc_codec *codec)
 static int wm8903_get_deemph(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = wm8903->deemph;
@@ -450,7 +450,7 @@ static int wm8903_get_deemph(struct snd_kcontrol *kcontrol,
 static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
        int deemph = ucontrol->value.enumerated.item[0];
        int ret = 0;
index 49c35c3..f7c5499 100644 (file)
@@ -391,7 +391,7 @@ static void wm8904_set_drc(struct snd_soc_codec *codec)
 static int wm8904_put_drc_enum(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
        struct wm8904_pdata *pdata = wm8904->pdata;
        int value = ucontrol->value.integer.value[0];
@@ -409,7 +409,7 @@ static int wm8904_put_drc_enum(struct snd_kcontrol *kcontrol,
 static int wm8904_get_drc_enum(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = wm8904->drc_cfg;
@@ -462,7 +462,7 @@ static void wm8904_set_retune_mobile(struct snd_soc_codec *codec)
 static int wm8904_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
                                         struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
        struct wm8904_pdata *pdata = wm8904->pdata;
        int value = ucontrol->value.integer.value[0];
@@ -480,7 +480,7 @@ static int wm8904_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
 static int wm8904_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
                                         struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = wm8904->retune_mobile_cfg;
@@ -520,7 +520,7 @@ static int wm8904_set_deemph(struct snd_soc_codec *codec)
 static int wm8904_get_deemph(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = wm8904->deemph;
@@ -530,7 +530,7 @@ static int wm8904_get_deemph(struct snd_kcontrol *kcontrol,
 static int wm8904_put_deemph(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
        int deemph = ucontrol->value.enumerated.item[0];
 
@@ -570,7 +570,7 @@ static SOC_ENUM_SINGLE_DECL(hpf_mode, WM8904_ADC_DIGITAL_0, 5,
 static int wm8904_adc_osr_put(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int val;
        int ret;
 
index fecd4e4..2a35108 100644 (file)
@@ -390,7 +390,7 @@ static int wm8955_set_deemph(struct snd_soc_codec *codec)
 static int wm8955_get_deemph(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = wm8955->deemph;
@@ -400,7 +400,7 @@ static int wm8955_get_deemph(struct snd_kcontrol *kcontrol,
 static int wm8955_put_deemph(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
        int deemph = ucontrol->value.enumerated.item[0];
 
@@ -898,7 +898,7 @@ static int wm8955_probe(struct snd_soc_codec *codec)
        for (i = 0; i < ARRAY_SIZE(wm8955->supplies); i++)
                wm8955->supplies[i].supply = wm8955_supply_names[i];
 
-       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8955->supplies),
+       ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8955->supplies),
                                 wm8955->supplies);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
@@ -909,7 +909,7 @@ static int wm8955_probe(struct snd_soc_codec *codec)
                                    wm8955->supplies);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
-               goto err_get;
+               return ret;
        }
 
        ret = wm8955_reset(codec);
@@ -961,17 +961,12 @@ static int wm8955_probe(struct snd_soc_codec *codec)
 
 err_enable:
        regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
-err_get:
-       regulator_bulk_free(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
        return ret;
 }
 
 static int wm8955_remove(struct snd_soc_codec *codec)
 {
-       struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
-
        wm8955_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       regulator_bulk_free(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
        return 0;
 }
 
index 7ac2e51..b2ebb10 100644 (file)
@@ -456,7 +456,7 @@ static int wm8958_dsp2_busy(struct wm8994_priv *wm8994, int aif)
 static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        struct wm8994 *control = wm8994->wm8994;
        int value = ucontrol->value.integer.value[0];
@@ -478,7 +478,7 @@ static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
 static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
@@ -500,7 +500,7 @@ static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
        int mbc = kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
@@ -512,7 +512,7 @@ static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
        int mbc = kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        if (wm8994->mbc_ena[mbc] == ucontrol->value.integer.value[0])
@@ -546,7 +546,7 @@ static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
 static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        struct wm8994 *control = wm8994->wm8994;
        int value = ucontrol->value.integer.value[0];
@@ -568,7 +568,7 @@ static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
 static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = wm8994->vss_cfg;
@@ -579,7 +579,7 @@ static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol,
 static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        struct wm8994 *control = wm8994->wm8994;
        int value = ucontrol->value.integer.value[0];
@@ -601,7 +601,7 @@ static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
 static int wm8958_get_vss_hpf_enum(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = wm8994->vss_hpf_cfg;
@@ -623,7 +623,7 @@ static int wm8958_vss_get(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
        int vss = kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.integer.value[0] = wm8994->vss_ena[vss];
@@ -635,7 +635,7 @@ static int wm8958_vss_put(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
        int vss = kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        if (wm8994->vss_ena[vss] == ucontrol->value.integer.value[0])
@@ -684,7 +684,7 @@ static int wm8958_hpf_get(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
        int hpf = kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        if (hpf < 3)
@@ -699,7 +699,7 @@ static int wm8958_hpf_put(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
        int hpf = kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        if (hpf < 3) {
@@ -746,7 +746,7 @@ static int wm8958_hpf_put(struct snd_kcontrol *kcontrol,
 static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        struct wm8994 *control = wm8994->wm8994;
        int value = ucontrol->value.integer.value[0];
@@ -768,7 +768,7 @@ static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,
 static int wm8958_get_enh_eq_enum(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = wm8994->enh_eq_cfg;
@@ -790,7 +790,7 @@ static int wm8958_enh_eq_get(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
        int eq = kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.integer.value[0] = wm8994->enh_eq_ena[eq];
@@ -802,7 +802,7 @@ static int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
        int eq = kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        if (wm8994->enh_eq_ena[eq] == ucontrol->value.integer.value[0])
index d04e9ca..a145d04 100644 (file)
@@ -178,7 +178,7 @@ static int wm8960_set_deemph(struct snd_soc_codec *codec)
 static int wm8960_get_deemph(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.enumerated.item[0] = wm8960->deemph;
@@ -188,7 +188,7 @@ static int wm8960_get_deemph(struct snd_kcontrol *kcontrol,
 static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
        int deemph = ucontrol->value.enumerated.item[0];
 
index ecd26dd..ca2fda9 100644 (file)
@@ -74,11 +74,9 @@ struct wm8962_priv {
        struct regulator_bulk_data supplies[WM8962_NUM_SUPPLIES];
        struct notifier_block disable_nb[WM8962_NUM_SUPPLIES];
 
-#if IS_ENABLED(CONFIG_INPUT)
        struct input_dev *beep;
        struct work_struct beep_work;
        int beep_rate;
-#endif
 
 #ifdef CONFIG_GPIOLIB
        struct gpio_chip gpio_chip;
@@ -1552,7 +1550,7 @@ static int wm8962_dsp2_ena_get(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
        int shift = kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
 
        ucontrol->value.integer.value[0] = !!(wm8962->dsp2_ena & 1 << shift);
@@ -1564,7 +1562,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
        int shift = kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
        int old = wm8962->dsp2_ena;
        int ret = 0;
@@ -1602,7 +1600,7 @@ out:
 static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol,
                            struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        int ret;
 
        /* Apply the update (if any) */
@@ -1632,7 +1630,7 @@ static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol,
 static int wm8962_put_spk_sw(struct snd_kcontrol *kcontrol,
                            struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        int ret;
 
        /* Apply the update (if any) */
@@ -3154,7 +3152,6 @@ int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 }
 EXPORT_SYMBOL_GPL(wm8962_mic_detect);
 
-#if IS_ENABLED(CONFIG_INPUT)
 static int beep_rates[] = {
        500, 1000, 2000, 4000,
 };
@@ -3286,15 +3283,6 @@ static void wm8962_free_beep(struct snd_soc_codec *codec)
 
        snd_soc_update_bits(codec, WM8962_BEEP_GENERATOR_1, WM8962_BEEP_ENA,0);
 }
-#else
-static void wm8962_init_beep(struct snd_soc_codec *codec)
-{
-}
-
-static void wm8962_free_beep(struct snd_soc_codec *codec)
-{
-}
-#endif
 
 static void wm8962_set_gpio_mode(struct wm8962_priv *wm8962, int gpio)
 {
index 2b9bfa5..19d5baa 100644 (file)
@@ -552,7 +552,7 @@ static const struct snd_soc_dapm_route wm8983_audio_map[] = {
 static int eqmode_get(struct snd_kcontrol *kcontrol,
                      struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int reg;
 
        reg = snd_soc_read(codec, WM8983_EQ1_LOW_SHELF);
@@ -567,7 +567,7 @@ static int eqmode_get(struct snd_kcontrol *kcontrol,
 static int eqmode_put(struct snd_kcontrol *kcontrol,
                      struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int regpwr2, regpwr3;
        unsigned int reg_eq;
 
index 5473dc9..0f5780c 100644 (file)
@@ -526,7 +526,7 @@ static const struct snd_soc_dapm_route wm8985_dapm_routes[] = {
 static int eqmode_get(struct snd_kcontrol *kcontrol,
                      struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int reg;
 
        reg = snd_soc_read(codec, WM8985_EQ1_LOW_SHELF);
@@ -541,7 +541,7 @@ static int eqmode_get(struct snd_kcontrol *kcontrol,
 static int eqmode_put(struct snd_kcontrol *kcontrol,
                      struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int regpwr2, regpwr3;
        unsigned int reg_eq;
 
@@ -984,7 +984,6 @@ static int wm8985_remove(struct snd_soc_codec *codec)
 
        wm8985 = snd_soc_codec_get_drvdata(codec);
        wm8985_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       regulator_bulk_free(ARRAY_SIZE(wm8985->supplies), wm8985->supplies);
        return 0;
 }
 
@@ -999,7 +998,7 @@ static int wm8985_probe(struct snd_soc_codec *codec)
        for (i = 0; i < ARRAY_SIZE(wm8985->supplies); i++)
                wm8985->supplies[i].supply = wm8985_supply_names[i];
 
-       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8985->supplies),
+       ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8985->supplies),
                                 wm8985->supplies);
        if (ret) {
                dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
@@ -1010,7 +1009,7 @@ static int wm8985_probe(struct snd_soc_codec *codec)
                                    wm8985->supplies);
        if (ret) {
                dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
-               goto err_reg_get;
+               return ret;
        }
 
        ret = wm8985_reset(codec);
@@ -1032,8 +1031,6 @@ static int wm8985_probe(struct snd_soc_codec *codec)
 
 err_reg_enable:
        regulator_bulk_disable(ARRAY_SIZE(wm8985->supplies), wm8985->supplies);
-err_reg_get:
-       regulator_bulk_free(ARRAY_SIZE(wm8985->supplies), wm8985->supplies);
        return ret;
 }
 
index 3a1ae4f..d3fea46 100644 (file)
@@ -268,7 +268,7 @@ static const struct soc_enum wm8988_lline_enum =
                              wm8988_line_texts,
                              wm8988_line_values);
 static const struct snd_kcontrol_new wm8988_left_line_controls =
-       SOC_DAPM_VALUE_ENUM("Route", wm8988_lline_enum);
+       SOC_DAPM_ENUM("Route", wm8988_lline_enum);
 
 static const struct soc_enum wm8988_rline_enum =
        SOC_VALUE_ENUM_SINGLE(WM8988_ROUTM1, 0, 7,
@@ -276,7 +276,7 @@ static const struct soc_enum wm8988_rline_enum =
                              wm8988_line_texts,
                              wm8988_line_values);
 static const struct snd_kcontrol_new wm8988_right_line_controls =
-       SOC_DAPM_VALUE_ENUM("Route", wm8988_lline_enum);
+       SOC_DAPM_ENUM("Route", wm8988_lline_enum);
 
 /* Left Mixer */
 static const struct snd_kcontrol_new wm8988_left_mixer_controls[] = {
@@ -304,7 +304,7 @@ static const struct soc_enum wm8988_lpga_enum =
                              wm8988_pga_sel,
                              wm8988_pga_val);
 static const struct snd_kcontrol_new wm8988_left_pga_controls =
-       SOC_DAPM_VALUE_ENUM("Route", wm8988_lpga_enum);
+       SOC_DAPM_ENUM("Route", wm8988_lpga_enum);
 
 /* Right PGA Mux */
 static const struct soc_enum wm8988_rpga_enum =
@@ -313,7 +313,7 @@ static const struct soc_enum wm8988_rpga_enum =
                              wm8988_pga_sel,
                              wm8988_pga_val);
 static const struct snd_kcontrol_new wm8988_right_pga_controls =
-       SOC_DAPM_VALUE_ENUM("Route", wm8988_rpga_enum);
+       SOC_DAPM_ENUM("Route", wm8988_rpga_enum);
 
 /* Differential Mux */
 static const char *wm8988_diff_sel[] = {"Line 1", "Line 2"};
index c413c19..b5c1f0f 100644 (file)
@@ -132,7 +132,7 @@ static const DECLARE_TLV_DB_SCALE(out_sidetone_tlv, -3600, 0, 0);
 static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        int reg = mc->reg;
index 844cc4a..b8fd284 100644 (file)
@@ -154,7 +154,7 @@ static const unsigned int out_sidetone_tlv[] = {
 static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
                                      struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        int reg = kcontrol->private_value & 0xff;
        int ret;
        u16 val;
index 6303537..247b390 100644 (file)
@@ -298,7 +298,7 @@ static int wm8994_put_drc_sw(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);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        int mask, ret;
 
        /* Can't enable both ADC and DAC paths simultaneously */
@@ -355,7 +355,7 @@ static int wm8994_get_drc(const char *name)
 static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        struct wm8994 *control = wm8994->wm8994;
        struct wm8994_pdata *pdata = &control->pdata;
@@ -378,7 +378,7 @@ static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol,
 static int wm8994_get_drc_enum(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        int drc = wm8994_get_drc(kcontrol->id.name);
 
@@ -462,7 +462,7 @@ static int wm8994_get_retune_mobile_block(const char *name)
 static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
                                         struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        struct wm8994 *control = wm8994->wm8994;
        struct wm8994_pdata *pdata = &control->pdata;
@@ -485,7 +485,7 @@ static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
 static int wm8994_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
                                         struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
        int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
 
@@ -1347,10 +1347,10 @@ static const char *adc_mux_text[] = {
 static SOC_ENUM_SINGLE_VIRT_DECL(adc_enum, adc_mux_text);
 
 static const struct snd_kcontrol_new adcl_mux =
-       SOC_DAPM_ENUM_VIRT("ADCL Mux", adc_enum);
+       SOC_DAPM_ENUM("ADCL Mux", adc_enum);
 
 static const struct snd_kcontrol_new adcr_mux =
-       SOC_DAPM_ENUM_VIRT("ADCR Mux", adc_enum);
+       SOC_DAPM_ENUM("ADCR Mux", adc_enum);
 
 static const struct snd_kcontrol_new left_speaker_mixer[] = {
 SOC_DAPM_SINGLE("DAC2 Switch", WM8994_SPEAKER_MIXER, 9, 1, 0),
@@ -1651,15 +1651,15 @@ SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0),
 };
 
 static const struct snd_soc_dapm_widget wm8994_adc_revd_widgets[] = {
-SND_SOC_DAPM_VIRT_MUX_E("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux,
+SND_SOC_DAPM_MUX_E("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux,
                        adc_mux_ev, SND_SOC_DAPM_PRE_PMU),
-SND_SOC_DAPM_VIRT_MUX_E("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux,
+SND_SOC_DAPM_MUX_E("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux,
                        adc_mux_ev, SND_SOC_DAPM_PRE_PMU),
 };
 
 static const struct snd_soc_dapm_widget wm8994_adc_widgets[] = {
-SND_SOC_DAPM_VIRT_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux),
-SND_SOC_DAPM_VIRT_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux),
+SND_SOC_DAPM_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux),
+SND_SOC_DAPM_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux),
 };
 
 static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = {
@@ -3999,8 +3999,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 
        wm8994->hubs.codec = codec;
 
-       snd_soc_codec_set_cache_io(codec, control->regmap);
-
        mutex_init(&wm8994->accdet_lock);
        INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap,
                          wm1811_jackdet_bootstrap);
@@ -4434,11 +4432,19 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
        return 0;
 }
 
+static struct regmap *wm8994_get_regmap(struct device *dev)
+{
+       struct wm8994 *control = dev_get_drvdata(dev->parent);
+
+       return control->regmap;
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_wm8994 = {
        .probe =        wm8994_codec_probe,
        .remove =       wm8994_codec_remove,
        .suspend =      wm8994_codec_suspend,
        .resume =       wm8994_codec_resume,
+       .get_regmap =   wm8994_get_regmap,
        .set_bias_level = wm8994_set_bias_level,
 };
 
index d3152cf..863a2c3 100644 (file)
@@ -885,10 +885,10 @@ static const char *adc_mux_text[] = {
 static SOC_ENUM_SINGLE_VIRT_DECL(adc_enum, adc_mux_text);
 
 static const struct snd_kcontrol_new adcl_mux =
-       SOC_DAPM_ENUM_VIRT("ADCL Mux", adc_enum);
+       SOC_DAPM_ENUM("ADCL Mux", adc_enum);
 
 static const struct snd_kcontrol_new adcr_mux =
-       SOC_DAPM_ENUM_VIRT("ADCR Mux", adc_enum);
+       SOC_DAPM_ENUM("ADCR Mux", adc_enum);
 
 static const char *spk_src_text[] = {
        "DAC1L", "DAC1R", "DAC2L", "DAC2R"
@@ -948,10 +948,8 @@ static const struct snd_soc_dapm_widget wm8995_dapm_widgets[] = {
        SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", "AIF1 Capture",
                0, WM8995_POWER_MANAGEMENT_3, 10, 0),
 
-       SND_SOC_DAPM_VIRT_MUX("ADCL Mux", SND_SOC_NOPM, 1, 0,
-               &adcl_mux),
-       SND_SOC_DAPM_VIRT_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0,
-               &adcr_mux),
+       SND_SOC_DAPM_MUX("ADCL Mux", SND_SOC_NOPM, 1, 0, &adcl_mux),
+       SND_SOC_DAPM_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0, &adcr_mux),
 
        SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8995_POWER_MANAGEMENT_3, 5, 0),
        SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8995_POWER_MANAGEMENT_3, 4, 0),
index c6cbb3b..6926633 100644 (file)
@@ -412,7 +412,7 @@ static int wm8996_get_retune_mobile_block(const char *name)
 static int wm8996_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
                                         struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
        struct wm8996_pdata *pdata = &wm8996->pdata;
        int block = wm8996_get_retune_mobile_block(kcontrol->id.name);
@@ -434,7 +434,7 @@ static int wm8996_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
 static int wm8996_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
                                         struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
        int block = wm8996_get_retune_mobile_block(kcontrol->id.name);
 
index 004186b..bb9b47b 100644 (file)
@@ -245,8 +245,8 @@ SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
 SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
 SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
 
-SOC_VALUE_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
-SOC_VALUE_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
 
 ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE),
@@ -286,8 +286,8 @@ SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
                 ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
                 0xbf, 0, digital_tlv),
 
-SOC_VALUE_ENUM("HPOUT1 OSR", wm8997_hpout_osr[0]),
-SOC_VALUE_ENUM("EPOUT OSR", wm8997_hpout_osr[1]),
+SOC_ENUM("HPOUT1 OSR", wm8997_hpout_osr[0]),
+SOC_ENUM("EPOUT OSR", wm8997_hpout_osr[1]),
 
 SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
 SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
@@ -405,7 +405,7 @@ static const struct soc_enum wm8997_aec_loopback =
                              wm8997_aec_loopback_values);
 
 static const struct snd_kcontrol_new wm8997_aec_loopback_mux =
-       SOC_DAPM_VALUE_ENUM("AEC Loopback", wm8997_aec_loopback);
+       SOC_DAPM_ENUM("AEC Loopback", wm8997_aec_loopback);
 
 static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = {
 SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
@@ -604,7 +604,7 @@ SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
                    ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
                    ARIZONA_SLIMRX8_ENA_SHIFT, 0),
 
-SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
                       ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
                       &wm8997_aec_loopback_mux),
 
@@ -1051,11 +1051,6 @@ static struct snd_soc_dai_driver wm8997_dai[] = {
 static int wm8997_codec_probe(struct snd_soc_codec *codec)
 {
        struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
-       int ret;
-
-       ret = snd_soc_codec_set_cache_io(codec, priv->core.arizona->regmap);
-       if (ret != 0)
-               return ret;
 
        arizona_init_spk(codec);
 
@@ -1086,9 +1081,17 @@ static unsigned int wm8997_digital_vu[] = {
        ARIZONA_DAC_DIGITAL_VOLUME_5R,
 };
 
+static struct regmap *wm8997_get_regmap(struct device *dev)
+{
+       struct wm8997_priv *priv = dev_get_drvdata(dev);
+
+       return priv->core.arizona->regmap;
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_wm8997 = {
        .probe = wm8997_codec_probe,
        .remove = wm8997_codec_remove,
+       .get_regmap =   wm8997_get_regmap,
 
        .idle_bias_off = true,
 
index d18eff3..185eb97 100644 (file)
@@ -340,7 +340,7 @@ static SOC_ENUM_SINGLE_DECL(speaker_mode, WM9081_ANALOGUE_SPEAKER_2, 6,
 static int speaker_mode_get(struct snd_kcontrol *kcontrol,
                            struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int reg;
 
        reg = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_2);
@@ -361,7 +361,7 @@ static int speaker_mode_get(struct snd_kcontrol *kcontrol,
 static int speaker_mode_put(struct snd_kcontrol *kcontrol,
                            struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        unsigned int reg_pwr = snd_soc_read(codec, WM9081_POWER_MANAGEMENT);
        unsigned int reg2 = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_2);
 
index acea892..2a9c6d1 100644 (file)
@@ -74,8 +74,7 @@ static const char *wm9713_rec_src[] =
        "Mono Out", "Zh"};
 static const char *wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
 static const char *wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"};
-static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv",
-       "Mono Vmid", "Inv Vmid"};
+static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv"};
 static const char *wm9713_spk_pga[] =
        {"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid",
        "Speaker Vmid", "Inv Vmid"};
index bb5f7b4..0600271 100644 (file)
@@ -242,7 +242,7 @@ struct wm_coeff_ctl {
 static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
 
@@ -254,7 +254,7 @@ static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
 static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
 
@@ -1543,16 +1543,16 @@ static void wm_adsp2_boot_work(struct work_struct *work)
                ret = regmap_read(dsp->regmap,
                                  dsp->base + ADSP2_CLOCKING, &val);
                if (ret != 0) {
-                       dev_err(dsp->dev, "Failed to read clocking: %d\n", ret);
+                       adsp_err(dsp, "Failed to read clocking: %d\n", ret);
                        return;
                }
 
                if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
                        ret = regulator_enable(dsp->dvfs);
                        if (ret != 0) {
-                               dev_err(dsp->dev,
-                                       "Failed to enable supply: %d\n",
-                                       ret);
+                               adsp_err(dsp,
+                                        "Failed to enable supply: %d\n",
+                                        ret);
                                return;
                        }
 
@@ -1560,9 +1560,9 @@ static void wm_adsp2_boot_work(struct work_struct *work)
                                                    1800000,
                                                    1800000);
                        if (ret != 0) {
-                               dev_err(dsp->dev,
-                                       "Failed to raise supply: %d\n",
-                                       ret);
+                               adsp_err(dsp,
+                                        "Failed to raise supply: %d\n",
+                                        ret);
                                return;
                        }
                }
@@ -1625,7 +1625,7 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
                break;
        default:
                break;
-       };
+       }
 
        return 0;
 }
@@ -1672,15 +1672,15 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                        ret = regulator_set_voltage(dsp->dvfs, 1200000,
                                                    1800000);
                        if (ret != 0)
-                               dev_warn(dsp->dev,
-                                        "Failed to lower supply: %d\n",
-                                        ret);
+                               adsp_warn(dsp,
+                                         "Failed to lower supply: %d\n",
+                                         ret);
 
                        ret = regulator_disable(dsp->dvfs);
                        if (ret != 0)
-                               dev_err(dsp->dev,
-                                       "Failed to enable supply: %d\n",
-                                       ret);
+                               adsp_err(dsp,
+                                        "Failed to enable supply: %d\n",
+                                        ret);
                }
 
                list_for_each_entry(ctl, &dsp->ctl_list, list)
@@ -1732,28 +1732,25 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
                adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
                if (IS_ERR(adsp->dvfs)) {
                        ret = PTR_ERR(adsp->dvfs);
-                       dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
+                       adsp_err(adsp, "Failed to get DCVDD: %d\n", ret);
                        return ret;
                }
 
                ret = regulator_enable(adsp->dvfs);
                if (ret != 0) {
-                       dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
-                               ret);
+                       adsp_err(adsp, "Failed to enable DCVDD: %d\n", ret);
                        return ret;
                }
 
                ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
                if (ret != 0) {
-                       dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
-                               ret);
+                       adsp_err(adsp, "Failed to initialise DVFS: %d\n", ret);
                        return ret;
                }
 
                ret = regulator_disable(adsp->dvfs);
                if (ret != 0) {
-                       dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
-                               ret);
+                       adsp_err(adsp, "Failed to disable DCVDD: %d\n", ret);
                        return ret;
                }
        }
index b620966..916817f 100644 (file)
@@ -337,7 +337,7 @@ static void enable_dc_servo(struct snd_soc_codec *codec)
 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);
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
        int ret;
 
index a8ec1fc..50a0987 100644 (file)
@@ -18,7 +18,7 @@ config SND_DAVINCI_SOC_GENERIC_EVM
 
 config SND_AM33XX_SOC_EVM
        tristate "SoC Audio for the AM33XX chip based boards"
-       depends on SND_DAVINCI_SOC && SOC_AM33XX
+       depends on SND_DAVINCI_SOC && SOC_AM33XX && I2C
        select SND_DAVINCI_SOC_GENERIC_EVM
        help
          Say Y or M if you want to add support for SoC audio on AM33XX
@@ -28,7 +28,7 @@ config SND_AM33XX_SOC_EVM
 
 config SND_DAVINCI_SOC_EVM
        tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
-       depends on SND_DAVINCI_SOC
+       depends on SND_DAVINCI_SOC && I2C
        depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM
        select SND_DAVINCI_SOC_GENERIC_EVM
        help
@@ -56,7 +56,7 @@ endchoice
 
 config  SND_DM6467_SOC_EVM
        tristate "SoC Audio support for DaVinci DM6467 EVM"
-       depends on SND_DAVINCI_SOC && MACH_DAVINCI_DM6467_EVM
+       depends on SND_DAVINCI_SOC && MACH_DAVINCI_DM6467_EVM && I2C
        select SND_DAVINCI_SOC_GENERIC_EVM
        select SND_SOC_SPDIF
 
@@ -65,7 +65,7 @@ config  SND_DM6467_SOC_EVM
 
 config  SND_DA830_SOC_EVM
        tristate "SoC Audio support for DA830/OMAP-L137 EVM"
-       depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA830_EVM
+       depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA830_EVM && I2C
        select SND_DAVINCI_SOC_GENERIC_EVM
 
        help
@@ -74,7 +74,7 @@ config  SND_DA830_SOC_EVM
 
 config  SND_DA850_SOC_EVM
        tristate "SoC Audio support for DA850/OMAP-L138 EVM"
-       depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM
+       depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM && I2C
        select SND_DAVINCI_SOC_GENERIC_EVM
        help
          Say Y if you want to add support for SoC audio on TI
index cab98a5..a50010e 100644 (file)
@@ -38,7 +38,7 @@ struct snd_soc_card_drvdata_davinci {
 static int evm_startup(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_card *soc_card = rtd->codec->card;
+       struct snd_soc_card *soc_card = rtd->card;
        struct snd_soc_card_drvdata_davinci *drvdata =
                snd_soc_card_get_drvdata(soc_card);
 
@@ -51,7 +51,7 @@ static int evm_startup(struct snd_pcm_substream *substream)
 static void evm_shutdown(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_card *soc_card = rtd->codec->card;
+       struct snd_soc_card *soc_card = rtd->card;
        struct snd_soc_card_drvdata_davinci *drvdata =
                snd_soc_card_get_drvdata(soc_card);
 
@@ -65,8 +65,7 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_card *soc_card = codec->card;
+       struct snd_soc_card *soc_card = rtd->card;
        int ret = 0;
        unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *)
                           snd_soc_card_get_drvdata(soc_card))->sysclk;
@@ -125,7 +124,7 @@ static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_card *card = rtd->card;
        struct snd_soc_codec *codec = rtd->codec;
-       struct device_node *np = codec->card->dev->of_node;
+       struct device_node *np = card->dev->of_node;
        int ret;
 
        /* Add davinci-evm specific widgets */
index ebe8294..7682af3 100644 (file)
@@ -757,7 +757,6 @@ static int davinci_i2s_remove(struct platform_device *pdev)
        struct davinci_mcbsp_dev *dev = dev_get_drvdata(&pdev->dev);
 
        snd_soc_unregister_component(&pdev->dev);
-       davinci_soc_platform_unregister(&pdev->dev);
 
        clk_disable(dev->clk);
        clk_put(dev->clk);
index 4f75cac..9afb146 100644 (file)
 #include <sound/initval.h>
 #include <sound/soc.h>
 #include <sound/dmaengine_pcm.h>
+#include <sound/omap-pcm.h>
 
 #include "davinci-pcm.h"
 #include "davinci-mcasp.h"
 
+#define MCASP_MAX_AFIFO_DEPTH  64
+
 struct davinci_mcasp_context {
        u32     txfmtctl;
        u32     rxfmtctl;
@@ -269,25 +272,51 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 {
        struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
        int ret = 0;
+       u32 data_delay;
+       bool fs_pol_rising;
+       bool inv_fs = false;
 
        pm_runtime_get_sync(mcasp->dev);
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+               mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+               mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+               /* 1st data bit occur one ACLK cycle after the frame sync */
+               data_delay = 1;
+               break;
        case SND_SOC_DAIFMT_DSP_B:
        case SND_SOC_DAIFMT_AC97:
                mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
                mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+               /* No delay after FS */
+               data_delay = 0;
                break;
-       default:
+       case SND_SOC_DAIFMT_I2S:
                /* configure a full-word SYNC pulse (LRCLK) */
                mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
                mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
-
-               /* make 1st data bit occur one ACLK cycle after the frame sync */
-               mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, FSXDLY(1));
-               mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, FSRDLY(1));
+               /* 1st data bit occur one ACLK cycle after the frame sync */
+               data_delay = 1;
+               /* FS need to be inverted */
+               inv_fs = true;
                break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               /* configure a full-word SYNC pulse (LRCLK) */
+               mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+               mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+               /* No delay after FS */
+               data_delay = 0;
+               break;
+       default:
+               ret = -EINVAL;
+               goto out;
        }
 
+       mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, FSXDLY(data_delay),
+                      FSXDLY(3));
+       mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, FSRDLY(data_delay),
+                      FSRDLY(3));
+
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
        case SND_SOC_DAIFMT_CBS_CFS:
                /* codec is clock and frame slave */
@@ -325,7 +354,6 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                               ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR);
                mcasp->bclk_master = 0;
                break;
-
        default:
                ret = -EINVAL;
                goto out;
@@ -334,39 +362,38 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
        case SND_SOC_DAIFMT_IB_NF:
                mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
-               mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
-
                mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
-               mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+               fs_pol_rising = true;
                break;
-
        case SND_SOC_DAIFMT_NB_IF:
                mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
-               mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
-
                mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
-               mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+               fs_pol_rising = false;
                break;
-
        case SND_SOC_DAIFMT_IB_IF:
                mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
-               mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
-
                mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
-               mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+               fs_pol_rising = false;
                break;
-
        case SND_SOC_DAIFMT_NB_NF:
                mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
-               mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
-
                mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
-               mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+               fs_pol_rising = true;
                break;
-
        default:
                ret = -EINVAL;
-               break;
+               goto out;
+       }
+
+       if (inv_fs)
+               fs_pol_rising = !fs_pol_rising;
+
+       if (fs_pol_rising) {
+               mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+               mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+       } else {
+               mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+               mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
        }
 out:
        pm_runtime_put_sync(mcasp->dev);
@@ -464,17 +491,19 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
 }
 
 static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
-                                   int channels)
+                                int period_words, int channels)
 {
+       struct davinci_pcm_dma_params *dma_params = &mcasp->dma_params[stream];
+       struct snd_dmaengine_dai_dma_data *dma_data = &mcasp->dma_data[stream];
        int i;
        u8 tx_ser = 0;
        u8 rx_ser = 0;
-       u8 ser;
        u8 slots = mcasp->tdm_slots;
        u8 max_active_serializers = (channels + slots - 1) / slots;
+       int active_serializers, numevt, n;
        u32 reg;
        /* Default configuration */
-       if (mcasp->version != MCASP_VERSION_4)
+       if (mcasp->version < MCASP_VERSION_3)
                mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
 
        /* All PINS as McASP */
@@ -505,37 +534,71 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
                }
        }
 
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-               ser = tx_ser;
-       else
-               ser = rx_ser;
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               active_serializers = tx_ser;
+               numevt = mcasp->txnumevt;
+               reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+       } else {
+               active_serializers = rx_ser;
+               numevt = mcasp->rxnumevt;
+               reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+       }
 
-       if (ser < max_active_serializers) {
+       if (active_serializers < max_active_serializers) {
                dev_warn(mcasp->dev, "stream has more channels (%d) than are "
-                       "enabled in mcasp (%d)\n", channels, ser * slots);
+                        "enabled in mcasp (%d)\n", channels,
+                        active_serializers * slots);
                return -EINVAL;
        }
 
-       if (mcasp->txnumevt && stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               if (mcasp->txnumevt * tx_ser > 64)
-                       mcasp->txnumevt = 1;
-
-               reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
-               mcasp_mod_bits(mcasp, reg, tx_ser, NUMDMA_MASK);
-               mcasp_mod_bits(mcasp, reg, ((mcasp->txnumevt * tx_ser) << 8),
-                              NUMEVT_MASK);
+       /* AFIFO is not in use */
+       if (!numevt) {
+               /* Configure the burst size for platform drivers */
+               if (active_serializers > 1) {
+                       /*
+                        * If more than one serializers are in use we have one
+                        * DMA request to provide data for all serializers.
+                        * For example if three serializers are enabled the DMA
+                        * need to transfer three words per DMA request.
+                        */
+                       dma_params->fifo_level = active_serializers;
+                       dma_data->maxburst = active_serializers;
+               } else {
+                       dma_params->fifo_level = 0;
+                       dma_data->maxburst = 0;
+               }
+               return 0;
        }
 
-       if (mcasp->rxnumevt && stream == SNDRV_PCM_STREAM_CAPTURE) {
-               if (mcasp->rxnumevt * rx_ser > 64)
-                       mcasp->rxnumevt = 1;
-
-               reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
-               mcasp_mod_bits(mcasp, reg, rx_ser, NUMDMA_MASK);
-               mcasp_mod_bits(mcasp, reg, ((mcasp->rxnumevt * rx_ser) << 8),
-                              NUMEVT_MASK);
+       if (period_words % active_serializers) {
+               dev_err(mcasp->dev, "Invalid combination of period words and "
+                       "active serializers: %d, %d\n", period_words,
+                       active_serializers);
+               return -EINVAL;
        }
 
+       /*
+        * Calculate the optimal AFIFO depth for platform side:
+        * The number of words for numevt need to be in steps of active
+        * serializers.
+        */
+       n = numevt % active_serializers;
+       if (n)
+               numevt += (active_serializers - n);
+       while (period_words % numevt && numevt > 0)
+               numevt -= active_serializers;
+       if (numevt <= 0)
+               numevt = active_serializers;
+
+       mcasp_mod_bits(mcasp, reg, active_serializers, NUMDMA_MASK);
+       mcasp_mod_bits(mcasp, reg, NUMEVT(numevt), NUMEVT_MASK);
+
+       /* Configure the burst size for platform drivers */
+       if (numevt == 1)
+               numevt = 0;
+       dma_params->fifo_level = numevt;
+       dma_data->maxburst = numevt;
+
        return 0;
 }
 
@@ -607,27 +670,24 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
        struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
        struct davinci_pcm_dma_params *dma_params =
                                        &mcasp->dma_params[substream->stream];
-       struct snd_dmaengine_dai_dma_data *dma_data =
-                                       &mcasp->dma_data[substream->stream];
        int word_length;
-       u8 fifo_level;
-       u8 slots = mcasp->tdm_slots;
-       u8 active_serializers;
        int channels = params_channels(params);
+       int period_size = params_period_size(params);
        int ret;
 
        /* If mcasp is BCLK master we need to set BCLK divider */
        if (mcasp->bclk_master) {
                unsigned int bclk_freq = snd_soc_params_to_bclk(params);
                if (mcasp->sysclk_freq % bclk_freq != 0) {
-                       dev_err(mcasp->dev, "Can't produce requred BCLK\n");
+                       dev_err(mcasp->dev, "Can't produce required BCLK\n");
                        return -EINVAL;
                }
                davinci_mcasp_set_clkdiv(
                        cpu_dai, 1, mcasp->sysclk_freq / bclk_freq);
        }
 
-       ret = mcasp_common_hw_param(mcasp, substream->stream, channels);
+       ret = mcasp_common_hw_param(mcasp, substream->stream,
+                                   period_size * channels, channels);
        if (ret)
                return ret;
 
@@ -671,21 +731,11 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       /* Calculate FIFO level */
-       active_serializers = (channels + slots - 1) / slots;
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               fifo_level = mcasp->txnumevt * active_serializers;
-       else
-               fifo_level = mcasp->rxnumevt * active_serializers;
-
-       if (mcasp->version == MCASP_VERSION_2 && !fifo_level)
+       if (mcasp->version == MCASP_VERSION_2 && !dma_params->fifo_level)
                dma_params->acnt = 4;
        else
                dma_params->acnt = dma_params->data_type;
 
-       dma_params->fifo_level = fifo_level;
-       dma_data->maxburst = fifo_level;
-
        davinci_config_channel_size(mcasp, word_length);
 
        return 0;
@@ -716,22 +766,7 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
        return ret;
 }
 
-static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
-                                struct snd_soc_dai *dai)
-{
-       struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
-
-       if (mcasp->version == MCASP_VERSION_4)
-               snd_soc_dai_set_dma_data(dai, substream,
-                                       &mcasp->dma_data[substream->stream]);
-       else
-               snd_soc_dai_set_dma_data(dai, substream, mcasp->dma_params);
-
-       return 0;
-}
-
 static const 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,
@@ -739,6 +774,25 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
        .set_sysclk     = davinci_mcasp_set_sysclk,
 };
 
+static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
+{
+       struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+       if (mcasp->version == MCASP_VERSION_4) {
+               /* Using dmaengine PCM */
+               dai->playback_dma_data =
+                               &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+               dai->capture_dma_data =
+                               &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+       } else {
+               /* Using davinci-pcm */
+               dai->playback_dma_data = mcasp->dma_params;
+               dai->capture_dma_data = mcasp->dma_params;
+       }
+
+       return 0;
+}
+
 #ifdef CONFIG_PM_SLEEP
 static int davinci_mcasp_suspend(struct snd_soc_dai *dai)
 {
@@ -792,6 +846,7 @@ static int davinci_mcasp_resume(struct snd_soc_dai *dai)
 static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
        {
                .name           = "davinci-mcasp.0",
+               .probe          = davinci_mcasp_dai_probe,
                .suspend        = davinci_mcasp_suspend,
                .resume         = davinci_mcasp_resume,
                .playback       = {
@@ -811,6 +866,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
        },
        {
                .name           = "davinci-mcasp.1",
+               .probe          = davinci_mcasp_dai_probe,
                .playback       = {
                        .channels_min   = 1,
                        .channels_max   = 384,
@@ -1078,7 +1134,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        if (!mcasp->base) {
                dev_err(&pdev->dev, "ioremap failed\n");
                ret = -ENOMEM;
-               goto err_release_clk;
+               goto err;
        }
 
        mcasp->op_mode = pdata->op_mode;
@@ -1159,25 +1215,37 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 
        mcasp_reparent_fck(pdev);
 
-       ret = snd_soc_register_component(&pdev->dev, &davinci_mcasp_component,
-                                        &davinci_mcasp_dai[pdata->op_mode], 1);
+       ret = devm_snd_soc_register_component(&pdev->dev,
+                                       &davinci_mcasp_component,
+                                       &davinci_mcasp_dai[pdata->op_mode], 1);
 
        if (ret != 0)
-               goto err_release_clk;
+               goto err;
 
-       if (mcasp->version != MCASP_VERSION_4) {
+       switch (mcasp->version) {
+       case MCASP_VERSION_1:
+       case MCASP_VERSION_2:
+       case MCASP_VERSION_3:
                ret = davinci_soc_platform_register(&pdev->dev);
-               if (ret) {
-                       dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
-                       goto err_unregister_component;
-               }
+               break;
+       case MCASP_VERSION_4:
+               ret = omap_pcm_platform_register(&pdev->dev);
+               break;
+       default:
+               dev_err(&pdev->dev, "Invalid McASP version: %d\n",
+                       mcasp->version);
+               ret = -EINVAL;
+               break;
+       }
+
+       if (ret) {
+               dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+               goto err;
        }
 
        return 0;
 
-err_unregister_component:
-       snd_soc_unregister_component(&pdev->dev);
-err_release_clk:
+err:
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
        return ret;
@@ -1185,12 +1253,6 @@ err_release_clk:
 
 static int davinci_mcasp_remove(struct platform_device *pdev)
 {
-       struct davinci_mcasp *mcasp = dev_get_drvdata(&pdev->dev);
-
-       snd_soc_unregister_component(&pdev->dev);
-       if (mcasp->version != MCASP_VERSION_4)
-               davinci_soc_platform_unregister(&pdev->dev);
-
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
index 8fed757..98fbc45 100644 (file)
  */
 #define FIFO_ENABLE    BIT(16)
 #define NUMEVT_MASK    (0xFF << 8)
+#define NUMEVT(x)      (((x) & 0xFF) << 8)
 #define NUMDMA_MASK    (0xFF)
 
 #endif /* DAVINCI_MCASP_H */
index 14145cd..7809e9d 100644 (file)
@@ -852,16 +852,10 @@ static struct snd_soc_platform_driver davinci_soc_platform = {
 
 int davinci_soc_platform_register(struct device *dev)
 {
-       return snd_soc_register_platform(dev, &davinci_soc_platform);
+       return devm_snd_soc_register_platform(dev, &davinci_soc_platform);
 }
 EXPORT_SYMBOL_GPL(davinci_soc_platform_register);
 
-void davinci_soc_platform_unregister(struct device *dev)
-{
-       snd_soc_unregister_platform(dev);
-}
-EXPORT_SYMBOL_GPL(davinci_soc_platform_unregister);
-
 MODULE_AUTHOR("Vladimir Barinov");
 MODULE_DESCRIPTION("TI DAVINCI PCM DMA module");
 MODULE_LICENSE("GPL");
index fbb710c..0fe2346 100644 (file)
@@ -29,7 +29,13 @@ struct davinci_pcm_dma_params {
        unsigned int fifo_level;
 };
 
+#if IS_ENABLED(CONFIG_SND_DAVINCI_SOC)
 int davinci_soc_platform_register(struct device *dev);
-void davinci_soc_platform_unregister(struct device *dev);
+#else
+static inline int davinci_soc_platform_register(struct device *dev)
+{
+       return 0;
+}
+#endif /* CONFIG_SND_DAVINCI_SOC */
 
 #endif
index 30587c0..77aef05 100644 (file)
@@ -258,7 +258,6 @@ static int davinci_vcif_probe(struct platform_device *pdev)
 static int davinci_vcif_remove(struct platform_device *pdev)
 {
        snd_soc_unregister_component(&pdev->dev);
-       davinci_soc_platform_unregister(&pdev->dev);
 
        return 0;
 }
index 338a916..3793362 100644 (file)
@@ -1,30 +1,78 @@
+menu "SoC Audio for Freescale CPUs"
+
+comment "Common SoC Audio options for Freescale CPUs:"
+
 config SND_SOC_FSL_SAI
-       tristate
+       tristate "Synchronous Audio Interface (SAI) module support"
        select REGMAP_MMIO
        select SND_SOC_GENERIC_DMAENGINE_PCM
+       help
+         Say Y if you want to add Synchronous Audio Interface (SAI)
+         support for the Freescale CPUs.
+         This option is only useful for out-of-tree drivers since
+         in-tree drivers select it automatically.
 
 config SND_SOC_FSL_SSI
-       tristate
+       tristate "Synchronous Serial Interface module support"
+       select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
+       select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC
+       select REGMAP_MMIO
+       help
+         Say Y if you want to add Synchronous Serial Interface (SSI)
+         support for the Freescale CPUs.
+         This option is only useful for out-of-tree drivers since
+         in-tree drivers select it automatically.
 
 config SND_SOC_FSL_SPDIF
-       tristate
+       tristate "Sony/Philips Digital Interface module support"
        select REGMAP_MMIO
+       select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
+       select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC
+       help
+         Say Y if you want to add Sony/Philips Digital Interface (SPDIF)
+         support for the Freescale CPUs.
+         This option is only useful for out-of-tree drivers since
+         in-tree drivers select it automatically.
 
 config SND_SOC_FSL_ESAI
-       tristate
+       tristate "Enhanced Serial Audio Interface (ESAI) module support"
        select REGMAP_MMIO
        select SND_SOC_FSL_UTILS
+       help
+         Say Y if you want to add Enhanced Synchronous Audio Interface
+         (ESAI) support for the Freescale CPUs.
+         This option is only useful for out-of-tree drivers since
+         in-tree drivers select it automatically.
 
 config SND_SOC_FSL_UTILS
        tristate
 
-menuconfig SND_POWERPC_SOC
+config SND_SOC_IMX_PCM_DMA
+       tristate
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+
+config SND_SOC_IMX_AUDMUX
+       tristate "Digital Audio Mux module support"
+       help
+         Say Y if you want to add Digital Audio Mux (AUDMUX) support
+         for the ARM i.MX CPUs.
+         This option is only useful for out-of-tree drivers since
+         in-tree drivers select it automatically.
+
+config SND_POWERPC_SOC
        tristate "SoC Audio for Freescale PowerPC CPUs"
        depends on FSL_SOC || PPC_MPC52xx
        help
          Say Y or M if you want to add support for codecs attached to
          the PowerPC CPUs.
 
+config SND_IMX_SOC
+       tristate "SoC Audio for Freescale i.MX CPUs"
+       depends on ARCH_MXC || COMPILE_TEST
+       help
+         Say Y or M if you want to add support for codecs attached to
+         the i.MX CPUs.
+
 if SND_POWERPC_SOC
 
 config SND_MPC52xx_DMA
@@ -33,6 +81,8 @@ config SND_MPC52xx_DMA
 config SND_SOC_POWERPC_DMA
        tristate
 
+comment "SoC Audio support for Freescale PPC boards:"
+
 config SND_SOC_MPC8610_HPCD
        tristate "ALSA SoC support for the Freescale MPC8610 HPCD board"
        # I2C is necessary for the CS4270 driver
@@ -110,13 +160,6 @@ config SND_MPC52xx_SOC_EFIKA
 
 endif # SND_POWERPC_SOC
 
-menuconfig SND_IMX_SOC
-       tristate "SoC Audio for Freescale i.MX CPUs"
-       depends on ARCH_MXC || COMPILE_TEST
-       help
-         Say Y or M if you want to add support for codecs attached to
-         the i.MX CPUs.
-
 if SND_IMX_SOC
 
 config SND_SOC_IMX_SSI
@@ -127,12 +170,7 @@ config SND_SOC_IMX_PCM_FIQ
        tristate
        select FIQ
 
-config SND_SOC_IMX_PCM_DMA
-       tristate
-       select SND_SOC_GENERIC_DMAENGINE_PCM
-
-config SND_SOC_IMX_AUDMUX
-       tristate
+comment "SoC Audio support for Freescale i.MX boards:"
 
 config SND_MXC_SOC_WM1133_EV1
        tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted"
@@ -170,12 +208,7 @@ config SND_SOC_PHYCORE_AC97
 
 config SND_SOC_EUKREA_TLV320
        tristate "Eukrea TLV320"
-       depends on MACH_EUKREA_MBIMX27_BASEBOARD \
-               || MACH_EUKREA_MBIMXSD25_BASEBOARD \
-               || MACH_EUKREA_MBIMXSD35_BASEBOARD \
-               || MACH_EUKREA_MBIMXSD51_BASEBOARD \
-               || (OF && ARM)
-       depends on I2C
+       depends on ARCH_MXC && I2C
        select SND_SOC_TLV320AIC23_I2C
        select SND_SOC_IMX_AUDMUX
        select SND_SOC_IMX_SSI
@@ -187,7 +220,7 @@ config SND_SOC_EUKREA_TLV320
 
 config SND_SOC_IMX_WM8962
        tristate "SoC Audio support for i.MX boards with wm8962"
-       depends on OF && I2C
+       depends on OF && I2C && INPUT
        select SND_SOC_WM8962
        select SND_SOC_IMX_PCM_DMA
        select SND_SOC_IMX_AUDMUX
@@ -225,3 +258,5 @@ config SND_SOC_IMX_MC13783
        select SND_SOC_IMX_PCM_DMA
 
 endif # SND_IMX_SOC
+
+endmenu
index b12ad4b..db254e3 100644 (file)
@@ -12,7 +12,8 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
 
 # Freescale SSI/DMA/SAI/SPDIF Support
 snd-soc-fsl-sai-objs := fsl_sai.o
-snd-soc-fsl-ssi-objs := fsl_ssi.o
+snd-soc-fsl-ssi-y := fsl_ssi.o
+snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o
 snd-soc-fsl-spdif-objs := fsl_spdif.o
 snd-soc-fsl-esai-objs := fsl_esai.o
 snd-soc-fsl-utils-objs := fsl_utils.o
index 496ce2e..d719caf 100644 (file)
@@ -39,6 +39,8 @@
  * @fifo_depth: depth of tx/rx FIFO
  * @slot_width: width of each DAI slot
  * @hck_rate: clock rate of desired HCKx clock
+ * @sck_rate: clock rate of desired SCKx clock
+ * @hck_dir: the direction of HCKx pads
  * @sck_div: if using PSR/PM dividers for SCKx clock
  * @slave_mode: if fully using DAI slave mode
  * @synchronous: if using tx/rx synchronous mode
@@ -55,6 +57,8 @@ struct fsl_esai {
        u32 fifo_depth;
        u32 slot_width;
        u32 hck_rate[2];
+       u32 sck_rate[2];
+       bool hck_dir[2];
        bool sck_div[2];
        bool slave_mode;
        bool synchronous;
@@ -209,8 +213,13 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
        struct clk *clksrc = esai_priv->extalclk;
        bool tx = clk_id <= ESAI_HCKT_EXTAL;
        bool in = dir == SND_SOC_CLOCK_IN;
-       u32 ret, ratio, ecr = 0;
+       u32 ratio, ecr = 0;
        unsigned long clk_rate;
+       int ret;
+
+       /* Bypass divider settings if the requirement doesn't change */
+       if (freq == esai_priv->hck_rate[tx] && dir == esai_priv->hck_dir[tx])
+               return 0;
 
        /* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */
        esai_priv->sck_div[tx] = true;
@@ -277,6 +286,7 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
        esai_priv->sck_div[tx] = false;
 
 out:
+       esai_priv->hck_dir[tx] = dir;
        esai_priv->hck_rate[tx] = freq;
 
        regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
@@ -294,9 +304,10 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
        struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
        u32 hck_rate = esai_priv->hck_rate[tx];
        u32 sub, ratio = hck_rate / freq;
+       int ret;
 
-       /* Don't apply for fully slave mode*/
-       if (esai_priv->slave_mode)
+       /* Don't apply for fully slave mode or unchanged bclk */
+       if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq)
                return 0;
 
        if (ratio * freq > hck_rate)
@@ -319,8 +330,15 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
                return -EINVAL;
        }
 
-       return fsl_esai_divisor_cal(dai, tx, ratio, true,
+       ret = fsl_esai_divisor_cal(dai, tx, ratio, true,
                        esai_priv->sck_div[tx] ? 0 : ratio);
+       if (ret)
+               return ret;
+
+       /* Save current bclk rate */
+       esai_priv->sck_rate[tx] = freq;
+
+       return 0;
 }
 
 static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
@@ -439,8 +457,8 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 static int fsl_esai_startup(struct snd_pcm_substream *substream,
                            struct snd_soc_dai *dai)
 {
-       int ret;
        struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+       int ret;
 
        /*
         * Some platforms might use the same bit to gate all three or two of
@@ -492,7 +510,8 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
        bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        u32 width = snd_pcm_format_width(params_format(params));
        u32 channels = params_channels(params);
-       u32 bclk, mask, val, ret;
+       u32 bclk, mask, val;
+       int ret;
 
        bclk = params_rate(params) * esai_priv->slot_width * 2;
 
@@ -822,6 +841,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
 
 static const struct of_device_id fsl_esai_dt_ids[] = {
        { .compatible = "fsl,imx35-esai", },
+       { .compatible = "fsl,vf610-esai", },
        {}
 };
 MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
index 56da8c8..c5a0e8a 100644 (file)
@@ -22,6 +22,7 @@
 #include <sound/pcm_params.h>
 
 #include "fsl_sai.h"
+#include "imx-pcm.h"
 
 #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
                       FSL_SAI_CSR_FEIE)
@@ -30,78 +31,96 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
 {
        struct fsl_sai *sai = (struct fsl_sai *)devid;
        struct device *dev = &sai->pdev->dev;
-       u32 xcsr, mask;
+       u32 flags, xcsr, mask;
+       bool irq_none = true;
 
-       /* Only handle those what we enabled */
+       /*
+        * Both IRQ status bits and IRQ mask bits are in the xCSR but
+        * different shifts. And we here create a mask only for those
+        * IRQs that we activated.
+        */
        mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT;
 
        /* Tx IRQ */
        regmap_read(sai->regmap, FSL_SAI_TCSR, &xcsr);
-       xcsr &= mask;
+       flags = xcsr & mask;
+
+       if (flags)
+               irq_none = false;
+       else
+               goto irq_rx;
 
-       if (xcsr & FSL_SAI_CSR_WSF)
+       if (flags & FSL_SAI_CSR_WSF)
                dev_dbg(dev, "isr: Start of Tx word detected\n");
 
-       if (xcsr & FSL_SAI_CSR_SEF)
+       if (flags & FSL_SAI_CSR_SEF)
                dev_warn(dev, "isr: Tx Frame sync error detected\n");
 
-       if (xcsr & FSL_SAI_CSR_FEF) {
+       if (flags & FSL_SAI_CSR_FEF) {
                dev_warn(dev, "isr: Transmit underrun detected\n");
                /* FIFO reset for safety */
                xcsr |= FSL_SAI_CSR_FR;
        }
 
-       if (xcsr & FSL_SAI_CSR_FWF)
+       if (flags & FSL_SAI_CSR_FWF)
                dev_dbg(dev, "isr: Enabled transmit FIFO is empty\n");
 
-       if (xcsr & FSL_SAI_CSR_FRF)
+       if (flags & FSL_SAI_CSR_FRF)
                dev_dbg(dev, "isr: Transmit FIFO watermark has been reached\n");
 
-       regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
-                          FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr);
+       flags &= FSL_SAI_CSR_xF_W_MASK;
+       xcsr &= ~FSL_SAI_CSR_xF_MASK;
+
+       if (flags)
+               regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr);
 
+irq_rx:
        /* Rx IRQ */
        regmap_read(sai->regmap, FSL_SAI_RCSR, &xcsr);
-       xcsr &= mask;
+       flags = xcsr & mask;
 
-       if (xcsr & FSL_SAI_CSR_WSF)
+       if (flags)
+               irq_none = false;
+       else
+               goto out;
+
+       if (flags & FSL_SAI_CSR_WSF)
                dev_dbg(dev, "isr: Start of Rx word detected\n");
 
-       if (xcsr & FSL_SAI_CSR_SEF)
+       if (flags & FSL_SAI_CSR_SEF)
                dev_warn(dev, "isr: Rx Frame sync error detected\n");
 
-       if (xcsr & FSL_SAI_CSR_FEF) {
+       if (flags & FSL_SAI_CSR_FEF) {
                dev_warn(dev, "isr: Receive overflow detected\n");
                /* FIFO reset for safety */
                xcsr |= FSL_SAI_CSR_FR;
        }
 
-       if (xcsr & FSL_SAI_CSR_FWF)
+       if (flags & FSL_SAI_CSR_FWF)
                dev_dbg(dev, "isr: Enabled receive FIFO is full\n");
 
-       if (xcsr & FSL_SAI_CSR_FRF)
+       if (flags & FSL_SAI_CSR_FRF)
                dev_dbg(dev, "isr: Receive FIFO watermark has been reached\n");
 
-       regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
-                          FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr);
+       flags &= FSL_SAI_CSR_xF_W_MASK;
+       xcsr &= ~FSL_SAI_CSR_xF_MASK;
+
+       if (flags)
+               regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr);
 
-       return IRQ_HANDLED;
+out:
+       if (irq_none)
+               return IRQ_NONE;
+       else
+               return IRQ_HANDLED;
 }
 
 static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
                int clk_id, unsigned int freq, int fsl_dir)
 {
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
-       u32 val_cr2, reg_cr2;
-
-       if (fsl_dir == FSL_FMT_TRANSMITTER)
-               reg_cr2 = FSL_SAI_TCR2;
-       else
-               reg_cr2 = FSL_SAI_RCR2;
-
-       regmap_read(sai->regmap, reg_cr2, &val_cr2);
-
-       val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
+       bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
+       u32 val_cr2 = 0;
 
        switch (clk_id) {
        case FSL_SAI_CLK_BUS:
@@ -120,7 +139,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
                return -EINVAL;
        }
 
-       regmap_write(sai->regmap, reg_cr2, val_cr2);
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx),
+                          FSL_SAI_CR2_MSEL_MASK, val_cr2);
 
        return 0;
 }
@@ -152,22 +172,10 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
                                unsigned int fmt, int fsl_dir)
 {
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
-       u32 val_cr2, val_cr4, reg_cr2, reg_cr4;
-
-       if (fsl_dir == FSL_FMT_TRANSMITTER) {
-               reg_cr2 = FSL_SAI_TCR2;
-               reg_cr4 = FSL_SAI_TCR4;
-       } else {
-               reg_cr2 = FSL_SAI_RCR2;
-               reg_cr4 = FSL_SAI_RCR4;
-       }
+       bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
+       u32 val_cr2 = 0, val_cr4 = 0;
 
-       regmap_read(sai->regmap, reg_cr2, &val_cr2);
-       regmap_read(sai->regmap, reg_cr4, &val_cr4);
-
-       if (sai->big_endian_data)
-               val_cr4 &= ~FSL_SAI_CR4_MF;
-       else
+       if (!sai->big_endian_data)
                val_cr4 |= FSL_SAI_CR4_MF;
 
        /* DAI mode */
@@ -188,7 +196,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
                 * frame sync asserts with the first bit of the frame.
                 */
                val_cr2 |= FSL_SAI_CR2_BCP;
-               val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
                break;
        case SND_SOC_DAIFMT_DSP_A:
                /*
@@ -198,7 +205,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
                 * data word.
                 */
                val_cr2 |= FSL_SAI_CR2_BCP;
-               val_cr4 &= ~FSL_SAI_CR4_FSP;
                val_cr4 |= FSL_SAI_CR4_FSE;
                sai->is_dsp_mode = true;
                break;
@@ -208,7 +214,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
                 * frame sync asserts with the first bit of the frame.
                 */
                val_cr2 |= FSL_SAI_CR2_BCP;
-               val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
                sai->is_dsp_mode = true;
                break;
        case SND_SOC_DAIFMT_RIGHT_J:
@@ -246,23 +251,22 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
                val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
                break;
        case SND_SOC_DAIFMT_CBM_CFM:
-               val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
-               val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
                break;
        case SND_SOC_DAIFMT_CBS_CFM:
                val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
-               val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
                break;
        case SND_SOC_DAIFMT_CBM_CFS:
-               val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
                val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
                break;
        default:
                return -EINVAL;
        }
 
-       regmap_write(sai->regmap, reg_cr2, val_cr2);
-       regmap_write(sai->regmap, reg_cr4, val_cr4);
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx),
+                          FSL_SAI_CR2_BCP | FSL_SAI_CR2_BCD_MSTR, val_cr2);
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
+                          FSL_SAI_CR4_MF | FSL_SAI_CR4_FSE |
+                          FSL_SAI_CR4_FSP | FSL_SAI_CR4_FSD_MSTR, val_cr4);
 
        return 0;
 }
@@ -289,29 +293,10 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
                struct snd_soc_dai *cpu_dai)
 {
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
-       u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr;
+       bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        unsigned int channels = params_channels(params);
        u32 word_width = snd_pcm_format_width(params_format(params));
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               reg_cr4 = FSL_SAI_TCR4;
-               reg_cr5 = FSL_SAI_TCR5;
-               reg_mr = FSL_SAI_TMR;
-       } else {
-               reg_cr4 = FSL_SAI_RCR4;
-               reg_cr5 = FSL_SAI_RCR5;
-               reg_mr = FSL_SAI_RMR;
-       }
-
-       regmap_read(sai->regmap, reg_cr4, &val_cr4);
-       regmap_read(sai->regmap, reg_cr4, &val_cr5);
-
-       val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK;
-       val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK;
-
-       val_cr5 &= ~FSL_SAI_CR5_WNW_MASK;
-       val_cr5 &= ~FSL_SAI_CR5_W0W_MASK;
-       val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
+       u32 val_cr4 = 0, val_cr5 = 0;
 
        if (!sai->is_dsp_mode)
                val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
@@ -319,18 +304,20 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
        val_cr5 |= FSL_SAI_CR5_WNW(word_width);
        val_cr5 |= FSL_SAI_CR5_W0W(word_width);
 
-       val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
        if (sai->big_endian_data)
                val_cr5 |= FSL_SAI_CR5_FBT(0);
        else
                val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
 
        val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
-       val_mr = ~0UL - ((1 << channels) - 1);
 
-       regmap_write(sai->regmap, reg_cr4, val_cr4);
-       regmap_write(sai->regmap, reg_cr5, val_cr5);
-       regmap_write(sai->regmap, reg_mr, val_mr);
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
+                          FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
+                          val_cr4);
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx),
+                          FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
+                          FSL_SAI_CR5_FBT_MASK, val_cr5);
+       regmap_write(sai->regmap, FSL_SAI_xMR(tx), ~0UL - ((1 << channels) - 1));
 
        return 0;
 }
@@ -339,6 +326,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
                struct snd_soc_dai *cpu_dai)
 {
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+       bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        u32 tcsr, rcsr;
 
        /*
@@ -353,14 +341,6 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
        regmap_read(sai->regmap, FSL_SAI_TCSR, &tcsr);
        regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr);
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               tcsr |= FSL_SAI_CSR_FRDE;
-               rcsr &= ~FSL_SAI_CSR_FRDE;
-       } else {
-               rcsr |= FSL_SAI_CSR_FRDE;
-               tcsr &= ~FSL_SAI_CSR_FRDE;
-       }
-
        /*
         * It is recommended that the transmitter is the last enabled
         * and the first disabled.
@@ -369,22 +349,33 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               tcsr |= FSL_SAI_CSR_TERE;
-               rcsr |= FSL_SAI_CSR_TERE;
+               if (!(tcsr & FSL_SAI_CSR_FRDE || rcsr & FSL_SAI_CSR_FRDE)) {
+                       regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
+                                          FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+                       regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
+                                          FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+               }
 
-               regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
-               regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
+               regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+                                  FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS);
+               regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+                                  FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               if (!(cpu_dai->playback_active || cpu_dai->capture_active)) {
-                       tcsr &= ~FSL_SAI_CSR_TERE;
-                       rcsr &= ~FSL_SAI_CSR_TERE;
+               regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+                                  FSL_SAI_CSR_FRDE, 0);
+               regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+                                  FSL_SAI_CSR_xIE_MASK, 0);
+
+               /* Check if the opposite FRDE is also disabled */
+               if (!(tx ? rcsr & FSL_SAI_CSR_FRDE : tcsr & FSL_SAI_CSR_FRDE)) {
+                       regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
+                                          FSL_SAI_CSR_TERE, 0);
+                       regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
+                                          FSL_SAI_CSR_TERE, 0);
                }
-
-               regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
-               regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
                break;
        default:
                return -EINVAL;
@@ -397,14 +388,17 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream,
                struct snd_soc_dai *cpu_dai)
 {
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
-       u32 reg;
+       bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       struct device *dev = &sai->pdev->dev;
+       int ret;
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               reg = FSL_SAI_TCR3;
-       else
-               reg = FSL_SAI_RCR3;
+       ret = clk_prepare_enable(sai->bus_clk);
+       if (ret) {
+               dev_err(dev, "failed to enable bus clock: %d\n", ret);
+               return ret;
+       }
 
-       regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE,
                           FSL_SAI_CR3_TRCE);
 
        return 0;
@@ -414,15 +408,11 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
                struct snd_soc_dai *cpu_dai)
 {
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
-       u32 reg;
+       bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               reg = FSL_SAI_TCR3;
-       else
-               reg = FSL_SAI_RCR3;
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE, 0);
 
-       regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
-                          ~FSL_SAI_CR3_TRCE);
+       clk_disable_unprepare(sai->bus_clk);
 }
 
 static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
@@ -438,8 +428,8 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
 {
        struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
 
-       regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, FSL_SAI_FLAGS);
-       regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, FSL_SAI_FLAGS);
+       regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0);
+       regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0);
        regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK,
                           FSL_SAI_MAXBURST_TX * 2);
        regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK,
@@ -555,7 +545,8 @@ static int fsl_sai_probe(struct platform_device *pdev)
        struct fsl_sai *sai;
        struct resource *res;
        void __iomem *base;
-       int irq, ret;
+       char tmp[8];
+       int irq, ret, i;
 
        sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
        if (!sai)
@@ -563,6 +554,9 @@ static int fsl_sai_probe(struct platform_device *pdev)
 
        sai->pdev = pdev;
 
+       if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai"))
+               sai->sai_on_imx = true;
+
        sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
        if (sai->big_endian_regs)
                fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
@@ -575,12 +569,35 @@ static int fsl_sai_probe(struct platform_device *pdev)
                return PTR_ERR(base);
 
        sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
-                       "sai", base, &fsl_sai_regmap_config);
+                       "bus", base, &fsl_sai_regmap_config);
+
+       /* Compatible with old DTB cases */
+       if (IS_ERR(sai->regmap))
+               sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+                               "sai", base, &fsl_sai_regmap_config);
        if (IS_ERR(sai->regmap)) {
                dev_err(&pdev->dev, "regmap init failed\n");
                return PTR_ERR(sai->regmap);
        }
 
+       /* No error out for old DTB cases but only mark the clock NULL */
+       sai->bus_clk = devm_clk_get(&pdev->dev, "bus");
+       if (IS_ERR(sai->bus_clk)) {
+               dev_err(&pdev->dev, "failed to get bus clock: %ld\n",
+                               PTR_ERR(sai->bus_clk));
+               sai->bus_clk = NULL;
+       }
+
+       for (i = 0; i < FSL_SAI_MCLK_MAX; i++) {
+               sprintf(tmp, "mclk%d", i + 1);
+               sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp);
+               if (IS_ERR(sai->mclk_clk[i])) {
+                       dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n",
+                                       i + 1, PTR_ERR(sai->mclk_clk[i]));
+                       sai->mclk_clk[i] = NULL;
+               }
+       }
+
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
@@ -605,12 +622,16 @@ static int fsl_sai_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
-                       SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
+       if (sai->sai_on_imx)
+               return imx_pcm_dma_init(pdev);
+       else
+               return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+                               SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
 }
 
 static const struct of_device_id fsl_sai_ids[] = {
        { .compatible = "fsl,vf610-sai", },
+       { .compatible = "fsl,imx6sx-sai", },
        { /* sentinel */ }
 };
 
index a264185..0e6c9f5 100644 (file)
 #define FSL_SAI_RFR    0xc0 /* SAI Receive FIFO */
 #define FSL_SAI_RMR    0xe0 /* SAI Receive Mask */
 
+#define FSL_SAI_xCSR(tx)       (tx ? FSL_SAI_TCSR : FSL_SAI_RCSR)
+#define FSL_SAI_xCR1(tx)       (tx ? FSL_SAI_TCR1 : FSL_SAI_RCR1)
+#define FSL_SAI_xCR2(tx)       (tx ? FSL_SAI_TCR2 : FSL_SAI_RCR2)
+#define FSL_SAI_xCR3(tx)       (tx ? FSL_SAI_TCR3 : FSL_SAI_RCR3)
+#define FSL_SAI_xCR4(tx)       (tx ? FSL_SAI_TCR4 : FSL_SAI_RCR4)
+#define FSL_SAI_xCR5(tx)       (tx ? FSL_SAI_TCR5 : FSL_SAI_RCR5)
+#define FSL_SAI_xDR(tx)                (tx ? FSL_SAI_TDR : FSL_SAI_RDR)
+#define FSL_SAI_xFR(tx)                (tx ? FSL_SAI_TFR : FSL_SAI_RFR)
+#define FSL_SAI_xMR(tx)                (tx ? FSL_SAI_TMR : FSL_SAI_RMR)
+
 /* SAI Transmit/Recieve Control Register */
 #define FSL_SAI_CSR_TERE       BIT(31)
 #define FSL_SAI_CSR_FR         BIT(25)
@@ -48,6 +58,7 @@
 #define FSL_SAI_CSR_FWF                BIT(17)
 #define FSL_SAI_CSR_FRF                BIT(16)
 #define FSL_SAI_CSR_xIE_SHIFT  8
+#define FSL_SAI_CSR_xIE_MASK   (0x1f << FSL_SAI_CSR_xIE_SHIFT)
 #define FSL_SAI_CSR_WSIE       BIT(12)
 #define FSL_SAI_CSR_SEIE       BIT(11)
 #define FSL_SAI_CSR_FEIE       BIT(10)
 #define FSL_SAI_CLK_MAST2      2
 #define FSL_SAI_CLK_MAST3      3
 
+#define FSL_SAI_MCLK_MAX       3
+
 /* SAI data transfer numbers per DMA request */
 #define FSL_SAI_MAXBURST_TX 6
 #define FSL_SAI_MAXBURST_RX 6
 struct fsl_sai {
        struct platform_device *pdev;
        struct regmap *regmap;
+       struct clk *bus_clk;
+       struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
 
        bool big_endian_regs;
        bool big_endian_data;
        bool is_dsp_mode;
+       bool sai_on_imx;
 
        struct snd_dmaengine_dai_dma_data dma_params_rx;
        struct snd_dmaengine_dai_dma_data dma_params_tx;
index 6452ca8..b912d45 100644 (file)
  * kind, whether express or implied.
  */
 
-#include <linux/module.h>
+#include <linux/bitrev.h>
 #include <linux/clk.h>
 #include <linux/clk-private.h>
-#include <linux/bitrev.h>
-#include <linux/regmap.h>
+#include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/of_irq.h>
+#include <linux/regmap.h>
 
 #include <sound/asoundef.h>
-#include <sound/soc.h>
 #include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
 
 #include "fsl_spdif.h"
 #include "imx-pcm.h"
@@ -69,17 +69,42 @@ struct spdif_mixer_control {
        u32 ready_buf;
 };
 
+/**
+ * fsl_spdif_priv: Freescale SPDIF private data
+ *
+ * @fsl_spdif_control: SPDIF control data
+ * @cpu_dai_drv: cpu dai driver
+ * @pdev: platform device pointer
+ * @regmap: regmap handler
+ * @dpll_locked: dpll lock flag
+ * @txrate: the best rates for playback
+ * @txclk_df: STC_TXCLK_DF dividers value for playback
+ * @sysclk_df: STC_SYSCLK_DF dividers value for playback
+ * @txclk_src: STC_TXCLK_SRC values for playback
+ * @rxclk_src: SRPC_CLKSRC_SEL values for capture
+ * @txclk: tx clock sources for playback
+ * @rxclk: rx clock sources for capture
+ * @coreclk: core clock for register access via DMA
+ * @sysclk: system clock for rx clock rate measurement
+ * @dma_params_tx: DMA parameters for transmit channel
+ * @dma_params_rx: DMA parameters for receive channel
+ * @name: driver name
+ */
 struct fsl_spdif_priv {
        struct spdif_mixer_control fsl_spdif_control;
        struct snd_soc_dai_driver cpu_dai_drv;
        struct platform_device *pdev;
        struct regmap *regmap;
        bool dpll_locked;
-       u8 txclk_div[SPDIF_TXRATE_MAX];
+       u16 txrate[SPDIF_TXRATE_MAX];
+       u8 txclk_df[SPDIF_TXRATE_MAX];
+       u8 sysclk_df[SPDIF_TXRATE_MAX];
        u8 txclk_src[SPDIF_TXRATE_MAX];
        u8 rxclk_src;
        struct clk *txclk[SPDIF_TXRATE_MAX];
        struct clk *rxclk;
+       struct clk *coreclk;
+       struct clk *sysclk;
        struct snd_dmaengine_dai_dma_data dma_params_tx;
        struct snd_dmaengine_dai_dma_data dma_params_rx;
 
@@ -349,7 +374,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
        struct platform_device *pdev = spdif_priv->pdev;
        unsigned long csfs = 0;
        u32 stc, mask, rate;
-       u8 clk, div;
+       u8 clk, txclk_df, sysclk_df;
        int ret;
 
        switch (sample_rate) {
@@ -376,25 +401,31 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       div = spdif_priv->txclk_div[rate];
-       if (div == 0) {
-               dev_err(&pdev->dev, "the divisor can't be zero\n");
+       txclk_df = spdif_priv->txclk_df[rate];
+       if (txclk_df == 0) {
+               dev_err(&pdev->dev, "the txclk_df can't be zero\n");
                return -EINVAL;
        }
 
+       sysclk_df = spdif_priv->sysclk_df[rate];
+
+       /* Don't mess up the clocks from other modules */
+       if (clk != STC_TXCLK_SPDIF_ROOT)
+               goto clk_set_bypass;
+
        /*
-        * The S/PDIF block needs a clock of 64 * fs * div.  The S/PDIF block
-        * will divide by (div).  So request 64 * fs * (div+1) which will
-        * get rounded.
+        * The S/PDIF block needs a clock of 64 * fs * txclk_df.
+        * So request 64 * fs * (txclk_df + 1) to get rounded.
         */
-       ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1));
+       ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (txclk_df + 1));
        if (ret) {
                dev_err(&pdev->dev, "failed to set tx clock rate\n");
                return ret;
        }
 
+clk_set_bypass:
        dev_dbg(&pdev->dev, "expected clock rate = %d\n",
-                       (64 * sample_rate * div));
+                       (64 * sample_rate * txclk_df * sysclk_df));
        dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
                        clk_get_rate(spdif_priv->txclk[rate]));
 
@@ -402,11 +433,15 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
        spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
 
        /* select clock source and divisor */
-       stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div);
-       mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK;
+       stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DF(txclk_df);
+       mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DF_MASK;
        regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
 
-       dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate);
+       regmap_update_bits(regmap, REG_SPDIF_STC,
+                          STC_SYSCLK_DF_MASK, STC_SYSCLK_DF(sysclk_df));
+
+       dev_dbg(&pdev->dev, "set sample rate to %dHz for %dHz playback\n",
+                       spdif_priv->txrate[rate], sample_rate);
 
        return 0;
 }
@@ -423,10 +458,16 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
 
        /* Reset module and interrupts only for first initialization */
        if (!cpu_dai->active) {
+               ret = clk_prepare_enable(spdif_priv->coreclk);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to enable core clock\n");
+                       return ret;
+               }
+
                ret = spdif_softreset(spdif_priv);
                if (ret) {
                        dev_err(&pdev->dev, "failed to soft reset\n");
-                       return ret;
+                       goto err;
                }
 
                /* Disable all the interrupts */
@@ -454,6 +495,11 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
        regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
 
        return 0;
+
+err:
+       clk_disable_unprepare(spdif_priv->coreclk);
+
+       return ret;
 }
 
 static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
@@ -484,6 +530,7 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
                spdif_intr_status_clear(spdif_priv);
                regmap_update_bits(regmap, REG_SPDIF_SCR,
                                SCR_LOW_POWER, SCR_LOW_POWER);
+               clk_disable_unprepare(spdif_priv->coreclk);
        }
 }
 
@@ -754,7 +801,7 @@ static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
        clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
        if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) {
                /* Get bus clock from system */
-               busclk_freq = clk_get_rate(spdif_priv->rxclk);
+               busclk_freq = clk_get_rate(spdif_priv->sysclk);
        }
 
        /* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
@@ -997,43 +1044,61 @@ static struct regmap_config fsl_spdif_regmap_config = {
 
 static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
                                struct clk *clk, u64 savesub,
-                               enum spdif_txrate index)
+                               enum spdif_txrate index, bool round)
 {
        const u32 rate[] = { 32000, 44100, 48000 };
+       bool is_sysclk = clk == spdif_priv->sysclk;
        u64 rate_ideal, rate_actual, sub;
-       u32 div, arate;
-
-       for (div = 1; div <= 128; div++) {
-               rate_ideal = rate[index] * (div + 1) * 64;
-               rate_actual = clk_round_rate(clk, rate_ideal);
-
-               arate = rate_actual / 64;
-               arate /= div;
-
-               if (arate == rate[index]) {
-                       /* We are lucky */
-                       savesub = 0;
-                       spdif_priv->txclk_div[index] = div;
-                       break;
-               } else if (arate / rate[index] == 1) {
-                       /* A little bigger than expect */
-                       sub = (arate - rate[index]) * 100000;
-                       do_div(sub, rate[index]);
-                       if (sub < savesub) {
+       u32 sysclk_dfmin, sysclk_dfmax;
+       u32 txclk_df, sysclk_df, arate;
+
+       /* The sysclk has an extra divisor [2, 512] */
+       sysclk_dfmin = is_sysclk ? 2 : 1;
+       sysclk_dfmax = is_sysclk ? 512 : 1;
+
+       for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
+               for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
+                       rate_ideal = rate[index] * (txclk_df + 1) * 64;
+                       if (round)
+                               rate_actual = clk_round_rate(clk, rate_ideal);
+                       else
+                               rate_actual = clk_get_rate(clk);
+
+                       arate = rate_actual / 64;
+                       arate /= txclk_df * sysclk_df;
+
+                       if (arate == rate[index]) {
+                               /* We are lucky */
+                               savesub = 0;
+                               spdif_priv->txclk_df[index] = txclk_df;
+                               spdif_priv->sysclk_df[index] = sysclk_df;
+                               spdif_priv->txrate[index] = arate;
+                               goto out;
+                       } else if (arate / rate[index] == 1) {
+                               /* A little bigger than expect */
+                               sub = (arate - rate[index]) * 100000;
+                               do_div(sub, rate[index]);
+                               if (sub >= savesub)
+                                       continue;
                                savesub = sub;
-                               spdif_priv->txclk_div[index] = div;
-                       }
-               } else if (rate[index] / arate == 1) {
-                       /* A little smaller than expect */
-                       sub = (rate[index] - arate) * 100000;
-                       do_div(sub, rate[index]);
-                       if (sub < savesub) {
+                               spdif_priv->txclk_df[index] = txclk_df;
+                               spdif_priv->sysclk_df[index] = sysclk_df;
+                               spdif_priv->txrate[index] = arate;
+                       } else if (rate[index] / arate == 1) {
+                               /* A little smaller than expect */
+                               sub = (rate[index] - arate) * 100000;
+                               do_div(sub, rate[index]);
+                               if (sub >= savesub)
+                                       continue;
                                savesub = sub;
-                               spdif_priv->txclk_div[index] = div;
+                               spdif_priv->txclk_df[index] = txclk_df;
+                               spdif_priv->sysclk_df[index] = sysclk_df;
+                               spdif_priv->txrate[index] = arate;
                        }
                }
        }
 
+out:
        return savesub;
 }
 
@@ -1058,7 +1123,8 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
                if (!clk_get_rate(clk))
                        continue;
 
-               ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index);
+               ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index,
+                                            i == STC_TXCLK_SPDIF_ROOT);
                if (savesub == ret)
                        continue;
 
@@ -1073,8 +1139,13 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
 
        dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
                        spdif_priv->txclk_src[index], rate[index]);
-       dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate\n",
-                       spdif_priv->txclk_div[index], rate[index]);
+       dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n",
+                       spdif_priv->txclk_df[index], rate[index]);
+       if (spdif_priv->txclk[index] == spdif_priv->sysclk)
+               dev_dbg(&pdev->dev, "use sysclk df %d for %dHz sample rate\n",
+                               spdif_priv->sysclk_df[index], rate[index]);
+       dev_dbg(&pdev->dev, "the best rate for %dHz sample rate is %dHz\n",
+                       rate[index], spdif_priv->txrate[index]);
 
        return 0;
 }
@@ -1134,6 +1205,20 @@ static int fsl_spdif_probe(struct platform_device *pdev)
                return ret;
        }
 
+       /* Get system clock for rx clock rate calculation */
+       spdif_priv->sysclk = devm_clk_get(&pdev->dev, "rxtx5");
+       if (IS_ERR(spdif_priv->sysclk)) {
+               dev_err(&pdev->dev, "no sys clock (rxtx5) in devicetree\n");
+               return PTR_ERR(spdif_priv->sysclk);
+       }
+
+       /* Get core clock for data register access via DMA */
+       spdif_priv->coreclk = devm_clk_get(&pdev->dev, "core");
+       if (IS_ERR(spdif_priv->coreclk)) {
+               dev_err(&pdev->dev, "no core clock in devicetree\n");
+               return PTR_ERR(spdif_priv->coreclk);
+       }
+
        /* Select clock source for rx/tx clock */
        spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
        if (IS_ERR(spdif_priv->rxclk)) {
@@ -1186,6 +1271,7 @@ static int fsl_spdif_probe(struct platform_device *pdev)
 
 static const struct of_device_id fsl_spdif_dt_ids[] = {
        { .compatible = "fsl,imx35-spdif", },
+       { .compatible = "fsl,vf610-spdif", },
        {}
 };
 MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
index 605a10b..16fde4b 100644 (file)
@@ -143,20 +143,22 @@ enum spdif_gainsel {
 #define INT_RXFIFO_FUL                 (1 << 0)
 
 /* SPDIF Clock register */
-#define STC_SYSCLK_DIV_OFFSET          11
-#define STC_SYSCLK_DIV_MASK            (0x1ff << STC_SYSCLK_DIV_OFFSET)
-#define STC_SYSCLK_DIV(x)              ((((x) - 1) << STC_SYSCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK)
+#define STC_SYSCLK_DF_OFFSET           11
+#define STC_SYSCLK_DF_MASK             (0x1ff << STC_SYSCLK_DF_OFFSET)
+#define STC_SYSCLK_DF(x)               ((((x) - 1) << STC_SYSCLK_DF_OFFSET) & STC_SYSCLK_DF_MASK)
 #define STC_TXCLK_SRC_OFFSET           8
 #define STC_TXCLK_SRC_MASK             (0x7 << STC_TXCLK_SRC_OFFSET)
 #define STC_TXCLK_SRC_SET(x)           ((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK)
 #define STC_TXCLK_ALL_EN_OFFSET                7
 #define STC_TXCLK_ALL_EN_MASK          (1 << STC_TXCLK_ALL_EN_OFFSET)
 #define STC_TXCLK_ALL_EN               (1 << STC_TXCLK_ALL_EN_OFFSET)
-#define STC_TXCLK_DIV_OFFSET           0
-#define STC_TXCLK_DIV_MASK             (0x7ff << STC_TXCLK_DIV_OFFSET)
-#define STC_TXCLK_DIV(x)               ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK)
+#define STC_TXCLK_DF_OFFSET            0
+#define STC_TXCLK_DF_MASK              (0x7ff << STC_TXCLK_DF_OFFSET)
+#define STC_TXCLK_DF(x)                ((((x) - 1) << STC_TXCLK_DF_OFFSET) & STC_TXCLK_DF_MASK)
 #define STC_TXCLK_SRC_MAX              8
 
+#define STC_TXCLK_SPDIF_ROOT           1
+
 /* SPDIF tx rate */
 enum spdif_txrate {
        SPDIF_TXRATE_32000 = 0,
index 5428a1f..9bfef55 100644 (file)
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/clk.h>
-#include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
+#include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include "fsl_ssi.h"
 #include "imx-pcm.h"
 
-#ifdef PPC
-#define read_ssi(addr)                  in_be32(addr)
-#define write_ssi(val, addr)            out_be32(addr, val)
-#define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set)
-#else
-#define read_ssi(addr)                  readl(addr)
-#define write_ssi(val, addr)            writel(val, addr)
-/*
- * FIXME: Proper locking should be added at write_ssi_mask caller level
- * to ensure this register read/modify/write sequence is race free.
- */
-static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
-{
-       u32 val = readl(addr);
-       val = (val & ~clear) | set;
-       writel(val, addr);
-}
-#endif
-
 /**
  * FSLSSI_I2S_RATES: sample rates supported by the I2S
  *
@@ -113,8 +94,6 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
 #define FSLSSI_SIER_DBG_TX_FLAGS (CCSR_SSI_SIER_TFE0_EN | \
                CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_EN | \
                CCSR_SSI_SIER_TUE0_EN | CCSR_SSI_SIER_TFRC_EN)
-#define FSLSSI_SISR_MASK (FSLSSI_SIER_DBG_RX_FLAGS | FSLSSI_SIER_DBG_TX_FLAGS)
-
 
 enum fsl_ssi_type {
        FSL_SSI_MCP8610,
@@ -134,87 +113,152 @@ struct fsl_ssi_rxtx_reg_val {
        struct fsl_ssi_reg_val rx;
        struct fsl_ssi_reg_val tx;
 };
+static const struct regmap_config fsl_ssi_regconfig = {
+       .max_register = CCSR_SSI_SACCDIS,
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .val_format_endian = REGMAP_ENDIAN_NATIVE,
+};
+
+struct fsl_ssi_soc_data {
+       bool imx;
+       bool offline_config;
+       u32 sisr_write_mask;
+};
 
 /**
  * fsl_ssi_private: per-SSI private data
  *
- * @ssi: pointer to the SSI's registers
- * @ssi_phys: physical address of the SSI registers
+ * @reg: Pointer to the regmap registers
  * @irq: IRQ of this SSI
- * @playback: the number of playback streams opened
- * @capture: the number of capture streams opened
- * @cpu_dai: the CPU DAI for this device
- * @dev_attr: the sysfs device attribute structure
- * @stats: SSI statistics
- * @name: name for this device
+ * @cpu_dai_drv: CPU DAI driver for this device
+ *
+ * @dai_fmt: DAI configuration this device is currently used with
+ * @i2s_mode: i2s and network mode configuration of the device. Is used to
+ * switch between normal and i2s/network mode
+ * mode depending on the number of channels
+ * @use_dma: DMA is used or FIQ with stream filter
+ * @use_dual_fifo: DMA with support for both FIFOs used
+ * @fifo_deph: Depth of the SSI FIFOs
+ * @rxtx_reg_val: Specific register settings for receive/transmit configuration
+ *
+ * @clk: SSI clock
+ * @baudclk: SSI baud clock for master mode
+ * @baudclk_streams: Active streams that are using baudclk
+ * @bitclk_freq: bitclock frequency set by .set_dai_sysclk
+ *
+ * @dma_params_tx: DMA transmit parameters
+ * @dma_params_rx: DMA receive parameters
+ * @ssi_phys: physical address of the SSI registers
+ *
+ * @fiq_params: FIQ stream filtering parameters
+ *
+ * @pdev: Pointer to pdev used for deprecated fsl-ssi sound card
+ *
+ * @dbg_stats: Debugging statistics
+ *
+ * @soc: SoC specifc data
  */
 struct fsl_ssi_private {
-       struct ccsr_ssi __iomem *ssi;
-       dma_addr_t ssi_phys;
+       struct regmap *regs;
        unsigned int irq;
-       unsigned int fifo_depth;
        struct snd_soc_dai_driver cpu_dai_drv;
-       struct platform_device *pdev;
 
-       enum fsl_ssi_type hw_type;
-       bool new_binding;
-       bool ssi_on_imx;
-       bool imx_ac97;
+       unsigned int dai_fmt;
+       u8 i2s_mode;
        bool use_dma;
-       bool baudclk_locked;
-       bool irq_stats;
-       bool offline_config;
        bool use_dual_fifo;
-       u8 i2s_mode;
-       spinlock_t baudclk_lock;
-       struct clk *baudclk;
+       unsigned int fifo_depth;
+       struct fsl_ssi_rxtx_reg_val rxtx_reg_val;
+
        struct clk *clk;
+       struct clk *baudclk;
+       unsigned int baudclk_streams;
+       unsigned int bitclk_freq;
+
+       /* DMA params */
        struct snd_dmaengine_dai_dma_data dma_params_tx;
        struct snd_dmaengine_dai_dma_data dma_params_rx;
-       struct imx_dma_data filter_data_tx;
-       struct imx_dma_data filter_data_rx;
+       dma_addr_t ssi_phys;
+
+       /* params for non-dma FIQ stream filtered mode */
        struct imx_pcm_fiq_params fiq_params;
-       /* Register values for rx/tx configuration */
-       struct fsl_ssi_rxtx_reg_val rxtx_reg_val;
 
-       struct {
-               unsigned int rfrc;
-               unsigned int tfrc;
-               unsigned int cmdau;
-               unsigned int cmddu;
-               unsigned int rxt;
-               unsigned int rdr1;
-               unsigned int rdr0;
-               unsigned int tde1;
-               unsigned int tde0;
-               unsigned int roe1;
-               unsigned int roe0;
-               unsigned int tue1;
-               unsigned int tue0;
-               unsigned int tfs;
-               unsigned int rfs;
-               unsigned int tls;
-               unsigned int rls;
-               unsigned int rff1;
-               unsigned int rff0;
-               unsigned int tfe1;
-               unsigned int tfe0;
-       } stats;
-       struct dentry *dbg_dir;
-       struct dentry *dbg_stats;
-
-       char name[1];
+       /* Used when using fsl-ssi as sound-card. This is only used by ppc and
+        * should be replaced with simple-sound-card. */
+       struct platform_device *pdev;
+
+       struct fsl_ssi_dbg dbg_stats;
+
+       const struct fsl_ssi_soc_data *soc;
+};
+
+/*
+ * imx51 and later SoCs have a slightly different IP that allows the
+ * SSI configuration while the SSI unit is running.
+ *
+ * More important, it is necessary on those SoCs to configure the
+ * sperate TX/RX DMA bits just before starting the stream
+ * (fsl_ssi_trigger). The SDMA unit has to be configured before fsl_ssi
+ * sends any DMA requests to the SDMA unit, otherwise it is not defined
+ * how the SDMA unit handles the DMA request.
+ *
+ * SDMA units are present on devices starting at imx35 but the imx35
+ * reference manual states that the DMA bits should not be changed
+ * while the SSI unit is running (SSIEN). So we support the necessary
+ * online configuration of fsl-ssi starting at imx51.
+ */
+
+static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = {
+       .imx = false,
+       .offline_config = true,
+       .sisr_write_mask = CCSR_SSI_SISR_RFRC | CCSR_SSI_SISR_TFRC |
+                       CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 |
+                       CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1,
+};
+
+static struct fsl_ssi_soc_data fsl_ssi_imx21 = {
+       .imx = true,
+       .offline_config = true,
+       .sisr_write_mask = 0,
+};
+
+static struct fsl_ssi_soc_data fsl_ssi_imx35 = {
+       .imx = true,
+       .offline_config = true,
+       .sisr_write_mask = CCSR_SSI_SISR_RFRC | CCSR_SSI_SISR_TFRC |
+                       CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 |
+                       CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1,
+};
+
+static struct fsl_ssi_soc_data fsl_ssi_imx51 = {
+       .imx = true,
+       .offline_config = false,
+       .sisr_write_mask = CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 |
+               CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1,
 };
 
 static const struct of_device_id fsl_ssi_ids[] = {
-       { .compatible = "fsl,mpc8610-ssi", .data = (void *) FSL_SSI_MCP8610},
-       { .compatible = "fsl,imx51-ssi", .data = (void *) FSL_SSI_MX51},
-       { .compatible = "fsl,imx35-ssi", .data = (void *) FSL_SSI_MX35},
-       { .compatible = "fsl,imx21-ssi", .data = (void *) FSL_SSI_MX21},
+       { .compatible = "fsl,mpc8610-ssi", .data = &fsl_ssi_mpc8610 },
+       { .compatible = "fsl,imx51-ssi", .data = &fsl_ssi_imx51 },
+       { .compatible = "fsl,imx35-ssi", .data = &fsl_ssi_imx35 },
+       { .compatible = "fsl,imx21-ssi", .data = &fsl_ssi_imx21 },
        {}
 };
 MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
 
+static bool fsl_ssi_is_ac97(struct fsl_ssi_private *ssi_private)
+{
+       return !!(ssi_private->dai_fmt & SND_SOC_DAIFMT_AC97);
+}
+
+static bool fsl_ssi_is_i2s_master(struct fsl_ssi_private *ssi_private)
+{
+       return (ssi_private->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
+               SND_SOC_DAIFMT_CBS_CFS;
+}
+
 /**
  * fsl_ssi_isr: SSI interrupt handler
  *
@@ -230,265 +274,75 @@ MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
 static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
 {
        struct fsl_ssi_private *ssi_private = dev_id;
-       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-       irqreturn_t ret = IRQ_NONE;
+       struct regmap *regs = ssi_private->regs;
        __be32 sisr;
        __be32 sisr2;
-       __be32 sisr_write_mask = 0;
-
-       switch (ssi_private->hw_type) {
-       case FSL_SSI_MX21:
-               sisr_write_mask = 0;
-               break;
-
-       case FSL_SSI_MCP8610:
-       case FSL_SSI_MX35:
-               sisr_write_mask = CCSR_SSI_SISR_RFRC | CCSR_SSI_SISR_TFRC |
-                       CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 |
-                       CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1;
-               break;
-
-       case FSL_SSI_MX51:
-               sisr_write_mask = CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 |
-                       CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1;
-               break;
-       }
 
        /* We got an interrupt, so read the status register to see what we
           were interrupted for.  We mask it with the Interrupt Enable register
           so that we only check for events that we're interested in.
         */
-       sisr = read_ssi(&ssi->sisr) & FSLSSI_SISR_MASK;
-
-       if (sisr & CCSR_SSI_SISR_RFRC) {
-               ssi_private->stats.rfrc++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TFRC) {
-               ssi_private->stats.tfrc++;
-               ret = IRQ_HANDLED;
-       }
+       regmap_read(regs, CCSR_SSI_SISR, &sisr);
 
-       if (sisr & CCSR_SSI_SISR_CMDAU) {
-               ssi_private->stats.cmdau++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_CMDDU) {
-               ssi_private->stats.cmddu++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RXT) {
-               ssi_private->stats.rxt++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RDR1) {
-               ssi_private->stats.rdr1++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RDR0) {
-               ssi_private->stats.rdr0++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TDE1) {
-               ssi_private->stats.tde1++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TDE0) {
-               ssi_private->stats.tde0++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_ROE1) {
-               ssi_private->stats.roe1++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_ROE0) {
-               ssi_private->stats.roe0++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TUE1) {
-               ssi_private->stats.tue1++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TUE0) {
-               ssi_private->stats.tue0++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TFS) {
-               ssi_private->stats.tfs++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RFS) {
-               ssi_private->stats.rfs++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TLS) {
-               ssi_private->stats.tls++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RLS) {
-               ssi_private->stats.rls++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RFF1) {
-               ssi_private->stats.rff1++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RFF0) {
-               ssi_private->stats.rff0++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TFE1) {
-               ssi_private->stats.tfe1++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TFE0) {
-               ssi_private->stats.tfe0++;
-               ret = IRQ_HANDLED;
-       }
-
-       sisr2 = sisr & sisr_write_mask;
+       sisr2 = sisr & ssi_private->soc->sisr_write_mask;
        /* Clear the bits that we set */
        if (sisr2)
-               write_ssi(sisr2, &ssi->sisr);
-
-       return ret;
-}
-
-#if IS_ENABLED(CONFIG_DEBUG_FS)
-/* Show the statistics of a flag only if its interrupt is enabled.  The
- * compiler will optimze this code to a no-op if the interrupt is not
- * enabled.
- */
-#define SIER_SHOW(flag, name) \
-       do { \
-               if (FSLSSI_SISR_MASK & CCSR_SSI_SIER_##flag) \
-                       seq_printf(s, #name "=%u\n", ssi_private->stats.name); \
-       } while (0)
-
-
-/**
- * fsl_sysfs_ssi_show: display SSI statistics
- *
- * Display the statistics for the current SSI device.  To avoid confusion,
- * we only show those counts that are enabled.
- */
-static int fsl_ssi_stats_show(struct seq_file *s, void *unused)
-{
-       struct fsl_ssi_private *ssi_private = s->private;
-
-       SIER_SHOW(RFRC_EN, rfrc);
-       SIER_SHOW(TFRC_EN, tfrc);
-       SIER_SHOW(CMDAU_EN, cmdau);
-       SIER_SHOW(CMDDU_EN, cmddu);
-       SIER_SHOW(RXT_EN, rxt);
-       SIER_SHOW(RDR1_EN, rdr1);
-       SIER_SHOW(RDR0_EN, rdr0);
-       SIER_SHOW(TDE1_EN, tde1);
-       SIER_SHOW(TDE0_EN, tde0);
-       SIER_SHOW(ROE1_EN, roe1);
-       SIER_SHOW(ROE0_EN, roe0);
-       SIER_SHOW(TUE1_EN, tue1);
-       SIER_SHOW(TUE0_EN, tue0);
-       SIER_SHOW(TFS_EN, tfs);
-       SIER_SHOW(RFS_EN, rfs);
-       SIER_SHOW(TLS_EN, tls);
-       SIER_SHOW(RLS_EN, rls);
-       SIER_SHOW(RFF1_EN, rff1);
-       SIER_SHOW(RFF0_EN, rff0);
-       SIER_SHOW(TFE1_EN, tfe1);
-       SIER_SHOW(TFE0_EN, tfe0);
-
-       return 0;
-}
-
-static int fsl_ssi_stats_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, fsl_ssi_stats_show, inode->i_private);
-}
-
-static const struct file_operations fsl_ssi_stats_ops = {
-       .open = fsl_ssi_stats_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
-
-static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private,
-               struct device *dev)
-{
-       ssi_private->dbg_dir = debugfs_create_dir(dev_name(dev), NULL);
-       if (!ssi_private->dbg_dir)
-               return -ENOMEM;
-
-       ssi_private->dbg_stats = debugfs_create_file("stats", S_IRUGO,
-                       ssi_private->dbg_dir, ssi_private, &fsl_ssi_stats_ops);
-       if (!ssi_private->dbg_stats) {
-               debugfs_remove(ssi_private->dbg_dir);
-               return -ENOMEM;
-       }
+               regmap_write(regs, CCSR_SSI_SISR, sisr2);
 
-       return 0;
-}
-
-static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private)
-{
-       debugfs_remove(ssi_private->dbg_stats);
-       debugfs_remove(ssi_private->dbg_dir);
-}
-
-#else
-
-static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private,
-               struct device *dev)
-{
-       return 0;
-}
+       fsl_ssi_dbg_isr(&ssi_private->dbg_stats, sisr);
 
-static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private)
-{
+       return IRQ_HANDLED;
 }
 
-#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
-
 /*
  * Enable/Disable all rx/tx config flags at once.
  */
 static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
                bool enable)
 {
-       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+       struct regmap *regs = ssi_private->regs;
        struct fsl_ssi_rxtx_reg_val *vals = &ssi_private->rxtx_reg_val;
 
        if (enable) {
-               write_ssi_mask(&ssi->sier, 0, vals->rx.sier | vals->tx.sier);
-               write_ssi_mask(&ssi->srcr, 0, vals->rx.srcr | vals->tx.srcr);
-               write_ssi_mask(&ssi->stcr, 0, vals->rx.stcr | vals->tx.stcr);
+               regmap_update_bits(regs, CCSR_SSI_SIER,
+                               vals->rx.sier | vals->tx.sier,
+                               vals->rx.sier | vals->tx.sier);
+               regmap_update_bits(regs, CCSR_SSI_SRCR,
+                               vals->rx.srcr | vals->tx.srcr,
+                               vals->rx.srcr | vals->tx.srcr);
+               regmap_update_bits(regs, CCSR_SSI_STCR,
+                               vals->rx.stcr | vals->tx.stcr,
+                               vals->rx.stcr | vals->tx.stcr);
        } else {
-               write_ssi_mask(&ssi->srcr, vals->rx.srcr | vals->tx.srcr, 0);
-               write_ssi_mask(&ssi->stcr, vals->rx.stcr | vals->tx.stcr, 0);
-               write_ssi_mask(&ssi->sier, vals->rx.sier | vals->tx.sier, 0);
+               regmap_update_bits(regs, CCSR_SSI_SRCR,
+                               vals->rx.srcr | vals->tx.srcr, 0);
+               regmap_update_bits(regs, CCSR_SSI_STCR,
+                               vals->rx.stcr | vals->tx.stcr, 0);
+               regmap_update_bits(regs, CCSR_SSI_SIER,
+                               vals->rx.sier | vals->tx.sier, 0);
        }
 }
 
+/*
+ * Calculate the bits that have to be disabled for the current stream that is
+ * getting disabled. This keeps the bits enabled that are necessary for the
+ * second stream to work if 'stream_active' is true.
+ *
+ * Detailed calculation:
+ * These are the values that need to be active after disabling. For non-active
+ * second stream, this is 0:
+ *     vals_stream * !!stream_active
+ *
+ * The following computes the overall differences between the setup for the
+ * to-disable stream and the active stream, a simple XOR:
+ *     vals_disable ^ (vals_stream * !!(stream_active))
+ *
+ * The full expression adds a mask on all values we care about
+ */
+#define fsl_ssi_disable_val(vals_disable, vals_stream, stream_active) \
+       ((vals_disable) & \
+        ((vals_disable) ^ ((vals_stream) * (u32)!!(stream_active))))
+
 /*
  * Enable/Disable a ssi configuration. You have to pass either
  * ssi_private->rxtx_reg_val.rx or tx as vals parameter.
@@ -496,12 +350,22 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
 static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
                struct fsl_ssi_reg_val *vals)
 {
-       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+       struct regmap *regs = ssi_private->regs;
        struct fsl_ssi_reg_val *avals;
-       u32 scr_val = read_ssi(&ssi->scr);
-       int nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) +
+       int nr_active_streams;
+       u32 scr_val;
+       int keep_active;
+
+       regmap_read(regs, CCSR_SSI_SCR, &scr_val);
+
+       nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) +
                                !!(scr_val & CCSR_SSI_SCR_RE);
 
+       if (nr_active_streams - 1 > 0)
+               keep_active = 1;
+       else
+               keep_active = 0;
+
        /* Find the other direction values rx or tx which we do not want to
         * modify */
        if (&ssi_private->rxtx_reg_val.rx == vals)
@@ -511,8 +375,9 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
 
        /* If vals should be disabled, start with disabling the unit */
        if (!enable) {
-               u32 scr = vals->scr & (vals->scr ^ avals->scr);
-               write_ssi_mask(&ssi->scr, scr, 0);
+               u32 scr = fsl_ssi_disable_val(vals->scr, avals->scr,
+                               keep_active);
+               regmap_update_bits(regs, CCSR_SSI_SCR, scr, 0);
        }
 
        /*
@@ -520,9 +385,9 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
         * reconfiguration, so we have to enable all necessary flags at once
         * even if we do not use them later (capture and playback configuration)
         */
-       if (ssi_private->offline_config) {
+       if (ssi_private->soc->offline_config) {
                if ((enable && !nr_active_streams) ||
-                               (!enable && nr_active_streams == 1))
+                               (!enable && !keep_active))
                        fsl_ssi_rxtx_config(ssi_private, enable);
 
                goto config_done;
@@ -533,9 +398,9 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
         * (online configuration)
         */
        if (enable) {
-               write_ssi_mask(&ssi->sier, 0, vals->sier);
-               write_ssi_mask(&ssi->srcr, 0, vals->srcr);
-               write_ssi_mask(&ssi->stcr, 0, vals->stcr);
+               regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier);
+               regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr);
+               regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr);
        } else {
                u32 sier;
                u32 srcr;
@@ -551,19 +416,22 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
                 */
 
                /* These assignments are simply vals without bits set in avals*/
-               sier = vals->sier & (vals->sier ^ avals->sier);
-               srcr = vals->srcr & (vals->srcr ^ avals->srcr);
-               stcr = vals->stcr & (vals->stcr ^ avals->stcr);
-
-               write_ssi_mask(&ssi->srcr, srcr, 0);
-               write_ssi_mask(&ssi->stcr, stcr, 0);
-               write_ssi_mask(&ssi->sier, sier, 0);
+               sier = fsl_ssi_disable_val(vals->sier, avals->sier,
+                               keep_active);
+               srcr = fsl_ssi_disable_val(vals->srcr, avals->srcr,
+                               keep_active);
+               stcr = fsl_ssi_disable_val(vals->stcr, avals->stcr,
+                               keep_active);
+
+               regmap_update_bits(regs, CCSR_SSI_SRCR, srcr, 0);
+               regmap_update_bits(regs, CCSR_SSI_STCR, stcr, 0);
+               regmap_update_bits(regs, CCSR_SSI_SIER, sier, 0);
        }
 
 config_done:
        /* Enabling of subunits is done after configuration */
        if (enable)
-               write_ssi_mask(&ssi->scr, 0, vals->scr);
+               regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr);
 }
 
 
@@ -593,7 +461,7 @@ static void fsl_ssi_setup_reg_vals(struct fsl_ssi_private *ssi_private)
        reg->tx.stcr = CCSR_SSI_STCR_TFEN0;
        reg->tx.scr = 0;
 
-       if (!ssi_private->imx_ac97) {
+       if (!fsl_ssi_is_ac97(ssi_private)) {
                reg->rx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE;
                reg->rx.sier |= CCSR_SSI_SIER_RFF0_EN;
                reg->tx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE;
@@ -614,159 +482,174 @@ static void fsl_ssi_setup_reg_vals(struct fsl_ssi_private *ssi_private)
 
 static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private)
 {
-       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+       struct regmap *regs = ssi_private->regs;
 
        /*
         * Setup the clock control register
         */
-       write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
-                       &ssi->stccr);
-       write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
-                       &ssi->srccr);
+       regmap_write(regs, CCSR_SSI_STCCR,
+                       CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13));
+       regmap_write(regs, CCSR_SSI_SRCCR,
+                       CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13));
 
        /*
         * Enable AC97 mode and startup the SSI
         */
-       write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV,
-                       &ssi->sacnt);
-       write_ssi(0xff, &ssi->saccdis);
-       write_ssi(0x300, &ssi->saccen);
+       regmap_write(regs, CCSR_SSI_SACNT,
+                       CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV);
+       regmap_write(regs, CCSR_SSI_SACCDIS, 0xff);
+       regmap_write(regs, CCSR_SSI_SACCEN, 0x300);
 
        /*
         * Enable SSI, Transmit and Receive. AC97 has to communicate with the
         * codec before a stream is started.
         */
-       write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN |
-                       CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE);
+       regmap_update_bits(regs, CCSR_SSI_SCR,
+                       CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE,
+                       CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE);
 
-       write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor);
+       regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_WAIT(3));
 }
 
-static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
+/**
+ * fsl_ssi_startup: create a new substream
+ *
+ * This is the first function called when a stream is opened.
+ *
+ * If this is the first stream open, then grab the IRQ and program most of
+ * the SSI registers.
+ */
+static int fsl_ssi_startup(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
 {
-       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-       u8 wm;
-       int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct fsl_ssi_private *ssi_private =
+               snd_soc_dai_get_drvdata(rtd->cpu_dai);
 
-       fsl_ssi_setup_reg_vals(ssi_private);
+       /* When using dual fifo mode, it is safer to ensure an even period
+        * size. If appearing to an odd number while DMA always starts its
+        * task from fifo0, fifo1 would be neglected at the end of each
+        * period. But SSI would still access fifo1 with an invalid data.
+        */
+       if (ssi_private->use_dual_fifo)
+               snd_pcm_hw_constraint_step(substream->runtime, 0,
+                               SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
+
+       return 0;
+}
+
+/**
+ * fsl_ssi_set_bclk - configure Digital Audio Interface bit clock
+ *
+ * Note: This function can be only called when using SSI as DAI master
+ *
+ * Quick instruction for parameters:
+ * freq: Output BCLK frequency = samplerate * 32 (fixed) * channels
+ * dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK.
+ */
+static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *cpu_dai,
+               struct snd_pcm_hw_params *hw_params)
+{
+       struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
+       struct regmap *regs = ssi_private->regs;
+       int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret;
+       u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
+       unsigned long clkrate, baudrate, tmprate;
+       u64 sub, savesub = 100000;
+       unsigned int freq;
+       bool baudclk_is_used;
 
-       if (ssi_private->imx_ac97)
-               ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET;
+       /* Prefer the explicitly set bitclock frequency */
+       if (ssi_private->bitclk_freq)
+               freq = ssi_private->bitclk_freq;
        else
-               ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
+               freq = params_channels(hw_params) * 32 * params_rate(hw_params);
 
-       /*
-        * Section 16.5 of the MPC8610 reference manual says that the SSI needs
-        * to be disabled before updating the registers we set here.
-        */
-       write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
+       /* Don't apply it to any non-baudclk circumstance */
+       if (IS_ERR(ssi_private->baudclk))
+               return -EINVAL;
 
-       /*
-        * Program the SSI into I2S Slave Non-Network Synchronous mode. Also
-        * enable the transmit and receive FIFO.
-        *
-        * FIXME: Little-endian samples require a different shift dir
-        */
-       write_ssi_mask(&ssi->scr,
-               CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
-               CCSR_SSI_SCR_TFR_CLK_DIS |
-               ssi_private->i2s_mode |
-               (synchronous ? CCSR_SSI_SCR_SYN : 0));
+       baudclk_is_used = ssi_private->baudclk_streams & ~(BIT(substream->stream));
 
-       write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFSI |
-                       CCSR_SSI_STCR_TEFS | CCSR_SSI_STCR_TSCKP, &ssi->stcr);
+       /* It should be already enough to divide clock by setting pm alone */
+       psr = 0;
+       div2 = 0;
 
-       write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFSI |
-                       CCSR_SSI_SRCR_REFS | CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
+       factor = (div2 + 1) * (7 * psr + 1) * 2;
 
-       /*
-        * The DC and PM bits are only used if the SSI is the clock master.
-        */
+       for (i = 0; i < 255; i++) {
+               /* The bclk rate must be smaller than 1/5 sysclk rate */
+               if (factor * (i + 1) < 5)
+                       continue;
 
-       /*
-        * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't
-        * use FIFO 1. We program the transmit water to signal a DMA transfer
-        * if there are only two (or fewer) elements left in the FIFO. Two
-        * elements equals one frame (left channel, right channel). This value,
-        * however, depends on the depth of the transmit buffer.
-        *
-        * We set the watermark on the same level as the DMA burstsize.  For
-        * fiq it is probably better to use the biggest possible watermark
-        * size.
-        */
-       if (ssi_private->use_dma)
-               wm = ssi_private->fifo_depth - 2;
-       else
-               wm = ssi_private->fifo_depth;
+               tmprate = freq * factor * (i + 2);
 
-       write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) |
-               CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm),
-               &ssi->sfcsr);
+               if (baudclk_is_used)
+                       clkrate = clk_get_rate(ssi_private->baudclk);
+               else
+                       clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
 
-       /*
-        * For ac97 interrupts are enabled with the startup of the substream
-        * because it is also running without an active substream. Normally SSI
-        * is only enabled when there is a substream.
-        */
-       if (ssi_private->imx_ac97)
-               fsl_ssi_setup_ac97(ssi_private);
+               do_div(clkrate, factor);
+               afreq = (u32)clkrate / (i + 1);
 
-       /*
-        * Set a default slot number so that there is no need for those common
-        * cases like I2S mode to call the extra set_tdm_slot() any more.
-        */
-       if (!ssi_private->imx_ac97) {
-               write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_DC_MASK,
-                               CCSR_SSI_SxCCR_DC(2));
-               write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_DC_MASK,
-                               CCSR_SSI_SxCCR_DC(2));
-       }
+               if (freq == afreq)
+                       sub = 0;
+               else if (freq / afreq == 1)
+                       sub = freq - afreq;
+               else if (afreq / freq == 1)
+                       sub = afreq - freq;
+               else
+                       continue;
 
-       if (ssi_private->use_dual_fifo) {
-               write_ssi_mask(&ssi->srcr, 0, CCSR_SSI_SRCR_RFEN1);
-               write_ssi_mask(&ssi->stcr, 0, CCSR_SSI_STCR_TFEN1);
-               write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_TCH_EN);
+               /* Calculate the fraction */
+               sub *= 100000;
+               do_div(sub, freq);
+
+               if (sub < savesub) {
+                       baudrate = tmprate;
+                       savesub = sub;
+                       pm = i;
+               }
+
+               /* We are lucky */
+               if (savesub == 0)
+                       break;
        }
 
-       return 0;
-}
+       /* No proper pm found if it is still remaining the initial value */
+       if (pm == 999) {
+               dev_err(cpu_dai->dev, "failed to handle the required sysclk\n");
+               return -EINVAL;
+       }
 
+       stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0) |
+               (psr ? CCSR_SSI_SxCCR_PSR : 0);
+       mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2 |
+               CCSR_SSI_SxCCR_PSR;
 
-/**
- * fsl_ssi_startup: create a new substream
- *
- * This is the first function called when a stream is opened.
- *
- * If this is the first stream open, then grab the IRQ and program most of
- * the SSI registers.
- */
-static int fsl_ssi_startup(struct snd_pcm_substream *substream,
-                          struct snd_soc_dai *dai)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct fsl_ssi_private *ssi_private =
-               snd_soc_dai_get_drvdata(rtd->cpu_dai);
-       unsigned long flags;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || synchronous)
+               regmap_update_bits(regs, CCSR_SSI_STCCR, mask, stccr);
+       else
+               regmap_update_bits(regs, CCSR_SSI_SRCCR, mask, stccr);
 
-       /* First, we only do fsl_ssi_setup() when SSI is going to be active.
-        * Second, fsl_ssi_setup was already called by ac97_init earlier if
-        * the driver is in ac97 mode.
-        */
-       if (!dai->active && !ssi_private->imx_ac97) {
-               fsl_ssi_setup(ssi_private);
-               spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
-               ssi_private->baudclk_locked = false;
-               spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
+       if (!baudclk_is_used) {
+               ret = clk_set_rate(ssi_private->baudclk, baudrate);
+               if (ret) {
+                       dev_err(cpu_dai->dev, "failed to set baudclk rate\n");
+                       return -EINVAL;
+               }
        }
 
-       /* When using dual fifo mode, it is safer to ensure an even period
-        * size. If appearing to an odd number while DMA always starts its
-        * task from fifo0, fifo1 would be neglected at the end of each
-        * period. But SSI would still access fifo1 with an invalid data.
-        */
-       if (ssi_private->use_dual_fifo)
-               snd_pcm_hw_constraint_step(substream->runtime, 0,
-                               SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
+       return 0;
+}
+
+static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
+
+       ssi_private->bitclk_freq = freq;
 
        return 0;
 }
@@ -788,12 +671,17 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
 {
        struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
-       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+       struct regmap *regs = ssi_private->regs;
        unsigned int channels = params_channels(hw_params);
        unsigned int sample_size =
                snd_pcm_format_width(params_format(hw_params));
        u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
-       int enabled = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
+       int ret;
+       u32 scr_val;
+       int enabled;
+
+       regmap_read(regs, CCSR_SSI_SCR, &scr_val);
+       enabled = scr_val & CCSR_SSI_SCR_SSIEN;
 
        /*
         * If we're in synchronous mode, and the SSI is already enabled,
@@ -802,6 +690,21 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
        if (enabled && ssi_private->cpu_dai_drv.symmetric_rates)
                return 0;
 
+       if (fsl_ssi_is_i2s_master(ssi_private)) {
+               ret = fsl_ssi_set_bclk(substream, cpu_dai, hw_params);
+               if (ret)
+                       return ret;
+
+               /* Do not enable the clock if it is already enabled */
+               if (!(ssi_private->baudclk_streams & BIT(substream->stream))) {
+                       ret = clk_prepare_enable(ssi_private->baudclk);
+                       if (ret)
+                               return ret;
+
+                       ssi_private->baudclk_streams |= BIT(substream->stream);
+               }
+       }
+
        /*
         * FIXME: The documentation says that SxCCR[WL] should not be
         * modified while the SSI is enabled.  The only time this can
@@ -815,49 +718,83 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
        /* In synchronous mode, the SSI uses STCCR for capture */
        if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
            ssi_private->cpu_dai_drv.symmetric_rates)
-               write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
+               regmap_update_bits(regs, CCSR_SSI_STCCR, CCSR_SSI_SxCCR_WL_MASK,
+                               wl);
        else
-               write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
+               regmap_update_bits(regs, CCSR_SSI_SRCCR, CCSR_SSI_SxCCR_WL_MASK,
+                               wl);
 
-       if (!ssi_private->imx_ac97)
-               write_ssi_mask(&ssi->scr,
+       if (!fsl_ssi_is_ac97(ssi_private))
+               regmap_update_bits(regs, CCSR_SSI_SCR,
                                CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK,
                                channels == 1 ? 0 : ssi_private->i2s_mode);
 
        return 0;
 }
 
-/**
- * fsl_ssi_set_dai_fmt - configure Digital Audio Interface Format.
- */
-static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+static int fsl_ssi_hw_free(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *cpu_dai)
 {
-       struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
-       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct fsl_ssi_private *ssi_private =
+               snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+       if (fsl_ssi_is_i2s_master(ssi_private) &&
+                       ssi_private->baudclk_streams & BIT(substream->stream)) {
+               clk_disable_unprepare(ssi_private->baudclk);
+               ssi_private->baudclk_streams &= ~BIT(substream->stream);
+       }
+
+       return 0;
+}
+
+static int _fsl_ssi_set_dai_fmt(struct fsl_ssi_private *ssi_private,
+               unsigned int fmt)
+{
+       struct regmap *regs = ssi_private->regs;
        u32 strcr = 0, stcr, srcr, scr, mask;
+       u8 wm;
+
+       ssi_private->dai_fmt = fmt;
+
+       if (fsl_ssi_is_i2s_master(ssi_private) && IS_ERR(ssi_private->baudclk)) {
+               dev_err(&ssi_private->pdev->dev, "baudclk is missing which is necessary for master mode\n");
+               return -EINVAL;
+       }
 
-       scr = read_ssi(&ssi->scr) & ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_I2S_MODE_MASK);
-       scr |= CCSR_SSI_SCR_NET;
+       fsl_ssi_setup_reg_vals(ssi_private);
+
+       regmap_read(regs, CCSR_SSI_SCR, &scr);
+       scr &= ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_I2S_MODE_MASK);
+       scr |= CCSR_SSI_SCR_SYNC_TX_FS;
 
        mask = CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR |
                CCSR_SSI_STCR_TSCKP | CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TFSL |
                CCSR_SSI_STCR_TEFS;
-       stcr = read_ssi(&ssi->stcr) & ~mask;
-       srcr = read_ssi(&ssi->srcr) & ~mask;
+       regmap_read(regs, CCSR_SSI_STCR, &stcr);
+       regmap_read(regs, CCSR_SSI_SRCR, &srcr);
+       stcr &= ~mask;
+       srcr &= ~mask;
 
+       ssi_private->i2s_mode = CCSR_SSI_SCR_NET;
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
                switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
                case SND_SOC_DAIFMT_CBS_CFS:
-                       ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_MASTER;
+                       ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_MASTER;
+                       regmap_update_bits(regs, CCSR_SSI_STCCR,
+                                       CCSR_SSI_SxCCR_DC_MASK,
+                                       CCSR_SSI_SxCCR_DC(2));
+                       regmap_update_bits(regs, CCSR_SSI_SRCCR,
+                                       CCSR_SSI_SxCCR_DC_MASK,
+                                       CCSR_SSI_SxCCR_DC(2));
                        break;
                case SND_SOC_DAIFMT_CBM_CFM:
-                       ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
+                       ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_SLAVE;
                        break;
                default:
                        return -EINVAL;
                }
-               scr |= ssi_private->i2s_mode;
 
                /* Data on rising edge of bclk, frame low, 1clk before data */
                strcr |= CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TSCKP |
@@ -877,9 +814,13 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
                strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP |
                        CCSR_SSI_STCR_TXBIT0;
                break;
+       case SND_SOC_DAIFMT_AC97:
+               ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_NORMAL;
+               break;
        default:
                return -EINVAL;
        }
+       scr |= ssi_private->i2s_mode;
 
        /* DAI clock inversion */
        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -925,105 +866,54 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
                scr |= CCSR_SSI_SCR_SYN;
        }
 
-       write_ssi(stcr, &ssi->stcr);
-       write_ssi(srcr, &ssi->srcr);
-       write_ssi(scr, &ssi->scr);
-
-       return 0;
-}
-
-/**
- * fsl_ssi_set_dai_sysclk - configure Digital Audio Interface bit clock
- *
- * Note: This function can be only called when using SSI as DAI master
- *
- * Quick instruction for parameters:
- * freq: Output BCLK frequency = samplerate * 32 (fixed) * channels
- * dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK.
- */
-static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
-                                 int clk_id, unsigned int freq, int dir)
-{
-       struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
-       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-       int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret;
-       u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
-       unsigned long flags, clkrate, baudrate, tmprate;
-       u64 sub, savesub = 100000;
-
-       /* Don't apply it to any non-baudclk circumstance */
-       if (IS_ERR(ssi_private->baudclk))
-               return -EINVAL;
-
-       /* It should be already enough to divide clock by setting pm alone */
-       psr = 0;
-       div2 = 0;
-
-       factor = (div2 + 1) * (7 * psr + 1) * 2;
-
-       for (i = 0; i < 255; i++) {
-               /* The bclk rate must be smaller than 1/5 sysclk rate */
-               if (factor * (i + 1) < 5)
-                       continue;
-
-               tmprate = freq * factor * (i + 2);
-               clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
-
-               do_div(clkrate, factor);
-               afreq = (u32)clkrate / (i + 1);
-
-               if (freq == afreq)
-                       sub = 0;
-               else if (freq / afreq == 1)
-                       sub = freq - afreq;
-               else if (afreq / freq == 1)
-                       sub = afreq - freq;
-               else
-                       continue;
+       regmap_write(regs, CCSR_SSI_STCR, stcr);
+       regmap_write(regs, CCSR_SSI_SRCR, srcr);
+       regmap_write(regs, CCSR_SSI_SCR, scr);
 
-               /* Calculate the fraction */
-               sub *= 100000;
-               do_div(sub, freq);
+       /*
+        * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't
+        * use FIFO 1. We program the transmit water to signal a DMA transfer
+        * if there are only two (or fewer) elements left in the FIFO. Two
+        * elements equals one frame (left channel, right channel). This value,
+        * however, depends on the depth of the transmit buffer.
+        *
+        * We set the watermark on the same level as the DMA burstsize.  For
+        * fiq it is probably better to use the biggest possible watermark
+        * size.
+        */
+       if (ssi_private->use_dma)
+               wm = ssi_private->fifo_depth - 2;
+       else
+               wm = ssi_private->fifo_depth;
 
-               if (sub < savesub) {
-                       baudrate = tmprate;
-                       savesub = sub;
-                       pm = i;
-               }
+       regmap_write(regs, CCSR_SSI_SFCSR,
+                       CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) |
+                       CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm));
 
-               /* We are lucky */
-               if (savesub == 0)
-                       break;
+       if (ssi_private->use_dual_fifo) {
+               regmap_update_bits(regs, CCSR_SSI_SRCR, CCSR_SSI_SRCR_RFEN1,
+                               CCSR_SSI_SRCR_RFEN1);
+               regmap_update_bits(regs, CCSR_SSI_STCR, CCSR_SSI_STCR_TFEN1,
+                               CCSR_SSI_STCR_TFEN1);
+               regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_TCH_EN,
+                               CCSR_SSI_SCR_TCH_EN);
        }
 
-       /* No proper pm found if it is still remaining the initial value */
-       if (pm == 999) {
-               dev_err(cpu_dai->dev, "failed to handle the required sysclk\n");
-               return -EINVAL;
-       }
+       if (fmt & SND_SOC_DAIFMT_AC97)
+               fsl_ssi_setup_ac97(ssi_private);
 
-       stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0) |
-               (psr ? CCSR_SSI_SxCCR_PSR : 0);
-       mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2 | CCSR_SSI_SxCCR_PSR;
+       return 0;
 
-       if (dir == SND_SOC_CLOCK_OUT || synchronous)
-               write_ssi_mask(&ssi->stccr, mask, stccr);
-       else
-               write_ssi_mask(&ssi->srccr, mask, stccr);
+}
 
-       spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
-       if (!ssi_private->baudclk_locked) {
-               ret = clk_set_rate(ssi_private->baudclk, baudrate);
-               if (ret) {
-                       spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
-                       dev_err(cpu_dai->dev, "failed to set baudclk rate\n");
-                       return -EINVAL;
-               }
-               ssi_private->baudclk_locked = true;
-       }
-       spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
+/**
+ * fsl_ssi_set_dai_fmt - configure Digital Audio Interface Format.
+ */
+static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+       struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
 
-       return 0;
+       return _fsl_ssi_set_dai_fmt(ssi_private, fmt);
 }
 
 /**
@@ -1035,31 +925,34 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
                                u32 rx_mask, int slots, int slot_width)
 {
        struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
-       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+       struct regmap *regs = ssi_private->regs;
        u32 val;
 
        /* The slot number should be >= 2 if using Network mode or I2S mode */
-       val = read_ssi(&ssi->scr) & (CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_NET);
+       regmap_read(regs, CCSR_SSI_SCR, &val);
+       val &= CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_NET;
        if (val && slots < 2) {
                dev_err(cpu_dai->dev, "slot number should be >= 2 in I2S or NET\n");
                return -EINVAL;
        }
 
-       write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_DC_MASK,
+       regmap_update_bits(regs, CCSR_SSI_STCCR, CCSR_SSI_SxCCR_DC_MASK,
                        CCSR_SSI_SxCCR_DC(slots));
-       write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_DC_MASK,
+       regmap_update_bits(regs, CCSR_SSI_SRCCR, CCSR_SSI_SxCCR_DC_MASK,
                        CCSR_SSI_SxCCR_DC(slots));
 
        /* The register SxMSKs needs SSI to provide essential clock due to
         * hardware design. So we here temporarily enable SSI to set them.
         */
-       val = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
-       write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN);
+       regmap_read(regs, CCSR_SSI_SCR, &val);
+       val &= CCSR_SSI_SCR_SSIEN;
+       regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN,
+                       CCSR_SSI_SCR_SSIEN);
 
-       write_ssi(tx_mask, &ssi->stmsk);
-       write_ssi(rx_mask, &ssi->srmsk);
+       regmap_write(regs, CCSR_SSI_STMSK, tx_mask);
+       regmap_write(regs, CCSR_SSI_SRMSK, rx_mask);
 
-       write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, val);
+       regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN, val);
 
        return 0;
 }
@@ -1078,11 +971,11 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
-       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-       unsigned long flags;
+       struct regmap *regs = ssi_private->regs;
 
        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)
                        fsl_ssi_tx_config(ssi_private, true);
@@ -1091,29 +984,23 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
                break;
 
        case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                        fsl_ssi_tx_config(ssi_private, false);
                else
                        fsl_ssi_rx_config(ssi_private, false);
-
-               if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) &
-                                       (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) {
-                       spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
-                       ssi_private->baudclk_locked = false;
-                       spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
-               }
                break;
 
        default:
                return -EINVAL;
        }
 
-       if (ssi_private->imx_ac97) {
+       if (fsl_ssi_is_ac97(ssi_private)) {
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-                       write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor);
+                       regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_TX_CLR);
                else
-                       write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor);
+                       regmap_write(regs, CCSR_SSI_SOR, CCSR_SSI_SOR_RX_CLR);
        }
 
        return 0;
@@ -1123,7 +1010,7 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
 {
        struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai);
 
-       if (ssi_private->ssi_on_imx && ssi_private->use_dma) {
+       if (ssi_private->soc->imx && ssi_private->use_dma) {
                dai->playback_dma_data = &ssi_private->dma_params_tx;
                dai->capture_dma_data = &ssi_private->dma_params_rx;
        }
@@ -1134,6 +1021,7 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
 static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
        .startup        = fsl_ssi_startup,
        .hw_params      = fsl_ssi_hw_params,
+       .hw_free        = fsl_ssi_hw_free,
        .set_fmt        = fsl_ssi_set_dai_fmt,
        .set_sysclk     = fsl_ssi_set_dai_sysclk,
        .set_tdm_slot   = fsl_ssi_set_dai_tdm_slot,
@@ -1184,15 +1072,10 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
 
 static struct fsl_ssi_private *fsl_ac97_data;
 
-static void fsl_ssi_ac97_init(void)
-{
-       fsl_ssi_setup(fsl_ac97_data);
-}
-
 static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
                unsigned short val)
 {
-       struct ccsr_ssi *ssi = fsl_ac97_data->ssi;
+       struct regmap *regs = fsl_ac97_data->regs;
        unsigned int lreg;
        unsigned int lval;
 
@@ -1201,12 +1084,12 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
 
 
        lreg = reg <<  12;
-       write_ssi(lreg, &ssi->sacadd);
+       regmap_write(regs, CCSR_SSI_SACADD, lreg);
 
        lval = val << 4;
-       write_ssi(lval , &ssi->sacdat);
+       regmap_write(regs, CCSR_SSI_SACDAT, lval);
 
-       write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK,
+       regmap_update_bits(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_RDWR_MASK,
                        CCSR_SSI_SACNT_WR);
        udelay(100);
 }
@@ -1214,19 +1097,21 @@ static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
 static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
                unsigned short reg)
 {
-       struct ccsr_ssi *ssi = fsl_ac97_data->ssi;
+       struct regmap *regs = fsl_ac97_data->regs;
 
        unsigned short val = -1;
+       u32 reg_val;
        unsigned int lreg;
 
        lreg = (reg & 0x7f) <<  12;
-       write_ssi(lreg, &ssi->sacadd);
-       write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK,
+       regmap_write(regs, CCSR_SSI_SACADD, lreg);
+       regmap_update_bits(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_RDWR_MASK,
                        CCSR_SSI_SACNT_RD);
 
        udelay(100);
 
-       val = (read_ssi(&ssi->sacdat) >> 4) & 0xffff;
+       regmap_read(regs, CCSR_SSI_SACDAT, &reg_val);
+       val = (reg_val >> 4) & 0xffff;
 
        return val;
 }
@@ -1251,20 +1136,105 @@ static void make_lowercase(char *s)
        }
 }
 
+static int fsl_ssi_imx_probe(struct platform_device *pdev,
+               struct fsl_ssi_private *ssi_private, void __iomem *iomem)
+{
+       struct device_node *np = pdev->dev.of_node;
+       u32 dmas[4];
+       int ret;
+
+       ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(ssi_private->clk)) {
+               ret = PTR_ERR(ssi_private->clk);
+               dev_err(&pdev->dev, "could not get clock: %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(ssi_private->clk);
+       if (ret) {
+               dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
+               return ret;
+       }
+
+       /* For those SLAVE implementations, we ingore non-baudclk cases
+        * and, instead, abandon MASTER mode that needs baud clock.
+        */
+       ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
+       if (IS_ERR(ssi_private->baudclk))
+               dev_dbg(&pdev->dev, "could not get baud clock: %ld\n",
+                        PTR_ERR(ssi_private->baudclk));
+
+       /*
+        * We have burstsize be "fifo_depth - 2" to match the SSI
+        * watermark setting in fsl_ssi_startup().
+        */
+       ssi_private->dma_params_tx.maxburst = ssi_private->fifo_depth - 2;
+       ssi_private->dma_params_rx.maxburst = ssi_private->fifo_depth - 2;
+       ssi_private->dma_params_tx.addr = ssi_private->ssi_phys + CCSR_SSI_STX0;
+       ssi_private->dma_params_rx.addr = ssi_private->ssi_phys + CCSR_SSI_SRX0;
+
+       ret = !of_property_read_u32_array(np, "dmas", dmas, 4);
+       if (ssi_private->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) {
+               ssi_private->use_dual_fifo = true;
+               /* When using dual fifo mode, we need to keep watermark
+                * as even numbers due to dma script limitation.
+                */
+               ssi_private->dma_params_tx.maxburst &= ~0x1;
+               ssi_private->dma_params_rx.maxburst &= ~0x1;
+       }
+
+       if (!ssi_private->use_dma) {
+
+               /*
+                * Some boards use an incompatible codec. To get it
+                * working, we are using imx-fiq-pcm-audio, that
+                * can handle those codecs. DMA is not possible in this
+                * situation.
+                */
+
+               ssi_private->fiq_params.irq = ssi_private->irq;
+               ssi_private->fiq_params.base = iomem;
+               ssi_private->fiq_params.dma_params_rx =
+                       &ssi_private->dma_params_rx;
+               ssi_private->fiq_params.dma_params_tx =
+                       &ssi_private->dma_params_tx;
+
+               ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
+               if (ret)
+                       goto error_pcm;
+       } else {
+               ret = imx_pcm_dma_init(pdev);
+               if (ret)
+                       goto error_pcm;
+       }
+
+       return 0;
+
+error_pcm:
+       clk_disable_unprepare(ssi_private->clk);
+
+       return ret;
+}
+
+static void fsl_ssi_imx_clean(struct platform_device *pdev,
+               struct fsl_ssi_private *ssi_private)
+{
+       if (!ssi_private->use_dma)
+               imx_pcm_fiq_exit(pdev);
+       clk_disable_unprepare(ssi_private->clk);
+}
+
 static int fsl_ssi_probe(struct platform_device *pdev)
 {
        struct fsl_ssi_private *ssi_private;
        int ret = 0;
-       struct device_attribute *dev_attr = NULL;
        struct device_node *np = pdev->dev.of_node;
        const struct of_device_id *of_id;
-       enum fsl_ssi_type hw_type;
        const char *p, *sprop;
        const uint32_t *iprop;
        struct resource res;
+       void __iomem *iomem;
        char name[64];
-       bool shared;
-       bool ac97 = false;
 
        /* SSIs that are not connected on the board should have a
         *      status = "disabled"
@@ -1274,39 +1244,35 @@ static int fsl_ssi_probe(struct platform_device *pdev)
                return -ENODEV;
 
        of_id = of_match_device(fsl_ssi_ids, &pdev->dev);
-       if (!of_id)
-               return -EINVAL;
-       hw_type = (enum fsl_ssi_type) of_id->data;
-
-       sprop = of_get_property(np, "fsl,mode", NULL);
-       if (!sprop) {
-               dev_err(&pdev->dev, "fsl,mode property is necessary\n");
+       if (!of_id || !of_id->data)
                return -EINVAL;
-       }
-       if (!strcmp(sprop, "ac97-slave"))
-               ac97 = true;
 
-       /* The DAI name is the last part of the full name of the node. */
-       p = strrchr(np->full_name, '/') + 1;
-       ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private) + strlen(p),
-                             GFP_KERNEL);
+       ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private),
+                       GFP_KERNEL);
        if (!ssi_private) {
                dev_err(&pdev->dev, "could not allocate DAI object\n");
                return -ENOMEM;
        }
 
-       strcpy(ssi_private->name, p);
+       ssi_private->soc = of_id->data;
+
+       sprop = of_get_property(np, "fsl,mode", NULL);
+       if (sprop) {
+               if (!strcmp(sprop, "ac97-slave"))
+                       ssi_private->dai_fmt = SND_SOC_DAIFMT_AC97;
+               else if (!strcmp(sprop, "i2s-slave"))
+                       ssi_private->dai_fmt = SND_SOC_DAIFMT_I2S |
+                               SND_SOC_DAIFMT_CBM_CFM;
+       }
 
        ssi_private->use_dma = !of_property_read_bool(np,
                        "fsl,fiq-stream-filter");
-       ssi_private->hw_type = hw_type;
 
-       if (ac97) {
+       if (fsl_ssi_is_ac97(ssi_private)) {
                memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai,
                                sizeof(fsl_ssi_ac97_dai));
 
                fsl_ac97_data = ssi_private;
-               ssi_private->imx_ac97 = true;
 
                snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
        } else {
@@ -1314,7 +1280,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
                memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
                       sizeof(fsl_ssi_dai_template));
        }
-       ssi_private->cpu_dai_drv.name = ssi_private->name;
+       ssi_private->cpu_dai_drv.name = dev_name(&pdev->dev);
 
        /* Get the addresses and IRQ */
        ret = of_address_to_resource(np, 0, &res);
@@ -1322,12 +1288,20 @@ static int fsl_ssi_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "could not determine device resources\n");
                return ret;
        }
-       ssi_private->ssi = of_iomap(np, 0);
-       if (!ssi_private->ssi) {
+       ssi_private->ssi_phys = res.start;
+
+       iomem = devm_ioremap(&pdev->dev, res.start, resource_size(&res));
+       if (!iomem) {
                dev_err(&pdev->dev, "could not map device resources\n");
                return -ENOMEM;
        }
-       ssi_private->ssi_phys = res.start;
+
+       ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem,
+                       &fsl_ssi_regconfig);
+       if (IS_ERR(ssi_private->regs)) {
+               dev_err(&pdev->dev, "Failed to init register map\n");
+               return PTR_ERR(ssi_private->regs);
+       }
 
        ssi_private->irq = irq_of_parse_and_map(np, 0);
        if (!ssi_private->irq) {
@@ -1350,180 +1324,43 @@ static int fsl_ssi_probe(struct platform_device *pdev)
                 /* Older 8610 DTs didn't have the fifo-depth property */
                ssi_private->fifo_depth = 8;
 
-       ssi_private->baudclk_locked = false;
-       spin_lock_init(&ssi_private->baudclk_lock);
-
-       /*
-        * imx51 and later SoCs have a slightly different IP that allows the
-        * SSI configuration while the SSI unit is running.
-        *
-        * More important, it is necessary on those SoCs to configure the
-        * sperate TX/RX DMA bits just before starting the stream
-        * (fsl_ssi_trigger). The SDMA unit has to be configured before fsl_ssi
-        * sends any DMA requests to the SDMA unit, otherwise it is not defined
-        * how the SDMA unit handles the DMA request.
-        *
-        * SDMA units are present on devices starting at imx35 but the imx35
-        * reference manual states that the DMA bits should not be changed
-        * while the SSI unit is running (SSIEN). So we support the necessary
-        * online configuration of fsl-ssi starting at imx51.
-        */
-       switch (hw_type) {
-       case FSL_SSI_MCP8610:
-       case FSL_SSI_MX21:
-       case FSL_SSI_MX35:
-               ssi_private->offline_config = true;
-               break;
-       case FSL_SSI_MX51:
-               ssi_private->offline_config = false;
-               break;
-       }
-
-       if (hw_type == FSL_SSI_MX21 || hw_type == FSL_SSI_MX51 ||
-                       hw_type == FSL_SSI_MX35) {
-               u32 dma_events[2], dmas[4];
-               ssi_private->ssi_on_imx = true;
+       dev_set_drvdata(&pdev->dev, ssi_private);
 
-               ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
-               if (IS_ERR(ssi_private->clk)) {
-                       ret = PTR_ERR(ssi_private->clk);
-                       dev_err(&pdev->dev, "could not get clock: %d\n", ret);
-                       goto error_irqmap;
-               }
-               ret = clk_prepare_enable(ssi_private->clk);
-               if (ret) {
-                       dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n",
-                               ret);
+       if (ssi_private->soc->imx) {
+               ret = fsl_ssi_imx_probe(pdev, ssi_private, iomem);
+               if (ret)
                        goto error_irqmap;
-               }
-
-               /* For those SLAVE implementations, we ingore non-baudclk cases
-                * and, instead, abandon MASTER mode that needs baud clock.
-                */
-               ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
-               if (IS_ERR(ssi_private->baudclk))
-                       dev_dbg(&pdev->dev, "could not get baud clock: %ld\n",
-                                PTR_ERR(ssi_private->baudclk));
-               else
-                       clk_prepare_enable(ssi_private->baudclk);
-
-               /*
-                * We have burstsize be "fifo_depth - 2" to match the SSI
-                * watermark setting in fsl_ssi_startup().
-                */
-               ssi_private->dma_params_tx.maxburst =
-                       ssi_private->fifo_depth - 2;
-               ssi_private->dma_params_rx.maxburst =
-                       ssi_private->fifo_depth - 2;
-               ssi_private->dma_params_tx.addr =
-                       ssi_private->ssi_phys + offsetof(struct ccsr_ssi, stx0);
-               ssi_private->dma_params_rx.addr =
-                       ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0);
-               ssi_private->dma_params_tx.filter_data =
-                       &ssi_private->filter_data_tx;
-               ssi_private->dma_params_rx.filter_data =
-                       &ssi_private->filter_data_rx;
-               if (!of_property_read_bool(pdev->dev.of_node, "dmas") &&
-                               ssi_private->use_dma) {
-                       /*
-                        * FIXME: This is a temporary solution until all
-                        * necessary dma drivers support the generic dma
-                        * bindings.
-                        */
-                       ret = of_property_read_u32_array(pdev->dev.of_node,
-                                       "fsl,ssi-dma-events", dma_events, 2);
-                       if (ret && ssi_private->use_dma) {
-                               dev_err(&pdev->dev, "could not get dma events but fsl-ssi is configured to use DMA\n");
-                               goto error_clk;
-                       }
-               }
-               /* Should this be merge with the above? */
-               if (!of_property_read_u32_array(pdev->dev.of_node, "dmas", dmas, 4)
-                               && dmas[2] == IMX_DMATYPE_SSI_DUAL) {
-                       ssi_private->use_dual_fifo = true;
-                       /* When using dual fifo mode, we need to keep watermark
-                        * as even numbers due to dma script limitation.
-                        */
-                       ssi_private->dma_params_tx.maxburst &= ~0x1;
-                       ssi_private->dma_params_rx.maxburst &= ~0x1;
-               }
-
-               shared = of_device_is_compatible(of_get_parent(np),
-                           "fsl,spba-bus");
+       }
 
-               imx_pcm_dma_params_init_data(&ssi_private->filter_data_tx,
-                       dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
-               imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx,
-                       dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
+       ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
+                                        &ssi_private->cpu_dai_drv, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
+               goto error_asoc_register;
        }
 
-       /*
-        * Enable interrupts only for MCP8610 and MX51. The other MXs have
-        * different writeable interrupt status registers.
-        */
        if (ssi_private->use_dma) {
-               /* The 'name' should not have any slashes in it. */
                ret = devm_request_irq(&pdev->dev, ssi_private->irq,
-                                       fsl_ssi_isr, 0, ssi_private->name,
+                                       fsl_ssi_isr, 0, dev_name(&pdev->dev),
                                        ssi_private);
-               ssi_private->irq_stats = true;
                if (ret < 0) {
                        dev_err(&pdev->dev, "could not claim irq %u\n",
                                        ssi_private->irq);
-                       goto error_clk;
+                       goto error_irq;
                }
        }
 
-       /* Register with ASoC */
-       dev_set_drvdata(&pdev->dev, ssi_private);
-
-       ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
-                                        &ssi_private->cpu_dai_drv, 1);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
-               goto error_dev;
-       }
-
-       ret = fsl_ssi_debugfs_create(ssi_private, &pdev->dev);
+       ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev);
        if (ret)
-               goto error_dbgfs;
-
-       if (ssi_private->ssi_on_imx) {
-               if (!ssi_private->use_dma) {
-
-                       /*
-                        * Some boards use an incompatible codec. To get it
-                        * working, we are using imx-fiq-pcm-audio, that
-                        * can handle those codecs. DMA is not possible in this
-                        * situation.
-                        */
-
-                       ssi_private->fiq_params.irq = ssi_private->irq;
-                       ssi_private->fiq_params.base = ssi_private->ssi;
-                       ssi_private->fiq_params.dma_params_rx =
-                               &ssi_private->dma_params_rx;
-                       ssi_private->fiq_params.dma_params_tx =
-                               &ssi_private->dma_params_tx;
-
-                       ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
-                       if (ret)
-                               goto error_pcm;
-               } else {
-                       ret = imx_pcm_dma_init(pdev);
-                       if (ret)
-                               goto error_pcm;
-               }
-       }
+               goto error_asoc_register;
 
        /*
         * If codec-handle property is missing from SSI node, we assume
         * that the machine driver uses new binding which does not require
         * SSI driver to trigger machine driver's probe.
         */
-       if (!of_get_property(np, "codec-handle", NULL)) {
-               ssi_private->new_binding = true;
+       if (!of_get_property(np, "codec-handle", NULL))
                goto done;
-       }
 
        /* Trigger the machine driver's probe function.  The platform driver
         * name of the machine driver is taken from /compatible property of the
@@ -1543,37 +1380,27 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        if (IS_ERR(ssi_private->pdev)) {
                ret = PTR_ERR(ssi_private->pdev);
                dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
-               goto error_dai;
+               goto error_sound_card;
        }
 
 done:
-       if (ssi_private->imx_ac97)
-               fsl_ssi_ac97_init();
+       if (ssi_private->dai_fmt)
+               _fsl_ssi_set_dai_fmt(ssi_private, ssi_private->dai_fmt);
 
        return 0;
 
-error_dai:
-       if (ssi_private->ssi_on_imx && !ssi_private->use_dma)
-               imx_pcm_fiq_exit(pdev);
-
-error_pcm:
-       fsl_ssi_debugfs_remove(ssi_private);
+error_sound_card:
+       fsl_ssi_debugfs_remove(&ssi_private->dbg_stats);
 
-error_dbgfs:
+error_irq:
        snd_soc_unregister_component(&pdev->dev);
 
-error_dev:
-       device_remove_file(&pdev->dev, dev_attr);
-
-error_clk:
-       if (ssi_private->ssi_on_imx) {
-               if (!IS_ERR(ssi_private->baudclk))
-                       clk_disable_unprepare(ssi_private->baudclk);
-               clk_disable_unprepare(ssi_private->clk);
-       }
+error_asoc_register:
+       if (ssi_private->soc->imx)
+               fsl_ssi_imx_clean(pdev, ssi_private);
 
 error_irqmap:
-       if (ssi_private->irq_stats)
+       if (ssi_private->use_dma)
                irq_dispose_mapping(ssi_private->irq);
 
        return ret;
@@ -1583,17 +1410,16 @@ static int fsl_ssi_remove(struct platform_device *pdev)
 {
        struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
 
-       fsl_ssi_debugfs_remove(ssi_private);
+       fsl_ssi_debugfs_remove(&ssi_private->dbg_stats);
 
-       if (!ssi_private->new_binding)
+       if (ssi_private->pdev)
                platform_device_unregister(ssi_private->pdev);
        snd_soc_unregister_component(&pdev->dev);
-       if (ssi_private->ssi_on_imx) {
-               if (!IS_ERR(ssi_private->baudclk))
-                       clk_disable_unprepare(ssi_private->baudclk);
-               clk_disable_unprepare(ssi_private->clk);
-       }
-       if (ssi_private->irq_stats)
+
+       if (ssi_private->soc->imx)
+               fsl_ssi_imx_clean(pdev, ssi_private);
+
+       if (ssi_private->use_dma)
                irq_dispose_mapping(ssi_private->irq);
 
        return 0;
index e6b6324..5065105 100644 (file)
 #ifndef _MPC8610_I2S_H
 #define _MPC8610_I2S_H
 
-/* SSI Register Map */
-struct ccsr_ssi {
-       __be32 stx0;    /* 0x.0000 - SSI Transmit Data Register 0 */
-       __be32 stx1;    /* 0x.0004 - SSI Transmit Data Register 1 */
-       __be32 srx0;    /* 0x.0008 - SSI Receive Data Register 0 */
-       __be32 srx1;    /* 0x.000C - SSI Receive Data Register 1 */
-       __be32 scr;     /* 0x.0010 - SSI Control Register */
-       __be32 sisr;    /* 0x.0014 - SSI Interrupt Status Register Mixed */
-       __be32 sier;    /* 0x.0018 - SSI Interrupt Enable Register */
-       __be32 stcr;    /* 0x.001C - SSI Transmit Configuration Register */
-       __be32 srcr;    /* 0x.0020 - SSI Receive Configuration Register */
-       __be32 stccr;   /* 0x.0024 - SSI Transmit Clock Control Register */
-       __be32 srccr;   /* 0x.0028 - SSI Receive Clock Control Register */
-       __be32 sfcsr;   /* 0x.002C - SSI FIFO Control/Status Register */
-       __be32 str;     /* 0x.0030 - SSI Test Register */
-       __be32 sor;     /* 0x.0034 - SSI Option Register */
-       __be32 sacnt;   /* 0x.0038 - SSI AC97 Control Register */
-       __be32 sacadd;  /* 0x.003C - SSI AC97 Command Address Register */
-       __be32 sacdat;  /* 0x.0040 - SSI AC97 Command Data Register */
-       __be32 satag;   /* 0x.0044 - SSI AC97 Tag Register */
-       __be32 stmsk;   /* 0x.0048 - SSI Transmit Time Slot Mask Register */
-       __be32 srmsk;   /* 0x.004C - SSI Receive Time Slot Mask Register */
-       __be32 saccst;  /* 0x.0050 - SSI AC97 Channel Status Register */
-       __be32 saccen;  /* 0x.0054 - SSI AC97 Channel Enable Register */
-       __be32 saccdis; /* 0x.0058 - SSI AC97 Channel Disable Register */
-};
+/* SSI registers */
+#define CCSR_SSI_STX0                  0x00
+#define CCSR_SSI_STX1                  0x04
+#define CCSR_SSI_SRX0                  0x08
+#define CCSR_SSI_SRX1                  0x0c
+#define CCSR_SSI_SCR                   0x10
+#define CCSR_SSI_SISR                  0x14
+#define CCSR_SSI_SIER                  0x18
+#define CCSR_SSI_STCR                  0x1c
+#define CCSR_SSI_SRCR                  0x20
+#define CCSR_SSI_STCCR                 0x24
+#define CCSR_SSI_SRCCR                 0x28
+#define CCSR_SSI_SFCSR                 0x2c
+#define CCSR_SSI_STR                   0x30
+#define CCSR_SSI_SOR                   0x34
+#define CCSR_SSI_SACNT                 0x38
+#define CCSR_SSI_SACADD                        0x3c
+#define CCSR_SSI_SACDAT                        0x40
+#define CCSR_SSI_SATAG                 0x44
+#define CCSR_SSI_STMSK                 0x48
+#define CCSR_SSI_SRMSK                 0x4c
+#define CCSR_SSI_SACCST                        0x50
+#define CCSR_SSI_SACCEN                        0x54
+#define CCSR_SSI_SACCDIS               0x58
 
+#define CCSR_SSI_SCR_SYNC_TX_FS                0x00001000
 #define CCSR_SSI_SCR_RFR_CLK_DIS       0x00000800
 #define CCSR_SSI_SCR_TFR_CLK_DIS       0x00000400
 #define CCSR_SSI_SCR_TCH_EN            0x00000100
@@ -206,5 +205,64 @@ struct ccsr_ssi {
 #define CCSR_SSI_SACNT_FV              0x00000002
 #define CCSR_SSI_SACNT_AC97EN          0x00000001
 
-#endif
 
+struct device;
+
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+
+struct fsl_ssi_dbg {
+       struct dentry *dbg_dir;
+       struct dentry *dbg_stats;
+
+       struct {
+               unsigned int rfrc;
+               unsigned int tfrc;
+               unsigned int cmdau;
+               unsigned int cmddu;
+               unsigned int rxt;
+               unsigned int rdr1;
+               unsigned int rdr0;
+               unsigned int tde1;
+               unsigned int tde0;
+               unsigned int roe1;
+               unsigned int roe0;
+               unsigned int tue1;
+               unsigned int tue0;
+               unsigned int tfs;
+               unsigned int rfs;
+               unsigned int tls;
+               unsigned int rls;
+               unsigned int rff1;
+               unsigned int rff0;
+               unsigned int tfe1;
+               unsigned int tfe0;
+       } stats;
+};
+
+void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *ssi_dbg, u32 sisr);
+
+int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev);
+
+void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg);
+
+#else
+
+struct fsl_ssi_dbg {
+};
+
+static inline void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *stats, u32 sisr)
+{
+}
+
+static inline int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg,
+               struct device *dev)
+{
+       return 0;
+}
+
+static inline void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg)
+{
+}
+#endif  /* ! IS_ENABLED(CONFIG_DEBUG_FS) */
+
+#endif
diff --git a/sound/soc/fsl/fsl_ssi_dbg.c b/sound/soc/fsl/fsl_ssi_dbg.c
new file mode 100644 (file)
index 0000000..5469ffb
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Freescale SSI ALSA SoC Digital Audio Interface (DAI) debugging functions
+ *
+ * Copyright 2014 Markus Pargmann <mpa@pengutronix.de>, Pengutronix
+ *
+ * Splitted from fsl_ssi.c
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+#include "fsl_ssi.h"
+
+void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *dbg, u32 sisr)
+{
+       if (sisr & CCSR_SSI_SISR_RFRC)
+               dbg->stats.rfrc++;
+
+       if (sisr & CCSR_SSI_SISR_TFRC)
+               dbg->stats.tfrc++;
+
+       if (sisr & CCSR_SSI_SISR_CMDAU)
+               dbg->stats.cmdau++;
+
+       if (sisr & CCSR_SSI_SISR_CMDDU)
+               dbg->stats.cmddu++;
+
+       if (sisr & CCSR_SSI_SISR_RXT)
+               dbg->stats.rxt++;
+
+       if (sisr & CCSR_SSI_SISR_RDR1)
+               dbg->stats.rdr1++;
+
+       if (sisr & CCSR_SSI_SISR_RDR0)
+               dbg->stats.rdr0++;
+
+       if (sisr & CCSR_SSI_SISR_TDE1)
+               dbg->stats.tde1++;
+
+       if (sisr & CCSR_SSI_SISR_TDE0)
+               dbg->stats.tde0++;
+
+       if (sisr & CCSR_SSI_SISR_ROE1)
+               dbg->stats.roe1++;
+
+       if (sisr & CCSR_SSI_SISR_ROE0)
+               dbg->stats.roe0++;
+
+       if (sisr & CCSR_SSI_SISR_TUE1)
+               dbg->stats.tue1++;
+
+       if (sisr & CCSR_SSI_SISR_TUE0)
+               dbg->stats.tue0++;
+
+       if (sisr & CCSR_SSI_SISR_TFS)
+               dbg->stats.tfs++;
+
+       if (sisr & CCSR_SSI_SISR_RFS)
+               dbg->stats.rfs++;
+
+       if (sisr & CCSR_SSI_SISR_TLS)
+               dbg->stats.tls++;
+
+       if (sisr & CCSR_SSI_SISR_RLS)
+               dbg->stats.rls++;
+
+       if (sisr & CCSR_SSI_SISR_RFF1)
+               dbg->stats.rff1++;
+
+       if (sisr & CCSR_SSI_SISR_RFF0)
+               dbg->stats.rff0++;
+
+       if (sisr & CCSR_SSI_SISR_TFE1)
+               dbg->stats.tfe1++;
+
+       if (sisr & CCSR_SSI_SISR_TFE0)
+               dbg->stats.tfe0++;
+}
+
+/* Show the statistics of a flag only if its interrupt is enabled.  The
+ * compiler will optimze this code to a no-op if the interrupt is not
+ * enabled.
+ */
+#define SIER_SHOW(flag, name) \
+       do { \
+               if (CCSR_SSI_SIER_##flag) \
+                       seq_printf(s, #name "=%u\n", ssi_dbg->stats.name); \
+       } while (0)
+
+
+/**
+ * fsl_sysfs_ssi_show: display SSI statistics
+ *
+ * Display the statistics for the current SSI device.  To avoid confusion,
+ * we only show those counts that are enabled.
+ */
+static int fsl_ssi_stats_show(struct seq_file *s, void *unused)
+{
+       struct fsl_ssi_dbg *ssi_dbg = s->private;
+
+       SIER_SHOW(RFRC_EN, rfrc);
+       SIER_SHOW(TFRC_EN, tfrc);
+       SIER_SHOW(CMDAU_EN, cmdau);
+       SIER_SHOW(CMDDU_EN, cmddu);
+       SIER_SHOW(RXT_EN, rxt);
+       SIER_SHOW(RDR1_EN, rdr1);
+       SIER_SHOW(RDR0_EN, rdr0);
+       SIER_SHOW(TDE1_EN, tde1);
+       SIER_SHOW(TDE0_EN, tde0);
+       SIER_SHOW(ROE1_EN, roe1);
+       SIER_SHOW(ROE0_EN, roe0);
+       SIER_SHOW(TUE1_EN, tue1);
+       SIER_SHOW(TUE0_EN, tue0);
+       SIER_SHOW(TFS_EN, tfs);
+       SIER_SHOW(RFS_EN, rfs);
+       SIER_SHOW(TLS_EN, tls);
+       SIER_SHOW(RLS_EN, rls);
+       SIER_SHOW(RFF1_EN, rff1);
+       SIER_SHOW(RFF0_EN, rff0);
+       SIER_SHOW(TFE1_EN, tfe1);
+       SIER_SHOW(TFE0_EN, tfe0);
+
+       return 0;
+}
+
+static int fsl_ssi_stats_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, fsl_ssi_stats_show, inode->i_private);
+}
+
+static const struct file_operations fsl_ssi_stats_ops = {
+       .open = fsl_ssi_stats_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev)
+{
+       ssi_dbg->dbg_dir = debugfs_create_dir(dev_name(dev), NULL);
+       if (!ssi_dbg->dbg_dir)
+               return -ENOMEM;
+
+       ssi_dbg->dbg_stats = debugfs_create_file("stats", S_IRUGO,
+                       ssi_dbg->dbg_dir, ssi_dbg, &fsl_ssi_stats_ops);
+       if (!ssi_dbg->dbg_stats) {
+               debugfs_remove(ssi_dbg->dbg_dir);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg)
+{
+       debugfs_remove(ssi_dbg->dbg_stats);
+       debugfs_remove(ssi_dbg->dbg_dir);
+}
index 2585ae4..0849b7b 100644 (file)
@@ -40,7 +40,6 @@ static const struct snd_pcm_hardware imx_pcm_hardware = {
                SNDRV_PCM_INFO_MMAP_VALID |
                SNDRV_PCM_INFO_PAUSE |
                SNDRV_PCM_INFO_RESUME,
-       .formats = SNDRV_PCM_FMTBIT_S16_LE,
        .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
        .period_bytes_min = 128,
        .period_bytes_max = 65535, /* Limited by SDMA engine */
index 21f1ccb..03a7fdc 100644 (file)
@@ -24,9 +24,32 @@ struct simple_card_data {
                struct asoc_simple_dai cpu_dai;
                struct asoc_simple_dai codec_dai;
        } *dai_props;
+       unsigned int mclk_fs;
        struct snd_soc_dai_link dai_link[];     /* dynamically allocated */
 };
 
+static int asoc_simple_card_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->codec_dai;
+       struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
+       unsigned int mclk;
+       int ret = 0;
+
+       if (priv->mclk_fs) {
+               mclk = params_rate(params) * priv->mclk_fs;
+               ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+                                            SND_SOC_CLOCK_IN);
+       }
+
+       return ret;
+}
+
+static struct snd_soc_ops asoc_simple_card_ops = {
+       .hw_params = asoc_simple_card_hw_params,
+};
+
 static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
                                       struct asoc_simple_dai *set)
 {
@@ -66,8 +89,7 @@ err:
 
 static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct simple_card_data *priv =
-                               snd_soc_card_get_drvdata(rtd->card);
+       struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *codec = rtd->codec_dai;
        struct snd_soc_dai *cpu = rtd->cpu_dai;
        struct simple_dai_props *dai_props;
@@ -88,7 +110,6 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
 
 static int
 asoc_simple_card_sub_parse_of(struct device_node *np,
-                             unsigned int daifmt,
                              struct asoc_simple_dai *dai,
                              const struct device_node **p_node,
                              const char **name)
@@ -116,14 +137,6 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
        if (ret)
                return ret;
 
-       /*
-        * bitclock-inversion, frame-inversion
-        * bitclock-master,    frame-master
-        * and specific "format" if it has
-        */
-       dai->fmt = snd_soc_of_parse_daifmt(np, NULL);
-       dai->fmt |= daifmt;
-
        /*
         * dai->sysclk come from
         *  "clocks = <&xxx>" (if system has common clock)
@@ -151,37 +164,135 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
        return 0;
 }
 
-static int simple_card_cpu_codec_of(struct device_node *node,
-                               int daifmt,
-                               struct snd_soc_dai_link *dai_link,
-                               struct simple_dai_props *dai_props)
+static int simple_card_dai_link_of(struct device_node *node,
+                                  struct device *dev,
+                                  struct snd_soc_dai_link *dai_link,
+                                  struct simple_dai_props *dai_props,
+                                  bool is_top_level_node)
 {
-       struct device_node *np;
+       struct device_node *np = NULL;
+       struct device_node *bitclkmaster = NULL;
+       struct device_node *framemaster = NULL;
+       unsigned int daifmt;
+       char *name;
+       char prop[128];
+       char *prefix = "";
        int ret;
 
-       /* CPU sub-node */
-       ret = -EINVAL;
-       np = of_get_child_by_name(node, "simple-audio-card,cpu");
-       if (np) {
-               ret = asoc_simple_card_sub_parse_of(np, daifmt,
-                                               &dai_props->cpu_dai,
-                                               &dai_link->cpu_of_node,
-                                               &dai_link->cpu_dai_name);
-               of_node_put(np);
+       if (is_top_level_node)
+               prefix = "simple-audio-card,";
+
+       daifmt = snd_soc_of_parse_daifmt(node, prefix,
+                                        &bitclkmaster, &framemaster);
+       daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+
+       snprintf(prop, sizeof(prop), "%scpu", prefix);
+       np = of_get_child_by_name(node, prop);
+       if (!np) {
+               ret = -EINVAL;
+               dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
+               goto dai_link_of_err;
+       }
+
+       ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai,
+                                           &dai_link->cpu_of_node,
+                                           &dai_link->cpu_dai_name);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       dai_props->cpu_dai.fmt = daifmt;
+       switch (((np == bitclkmaster) << 4) | (np == framemaster)) {
+       case 0x11:
+               dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS;
+               break;
+       case 0x10:
+               dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM;
+               break;
+       case 0x01:
+               dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS;
+               break;
+       default:
+               dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM;
+               break;
+       }
+
+       of_node_put(np);
+       snprintf(prop, sizeof(prop), "%scodec", prefix);
+       np = of_get_child_by_name(node, prop);
+       if (!np) {
+               ret = -EINVAL;
+               dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
+               goto dai_link_of_err;
        }
+
+       ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai,
+                                           &dai_link->codec_of_node,
+                                           &dai_link->codec_dai_name);
        if (ret < 0)
-               return ret;
+               goto dai_link_of_err;
+
+       if (strlen(prefix) && !bitclkmaster && !framemaster) {
+               /* No dai-link level and master setting was not found from
+                  sound node level, revert back to legacy DT parsing and
+                  take the settings from codec node. */
+               dev_dbg(dev, "%s: Revert to legacy daifmt parsing\n",
+                       __func__);
+               dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt =
+                       snd_soc_of_parse_daifmt(np, NULL, NULL, NULL) |
+                       (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
+       } else {
+               dai_props->codec_dai.fmt = daifmt;
+               switch (((np == bitclkmaster) << 4) | (np == framemaster)) {
+               case 0x11:
+                       dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM;
+                       break;
+               case 0x10:
+                       dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS;
+                       break;
+               case 0x01:
+                       dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM;
+                       break;
+               default:
+                       dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS;
+                       break;
+               }
+       }
 
-       /* CODEC sub-node */
-       ret = -EINVAL;
-       np = of_get_child_by_name(node, "simple-audio-card,codec");
-       if (np) {
-               ret = asoc_simple_card_sub_parse_of(np, daifmt,
-                                               &dai_props->codec_dai,
-                                               &dai_link->codec_of_node,
-                                               &dai_link->codec_dai_name);
-               of_node_put(np);
+       if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
+               ret = -EINVAL;
+               goto dai_link_of_err;
        }
+
+       /* simple-card assumes platform == cpu */
+       dai_link->platform_of_node = dai_link->cpu_of_node;
+
+       /* Link name is created from CPU/CODEC dai name */
+       name = devm_kzalloc(dev,
+                           strlen(dai_link->cpu_dai_name)   +
+                           strlen(dai_link->codec_dai_name) + 2,
+                           GFP_KERNEL);
+       sprintf(name, "%s-%s", dai_link->cpu_dai_name,
+                               dai_link->codec_dai_name);
+       dai_link->name = dai_link->stream_name = name;
+       dai_link->ops = &asoc_simple_card_ops;
+
+       dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
+       dev_dbg(dev, "\tcpu : %s / %04x / %d\n",
+               dai_link->cpu_dai_name,
+               dai_props->cpu_dai.fmt,
+               dai_props->cpu_dai.sysclk);
+       dev_dbg(dev, "\tcodec : %s / %04x / %d\n",
+               dai_link->codec_dai_name,
+               dai_props->codec_dai.fmt,
+               dai_props->codec_dai.sysclk);
+
+dai_link_of_err:
+       if (np)
+               of_node_put(np);
+       if (bitclkmaster)
+               of_node_put(bitclkmaster);
+       if (framemaster)
+               of_node_put(framemaster);
        return ret;
 }
 
@@ -192,18 +303,11 @@ static int asoc_simple_card_parse_of(struct device_node *node,
 {
        struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link;
        struct simple_dai_props *dai_props = priv->dai_props;
-       struct device_node *np;
-       char *name;
-       unsigned int daifmt;
        int ret;
 
        /* parsing the card name from DT */
        snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name");
 
-       /* get CPU/CODEC common format via simple-audio-card,format */
-       daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,") &
-               (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK);
-
        /* off-codec widgets */
        if (of_property_read_bool(node, "simple-audio-card,widgets")) {
                ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
@@ -220,71 +324,36 @@ static int asoc_simple_card_parse_of(struct device_node *node,
                        return ret;
        }
 
-       /* loop on the DAI links */
-       np = NULL;
-       for (;;) {
-               if (multi) {
-                       np = of_get_next_child(node, np);
-                       if (!np)
-                               break;
+       /* Factor to mclk, used in hw_params() */
+       of_property_read_u32(node, "simple-audio-card,mclk-fs",
+                            &priv->mclk_fs);
+
+       dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
+               priv->snd_card.name : "");
+
+       if (multi) {
+               struct device_node *np = NULL;
+               int i;
+               for (i = 0; (np = of_get_next_child(node, np)); i++) {
+                       dev_dbg(dev, "\tlink %d:\n", i);
+                       ret = simple_card_dai_link_of(np, dev, dai_link + i,
+                                                     dai_props + i, false);
+                       if (ret < 0) {
+                               of_node_put(np);
+                               return ret;
+                       }
                }
-
-               ret = simple_card_cpu_codec_of(multi ? np : node,
-                                       daifmt, dai_link, dai_props);
+       } else {
+               ret = simple_card_dai_link_of(node, dev, dai_link, dai_props,
+                                             true);
                if (ret < 0)
-                       goto err;
-
-               /*
-                * overwrite cpu_dai->fmt as its DAIFMT_MASTER bit is based on CODEC
-                * while the other bits should be identical unless buggy SW/HW design.
-                */
-               dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt;
-
-               if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
-                       ret = -EINVAL;
-                       goto err;
-               }
-
-               /* simple-card assumes platform == cpu */
-               dai_link->platform_of_node = dai_link->cpu_of_node;
-
-               name = devm_kzalloc(dev,
-                                   strlen(dai_link->cpu_dai_name)   +
-                                   strlen(dai_link->codec_dai_name) + 2,
-                                   GFP_KERNEL);
-               sprintf(name, "%s-%s", dai_link->cpu_dai_name,
-                                       dai_link->codec_dai_name);
-               dai_link->name = dai_link->stream_name = name;
-
-               if (!multi)
-                       break;
-
-               dai_link++;
-               dai_props++;
+                       return ret;
        }
 
-       /* card name is created from CPU/CODEC dai name */
-       dai_link = priv->snd_card.dai_link;
        if (!priv->snd_card.name)
-               priv->snd_card.name = dai_link->name;
-
-       dev_dbg(dev, "card-name : %s\n", priv->snd_card.name);
-       dev_dbg(dev, "platform : %04x\n", daifmt);
-       dai_props = priv->dai_props;
-       dev_dbg(dev, "cpu : %s / %04x / %d\n",
-               dai_link->cpu_dai_name,
-               dai_props->cpu_dai.fmt,
-               dai_props->cpu_dai.sysclk);
-       dev_dbg(dev, "codec : %s / %04x / %d\n",
-               dai_link->codec_dai_name,
-               dai_props->codec_dai.fmt,
-               dai_props->codec_dai.sysclk);
+               priv->snd_card.name = priv->snd_card.dai_link->name;
 
        return 0;
-
-err:
-       of_node_put(np);
-       return ret;
 }
 
 /* update the reference count of the devices nodes at end of probe */
@@ -378,10 +447,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
                        return -EINVAL;
                }
 
-               if (!cinfo->name        ||
-                   !cinfo->codec_dai.name      ||
-                   !cinfo->codec       ||
-                   !cinfo->platform    ||
+               if (!cinfo->name ||
+                   !cinfo->codec_dai.name ||
+                   !cinfo->codec ||
+                   !cinfo->platform ||
                    !cinfo->cpu_dai.name) {
                        dev_err(dev, "insufficient asoc_simple_card_info settings\n");
                        return -EINVAL;
@@ -425,11 +494,11 @@ MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
 
 static struct platform_driver asoc_simple_card = {
        .driver = {
-               .name   = "asoc-simple-card",
+               .name = "asoc-simple-card",
                .owner = THIS_MODULE,
                .of_match_table = asoc_simple_of_match,
        },
-       .probe          = asoc_simple_card_probe,
+       .probe = asoc_simple_card_probe,
 };
 
 module_platform_driver(asoc_simple_card);
index 3c81b38..c30fedb 100644 (file)
@@ -49,3 +49,12 @@ config SND_SOC_INTEL_BYT_RT5640_MACH
        help
          This adds audio driver for Intel Baytrail platform based boards
          with the RT5640 audio codec.
+
+config SND_SOC_INTEL_BYT_MAX98090_MACH
+       tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec"
+       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C
+       select SND_SOC_INTEL_BAYTRAIL
+       select SND_SOC_MAX98090
+       help
+         This adds audio driver for Intel Baytrail platform based boards
+         with the MAX98090 audio codec.
index edeb79a..4bfca79 100644 (file)
@@ -2,7 +2,7 @@
 snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
 snd-soc-sst-acpi-objs := sst-acpi.o
 
-snd-soc-sst-mfld-platform-objs := sst-mfld-platform.o
+snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o sst-mfld-platform-compress.o
 snd-soc-mfld-machine-objs := mfld_machine.o
 
 obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
@@ -23,6 +23,8 @@ obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
 # Machine support
 snd-soc-sst-haswell-objs := haswell.o
 snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
+snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
 
 obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
diff --git a/sound/soc/intel/byt-max98090.c b/sound/soc/intel/byt-max98090.c
new file mode 100644 (file)
index 0000000..5fc98c6
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Intel Baytrail SST MAX98090 machine driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../codecs/max98090.h"
+
+struct byt_max98090_private {
+       struct snd_soc_jack jack;
+};
+
+static const struct snd_soc_dapm_widget byt_max98090_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Int Mic", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_max98090_audio_map[] = {
+       {"IN34", NULL, "Headset Mic"},
+       {"IN34", NULL, "MICBIAS"},
+       {"MICBIAS", NULL, "Headset Mic"},
+       {"DMICL", NULL, "Int Mic"},
+       {"Headphone", NULL, "HPL"},
+       {"Headphone", NULL, "HPR"},
+       {"Ext Spk", NULL, "SPKL"},
+       {"Ext Spk", NULL, "SPKR"},
+};
+
+static const struct snd_kcontrol_new byt_max98090_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Headphone"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Int Mic"),
+       SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+       {
+               .pin    = "Headphone",
+               .mask   = SND_JACK_HEADPHONE,
+       },
+       {
+               .pin    = "Headset Mic",
+               .mask   = SND_JACK_MICROPHONE,
+       },
+       {
+               .pin    = "Ext Spk",
+               .mask   = SND_JACK_LINEOUT,
+       },
+       {
+               .pin    = "Int Mic",
+               .mask   = SND_JACK_LINEIN,
+       },
+};
+
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+       {
+               .name           = "hp-gpio",
+               .idx            = 0,
+               .report         = SND_JACK_HEADPHONE | SND_JACK_LINEOUT,
+               .debounce_time  = 200,
+       },
+       {
+               .name           = "mic-gpio",
+               .idx            = 1,
+               .report         = SND_JACK_MICROPHONE | SND_JACK_LINEIN,
+               .debounce_time  = 200,
+       },
+};
+
+static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime)
+{
+       int ret;
+       struct snd_soc_codec *codec = runtime->codec;
+       struct snd_soc_card *card = runtime->card;
+       struct byt_max98090_private *drv = snd_soc_card_get_drvdata(card);
+       struct snd_soc_jack *jack = &drv->jack;
+
+       card->dapm.idle_bias_off = true;
+
+       ret = snd_soc_dai_set_sysclk(runtime->codec_dai,
+                                    M98090_REG_SYSTEM_CLOCK,
+                                    25000000, SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               dev_err(card->dev, "Can't set codec clock %d\n", ret);
+               return ret;
+       }
+
+       /* Enable jack detection */
+       ret = snd_soc_jack_new(codec, "Headphone", SND_JACK_HEADPHONE, jack);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_jack_add_pins(jack, ARRAY_SIZE(hs_jack_pins),
+                                   hs_jack_pins);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_jack_add_gpiods(card->dev->parent, jack,
+                                     ARRAY_SIZE(hs_jack_gpios),
+                                     hs_jack_gpios);
+       if (ret)
+               return ret;
+
+       return max98090_mic_detect(codec, jack);
+}
+
+static struct snd_soc_dai_link byt_max98090_dais[] = {
+       {
+               .name = "Baytrail Audio",
+               .stream_name = "Audio",
+               .cpu_dai_name = "baytrail-pcm-audio",
+               .codec_dai_name = "HiFi",
+               .codec_name = "i2c-193C9890:00",
+               .platform_name = "baytrail-pcm-audio",
+               .init = byt_max98090_init,
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+                          SND_SOC_DAIFMT_CBS_CFS,
+       },
+};
+
+static struct snd_soc_card byt_max98090_card = {
+       .name = "byt-max98090",
+       .dai_link = byt_max98090_dais,
+       .num_links = ARRAY_SIZE(byt_max98090_dais),
+       .dapm_widgets = byt_max98090_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(byt_max98090_widgets),
+       .dapm_routes = byt_max98090_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(byt_max98090_audio_map),
+       .controls = byt_max98090_controls,
+       .num_controls = ARRAY_SIZE(byt_max98090_controls),
+};
+
+static int byt_max98090_probe(struct platform_device *pdev)
+{
+       int ret_val = 0;
+       struct byt_max98090_private *priv;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
+       if (!priv) {
+               dev_err(&pdev->dev, "allocation failed\n");
+               return -ENOMEM;
+       }
+
+       byt_max98090_card.dev = &pdev->dev;
+       snd_soc_card_set_drvdata(&byt_max98090_card, priv);
+       ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_max98090_card);
+       if (ret_val) {
+               dev_err(&pdev->dev,
+                       "snd_soc_register_card failed %d\n", ret_val);
+               return ret_val;
+       }
+
+       return ret_val;
+}
+
+static int byt_max98090_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct byt_max98090_private *priv = snd_soc_card_get_drvdata(card);
+
+       snd_soc_jack_free_gpios(&priv->jack, ARRAY_SIZE(hs_jack_gpios),
+                               hs_jack_gpios);
+
+       return 0;
+}
+
+static struct platform_driver byt_max98090_driver = {
+       .probe = byt_max98090_probe,
+       .remove = byt_max98090_remove,
+       .driver = {
+               .name = "byt-max98090",
+               .owner = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+       },
+};
+module_platform_driver(byt_max98090_driver)
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver");
+MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:byt-max98090");
index eff97c8..53d160d 100644 (file)
@@ -100,12 +100,6 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
        snd_soc_dapm_ignore_suspend(dapm, "SPORP");
        snd_soc_dapm_ignore_suspend(dapm, "SPORN");
 
-       snd_soc_dapm_enable_pin(dapm, "Headset Mic");
-       snd_soc_dapm_enable_pin(dapm, "Headphone");
-       snd_soc_dapm_enable_pin(dapm, "Speaker");
-       snd_soc_dapm_enable_pin(dapm, "Internal Mic");
-
-       snd_soc_dapm_sync(dapm);
        return ret;
 }
 
@@ -117,27 +111,13 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
        {
                .name = "Baytrail Audio",
                .stream_name = "Audio",
-               .cpu_dai_name = "Front-cpu-dai",
+               .cpu_dai_name = "baytrail-pcm-audio",
                .codec_dai_name = "rt5640-aif1",
                .codec_name = "i2c-10EC5640:00",
                .platform_name = "baytrail-pcm-audio",
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
                           SND_SOC_DAIFMT_CBS_CFS,
                .init = byt_rt5640_init,
-               .ignore_suspend = 1,
-               .ops = &byt_rt5640_ops,
-       },
-       {
-               .name = "Baytrail Voice",
-               .stream_name = "Voice",
-               .cpu_dai_name = "Mic1-cpu-dai",
-               .codec_dai_name = "rt5640-aif1",
-               .codec_name = "i2c-10EC5640:00",
-               .platform_name = "baytrail-pcm-audio",
-               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
-                          SND_SOC_DAIFMT_CBS_CFS,
-               .init = NULL,
-               .ignore_suspend = 1,
                .ops = &byt_rt5640_ops,
        },
 };
@@ -155,28 +135,17 @@ static struct snd_soc_card byt_rt5640_card = {
 static int byt_rt5640_probe(struct platform_device *pdev)
 {
        struct snd_soc_card *card = &byt_rt5640_card;
-       struct device *dev = &pdev->dev;
 
        card->dev = &pdev->dev;
-       dev_set_drvdata(dev, card);
-       return snd_soc_register_card(card);
-}
-
-static int byt_rt5640_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, card);
 }
 
 static struct platform_driver byt_rt5640_audio = {
        .probe = byt_rt5640_probe,
-       .remove = byt_rt5640_remove,
        .driver = {
                .name = "byt-rt5640",
                .owner = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
        },
 };
 module_platform_driver(byt_rt5640_audio)
index 54345a2..3981982 100644 (file)
@@ -89,8 +89,6 @@ static struct snd_soc_ops haswell_rt5640_ops = {
 
 static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
        struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
        struct sst_hsw *haswell = pdata->dsp;
        int ret;
@@ -104,10 +102,6 @@ static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd)
                return ret;
        }
 
-       /* always connected */
-       snd_soc_dapm_enable_pin(dapm, "Headphones");
-       snd_soc_dapm_enable_pin(dapm, "Mic");
-
        return 0;
 }
 
@@ -208,18 +202,11 @@ static int haswell_audio_probe(struct platform_device *pdev)
 {
        haswell_rt5640.dev = &pdev->dev;
 
-       return snd_soc_register_card(&haswell_rt5640);
-}
-
-static int haswell_audio_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_card(&haswell_rt5640);
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, &haswell_rt5640);
 }
 
 static struct platform_driver haswell_audio = {
        .probe = haswell_audio_probe,
-       .remove = haswell_audio_remove,
        .driver = {
                .name = "haswell-audio",
                .owner = THIS_MODULE,
index 18aee77..42edc6f 100644 (file)
@@ -247,6 +247,7 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = {
 
 static struct sst_acpi_mach baytrail_machines[] = {
        { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-i2s_master" },
+       { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-i2s_master" },
        {}
 };
 
index adf0aca..fc58876 100644 (file)
@@ -214,6 +214,13 @@ static void sst_byt_boot(struct sst_dsp *sst)
 {
        int tries = 10;
 
+       /*
+        * save the physical address of extended firmware block in the first
+        * 4 bytes of the mailbox
+        */
+       memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET,
+              &sst->pdata->fw_base, sizeof(u32));
+
        /* release stall and wait to unstall */
        sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_STALL, 0x0);
        while (tries--) {
@@ -317,13 +324,6 @@ static int sst_byt_init(struct sst_dsp *sst, struct sst_pdata *pdata)
                return ret;
        }
 
-       /*
-        * save the physical address of extended firmware block in the first
-        * 4 bytes of the mailbox
-        */
-       memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET,
-              &pdata->fw_base, sizeof(u32));
-
        ret = dma_coerce_mask_and_coherent(sst->dma_dev, DMA_BIT_MASK(32));
        if (ret)
                return ret;
index 0d31dbb..d207b22 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
-#include <linux/list.h>
 #include <linux/platform_device.h>
 #include <linux/kthread.h>
 #include <linux/firmware.h>
@@ -173,6 +172,7 @@ struct sst_byt {
        /* boot */
        wait_queue_head_t boot_wait;
        bool boot_complete;
+       struct sst_fw *fw;
 
        /* IPC messaging */
        struct list_head tx_list;
@@ -299,6 +299,24 @@ static inline void sst_byt_tx_msg_reply_complete(struct sst_byt *byt,
                wake_up(&msg->waitq);
 }
 
+static void sst_byt_drop_all(struct sst_byt *byt)
+{
+       struct ipc_message *msg, *tmp;
+       unsigned long flags;
+
+       /* drop all TX and Rx messages before we stall + reset DSP */
+       spin_lock_irqsave(&byt->dsp->spinlock, flags);
+       list_for_each_entry_safe(msg, tmp, &byt->tx_list, list) {
+               list_move(&msg->list, &byt->empty_list);
+       }
+
+       list_for_each_entry_safe(msg, tmp, &byt->rx_list, list) {
+               list_move(&msg->list, &byt->empty_list);
+       }
+
+       spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
+}
+
 static int sst_byt_tx_wait_done(struct sst_byt *byt, struct ipc_message *msg,
                                void *rx_data)
 {
@@ -661,36 +679,33 @@ out:
 static int sst_byt_stream_operations(struct sst_byt *byt, int type,
                                     int stream_id, int wait)
 {
-       struct sst_byt_start_stream_params start_stream;
        u64 header;
-       void *tx_msg = NULL;
-       size_t size = 0;
-
-       if (type != IPC_IA_START_STREAM) {
-               header = sst_byt_header(type, 0, false, stream_id);
-       } else {
-               start_stream.byte_offset = 0;
-               header = sst_byt_header(IPC_IA_START_STREAM,
-                                       sizeof(start_stream) + sizeof(u32),
-                                       true, stream_id);
-               tx_msg = &start_stream;
-               size = sizeof(start_stream);
-       }
 
+       header = sst_byt_header(type, 0, false, stream_id);
        if (wait)
-               return sst_byt_ipc_tx_msg_wait(byt, header,
-                                              tx_msg, size, NULL, 0);
+               return sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0);
        else
-               return sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size);
+               return sst_byt_ipc_tx_msg_nowait(byt, header, NULL, 0);
 }
 
 /* stream ALSA trigger operations */
-int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream)
+int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
+                        u32 start_offset)
 {
+       struct sst_byt_start_stream_params start_stream;
+       void *tx_msg;
+       size_t size;
+       u64 header;
        int ret;
 
-       ret = sst_byt_stream_operations(byt, IPC_IA_START_STREAM,
-                                       stream->str_id, 0);
+       start_stream.byte_offset = start_offset;
+       header = sst_byt_header(IPC_IA_START_STREAM,
+                               sizeof(start_stream) + sizeof(u32),
+                               true, stream->str_id);
+       tx_msg = &start_stream;
+       size = sizeof(start_stream);
+
+       ret = sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size);
        if (ret < 0)
                dev_err(byt->dev, "ipc: error failed to start stream %d\n",
                        stream->str_id);
@@ -782,6 +797,73 @@ static struct sst_dsp_device byt_dev = {
        .ops = &sst_byt_ops,
 };
 
+int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata)
+{
+       struct sst_byt *byt = pdata->dsp;
+
+       dev_dbg(byt->dev, "dsp reset\n");
+       sst_dsp_reset(byt->dsp);
+       sst_byt_drop_all(byt);
+       dev_dbg(byt->dev, "dsp in reset\n");
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_noirq);
+
+int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata)
+{
+       struct sst_byt *byt = pdata->dsp;
+
+       dev_dbg(byt->dev, "free all blocks and unload fw\n");
+       sst_fw_unload(byt->fw);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_late);
+
+int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata)
+{
+       struct sst_byt *byt = pdata->dsp;
+       int ret;
+
+       dev_dbg(byt->dev, "reload dsp fw\n");
+
+       sst_dsp_reset(byt->dsp);
+
+       ret = sst_fw_reload(byt->fw);
+       if (ret <  0) {
+               dev_err(dev, "error: failed to reload firmware\n");
+               return ret;
+       }
+
+       /* wait for DSP boot completion */
+       byt->boot_complete = false;
+       sst_dsp_boot(byt->dsp);
+       dev_dbg(byt->dev, "dsp booting...\n");
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_boot);
+
+int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata)
+{
+       struct sst_byt *byt = pdata->dsp;
+       int err;
+
+       dev_dbg(byt->dev, "wait for dsp reboot\n");
+
+       err = wait_event_timeout(byt->boot_wait, byt->boot_complete,
+                                msecs_to_jiffies(IPC_BOOT_MSECS));
+       if (err == 0) {
+               dev_err(byt->dev, "ipc: error DSP boot timeout\n");
+               return -EIO;
+       }
+
+       dev_dbg(byt->dev, "dsp rebooted\n");
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready);
+
 int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
 {
        struct sst_byt *byt;
@@ -809,7 +891,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
        /* start the IPC message thread */
        init_kthread_worker(&byt->kworker);
        byt->tx_thread = kthread_run(kthread_worker_fn,
-                                    &byt->kworker,
+                                    &byt->kworker, "%s",
                                     dev_name(byt->dev));
        if (IS_ERR(byt->tx_thread)) {
                err = PTR_ERR(byt->tx_thread);
@@ -824,7 +906,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
        byt->dsp = sst_dsp_new(dev, &byt_dev, pdata);
        if (byt->dsp == NULL) {
                err = -ENODEV;
-               goto err_free_msg;
+               goto dsp_err;
        }
 
        /* keep the DSP in reset state for base FW loading */
@@ -848,6 +930,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
        }
 
        pdata->dsp = byt;
+       byt->fw = byt_sst_fw;
 
        return 0;
 
@@ -856,6 +939,8 @@ boot_err:
        sst_fw_free(byt_sst_fw);
 fw_err:
        sst_dsp_free(byt->dsp);
+dsp_err:
+       kthread_stop(byt->tx_thread);
 err_free_msg:
        kfree(byt->msg);
 
@@ -870,6 +955,7 @@ void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata)
        sst_dsp_reset(byt->dsp);
        sst_fw_free_all(byt->dsp);
        sst_dsp_free(byt->dsp);
+       kthread_stop(byt->tx_thread);
        kfree(byt->msg);
 }
 EXPORT_SYMBOL_GPL(sst_byt_dsp_free);
index f172b64..06a4d20 100644 (file)
@@ -53,7 +53,8 @@ int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream);
 int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream);
 
 /* stream ALSA trigger operations */
-int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream);
+int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
+                        u32 start_offset);
 int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream);
 int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream);
 int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream);
@@ -65,5 +66,9 @@ int sst_byt_get_dsp_position(struct sst_byt *byt,
 int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata);
 void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata);
 struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt);
+int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata);
+int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata);
+int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata);
+int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata);
 
 #endif
index 6d101f3..8eab973 100644 (file)
@@ -45,6 +45,11 @@ struct sst_byt_pcm_data {
        struct sst_byt_stream *stream;
        struct snd_pcm_substream *substream;
        struct mutex mutex;
+
+       /* latest DSP DMA hw pointer */
+       u32 hw_ptr;
+
+       struct work_struct work;
 };
 
 /* private data for the driver */
@@ -63,7 +68,7 @@ static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct sst_byt_priv_data *pdata =
                snd_soc_platform_get_drvdata(rtd->platform);
-       struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+       struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
        struct sst_byt *byt = pdata->byt;
        u32 rate, bits;
        u8 channels;
@@ -130,21 +135,57 @@ static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
+static int sst_byt_pcm_restore_stream_context(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct sst_byt_priv_data *pdata =
+               snd_soc_platform_get_drvdata(rtd->platform);
+       struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
+       struct sst_byt *byt = pdata->byt;
+       int ret;
+
+       /* commit stream using existing stream params */
+       ret = sst_byt_stream_commit(byt, pcm_data->stream);
+       if (ret < 0) {
+               dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret);
+               return ret;
+       }
+
+       sst_byt_stream_start(byt, pcm_data->stream, pcm_data->hw_ptr);
+
+       dev_dbg(rtd->dev, "stream context restored at offset %d\n",
+               pcm_data->hw_ptr);
+
+       return 0;
+}
+
+static void sst_byt_pcm_work(struct work_struct *work)
+{
+       struct sst_byt_pcm_data *pcm_data =
+               container_of(work, struct sst_byt_pcm_data, work);
+
+       if (snd_pcm_running(pcm_data->substream))
+               sst_byt_pcm_restore_stream_context(pcm_data->substream);
+}
+
 static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct sst_byt_priv_data *pdata =
                snd_soc_platform_get_drvdata(rtd->platform);
-       struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+       struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
        struct sst_byt *byt = pdata->byt;
 
        dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd);
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-               sst_byt_stream_start(byt, pcm_data->stream);
+               pcm_data->hw_ptr = 0;
+               sst_byt_stream_start(byt, pcm_data->stream, 0);
                break;
        case SNDRV_PCM_TRIGGER_RESUME:
+               schedule_work(&pcm_data->work);
+               break;
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                sst_byt_stream_resume(byt, pcm_data->stream);
                break;
@@ -168,13 +209,19 @@ static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data)
        struct snd_pcm_substream *substream = pcm_data->substream;
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       u32 pos;
+       struct sst_byt_priv_data *pdata =
+               snd_soc_platform_get_drvdata(rtd->platform);
+       struct sst_byt *byt = pdata->byt;
+       u32 pos, hw_pos;
 
+       hw_pos = sst_byt_get_dsp_position(byt, pcm_data->stream,
+                                         snd_pcm_lib_buffer_bytes(substream));
+       pcm_data->hw_ptr = hw_pos;
        pos = frames_to_bytes(runtime,
                              (runtime->control->appl_ptr %
                               runtime->buffer_size));
 
-       dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
+       dev_dbg(rtd->dev, "PCM: App/DMA pointer %u/%u bytes\n", pos, hw_pos);
 
        snd_pcm_period_elapsed(substream);
        return pos;
@@ -186,18 +233,11 @@ static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct sst_byt_priv_data *pdata =
                snd_soc_platform_get_drvdata(rtd->platform);
-       struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
-       struct sst_byt *byt = pdata->byt;
-       snd_pcm_uframes_t offset;
-       int pos;
+       struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
 
-       pos = sst_byt_get_dsp_position(byt, pcm_data->stream,
-                                      snd_pcm_lib_buffer_bytes(substream));
-       offset = bytes_to_frames(runtime, pos);
+       dev_dbg(rtd->dev, "PCM: DMA pointer %u bytes\n", pcm_data->hw_ptr);
 
-       dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n",
-               frames_to_bytes(runtime, (u32)offset));
-       return offset;
+       return bytes_to_frames(runtime, pcm_data->hw_ptr);
 }
 
 static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
@@ -205,20 +245,18 @@ static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct sst_byt_priv_data *pdata =
                snd_soc_platform_get_drvdata(rtd->platform);
-       struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+       struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
        struct sst_byt *byt = pdata->byt;
 
        dev_dbg(rtd->dev, "PCM: open\n");
 
-       pcm_data = &pdata->pcm[rtd->cpu_dai->id];
        mutex_lock(&pcm_data->mutex);
 
-       snd_soc_pcm_set_drvdata(rtd, pcm_data);
        pcm_data->substream = substream;
 
        snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware);
 
-       pcm_data->stream = sst_byt_stream_new(byt, rtd->cpu_dai->id + 1,
+       pcm_data->stream = sst_byt_stream_new(byt, substream->stream + 1,
                                              byt_notify_pointer, pcm_data);
        if (pcm_data->stream == NULL) {
                dev_err(rtd->dev, "failed to create stream\n");
@@ -235,12 +273,13 @@ static int sst_byt_pcm_close(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct sst_byt_priv_data *pdata =
                snd_soc_platform_get_drvdata(rtd->platform);
-       struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
+       struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
        struct sst_byt *byt = pdata->byt;
        int ret;
 
        dev_dbg(rtd->dev, "PCM: close\n");
 
+       cancel_work_sync(&pcm_data->work);
        mutex_lock(&pcm_data->mutex);
        ret = sst_byt_stream_free(byt, pcm_data->stream);
        if (ret < 0) {
@@ -283,18 +322,16 @@ static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_pcm *pcm = rtd->pcm;
        size_t size;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct sst_pdata *pdata = dev_get_platdata(platform->dev);
        int ret = 0;
 
-       ret = dma_coerce_mask_and_coherent(rtd->card->dev, DMA_BIT_MASK(32));
-       if (ret)
-               return ret;
-
        if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
            pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
                size = sst_byt_pcm_hardware.buffer_bytes_max;
                ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
                                                            SNDRV_DMA_TYPE_DEV,
-                                                           rtd->card->dev,
+                                                           pdata->dma_dev,
                                                            size, size);
                if (ret) {
                        dev_err(rtd->dev, "dma buffer allocation failed %d\n",
@@ -308,7 +345,7 @@ static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
 
 static struct snd_soc_dai_driver byt_dais[] = {
        {
-               .name  = "Front-cpu-dai",
+               .name  = "Baytrail PCM",
                .playback = {
                        .stream_name = "System Playback",
                        .channels_min = 2,
@@ -317,9 +354,6 @@ static struct snd_soc_dai_driver byt_dais[] = {
                        .formats = SNDRV_PCM_FMTBIT_S24_3LE |
                                   SNDRV_PCM_FMTBIT_S16_LE,
                },
-       },
-       {
-               .name  = "Mic1-cpu-dai",
                .capture = {
                        .stream_name = "Analog Capture",
                        .channels_min = 2,
@@ -344,8 +378,10 @@ static int sst_byt_pcm_probe(struct snd_soc_platform *platform)
        priv_data->byt = plat_data->dsp;
        snd_soc_platform_set_drvdata(platform, priv_data);
 
-       for (i = 0; i < ARRAY_SIZE(byt_dais); i++)
+       for (i = 0; i < BYT_PCM_COUNT; i++) {
                mutex_init(&priv_data->pcm[i].mutex);
+               INIT_WORK(&priv_data->pcm[i].work, sst_byt_pcm_work);
+       }
 
        return 0;
 }
@@ -367,6 +403,72 @@ static const struct snd_soc_component_driver byt_dai_component = {
        .name           = "byt-dai",
 };
 
+#ifdef CONFIG_PM
+static int sst_byt_pcm_dev_suspend_noirq(struct device *dev)
+{
+       struct sst_pdata *sst_pdata = dev_get_platdata(dev);
+       int ret;
+
+       dev_dbg(dev, "suspending noirq\n");
+
+       /* at this point all streams will be stopped and context saved */
+       ret = sst_byt_dsp_suspend_noirq(dev, sst_pdata);
+       if (ret < 0) {
+               dev_err(dev, "failed to suspend %d\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int sst_byt_pcm_dev_suspend_late(struct device *dev)
+{
+       struct sst_pdata *sst_pdata = dev_get_platdata(dev);
+       int ret;
+
+       dev_dbg(dev, "suspending late\n");
+
+       ret = sst_byt_dsp_suspend_late(dev, sst_pdata);
+       if (ret < 0) {
+               dev_err(dev, "failed to suspend %d\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int sst_byt_pcm_dev_resume_early(struct device *dev)
+{
+       struct sst_pdata *sst_pdata = dev_get_platdata(dev);
+
+       dev_dbg(dev, "resume early\n");
+
+       /* load fw and boot DSP */
+       return sst_byt_dsp_boot(dev, sst_pdata);
+}
+
+static int sst_byt_pcm_dev_resume(struct device *dev)
+{
+       struct sst_pdata *sst_pdata = dev_get_platdata(dev);
+
+       dev_dbg(dev, "resume\n");
+
+       /* wait for FW to finish booting */
+       return sst_byt_dsp_wait_for_ready(dev, sst_pdata);
+}
+
+static const struct dev_pm_ops sst_byt_pm_ops = {
+       .suspend_noirq = sst_byt_pcm_dev_suspend_noirq,
+       .suspend_late = sst_byt_pcm_dev_suspend_late,
+       .resume_early = sst_byt_pcm_dev_resume_early,
+       .resume = sst_byt_pcm_dev_resume,
+};
+
+#define SST_BYT_PM_OPS (&sst_byt_pm_ops)
+#else
+#define SST_BYT_PM_OPS NULL
+#endif
+
 static int sst_byt_pcm_dev_probe(struct platform_device *pdev)
 {
        struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
@@ -409,6 +511,7 @@ static struct platform_driver sst_byt_pcm_driver = {
        .driver = {
                .name = "baytrail-pcm-audio",
                .owner = THIS_MODULE,
+               .pm = SST_BYT_PM_OPS,
        },
 
        .probe = sst_byt_pcm_dev_probe,
index 4012134..ffb308b 100644 (file)
@@ -284,6 +284,8 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
        const struct firmware *fw, void *private);
 void sst_fw_free(struct sst_fw *sst_fw);
 void sst_fw_free_all(struct sst_dsp *dsp);
+int sst_fw_reload(struct sst_fw *sst_fw);
+void sst_fw_unload(struct sst_fw *sst_fw);
 
 /* Create/Free firmware modules */
 struct sst_module *sst_module_new(struct sst_fw *sst_fw,
index 928f228..3bb43da 100644 (file)
@@ -30,6 +30,8 @@
 #include "sst-dsp.h"
 #include "sst-dsp-priv.h"
 
+static void block_module_remove(struct sst_module *module);
+
 static void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
 {
        u32 i;
@@ -91,6 +93,42 @@ parse_err:
 }
 EXPORT_SYMBOL_GPL(sst_fw_new);
 
+int sst_fw_reload(struct sst_fw *sst_fw)
+{
+       struct sst_dsp *dsp = sst_fw->dsp;
+       int ret;
+
+       dev_dbg(dsp->dev, "reloading firmware\n");
+
+       /* call core specific FW paser to load FW data into DSP */
+       ret = dsp->ops->parse_fw(sst_fw);
+       if (ret < 0)
+               dev_err(dsp->dev, "error: parse fw failed %d\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sst_fw_reload);
+
+void sst_fw_unload(struct sst_fw *sst_fw)
+{
+        struct sst_dsp *dsp = sst_fw->dsp;
+        struct sst_module *module, *tmp;
+
+        dev_dbg(dsp->dev, "unloading firmware\n");
+
+        mutex_lock(&dsp->mutex);
+        list_for_each_entry_safe(module, tmp, &dsp->module_list, list) {
+                if (module->sst_fw == sst_fw) {
+                        block_module_remove(module);
+                        list_del(&module->list);
+                        kfree(module);
+                }
+        }
+
+        mutex_unlock(&dsp->mutex);
+}
+EXPORT_SYMBOL_GPL(sst_fw_unload);
+
 /* free single firmware object */
 void sst_fw_free(struct sst_fw *sst_fw)
 {
@@ -496,9 +534,7 @@ struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp)
 
        /* calculate required scratch size */
        list_for_each_entry(sst_module, &dsp->module_list, list) {
-               if (scratch->s.size > sst_module->s.size)
-                       scratch->s.size = scratch->s.size;
-               else
+               if (scratch->s.size < sst_module->s.size)
                        scratch->s.size = sst_module->s.size;
        }
 
index e7996b3..4342363 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/sched.h>
-#include <linux/list.h>
 #include <linux/platform_device.h>
 #include <linux/kthread.h>
 #include <linux/firmware.h>
@@ -1730,17 +1729,17 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 
        ret = msg_empty_list_init(hsw);
        if (ret < 0)
-               goto list_err;
+               return -ENOMEM;
 
        /* start the IPC message thread */
        init_kthread_worker(&hsw->kworker);
        hsw->tx_thread = kthread_run(kthread_worker_fn,
-                                          &hsw->kworker,
+                                          &hsw->kworker, "%s",
                                           dev_name(hsw->dev));
        if (IS_ERR(hsw->tx_thread)) {
                ret = PTR_ERR(hsw->tx_thread);
                dev_err(hsw->dev, "error: failed to create message TX task\n");
-               goto list_err;
+               goto err_free_msg;
        }
        init_kthread_work(&hsw->kwork, ipc_tx_msgs);
 
@@ -1750,7 +1749,7 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
        hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata);
        if (hsw->dsp == NULL) {
                ret = -ENODEV;
-               goto list_err;
+               goto dsp_err;
        }
 
        /* keep the DSP in reset state for base FW loading */
@@ -1794,8 +1793,11 @@ boot_err:
        sst_fw_free(hsw_sst_fw);
 fw_err:
        sst_dsp_free(hsw->dsp);
+dsp_err:
+       kthread_stop(hsw->tx_thread);
+err_free_msg:
        kfree(hsw->msg);
-list_err:
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(sst_hsw_dsp_init);
@@ -1808,6 +1810,7 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
        sst_fw_free_all(hsw->dsp);
        sst_dsp_free(hsw->dsp);
        kfree(hsw->scratch);
+       kthread_stop(hsw->tx_thread);
        kfree(hsw->msg);
 }
 EXPORT_SYMBOL_GPL(sst_hsw_dsp_free);
index 9d5f64a..058efb1 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/module.h>
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
-#include <linux/module.h>
 #include <linux/delay.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -139,7 +138,7 @@ static inline unsigned int hsw_ipc_to_mixer(u32 value)
 static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        struct hsw_priv_data *pdata =
@@ -177,7 +176,7 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
 static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        struct hsw_priv_data *pdata =
@@ -209,7 +208,7 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
 static int hsw_volume_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
        struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
@@ -234,7 +233,7 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol,
 static int hsw_volume_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
        struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
        struct sst_hsw *hsw = pdata->hsw;
        unsigned int volume = 0;
index 3b63edc..8d482d7 100644 (file)
  *  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.
- *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 
@@ -40,7 +36,6 @@ enum stream_type {
 };
 
 struct snd_pcm_params {
-       u16 codec;      /* codec type */
        u8 num_chan;    /* 1=Mono, 2=Stereo */
        u8 pcm_wd_sz;   /* 16/24 - bit*/
        u32 reserved;   /* Bitrate in bits per second */
@@ -53,7 +48,6 @@ struct snd_pcm_params {
 
 /* MP3 Music Parameters Message */
 struct snd_mp3_params {
-       u16 codec;
        u8  num_chan;   /* 1=Mono, 2=Stereo     */
        u8  pcm_wd_sz; /* 16/24 - bit*/
        u8  crc_check; /* crc_check - disable (0) or enable (1) */
@@ -67,7 +61,6 @@ struct snd_mp3_params {
 
 /* AAC Music Parameters Message */
 struct snd_aac_params {
-       u16 codec;
        u8 num_chan; /* 1=Mono, 2=Stereo*/
        u8 pcm_wd_sz; /* 16/24 - bit*/
        u8 bdownsample; /*SBR downsampling 0 - disable 1 -enabled AAC+ only */
@@ -81,7 +74,6 @@ struct snd_aac_params {
 
 /* WMA Music Parameters Message */
 struct snd_wma_params {
-       u16 codec;
        u8  num_chan;   /* 1=Mono, 2=Stereo */
        u8  pcm_wd_sz;  /* 16/24 - bit*/
        u32 brate;      /* Use the hard coded value. */
diff --git a/sound/soc/intel/sst-mfld-platform-compress.c b/sound/soc/intel/sst-mfld-platform-compress.c
new file mode 100644 (file)
index 0000000..02abd19
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ *  sst_mfld_platform.c - Intel MID Platform driver
+ *
+ *  Copyright (C) 2010-2014 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include "sst-mfld-platform.h"
+
+/* compress stream operations */
+static void sst_compr_fragment_elapsed(void *arg)
+{
+       struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
+
+       pr_debug("fragment elapsed by driver\n");
+       if (cstream)
+               snd_compr_fragment_elapsed(cstream);
+}
+
+static void sst_drain_notify(void *arg)
+{
+       struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
+
+       pr_debug("drain notify by driver\n");
+       if (cstream)
+               snd_compr_drain_notify(cstream);
+}
+
+static int sst_platform_compr_open(struct snd_compr_stream *cstream)
+{
+
+       int ret_val = 0;
+       struct snd_compr_runtime *runtime = cstream->runtime;
+       struct sst_runtime_stream *stream;
+
+       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+       if (!stream)
+               return -ENOMEM;
+
+       spin_lock_init(&stream->status_lock);
+
+       /* get the sst ops */
+       if (!sst || !try_module_get(sst->dev->driver->owner)) {
+               pr_err("no device available to run\n");
+               ret_val = -ENODEV;
+               goto out_ops;
+       }
+       stream->compr_ops = sst->compr_ops;
+
+       stream->id = 0;
+       sst_set_stream_status(stream, SST_PLATFORM_INIT);
+       runtime->private_data = stream;
+       return 0;
+out_ops:
+       kfree(stream);
+       return ret_val;
+}
+
+static int sst_platform_compr_free(struct snd_compr_stream *cstream)
+{
+       struct sst_runtime_stream *stream;
+       int ret_val = 0, str_id;
+
+       stream = cstream->runtime->private_data;
+       /*need to check*/
+       str_id = stream->id;
+       if (str_id)
+               ret_val = stream->compr_ops->close(str_id);
+       module_put(sst->dev->driver->owner);
+       kfree(stream);
+       pr_debug("%s: %d\n", __func__, ret_val);
+       return 0;
+}
+
+static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
+                                       struct snd_compr_params *params)
+{
+       struct sst_runtime_stream *stream;
+       int retval;
+       struct snd_sst_params str_params;
+       struct sst_compress_cb cb;
+
+       stream = cstream->runtime->private_data;
+       /* construct fw structure for this*/
+       memset(&str_params, 0, sizeof(str_params));
+
+       str_params.ops = STREAM_OPS_PLAYBACK;
+       str_params.stream_type = SST_STREAM_TYPE_MUSIC;
+       str_params.device_type = SND_SST_DEVICE_COMPRESS;
+
+       switch (params->codec.id) {
+       case SND_AUDIOCODEC_MP3: {
+               str_params.codec = SST_CODEC_TYPE_MP3;
+               str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in;
+               str_params.sparams.uc.mp3_params.pcm_wd_sz = 16;
+               break;
+       }
+
+       case SND_AUDIOCODEC_AAC: {
+               str_params.codec = SST_CODEC_TYPE_AAC;
+               str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in;
+               str_params.sparams.uc.aac_params.pcm_wd_sz = 16;
+               if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS)
+                       str_params.sparams.uc.aac_params.bs_format =
+                                                       AAC_BIT_STREAM_ADTS;
+               else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW)
+                       str_params.sparams.uc.aac_params.bs_format =
+                                                       AAC_BIT_STREAM_RAW;
+               else {
+                       pr_err("Undefined format%d\n", params->codec.format);
+                       return -EINVAL;
+               }
+               str_params.sparams.uc.aac_params.externalsr =
+                                               params->codec.sample_rate;
+               break;
+       }
+
+       default:
+               pr_err("codec not supported, id =%d\n", params->codec.id);
+               return -EINVAL;
+       }
+
+       str_params.aparams.ring_buf_info[0].addr  =
+                                       virt_to_phys(cstream->runtime->buffer);
+       str_params.aparams.ring_buf_info[0].size =
+                                       cstream->runtime->buffer_size;
+       str_params.aparams.sg_count = 1;
+       str_params.aparams.frag_size = cstream->runtime->fragment_size;
+
+       cb.param = cstream;
+       cb.compr_cb = sst_compr_fragment_elapsed;
+       cb.drain_cb_param = cstream;
+       cb.drain_notify = sst_drain_notify;
+
+       retval = stream->compr_ops->open(&str_params, &cb);
+       if (retval < 0) {
+               pr_err("stream allocation failed %d\n", retval);
+               return retval;
+       }
+
+       stream->id = retval;
+       return 0;
+}
+
+static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
+{
+       struct sst_runtime_stream *stream =
+               cstream->runtime->private_data;
+
+       return stream->compr_ops->control(cmd, stream->id);
+}
+
+static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
+                                       struct snd_compr_tstamp *tstamp)
+{
+       struct sst_runtime_stream *stream;
+
+       stream  = cstream->runtime->private_data;
+       stream->compr_ops->tstamp(stream->id, tstamp);
+       tstamp->byte_offset = tstamp->copied_total %
+                                (u32)cstream->runtime->buffer_size;
+       pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
+       return 0;
+}
+
+static int sst_platform_compr_ack(struct snd_compr_stream *cstream,
+                                       size_t bytes)
+{
+       struct sst_runtime_stream *stream;
+
+       stream  = cstream->runtime->private_data;
+       stream->compr_ops->ack(stream->id, (unsigned long)bytes);
+       stream->bytes_written += bytes;
+
+       return 0;
+}
+
+static int sst_platform_compr_get_caps(struct snd_compr_stream *cstream,
+                                       struct snd_compr_caps *caps)
+{
+       struct sst_runtime_stream *stream =
+               cstream->runtime->private_data;
+
+       return stream->compr_ops->get_caps(caps);
+}
+
+static int sst_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
+                                       struct snd_compr_codec_caps *codec)
+{
+       struct sst_runtime_stream *stream =
+               cstream->runtime->private_data;
+
+       return stream->compr_ops->get_codec_caps(codec);
+}
+
+static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream,
+                                       struct snd_compr_metadata *metadata)
+{
+       struct sst_runtime_stream *stream  =
+                cstream->runtime->private_data;
+
+       return stream->compr_ops->set_metadata(stream->id, metadata);
+}
+
+struct snd_compr_ops sst_platform_compr_ops = {
+
+       .open = sst_platform_compr_open,
+       .free = sst_platform_compr_free,
+       .set_params = sst_platform_compr_set_params,
+       .set_metadata = sst_platform_compr_set_metadata,
+       .trigger = sst_platform_compr_trigger,
+       .pointer = sst_platform_compr_pointer,
+       .ack = sst_platform_compr_ack,
+       .get_caps = sst_platform_compr_get_caps,
+       .get_codec_caps = sst_platform_compr_get_codec_caps,
+};
similarity index 67%
rename from sound/soc/intel/sst-mfld-platform.c
rename to sound/soc/intel/sst-mfld-platform-pcm.c
index 840306c..7c790f5 100644 (file)
  *  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.
- *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
@@ -35,8 +29,9 @@
 #include <sound/compress_driver.h>
 #include "sst-mfld-platform.h"
 
-static struct sst_device *sst;
+struct sst_device *sst;
 static DEFINE_MUTEX(sst_lock);
+extern struct snd_compr_ops sst_platform_compr_ops;
 
 int sst_register_dsp(struct sst_device *dev)
 {
@@ -115,36 +110,6 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
                .formats = SNDRV_PCM_FMTBIT_S24_LE,
        },
 },
-{
-       .name = "Speaker-cpu-dai",
-       .id = 1,
-       .playback = {
-               .channels_min = SST_MONO,
-               .channels_max = SST_STEREO,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S24_LE,
-       },
-},
-{
-       .name = "Vibra1-cpu-dai",
-       .id = 2,
-       .playback = {
-               .channels_min = SST_MONO,
-               .channels_max = SST_MONO,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S24_LE,
-       },
-},
-{
-       .name = "Vibra2-cpu-dai",
-       .id = 3,
-       .playback = {
-               .channels_min = SST_MONO,
-               .channels_max = SST_STEREO,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S24_LE,
-       },
-},
 {
        .name = "Compress-cpu-dai",
        .compress_dai = 1,
@@ -157,12 +122,8 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
 },
 };
 
-static const struct snd_soc_component_driver sst_component = {
-       .name           = "sst",
-};
-
 /* helper functions */
-static inline void sst_set_stream_status(struct sst_runtime_stream *stream,
+void sst_set_stream_status(struct sst_runtime_stream *stream,
                                        int state)
 {
        unsigned long flags;
@@ -186,7 +147,6 @@ static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
                                struct sst_pcm_params *param)
 {
 
-       param->codec = SST_CODEC_TYPE_PCM;
        param->num_chan = (u8) substream->runtime->channels;
        param->pcm_wd_sz = substream->runtime->sample_bits;
        param->reserved = 0;
@@ -471,205 +431,6 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
        return retval;
 }
 
-/* compress stream operations */
-static void sst_compr_fragment_elapsed(void *arg)
-{
-       struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
-
-       pr_debug("fragment elapsed by driver\n");
-       if (cstream)
-               snd_compr_fragment_elapsed(cstream);
-}
-
-static int sst_platform_compr_open(struct snd_compr_stream *cstream)
-{
-
-       int ret_val = 0;
-       struct snd_compr_runtime *runtime = cstream->runtime;
-       struct sst_runtime_stream *stream;
-
-       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
-       if (!stream)
-               return -ENOMEM;
-
-       spin_lock_init(&stream->status_lock);
-
-       /* get the sst ops */
-       if (!sst || !try_module_get(sst->dev->driver->owner)) {
-               pr_err("no device available to run\n");
-               ret_val = -ENODEV;
-               goto out_ops;
-       }
-       stream->compr_ops = sst->compr_ops;
-
-       stream->id = 0;
-       sst_set_stream_status(stream, SST_PLATFORM_INIT);
-       runtime->private_data = stream;
-       return 0;
-out_ops:
-       kfree(stream);
-       return ret_val;
-}
-
-static int sst_platform_compr_free(struct snd_compr_stream *cstream)
-{
-       struct sst_runtime_stream *stream;
-       int ret_val = 0, str_id;
-
-       stream = cstream->runtime->private_data;
-       /*need to check*/
-       str_id = stream->id;
-       if (str_id)
-               ret_val = stream->compr_ops->close(str_id);
-       module_put(sst->dev->driver->owner);
-       kfree(stream);
-       pr_debug("%s: %d\n", __func__, ret_val);
-       return 0;
-}
-
-static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
-                                       struct snd_compr_params *params)
-{
-       struct sst_runtime_stream *stream;
-       int retval;
-       struct snd_sst_params str_params;
-       struct sst_compress_cb cb;
-
-       stream = cstream->runtime->private_data;
-       /* construct fw structure for this*/
-       memset(&str_params, 0, sizeof(str_params));
-
-       str_params.ops = STREAM_OPS_PLAYBACK;
-       str_params.stream_type = SST_STREAM_TYPE_MUSIC;
-       str_params.device_type = SND_SST_DEVICE_COMPRESS;
-
-       switch (params->codec.id) {
-       case SND_AUDIOCODEC_MP3: {
-               str_params.codec = SST_CODEC_TYPE_MP3;
-               str_params.sparams.uc.mp3_params.codec = SST_CODEC_TYPE_MP3;
-               str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in;
-               str_params.sparams.uc.mp3_params.pcm_wd_sz = 16;
-               break;
-       }
-
-       case SND_AUDIOCODEC_AAC: {
-               str_params.codec = SST_CODEC_TYPE_AAC;
-               str_params.sparams.uc.aac_params.codec = SST_CODEC_TYPE_AAC;
-               str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in;
-               str_params.sparams.uc.aac_params.pcm_wd_sz = 16;
-               if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS)
-                       str_params.sparams.uc.aac_params.bs_format =
-                                                       AAC_BIT_STREAM_ADTS;
-               else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW)
-                       str_params.sparams.uc.aac_params.bs_format =
-                                                       AAC_BIT_STREAM_RAW;
-               else {
-                       pr_err("Undefined format%d\n", params->codec.format);
-                       return -EINVAL;
-               }
-               str_params.sparams.uc.aac_params.externalsr =
-                                               params->codec.sample_rate;
-               break;
-       }
-
-       default:
-               pr_err("codec not supported, id =%d\n", params->codec.id);
-               return -EINVAL;
-       }
-
-       str_params.aparams.ring_buf_info[0].addr  =
-                                       virt_to_phys(cstream->runtime->buffer);
-       str_params.aparams.ring_buf_info[0].size =
-                                       cstream->runtime->buffer_size;
-       str_params.aparams.sg_count = 1;
-       str_params.aparams.frag_size = cstream->runtime->fragment_size;
-
-       cb.param = cstream;
-       cb.compr_cb = sst_compr_fragment_elapsed;
-
-       retval = stream->compr_ops->open(&str_params, &cb);
-       if (retval < 0) {
-               pr_err("stream allocation failed %d\n", retval);
-               return retval;
-       }
-
-       stream->id = retval;
-       return 0;
-}
-
-static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
-{
-       struct sst_runtime_stream *stream =
-               cstream->runtime->private_data;
-
-       return stream->compr_ops->control(cmd, stream->id);
-}
-
-static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
-                                       struct snd_compr_tstamp *tstamp)
-{
-       struct sst_runtime_stream *stream;
-
-       stream  = cstream->runtime->private_data;
-       stream->compr_ops->tstamp(stream->id, tstamp);
-       tstamp->byte_offset = tstamp->copied_total %
-                                (u32)cstream->runtime->buffer_size;
-       pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
-       return 0;
-}
-
-static int sst_platform_compr_ack(struct snd_compr_stream *cstream,
-                                       size_t bytes)
-{
-       struct sst_runtime_stream *stream;
-
-       stream  = cstream->runtime->private_data;
-       stream->compr_ops->ack(stream->id, (unsigned long)bytes);
-       stream->bytes_written += bytes;
-
-       return 0;
-}
-
-static int sst_platform_compr_get_caps(struct snd_compr_stream *cstream,
-                                       struct snd_compr_caps *caps)
-{
-       struct sst_runtime_stream *stream =
-               cstream->runtime->private_data;
-
-       return stream->compr_ops->get_caps(caps);
-}
-
-static int sst_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
-                                       struct snd_compr_codec_caps *codec)
-{
-       struct sst_runtime_stream *stream =
-               cstream->runtime->private_data;
-
-       return stream->compr_ops->get_codec_caps(codec);
-}
-
-static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream,
-                                       struct snd_compr_metadata *metadata)
-{
-       struct sst_runtime_stream *stream  =
-                cstream->runtime->private_data;
-
-       return stream->compr_ops->set_metadata(stream->id, metadata);
-}
-
-static struct snd_compr_ops sst_platform_compr_ops = {
-
-       .open = sst_platform_compr_open,
-       .free = sst_platform_compr_free,
-       .set_params = sst_platform_compr_set_params,
-       .set_metadata = sst_platform_compr_set_metadata,
-       .trigger = sst_platform_compr_trigger,
-       .pointer = sst_platform_compr_pointer,
-       .ack = sst_platform_compr_ack,
-       .get_caps = sst_platform_compr_get_caps,
-       .get_codec_caps = sst_platform_compr_get_codec_caps,
-};
-
 static struct snd_soc_platform_driver sst_soc_platform_drv = {
        .ops            = &sst_platform_ops,
        .compr_ops      = &sst_platform_compr_ops,
@@ -677,6 +438,11 @@ static struct snd_soc_platform_driver sst_soc_platform_drv = {
        .pcm_free       = sst_pcm_free,
 };
 
+static const struct snd_soc_component_driver sst_component = {
+       .name           = "sst",
+};
+
+
 static int sst_platform_probe(struct platform_device *pdev)
 {
        int ret;
index 0c4e2dd..6c5e7dc 100644 (file)
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *
  */
 
 #ifndef __SST_PLATFORMDRV_H__
@@ -29,6 +23,8 @@
 
 #include "sst-mfld-dsp.h"
 
+extern struct sst_device *sst;
+
 #define SST_MONO               1
 #define SST_STEREO             2
 #define SST_MAX_CAP            5
@@ -108,6 +104,8 @@ struct sst_stream_params {
 struct sst_compress_cb {
        void *param;
        void (*compr_cb)(void *param);
+       void *drain_cb_param;
+       void (*drain_notify)(void *param);
 };
 
 struct compress_sst_ops {
@@ -148,6 +146,7 @@ struct sst_device {
        struct compress_sst_ops *compr_ops;
 };
 
+void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
 int sst_register_dsp(struct sst_device *sst);
 int sst_unregister_dsp(struct sst_device *sst);
 #endif
index 29f76af..1a354a6 100644 (file)
@@ -1,24 +1,29 @@
 config SND_JZ4740_SOC
        tristate "SoC Audio for Ingenic JZ4740 SoC"
-       depends on MACH_JZ4740 && SND_SOC
+       depends on MACH_JZ4740 || COMPILE_TEST
        select SND_SOC_GENERIC_DMAENGINE_PCM
        help
          Say Y or M if you want to add support for codecs attached to
          the JZ4740 I2S interface. You will also need to select the audio
          interfaces to support below.
 
+if SND_JZ4740_SOC
+
 config SND_JZ4740_SOC_I2S
-       depends on SND_JZ4740_SOC
        tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC"
+       depends on HAS_IOMEM
        help
          Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740
          based boards.
 
 config SND_JZ4740_SOC_QI_LB60
        tristate "SoC Audio support for Qi LB60"
-       depends on SND_JZ4740_SOC && JZ4740_QI_LB60
+       depends on HAS_IOMEM
+       depends on JZ4740_QI_LB60 || COMPILE_TEST
        select SND_JZ4740_SOC_I2S
     select SND_SOC_JZ4740_CODEC
        help
          Say Y if you want to add support for ASoC audio on the Qi LB60 board
          a.k.a Qi Ben NanoNote.
+
+endif
index 8f22000..3f9c3a9 100644 (file)
 #include <sound/initval.h>
 #include <sound/dmaengine_pcm.h>
 
-#include <asm/mach-jz4740/dma.h>
-
 #include "jz4740-i2s.h"
 
+#define JZ4740_DMA_TYPE_AIC_TRANSMIT 24
+#define JZ4740_DMA_TYPE_AIC_RECEIVE 25
+
 #define JZ_REG_AIC_CONF                0x00
 #define JZ_REG_AIC_CTRL                0x04
 #define JZ_REG_AIC_I2S_FMT     0x10
index 82b5f37..5cb91f9 100644 (file)
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 
-#define QI_LB60_SND_GPIO JZ_GPIO_PORTB(29)
-#define QI_LB60_AMP_GPIO JZ_GPIO_PORTD(4)
+struct qi_lb60 {
+       struct gpio_desc *snd_gpio;
+       struct gpio_desc *amp_gpio;
+};
 
 static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget,
                             struct snd_kcontrol *ctrl, int event)
 {
+       struct qi_lb60 *qi_lb60 = snd_soc_card_get_drvdata(widget->dapm->card);
        int on = !SND_SOC_DAPM_EVENT_OFF(event);
 
-       gpio_set_value(QI_LB60_SND_GPIO, on);
-       gpio_set_value(QI_LB60_AMP_GPIO, on);
+       gpiod_set_value_cansleep(qi_lb60->snd_gpio, on);
+       gpiod_set_value_cansleep(qi_lb60->amp_gpio, on);
 
        return 0;
 }
@@ -46,29 +49,6 @@ static const struct snd_soc_dapm_route qi_lb60_routes[] = {
        {"Speaker", NULL, "ROUT"},
 };
 
-#define QI_LB60_DAIFMT (SND_SOC_DAIFMT_I2S | \
-                       SND_SOC_DAIFMT_NB_NF | \
-                       SND_SOC_DAIFMT_CBM_CFM)
-
-static int qi_lb60_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-       int ret;
-
-       snd_soc_dapm_nc_pin(dapm, "LIN");
-       snd_soc_dapm_nc_pin(dapm, "RIN");
-
-       ret = snd_soc_dai_set_fmt(cpu_dai, QI_LB60_DAIFMT);
-       if (ret < 0) {
-               dev_err(codec->dev, "Failed to set cpu dai format: %d\n", ret);
-               return ret;
-       }
-
-       return 0;
-}
-
 static struct snd_soc_dai_link qi_lb60_dai = {
        .name = "jz4740",
        .stream_name = "jz4740",
@@ -76,10 +56,11 @@ static struct snd_soc_dai_link qi_lb60_dai = {
        .platform_name = "jz4740-i2s",
        .codec_dai_name = "jz4740-hifi",
        .codec_name = "jz4740-codec",
-       .init = qi_lb60_codec_init,
+       .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+               SND_SOC_DAIFMT_CBM_CFM,
 };
 
-static struct snd_soc_card qi_lb60 = {
+static struct snd_soc_card qi_lb60_card = {
        .name = "QI LB60",
        .owner = THIS_MODULE,
        .dai_link = &qi_lb60_dai,
@@ -89,40 +70,38 @@ static struct snd_soc_card qi_lb60 = {
        .num_dapm_widgets = ARRAY_SIZE(qi_lb60_widgets),
        .dapm_routes = qi_lb60_routes,
        .num_dapm_routes = ARRAY_SIZE(qi_lb60_routes),
-};
-
-static const struct gpio qi_lb60_gpios[] = {
-       { QI_LB60_SND_GPIO, GPIOF_OUT_INIT_LOW, "SND" },
-       { QI_LB60_AMP_GPIO, GPIOF_OUT_INIT_LOW, "AMP" },
+       .fully_routed = true,
 };
 
 static int qi_lb60_probe(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = &qi_lb60;
+       struct qi_lb60 *qi_lb60;
+       struct snd_soc_card *card = &qi_lb60_card;
        int ret;
 
-       ret = gpio_request_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
+       qi_lb60 = devm_kzalloc(&pdev->dev, sizeof(*qi_lb60), GFP_KERNEL);
+       if (!qi_lb60)
+               return -ENOMEM;
+
+       qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd");
+       if (IS_ERR(qi_lb60->snd_gpio))
+               return PTR_ERR(qi_lb60->snd_gpio);
+       ret = gpiod_direction_output(qi_lb60->snd_gpio, 0);
        if (ret)
                return ret;
 
-       card->dev = &pdev->dev;
+       qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp");
+       if (IS_ERR(qi_lb60->amp_gpio))
+               return PTR_ERR(qi_lb60->amp_gpio);
+       ret = gpiod_direction_output(qi_lb60->amp_gpio, 0);
+       if (ret)
+               return ret;
 
-       ret = snd_soc_register_card(card);
-       if (ret) {
-               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
-                       ret);
-               gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
-       }
-       return ret;
-}
+       card->dev = &pdev->dev;
 
-static int qi_lb60_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       snd_soc_card_set_drvdata(card, qi_lb60);
 
-       snd_soc_unregister_card(card);
-       gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, card);
 }
 
 static struct platform_driver qi_lb60_driver = {
@@ -131,7 +110,6 @@ static struct platform_driver qi_lb60_driver = {
                .owner  = THIS_MODULE,
        },
        .probe          = qi_lb60_probe,
-       .remove         = qi_lb60_remove,
 };
 
 module_platform_driver(qi_lb60_driver);
index d213832..844b841 100644 (file)
@@ -52,18 +52,6 @@ static const struct snd_soc_dapm_route t5325_route[] = {
        { "MIC2",               NULL,   "Mic Jack" },
 };
 
-static int t5325_dai_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       snd_soc_dapm_enable_pin(dapm, "Mic Jack");
-       snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
-       snd_soc_dapm_enable_pin(dapm, "Speaker");
-
-       return 0;
-}
-
 static struct snd_soc_dai_link t5325_dai[] = {
 {
        .name = "ALC5621",
@@ -74,7 +62,6 @@ static struct snd_soc_dai_link t5325_dai[] = {
        .codec_name = "alc562x-codec.0-001a",
        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
        .ops = &t5325_ops,
-       .init = t5325_dai_init,
 },
 };
 
index a0ed1c6..7f0c954 100644 (file)
@@ -4,6 +4,7 @@
 config SND_SOC_NUC900
        tristate "SoC Audio for NUC900 series"
        depends on ARCH_W90X900
+       select SND_SOC_NUC900_AC97
        help
          This option enables support for AC97 mode on the NUC900 SoC.
 
index 8987bf9..f2f6794 100644 (file)
@@ -28,6 +28,7 @@
 
 static DEFINE_MUTEX(ac97_mutex);
 struct nuc900_audio *nuc900_ac97_data;
+EXPORT_SYMBOL_GPL(nuc900_ac97_data);
 
 static int nuc900_checkready(void)
 {
index e006593..d44463a 100644 (file)
@@ -26,7 +26,7 @@ config SND_OMAP_SOC_N810
 
 config SND_OMAP_SOC_RX51
        tristate "SoC Audio support for Nokia RX-51"
-       depends on SND_OMAP_SOC && ARM && (MACH_NOKIA_RX51 || COMPILE_TEST)
+       depends on SND_OMAP_SOC && ARM && (MACH_NOKIA_RX51 || COMPILE_TEST) && I2C
        select SND_OMAP_SOC_MCBSP
        select SND_SOC_TLV320AIC3X
        select SND_SOC_TPA6130A2
@@ -37,7 +37,7 @@ config SND_OMAP_SOC_RX51
 
 config SND_OMAP_SOC_AMS_DELTA
        tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
-       depends on SND_OMAP_SOC && MACH_AMS_DELTA
+       depends on SND_OMAP_SOC && MACH_AMS_DELTA && TTY
        select SND_OMAP_SOC_MCBSP
        select SND_SOC_CX20442
        help
index 994dcf3..25a33e9 100644 (file)
@@ -77,7 +77,7 @@ static struct snd_soc_dai_link am3517evm_dai = {
        .stream_name = "AIC23",
        .cpu_dai_name = "omap-mcbsp.1",
        .codec_dai_name = "tlv320aic23-hifi",
-       .platform_name = "omap-pcm-audio",
+       .platform_name = "omap-mcbsp.1",
        .codec_name = "tlv320aic23-codec.2-001a",
        .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
                   SND_SOC_DAIFMT_CBM_CFM,
index 56a5219..0cc41f9 100644 (file)
@@ -38,7 +38,6 @@
 #include "omap-mcbsp.h"
 #include "../codecs/cx20442.h"
 
-
 /* Board specific DAPM widgets */
 static const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
        /* Handset */
@@ -90,17 +89,23 @@ static const unsigned short ams_delta_audio_mode_pins[] = {
 
 static unsigned short ams_delta_audio_agc;
 
+/*
+ * Used for passing a codec structure pointer
+ * from the board initialization code to the tty line discipline.
+ */
+static struct snd_soc_codec *cx20442_codec;
+
 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 snd_soc_dapm_context *dapm = &codec->dapm;
+       struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_context *dapm = &card->dapm;
        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->hw_write)
+       if (!cx20442_codec->hw_write)
                return -EUNATCH;
 
        if (ucontrol->value.enumerated.item[0] >= control->items)
@@ -166,8 +171,8 @@ static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol,
 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);
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_context *dapm = &card->dapm;
        unsigned short pins, mode;
 
        pins = ((snd_soc_dapm_get_pin_status(dapm, "Mouthpiece") <<
@@ -270,12 +275,6 @@ static void cx81801_timeout(unsigned long data)
                ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_CODEC, 0);
 }
 
-/*
- * Used for passing a codec structure pointer
- * from the board initialization code to the tty line discipline.
- */
-static struct snd_soc_codec *cx20442_codec;
-
 /* Line discipline .open() */
 static int cx81801_open(struct tty_struct *tty)
 {
@@ -302,7 +301,7 @@ static int cx81801_open(struct tty_struct *tty)
 static void cx81801_close(struct tty_struct *tty)
 {
        struct snd_soc_codec *codec = tty->disc_data;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       struct snd_soc_dapm_context *dapm = &codec->card->dapm;
 
        del_timer_sync(&cx81801_timer);
 
@@ -475,15 +474,14 @@ static void ams_delta_shutdown(struct snd_pcm_substream *substream)
 
 static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_card *card = rtd->card;
+       struct snd_soc_dapm_context *dapm = &card->dapm;
        int ret;
        /* Codec is ready, now add/activate board specific controls */
 
        /* Store a pointer to the codec structure for tty ldisc use */
-       cx20442_codec = codec;
+       cx20442_codec = rtd->codec;
 
        /* Set up digital mute if not provided by the codec */
        if (!codec_dai->driver->ops) {
@@ -520,40 +518,20 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd)
                return 0;
        }
 
-       /* Add board specific DAPM widgets and routes */
-       ret = snd_soc_dapm_new_controls(dapm, 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(dapm, 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(dapm, "Mouthpiece");
-       snd_soc_dapm_enable_pin(dapm, "Earpiece");
-       snd_soc_dapm_enable_pin(dapm, "Microphone");
        snd_soc_dapm_disable_pin(dapm, "Speaker");
        snd_soc_dapm_disable_pin(dapm, "AGCIN");
        snd_soc_dapm_disable_pin(dapm, "AGCOUT");
 
-       /* Add virtual switch */
-       ret = snd_soc_add_codec_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;
+}
+
+static int ams_delta_card_remove(struct snd_soc_card *card)
+{
+       snd_soc_jack_free_gpios(&ams_delta_hook_switch,
+                       ARRAY_SIZE(ams_delta_hook_switch_gpios),
+                       ams_delta_hook_switch_gpios);
 
        return 0;
 }
@@ -565,7 +543,7 @@ static struct snd_soc_dai_link ams_delta_dai_link = {
        .cpu_dai_name = "omap-mcbsp.1",
        .codec_dai_name = "cx20442-voice",
        .init = ams_delta_cx20442_init,
-       .platform_name = "omap-pcm-audio",
+       .platform_name = "omap-mcbsp.1",
        .codec_name = "cx20442-codec",
        .ops = &ams_delta_ops,
 };
@@ -574,8 +552,16 @@ static struct snd_soc_dai_link ams_delta_dai_link = {
 static struct snd_soc_card ams_delta_audio_card = {
        .name = "AMS_DELTA",
        .owner = THIS_MODULE,
+       .remove = ams_delta_card_remove,
        .dai_link = &ams_delta_dai_link,
        .num_links = 1,
+
+       .controls = ams_delta_audio_controls,
+       .num_controls = ARRAY_SIZE(ams_delta_audio_controls),
+       .dapm_widgets = ams_delta_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(ams_delta_dapm_widgets),
+       .dapm_routes = ams_delta_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(ams_delta_audio_map),
 };
 
 /* Module init/exit */
@@ -603,10 +589,6 @@ static int ams_delta_remove(struct platform_device *pdev)
                dev_warn(&pdev->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);
-
        snd_soc_unregister_card(card);
        card->dev = NULL;
        return 0;
index fd4d9c8..5d7f9ce 100644 (file)
@@ -278,7 +278,7 @@ static struct snd_soc_dai_link n810_dai = {
        .name = "TLV320AIC33",
        .stream_name = "AIC33",
        .cpu_dai_name = "omap-mcbsp.2",
-       .platform_name = "omap-pcm-audio",
+       .platform_name = "omap-mcbsp.2",
        .codec_name = "tlv320aic3x-codec.2-0018",
        .codec_dai_name = "tlv320aic3x-hifi",
        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
index 024dafc..cec836e 100644 (file)
@@ -47,8 +47,7 @@ static int omap_abe_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_card *card = rtd->card;
        struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
        int clk_id, freq;
        int ret;
@@ -168,7 +167,7 @@ static const struct snd_soc_dapm_route audio_map[] = {
 static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_card *card = rtd->card;
        struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
        int hs_trim;
        int ret = 0;
@@ -214,9 +213,7 @@ static struct snd_soc_dai_link abe_twl6040_dai_links[] = {
        {
                .name = "TWL6040",
                .stream_name = "TWL6040",
-               .cpu_dai_name = "omap-mcpdm",
                .codec_dai_name = "twl6040-legacy",
-               .platform_name = "omap-pcm-audio",
                .codec_name = "twl6040-codec",
                .init = omap_abe_twl6040_init,
                .ops = &omap_abe_ops,
@@ -224,9 +221,7 @@ static struct snd_soc_dai_link abe_twl6040_dai_links[] = {
        {
                .name = "DMIC",
                .stream_name = "DMIC Capture",
-               .cpu_dai_name = "omap-dmic",
                .codec_dai_name = "dmic-hifi",
-               .platform_name = "omap-pcm-audio",
                .codec_name = "dmic-codec",
                .init = omap_abe_dmic_init,
                .ops = &omap_abe_dmic_ops,
@@ -281,14 +276,14 @@ static int omap_abe_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "McPDM node is not provided\n");
                return -EINVAL;
        }
-       abe_twl6040_dai_links[0].cpu_dai_name  = NULL;
        abe_twl6040_dai_links[0].cpu_of_node = dai_node;
+       abe_twl6040_dai_links[0].platform_of_node = dai_node;
 
        dai_node = of_parse_phandle(node, "ti,dmic", 0);
        if (dai_node) {
                num_links = 2;
-               abe_twl6040_dai_links[1].cpu_dai_name  = NULL;
                abe_twl6040_dai_links[1].cpu_of_node = dai_node;
+               abe_twl6040_dai_links[1].platform_of_node = dai_node;
 
                priv->dmic_codec_dev = platform_device_register_simple(
                                                "dmic-codec", -1, NULL, 0);
index 1bd531d..6925d71 100644 (file)
@@ -40,6 +40,7 @@
 #include <sound/initval.h>
 #include <sound/soc.h>
 #include <sound/dmaengine_pcm.h>
+#include <sound/omap-pcm.h>
 
 #include "omap-dmic.h"
 
@@ -113,7 +114,6 @@ static int omap_dmic_dai_startup(struct snd_pcm_substream *substream,
 
        mutex_unlock(&dmic->mutex);
 
-       snd_soc_dai_set_dma_data(dai, substream, &dmic->dma_data);
        return ret;
 }
 
@@ -417,6 +417,9 @@ static int omap_dmic_probe(struct snd_soc_dai *dai)
 
        /* Configure DMIC threshold value */
        dmic->threshold = OMAP_DMIC_THRES_MAX - 3;
+
+       snd_soc_dai_init_dma_data(dai, NULL, &dmic->dma_data);
+
        return 0;
 }
 
@@ -492,6 +495,10 @@ static int asoc_dmic_probe(struct platform_device *pdev)
        if (ret)
                goto err_put_clk;
 
+       ret = omap_pcm_platform_register(&pdev->dev);
+       if (ret)
+               goto err_put_clk;
+
        return 0;
 
 err_put_clk:
index 7e66e9c..f649fe8 100644 (file)
@@ -33,7 +33,7 @@ static struct snd_soc_dai_link omap_hdmi_dai = {
        .name = "HDMI",
        .stream_name = "HDMI",
        .cpu_dai_name = "omap-hdmi-audio-dai",
-       .platform_name = "omap-pcm-audio",
+       .platform_name = "omap-hdmi-audio-dai",
        .codec_name = "hdmi-audio-codec",
        .codec_dai_name = "hdmi-hifi",
 };
index ced3b88..eb9c392 100644 (file)
@@ -34,6 +34,7 @@
 #include <sound/asoundef.h>
 #include <sound/dmaengine_pcm.h>
 #include <video/omapdss.h>
+#include <sound/omap-pcm.h>
 
 #include "omap-hdmi.h"
 
@@ -324,7 +325,10 @@ static int omap_hdmi_probe(struct platform_device *pdev)
        ret = snd_soc_register_component(&pdev->dev, &omap_hdmi_component,
                                         &omap_hdmi_dai, 1);
 
-       return ret;
+       if (ret)
+               return ret;
+
+       return omap_pcm_platform_register(&pdev->dev);
 }
 
 static int omap_hdmi_remove(struct platform_device *pdev)
index 6c19bba..efe2cd6 100644 (file)
@@ -34,6 +34,7 @@
 #include <sound/initval.h>
 #include <sound/soc.h>
 #include <sound/dmaengine_pcm.h>
+#include <sound/omap-pcm.h>
 
 #include <linux/platform_data/asoc-ti-mcbsp.h>
 #include "mcbsp.h"
@@ -149,9 +150,6 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
                                           SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
        }
 
-       snd_soc_dai_set_dma_data(cpu_dai, substream,
-                                &mcbsp->dma_data[substream->stream]);
-
        return err;
 }
 
@@ -559,6 +557,10 @@ static int omap_mcbsp_probe(struct snd_soc_dai *dai)
 
        pm_runtime_enable(mcbsp->dev);
 
+       snd_soc_dai_init_dma_data(dai,
+                                 &mcbsp->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
+                                 &mcbsp->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
+
        return 0;
 }
 
@@ -691,7 +693,7 @@ OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \
 OMAP_MCBSP_ST_CONTROLS(2);
 OMAP_MCBSP_ST_CONTROLS(3);
 
-int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd)
+int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id)
 {
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
@@ -701,7 +703,7 @@ int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd)
                return 0;
        }
 
-       switch (mcbsp->id) {
+       switch (port_id) {
        case 2: /* McBSP 2 */
                return snd_soc_add_dai_controls(cpu_dai,
                                        omap_mcbsp2_st_controls,
@@ -711,6 +713,7 @@ int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd)
                                        omap_mcbsp3_st_controls,
                                        ARRAY_SIZE(omap_mcbsp3_st_controls));
        default:
+               dev_err(mcbsp->dev, "Port %d not supported\n", port_id);
                break;
        }
 
@@ -799,11 +802,15 @@ static int asoc_mcbsp_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, mcbsp);
 
        ret = omap_mcbsp_init(pdev);
-       if (!ret)
-               return snd_soc_register_component(&pdev->dev, &omap_mcbsp_component,
-                                                 &omap_mcbsp_dai, 1);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_register_component(&pdev->dev, &omap_mcbsp_component,
+                                        &omap_mcbsp_dai, 1);
+       if (ret)
+               return ret;
 
-       return ret;
+       return omap_pcm_platform_register(&pdev->dev);
 }
 
 static int asoc_mcbsp_remove(struct platform_device *pdev)
index ba8386a..2e3369c 100644 (file)
@@ -39,6 +39,6 @@ enum omap_mcbsp_div {
        OMAP_MCBSP_CLKGDV,              /* Sample rate generator divider */
 };
 
-int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd);
+int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id);
 
 #endif
index 2f5b153..f0e2ebe 100644 (file)
@@ -40,6 +40,7 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/dmaengine_pcm.h>
+#include <sound/omap-pcm.h>
 
 #include "omap-mcpdm.h"
 
@@ -265,9 +266,6 @@ static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
        }
        mutex_unlock(&mcpdm->mutex);
 
-       snd_soc_dai_set_dma_data(dai, substream,
-                                &mcpdm->dma_data[substream->stream]);
-
        return 0;
 }
 
@@ -406,6 +404,11 @@ static int omap_mcpdm_probe(struct snd_soc_dai *dai)
        mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold = 2;
        mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold =
                                                        MCPDM_UP_THRES_MAX - 3;
+
+       snd_soc_dai_init_dma_data(dai,
+                                 &mcpdm->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
+                                 &mcpdm->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
+
        return ret;
 }
 
@@ -460,6 +463,7 @@ static int asoc_mcpdm_probe(struct platform_device *pdev)
 {
        struct omap_mcpdm *mcpdm;
        struct resource *res;
+       int ret;
 
        mcpdm = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcpdm), GFP_KERNEL);
        if (!mcpdm)
@@ -490,9 +494,13 @@ static int asoc_mcpdm_probe(struct platform_device *pdev)
 
        mcpdm->dev = &pdev->dev;
 
-       return devm_snd_soc_register_component(&pdev->dev,
+       ret =  devm_snd_soc_register_component(&pdev->dev,
                                               &omap_mcpdm_component,
                                               &omap_mcpdm_dai, 1);
+       if (ret)
+               return ret;
+
+       return omap_pcm_platform_register(&pdev->dev);
 }
 
 static const struct of_device_id omap_mcpdm_of_match[] = {
index 07b8b7b..8d809f8 100644 (file)
@@ -232,31 +232,12 @@ static struct snd_soc_platform_driver omap_soc_platform = {
        .pcm_free       = omap_pcm_free_dma_buffers,
 };
 
-static int omap_pcm_probe(struct platform_device *pdev)
+int omap_pcm_platform_register(struct device *dev)
 {
-       return snd_soc_register_platform(&pdev->dev,
-                       &omap_soc_platform);
+       return devm_snd_soc_register_platform(dev, &omap_soc_platform);
 }
-
-static int omap_pcm_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_platform(&pdev->dev);
-       return 0;
-}
-
-static struct platform_driver omap_pcm_driver = {
-       .driver = {
-                       .name = "omap-pcm-audio",
-                       .owner = THIS_MODULE,
-       },
-
-       .probe = omap_pcm_probe,
-       .remove = omap_pcm_remove,
-};
-
-module_platform_driver(omap_pcm_driver);
+EXPORT_SYMBOL_GPL(omap_pcm_platform_register);
 
 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
 MODULE_DESCRIPTION("OMAP PCM DMA module");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:omap-pcm-audio");
index 6a8d6b5..f8a6adc 100644 (file)
@@ -55,8 +55,7 @@ static int omap_twl4030_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_card *card = rtd->card;
        unsigned int fmt;
        int ret;
 
@@ -179,7 +178,7 @@ static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
 static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_card *card = rtd->card;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
        struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
        struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
@@ -232,6 +231,18 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
        return ret;
 }
 
+static int omap_twl4030_card_remove(struct snd_soc_card *card)
+{
+       struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
+
+       if (priv->jack_detect > 0)
+               snd_soc_jack_free_gpios(&priv->hs_jack,
+                                       ARRAY_SIZE(hs_jack_gpios),
+                                       hs_jack_gpios);
+
+       return 0;
+}
+
 /* Digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
        {
@@ -239,7 +250,7 @@ static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
                .stream_name = "TWL4030 HiFi",
                .cpu_dai_name = "omap-mcbsp.2",
                .codec_dai_name = "twl4030-hifi",
-               .platform_name = "omap-pcm-audio",
+               .platform_name = "omap-mcbsp.2",
                .codec_name = "twl4030-codec",
                .init = omap_twl4030_init,
                .ops = &omap_twl4030_ops,
@@ -249,7 +260,7 @@ static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
                .stream_name = "TWL4030 Voice",
                .cpu_dai_name = "omap-mcbsp.3",
                .codec_dai_name = "twl4030-voice",
-               .platform_name = "omap-pcm-audio",
+               .platform_name = "omap-mcbsp.2",
                .codec_name = "twl4030-codec",
                .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
                           SND_SOC_DAIFMT_CBM_CFM,
@@ -259,6 +270,7 @@ static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
 /* Audio machine driver */
 static struct snd_soc_card omap_twl4030_card = {
        .owner = THIS_MODULE,
+       .remove = omap_twl4030_card_remove,
        .dai_link = omap_twl4030_dai_links,
        .num_links = ARRAY_SIZE(omap_twl4030_dai_links),
 
@@ -299,12 +311,18 @@ static int omap_twl4030_probe(struct platform_device *pdev)
                omap_twl4030_dai_links[0].cpu_dai_name  = NULL;
                omap_twl4030_dai_links[0].cpu_of_node = dai_node;
 
+               omap_twl4030_dai_links[0].platform_name  = NULL;
+               omap_twl4030_dai_links[0].platform_of_node = dai_node;
+
                dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0);
                if (!dai_node) {
                        card->num_links = 1;
                } else {
                        omap_twl4030_dai_links[1].cpu_dai_name  = NULL;
                        omap_twl4030_dai_links[1].cpu_of_node = dai_node;
+
+                       omap_twl4030_dai_links[1].platform_name  = NULL;
+                       omap_twl4030_dai_links[1].platform_of_node = dai_node;
                }
 
                priv->jack_detect = of_get_named_gpio(node,
@@ -348,19 +366,6 @@ static int omap_twl4030_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int omap_twl4030_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-       struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
-
-       if (priv->jack_detect > 0)
-               snd_soc_jack_free_gpios(&priv->hs_jack,
-                                       ARRAY_SIZE(hs_jack_gpios),
-                                       hs_jack_gpios);
-
-       return 0;
-}
-
 static const struct of_device_id omap_twl4030_of_match[] = {
        {.compatible = "ti,omap-twl4030", },
        { },
@@ -375,7 +380,6 @@ static struct platform_driver omap_twl4030_driver = {
                .of_match_table = omap_twl4030_of_match,
        },
        .probe = omap_twl4030_probe,
-       .remove = omap_twl4030_remove,
 };
 
 module_platform_driver(omap_twl4030_driver);
index cf604a2..076bec6 100644 (file)
@@ -121,7 +121,7 @@ static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w,
  *  |A| <~~clk~~+
  *  |P| <--- TWL4030 <--------- Line In and MICs
  */
-static const struct snd_soc_dapm_widget omap3pandora_out_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget omap3pandora_dapm_widgets[] = {
        SND_SOC_DAPM_DAC_E("PCM DAC", "HiFi Playback", SND_SOC_NOPM,
                           0, 0, omap3pandora_dac_event,
                           SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -130,22 +130,18 @@ static const struct snd_soc_dapm_widget omap3pandora_out_dapm_widgets[] = {
                           SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
        SND_SOC_DAPM_HP("Headphone Jack", NULL),
        SND_SOC_DAPM_LINE("Line Out", NULL),
-};
 
-static const struct snd_soc_dapm_widget omap3pandora_in_dapm_widgets[] = {
        SND_SOC_DAPM_MIC("Mic (internal)", NULL),
        SND_SOC_DAPM_MIC("Mic (external)", NULL),
        SND_SOC_DAPM_LINE("Line In", NULL),
 };
 
-static const struct snd_soc_dapm_route omap3pandora_out_map[] = {
+static const struct snd_soc_dapm_route omap3pandora_map[] = {
        {"PCM DAC", NULL, "APLL Enable"},
        {"Headphone Amplifier", NULL, "PCM DAC"},
        {"Line Out", NULL, "PCM DAC"},
        {"Headphone Jack", NULL, "Headphone Amplifier"},
-};
 
-static const struct snd_soc_dapm_route omap3pandora_in_map[] = {
        {"AUXL", NULL, "Line In"},
        {"AUXR", NULL, "Line In"},
 
@@ -160,7 +156,6 @@ static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
-       int ret;
 
        /* All TWL4030 output pins are floating */
        snd_soc_dapm_nc_pin(dapm, "EARPIECE");
@@ -174,20 +169,13 @@ static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd)
        snd_soc_dapm_nc_pin(dapm, "HFR");
        snd_soc_dapm_nc_pin(dapm, "VIBRA");
 
-       ret = snd_soc_dapm_new_controls(dapm, omap3pandora_out_dapm_widgets,
-                               ARRAY_SIZE(omap3pandora_out_dapm_widgets));
-       if (ret < 0)
-               return ret;
-
-       return snd_soc_dapm_add_routes(dapm, omap3pandora_out_map,
-               ARRAY_SIZE(omap3pandora_out_map));
+       return 0;
 }
 
 static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
-       int ret;
 
        /* Not comnnected */
        snd_soc_dapm_nc_pin(dapm, "HSMIC");
@@ -195,13 +183,7 @@ static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd)
        snd_soc_dapm_nc_pin(dapm, "DIGIMIC0");
        snd_soc_dapm_nc_pin(dapm, "DIGIMIC1");
 
-       ret = snd_soc_dapm_new_controls(dapm, omap3pandora_in_dapm_widgets,
-                               ARRAY_SIZE(omap3pandora_in_dapm_widgets));
-       if (ret < 0)
-               return ret;
-
-       return snd_soc_dapm_add_routes(dapm, omap3pandora_in_map,
-               ARRAY_SIZE(omap3pandora_in_map));
+       return 0;
 }
 
 static struct snd_soc_ops omap3pandora_ops = {
@@ -215,7 +197,7 @@ static struct snd_soc_dai_link omap3pandora_dai[] = {
                .stream_name = "HiFi Out",
                .cpu_dai_name = "omap-mcbsp.2",
                .codec_dai_name = "twl4030-hifi",
-               .platform_name = "omap-pcm-audio",
+               .platform_name = "omap-mcbsp.2",
                .codec_name = "twl4030-codec",
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
                           SND_SOC_DAIFMT_CBS_CFS,
@@ -226,7 +208,7 @@ static struct snd_soc_dai_link omap3pandora_dai[] = {
                .stream_name = "Line/Mic In",
                .cpu_dai_name = "omap-mcbsp.4",
                .codec_dai_name = "twl4030-hifi",
-               .platform_name = "omap-pcm-audio",
+               .platform_name = "omap-mcbsp.4",
                .codec_name = "twl4030-codec",
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
                           SND_SOC_DAIFMT_CBS_CFS,
@@ -241,6 +223,11 @@ static struct snd_soc_card snd_soc_card_omap3pandora = {
        .owner = THIS_MODULE,
        .dai_link = omap3pandora_dai,
        .num_links = ARRAY_SIZE(omap3pandora_dai),
+
+       .dapm_widgets = omap3pandora_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(omap3pandora_dapm_widgets),
+       .dapm_routes = omap3pandora_map,
+       .num_dapm_routes = ARRAY_SIZE(omap3pandora_map),
 };
 
 static struct platform_device *omap3pandora_snd_device;
index d03e57d..aa4053b 100644 (file)
@@ -96,7 +96,7 @@ static struct snd_soc_dai_link osk_dai = {
        .stream_name = "AIC23",
        .cpu_dai_name = "omap-mcbsp.1",
        .codec_dai_name = "tlv320aic23-hifi",
-       .platform_name = "omap-pcm-audio",
+       .platform_name = "omap-mcbsp.1",
        .codec_name = "tlv320aic23-codec",
        .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
                   SND_SOC_DAIFMT_CBM_CFM,
index 7fb3d4b..943922c 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/delay.h>
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
 #include <linux/module.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 
 #include "omap-mcbsp.h"
 
-#define RX51_TVOUT_SEL_GPIO            40
-#define RX51_JACK_DETECT_GPIO          177
-#define RX51_ECI_SW_GPIO               182
-/*
- * REVISIT: TWL4030 GPIO base in RX-51. Now statically defined to 192. This
- * gpio is reserved in arch/arm/mach-omap2/board-rx51-peripherals.c
- */
-#define RX51_SPEAKER_AMP_TWL_GPIO      (192 + 7)
-
 enum {
        RX51_JACK_DISABLED,
        RX51_JACK_TVOUT,                /* tv-out with stereo output */
@@ -54,12 +46,21 @@ enum {
        RX51_JACK_HS,                   /* headset: stereo output with mic */
 };
 
+struct rx51_audio_pdata {
+       struct gpio_desc *tvout_selection_gpio;
+       struct gpio_desc *jack_detection_gpio;
+       struct gpio_desc *eci_sw_gpio;
+       struct gpio_desc *speaker_amp_gpio;
+};
+
 static int rx51_spk_func;
 static int rx51_dmic_func;
 static int rx51_jack_func;
 
 static void rx51_ext_control(struct snd_soc_dapm_context *dapm)
 {
+       struct snd_soc_card *card = dapm->card;
+       struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
        int hp = 0, hs = 0, tvout = 0;
 
        switch (rx51_jack_func) {
@@ -93,7 +94,7 @@ static void rx51_ext_control(struct snd_soc_dapm_context *dapm)
        else
                snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic");
 
-       gpio_set_value(RX51_TVOUT_SEL_GPIO, tvout);
+       gpiod_set_value(pdata->tvout_selection_gpio, tvout);
 
        snd_soc_dapm_sync_unlocked(dapm);
 
@@ -154,10 +155,12 @@ static int rx51_set_spk(struct snd_kcontrol *kcontrol,
 static int rx51_spk_event(struct snd_soc_dapm_widget *w,
                          struct snd_kcontrol *k, int event)
 {
-       if (SND_SOC_DAPM_EVENT_ON(event))
-               gpio_set_value_cansleep(RX51_SPEAKER_AMP_TWL_GPIO, 1);
-       else
-               gpio_set_value_cansleep(RX51_SPEAKER_AMP_TWL_GPIO, 0);
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct snd_soc_card *card = dapm->card;
+       struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
+       gpiod_set_raw_value_cansleep(pdata->speaker_amp_gpio,
+                                    !!SND_SOC_DAPM_EVENT_ON(event));
 
        return 0;
 }
@@ -223,7 +226,6 @@ static struct snd_soc_jack rx51_av_jack;
 
 static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = {
        {
-               .gpio = RX51_JACK_DETECT_GPIO,
                .name = "avdet-gpio",
                .report = SND_JACK_HEADSET,
                .invert = 1,
@@ -237,9 +239,6 @@ static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = {
        SND_SOC_DAPM_HP("Headphone Jack", rx51_hp_event),
        SND_SOC_DAPM_MIC("HS Mic", NULL),
        SND_SOC_DAPM_LINE("FM Transmitter", NULL),
-};
-
-static const struct snd_soc_dapm_widget aic34_dapm_widgetsb[] = {
        SND_SOC_DAPM_SPK("Earphone", NULL),
 };
 
@@ -253,9 +252,7 @@ static const struct snd_soc_dapm_route audio_map[] = {
 
        {"DMic Rate 64", NULL, "Mic Bias"},
        {"Mic Bias", NULL, "DMic"},
-};
 
-static const struct snd_soc_dapm_route audio_mapb[] = {
        {"b LINE2R", NULL, "MONO_LOUT"},
        {"Earphone", NULL, "b HPLOUT"},
 
@@ -263,9 +260,11 @@ static const struct snd_soc_dapm_route audio_mapb[] = {
        {"b Mic Bias", NULL, "HS Mic"}
 };
 
-static const char *spk_function[] = {"Off", "On"};
-static const char *input_function[] = {"ADC", "Digital Mic"};
-static const char *jack_function[] = {"Off", "TV-OUT", "Headphone", "Headset"};
+static const char * const spk_function[] = {"Off", "On"};
+static const char * const input_function[] = {"ADC", "Digital Mic"};
+static const char * const jack_function[] = {
+       "Off", "TV-OUT", "Headphone", "Headset"
+};
 
 static const struct soc_enum rx51_enum[] = {
        SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
@@ -281,15 +280,15 @@ static const struct snd_kcontrol_new aic34_rx51_controls[] = {
        SOC_ENUM_EXT("Jack Function", rx51_enum[2],
                     rx51_get_jack, rx51_set_jack),
        SOC_DAPM_PIN_SWITCH("FM Transmitter"),
-};
-
-static const struct snd_kcontrol_new aic34_rx51_controlsb[] = {
        SOC_DAPM_PIN_SWITCH("Earphone"),
 };
 
 static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_card *card = rtd->card;
+       struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
        struct snd_soc_dapm_context *dapm = &codec->dapm;
        int err;
 
@@ -298,57 +297,49 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
        snd_soc_dapm_nc_pin(dapm, "MIC3R");
        snd_soc_dapm_nc_pin(dapm, "LINE1R");
 
-       /* Add RX-51 specific controls */
-       err = snd_soc_add_card_controls(rtd->card, aic34_rx51_controls,
-                                  ARRAY_SIZE(aic34_rx51_controls));
-       if (err < 0)
-               return err;
-
-       /* Add RX-51 specific widgets */
-       snd_soc_dapm_new_controls(dapm, aic34_dapm_widgets,
-                                 ARRAY_SIZE(aic34_dapm_widgets));
-
-       /* Set up RX-51 specific audio path audio_map */
-       snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-
        err = tpa6130a2_add_controls(codec);
-       if (err < 0)
+       if (err < 0) {
+               dev_err(card->dev, "Failed to add TPA6130A2 controls\n");
                return err;
+       }
        snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42);
 
-       err = omap_mcbsp_st_add_controls(rtd);
-       if (err < 0)
+       err = omap_mcbsp_st_add_controls(rtd, 2);
+       if (err < 0) {
+               dev_err(card->dev, "Failed to add MCBSP controls\n");
                return err;
+       }
 
        /* AV jack detection */
        err = snd_soc_jack_new(codec, "AV Jack",
                               SND_JACK_HEADSET | SND_JACK_VIDEOOUT,
                               &rx51_av_jack);
-       if (err)
+       if (err) {
+               dev_err(card->dev, "Failed to add AV Jack\n");
                return err;
+       }
+
+       /* prepare gpio for snd_soc_jack_add_gpios */
+       rx51_av_jack_gpios[0].gpio = desc_to_gpio(pdata->jack_detection_gpio);
+       devm_gpiod_put(card->dev, pdata->jack_detection_gpio);
+
        err = snd_soc_jack_add_gpios(&rx51_av_jack,
                                     ARRAY_SIZE(rx51_av_jack_gpios),
                                     rx51_av_jack_gpios);
+       if (err) {
+               dev_err(card->dev, "Failed to add GPIOs\n");
+               return err;
+       }
 
        return err;
 }
 
-static int rx51_aic34b_init(struct snd_soc_dapm_context *dapm)
+static int rx51_card_remove(struct snd_soc_card *card)
 {
-       int err;
-
-       err = snd_soc_add_card_controls(dapm->card, aic34_rx51_controlsb,
-                                  ARRAY_SIZE(aic34_rx51_controlsb));
-       if (err < 0)
-               return err;
-
-       err = snd_soc_dapm_new_controls(dapm, aic34_dapm_widgetsb,
-                                       ARRAY_SIZE(aic34_dapm_widgetsb));
-       if (err < 0)
-               return 0;
+       snd_soc_jack_free_gpios(&rx51_av_jack, ARRAY_SIZE(rx51_av_jack_gpios),
+                               rx51_av_jack_gpios);
 
-       return snd_soc_dapm_add_routes(dapm, audio_mapb,
-                                      ARRAY_SIZE(audio_mapb));
+       return 0;
 }
 
 /* Digital audio interface glue - connects codec <--> CPU */
@@ -358,7 +349,7 @@ static struct snd_soc_dai_link rx51_dai[] = {
                .stream_name = "AIC34",
                .cpu_dai_name = "omap-mcbsp.2",
                .codec_dai_name = "tlv320aic3x-hifi",
-               .platform_name = "omap-pcm-audio",
+               .platform_name = "omap-mcbsp.2",
                .codec_name = "tlv320aic3x-codec.2-0018",
                .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
                           SND_SOC_DAIFMT_CBM_CFM,
@@ -371,7 +362,6 @@ static struct snd_soc_aux_dev rx51_aux_dev[] = {
        {
                .name = "TLV320AIC34b",
                .codec_name = "tlv320aic3x-codec.2-0019",
-               .init = rx51_aic34b_init,
        },
 };
 
@@ -386,69 +376,158 @@ static struct snd_soc_codec_conf rx51_codec_conf[] = {
 static struct snd_soc_card rx51_sound_card = {
        .name = "RX-51",
        .owner = THIS_MODULE,
+       .remove = rx51_card_remove,
        .dai_link = rx51_dai,
        .num_links = ARRAY_SIZE(rx51_dai),
        .aux_dev = rx51_aux_dev,
        .num_aux_devs = ARRAY_SIZE(rx51_aux_dev),
        .codec_conf = rx51_codec_conf,
        .num_configs = ARRAY_SIZE(rx51_codec_conf),
-};
 
-static struct platform_device *rx51_snd_device;
+       .controls = aic34_rx51_controls,
+       .num_controls = ARRAY_SIZE(aic34_rx51_controls),
+       .dapm_widgets = aic34_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(aic34_dapm_widgets),
+       .dapm_routes = audio_map,
+       .num_dapm_routes = ARRAY_SIZE(audio_map),
+};
 
-static int __init rx51_soc_init(void)
+static int rx51_soc_probe(struct platform_device *pdev)
 {
+       struct rx51_audio_pdata *pdata;
+       struct device_node *np = pdev->dev.of_node;
+       struct snd_soc_card *card = &rx51_sound_card;
        int err;
 
        if (!machine_is_nokia_rx51() && !of_machine_is_compatible("nokia,omap3-n900"))
                return -ENODEV;
 
-       err = gpio_request_one(RX51_TVOUT_SEL_GPIO,
-                              GPIOF_DIR_OUT | GPIOF_INIT_LOW, "tvout_sel");
-       if (err)
-               goto err_gpio_tvout_sel;
-       err = gpio_request_one(RX51_ECI_SW_GPIO,
-                              GPIOF_DIR_OUT | GPIOF_INIT_HIGH, "eci_sw");
-       if (err)
-               goto err_gpio_eci_sw;
-
-       rx51_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!rx51_snd_device) {
-               err = -ENOMEM;
-               goto err1;
+       card->dev = &pdev->dev;
+
+       if (np) {
+               struct device_node *dai_node;
+
+               dai_node = of_parse_phandle(np, "nokia,cpu-dai", 0);
+               if (!dai_node) {
+                       dev_err(&pdev->dev, "McBSP node is not provided\n");
+                       return -EINVAL;
+               }
+               rx51_dai[0].cpu_dai_name = NULL;
+               rx51_dai[0].platform_name = NULL;
+               rx51_dai[0].cpu_of_node = dai_node;
+               rx51_dai[0].platform_of_node = dai_node;
+
+               dai_node = of_parse_phandle(np, "nokia,audio-codec", 0);
+               if (!dai_node) {
+                       dev_err(&pdev->dev, "Codec node is not provided\n");
+                       return -EINVAL;
+               }
+               rx51_dai[0].codec_name = NULL;
+               rx51_dai[0].codec_of_node = dai_node;
+
+               dai_node = of_parse_phandle(np, "nokia,audio-codec", 1);
+               if (!dai_node) {
+                       dev_err(&pdev->dev, "Auxiliary Codec node is not provided\n");
+                       return -EINVAL;
+               }
+               rx51_aux_dev[0].codec_name = NULL;
+               rx51_aux_dev[0].codec_of_node = dai_node;
+               rx51_codec_conf[0].dev_name = NULL;
+               rx51_codec_conf[0].of_node = dai_node;
+
+               dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0);
+               if (!dai_node) {
+                       dev_err(&pdev->dev, "Headphone amplifier node is not provided\n");
+                       return -EINVAL;
+               }
+
+               /* TODO: tpa6130a2a driver supports only a single instance, so
+                * this driver ignores the headphone-amplifier node for now.
+                * It's already mandatory in the DT binding to be future proof.
+                */
        }
 
-       platform_set_drvdata(rx51_snd_device, &rx51_sound_card);
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (pdata == NULL) {
+               dev_err(card->dev, "failed to create private data\n");
+               return -ENOMEM;
+       }
+       snd_soc_card_set_drvdata(card, pdata);
 
-       err = platform_device_add(rx51_snd_device);
-       if (err)
-               goto err2;
+       pdata->tvout_selection_gpio = devm_gpiod_get(card->dev,
+                                                    "tvout-selection");
+       if (IS_ERR(pdata->tvout_selection_gpio)) {
+               dev_err(card->dev, "could not get tvout selection gpio\n");
+               return PTR_ERR(pdata->tvout_selection_gpio);
+       }
 
-       return 0;
-err2:
-       platform_device_put(rx51_snd_device);
-err1:
-       gpio_free(RX51_ECI_SW_GPIO);
-err_gpio_eci_sw:
-       gpio_free(RX51_TVOUT_SEL_GPIO);
-err_gpio_tvout_sel:
+       err = gpiod_direction_output(pdata->tvout_selection_gpio, 0);
+       if (err) {
+               dev_err(card->dev, "could not setup tvout selection gpio\n");
+               return err;
+       }
 
-       return err;
-}
+       pdata->jack_detection_gpio = devm_gpiod_get(card->dev,
+                                                   "jack-detection");
+       if (IS_ERR(pdata->jack_detection_gpio)) {
+               dev_err(card->dev, "could not get jack detection gpio\n");
+               return PTR_ERR(pdata->jack_detection_gpio);
+       }
 
-static void __exit rx51_soc_exit(void)
-{
-       snd_soc_jack_free_gpios(&rx51_av_jack, ARRAY_SIZE(rx51_av_jack_gpios),
-                               rx51_av_jack_gpios);
+       pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch");
+       if (IS_ERR(pdata->eci_sw_gpio)) {
+               dev_err(card->dev, "could not get eci switch gpio\n");
+               return PTR_ERR(pdata->eci_sw_gpio);
+       }
 
-       platform_device_unregister(rx51_snd_device);
-       gpio_free(RX51_ECI_SW_GPIO);
-       gpio_free(RX51_TVOUT_SEL_GPIO);
+       err = gpiod_direction_output(pdata->eci_sw_gpio, 1);
+       if (err) {
+               dev_err(card->dev, "could not setup eci switch gpio\n");
+               return err;
+       }
+
+       pdata->speaker_amp_gpio = devm_gpiod_get(card->dev,
+                                                "speaker-amplifier");
+       if (IS_ERR(pdata->speaker_amp_gpio)) {
+               dev_err(card->dev, "could not get speaker enable gpio\n");
+               return PTR_ERR(pdata->speaker_amp_gpio);
+       }
+
+       err = gpiod_direction_output(pdata->speaker_amp_gpio, 0);
+       if (err) {
+               dev_err(card->dev, "could not setup speaker enable gpio\n");
+               return err;
+       }
+
+       err = devm_snd_soc_register_card(card->dev, card);
+       if (err) {
+               dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", err);
+               return err;
+       }
+
+       return 0;
 }
 
-module_init(rx51_soc_init);
-module_exit(rx51_soc_exit);
+#if defined(CONFIG_OF)
+static const struct of_device_id rx51_audio_of_match[] = {
+       { .compatible = "nokia,n900-audio", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rx51_audio_of_match);
+#endif
+
+static struct platform_driver rx51_soc_driver = {
+       .driver = {
+               .name = "rx51-audio",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(rx51_audio_of_match),
+       },
+       .probe = rx51_soc_probe,
+};
+
+module_platform_driver(rx51_soc_driver);
 
 MODULE_AUTHOR("Nokia Corporation");
 MODULE_DESCRIPTION("ALSA SoC Nokia RX-51");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rx51-audio");
index 6473052..6acb225 100644 (file)
@@ -140,7 +140,7 @@ config SND_PXA910_SOC
 
 config SND_SOC_TTC_DKB
        bool "SoC Audio support for TTC DKB"
-       depends on SND_PXA910_SOC && MACH_TTC_DKB
+       depends on SND_PXA910_SOC && MACH_TTC_DKB && I2C=y
        select PXA_SSP
        select SND_PXA_SOC_SSP
        select SND_MMP_SOC
index 08acdc2..c8dd53f 100644 (file)
@@ -50,11 +50,6 @@ static int brownstone_wm8994_init(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
 
-       snd_soc_dapm_enable_pin(dapm, "Ext Spk");
-       snd_soc_dapm_enable_pin(dapm, "Headset Stereophone");
-       snd_soc_dapm_enable_pin(dapm, "Headset Mic");
-       snd_soc_dapm_enable_pin(dapm, "Main Mic");
-
        /* set endpoints to not connected */
        snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
        snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
@@ -70,8 +65,6 @@ static int brownstone_wm8994_init(struct snd_soc_pcm_runtime *rtd)
        snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
        snd_soc_dapm_nc_pin(dapm, "IN2LN");
 
-       snd_soc_dapm_sync(dapm);
-
        return 0;
 }
 
index dcc9b04..05559a7 100644 (file)
@@ -152,6 +152,13 @@ static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
        return err;
 }
 
+static int hx4700_card_remove(struct snd_soc_card *card)
+{
+       snd_soc_jack_free_gpios(&hs_jack, 1, &hs_jack_gpio);
+
+       return 0;
+}
+
 /* hx4700 digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link hx4700_dai = {
        .name = "ak4641",
@@ -170,6 +177,7 @@ static struct snd_soc_dai_link hx4700_dai = {
 static struct snd_soc_card snd_soc_card_hx4700 = {
        .name                   = "iPAQ hx4700",
        .owner                  = THIS_MODULE,
+       .remove                 = hx4700_card_remove,
        .dai_link               = &hx4700_dai,
        .num_links              = 1,
        .dapm_widgets           = hx4700_dapm_widgets,
@@ -206,7 +214,6 @@ static int hx4700_audio_probe(struct platform_device *pdev)
 
 static int hx4700_audio_remove(struct platform_device *pdev)
 {
-       snd_soc_jack_free_gpios(&hs_jack, 1, &hs_jack_gpio);
        snd_soc_unregister_card(&snd_soc_card_hx4700);
 
        gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);
index 3284c4b..17f9521 100644 (file)
@@ -79,14 +79,6 @@ static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_dapm_context *dapm = &codec->dapm;
        int err;
 
-       /* connected pins */
-       if (machine_is_palmld())
-               snd_soc_dapm_enable_pin(dapm, "MIC1");
-       snd_soc_dapm_enable_pin(dapm, "HPOUTL");
-       snd_soc_dapm_enable_pin(dapm, "HPOUTR");
-       snd_soc_dapm_enable_pin(dapm, "LOUT2");
-       snd_soc_dapm_enable_pin(dapm, "ROUT2");
-
        /* not connected pins */
        snd_soc_dapm_nc_pin(dapm, "OUT3");
        snd_soc_dapm_nc_pin(dapm, "MONOOUT");
index c6bdc6c..21f3400 100644 (file)
@@ -230,7 +230,6 @@ static int poodle_wm8731_init(struct snd_soc_pcm_runtime *rtd)
 
        snd_soc_dapm_nc_pin(dapm, "LLINEIN");
        snd_soc_dapm_nc_pin(dapm, "RLINEIN");
-       snd_soc_dapm_enable_pin(dapm, "MICIN");
 
        return 0;
 }
index a3119a0..199a8b3 100644 (file)
@@ -34,8 +34,6 @@
 #include <sound/pxa2xx-lib.h>
 #include <sound/dmaengine_pcm.h>
 
-#include <mach/hardware.h>
-
 #include "../../arm/pxa2xx-pcm.h"
 #include "pxa-ssp.h"
 
@@ -810,6 +808,7 @@ static const struct snd_soc_component_driver pxa_ssp_component = {
 #ifdef CONFIG_OF
 static const struct of_device_id pxa_ssp_of_ids[] = {
        { .compatible = "mrvl,pxa-ssp-dai" },
+       {}
 };
 #endif
 
index d58b09f..42f2f01 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/dmaengine.h>
 #include <linux/of.h>
 
+#include <mach/dma.h>
+
 #include <sound/core.h>
 #include <sound/soc.h>
 #include <sound/pxa2xx-lib.h>
index 0b535b5..9d7c5b7 100644 (file)
@@ -78,10 +78,6 @@ static int ttc_pm860x_init(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
 
-       /* connected pins */
-       snd_soc_dapm_enable_pin(dapm, "Ext Speaker");
-       snd_soc_dapm_enable_pin(dapm, "Ext Mic 1");
-       snd_soc_dapm_enable_pin(dapm, "Ext Mic 3");
        snd_soc_dapm_disable_pin(dapm, "Headset Mic 2");
        snd_soc_dapm_disable_pin(dapm, "Headset Stereophone");
 
index f2e2891..753b8c9 100644 (file)
@@ -64,6 +64,7 @@ config SND_SOC_SAMSUNG_JIVE_WM8750
 config SND_SOC_SAMSUNG_SMDK_WM8580
        tristate "SoC I2S Audio support for WM8580 on SMDK"
        depends on SND_SOC_SAMSUNG && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDK6440 || MACH_SMDK6450 || MACH_SMDKV210 || MACH_SMDKC110)
+       depends on REGMAP_I2C
        select SND_SOC_WM8580
        select SND_SAMSUNG_I2S
        help
@@ -115,21 +116,21 @@ config SND_SOC_SAMSUNG_SIMTEC
 
 config SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23
        tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards"
-       depends on SND_SOC_SAMSUNG && ARCH_S3C24XX
+       depends on SND_SOC_SAMSUNG && ARCH_S3C24XX && I2C
        select SND_S3C24XX_I2S
        select SND_SOC_TLV320AIC23_I2C
        select SND_SOC_SAMSUNG_SIMTEC
 
 config SND_SOC_SAMSUNG_SIMTEC_HERMES
        tristate "SoC I2S Audio support for Simtec Hermes board"
-       depends on SND_SOC_SAMSUNG && ARCH_S3C24XX
+       depends on SND_SOC_SAMSUNG && ARCH_S3C24XX && I2C
        select SND_S3C24XX_I2S
        select SND_SOC_TLV320AIC3X
        select SND_SOC_SAMSUNG_SIMTEC
 
 config SND_SOC_SAMSUNG_H1940_UDA1380
        tristate "Audio support for the HP iPAQ H1940"
-       depends on SND_SOC_SAMSUNG && ARCH_H1940
+       depends on SND_SOC_SAMSUNG && ARCH_H1940 && I2C
        select SND_S3C24XX_I2S
        select SND_SOC_UDA1380
        help
@@ -137,7 +138,7 @@ config SND_SOC_SAMSUNG_H1940_UDA1380
 
 config SND_SOC_SAMSUNG_RX1950_UDA1380
        tristate "Audio support for the HP iPAQ RX1950"
-       depends on SND_SOC_SAMSUNG && MACH_RX1950
+       depends on SND_SOC_SAMSUNG && MACH_RX1950 && I2C
        select SND_S3C24XX_I2S
        select SND_SOC_UDA1380
        help
@@ -178,6 +179,7 @@ config SND_SOC_SAMSUNG_SMDK_SPDIF
 config SND_SOC_SMDK_WM8580_PCM
        tristate "SoC PCM Audio support for WM8580 on SMDK"
        depends on SND_SOC_SAMSUNG && (MACH_SMDK6450 || MACH_SMDKV210 || MACH_SMDKC110)
+       depends on REGMAP_I2C
        select SND_SOC_WM8580
        select SND_SAMSUNG_PCM
        help
@@ -204,7 +206,7 @@ config SND_SOC_SPEYSIDE
 
 config SND_SOC_TOBERMORY
        tristate "Audio support for Wolfson Tobermory"
-       depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
+       depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && INPUT
        select SND_SAMSUNG_I2S
        select SND_SOC_WM8962
 
@@ -231,3 +233,13 @@ config SND_SOC_LITTLEMILL
        select SND_SAMSUNG_I2S
        select MFD_WM8994
        select SND_SOC_WM8994
+
+config SND_SOC_SNOW
+       tristate "Audio support for Google Snow boards"
+       depends on SND_SOC_SAMSUNG
+       select SND_SOC_MAX98090
+       select SND_SOC_MAX98095
+       select SND_SAMSUNG_I2S
+       help
+         Say Y if you want to add audio support for various Snow
+         boards based on Exynos5 series of SoCs.
index 86715d8..6d0212b 100644 (file)
@@ -34,6 +34,7 @@ snd-soc-h1940-uda1380-objs := h1940_uda1380.o
 snd-soc-rx1950-uda1380-objs := rx1950_uda1380.o
 snd-soc-smdk-wm8580-objs := smdk_wm8580.o
 snd-soc-smdk-wm8994-objs := smdk_wm8994.o
+snd-soc-snow-objs := snow.o
 snd-soc-smdk-wm9713-objs := smdk_wm9713.o
 snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o
 snd-soc-goni-wm8994-objs := goni_wm8994.o
@@ -58,6 +59,7 @@ obj-$(CONFIG_SND_SOC_SAMSUNG_H1940_UDA1380) += snd-soc-h1940-uda1380.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_RX1950_UDA1380) += snd-soc-rx1950-uda1380.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM8580) += snd-soc-smdk-wm8580.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM8994) += snd-soc-smdk-wm8994.o
+obj-$(CONFIG_SND_SOC_SNOW) += snd-soc-snow.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM9713) += snd-soc-smdk-wm9713.o
 obj-$(CONFIG_SND_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o
index 76b072b..68d9303 100644 (file)
@@ -433,7 +433,7 @@ static int s3c_ac97_probe(struct platform_device *pdev)
                goto err4;
        }
 
-       ret = snd_soc_register_component(&pdev->dev, &s3c_ac97_component,
+       ret = devm_snd_soc_register_component(&pdev->dev, &s3c_ac97_component,
                                         s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
        if (ret)
                goto err5;
@@ -441,12 +441,10 @@ static int s3c_ac97_probe(struct platform_device *pdev)
        ret = samsung_asoc_dma_platform_register(&pdev->dev);
        if (ret) {
                dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
-               goto err6;
+               goto err5;
        }
 
        return 0;
-err6:
-       snd_soc_unregister_component(&pdev->dev);
 err5:
        free_irq(irq_res->start, NULL);
 err4:
@@ -461,9 +459,6 @@ static int s3c_ac97_remove(struct platform_device *pdev)
 {
        struct resource *irq_res;
 
-       samsung_asoc_dma_platform_unregister(&pdev->dev);
-       snd_soc_unregister_component(&pdev->dev);
-
        irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (irq_res)
                free_irq(irq_res->start, NULL);
index 84f5d8b..5b21207 100644 (file)
@@ -433,22 +433,13 @@ static int bells_probe(struct platform_device *pdev)
 
        bells_cards[pdev->id].dev = &pdev->dev;
 
-       ret = snd_soc_register_card(&bells_cards[pdev->id]);
-       if (ret) {
+       ret = devm_snd_soc_register_card(&pdev->dev, &bells_cards[pdev->id]);
+       if (ret)
                dev_err(&pdev->dev,
                        "snd_soc_register_card(%s) failed: %d\n",
                        bells_cards[pdev->id].name, ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int bells_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_card(&bells_cards[pdev->id]);
 
-       return 0;
+       return ret;
 }
 
 static struct platform_driver bells_driver = {
@@ -458,7 +449,6 @@ static struct platform_driver bells_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = bells_probe,
-       .remove = bells_remove,
 };
 
 module_platform_driver(bells_driver);
index dc09b71..d9dc7bc 100644 (file)
@@ -445,16 +445,10 @@ EXPORT_SYMBOL_GPL(samsung_asoc_init_dma_data);
 
 int samsung_asoc_dma_platform_register(struct device *dev)
 {
-       return snd_soc_register_platform(dev, &samsung_asoc_platform);
+       return devm_snd_soc_register_platform(dev, &samsung_asoc_platform);
 }
 EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
 
-void samsung_asoc_dma_platform_unregister(struct device *dev)
-{
-       snd_soc_unregister_platform(dev);
-}
-EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_unregister);
-
 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
 MODULE_DESCRIPTION("Samsung ASoC DMA Driver");
 MODULE_LICENSE("GPL");
index ad7c0f0..070ab0f 100644 (file)
@@ -33,6 +33,5 @@ void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
                                struct s3c_dma_params *playback,
                                struct s3c_dma_params *capture);
 int samsung_asoc_dma_platform_register(struct device *dev);
-void samsung_asoc_dma_platform_unregister(struct device *dev);
 
 #endif
index 750ce58..a0e4e79 100644 (file)
@@ -66,18 +66,13 @@ EXPORT_SYMBOL_GPL(samsung_asoc_init_dma_data);
 
 int samsung_asoc_dma_platform_register(struct device *dev)
 {
-       return snd_dmaengine_pcm_register(dev, &samsung_dmaengine_pcm_config,
-                                         SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME |
-                                         SND_DMAENGINE_PCM_FLAG_COMPAT);
+       return devm_snd_dmaengine_pcm_register(dev,
+                       &samsung_dmaengine_pcm_config,
+                       SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME |
+                       SND_DMAENGINE_PCM_FLAG_COMPAT);
 }
 EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
 
-void samsung_asoc_dma_platform_unregister(struct device *dev)
-{
-       return snd_dmaengine_pcm_unregister(dev);
-}
-EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_unregister);
-
 MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
 MODULE_DESCRIPTION("Samsung dmaengine ASoC driver");
 MODULE_LICENSE("GPL");
index 415ad81..9506d76 100644 (file)
@@ -274,8 +274,8 @@ static int __init goni_init(void)
                return -ENOMEM;
 
        /* register voice DAI here */
-       ret = snd_soc_register_component(&goni_snd_device->dev, &voice_component,
-                                        &voice_dai, 1);
+       ret = devm_snd_soc_register_component(&goni_snd_device->dev,
+                       &voice_component, &voice_dai, 1);
        if (ret) {
                platform_device_put(goni_snd_device);
                return ret;
@@ -284,17 +284,14 @@ static int __init goni_init(void)
        platform_set_drvdata(goni_snd_device, &goni);
        ret = platform_device_add(goni_snd_device);
 
-       if (ret) {
-               snd_soc_unregister_component(&goni_snd_device->dev);
+       if (ret)
                platform_device_put(goni_snd_device);
-       }
 
        return ret;
 }
 
 static void __exit goni_exit(void)
 {
-       snd_soc_unregister_component(&goni_snd_device->dev);
        platform_device_unregister(goni_snd_device);
 }
 
index 88b09e0..f2d7980 100644 (file)
@@ -176,11 +176,6 @@ static struct platform_device *s3c24xx_snd_device;
 static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
-       snd_soc_dapm_enable_pin(dapm, "Speaker");
-       snd_soc_dapm_enable_pin(dapm, "Mic Jack");
 
        snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
                &hp_jack);
@@ -194,6 +189,14 @@ static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd)
        return 0;
 }
 
+static int h1940_uda1380_card_remove(struct snd_soc_card *card)
+{
+       snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
+               hp_jack_gpios);
+
+       return 0;
+}
+
 /* s3c24xx digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link h1940_uda1380_dai[] = {
        {
@@ -211,6 +214,7 @@ static struct snd_soc_dai_link h1940_uda1380_dai[] = {
 static struct snd_soc_card h1940_asoc = {
        .name = "h1940",
        .owner = THIS_MODULE,
+       .remove = h1940_uda1380_card_remove,
        .dai_link = h1940_uda1380_dai,
        .num_links = ARRAY_SIZE(h1940_uda1380_dai),
 
@@ -262,8 +266,6 @@ err_out:
 static void __exit h1940_exit(void)
 {
        platform_device_unregister(s3c24xx_snd_device);
-       snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
-               hp_jack_gpios);
        gpio_free(S3C_GPIO_END + 9);
 }
 
index 048ead9..2ac76fa 100644 (file)
@@ -451,6 +451,10 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
        u32 mod = readl(i2s->addr + I2SMOD);
 
        switch (clk_id) {
+       case SAMSUNG_I2S_OPCLK:
+               mod &= ~MOD_OPCLK_MASK;
+               mod |= dir;
+               break;
        case SAMSUNG_I2S_CDCLK:
                /* Shouldn't matter in GATING(CLOCK_IN) mode */
                if (dir == SND_SOC_CLOCK_IN)
@@ -484,7 +488,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
                        clk_id = 1;
 
                if (!any_active(i2s)) {
-                       if (i2s->op_clk) {
+                       if (i2s->op_clk && !IS_ERR(i2s->op_clk)) {
                                if ((clk_id && !(mod & MOD_IMS_SYSMUX)) ||
                                        (!clk_id && (mod & MOD_IMS_SYSMUX))) {
                                        clk_disable_unprepare(i2s->op_clk);
@@ -502,6 +506,10 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
                        else
                                i2s->op_clk = clk_get(&i2s->pdev->dev,
                                                "i2s_opclk0");
+
+                       if (WARN_ON(IS_ERR(i2s->op_clk)))
+                               return PTR_ERR(i2s->op_clk);
+
                        clk_prepare_enable(i2s->op_clk);
                        i2s->rclk_srcrate = clk_get_rate(i2s->op_clk);
 
@@ -668,8 +676,8 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
        if (is_manager(i2s))
                mod &= ~MOD_BLC_MASK;
 
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S8:
+       switch (params_width(params)) {
+       case 8:
                if (is_secondary(i2s))
                        mod |= MOD_BLCS_8BIT;
                else
@@ -677,7 +685,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
                if (is_manager(i2s))
                        mod |= MOD_BLC_8BIT;
                break;
-       case SNDRV_PCM_FORMAT_S16_LE:
+       case 16:
                if (is_secondary(i2s))
                        mod |= MOD_BLCS_16BIT;
                else
@@ -685,7 +693,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
                if (is_manager(i2s))
                        mod |= MOD_BLC_16BIT;
                break;
-       case SNDRV_PCM_FORMAT_S24_LE:
+       case 24:
                if (is_secondary(i2s))
                        mod |= MOD_BLCS_24BIT;
                else
@@ -724,9 +732,6 @@ static int i2s_startup(struct snd_pcm_substream *substream,
        else
                i2s->mode |= DAI_MANAGER;
 
-       /* Enforce set_sysclk in Master mode */
-       i2s->rclk_srcrate = 0;
-
        if (!any_active(i2s) && (i2s->quirks & QUIRK_NEED_RSTCLR))
                writel(CON_RSTCLR, i2s->addr + I2SCON);
 
@@ -984,6 +989,7 @@ probe_exit:
        /* Reset any constraint on RFS and BFS */
        i2s->rfs = 0;
        i2s->bfs = 0;
+       i2s->rclk_srcrate = 0;
        i2s_txctrl(i2s, 0);
        i2s_rxctrl(i2s, 0);
        i2s_fifo(i2s, FIC_TXFLUSH);
@@ -1293,8 +1299,6 @@ static int samsung_i2s_remove(struct platform_device *pdev)
        i2s->pri_dai = NULL;
        i2s->sec_dai = NULL;
 
-       samsung_asoc_dma_platform_unregister(&pdev->dev);
-
        return 0;
 }
 
index 7966afc..21ff24e 100644 (file)
@@ -18,5 +18,6 @@
 #define SAMSUNG_I2S_RCLKSRC_0  0
 #define SAMSUNG_I2S_RCLKSRC_1  1
 #define SAMSUNG_I2S_CDCLK              2
+#define SAMSUNG_I2S_OPCLK              3
 
 #endif /* __SND_SOC_SAMSUNG_I2S_H */
index 3d5cf15..8cc5770 100644 (file)
@@ -274,7 +274,7 @@ static irqreturn_t iis_irq(int irqno, void *dev_id)
 
                addr = readl(idma.regs + I2SLVL0ADDR) - idma.lp_tx_addr;
                addr += prtd->periodsz;
-               addr %= (prtd->end - prtd->start);
+               addr %= (u32)(prtd->end - prtd->start);
                addr += idma.lp_tx_addr;
 
                writel(addr, idma.regs + I2SLVL0ADDR);
@@ -413,13 +413,7 @@ static int asoc_idma_platform_probe(struct platform_device *pdev)
        if (idma_irq < 0)
                return idma_irq;
 
-       return snd_soc_register_platform(&pdev->dev, &asoc_idma_platform);
-}
-
-static int asoc_idma_platform_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_platform(&pdev->dev);
-       return 0;
+       return devm_snd_soc_register_platform(&pdev->dev, &asoc_idma_platform);
 }
 
 static struct platform_driver asoc_idma_driver = {
@@ -429,7 +423,6 @@ static struct platform_driver asoc_idma_driver = {
        },
 
        .probe = asoc_idma_platform_probe,
-       .remove = asoc_idma_platform_remove,
 };
 
 module_platform_driver(asoc_idma_driver);
index bfb91f3..840787e 100644 (file)
@@ -304,23 +304,12 @@ static int littlemill_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
-       if (ret) {
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
+       if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int littlemill_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
 
-       snd_soc_unregister_card(card);
-
-       return 0;
+       return ret;
 }
 
 static struct platform_driver littlemill_driver = {
@@ -330,7 +319,6 @@ static struct platform_driver littlemill_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = littlemill_probe,
-       .remove = littlemill_remove,
 };
 
 module_platform_driver(littlemill_driver);
index 570cf52..bd5f0d6 100644 (file)
@@ -187,23 +187,12 @@ static int lowland_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
-       if (ret) {
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
+       if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int lowland_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
 
-       snd_soc_unregister_card(card);
-
-       return 0;
+       return ret;
 }
 
 static struct platform_driver lowland_driver = {
@@ -213,7 +202,6 @@ static struct platform_driver lowland_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = lowland_probe,
-       .remove = lowland_remove,
 };
 
 module_platform_driver(lowland_driver);
index b080033..9b4a09f 100644 (file)
@@ -271,15 +271,8 @@ static const struct snd_kcontrol_new neo1973_wm8753_controls[] = {
 
 static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_card *card = rtd->card;
 
-       /* set up NC codec pins */
-       snd_soc_dapm_nc_pin(&codec->dapm, "OUT3");
-       snd_soc_dapm_nc_pin(&codec->dapm, "OUT4");
-       snd_soc_dapm_nc_pin(&codec->dapm, "LINE1");
-       snd_soc_dapm_nc_pin(&codec->dapm, "LINE2");
-
        /* set endpoints to default off mode */
        snd_soc_dapm_disable_pin(&card->dapm, "GSM Line Out");
        snd_soc_dapm_disable_pin(&card->dapm, "GSM Line In");
@@ -355,6 +348,7 @@ static struct snd_soc_card neo1973 = {
        .num_dapm_widgets = ARRAY_SIZE(neo1973_wm8753_dapm_widgets),
        .dapm_routes = neo1973_wm8753_routes,
        .num_dapm_routes = ARRAY_SIZE(neo1973_wm8753_routes),
+       .fully_routed = true,
 };
 
 static struct platform_device *neo1973_snd_device;
index ab54e29..4c5f97f 100644 (file)
@@ -283,8 +283,8 @@ static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
        dev_dbg(pcm->dev, "Entered %s\n", __func__);
 
        /* Strictly check for sample size */
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S16_LE:
+       switch (params_width(params)) {
+       case 16:
                break;
        default:
                return -EINVAL;
@@ -542,7 +542,7 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
        /* Default is 128fs */
        pcm->sclk_per_fs = 128;
 
-       pcm->cclk = clk_get(&pdev->dev, "audio-bus");
+       pcm->cclk = devm_clk_get(&pdev->dev, "audio-bus");
        if (IS_ERR(pcm->cclk)) {
                dev_err(&pdev->dev, "failed to get audio-bus\n");
                ret = PTR_ERR(pcm->cclk);
@@ -567,7 +567,7 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
                goto err3;
        }
 
-       pcm->pclk = clk_get(&pdev->dev, "pcm");
+       pcm->pclk = devm_clk_get(&pdev->dev, "pcm");
        if (IS_ERR(pcm->pclk)) {
                dev_err(&pdev->dev, "failed to get pcm_clock\n");
                ret = -ENOENT;
@@ -588,7 +588,7 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
 
        pm_runtime_enable(&pdev->dev);
 
-       ret = snd_soc_register_component(&pdev->dev, &s3c_pcm_component,
+       ret = devm_snd_soc_register_component(&pdev->dev, &s3c_pcm_component,
                                         &s3c_pcm_dai[pdev->id], 1);
        if (ret != 0) {
                dev_err(&pdev->dev, "failed to get register DAI: %d\n", ret);
@@ -598,23 +598,19 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
        ret = samsung_asoc_dma_platform_register(&pdev->dev);
        if (ret) {
                dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
-               goto err6;
+               goto err5;
        }
 
        return 0;
 
-err6:
-       snd_soc_unregister_component(&pdev->dev);
 err5:
        clk_disable_unprepare(pcm->pclk);
-       clk_put(pcm->pclk);
 err4:
        iounmap(pcm->regs);
 err3:
        release_mem_region(mem_res->start, resource_size(mem_res));
 err2:
        clk_disable_unprepare(pcm->cclk);
-       clk_put(pcm->cclk);
 err1:
        return ret;
 }
@@ -624,9 +620,6 @@ static int s3c_pcm_dev_remove(struct platform_device *pdev)
        struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id];
        struct resource *mem_res;
 
-       samsung_asoc_dma_platform_unregister(&pdev->dev);
-       snd_soc_unregister_component(&pdev->dev);
-
        pm_runtime_disable(&pdev->dev);
 
        iounmap(pcm->regs);
@@ -636,8 +629,6 @@ static int s3c_pcm_dev_remove(struct platform_device *pdev)
 
        clk_disable_unprepare(pcm->cclk);
        clk_disable_unprepare(pcm->pclk);
-       clk_put(pcm->pclk);
-       clk_put(pcm->cclk);
 
        return 0;
 }
index 2982d9e..37688eb 100644 (file)
@@ -31,6 +31,7 @@
 #include "s3c24xx-i2s.h"
 
 static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd);
+static int rx1950_uda1380_card_remove(struct snd_soc_card *card);
 static int rx1950_startup(struct snd_pcm_substream *substream);
 static int rx1950_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params);
@@ -116,6 +117,7 @@ static const struct snd_soc_dapm_route audio_map[] = {
 static struct snd_soc_card rx1950_asoc = {
        .name = "rx1950",
        .owner = THIS_MODULE,
+       .remove = rx1950_uda1380_card_remove,
        .dai_link = rx1950_uda1380_dai,
        .num_links = ARRAY_SIZE(rx1950_uda1380_dai),
 
@@ -221,11 +223,6 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream,
 static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
-       snd_soc_dapm_enable_pin(dapm, "Speaker");
-       snd_soc_dapm_enable_pin(dapm, "Mic Jack");
 
        snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
                &hp_jack);
@@ -239,6 +236,14 @@ static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd)
        return 0;
 }
 
+static int rx1950_uda1380_card_remove(struct snd_soc_card *card)
+{
+       snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
+               hp_jack_gpios);
+
+       return 0;
+}
+
 static int __init rx1950_init(void)
 {
        int ret;
@@ -283,8 +288,6 @@ err_gpio:
 static void __exit rx1950_exit(void)
 {
        platform_device_unregister(s3c24xx_snd_device);
-       snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
-               hp_jack_gpios);
        gpio_free(S3C2410_GPA(1));
 }
 
index 79e7efb..0ff4bbe 100644 (file)
@@ -322,13 +322,13 @@ static int s3c_i2sv2_hw_params(struct snd_pcm_substream *substream,
 
        iismod &= ~S3C64XX_IISMOD_BLC_MASK;
        /* Sample size */
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S8:
+       switch (params_width(params)) {
+       case 8:
                iismod |= S3C64XX_IISMOD_BLC_8BIT;
                break;
-       case SNDRV_PCM_FORMAT_S16_LE:
+       case 16:
                break;
-       case SNDRV_PCM_FORMAT_S24_LE:
+       case 24:
                iismod |= S3C64XX_IISMOD_BLC_24BIT;
                break;
        }
@@ -745,7 +745,7 @@ int s3c_i2sv2_register_component(struct device *dev, int id,
        dai_drv->suspend = s3c2412_i2s_suspend;
        dai_drv->resume = s3c2412_i2s_resume;
 
-       return snd_soc_register_component(dev, cmp_drv, dai_drv, 1);
+       return devm_snd_soc_register_component(dev, cmp_drv, dai_drv, 1);
 }
 EXPORT_SYMBOL_GPL(s3c_i2sv2_register_component);
 
index e9bb5d7..08c059b 100644 (file)
@@ -120,11 +120,11 @@ static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
        iismod = readl(i2s->regs + S3C2412_IISMOD);
        pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
 
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S8:
+       switch (params_width(params)) {
+       case 8:
                iismod |= S3C2412_IISMOD_8BIT;
                break;
-       case SNDRV_PCM_FORMAT_S16_LE:
+       case 16:
                iismod &= ~S3C2412_IISMOD_8BIT;
                break;
        }
@@ -179,27 +179,14 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev)
        }
 
        ret = samsung_asoc_dma_platform_register(&pdev->dev);
-       if (ret) {
+       if (ret)
                pr_err("failed to register the DMA: %d\n", ret);
-               goto err;
-       }
 
-       return 0;
-err:
-       snd_soc_unregister_component(&pdev->dev);
        return ret;
 }
 
-static int s3c2412_iis_dev_remove(struct platform_device *pdev)
-{
-       samsung_asoc_dma_platform_unregister(&pdev->dev);
-       snd_soc_unregister_component(&pdev->dev);
-       return 0;
-}
-
 static struct platform_driver s3c2412_iis_driver = {
        .probe  = s3c2412_iis_dev_probe,
-       .remove = s3c2412_iis_dev_remove,
        .driver = {
                .name = "s3c2412-iis",
                .owner = THIS_MODULE,
index d7b8457..9aba9fb 100644 (file)
@@ -248,12 +248,12 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
        iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
        pr_debug("hw_params r: IISMOD: %x\n", iismod);
 
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S8:
+       switch (params_width(params)) {
+       case 8:
                iismod &= ~S3C2410_IISMOD_16BIT;
                dma_data->dma_size = 1;
                break;
-       case SNDRV_PCM_FORMAT_S16_LE:
+       case 16:
                iismod |= S3C2410_IISMOD_16BIT;
                dma_data->dma_size = 2;
                break;
@@ -475,35 +475,22 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
 {
        int ret = 0;
 
-       ret = snd_soc_register_component(&pdev->dev, &s3c24xx_i2s_component,
-                                        &s3c24xx_i2s_dai, 1);
+       ret = devm_snd_soc_register_component(&pdev->dev,
+                       &s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1);
        if (ret) {
                pr_err("failed to register the dai\n");
                return ret;
        }
 
        ret = samsung_asoc_dma_platform_register(&pdev->dev);
-       if (ret) {
+       if (ret)
                pr_err("failed to register the dma: %d\n", ret);
-               goto err;
-       }
 
-       return 0;
-err:
-       snd_soc_unregister_component(&pdev->dev);
        return ret;
 }
 
-static int s3c24xx_iis_dev_remove(struct platform_device *pdev)
-{
-       samsung_asoc_dma_platform_unregister(&pdev->dev);
-       snd_soc_unregister_component(&pdev->dev);
-       return 0;
-}
-
 static struct platform_driver s3c24xx_iis_driver = {
        .probe  = s3c24xx_iis_dev_probe,
-       .remove = s3c24xx_iis_dev_remove,
        .driver = {
                .name = "s3c24xx-iis",
                .owner = THIS_MODULE,
index d8a0543..2d30b7b 100644 (file)
@@ -63,14 +63,6 @@ static const struct snd_soc_dapm_route base_map[] = {
 */
 static int simtec_hermes_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
-       snd_soc_dapm_enable_pin(dapm, "Line In");
-       snd_soc_dapm_enable_pin(dapm, "Line Out");
-       snd_soc_dapm_enable_pin(dapm, "Mic Jack");
-
        simtec_audio_init(rtd);
 
        return 0;
index 1ac0d7a..83f6c7d 100644 (file)
@@ -52,14 +52,6 @@ static const struct snd_soc_dapm_route base_map[] = {
 */
 static int simtec_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
-       snd_soc_dapm_enable_pin(dapm, "Line In");
-       snd_soc_dapm_enable_pin(dapm, "Line Out");
-       snd_soc_dapm_enable_pin(dapm, "Mic Jack");
-
        simtec_audio_init(rtd);
 
        return 0;
index c3b2ada..9b0ffac 100644 (file)
@@ -162,8 +162,6 @@ static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
        snd_soc_dapm_nc_pin(dapm, "ROUT1");
 
        /* set endpoints to default off mode */
-       snd_soc_dapm_enable_pin(dapm, "Internal Speaker");
-       snd_soc_dapm_enable_pin(dapm, "Internal Mic");
        snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
 
        /* Headphone jack detection */
@@ -184,6 +182,14 @@ static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
        return err;
 }
 
+static int smartq_wm8987_card_remove(struct snd_soc_card *card)
+{
+       snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios),
+                               smartq_jack_gpios);
+
+       return 0;
+}
+
 static struct snd_soc_dai_link smartq_dai[] = {
        {
                .name           = "wm8987",
@@ -200,6 +206,7 @@ static struct snd_soc_dai_link smartq_dai[] = {
 static struct snd_soc_card snd_soc_smartq = {
        .name = "SmartQ",
        .owner = THIS_MODULE,
+       .remove = smartq_wm8987_card_remove,
        .dai_link = smartq_dai,
        .num_links = ARRAY_SIZE(smartq_dai),
 
@@ -261,8 +268,6 @@ err_unregister_device:
 static void __exit smartq_exit(void)
 {
        gpio_free(S3C64XX_GPK(12));
-       snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios),
-                               smartq_jack_gpios);
 
        platform_device_unregister(smartq_snd_device);
 }
index 7a16b32..b1a519f 100644 (file)
@@ -37,13 +37,11 @@ static int smdk_hw_params(struct snd_pcm_substream *substream,
        unsigned int pll_out;
        int bfs, rfs, ret;
 
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_U8:
-       case SNDRV_PCM_FORMAT_S8:
+       switch (params_width(params)) {
+       case 8:
                bfs = 16;
                break;
-       case SNDRV_PCM_FORMAT_U16_LE:
-       case SNDRV_PCM_FORMAT_S16_LE:
+       case 16:
                bfs = 32;
                break;
        default:
index 23a9204..e119aaa 100644 (file)
@@ -164,19 +164,11 @@ static int snd_smdk_probe(struct platform_device *pdev)
                xtal_freq = mclk_freq = SMDK_WM8580_EXT_VOICE;
 
        smdk_pcm.dev = &pdev->dev;
-       ret = snd_soc_register_card(&smdk_pcm);
-       if (ret) {
+       ret = devm_snd_soc_register_card(&pdev->dev, &smdk_pcm);
+       if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
-               return ret;
-       }
 
-       return 0;
-}
-
-static int snd_smdk_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_card(&smdk_pcm);
-       return 0;
+       return ret;
 }
 
 static struct platform_driver snd_smdk_driver = {
@@ -185,7 +177,6 @@ static struct platform_driver snd_smdk_driver = {
                .name = "samsung-smdk-pcm",
        },
        .probe = snd_smdk_probe,
-       .remove = snd_smdk_remove,
 };
 
 module_platform_driver(snd_smdk_driver);
index 682eb4f..3d6272a 100644 (file)
@@ -57,7 +57,7 @@ static int smdk_hw_params(struct snd_pcm_substream *substream,
        int ret;
 
        /* AIF1CLK should be >=3MHz for optimal performance */
-       if (params_format(params) == SNDRV_PCM_FORMAT_S24_LE)
+       if (params_width(params) == 24)
                pll_out = params_rate(params) * 384;
        else if (params_rate(params) == 8000 || params_rate(params) == 11025)
                pll_out = params_rate(params) * 512;
@@ -89,18 +89,6 @@ static int smdk_wm8994_init_paiftx(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
 
-       /* HeadPhone */
-       snd_soc_dapm_enable_pin(dapm, "HPOUT1R");
-       snd_soc_dapm_enable_pin(dapm, "HPOUT1L");
-
-       /* MicIn */
-       snd_soc_dapm_enable_pin(dapm, "IN1LN");
-       snd_soc_dapm_enable_pin(dapm, "IN1RN");
-
-       /* LineIn */
-       snd_soc_dapm_enable_pin(dapm, "IN2LN");
-       snd_soc_dapm_enable_pin(dapm, "IN2RN");
-
        /* Other pins NC */
        snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
        snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
index 0c84ca0..b6c0997 100644 (file)
@@ -134,19 +134,11 @@ static int snd_smdk_probe(struct platform_device *pdev)
        int ret = 0;
 
        smdk_pcm.dev = &pdev->dev;
-       ret = snd_soc_register_card(&smdk_pcm);
-       if (ret) {
+       ret = devm_snd_soc_register_card(&pdev->dev, &smdk_pcm);
+       if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
-               return ret;
-       }
 
-       return 0;
-}
-
-static int snd_smdk_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_card(&smdk_pcm);
-       return 0;
+       return ret;
 }
 
 static struct platform_driver snd_smdk_driver = {
@@ -155,7 +147,6 @@ static struct platform_driver snd_smdk_driver = {
                .name = "samsung-smdk-pcm",
        },
        .probe = snd_smdk_probe,
-       .remove = snd_smdk_remove,
 };
 
 module_platform_driver(snd_smdk_driver);
diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c
new file mode 100644 (file)
index 0000000..014c177
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * ASoC machine driver for Snow boards
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <sound/soc.h>
+
+#include "i2s.h"
+
+#define FIN_PLL_RATE           24000000
+
+static struct snd_soc_dai_link snow_dai[] = {
+       {
+               .name = "Primary",
+               .stream_name = "Primary",
+               .codec_dai_name = "HiFi",
+               .dai_fmt = SND_SOC_DAIFMT_I2S |
+                               SND_SOC_DAIFMT_NB_NF |
+                               SND_SOC_DAIFMT_CBS_CFS,
+       },
+};
+
+static int snow_late_probe(struct snd_soc_card *card)
+{
+       struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+       struct snd_soc_dai *cpu_dai = card->rtd[0].cpu_dai;
+       int ret;
+
+       /* Set the MCLK rate for the codec */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+                                       FIN_PLL_RATE, SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               return ret;
+
+       /* Select I2S Bus clock to set RCLK and BCLK */
+       ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
+                                       0, SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static struct snd_soc_card snow_snd = {
+       .name = "Snow-I2S",
+       .dai_link = snow_dai,
+       .num_links = ARRAY_SIZE(snow_dai),
+
+       .late_probe = snow_late_probe,
+};
+
+static int snow_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = &snow_snd;
+       struct device_node *i2s_node, *codec_node;
+       int i, ret;
+
+       i2s_node = of_parse_phandle(pdev->dev.of_node,
+                                   "samsung,i2s-controller", 0);
+       if (!i2s_node) {
+               dev_err(&pdev->dev,
+                       "Property 'i2s-controller' missing or invalid\n");
+               return -EINVAL;
+       }
+
+       codec_node = of_parse_phandle(pdev->dev.of_node,
+                                     "samsung,audio-codec", 0);
+       if (!codec_node) {
+               dev_err(&pdev->dev,
+                       "Property 'audio-codec' missing or invalid\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(snow_dai); i++) {
+               snow_dai[i].codec_of_node = codec_node;
+               snow_dai[i].cpu_of_node = i2s_node;
+               snow_dai[i].platform_of_node = i2s_node;
+       }
+
+       card->dev = &pdev->dev;
+
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static const struct of_device_id snow_of_match[] = {
+       { .compatible = "google,snow-audio-max98090", },
+       { .compatible = "google,snow-audio-max98095", },
+       {},
+};
+
+static struct platform_driver snow_driver = {
+       .driver = {
+               .name = "snow-audio",
+               .owner = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+               .of_match_table = snow_of_match,
+       },
+       .probe = snow_probe,
+};
+
+module_platform_driver(snow_driver);
+
+MODULE_DESCRIPTION("ALSA SoC Audio machine driver for Snow");
+MODULE_LICENSE("GPL");
index cfe63b7..d9ffc48 100644 (file)
@@ -211,8 +211,8 @@ static int spdif_hw_params(struct snd_pcm_substream *substream,
        con |= CON_PCM_DATA;
 
        con &= ~CON_PCM_MASK;
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S16_LE:
+       switch (params_width(params)) {
+       case 16:
                con |= CON_PCM_16BIT;
                break;
        default:
@@ -427,8 +427,8 @@ static int spdif_probe(struct platform_device *pdev)
 
        dev_set_drvdata(&pdev->dev, spdif);
 
-       ret = snd_soc_register_component(&pdev->dev, &samsung_spdif_component,
-                                        &samsung_spdif_dai, 1);
+       ret = devm_snd_soc_register_component(&pdev->dev,
+                       &samsung_spdif_component, &samsung_spdif_dai, 1);
        if (ret != 0) {
                dev_err(&pdev->dev, "fail to register dai\n");
                goto err4;
@@ -444,12 +444,10 @@ static int spdif_probe(struct platform_device *pdev)
        ret = samsung_asoc_dma_platform_register(&pdev->dev);
        if (ret) {
                dev_err(&pdev->dev, "failed to register DMA: %d\n", ret);
-               goto err5;
+               goto err4;
        }
 
        return 0;
-err5:
-       snd_soc_unregister_component(&pdev->dev);
 err4:
        iounmap(spdif->regs);
 err3:
@@ -467,9 +465,6 @@ static int spdif_remove(struct platform_device *pdev)
        struct samsung_spdif_info *spdif = &spdif_info;
        struct resource *mem_res;
 
-       samsung_asoc_dma_platform_unregister(&pdev->dev);
-       snd_soc_unregister_component(&pdev->dev);
-
        iounmap(spdif->regs);
 
        mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
index 57df90d..9902efc 100644 (file)
@@ -327,23 +327,12 @@ static int speyside_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
-       if (ret) {
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
+       if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int speyside_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
 
-       snd_soc_unregister_card(card);
-
-       return 0;
+       return ret;
 }
 
 static struct platform_driver speyside_driver = {
@@ -353,7 +342,6 @@ static struct platform_driver speyside_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = speyside_probe,
-       .remove = speyside_remove,
 };
 
 module_platform_driver(speyside_driver);
index 1807b75..6a2b9f1 100644 (file)
@@ -223,23 +223,12 @@ static int tobermory_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
-       if (ret) {
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
+       if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int tobermory_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
 
-       snd_soc_unregister_card(card);
-
-       return 0;
+       return ret;
 }
 
 static struct platform_driver tobermory_driver = {
@@ -249,7 +238,6 @@ static struct platform_driver tobermory_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = tobermory_probe,
-       .remove = tobermory_remove,
 };
 
 module_platform_driver(tobermory_driver);
index ff60e11..b43fdf0 100644 (file)
@@ -56,7 +56,7 @@ config SND_SH7760_AC97
 
 config SND_SIU_MIGOR
        tristate "SIU sound support on Migo-R"
-       depends on SH_MIGOR
+       depends on SH_MIGOR && I2C
        select SND_SOC_SH4_SIU
        select SND_SOC_WM8978
        help
index 7d0051c..9ac5364 100644 (file)
@@ -1,2 +1,2 @@
-snd-soc-rcar-objs      := core.o gen.o src.o adg.o ssi.o
+snd-soc-rcar-objs      := core.o gen.o src.o adg.o ssi.o dvc.o
 obj-$(CONFIG_SND_SOC_RCAR)     += snd-soc-rcar.o
\ No newline at end of file
index 69c4426..fc41a0e 100644 (file)
@@ -57,6 +57,24 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
        return (0x6 + ws) << 8;
 }
 
+int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_dai *rdai,
+                                struct rsnd_mod *mod,
+                                struct rsnd_dai_stream *io)
+{
+       int id = rsnd_mod_id(mod);
+       int shift = (id % 2) ? 16 : 0;
+       u32 mask, val;
+
+       val = rsnd_adg_ssi_ws_timing_gen2(io);
+
+       val  = val      << shift;
+       mask = 0xffff   << shift;
+
+       rsnd_mod_bset(mod, CMDOUT_TIMSEL, mask, val);
+
+       return 0;
+}
+
 static int rsnd_adg_set_src_timsel_gen2(struct rsnd_dai *rdai,
                                        struct rsnd_mod *mod,
                                        struct rsnd_dai_stream *io,
@@ -397,9 +415,8 @@ int rsnd_adg_probe(struct platform_device *pdev,
 {
        struct rsnd_adg *adg;
        struct device *dev = rsnd_priv_to_dev(priv);
-       struct clk *clk, *clk_orig;
+       struct clk *clk;
        int i;
-       bool use_old_style = false;
 
        adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
        if (!adg) {
@@ -407,45 +424,13 @@ int rsnd_adg_probe(struct platform_device *pdev,
                return -ENOMEM;
        }
 
-       clk_orig        = devm_clk_get(dev, NULL);
        adg->clk[CLKA]  = devm_clk_get(dev, "clk_a");
        adg->clk[CLKB]  = devm_clk_get(dev, "clk_b");
        adg->clk[CLKC]  = devm_clk_get(dev, "clk_c");
        adg->clk[CLKI]  = devm_clk_get(dev, "clk_i");
 
-       /*
-        * It request device dependent audio clock.
-        * But above all clks will indicate rsnd module clock
-        * if platform doesn't it
-        */
-       for_each_rsnd_clk(clk, adg, i) {
-               if (clk_orig == clk) {
-                       dev_warn(dev,
-                                "doesn't have device dependent clock, use independent clock\n");
-                       use_old_style = true;
-                       break;
-               }
-       }
-
-       /*
-        * note:
-        * these exist in order to keep compatible with
-        * platform which has device independent audio clock,
-        * but will be removed soon
-        */
-       if (use_old_style) {
-               adg->clk[CLKA] = devm_clk_get(NULL, "audio_clk_a");
-               adg->clk[CLKB] = devm_clk_get(NULL, "audio_clk_b");
-               adg->clk[CLKC] = devm_clk_get(NULL, "audio_clk_c");
-               adg->clk[CLKI] = devm_clk_get(NULL, "audio_clk_internal");
-       }
-
-       for_each_rsnd_clk(clk, adg, i) {
-               if (IS_ERR(clk)) {
-                       dev_err(dev, "Audio clock failed\n");
-                       return -EIO;
-               }
-       }
+       for_each_rsnd_clk(clk, adg, i)
+               dev_dbg(dev, "clk %d : %p\n", i, clk);
 
        rsnd_adg_ssi_clk_init(priv, adg);
 
index 8942447..9188015 100644 (file)
@@ -255,11 +255,81 @@ int rsnd_dma_available(struct rsnd_dma *dma)
        return !!dma->chan;
 }
 
+#define DMA_NAME_SIZE 16
+#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */
+static int _rsnd_dma_of_name(char *dma_name, struct rsnd_mod *mod)
+{
+       if (mod)
+               return snprintf(dma_name, DMA_NAME_SIZE / 2, "%s%d",
+                        rsnd_mod_name(mod), rsnd_mod_id(mod));
+       else
+               return snprintf(dma_name, DMA_NAME_SIZE / 2, "mem");
+
+}
+
+static void rsnd_dma_of_name(struct rsnd_dma *dma,
+                            int is_play, char *dma_name)
+{
+       struct rsnd_mod *this = rsnd_dma_to_mod(dma);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(this);
+       struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+       struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+       struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
+       struct rsnd_mod *mod[MOD_MAX];
+       struct rsnd_mod *src_mod, *dst_mod;
+       int i, index;
+
+
+       for (i = 0; i < MOD_MAX; i++)
+               mod[i] = NULL;
+
+       /*
+        * in play case...
+        *
+        * src -> dst
+        *
+        * mem -> SSI
+        * mem -> SRC -> SSI
+        * mem -> SRC -> DVC -> SSI
+        */
+       mod[0] = NULL; /* for "mem" */
+       index = 1;
+       for (i = 1; i < MOD_MAX; i++) {
+               if (!src) {
+                       mod[i] = ssi;
+                       break;
+               } else if (!dvc) {
+                       mod[i] = src;
+                       src = NULL;
+               } else {
+                       mod[i] = dvc;
+                       dvc = NULL;
+               }
+
+               if (mod[i] == this)
+                       index = i;
+       }
+
+       if (is_play) {
+               src_mod = mod[index - 1];
+               dst_mod = mod[index];
+       } else {
+               src_mod = mod[index];
+               dst_mod = mod[index + 1];
+       }
+
+       index = 0;
+       index = _rsnd_dma_of_name(dma_name + index, src_mod);
+       *(dma_name + index++) = '_';
+       index = _rsnd_dma_of_name(dma_name + index, dst_mod);
+}
+
 int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
                  int is_play, int id)
 {
        struct device *dev = rsnd_priv_to_dev(priv);
        struct dma_slave_config cfg;
+       char dma_name[DMA_NAME_SIZE];
        dma_cap_mask_t mask;
        int ret;
 
@@ -271,18 +341,23 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
        dma_cap_zero(mask);
        dma_cap_set(DMA_SLAVE, mask);
 
+       if (dev->of_node)
+               rsnd_dma_of_name(dma, is_play, dma_name);
+       else
+               snprintf(dma_name, DMA_NAME_SIZE,
+                        is_play ? "tx" : "rx");
+
+       dev_dbg(dev, "dma name : %s\n", dma_name);
+
        dma->chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
                                                     (void *)id, dev,
-                                                    is_play ? "tx" : "rx");
+                                                    dma_name);
        if (!dma->chan) {
                dev_err(dev, "can't get dma channel\n");
                return -EIO;
        }
 
-       cfg.slave_id    = id;
-       cfg.dst_addr    = 0; /* use default addr when playback */
-       cfg.src_addr    = 0; /* use default addr when capture */
-       cfg.direction   = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+       rsnd_gen_dma_addr(priv, dma, &cfg, is_play, id);
 
        ret = dmaengine_slave_config(dma->chan, &cfg);
        if (ret < 0)
@@ -308,24 +383,50 @@ void  rsnd_dma_quit(struct rsnd_priv *priv,
        dma->chan = NULL;
 }
 
+/*
+ *     settting function
+ */
+u32 rsnd_get_adinr(struct rsnd_mod *mod)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       u32 adinr = runtime->channels;
+
+       switch (runtime->sample_bits) {
+       case 16:
+               adinr |= (8 << 16);
+               break;
+       case 32:
+               adinr |= (0 << 16);
+               break;
+       default:
+               dev_warn(dev, "not supported sample bits\n");
+               return 0;
+       }
+
+       return adinr;
+}
+
 /*
  *     rsnd_dai functions
  */
-#define __rsnd_mod_call(mod, func, rdai, io)                   \
+#define __rsnd_mod_call(mod, func, rdai...)                    \
 ({                                                             \
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);         \
        struct device *dev = rsnd_priv_to_dev(priv);            \
        dev_dbg(dev, "%s [%d] %s\n",                            \
                rsnd_mod_name(mod), rsnd_mod_id(mod), #func);   \
-       (mod)->ops->func(mod, rdai, io);                        \
+       (mod)->ops->func(mod, rdai);                            \
 })
 
-#define rsnd_mod_call(mod, func, rdai, io)     \
+#define rsnd_mod_call(mod, func, rdai...)      \
        (!(mod) ? -ENODEV :                     \
         !((mod)->ops->func) ? 0 :              \
-        __rsnd_mod_call(mod, func, (rdai), (io)))
+        __rsnd_mod_call(mod, func, rdai))
 
-#define rsnd_dai_call(rdai, io, fn)                            \
+#define rsnd_dai_call(fn, io, rdai...)                         \
 ({                                                             \
        struct rsnd_mod *mod;                                   \
        int ret = 0, i;                                         \
@@ -333,7 +434,7 @@ void  rsnd_dma_quit(struct rsnd_priv *priv,
                mod = (io)->mod[i];                             \
                if (!mod)                                       \
                        continue;                               \
-               ret = rsnd_mod_call(mod, fn, (rdai), (io));     \
+               ret = rsnd_mod_call(mod, fn, rdai);             \
                if (ret < 0)                                    \
                        break;                                  \
        }                                                       \
@@ -467,10 +568,7 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
        struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
        struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
        struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
-       struct rsnd_mod *mod = rsnd_ssi_mod_get_frm_dai(priv,
-                                               rsnd_dai_id(priv, rdai),
-                                               rsnd_dai_is_play(rdai, io));
-       int ssi_id = rsnd_mod_id(mod);
+       int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io));
        int ret;
        unsigned long flags;
 
@@ -486,20 +584,20 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
                if (ret < 0)
                        goto dai_trigger_end;
 
-               ret = rsnd_dai_call(rdai, io, init);
+               ret = rsnd_dai_call(init, io, rdai);
                if (ret < 0)
                        goto dai_trigger_end;
 
-               ret = rsnd_dai_call(rdai, io, start);
+               ret = rsnd_dai_call(start, io, rdai);
                if (ret < 0)
                        goto dai_trigger_end;
                break;
        case SNDRV_PCM_TRIGGER_STOP:
-               ret = rsnd_dai_call(rdai, io, stop);
+               ret = rsnd_dai_call(stop, io, rdai);
                if (ret < 0)
                        goto dai_trigger_end;
 
-               ret = rsnd_dai_call(rdai, io, quit);
+               ret = rsnd_dai_call(quit, io, rdai);
                if (ret < 0)
                        goto dai_trigger_end;
 
@@ -578,15 +676,27 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
        .set_fmt        = rsnd_soc_dai_set_fmt,
 };
 
+#define rsnd_path_parse(priv, io, type)                                \
+({                                                             \
+       struct rsnd_mod *mod;                                   \
+       int ret = 0;                                            \
+       int id = -1;                                            \
+                                                               \
+       if (rsnd_is_enable_path(io, type)) {                    \
+               id = rsnd_info_id(priv, io, type);              \
+               if (id >= 0) {                                  \
+                       mod = rsnd_##type##_mod_get(priv, id);  \
+                       ret = rsnd_dai_connect(mod, io);        \
+               }                                               \
+       }                                                       \
+       ret;                                                    \
+})
+
 static int rsnd_path_init(struct rsnd_priv *priv,
                          struct rsnd_dai *rdai,
                          struct rsnd_dai_stream *io)
 {
-       struct rsnd_mod *mod;
-       struct rsnd_dai_platform_info *dai_info = rdai->info;
        int ret;
-       int ssi_id = -1;
-       int src_id = -1;
 
        /*
         * Gen1 is created by SRU/SSI, and this SRU is base module of
@@ -598,38 +708,21 @@ static int rsnd_path_init(struct rsnd_priv *priv,
         * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is
         * using fixed path.
         */
-       if (dai_info) {
-               if (rsnd_is_enable_path(io, ssi))
-                       ssi_id = rsnd_info_id(priv, io, ssi);
-               if (rsnd_is_enable_path(io, src))
-                       src_id = rsnd_info_id(priv, io, src);
-       } else {
-               /* get SSI's ID */
-               mod = rsnd_ssi_mod_get_frm_dai(priv,
-                                              rsnd_dai_id(priv, rdai),
-                                              rsnd_dai_is_play(rdai, io));
-               if (!mod)
-                       return 0;
-               ssi_id = src_id = rsnd_mod_id(mod);
-       }
-
-       ret = 0;
 
        /* SRC */
-       if (src_id >= 0) {
-               mod = rsnd_src_mod_get(priv, src_id);
-               ret = rsnd_dai_connect(mod, io);
-               if (ret < 0)
-                       return ret;
-       }
+       ret = rsnd_path_parse(priv, io, src);
+       if (ret < 0)
+               return ret;
 
        /* SSI */
-       if (ssi_id >= 0) {
-               mod = rsnd_ssi_mod_get(priv, ssi_id);
-               ret = rsnd_dai_connect(mod, io);
-               if (ret < 0)
-                       return ret;
-       }
+       ret = rsnd_path_parse(priv, io, ssi);
+       if (ret < 0)
+               return ret;
+
+       /* DVC */
+       ret = rsnd_path_parse(priv, io, dvc);
+       if (ret < 0)
+               return ret;
 
        return ret;
 }
@@ -725,29 +818,14 @@ static int rsnd_dai_probe(struct platform_device *pdev,
        struct snd_soc_dai_driver *drv;
        struct rcar_snd_info *info = rsnd_priv_to_info(priv);
        struct rsnd_dai *rdai;
-       struct rsnd_mod *pmod, *cmod;
+       struct rsnd_ssi_platform_info *pmod, *cmod;
        struct device *dev = rsnd_priv_to_dev(priv);
        int dai_nr;
        int i;
 
        rsnd_of_parse_dai(pdev, of_data, priv);
 
-       /*
-        * dai_nr should be set via dai_info_nr,
-        * but allow it to keeping compatible
-        */
        dai_nr = info->dai_info_nr;
-       if (!dai_nr) {
-               /* get max dai nr */
-               for (dai_nr = 0; dai_nr < 32; dai_nr++) {
-                       pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1);
-                       cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0);
-
-                       if (!pmod && !cmod)
-                               break;
-               }
-       }
-
        if (!dai_nr) {
                dev_err(dev, "no dai\n");
                return -EIO;
@@ -765,11 +843,10 @@ static int rsnd_dai_probe(struct platform_device *pdev,
        priv->rdai      = rdai;
 
        for (i = 0; i < dai_nr; i++) {
-               if (info->dai_info)
-                       rdai[i].info = &info->dai_info[i];
+               rdai[i].info = &info->dai_info[i];
 
-               pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1);
-               cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0);
+               pmod = rdai[i].info->playback.ssi;
+               cmod = rdai[i].info->capture.ssi;
 
                /*
                 *      init rsnd_dai
@@ -787,8 +864,7 @@ static int rsnd_dai_probe(struct platform_device *pdev,
                        drv[i].playback.channels_min    = 2;
                        drv[i].playback.channels_max    = 2;
 
-                       if (info->dai_info)
-                               rdai[i].playback.info = &info->dai_info[i].playback;
+                       rdai[i].playback.info = &info->dai_info[i].playback;
                        rsnd_path_init(priv, &rdai[i], &rdai[i].playback);
                }
                if (cmod) {
@@ -797,8 +873,7 @@ static int rsnd_dai_probe(struct platform_device *pdev,
                        drv[i].capture.channels_min     = 2;
                        drv[i].capture.channels_max     = 2;
 
-                       if (info->dai_info)
-                               rdai[i].capture.info = &info->dai_info[i].capture;
+                       rdai[i].capture.info = &info->dai_info[i].capture;
                        rsnd_path_init(priv, &rdai[i], &rdai[i].capture);
                }
 
@@ -873,6 +948,20 @@ static struct snd_pcm_ops rsnd_pcm_ops = {
 
 static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct rsnd_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+       struct rsnd_dai *rdai;
+       int i, ret;
+
+       for_each_rsnd_dai(rdai, priv, i) {
+               ret = rsnd_dai_call(pcm_new, &rdai->playback, rdai, rtd);
+               if (ret)
+                       return ret;
+
+               ret = rsnd_dai_call(pcm_new, &rdai->capture, rdai, rtd);
+               if (ret)
+                       return ret;
+       }
+
        return snd_pcm_lib_preallocate_pages_for_all(
                rtd->pcm,
                SNDRV_DMA_TYPE_DEV,
@@ -912,6 +1001,7 @@ static int rsnd_probe(struct platform_device *pdev)
                rsnd_gen_probe,
                rsnd_ssi_probe,
                rsnd_src_probe,
+               rsnd_dvc_probe,
                rsnd_adg_probe,
                rsnd_dai_probe,
        };
@@ -941,7 +1031,7 @@ static int rsnd_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       priv->dev       = dev;
+       priv->pdev      = pdev;
        priv->info      = info;
        spin_lock_init(&priv->lock);
 
@@ -955,11 +1045,11 @@ static int rsnd_probe(struct platform_device *pdev)
        }
 
        for_each_rsnd_dai(rdai, priv, i) {
-               ret = rsnd_dai_call(rdai, &rdai->playback, probe);
+               ret = rsnd_dai_call(probe, &rdai->playback, rdai);
                if (ret)
                        return ret;
 
-               ret = rsnd_dai_call(rdai, &rdai->capture, probe);
+               ret = rsnd_dai_call(probe, &rdai->capture, rdai);
                if (ret)
                        return ret;
        }
@@ -1002,11 +1092,11 @@ static int rsnd_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
 
        for_each_rsnd_dai(rdai, priv, i) {
-               ret = rsnd_dai_call(rdai, &rdai->playback, remove);
+               ret = rsnd_dai_call(remove, &rdai->playback, rdai);
                if (ret)
                        return ret;
 
-               ret = rsnd_dai_call(rdai, &rdai->capture, remove);
+               ret = rsnd_dai_call(remove, &rdai->capture, rdai);
                if (ret)
                        return ret;
        }
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
new file mode 100644 (file)
index 0000000..ed00070
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Renesas R-Car DVC support
+ *
+ * Copyright (C) 2014 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@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.
+ */
+#include "rsnd.h"
+
+#define RSND_DVC_NAME_SIZE     16
+#define RSND_DVC_VOLUME_MAX    100
+#define RSND_DVC_VOLUME_NUM    2
+
+#define DVC_NAME "dvc"
+
+struct rsnd_dvc {
+       struct rsnd_dvc_platform_info *info; /* rcar_snd.h */
+       struct rsnd_mod mod;
+       struct clk *clk;
+       long volume[RSND_DVC_VOLUME_NUM];
+};
+
+#define rsnd_mod_to_dvc(_mod)  \
+       container_of((_mod), struct rsnd_dvc, mod)
+
+#define for_each_rsnd_dvc(pos, priv, i)                                \
+       for ((i) = 0;                                           \
+            ((i) < rsnd_dvc_nr(priv)) &&                       \
+            ((pos) = (struct rsnd_dvc *)(priv)->dvc + i);      \
+            i++)
+
+static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
+{
+       struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+       u32 max = (0x00800000 - 1);
+       u32 vol[RSND_DVC_VOLUME_NUM];
+       int i;
+
+       for (i = 0; i < RSND_DVC_VOLUME_NUM; i++)
+               vol[i] = max / RSND_DVC_VOLUME_MAX * dvc->volume[i];
+
+       rsnd_mod_write(mod, DVC_VOL0R, vol[0]);
+       rsnd_mod_write(mod, DVC_VOL1R, vol[1]);
+}
+
+static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
+                              struct rsnd_dai *rdai)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+
+       dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
+
+       return 0;
+}
+
+static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
+                        struct rsnd_dai *rdai)
+{
+       struct rsnd_dvc *dvc = rsnd_mod_to_dvc(dvc_mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(dvc_mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(dvc_mod);
+       struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       int dvc_id = rsnd_mod_id(dvc_mod);
+       int src_id = rsnd_mod_id(src_mod);
+       u32 route[] = {
+               [0] = 0x30000,
+               [1] = 0x30001,
+               [2] = 0x40000,
+               [3] = 0x10000,
+               [4] = 0x20000,
+               [5] = 0x40100
+       };
+
+       if (src_id >= ARRAY_SIZE(route)) {
+               dev_err(dev, "DVC%d isn't connected to SRC%d\n", dvc_id, src_id);
+               return -EINVAL;
+       }
+
+       clk_prepare_enable(dvc->clk);
+
+       /*
+        * fixme
+        * it doesn't support CTU/MIX
+        */
+       rsnd_mod_write(dvc_mod, CMD_ROUTE_SLCT, route[src_id]);
+
+       rsnd_mod_write(dvc_mod, DVC_SWRSR, 0);
+       rsnd_mod_write(dvc_mod, DVC_SWRSR, 1);
+
+       rsnd_mod_write(dvc_mod, DVC_DVUIR, 1);
+
+       rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod));
+
+       /*  enable Volume  */
+       rsnd_mod_write(dvc_mod, DVC_DVUCR, 0x100);
+
+       /* ch0/ch1 Volume */
+       rsnd_dvc_volume_update(dvc_mod);
+
+       rsnd_mod_write(dvc_mod, DVC_DVUIR, 0);
+
+       rsnd_mod_write(dvc_mod, DVC_DVUER, 1);
+
+       rsnd_adg_set_cmd_timsel_gen2(rdai, dvc_mod, io);
+
+       return 0;
+}
+
+static int rsnd_dvc_quit(struct rsnd_mod *mod,
+                        struct rsnd_dai *rdai)
+{
+       struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+
+       clk_disable_unprepare(dvc->clk);
+
+       return 0;
+}
+
+static int rsnd_dvc_start(struct rsnd_mod *mod,
+                         struct rsnd_dai *rdai)
+{
+       rsnd_mod_write(mod, CMD_CTRL, 0x10);
+
+       return 0;
+}
+
+static int rsnd_dvc_stop(struct rsnd_mod *mod,
+                        struct rsnd_dai *rdai)
+{
+       rsnd_mod_write(mod, CMD_CTRL, 0);
+
+       return 0;
+}
+
+static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl,
+                              struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = RSND_DVC_VOLUME_NUM;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = RSND_DVC_VOLUME_MAX;
+
+       return 0;
+}
+
+static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
+       struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+       int i;
+
+       for (i = 0; i < RSND_DVC_VOLUME_NUM; i++)
+               ucontrol->value.integer.value[i] = dvc->volume[i];
+
+       return 0;
+}
+
+static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
+       struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+       int i, change = 0;
+
+       for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
+               if (ucontrol->value.integer.value[i] < 0 ||
+                   ucontrol->value.integer.value[i] > RSND_DVC_VOLUME_MAX)
+                       return -EINVAL;
+
+               change |= (ucontrol->value.integer.value[i] != dvc->volume[i]);
+       }
+
+       if (change) {
+               for (i = 0; i < RSND_DVC_VOLUME_NUM; i++)
+                       dvc->volume[i] = ucontrol->value.integer.value[i];
+
+               rsnd_dvc_volume_update(mod);
+       }
+
+       return change;
+}
+
+static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
+                           struct rsnd_dai *rdai,
+                           struct snd_soc_pcm_runtime *rtd)
+{
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_kcontrol *kctrl;
+       static struct snd_kcontrol_new knew = {
+               .iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name           = "Playback Volume",
+               .info           = rsnd_dvc_volume_info,
+               .get            = rsnd_dvc_volume_get,
+               .put            = rsnd_dvc_volume_put,
+       };
+       int ret;
+
+       if (!rsnd_dai_is_play(rdai, io)) {
+               dev_err(dev, "DVC%d is connected to Capture DAI\n",
+                       rsnd_mod_id(mod));
+               return -EINVAL;
+       }
+
+       kctrl = snd_ctl_new1(&knew, mod);
+       if (!kctrl)
+               return -ENOMEM;
+
+       ret = snd_ctl_add(card, kctrl);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static struct rsnd_mod_ops rsnd_dvc_ops = {
+       .name           = DVC_NAME,
+       .probe          = rsnd_dvc_probe_gen2,
+       .init           = rsnd_dvc_init,
+       .quit           = rsnd_dvc_quit,
+       .start          = rsnd_dvc_start,
+       .stop           = rsnd_dvc_stop,
+       .pcm_new        = rsnd_dvc_pcm_new,
+};
+
+struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
+{
+       if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv)))
+               id = 0;
+
+       return &((struct rsnd_dvc *)(priv->dvc) + id)->mod;
+}
+
+int rsnd_dvc_probe(struct platform_device *pdev,
+                  const struct rsnd_of_data *of_data,
+                  struct rsnd_priv *priv)
+{
+       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_dvc *dvc;
+       struct clk *clk;
+       char name[RSND_DVC_NAME_SIZE];
+       int i, nr;
+
+       nr = info->dvc_info_nr;
+       if (!nr)
+               return 0;
+
+       /* This driver doesn't support Gen1 at this point */
+       if (rsnd_is_gen1(priv)) {
+               dev_warn(dev, "CMD is not supported on Gen1\n");
+               return -EINVAL;
+       }
+
+       dvc     = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL);
+       if (!dvc) {
+               dev_err(dev, "CMD allocate failed\n");
+               return -ENOMEM;
+       }
+
+       priv->dvc_nr    = nr;
+       priv->dvc       = dvc;
+
+       for_each_rsnd_dvc(dvc, priv, i) {
+               snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d",
+                        DVC_NAME, i);
+
+               clk = devm_clk_get(dev, name);
+               if (IS_ERR(clk))
+                       return PTR_ERR(clk);
+
+               dvc->info = &info->dvc_info[i];
+               dvc->clk  = clk;
+
+               rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops, RSND_MOD_DVC, i);
+
+               dev_dbg(dev, "CMD%d probed\n", i);
+       }
+
+       return 0;
+}
index 50a1ef3..1dd2b7d 100644 (file)
@@ -155,6 +155,101 @@ static int rsnd_gen_regmap_init(struct rsnd_priv *priv,
        return 0;
 }
 
+/*
+ *     DMA read/write register offset
+ *
+ *     RSND_xxx_I_N    for Audio DMAC input
+ *     RSND_xxx_O_N    for Audio DMAC output
+ *     RSND_xxx_I_P    for Audio DMAC peri peri input
+ *     RSND_xxx_O_P    for Audio DMAC peri peri output
+ *
+ *     ex) R-Car H2 case
+ *           mod        / DMAC in    / DMAC out   / DMAC PP in / DMAC pp out
+ *     SSI : 0xec541000 / 0xec241008 / 0xec24100c / 0xec400000 / 0xec400000
+ *     SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000
+ *     CMD : 0xec500000 / 0xec008000                             0xec308000
+ */
+#define RDMA_SSI_I_N(addr, i)  (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8)
+#define RDMA_SSI_O_N(addr, i)  (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc)
+
+#define RDMA_SSI_I_P(addr, i)  (addr ##_reg - 0x00141000 + (0x1000 * i))
+#define RDMA_SSI_O_P(addr, i)  (addr ##_reg - 0x00141000 + (0x1000 * i))
+
+#define RDMA_SRC_I_N(addr, i)  (addr ##_reg - 0x00500000 + (0x400 * i))
+#define RDMA_SRC_O_N(addr, i)  (addr ##_reg - 0x004fc000 + (0x400 * i))
+
+#define RDMA_SRC_I_P(addr, i)  (addr ##_reg - 0x00200000 + (0x400 * i))
+#define RDMA_SRC_O_P(addr, i)  (addr ##_reg - 0x001fc000 + (0x400 * i))
+
+#define RDMA_CMD_O_N(addr, i)  (addr ##_reg - 0x004f8000 + (0x400 * i))
+#define RDMA_CMD_O_P(addr, i)  (addr ##_reg - 0x001f8000 + (0x400 * i))
+
+void rsnd_gen_dma_addr(struct rsnd_priv *priv,
+                      struct rsnd_dma *dma,
+                      struct dma_slave_config *cfg,
+                      int is_play, int slave_id)
+{
+       struct platform_device *pdev = rsnd_priv_to_pdev(priv);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       dma_addr_t ssi_reg = platform_get_resource(pdev,
+                               IORESOURCE_MEM, RSND_GEN2_SSI)->start;
+       dma_addr_t src_reg = platform_get_resource(pdev,
+                               IORESOURCE_MEM, RSND_GEN2_SCU)->start;
+       int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod);
+       int use_src = !!rsnd_io_to_mod_src(io);
+       int use_dvc = !!rsnd_io_to_mod_dvc(io);
+       int id = rsnd_mod_id(mod);
+       struct dma_addr {
+               dma_addr_t src_addr;
+               dma_addr_t dst_addr;
+       } dma_addrs[2][2][3] = {
+               { /* SRC */
+                       /* Capture */
+                       {{ 0,                           0 },
+                        { RDMA_SRC_O_N(src, id),       0 },
+                        { RDMA_CMD_O_N(src, id),       0 }},
+                       /* Playback */
+                       {{ 0,                           0, },
+                        { 0,                           RDMA_SRC_I_N(src, id) },
+                        { 0,                           RDMA_SRC_I_N(src, id) }}
+               }, { /* SSI */
+                       /* Capture */
+                       {{ RDMA_SSI_O_N(ssi, id),       0 },
+                        { RDMA_SSI_O_P(ssi, id),       RDMA_SRC_I_P(src, id) },
+                        { RDMA_SSI_O_P(ssi, id),       RDMA_SRC_I_P(src, id) }},
+                       /* Playback */
+                       {{ 0,                           RDMA_SSI_I_N(ssi, id) },
+                        { RDMA_SRC_O_P(src, id),       RDMA_SSI_I_P(ssi, id) },
+                        { RDMA_CMD_O_P(src, id),       RDMA_SSI_I_P(ssi, id) }}
+               }
+       };
+
+       cfg->slave_id   = slave_id;
+       cfg->src_addr   = 0;
+       cfg->dst_addr   = 0;
+       cfg->direction  = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+
+       /*
+        * gen1 uses default DMA addr
+        */
+       if (rsnd_is_gen1(priv))
+               return;
+
+       /* it shouldn't happen */
+       if (use_dvc & !use_src) {
+               dev_err(dev, "DVC is selected without SRC\n");
+               return;
+       }
+
+       cfg->src_addr = dma_addrs[is_ssi][is_play][use_src + use_dvc].src_addr;
+       cfg->dst_addr = dma_addrs[is_ssi][is_play][use_src + use_dvc].dst_addr;
+
+       dev_dbg(dev, "dma%d addr - src : %x / dst : %x\n",
+               id, cfg->src_addr, cfg->dst_addr);
+}
+
 /*
  *             Gen2
  */
@@ -181,6 +276,8 @@ static int rsnd_gen2_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)
                RSND_GEN2_M_REG(gen, SCU,       SRC_BUSIF_MODE, 0x0,    0x20),
                RSND_GEN2_M_REG(gen, SCU,       SRC_ROUTE_MODE0,0xc,    0x20),
                RSND_GEN2_M_REG(gen, SCU,       SRC_CTRL,       0x10,   0x20),
+               RSND_GEN2_M_REG(gen, SCU,       CMD_ROUTE_SLCT, 0x18c,  0x20),
+               RSND_GEN2_M_REG(gen, SCU,       CMD_CTRL,       0x190,  0x20),
                RSND_GEN2_M_REG(gen, SCU,       SRC_SWRSR,      0x200,  0x40),
                RSND_GEN2_M_REG(gen, SCU,       SRC_SRCIR,      0x204,  0x40),
                RSND_GEN2_M_REG(gen, SCU,       SRC_ADINR,      0x214,  0x40),
@@ -189,6 +286,14 @@ static int rsnd_gen2_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)
                RSND_GEN2_M_REG(gen, SCU,       SRC_SRCCR,      0x224,  0x40),
                RSND_GEN2_M_REG(gen, SCU,       SRC_BSDSR,      0x22c,  0x40),
                RSND_GEN2_M_REG(gen, SCU,       SRC_BSISR,      0x238,  0x40),
+               RSND_GEN2_M_REG(gen, SCU,       DVC_SWRSR,      0xe00,  0x100),
+               RSND_GEN2_M_REG(gen, SCU,       DVC_DVUIR,      0xe04,  0x100),
+               RSND_GEN2_M_REG(gen, SCU,       DVC_ADINR,      0xe08,  0x100),
+               RSND_GEN2_M_REG(gen, SCU,       DVC_DVUCR,      0xe10,  0x100),
+               RSND_GEN2_M_REG(gen, SCU,       DVC_ZCMCR,      0xe14,  0x100),
+               RSND_GEN2_M_REG(gen, SCU,       DVC_VOL0R,      0xe28,  0x100),
+               RSND_GEN2_M_REG(gen, SCU,       DVC_VOL1R,      0xe2c,  0x100),
+               RSND_GEN2_M_REG(gen, SCU,       DVC_DVUER,      0xe48,  0x100),
 
                RSND_GEN2_S_REG(gen, ADG,       BRRA,           0x00),
                RSND_GEN2_S_REG(gen, ADG,       BRRB,           0x04),
@@ -207,6 +312,7 @@ static int rsnd_gen2_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)
                RSND_GEN2_S_REG(gen, ADG,       SRCOUT_TIMSEL2, 0x50),
                RSND_GEN2_S_REG(gen, ADG,       SRCOUT_TIMSEL3, 0x54),
                RSND_GEN2_S_REG(gen, ADG,       SRCOUT_TIMSEL4, 0x58),
+               RSND_GEN2_S_REG(gen, ADG,       CMDOUT_TIMSEL,  0x5c),
 
                RSND_GEN2_M_REG(gen, SSI,       SSICR,          0x00,   0x40),
                RSND_GEN2_M_REG(gen, SSI,       SSISR,          0x04,   0x40),
@@ -252,13 +358,13 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
                return ret;
 
        dev_dbg(dev, "Gen2 device probed\n");
-       dev_dbg(dev, "SCU  : %08x => %p\n", scu_res->start,
+       dev_dbg(dev, "SCU  : %pap => %p\n", &scu_res->start,
                gen->base[RSND_GEN2_SCU]);
-       dev_dbg(dev, "ADG  : %08x => %p\n", adg_res->start,
+       dev_dbg(dev, "ADG  : %pap => %p\n", &adg_res->start,
                gen->base[RSND_GEN2_ADG]);
-       dev_dbg(dev, "SSIU : %08x => %p\n", ssiu_res->start,
+       dev_dbg(dev, "SSIU : %pap => %p\n", &ssiu_res->start,
                gen->base[RSND_GEN2_SSIU]);
-       dev_dbg(dev, "SSI  : %08x => %p\n", ssi_res->start,
+       dev_dbg(dev, "SSI  : %pap => %p\n", &ssi_res->start,
                gen->base[RSND_GEN2_SSI]);
 
        return 0;
@@ -345,11 +451,11 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
                return ret;
 
        dev_dbg(dev, "Gen1 device probed\n");
-       dev_dbg(dev, "SRU : %08x => %p\n",      sru_res->start,
+       dev_dbg(dev, "SRU : %pap => %p\n",      &sru_res->start,
                                                gen->base[RSND_GEN1_SRU]);
-       dev_dbg(dev, "ADG : %08x => %p\n",      adg_res->start,
+       dev_dbg(dev, "ADG : %pap => %p\n",      &adg_res->start,
                                                gen->base[RSND_GEN1_ADG]);
-       dev_dbg(dev, "SSI : %08x => %p\n",      ssi_res->start,
+       dev_dbg(dev, "SSI : %pap => %p\n",      &ssi_res->start,
                                                gen->base[RSND_GEN1_SSI]);
 
        return 0;
index 619d198..39d98af 100644 (file)
@@ -44,6 +44,15 @@ enum rsnd_reg {
        RSND_REG_SRC_IFSCR,
        RSND_REG_SRC_IFSVR,
        RSND_REG_SRC_SRCCR,
+       RSND_REG_CMD_ROUTE_SLCT,
+       RSND_REG_DVC_SWRSR,
+       RSND_REG_DVC_DVUIR,
+       RSND_REG_DVC_ADINR,
+       RSND_REG_DVC_DVUCR,
+       RSND_REG_DVC_ZCMCR,
+       RSND_REG_DVC_VOL0R,
+       RSND_REG_DVC_VOL1R,
+       RSND_REG_DVC_DVUER,
 
        /* ADG */
        RSND_REG_BRRA,
@@ -79,6 +88,8 @@ enum rsnd_reg {
        RSND_REG_SHARE17,
        RSND_REG_SHARE18,
        RSND_REG_SHARE19,
+       RSND_REG_SHARE20,
+       RSND_REG_SHARE21,
 
        RSND_REG_MAX,
 };
@@ -114,6 +125,8 @@ enum rsnd_reg {
 #define RSND_REG_SRCOUT_TIMSEL3                RSND_REG_SHARE17
 #define RSND_REG_SRCOUT_TIMSEL4                RSND_REG_SHARE18
 #define RSND_REG_AUDIO_CLK_SEL2                RSND_REG_SHARE19
+#define RSND_REG_CMD_CTRL              RSND_REG_SHARE20
+#define RSND_REG_CMDOUT_TIMSEL         RSND_REG_SHARE21
 
 struct rsnd_of_data;
 struct rsnd_priv;
@@ -136,6 +149,7 @@ void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
                enum rsnd_reg reg, u32 data);
 void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
                    u32 mask, u32 data);
+u32 rsnd_get_adinr(struct rsnd_mod *mod);
 
 /*
  *     R-Car DMA
@@ -165,29 +179,27 @@ void  rsnd_dma_quit(struct rsnd_priv *priv,
 enum rsnd_mod_type {
        RSND_MOD_SRC = 0,
        RSND_MOD_SSI,
+       RSND_MOD_DVC,
        RSND_MOD_MAX,
 };
 
 struct rsnd_mod_ops {
        char *name;
        int (*probe)(struct rsnd_mod *mod,
-                    struct rsnd_dai *rdai,
-                    struct rsnd_dai_stream *io);
+                    struct rsnd_dai *rdai);
        int (*remove)(struct rsnd_mod *mod,
-                     struct rsnd_dai *rdai,
-                     struct rsnd_dai_stream *io);
+                     struct rsnd_dai *rdai);
        int (*init)(struct rsnd_mod *mod,
-                   struct rsnd_dai *rdai,
-                   struct rsnd_dai_stream *io);
+                   struct rsnd_dai *rdai);
        int (*quit)(struct rsnd_mod *mod,
-                   struct rsnd_dai *rdai,
-                   struct rsnd_dai_stream *io);
+                   struct rsnd_dai *rdai);
        int (*start)(struct rsnd_mod *mod,
-                    struct rsnd_dai *rdai,
-                    struct rsnd_dai_stream *io);
+                    struct rsnd_dai *rdai);
        int (*stop)(struct rsnd_mod *mod,
-                   struct rsnd_dai *rdai,
-                   struct rsnd_dai_stream *io);
+                   struct rsnd_dai *rdai);
+       int (*pcm_new)(struct rsnd_mod *mod,
+                      struct rsnd_dai *rdai,
+                      struct snd_soc_pcm_runtime *rtd);
 };
 
 struct rsnd_dai_stream;
@@ -228,6 +240,7 @@ struct rsnd_dai_stream {
 };
 #define rsnd_io_to_mod_ssi(io) ((io)->mod[RSND_MOD_SSI])
 #define rsnd_io_to_mod_src(io) ((io)->mod[RSND_MOD_SRC])
+#define rsnd_io_to_mod_dvc(io) ((io)->mod[RSND_MOD_DVC])
 
 struct rsnd_dai {
        char name[RSND_DAI_NAME_SIZE];
@@ -268,6 +281,11 @@ int rsnd_gen_probe(struct platform_device *pdev,
 void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
                               struct rsnd_mod *mod,
                               enum rsnd_reg reg);
+void rsnd_gen_dma_addr(struct rsnd_priv *priv,
+                      struct rsnd_dma *dma,
+                      struct dma_slave_config *cfg,
+                      int is_play,  int slave_id);
+
 #define rsnd_is_gen1(s)                (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1)
 #define rsnd_is_gen2(s)                (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2)
 
@@ -291,6 +309,9 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
 int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
                                     struct rsnd_dai *rdai,
                                     struct rsnd_dai_stream *io);
+int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_dai *rdai,
+                                struct rsnd_mod *mod,
+                                struct rsnd_dai_stream *io);
 
 /*
  *     R-Car sound priv
@@ -301,7 +322,7 @@ struct rsnd_of_data {
 
 struct rsnd_priv {
 
-       struct device *dev;
+       struct platform_device *pdev;
        struct rcar_snd_info *info;
        spinlock_t lock;
 
@@ -327,6 +348,12 @@ struct rsnd_priv {
        void *ssi;
        int ssi_nr;
 
+       /*
+        * below value will be filled on rsnd_dvc_probe()
+        */
+       void *dvc;
+       int dvc_nr;
+
        /*
         * below value will be filled on rsnd_dai_probe()
         */
@@ -335,7 +362,8 @@ struct rsnd_priv {
        int rdai_nr;
 };
 
-#define rsnd_priv_to_dev(priv) ((priv)->dev)
+#define rsnd_priv_to_pdev(priv)        ((priv)->pdev)
+#define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev))
 #define rsnd_priv_to_info(priv)        ((priv)->info)
 #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
 #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)
@@ -364,11 +392,9 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
                                   struct rsnd_dai_stream *io,
                                   struct snd_pcm_runtime *runtime);
 int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod,
-                          struct rsnd_dai *rdai,
-                          struct rsnd_dai_stream *io);
+                          struct rsnd_dai *rdai);
 int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
-                           struct rsnd_dai *rdai,
-                           struct rsnd_dai_stream *io);
+                           struct rsnd_dai *rdai);
 
 #define rsnd_src_nr(priv) ((priv)->src_nr)
 
@@ -379,9 +405,19 @@ int rsnd_ssi_probe(struct platform_device *pdev,
                   const struct rsnd_of_data *of_data,
                   struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
-struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv,
-                                         int dai_id, int is_play);
 int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
-int rsnd_ssi_is_play(struct rsnd_mod *mod);
+
+/*
+ *     R-Car DVC
+ */
+int rsnd_dvc_probe(struct platform_device *pdev,
+                  const struct rsnd_of_data *of_data,
+                  struct rsnd_priv *priv);
+void rsnd_dvc_remove(struct platform_device *pdev,
+                    struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
+
+#define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
+
 
 #endif
index 4d0720e..200eda0 100644 (file)
@@ -10,6 +10,8 @@
  */
 #include "rsnd.h"
 
+#define SRC_NAME "src"
+
 struct rsnd_src {
        struct rsnd_src_platform_info *info; /* rcar_snd.h */
        struct rsnd_mod mod;
@@ -18,21 +20,9 @@ struct rsnd_src {
 
 #define RSND_SRC_NAME_SIZE 16
 
-/*
- * ADINR
- */
-#define OTBL_24                (0 << 16)
-#define OTBL_22                (2 << 16)
-#define OTBL_20                (4 << 16)
-#define OTBL_18                (6 << 16)
-#define OTBL_16                (8 << 16)
-
-#define rsnd_src_mode_flags(p) ((p)->info->flags)
 #define rsnd_src_convert_rate(p) ((p)->info->convert_rate)
 #define rsnd_mod_to_src(_mod)                          \
        container_of((_mod), struct rsnd_src, mod)
-#define rsnd_src_hpbif_is_enable(src)  \
-       (rsnd_src_mode_flags(src) & RSND_SCU_USE_HPBIF)
 #define rsnd_src_dma_available(src) \
        rsnd_dma_available(rsnd_mod_to_dma(&(src)->mod))
 
@@ -80,34 +70,35 @@ struct rsnd_src {
  *
  * This driver request
  * struct rsnd_src_platform_info {
- *     u32 flags;
  *     u32 convert_rate;
+ *     int dma_id;
  * }
  *
- * rsnd_src_hpbif_is_enable() will be true
- * if flags had RSND_SRC_USE_HPBIF,
- * and it controls whether SSIU is used or not.
- *
  * rsnd_src_convert_rate() indicates
  * above convert_rate, and it controls
  * whether SRC is used or not.
  *
  * ex) doesn't use SRC
- * struct rsnd_src_platform_info info = {
- *     .flags = 0,
- *     .convert_rate = 0,
+ * static struct rsnd_dai_platform_info rsnd_dai = {
+ *     .playback = { .ssi = &rsnd_ssi[0], },
  * };
  *
  * ex) uses SRC
- * struct rsnd_src_platform_info info = {
- *     .flags = RSND_SRC_USE_HPBIF,
- *     .convert_rate = 48000,
+ * static struct rsnd_src_platform_info rsnd_src[] = {
+ *     RSND_SCU(48000, 0),
+ *     ...
+ * };
+ * static struct rsnd_dai_platform_info rsnd_dai = {
+ *     .playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] },
  * };
  *
  * ex) uses SRC bypass mode
- * struct rsnd_src_platform_info info = {
- *     .flags = RSND_SRC_USE_HPBIF,
- *     .convert_rate = 0,
+ * static struct rsnd_src_platform_info rsnd_src[] = {
+ *     RSND_SCU(0, 0),
+ *     ...
+ * };
+ * static struct rsnd_dai_platform_info rsnd_dai = {
+ *     .playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] },
  * };
  *
  */
@@ -116,27 +107,17 @@ struct rsnd_src {
  *             Gen1/Gen2 common functions
  */
 int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod,
-                          struct rsnd_dai *rdai,
-                          struct rsnd_dai_stream *io)
+                          struct rsnd_dai *rdai)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(ssi_mod);
        struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
        int ssi_id = rsnd_mod_id(ssi_mod);
-       int has_src = 0;
 
        /*
         * SSI_MODE0
         */
-       if (info->dai_info) {
-               has_src = !!src_mod;
-       } else {
-               struct rsnd_src *src = rsnd_mod_to_src(src_mod);
-               has_src = rsnd_src_hpbif_is_enable(src);
-       }
-
        rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id),
-                     has_src ? 0 : (1 << ssi_id));
+                     src_mod ? 0 : (1 << ssi_id));
 
        /*
         * SSI_MODE1
@@ -166,8 +147,7 @@ int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod,
 }
 
 int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
-                           struct rsnd_dai *rdai,
-                           struct rsnd_dai_stream *io)
+                           struct rsnd_dai *rdai)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
 
@@ -203,13 +183,12 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
 }
 
 static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
-                                    struct rsnd_dai *rdai,
-                                    struct rsnd_dai_stream *io)
+                                    struct rsnd_dai *rdai)
 {
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        struct rsnd_src *src = rsnd_mod_to_src(mod);
        u32 convert_rate = rsnd_src_convert_rate(src);
-       u32 adinr = runtime->channels;
        u32 fsrate = 0;
 
        if (convert_rate)
@@ -226,17 +205,7 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
        rsnd_mod_write(mod, SRC_SRCIR, 1);
 
        /* Set channel number and output bit length */
-       switch (runtime->sample_bits) {
-       case 16:
-               adinr |= OTBL_16;
-               break;
-       case 32:
-               adinr |= OTBL_24;
-               break;
-       default:
-               return -EIO;
-       }
-       rsnd_mod_write(mod, SRC_ADINR, adinr);
+       rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod));
 
        /* Enable the initial value of IFS */
        if (fsrate) {
@@ -253,8 +222,7 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
 }
 
 static int rsnd_src_init(struct rsnd_mod *mod,
-                        struct rsnd_dai *rdai,
-                        struct rsnd_dai_stream *io)
+                        struct rsnd_dai *rdai)
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
 
@@ -264,8 +232,7 @@ static int rsnd_src_init(struct rsnd_mod *mod,
 }
 
 static int rsnd_src_quit(struct rsnd_mod *mod,
-                        struct rsnd_dai *rdai,
-                        struct rsnd_dai_stream *io)
+                        struct rsnd_dai *rdai)
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
 
@@ -275,8 +242,7 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
 }
 
 static int rsnd_src_start(struct rsnd_mod *mod,
-                         struct rsnd_dai *rdai,
-                         struct rsnd_dai_stream *io)
+                         struct rsnd_dai *rdai)
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
 
@@ -294,8 +260,7 @@ static int rsnd_src_start(struct rsnd_mod *mod,
 
 
 static int rsnd_src_stop(struct rsnd_mod *mod,
-                        struct rsnd_dai *rdai,
-                        struct rsnd_dai_stream *io)
+                        struct rsnd_dai *rdai)
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
 
@@ -305,17 +270,13 @@ static int rsnd_src_stop(struct rsnd_mod *mod,
        return 0;
 }
 
-static struct rsnd_mod_ops rsnd_src_non_ops = {
-       .name   = "src (non)",
-};
-
 /*
  *             Gen1 functions
  */
 static int rsnd_src_set_route_gen1(struct rsnd_mod *mod,
-                                  struct rsnd_dai *rdai,
-                                  struct rsnd_dai_stream *io)
+                                  struct rsnd_dai *rdai)
 {
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        struct src_route_config {
                u32 mask;
                int shift;
@@ -351,9 +312,9 @@ static int rsnd_src_set_route_gen1(struct rsnd_mod *mod,
 }
 
 static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod,
-                                           struct rsnd_dai *rdai,
-                                           struct rsnd_dai_stream *io)
+                                           struct rsnd_dai *rdai)
 {
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct rsnd_src *src = rsnd_mod_to_src(mod);
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
@@ -410,12 +371,11 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod,
 }
 
 static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
-                                         struct rsnd_dai *rdai,
-                                         struct rsnd_dai_stream *io)
+                                         struct rsnd_dai *rdai)
 {
        int ret;
 
-       ret = rsnd_src_set_convert_rate(mod, rdai, io);
+       ret = rsnd_src_set_convert_rate(mod, rdai);
        if (ret < 0)
                return ret;
 
@@ -431,25 +391,35 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
        return 0;
 }
 
+static int rsnd_src_probe_gen1(struct rsnd_mod *mod,
+                             struct rsnd_dai *rdai)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+
+       dev_dbg(dev, "%s (Gen1) is probed\n", rsnd_mod_name(mod));
+
+       return 0;
+}
+
 static int rsnd_src_init_gen1(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai,
-                             struct rsnd_dai_stream *io)
+                             struct rsnd_dai *rdai)
 {
        int ret;
 
-       ret = rsnd_src_init(mod, rdai, io);
+       ret = rsnd_src_init(mod, rdai);
        if (ret < 0)
                return ret;
 
-       ret = rsnd_src_set_route_gen1(mod, rdai, io);
+       ret = rsnd_src_set_route_gen1(mod, rdai);
        if (ret < 0)
                return ret;
 
-       ret = rsnd_src_set_convert_rate_gen1(mod, rdai, io);
+       ret = rsnd_src_set_convert_rate_gen1(mod, rdai);
        if (ret < 0)
                return ret;
 
-       ret = rsnd_src_set_convert_timing_gen1(mod, rdai, io);
+       ret = rsnd_src_set_convert_timing_gen1(mod, rdai);
        if (ret < 0)
                return ret;
 
@@ -457,29 +427,28 @@ static int rsnd_src_init_gen1(struct rsnd_mod *mod,
 }
 
 static int rsnd_src_start_gen1(struct rsnd_mod *mod,
-                              struct rsnd_dai *rdai,
-                              struct rsnd_dai_stream *io)
+                              struct rsnd_dai *rdai)
 {
        int id = rsnd_mod_id(mod);
 
        rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id));
 
-       return rsnd_src_start(mod, rdai, io);
+       return rsnd_src_start(mod, rdai);
 }
 
 static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai,
-                             struct rsnd_dai_stream *io)
+                             struct rsnd_dai *rdai)
 {
        int id = rsnd_mod_id(mod);
 
        rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0);
 
-       return rsnd_src_stop(mod, rdai, io);
+       return rsnd_src_stop(mod, rdai);
 }
 
 static struct rsnd_mod_ops rsnd_src_gen1_ops = {
-       .name   = "sru (gen1)",
+       .name   = SRC_NAME,
+       .probe  = rsnd_src_probe_gen1,
        .init   = rsnd_src_init_gen1,
        .quit   = rsnd_src_quit,
        .start  = rsnd_src_start_gen1,
@@ -490,17 +459,16 @@ static struct rsnd_mod_ops rsnd_src_gen1_ops = {
  *             Gen2 functions
  */
 static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
-                                         struct rsnd_dai *rdai,
-                                         struct rsnd_dai_stream *io)
+                                         struct rsnd_dai *rdai)
 {
        int ret;
 
-       ret = rsnd_src_set_convert_rate(mod, rdai, io);
+       ret = rsnd_src_set_convert_rate(mod, rdai);
        if (ret < 0)
                return ret;
 
-       rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_mod_read(mod, SRC_ADINR));
-       rsnd_mod_write(mod, SSI_BUSIF_MODE,  rsnd_mod_read(mod, SRC_BUSIF_MODE));
+       rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_get_adinr(mod));
+       rsnd_mod_write(mod, SSI_BUSIF_MODE,  1);
 
        rsnd_mod_write(mod, SRC_SRCCR, 0x00011110);
 
@@ -511,9 +479,9 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
 }
 
 static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod,
-                                           struct rsnd_dai *rdai,
-                                           struct rsnd_dai_stream *io)
+                                           struct rsnd_dai *rdai)
 {
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        struct rsnd_src *src = rsnd_mod_to_src(mod);
        u32 convert_rate = rsnd_src_convert_rate(src);
@@ -530,35 +498,27 @@ static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod,
 }
 
 static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
-                              struct rsnd_dai *rdai,
-                              struct rsnd_dai_stream *io)
+                              struct rsnd_dai *rdai)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
        struct rsnd_src *src = rsnd_mod_to_src(mod);
-       struct rsnd_mod *ssi = rsnd_ssi_mod_get(priv, rsnd_mod_id(mod));
        struct device *dev = rsnd_priv_to_dev(priv);
        int ret;
-       int is_play;
-
-       if (info->dai_info)
-               is_play = rsnd_info_is_playback(priv, src);
-       else
-               is_play = rsnd_ssi_is_play(ssi);
 
        ret = rsnd_dma_init(priv,
                            rsnd_mod_to_dma(mod),
-                           is_play,
+                           rsnd_info_is_playback(priv, src),
                            src->info->dma_id);
        if (ret < 0)
                dev_err(dev, "SRC DMA failed\n");
 
+       dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
+
        return ret;
 }
 
 static int rsnd_src_remove_gen2(struct rsnd_mod *mod,
-                               struct rsnd_dai *rdai,
-                               struct rsnd_dai_stream *io)
+                               struct rsnd_dai *rdai)
 {
        rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));
 
@@ -566,20 +526,19 @@ static int rsnd_src_remove_gen2(struct rsnd_mod *mod,
 }
 
 static int rsnd_src_init_gen2(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai,
-                             struct rsnd_dai_stream *io)
+                             struct rsnd_dai *rdai)
 {
        int ret;
 
-       ret = rsnd_src_init(mod, rdai, io);
+       ret = rsnd_src_init(mod, rdai);
        if (ret < 0)
                return ret;
 
-       ret = rsnd_src_set_convert_rate_gen2(mod, rdai, io);
+       ret = rsnd_src_set_convert_rate_gen2(mod, rdai);
        if (ret < 0)
                return ret;
 
-       ret = rsnd_src_set_convert_timing_gen2(mod, rdai, io);
+       ret = rsnd_src_set_convert_timing_gen2(mod, rdai);
        if (ret < 0)
                return ret;
 
@@ -587,22 +546,22 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod,
 }
 
 static int rsnd_src_start_gen2(struct rsnd_mod *mod,
-                              struct rsnd_dai *rdai,
-                              struct rsnd_dai_stream *io)
+                              struct rsnd_dai *rdai)
 {
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        struct rsnd_src *src = rsnd_mod_to_src(mod);
+       u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11;
 
        rsnd_dma_start(rsnd_mod_to_dma(&src->mod));
 
        rsnd_mod_write(mod, SSI_CTRL, 0x1);
-       rsnd_mod_write(mod, SRC_CTRL, 0x11);
+       rsnd_mod_write(mod, SRC_CTRL, val);
 
-       return rsnd_src_start(mod, rdai, io);
+       return rsnd_src_start(mod, rdai);
 }
 
 static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai,
-                             struct rsnd_dai_stream *io)
+                             struct rsnd_dai *rdai)
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
 
@@ -611,11 +570,11 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
 
        rsnd_dma_stop(rsnd_mod_to_dma(&src->mod));
 
-       return rsnd_src_stop(mod, rdai, io);
+       return rsnd_src_stop(mod, rdai);
 }
 
 static struct rsnd_mod_ops rsnd_src_gen2_ops = {
-       .name   = "src (gen2)",
+       .name   = SRC_NAME,
        .probe  = rsnd_src_probe_gen2,
        .remove = rsnd_src_remove_gen2,
        .init   = rsnd_src_init_gen2,
@@ -651,18 +610,21 @@ static void rsnd_of_parse_src(struct platform_device *pdev,
 
        nr = of_get_child_count(src_node);
        if (!nr)
-               return;
+               goto rsnd_of_parse_src_end;
 
        src_info = devm_kzalloc(dev,
                                sizeof(struct rsnd_src_platform_info) * nr,
                                GFP_KERNEL);
        if (!src_info) {
                dev_err(dev, "src info allocation error\n");
-               return;
+               goto rsnd_of_parse_src_end;
        }
 
        info->src_info          = src_info;
        info->src_info_nr       = nr;
+
+rsnd_of_parse_src_end:
+       of_node_put(src_node);
 }
 
 int rsnd_src_probe(struct platform_device *pdev,
@@ -677,6 +639,16 @@ int rsnd_src_probe(struct platform_device *pdev,
        char name[RSND_SRC_NAME_SIZE];
        int i, nr;
 
+       ops = NULL;
+       if (rsnd_is_gen1(priv))
+               ops = &rsnd_src_gen1_ops;
+       if (rsnd_is_gen2(priv))
+               ops = &rsnd_src_gen2_ops;
+       if (!ops) {
+               dev_err(dev, "unknown Generation\n");
+               return -EIO;
+       }
+
        rsnd_of_parse_src(pdev, of_data, priv);
 
        /*
@@ -696,28 +668,16 @@ int rsnd_src_probe(struct platform_device *pdev,
        priv->src       = src;
 
        for_each_rsnd_src(src, priv, i) {
-               snprintf(name, RSND_SRC_NAME_SIZE, "src.%d", i);
+               snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d",
+                        SRC_NAME, i);
 
                clk = devm_clk_get(dev, name);
-               if (IS_ERR(clk)) {
-                       snprintf(name, RSND_SRC_NAME_SIZE, "scu.%d", i);
-                       clk = devm_clk_get(dev, name);
-               }
-
                if (IS_ERR(clk))
                        return PTR_ERR(clk);
 
                src->info = &info->src_info[i];
                src->clk = clk;
 
-               ops = &rsnd_src_non_ops;
-               if (rsnd_src_hpbif_is_enable(src)) {
-                       if (rsnd_is_gen1(priv))
-                               ops = &rsnd_src_gen1_ops;
-                       if (rsnd_is_gen2(priv))
-                               ops = &rsnd_src_gen2_ops;
-               }
-
                rsnd_mod_init(priv, &src->mod, ops, RSND_MOD_SRC, i);
 
                dev_dbg(dev, "SRC%d probed\n", i);
index 1d8387c..2df723d 100644 (file)
@@ -57,6 +57,8 @@
  */
 #define CONT           (1 << 8)        /* WS Continue Function */
 
+#define SSI_NAME "ssi"
+
 struct rsnd_ssi {
        struct clk *clk;
        struct rsnd_ssi_platform_info *info; /* rcar_snd.h */
@@ -240,10 +242,10 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
  *     SSI mod common functions
  */
 static int rsnd_ssi_init(struct rsnd_mod *mod,
-                        struct rsnd_dai *rdai,
-                        struct rsnd_dai_stream *io)
+                        struct rsnd_dai *rdai)
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        u32 cr;
 
@@ -287,14 +289,13 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
        ssi->cr_own     = cr;
        ssi->err        = -1; /* ignore 1st error */
 
-       rsnd_src_ssi_mode_init(mod, rdai, io);
+       rsnd_src_ssi_mode_init(mod, rdai);
 
        return 0;
 }
 
 static int rsnd_ssi_quit(struct rsnd_mod *mod,
-                        struct rsnd_dai *rdai,
-                        struct rsnd_dai_stream *io)
+                        struct rsnd_dai *rdai)
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
@@ -359,8 +360,7 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
 }
 
 static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai,
-                             struct rsnd_dai_stream *io)
+                             struct rsnd_dai *rdai)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
@@ -375,19 +375,21 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
        if (ret)
                dev_err(dev, "SSI request interrupt failed\n");
 
+       dev_dbg(dev, "%s (PIO) is probed\n", rsnd_mod_name(mod));
+
        return ret;
 }
 
 static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai,
-                             struct rsnd_dai_stream *io)
+                             struct rsnd_dai *rdai)
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
 
        /* enable PIO IRQ */
        ssi->cr_etc = UIEN | OIEN | DIEN;
 
-       rsnd_src_enable_ssi_irq(mod, rdai, io);
+       rsnd_src_enable_ssi_irq(mod, rdai);
 
        rsnd_ssi_hw_start(ssi, rdai, io);
 
@@ -395,8 +397,7 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
 }
 
 static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
-                            struct rsnd_dai *rdai,
-                            struct rsnd_dai_stream *io)
+                            struct rsnd_dai *rdai)
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 
@@ -408,7 +409,7 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
 }
 
 static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
-       .name   = "ssi (pio)",
+       .name   = SSI_NAME,
        .probe  = rsnd_ssi_pio_probe,
        .init   = rsnd_ssi_init,
        .quit   = rsnd_ssi_quit,
@@ -417,36 +418,29 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
 };
 
 static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
-                         struct rsnd_dai *rdai,
-                         struct rsnd_dai_stream *io)
+                         struct rsnd_dai *rdai)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
        struct device *dev = rsnd_priv_to_dev(priv);
        int dma_id = ssi->info->dma_id;
-       int is_play;
        int ret;
 
-       if (info->dai_info)
-               is_play = rsnd_info_is_playback(priv, ssi);
-       else
-               is_play = rsnd_ssi_is_play(&ssi->mod);
-
        ret = rsnd_dma_init(
                priv, rsnd_mod_to_dma(mod),
-               is_play,
+               rsnd_info_is_playback(priv, ssi),
                dma_id);
 
        if (ret < 0)
                dev_err(dev, "SSI DMA failed\n");
 
+       dev_dbg(dev, "%s (DMA) is probed\n", rsnd_mod_name(mod));
+
        return ret;
 }
 
 static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
-                              struct rsnd_dai *rdai,
-                              struct rsnd_dai_stream *io)
+                              struct rsnd_dai *rdai)
 {
        rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));
 
@@ -454,11 +448,11 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
 }
 
 static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai,
-                             struct rsnd_dai_stream *io)
+                             struct rsnd_dai *rdai)
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
 
        /* enable DMA transfer */
        ssi->cr_etc = DMEN;
@@ -475,8 +469,7 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
 }
 
 static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
-                            struct rsnd_dai *rdai,
-                            struct rsnd_dai_stream *io)
+                            struct rsnd_dai *rdai)
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
@@ -493,7 +486,7 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
 }
 
 static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
-       .name   = "ssi (dma)",
+       .name   = SSI_NAME,
        .probe  = rsnd_ssi_dma_probe,
        .remove = rsnd_ssi_dma_remove,
        .init   = rsnd_ssi_init,
@@ -506,47 +499,12 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
  *             Non SSI
  */
 static struct rsnd_mod_ops rsnd_ssi_non_ops = {
-       .name   = "ssi (non)",
+       .name   = SSI_NAME,
 };
 
 /*
  *             ssi mod function
  */
-struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv,
-                                         int dai_id, int is_play)
-{
-       struct rsnd_dai_platform_info *dai_info = NULL;
-       struct rsnd_dai_path_info *path_info = NULL;
-       struct rsnd_ssi_platform_info *target_info = NULL;
-       struct rsnd_ssi *ssi;
-       int i, has_play;
-
-       if (priv->rdai)
-               dai_info = priv->rdai[dai_id].info;
-       if (dai_info)
-               path_info = (is_play) ? &dai_info->playback : &dai_info->capture;
-       if (path_info)
-               target_info = path_info->ssi;
-
-       is_play = !!is_play;
-
-       for_each_rsnd_ssi(ssi, priv, i) {
-               if (target_info == ssi->info)
-                       return &ssi->mod;
-
-               /* for compatible */
-               if (rsnd_ssi_dai_id(ssi) != dai_id)
-                       continue;
-
-               has_play = rsnd_ssi_is_play(&ssi->mod);
-
-               if (is_play == has_play)
-                       return &ssi->mod;
-       }
-
-       return NULL;
-}
-
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
 {
        if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
@@ -562,13 +520,6 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
        return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
 }
 
-int rsnd_ssi_is_play(struct rsnd_mod *mod)
-{
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-
-       return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY);
-}
-
 static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
 {
        if (!rsnd_ssi_is_pin_sharing(&ssi->mod))
@@ -609,14 +560,14 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
 
        nr = of_get_child_count(node);
        if (!nr)
-               return;
+               goto rsnd_of_parse_ssi_end;
 
        ssi_info = devm_kzalloc(dev,
                                sizeof(struct rsnd_ssi_platform_info) * nr,
                                GFP_KERNEL);
        if (!ssi_info) {
                dev_err(dev, "ssi info allocation error\n");
-               return;
+               goto rsnd_of_parse_ssi_end;
        }
 
        info->ssi_info          = ssi_info;
@@ -638,7 +589,16 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
                 * irq
                 */
                ssi_info->pio_irq = irq_of_parse_and_map(np, 0);
+
+               /*
+                * DMA
+                */
+               ssi_info->dma_id = of_get_property(np, "pio-transfer", NULL) ?
+                       0 : 1;
        }
+
+rsnd_of_parse_ssi_end:
+       of_node_put(node);
 }
 
 int rsnd_ssi_probe(struct platform_device *pdev,
@@ -672,7 +632,8 @@ int rsnd_ssi_probe(struct platform_device *pdev,
        for_each_rsnd_ssi(ssi, priv, i) {
                pinfo = &info->ssi_info[i];
 
-               snprintf(name, RSND_SSI_NAME_SIZE, "ssi.%d", i);
+               snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d",
+                        SSI_NAME, i);
 
                clk = devm_clk_get(dev, name);
                if (IS_ERR(clk))
index b04a53f..b4afa31 100644 (file)
@@ -6,60 +6,15 @@
  * Licensed under GPLv2 or later.
  */
 #include <linux/module.h>
-#include <linux/io.h>
-#include <linux/regmap.h>
 #include <sound/soc.h>
 #include <sound/dmaengine_pcm.h>
 
-#include "sirf-audio-port.h"
-
 struct sirf_audio_port {
        struct regmap *regmap;
        struct snd_dmaengine_dai_dma_data playback_dma_data;
        struct snd_dmaengine_dai_dma_data capture_dma_data;
 };
 
-static void sirf_audio_port_tx_enable(struct sirf_audio_port *port)
-{
-       regmap_update_bits(port->regmap, AUDIO_PORT_IC_TXFIFO_OP,
-               AUDIO_FIFO_RESET, AUDIO_FIFO_RESET);
-       regmap_write(port->regmap, AUDIO_PORT_IC_TXFIFO_INT_MSK, 0);
-       regmap_write(port->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0);
-       regmap_update_bits(port->regmap, AUDIO_PORT_IC_TXFIFO_OP,
-               AUDIO_FIFO_START, AUDIO_FIFO_START);
-       regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_TX_CTRL,
-               IC_TX_ENABLE, IC_TX_ENABLE);
-}
-
-static void sirf_audio_port_tx_disable(struct sirf_audio_port *port)
-{
-       regmap_write(port->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0);
-       regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_TX_CTRL,
-               IC_TX_ENABLE, ~IC_TX_ENABLE);
-}
-
-static void sirf_audio_port_rx_enable(struct sirf_audio_port *port,
-       int channels)
-{
-       regmap_update_bits(port->regmap, AUDIO_PORT_IC_RXFIFO_OP,
-               AUDIO_FIFO_RESET, AUDIO_FIFO_RESET);
-       regmap_write(port->regmap, AUDIO_PORT_IC_RXFIFO_INT_MSK, 0);
-       regmap_write(port->regmap, AUDIO_PORT_IC_RXFIFO_OP, 0);
-       regmap_update_bits(port->regmap, AUDIO_PORT_IC_RXFIFO_OP,
-               AUDIO_FIFO_START, AUDIO_FIFO_START);
-       if (channels == 1)
-               regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_RX_CTRL,
-                       IC_RX_ENABLE_MONO, IC_RX_ENABLE_MONO);
-       else
-               regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_RX_CTRL,
-                       IC_RX_ENABLE_STEREO, IC_RX_ENABLE_STEREO);
-}
-
-static void sirf_audio_port_rx_disable(struct sirf_audio_port *port)
-{
-       regmap_update_bits(port->regmap, AUDIO_PORT_IC_CODEC_RX_CTRL,
-                       IC_RX_ENABLE_STEREO, ~IC_RX_ENABLE_STEREO);
-}
 
 static int sirf_audio_port_dai_probe(struct snd_soc_dai *dai)
 {
@@ -69,41 +24,6 @@ static int sirf_audio_port_dai_probe(struct snd_soc_dai *dai)
        return 0;
 }
 
-static int sirf_audio_port_trigger(struct snd_pcm_substream *substream, int cmd,
-       struct snd_soc_dai *dai)
-{
-       struct sirf_audio_port *port = snd_soc_dai_get_drvdata(dai);
-       int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_STOP:
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               if (playback)
-                       sirf_audio_port_tx_disable(port);
-               else
-                       sirf_audio_port_rx_disable(port);
-               break;
-       case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_RESUME:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               if (playback)
-                       sirf_audio_port_tx_enable(port);
-               else
-                       sirf_audio_port_rx_enable(port,
-                               substream->runtime->channels);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static const struct snd_soc_dai_ops sirf_audio_port_dai_ops = {
-       .trigger = sirf_audio_port_trigger,
-};
-
 static struct snd_soc_dai_driver sirf_audio_port_dai = {
        .probe = sirf_audio_port_dai_probe,
        .name = "sirf-audio-port",
@@ -120,49 +40,22 @@ static struct snd_soc_dai_driver sirf_audio_port_dai = {
                .rates = SNDRV_PCM_RATE_48000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,
        },
-       .ops = &sirf_audio_port_dai_ops,
 };
 
 static const struct snd_soc_component_driver sirf_audio_port_component = {
        .name       = "sirf-audio-port",
 };
 
-static const struct regmap_config sirf_audio_port_regmap_config = {
-       .reg_bits = 32,
-       .reg_stride = 4,
-       .val_bits = 32,
-       .max_register = AUDIO_PORT_IC_RXFIFO_INT_MSK,
-       .cache_type = REGCACHE_NONE,
-};
-
 static int sirf_audio_port_probe(struct platform_device *pdev)
 {
        int ret;
        struct sirf_audio_port *port;
-       void __iomem *base;
-       struct resource *mem_res;
 
        port = devm_kzalloc(&pdev->dev,
                        sizeof(struct sirf_audio_port), GFP_KERNEL);
        if (!port)
                return -ENOMEM;
 
-       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!mem_res) {
-               dev_err(&pdev->dev, "no mem resource?\n");
-               return -ENODEV;
-       }
-
-       base = devm_ioremap(&pdev->dev, mem_res->start,
-                       resource_size(mem_res));
-       if (base == NULL)
-               return -ENOMEM;
-
-       port->regmap = devm_regmap_init_mmio(&pdev->dev, base,
-                                           &sirf_audio_port_regmap_config);
-       if (IS_ERR(port->regmap))
-               return PTR_ERR(port->regmap);
-
        ret = devm_snd_soc_register_component(&pdev->dev,
                        &sirf_audio_port_component, &sirf_audio_port_dai, 1);
        if (ret)
diff --git a/sound/soc/sirf/sirf-audio-port.h b/sound/soc/sirf/sirf-audio-port.h
deleted file mode 100644 (file)
index f32dc54..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * SiRF Audio port controllers define
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- *
- * Licensed under GPLv2 or later.
- */
-
-#ifndef _SIRF_AUDIO_PORT_H
-#define _SIRF_AUDIO_PORT_H
-
-#define AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK     0x3F
-#define AUDIO_PORT_TX_FIFO_SC_OFFSET    0
-#define AUDIO_PORT_TX_FIFO_LC_OFFSET    10
-#define AUDIO_PORT_TX_FIFO_HC_OFFSET    20
-
-#define TX_FIFO_SC(x)           (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
-                               << AUDIO_PORT_TX_FIFO_SC_OFFSET)
-#define TX_FIFO_LC(x)           (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
-                               << AUDIO_PORT_TX_FIFO_LC_OFFSET)
-#define TX_FIFO_HC(x)           (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
-                               << AUDIO_PORT_TX_FIFO_HC_OFFSET)
-
-#define AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK     0x0F
-#define AUDIO_PORT_RX_FIFO_SC_OFFSET    0
-#define AUDIO_PORT_RX_FIFO_LC_OFFSET    10
-#define AUDIO_PORT_RX_FIFO_HC_OFFSET    20
-
-#define RX_FIFO_SC(x)           (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
-                               << AUDIO_PORT_RX_FIFO_SC_OFFSET)
-#define RX_FIFO_LC(x)           (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
-                               << AUDIO_PORT_RX_FIFO_LC_OFFSET)
-#define RX_FIFO_HC(x)           (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
-                               << AUDIO_PORT_RX_FIFO_HC_OFFSET)
-#define AUDIO_PORT_IC_CODEC_TX_CTRL            (0x00F4)
-#define AUDIO_PORT_IC_CODEC_RX_CTRL            (0x00F8)
-
-#define AUDIO_PORT_IC_TXFIFO_OP                        (0x00FC)
-#define AUDIO_PORT_IC_TXFIFO_LEV_CHK           (0x0100)
-#define AUDIO_PORT_IC_TXFIFO_STS               (0x0104)
-#define AUDIO_PORT_IC_TXFIFO_INT               (0x0108)
-#define AUDIO_PORT_IC_TXFIFO_INT_MSK           (0x010C)
-
-#define AUDIO_PORT_IC_RXFIFO_OP                        (0x0110)
-#define AUDIO_PORT_IC_RXFIFO_LEV_CHK           (0x0114)
-#define AUDIO_PORT_IC_RXFIFO_STS               (0x0118)
-#define AUDIO_PORT_IC_RXFIFO_INT               (0x011C)
-#define AUDIO_PORT_IC_RXFIFO_INT_MSK           (0x0120)
-
-#define AUDIO_FIFO_START               (1 << 0)
-#define AUDIO_FIFO_RESET               (1 << 1)
-
-#define AUDIO_FIFO_FULL                        (1 << 0)
-#define AUDIO_FIFO_EMPTY               (1 << 1)
-#define AUDIO_FIFO_OFLOW               (1 << 2)
-#define AUDIO_FIFO_UFLOW               (1 << 3)
-
-#define IC_TX_ENABLE           (0x03)
-#define IC_RX_ENABLE_MONO      (0x01)
-#define IC_RX_ENABLE_STEREO    (0x03)
-
-#endif /*__SIRF_AUDIO_PORT_H*/
index bfed3e4..00e70b6 100644 (file)
@@ -72,6 +72,9 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
 
        reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
 
+       if (!reg_size)
+               return 0;
+
        mutex_init(&codec->cache_rw_mutex);
 
        dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n",
@@ -162,8 +165,6 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
                                                  i, codec_drv->reg_word_size) == val)
                                continue;
 
-               WARN_ON(!snd_soc_codec_writable_register(codec, i));
-
                ret = snd_soc_write(codec, i, val);
                if (ret)
                        return ret;
index 91083e6..10f7f1d 100644 (file)
@@ -203,7 +203,6 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
 
        if (platform->driver->compr_ops && platform->driver->compr_ops->free)
                platform->driver->compr_ops->free(cstream);
-       cpu_dai->runtime = NULL;
 
        if (cstream->direction == SND_COMPRESS_PLAYBACK) {
                if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
@@ -317,8 +316,9 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd)
                cmd == SND_COMPR_TRIGGER_DRAIN) {
 
                if (platform->driver->compr_ops &&
-                       platform->driver->compr_ops->trigger)
-               return platform->driver->compr_ops->trigger(cstream, cmd);
+                   platform->driver->compr_ops->trigger)
+                       return platform->driver->compr_ops->trigger(cstream,
+                                                                   cmd);
        }
 
        if (cstream->direction == SND_COMPRESS_PLAYBACK)
index 051c006..b87d7d8 100644 (file)
@@ -154,22 +154,15 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf,
                step = codec->driver->reg_cache_step;
 
        for (i = 0; i < codec->driver->reg_cache_size; i += step) {
-               if (!snd_soc_codec_readable_register(codec, i))
-                       continue;
-               if (codec->driver->display_register) {
-                       count += codec->driver->display_register(codec, buf + count,
-                                                        PAGE_SIZE - count, i);
-               } else {
-                       /* only support larger than PAGE_SIZE bytes debugfs
-                        * entries for the default case */
-                       if (p >= pos) {
-                               if (total + len >= count - 1)
-                                       break;
-                               format_register_str(codec, i, buf + total, len);
-                               total += len;
-                       }
-                       p += len;
+               /* only support larger than PAGE_SIZE bytes debugfs
+                * entries for the default case */
+               if (p >= pos) {
+                       if (total + len >= count - 1)
+                               break;
+                       format_register_str(codec, i, buf + total, len);
+                       total += len;
                }
+               p += len;
        }
 
        total = min(total, count - 1);
@@ -663,8 +656,8 @@ int snd_soc_suspend(struct device *dev)
                                codec->driver->suspend(codec);
                                codec->suspended = 1;
                                codec->cache_sync = 1;
-                               if (codec->using_regmap)
-                                       regcache_mark_dirty(codec->control_data);
+                               if (codec->component.regmap)
+                                       regcache_mark_dirty(codec->component.regmap);
                                /* deactivate pins to sleep state */
                                pinctrl_pm_select_sleep_state(codec->dev);
                                break;
@@ -854,14 +847,47 @@ EXPORT_SYMBOL_GPL(snd_soc_resume);
 static const struct snd_soc_dai_ops null_dai_ops = {
 };
 
+static struct snd_soc_codec *soc_find_codec(const struct device_node *codec_of_node,
+                                           const char *codec_name)
+{
+       struct snd_soc_codec *codec;
+
+       list_for_each_entry(codec, &codec_list, list) {
+               if (codec_of_node) {
+                       if (codec->dev->of_node != codec_of_node)
+                               continue;
+               } else {
+                       if (strcmp(codec->name, codec_name))
+                               continue;
+               }
+
+               return codec;
+       }
+
+       return NULL;
+}
+
+static struct snd_soc_dai *soc_find_codec_dai(struct snd_soc_codec *codec,
+                                             const char *codec_dai_name)
+{
+       struct snd_soc_dai *codec_dai;
+
+       list_for_each_entry(codec_dai, &codec->component.dai_list, list) {
+               if (!strcmp(codec_dai->name, codec_dai_name)) {
+                       return codec_dai;
+               }
+       }
+
+       return NULL;
+}
+
 static int soc_bind_dai_link(struct snd_soc_card *card, int num)
 {
        struct snd_soc_dai_link *dai_link = &card->dai_link[num];
        struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
        struct snd_soc_component *component;
-       struct snd_soc_codec *codec;
        struct snd_soc_platform *platform;
-       struct snd_soc_dai *codec_dai, *cpu_dai;
+       struct snd_soc_dai *cpu_dai;
        const char *platform_name;
 
        dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
@@ -889,42 +915,24 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
                return -EPROBE_DEFER;
        }
 
-       /* Find CODEC from registered CODECs */
-       list_for_each_entry(codec, &codec_list, list) {
-               if (dai_link->codec_of_node) {
-                       if (codec->dev->of_node != dai_link->codec_of_node)
-                               continue;
-               } else {
-                       if (strcmp(codec->name, dai_link->codec_name))
-                               continue;
-               }
-
-               rtd->codec = codec;
-
-               /*
-                * CODEC found, so find CODEC DAI from registered DAIs from
-                * this CODEC
-                */
-               list_for_each_entry(codec_dai, &codec->component.dai_list, list) {
-                       if (!strcmp(codec_dai->name, dai_link->codec_dai_name)) {
-                               rtd->codec_dai = codec_dai;
-                               break;
-                       }
-               }
-
-               if (!rtd->codec_dai) {
-                       dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
-                               dai_link->codec_dai_name);
-                       return -EPROBE_DEFER;
-               }
-       }
-
+       /* Find CODEC from registered list */
+       rtd->codec = soc_find_codec(dai_link->codec_of_node,
+                                   dai_link->codec_name);
        if (!rtd->codec) {
                dev_err(card->dev, "ASoC: CODEC %s not registered\n",
                        dai_link->codec_name);
                return -EPROBE_DEFER;
        }
 
+       /* Find CODEC DAI from registered list */
+       rtd->codec_dai = soc_find_codec_dai(rtd->codec,
+                                           dai_link->codec_dai_name);
+       if (!rtd->codec_dai) {
+               dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
+                       dai_link->codec_dai_name);
+               return -EPROBE_DEFER;
+       }
+
        /* if there's no platform we match on the empty platform */
        platform_name = dai_link->platform_name;
        if (!platform_name && !dai_link->platform_of_node)
@@ -995,6 +1003,23 @@ static void soc_remove_codec(struct snd_soc_codec *codec)
        module_put(codec->dev->driver->owner);
 }
 
+static void soc_remove_codec_dai(struct snd_soc_dai *codec_dai, int order)
+{
+       int err;
+
+       if (codec_dai && codec_dai->probed &&
+                       codec_dai->driver->remove_order == order) {
+               if (codec_dai->driver->remove) {
+                       err = codec_dai->driver->remove(codec_dai);
+                       if (err < 0)
+                               dev_err(codec_dai->dev,
+                                       "ASoC: failed to remove %s: %d\n",
+                                       codec_dai->name, err);
+               }
+               codec_dai->probed = 0;
+       }
+}
+
 static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
 {
        struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
@@ -1010,18 +1035,7 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
        }
 
        /* remove the CODEC DAI */
-       if (codec_dai && codec_dai->probed &&
-                       codec_dai->driver->remove_order == order) {
-               if (codec_dai->driver->remove) {
-                       err = codec_dai->driver->remove(codec_dai);
-                       if (err < 0)
-                               dev_err(codec_dai->dev,
-                                       "ASoC: failed to remove %s: %d\n",
-                                       codec_dai->name, err);
-               }
-               codec_dai->probed = 0;
-               list_del(&codec_dai->card_list);
-       }
+       soc_remove_codec_dai(codec_dai, order);
 
        /* remove the cpu_dai */
        if (cpu_dai && cpu_dai->probed &&
@@ -1034,7 +1048,6 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
                                        cpu_dai->name, err);
                }
                cpu_dai->probed = 0;
-               list_del(&cpu_dai->card_list);
 
                if (!cpu_dai->codec) {
                        snd_soc_dapm_free(&cpu_dai->dapm);
@@ -1104,10 +1117,12 @@ static void soc_set_name_prefix(struct snd_soc_card *card,
 
        for (i = 0; i < card->num_configs; i++) {
                struct snd_soc_codec_conf *map = &card->codec_conf[i];
-               if (map->dev_name && !strcmp(codec->name, map->dev_name)) {
-                       codec->name_prefix = map->name_prefix;
-                       break;
-               }
+               if (map->of_node && codec->dev->of_node != map->of_node)
+                       continue;
+               if (map->dev_name && strcmp(codec->name, map->dev_name))
+                       continue;
+               codec->name_prefix = map->name_prefix;
+               break;
        }
 }
 
@@ -1127,26 +1142,31 @@ static int soc_probe_codec(struct snd_soc_card *card,
 
        soc_init_codec_debugfs(codec);
 
-       if (driver->dapm_widgets)
-               snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
-                                         driver->num_dapm_widgets);
+       if (driver->dapm_widgets) {
+               ret = snd_soc_dapm_new_controls(&codec->dapm,
+                                               driver->dapm_widgets,
+                                               driver->num_dapm_widgets);
 
-       /* Create DAPM widgets for each DAI stream */
-       list_for_each_entry(dai, &codec->component.dai_list, list)
-               snd_soc_dapm_new_dai_widgets(&codec->dapm, dai);
+               if (ret != 0) {
+                       dev_err(codec->dev,
+                               "Failed to create new controls %d\n", ret);
+                       goto err_probe;
+               }
+       }
 
-       codec->dapm.idle_bias_off = driver->idle_bias_off;
+       /* Create DAPM widgets for each DAI stream */
+       list_for_each_entry(dai, &codec->component.dai_list, list) {
+               ret = snd_soc_dapm_new_dai_widgets(&codec->dapm, dai);
 
-       if (!codec->write && dev_get_regmap(codec->dev, NULL)) {
-               /* Set the default I/O up try regmap */
-               ret = snd_soc_codec_set_cache_io(codec, NULL);
-               if (ret < 0) {
+               if (ret != 0) {
                        dev_err(codec->dev,
-                               "Failed to set cache I/O: %d\n", ret);
+                               "Failed to create DAI widgets %d\n", ret);
                        goto err_probe;
                }
        }
 
+       codec->dapm.idle_bias_off = driver->idle_bias_off;
+
        if (driver->probe) {
                ret = driver->probe(codec);
                if (ret < 0) {
@@ -1246,6 +1266,50 @@ static void rtd_release(struct device *dev)
        kfree(dev);
 }
 
+static int soc_aux_dev_init(struct snd_soc_card *card,
+                           struct snd_soc_codec *codec,
+                           int num)
+{
+       struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
+       struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
+       int ret;
+
+       rtd->card = card;
+
+       /* do machine specific initialization */
+       if (aux_dev->init) {
+               ret = aux_dev->init(&codec->dapm);
+               if (ret < 0)
+                       return ret;
+       }
+
+       rtd->codec = codec;
+
+       return 0;
+}
+
+static int soc_dai_link_init(struct snd_soc_card *card,
+                            struct snd_soc_codec *codec,
+                            int num)
+{
+       struct snd_soc_dai_link *dai_link =  &card->dai_link[num];
+       struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
+       int ret;
+
+       rtd->card = card;
+
+       /* do machine specific initialization */
+       if (dai_link->init) {
+               ret = dai_link->init(rtd);
+               if (ret < 0)
+                       return ret;
+       }
+
+       rtd->codec = codec;
+
+       return 0;
+}
+
 static int soc_post_component_init(struct snd_soc_card *card,
                                   struct snd_soc_codec *codec,
                                   int num, int dailess)
@@ -1260,26 +1324,20 @@ static int soc_post_component_init(struct snd_soc_card *card,
                dai_link = &card->dai_link[num];
                rtd = &card->rtd[num];
                name = dai_link->name;
+               ret = soc_dai_link_init(card, codec, num);
        } else {
                aux_dev = &card->aux_dev[num];
                rtd = &card->rtd_aux[num];
                name = aux_dev->name;
+               ret = soc_aux_dev_init(card, codec, num);
        }
-       rtd->card = card;
 
-       /* do machine specific initialization */
-       if (!dailess && dai_link->init)
-               ret = dai_link->init(rtd);
-       else if (dailess && aux_dev->init)
-               ret = aux_dev->init(&codec->dapm);
        if (ret < 0) {
                dev_err(card->dev, "ASoC: failed to init %s: %d\n", name, ret);
                return ret;
        }
 
        /* register the rtd device */
-       rtd->codec = codec;
-
        rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
        if (!rtd->dev)
                return -ENOMEM;
@@ -1366,6 +1424,66 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num,
        return 0;
 }
 
+static int soc_probe_codec_dai(struct snd_soc_card *card,
+                              struct snd_soc_dai *codec_dai,
+                              int order)
+{
+       int ret;
+
+       if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
+               if (codec_dai->driver->probe) {
+                       ret = codec_dai->driver->probe(codec_dai);
+                       if (ret < 0) {
+                               dev_err(codec_dai->dev,
+                                       "ASoC: failed to probe CODEC DAI %s: %d\n",
+                                       codec_dai->name, ret);
+                               return ret;
+                       }
+               }
+
+               /* mark codec_dai as probed and add to card dai list */
+               codec_dai->probed = 1;
+       }
+
+       return 0;
+}
+
+static int soc_link_dai_widgets(struct snd_soc_card *card,
+                               struct snd_soc_dai_link *dai_link,
+                               struct snd_soc_dai *cpu_dai,
+                               struct snd_soc_dai *codec_dai)
+{
+       struct snd_soc_dapm_widget *play_w, *capture_w;
+       int ret;
+
+       /* link the DAI widgets */
+       play_w = codec_dai->playback_widget;
+       capture_w = cpu_dai->capture_widget;
+       if (play_w && capture_w) {
+               ret = snd_soc_dapm_new_pcm(card, dai_link->params,
+                                          capture_w, play_w);
+               if (ret != 0) {
+                       dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
+                               play_w->name, capture_w->name, ret);
+                       return ret;
+               }
+       }
+
+       play_w = cpu_dai->playback_widget;
+       capture_w = codec_dai->capture_widget;
+       if (play_w && capture_w) {
+               ret = snd_soc_dapm_new_pcm(card, dai_link->params,
+                                          capture_w, play_w);
+               if (ret != 0) {
+                       dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
+                               play_w->name, capture_w->name, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
 {
        struct snd_soc_dai_link *dai_link = &card->dai_link[num];
@@ -1374,7 +1492,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dapm_widget *play_w, *capture_w;
        int ret;
 
        dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
@@ -1410,26 +1527,12 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
                        }
                }
                cpu_dai->probed = 1;
-               /* mark cpu_dai as probed and add to card dai list */
-               list_add(&cpu_dai->card_list, &card->dai_dev_list);
        }
 
        /* probe the CODEC DAI */
-       if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
-               if (codec_dai->driver->probe) {
-                       ret = codec_dai->driver->probe(codec_dai);
-                       if (ret < 0) {
-                               dev_err(codec_dai->dev,
-                                       "ASoC: failed to probe CODEC DAI %s: %d\n",
-                                       codec_dai->name, ret);
-                               return ret;
-                       }
-               }
-
-               /* mark codec_dai as probed and add to card dai list */
-               codec_dai->probed = 1;
-               list_add(&codec_dai->card_list, &card->dai_dev_list);
-       }
+       ret = soc_probe_codec_dai(card, codec_dai, order);
+       if (ret)
+               return ret;
 
        /* complete DAI probe during last probe */
        if (order != SND_SOC_COMP_ORDER_LAST)
@@ -1467,29 +1570,10 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
                                                codec2codec_close_delayed_work);
 
                        /* link the DAI widgets */
-                       play_w = codec_dai->playback_widget;
-                       capture_w = cpu_dai->capture_widget;
-                       if (play_w && capture_w) {
-                               ret = snd_soc_dapm_new_pcm(card, dai_link->params,
-                                                  capture_w, play_w);
-                               if (ret != 0) {
-                                       dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
-                                               play_w->name, capture_w->name, ret);
-                                       return ret;
-                               }
-                       }
-
-                       play_w = cpu_dai->playback_widget;
-                       capture_w = codec_dai->capture_widget;
-                       if (play_w && capture_w) {
-                               ret = snd_soc_dapm_new_pcm(card, dai_link->params,
-                                                  capture_w, play_w);
-                               if (ret != 0) {
-                                       dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
-                                               play_w->name, capture_w->name, ret);
-                                       return ret;
-                               }
-                       }
+                       ret = soc_link_dai_widgets(card, dai_link,
+                                       cpu_dai, codec_dai);
+                       if (ret)
+                               return ret;
                }
        }
 
@@ -1501,14 +1585,15 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
 }
 
 #ifdef CONFIG_SND_SOC_AC97_BUS
-static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
+static int soc_register_ac97_codec(struct snd_soc_codec *codec,
+                                  struct snd_soc_dai *codec_dai)
 {
        int ret;
 
        /* Only instantiate AC97 if not already done by the adaptor
         * for the generic AC97 subsystem.
         */
-       if (rtd->codec_dai->driver->ac97_control && !rtd->codec->ac97_registered) {
+       if (codec_dai->driver->ac97_control && !codec->ac97_registered) {
                /*
                 * It is possible that the AC97 device is already registered to
                 * the device subsystem. This happens when the device is created
@@ -1517,76 +1602,101 @@ static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
                 *
                 * In those cases we don't try to register the device again.
                 */
-               if (!rtd->codec->ac97_created)
+               if (!codec->ac97_created)
                        return 0;
 
-               ret = soc_ac97_dev_register(rtd->codec);
+               ret = soc_ac97_dev_register(codec);
                if (ret < 0) {
-                       dev_err(rtd->codec->dev,
+                       dev_err(codec->dev,
                                "ASoC: AC97 device register failed: %d\n", ret);
                        return ret;
                }
 
-               rtd->codec->ac97_registered = 1;
+               codec->ac97_registered = 1;
        }
        return 0;
 }
 
-static void soc_unregister_ac97_dai_link(struct snd_soc_codec *codec)
+static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
+{
+       return soc_register_ac97_codec(rtd->codec, rtd->codec_dai);
+}
+
+static void soc_unregister_ac97_codec(struct snd_soc_codec *codec)
 {
        if (codec->ac97_registered) {
                soc_ac97_dev_unregister(codec);
                codec->ac97_registered = 0;
        }
 }
+
+static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
+{
+       soc_unregister_ac97_codec(rtd->codec);
+}
 #endif
 
-static int soc_check_aux_dev(struct snd_soc_card *card, int num)
+static struct snd_soc_codec *soc_find_matching_codec(struct snd_soc_card *card,
+       int num)
 {
        struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
        struct snd_soc_codec *codec;
 
-       /* find CODEC from registered CODECs*/
+       /* find CODEC from registered CODECs */
        list_for_each_entry(codec, &codec_list, list) {
-               if (!strcmp(codec->name, aux_dev->codec_name))
-                       return 0;
+               if (aux_dev->codec_of_node &&
+                  (codec->dev->of_node != aux_dev->codec_of_node))
+                       continue;
+               if (aux_dev->codec_name && strcmp(codec->name, aux_dev->codec_name))
+                       continue;
+               return codec;
        }
 
-       dev_err(card->dev, "ASoC: %s not registered\n", aux_dev->codec_name);
+       return NULL;
+}
 
+static int soc_check_aux_dev(struct snd_soc_card *card, int num)
+{
+       struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
+       const char *codecname = aux_dev->codec_name;
+       struct snd_soc_codec *codec = soc_find_matching_codec(card, num);
+
+       if (codec)
+               return 0;
+       if (aux_dev->codec_of_node)
+               codecname = of_node_full_name(aux_dev->codec_of_node);
+
+       dev_err(card->dev, "ASoC: %s not registered\n", codecname);
        return -EPROBE_DEFER;
 }
 
 static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
 {
        struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
-       struct snd_soc_codec *codec;
+       const char *codecname = aux_dev->codec_name;
        int ret = -ENODEV;
+       struct snd_soc_codec *codec = soc_find_matching_codec(card, num);
 
-       /* find CODEC from registered CODECs*/
-       list_for_each_entry(codec, &codec_list, list) {
-               if (!strcmp(codec->name, aux_dev->codec_name)) {
-                       if (codec->probed) {
-                               dev_err(codec->dev,
-                                       "ASoC: codec already probed");
-                               ret = -EBUSY;
-                               goto out;
-                       }
-                       goto found;
-               }
+       if (!codec) {
+               if (aux_dev->codec_of_node)
+                       codecname = of_node_full_name(aux_dev->codec_of_node);
+
+               /* codec not found */
+               dev_err(card->dev, "ASoC: codec %s not found", codecname);
+               return -EPROBE_DEFER;
+       }
+
+       if (codec->probed) {
+               dev_err(codec->dev, "ASoC: codec already probed");
+               return -EBUSY;
        }
-       /* codec not found */
-       dev_err(card->dev, "ASoC: codec %s not found", aux_dev->codec_name);
-       return -EPROBE_DEFER;
 
-found:
        ret = soc_probe_codec(card, codec);
        if (ret < 0)
                return ret;
 
        ret = soc_post_component_init(card, codec, num, 1);
 
-out:
        return ret;
 }
 
@@ -1837,7 +1947,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                        dev_err(card->dev,
                                "ASoC: failed to register AC97: %d\n", ret);
                        while (--i >= 0)
-                               soc_unregister_ac97_dai_link(card->rtd[i].codec);
+                               soc_unregister_ac97_dai_link(&card->rtd[i]);
                        goto probe_aux_dev_err;
                }
        }
@@ -1979,92 +2089,6 @@ static struct platform_driver soc_driver = {
        .remove         = soc_remove,
 };
 
-/**
- * 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,
-                                   unsigned int reg)
-{
-       if (codec->volatile_register)
-               return codec->volatile_register(codec, reg);
-       else
-               return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register);
-
-/**
- * snd_soc_codec_readable_register: Report if a register is readable.
- *
- * @codec: CODEC to query.
- * @reg: Register to query.
- *
- * Boolean function indicating if a CODEC register is readable.
- */
-int snd_soc_codec_readable_register(struct snd_soc_codec *codec,
-                                   unsigned int reg)
-{
-       if (codec->readable_register)
-               return codec->readable_register(codec, reg);
-       else
-               return 1;
-}
-EXPORT_SYMBOL_GPL(snd_soc_codec_readable_register);
-
-/**
- * snd_soc_codec_writable_register: Report if a register is writable.
- *
- * @codec: CODEC to query.
- * @reg: Register to query.
- *
- * Boolean function indicating if a CODEC register is writable.
- */
-int snd_soc_codec_writable_register(struct snd_soc_codec *codec,
-                                   unsigned int reg)
-{
-       if (codec->writable_register)
-               return codec->writable_register(codec, reg);
-       else
-               return 1;
-}
-EXPORT_SYMBOL_GPL(snd_soc_codec_writable_register);
-
-int snd_soc_platform_read(struct snd_soc_platform *platform,
-                                       unsigned int reg)
-{
-       unsigned int ret;
-
-       if (!platform->driver->read) {
-               dev_err(platform->dev, "ASoC: platform has no read back\n");
-               return -1;
-       }
-
-       ret = platform->driver->read(platform, reg);
-       dev_dbg(platform->dev, "read %x => %x\n", reg, ret);
-       trace_snd_soc_preg_read(platform, reg, ret);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_platform_read);
-
-int snd_soc_platform_write(struct snd_soc_platform *platform,
-                                        unsigned int reg, unsigned int val)
-{
-       if (!platform->driver->write) {
-               dev_err(platform->dev, "ASoC: platform has no write back\n");
-               return -1;
-       }
-
-       dev_dbg(platform->dev, "write %x = %x\n", reg, val);
-       trace_snd_soc_preg_write(platform, reg, val);
-       return platform->driver->write(platform, reg, val);
-}
-EXPORT_SYMBOL_GPL(snd_soc_platform_write);
-
 /**
  * snd_soc_new_ac97_codec - initailise AC97 device
  * @codec: audio codec
@@ -2153,28 +2177,28 @@ static int snd_soc_ac97_parse_pinctl(struct device *dev,
        p = devm_pinctrl_get(dev);
        if (IS_ERR(p)) {
                dev_err(dev, "Failed to get pinctrl\n");
-               return PTR_RET(p);
+               return PTR_ERR(p);
        }
        cfg->pctl = p;
 
        state = pinctrl_lookup_state(p, "ac97-reset");
        if (IS_ERR(state)) {
                dev_err(dev, "Can't find pinctrl state ac97-reset\n");
-               return PTR_RET(state);
+               return PTR_ERR(state);
        }
        cfg->pstate_reset = state;
 
        state = pinctrl_lookup_state(p, "ac97-warm-reset");
        if (IS_ERR(state)) {
                dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
-               return PTR_RET(state);
+               return PTR_ERR(state);
        }
        cfg->pstate_warm_reset = state;
 
        state = pinctrl_lookup_state(p, "ac97-running");
        if (IS_ERR(state)) {
                dev_err(dev, "Can't find pinctrl state ac97-running\n");
-               return PTR_RET(state);
+               return PTR_ERR(state);
        }
        cfg->pstate_run = state;
 
@@ -2273,7 +2297,7 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
 {
        mutex_lock(&codec->mutex);
 #ifdef CONFIG_SND_SOC_AC97_BUS
-       soc_unregister_ac97_dai_link(codec);
+       soc_unregister_ac97_codec(codec);
 #endif
        kfree(codec->ac97->bus);
        kfree(codec->ac97);
@@ -2283,118 +2307,6 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
 }
 EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
 
-unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg)
-{
-       unsigned int ret;
-
-       ret = codec->read(codec, reg);
-       dev_dbg(codec->dev, "read %x => %x\n", reg, ret);
-       trace_snd_soc_reg_read(codec, reg, ret);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_read);
-
-unsigned int snd_soc_write(struct snd_soc_codec *codec,
-                          unsigned int reg, unsigned int val)
-{
-       dev_dbg(codec->dev, "write %x = %x\n", reg, val);
-       trace_snd_soc_reg_write(codec, reg, val);
-       return codec->write(codec, reg, val);
-}
-EXPORT_SYMBOL_GPL(snd_soc_write);
-
-/**
- * snd_soc_update_bits - update codec register bits
- * @codec: audio codec
- * @reg: codec register
- * @mask: register mask
- * @value: new value
- *
- * Writes new register value.
- *
- * Returns 1 for change, 0 for no change, or negative error code.
- */
-int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
-                               unsigned int mask, unsigned int value)
-{
-       bool change;
-       unsigned int old, new;
-       int ret;
-
-       if (codec->using_regmap) {
-               ret = regmap_update_bits_check(codec->control_data, reg,
-                                              mask, value, &change);
-       } else {
-               ret = snd_soc_read(codec, reg);
-               if (ret < 0)
-                       return ret;
-
-               old = ret;
-               new = (old & ~mask) | (value & mask);
-               change = old != new;
-               if (change)
-                       ret = snd_soc_write(codec, reg, new);
-       }
-
-       if (ret < 0)
-               return ret;
-
-       return change;
-}
-EXPORT_SYMBOL_GPL(snd_soc_update_bits);
-
-/**
- * snd_soc_update_bits_locked - update codec register bits
- * @codec: audio codec
- * @reg: codec register
- * @mask: register mask
- * @value: new value
- *
- * Writes new register value, and takes the codec mutex.
- *
- * Returns 1 for change else 0.
- */
-int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
-                              unsigned short reg, unsigned int mask,
-                              unsigned int value)
-{
-       int change;
-
-       mutex_lock(&codec->mutex);
-       change = snd_soc_update_bits(codec, reg, mask, value);
-       mutex_unlock(&codec->mutex);
-
-       return change;
-}
-EXPORT_SYMBOL_GPL(snd_soc_update_bits_locked);
-
-/**
- * snd_soc_test_bits - test register for change
- * @codec: audio codec
- * @reg: codec register
- * @mask: register mask
- * @value: new value
- *
- * Tests a register with a new value and checks if the new value is
- * different from the old value.
- *
- * Returns 1 for change else 0.
- */
-int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
-                               unsigned int mask, unsigned int value)
-{
-       int change;
-       unsigned int old, new;
-
-       old = snd_soc_read(codec, reg);
-       new = (old & ~mask) | value;
-       change = old != new;
-
-       return change;
-}
-EXPORT_SYMBOL_GPL(snd_soc_test_bits);
-
 /**
  * snd_soc_cnew - create new control
  * @_template: control template
@@ -2491,7 +2403,7 @@ int snd_soc_add_codec_controls(struct snd_soc_codec *codec,
        struct snd_card *card = codec->card->snd_card;
 
        return snd_soc_add_controls(card, codec->dev, controls, num_controls,
-                       codec->name_prefix, codec);
+                       codec->name_prefix, &codec->component);
 }
 EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls);
 
@@ -2511,7 +2423,7 @@ int snd_soc_add_platform_controls(struct snd_soc_platform *platform,
        struct snd_card *card = platform->card->snd_card;
 
        return snd_soc_add_controls(card, platform->dev, controls, num_controls,
-                       NULL, platform);
+                       NULL, &platform->component);
 }
 EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls);
 
@@ -2595,12 +2507,15 @@ EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
 int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val, item;
        unsigned int reg_val;
+       int ret;
 
-       reg_val = snd_soc_read(codec, e->reg);
+       ret = snd_soc_component_read(component, e->reg, &reg_val);
+       if (ret)
+               return ret;
        val = (reg_val >> e->shift_l) & e->mask;
        item = snd_soc_enum_val_to_item(e, val);
        ucontrol->value.enumerated.item[0] = item;
@@ -2626,7 +2541,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
 int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int *item = ucontrol->value.enumerated.item;
        unsigned int val;
@@ -2643,38 +2558,48 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
                mask |= e->mask << e->shift_r;
        }
 
-       return snd_soc_update_bits_locked(codec, e->reg, mask, val);
+       return snd_soc_component_update_bits(component, e->reg, mask, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
 
 /**
  * snd_soc_read_signed - Read a codec register and interprete as signed value
- * @codec: codec
+ * @component: component
  * @reg: Register to read
  * @mask: Mask to use after shifting the register value
  * @shift: Right shift of register value
  * @sign_bit: Bit that describes if a number is negative or not.
+ * @signed_val: Pointer to where the read value should be stored
  *
  * This functions reads a codec register. The register value is shifted right
  * by 'shift' bits and masked with the given 'mask'. Afterwards it translates
  * the given registervalue into a signed integer if sign_bit is non-zero.
  *
- * Returns the register value as signed int.
+ * Returns 0 on sucess, otherwise an error value
  */
-static int snd_soc_read_signed(struct snd_soc_codec *codec, unsigned int reg,
-               unsigned int mask, unsigned int shift, unsigned int sign_bit)
+static int snd_soc_read_signed(struct snd_soc_component *component,
+       unsigned int reg, unsigned int mask, unsigned int shift,
+       unsigned int sign_bit, int *signed_val)
 {
        int ret;
        unsigned int val;
 
-       val = (snd_soc_read(codec, reg) >> shift) & mask;
+       ret = snd_soc_component_read(component, reg, &val);
+       if (ret < 0)
+               return ret;
 
-       if (!sign_bit)
-               return val;
+       val = (val >> shift) & mask;
+
+       if (!sign_bit) {
+               *signed_val = val;
+               return 0;
+       }
 
        /* non-negative number */
-       if (!(val & BIT(sign_bit)))
-               return val;
+       if (!(val & BIT(sign_bit))) {
+               *signed_val = val;
+               return 0;
+       }
 
        ret = val;
 
@@ -2686,7 +2611,9 @@ static int snd_soc_read_signed(struct snd_soc_codec *codec, unsigned int reg,
         */
        ret |= ~((int)(BIT(sign_bit) - 1));
 
-       return ret;
+       *signed_val = ret;
+
+       return 0;
 }
 
 /**
@@ -2735,9 +2662,9 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
 int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int reg2 = mc->rreg;
        unsigned int shift = mc->shift;
@@ -2747,25 +2674,32 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
        int sign_bit = mc->sign_bit;
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
+       int val;
+       int ret;
 
        if (sign_bit)
                mask = BIT(sign_bit + 1) - 1;
 
-       ucontrol->value.integer.value[0] = snd_soc_read_signed(codec, reg, mask,
-                       shift, sign_bit) - min;
+       ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
+       if (ret)
+               return ret;
+
+       ucontrol->value.integer.value[0] = val - min;
        if (invert)
                ucontrol->value.integer.value[0] =
                        max - ucontrol->value.integer.value[0];
 
        if (snd_soc_volsw_is_stereo(mc)) {
                if (reg == reg2)
-                       ucontrol->value.integer.value[1] =
-                               snd_soc_read_signed(codec, reg, mask, rshift,
-                                               sign_bit) - min;
+                       ret = snd_soc_read_signed(component, reg, mask, rshift,
+                               sign_bit, &val);
                else
-                       ucontrol->value.integer.value[1] =
-                               snd_soc_read_signed(codec, reg2, mask, shift,
-                                               sign_bit) - min;
+                       ret = snd_soc_read_signed(component, reg2, mask, shift,
+                               sign_bit, &val);
+               if (ret)
+                       return ret;
+
+               ucontrol->value.integer.value[1] = val - min;
                if (invert)
                        ucontrol->value.integer.value[1] =
                                max - ucontrol->value.integer.value[1];
@@ -2788,9 +2722,9 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
 int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int reg2 = mc->rreg;
        unsigned int shift = mc->shift;
@@ -2825,12 +2759,13 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
                        type_2r = true;
                }
        }
-       err = snd_soc_update_bits_locked(codec, reg, val_mask, val);
+       err = snd_soc_component_update_bits(component, reg, val_mask, val);
        if (err < 0)
                return err;
 
        if (type_2r)
-               err = snd_soc_update_bits_locked(codec, reg2, val_mask, val2);
+               err = snd_soc_component_update_bits(component, reg2, val_mask,
+                       val2);
 
        return err;
 }
@@ -2849,10 +2784,9 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
 int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
                      struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        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;
@@ -2860,13 +2794,23 @@ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
        int max = mc->max;
        int min = mc->min;
        int mask = (1 << (fls(min + max) - 1)) - 1;
+       unsigned int val;
+       int ret;
 
-       ucontrol->value.integer.value[0] =
-           ((snd_soc_read(codec, reg) >> shift) - min) & mask;
+       ret = snd_soc_component_read(component, reg, &val);
+       if (ret < 0)
+               return ret;
 
-       if (snd_soc_volsw_is_stereo(mc))
-               ucontrol->value.integer.value[1] =
-                       ((snd_soc_read(codec, reg2) >> rshift) - min) & mask;
+       ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               ret = snd_soc_component_read(component, reg2, &val);
+               if (ret < 0)
+                       return ret;
+
+               val = ((val >> rshift) - min) & mask;
+               ucontrol->value.integer.value[1] = val;
+       }
 
        return 0;
 }
@@ -2884,7 +2828,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
 int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
                         struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        struct soc_mixer_control *mc =
            (struct soc_mixer_control *)kcontrol->private_value;
 
@@ -2896,13 +2840,13 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
        int min = mc->min;
        int mask = (1 << (fls(min + max) - 1)) - 1;
        int err = 0;
-       unsigned short val, val_mask, val2 = 0;
+       unsigned int val, val_mask, val2 = 0;
 
        val_mask = mask << shift;
        val = (ucontrol->value.integer.value[0] + min) & mask;
        val = val << shift;
 
-       err = snd_soc_update_bits_locked(codec, reg, val_mask, val);
+       err = snd_soc_component_update_bits(component, reg, val_mask, val);
        if (err < 0)
                return err;
 
@@ -2911,10 +2855,10 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
                val2 = (ucontrol->value.integer.value[1] + min) & mask;
                val2 = val2 << rshift;
 
-               if (snd_soc_update_bits_locked(codec, reg2, val_mask, val2))
-                       return err;
+               err = snd_soc_component_update_bits(component, reg2, val_mask,
+                       val2);
        }
-       return 0;
+       return err;
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
 
@@ -2961,10 +2905,15 @@ int snd_soc_get_volsw_s8(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);
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
+       unsigned int val;
        int min = mc->min;
-       int val = snd_soc_read(codec, reg);
+       int ret;
+
+       ret = snd_soc_component_read(component, reg, &val);
+       if (ret)
+               return ret;
 
        ucontrol->value.integer.value[0] =
                ((signed char)(val & 0xff))-min;
@@ -2988,7 +2937,7 @@ int snd_soc_put_volsw_s8(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);
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
        int min = mc->min;
        unsigned int val;
@@ -2996,7 +2945,7 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
        val = (ucontrol->value.integer.value[0]+min) & 0xff;
        val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
 
-       return snd_soc_update_bits_locked(codec, reg, 0xffff, val);
+       return snd_soc_component_update_bits(component, reg, 0xffff, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
 
@@ -3045,7 +2994,7 @@ int snd_soc_put_volsw_range(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);
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int rreg = mc->rreg;
        unsigned int shift = mc->shift;
@@ -3062,7 +3011,7 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
        val_mask = mask << shift;
        val = val << shift;
 
-       ret = snd_soc_update_bits_locked(codec, reg, val_mask, val);
+       ret = snd_soc_component_update_bits(component, reg, val_mask, val);
        if (ret < 0)
                return ret;
 
@@ -3073,7 +3022,8 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
                val_mask = mask << shift;
                val = val << shift;
 
-               ret = snd_soc_update_bits_locked(codec, rreg, val_mask, val);
+               ret = snd_soc_component_update_bits(component, rreg, val_mask,
+                       val);
        }
 
        return ret;
@@ -3092,9 +3042,9 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
 int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int rreg = mc->rreg;
        unsigned int shift = mc->shift;
@@ -3102,9 +3052,14 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
        int max = mc->max;
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
+       unsigned int val;
+       int ret;
 
-       ucontrol->value.integer.value[0] =
-               (snd_soc_read(codec, reg) >> shift) & mask;
+       ret = snd_soc_component_read(component, reg, &val);
+       if (ret)
+               return ret;
+
+       ucontrol->value.integer.value[0] = (val >> shift) & mask;
        if (invert)
                ucontrol->value.integer.value[0] =
                        max - ucontrol->value.integer.value[0];
@@ -3112,8 +3067,11 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
                ucontrol->value.integer.value[0] - min;
 
        if (snd_soc_volsw_is_stereo(mc)) {
-               ucontrol->value.integer.value[1] =
-                       (snd_soc_read(codec, rreg) >> shift) & mask;
+               ret = snd_soc_component_read(component, rreg, &val);
+               if (ret)
+                       return ret;
+
+               ucontrol->value.integer.value[1] = (val >> shift) & mask;
                if (invert)
                        ucontrol->value.integer.value[1] =
                                max - ucontrol->value.integer.value[1];
@@ -3167,11 +3125,11 @@ EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
 int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
                       struct snd_ctl_elem_info *uinfo)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        struct soc_bytes *params = (void *)kcontrol->private_value;
 
        uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
-       uinfo->count = params->num_regs * codec->val_bytes;
+       uinfo->count = params->num_regs * component->val_bytes;
 
        return 0;
 }
@@ -3180,20 +3138,20 @@ EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
 int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
                      struct snd_ctl_elem_value *ucontrol)
 {
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        struct soc_bytes *params = (void *)kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        int ret;
 
-       if (codec->using_regmap)
-               ret = regmap_raw_read(codec->control_data, params->base,
+       if (component->regmap)
+               ret = regmap_raw_read(component->regmap, params->base,
                                      ucontrol->value.bytes.data,
-                                     params->num_regs * codec->val_bytes);
+                                     params->num_regs * component->val_bytes);
        else
                ret = -EINVAL;
 
        /* Hide any masked bytes to ensure consistent data reporting */
        if (ret == 0 && params->mask) {
-               switch (codec->val_bytes) {
+               switch (component->val_bytes) {
                case 1:
                        ucontrol->value.bytes.data[0] &= ~params->mask;
                        break;
@@ -3217,16 +3175,16 @@ EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
 int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
                      struct snd_ctl_elem_value *ucontrol)
 {
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        struct soc_bytes *params = (void *)kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        int ret, len;
        unsigned int val, mask;
        void *data;
 
-       if (!codec->using_regmap)
+       if (!component->regmap)
                return -EINVAL;
 
-       len = params->num_regs * codec->val_bytes;
+       len = params->num_regs * component->val_bytes;
 
        data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
        if (!data)
@@ -3238,27 +3196,27 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
         * copy.
         */
        if (params->mask) {
-               ret = regmap_read(codec->control_data, params->base, &val);
+               ret = regmap_read(component->regmap, params->base, &val);
                if (ret != 0)
                        goto out;
 
                val &= params->mask;
 
-               switch (codec->val_bytes) {
+               switch (component->val_bytes) {
                case 1:
                        ((u8 *)data)[0] &= ~params->mask;
                        ((u8 *)data)[0] |= val;
                        break;
                case 2:
                        mask = ~params->mask;
-                       ret = regmap_parse_val(codec->control_data,
+                       ret = regmap_parse_val(component->regmap,
                                                        &mask, &mask);
                        if (ret != 0)
                                goto out;
 
                        ((u16 *)data)[0] &= mask;
 
-                       ret = regmap_parse_val(codec->control_data,
+                       ret = regmap_parse_val(component->regmap,
                                                        &val, &val);
                        if (ret != 0)
                                goto out;
@@ -3267,14 +3225,14 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
                        break;
                case 4:
                        mask = ~params->mask;
-                       ret = regmap_parse_val(codec->control_data,
+                       ret = regmap_parse_val(component->regmap,
                                                        &mask, &mask);
                        if (ret != 0)
                                goto out;
 
                        ((u32 *)data)[0] &= mask;
 
-                       ret = regmap_parse_val(codec->control_data,
+                       ret = regmap_parse_val(component->regmap,
                                                        &val, &val);
                        if (ret != 0)
                                goto out;
@@ -3287,7 +3245,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
                }
        }
 
-       ret = regmap_raw_write(codec->control_data, params->base,
+       ret = regmap_raw_write(component->regmap, params->base,
                               data, len);
 
 out:
@@ -3297,6 +3255,18 @@ out:
 }
 EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
 
+int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_info *ucontrol)
+{
+       struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+
+       ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       ucontrol->count = params->max;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
+
 /**
  * snd_soc_info_xr_sx - signed multi register info callback
  * @kcontrol: mreg control
@@ -3338,24 +3308,27 @@ EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
 int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        struct soc_mreg_control *mc =
                (struct soc_mreg_control *)kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int regbase = mc->regbase;
        unsigned int regcount = mc->regcount;
-       unsigned int regwshift = codec->driver->reg_word_size * BITS_PER_BYTE;
+       unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
        unsigned int regwmask = (1<<regwshift)-1;
        unsigned int invert = mc->invert;
        unsigned long mask = (1UL<<mc->nbits)-1;
        long min = mc->min;
        long max = mc->max;
        long val = 0;
-       unsigned long regval;
+       unsigned int regval;
        unsigned int i;
+       int ret;
 
        for (i = 0; i < regcount; i++) {
-               regval = snd_soc_read(codec, regbase+i) & regwmask;
-               val |= regval << (regwshift*(regcount-i-1));
+               ret = snd_soc_component_read(component, regbase+i, &regval);
+               if (ret)
+                       return ret;
+               val |= (regval & regwmask) << (regwshift*(regcount-i-1));
        }
        val &= mask;
        if (min < 0 && val > max)
@@ -3384,12 +3357,12 @@ EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
 int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        struct soc_mreg_control *mc =
                (struct soc_mreg_control *)kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int regbase = mc->regbase;
        unsigned int regcount = mc->regcount;
-       unsigned int regwshift = codec->driver->reg_word_size * BITS_PER_BYTE;
+       unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
        unsigned int regwmask = (1<<regwshift)-1;
        unsigned int invert = mc->invert;
        unsigned long mask = (1UL<<mc->nbits)-1;
@@ -3404,7 +3377,7 @@ int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
        for (i = 0; i < regcount; i++) {
                regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
                regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
-               err = snd_soc_update_bits_locked(codec, regbase+i,
+               err = snd_soc_component_update_bits(component, regbase+i,
                                regmask, regval);
                if (err < 0)
                        return err;
@@ -3426,14 +3399,21 @@ EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
 int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int shift = mc->shift;
        unsigned int mask = 1 << shift;
        unsigned int invert = mc->invert != 0;
-       unsigned int val = snd_soc_read(codec, reg) & mask;
+       unsigned int val;
+       int ret;
+
+       ret = snd_soc_component_read(component, reg, &val);
+       if (ret)
+               return ret;
+
+       val &= mask;
 
        if (shift != 0 && val != 0)
                val = val >> shift;
@@ -3456,9 +3436,9 @@ EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
 int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
        unsigned int shift = mc->shift;
        unsigned int mask = 1 << shift;
@@ -3468,12 +3448,11 @@ int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
        unsigned int val2 = (strobe ^ invert) ? 0 : mask;
        int err;
 
-       err = snd_soc_update_bits_locked(codec, reg, mask, val1);
+       err = snd_soc_component_update_bits(component, reg, mask, val1);
        if (err < 0)
                return err;
 
-       err = snd_soc_update_bits_locked(codec, reg, mask, val2);
-       return err;
+       return snd_soc_component_update_bits(component, reg, mask, val2);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
 
@@ -3821,7 +3800,6 @@ int snd_soc_register_card(struct snd_soc_card *card)
        for (i = 0; i < card->num_links; i++)
                card->rtd[i].dai_link = &card->dai_link[i];
 
-       INIT_LIST_HEAD(&card->list);
        INIT_LIST_HEAD(&card->dapm_dirty);
        card->instantiated = 0;
        mutex_init(&card->mutex);
@@ -4037,6 +4015,8 @@ __snd_soc_register_component(struct device *dev,
                return -ENOMEM;
        }
 
+       mutex_init(&cmpnt->io_mutex);
+
        cmpnt->name = fmt_single_name(dev, &cmpnt->id);
        if (!cmpnt->name) {
                dev_err(dev, "ASoC: Failed to simplifying name\n");
@@ -4084,12 +4064,25 @@ int snd_soc_register_component(struct device *dev,
        }
 
        cmpnt->ignore_pmdown_time = true;
+       cmpnt->registered_as_component = true;
 
        return __snd_soc_register_component(dev, cmpnt, cmpnt_drv, NULL,
                                            dai_drv, num_dai, true);
 }
 EXPORT_SYMBOL_GPL(snd_soc_register_component);
 
+static void __snd_soc_unregister_component(struct snd_soc_component *cmpnt)
+{
+       snd_soc_unregister_dais(cmpnt);
+
+       mutex_lock(&client_mutex);
+       list_del(&cmpnt->list);
+       mutex_unlock(&client_mutex);
+
+       dev_dbg(cmpnt->dev, "ASoC: Unregistered component '%s'\n", cmpnt->name);
+       kfree(cmpnt->name);
+}
+
 /**
  * snd_soc_unregister_component - Unregister a component from the ASoC core
  *
@@ -4099,22 +4092,33 @@ void snd_soc_unregister_component(struct device *dev)
        struct snd_soc_component *cmpnt;
 
        list_for_each_entry(cmpnt, &component_list, list) {
-               if (dev == cmpnt->dev)
+               if (dev == cmpnt->dev && cmpnt->registered_as_component)
                        goto found;
        }
        return;
 
 found:
-       snd_soc_unregister_dais(cmpnt);
+       __snd_soc_unregister_component(cmpnt);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_component);
 
-       mutex_lock(&client_mutex);
-       list_del(&cmpnt->list);
-       mutex_unlock(&client_mutex);
+static int snd_soc_platform_drv_write(struct snd_soc_component *component,
+       unsigned int reg, unsigned int val)
+{
+       struct snd_soc_platform *platform = snd_soc_component_to_platform(component);
 
-       dev_dbg(dev, "ASoC: Unregistered component '%s'\n", cmpnt->name);
-       kfree(cmpnt->name);
+       return platform->driver->write(platform, reg, val);
+}
+
+static int snd_soc_platform_drv_read(struct snd_soc_component *component,
+       unsigned int reg, unsigned int *val)
+{
+       struct snd_soc_platform *platform = snd_soc_component_to_platform(component);
+
+       *val = platform->driver->read(platform, reg);
+
+       return 0;
 }
-EXPORT_SYMBOL_GPL(snd_soc_unregister_component);
 
 /**
  * snd_soc_add_platform - Add a platform to the ASoC core
@@ -4125,6 +4129,8 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_component);
 int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
                const struct snd_soc_platform_driver *platform_drv)
 {
+       int ret;
+
        /* create platform component name */
        platform->name = fmt_single_name(dev, &platform->id);
        if (platform->name == NULL)
@@ -4134,8 +4140,22 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
        platform->driver = platform_drv;
        platform->dapm.dev = dev;
        platform->dapm.platform = platform;
+       platform->dapm.component = &platform->component;
        platform->dapm.stream_event = platform_drv->stream_event;
-       mutex_init(&platform->mutex);
+       if (platform_drv->write)
+               platform->component.write = snd_soc_platform_drv_write;
+       if (platform_drv->read)
+               platform->component.read = snd_soc_platform_drv_read;
+
+       /* register component */
+       ret = __snd_soc_register_component(dev, &platform->component,
+                                          &platform_drv->component_driver,
+                                          NULL, NULL, 0, false);
+       if (ret < 0) {
+               dev_err(platform->component.dev,
+                       "ASoC: Failed to register component: %d\n", ret);
+               return ret;
+       }
 
        mutex_lock(&client_mutex);
        list_add(&platform->list, &platform_list);
@@ -4178,6 +4198,8 @@ EXPORT_SYMBOL_GPL(snd_soc_register_platform);
  */
 void snd_soc_remove_platform(struct snd_soc_platform *platform)
 {
+       __snd_soc_unregister_component(&platform->component);
+
        mutex_lock(&client_mutex);
        list_del(&platform->list);
        mutex_unlock(&client_mutex);
@@ -4252,6 +4274,24 @@ static void fixup_codec_formats(struct snd_soc_pcm_stream *stream)
                        stream->formats |= codec_format_map[i];
 }
 
+static int snd_soc_codec_drv_write(struct snd_soc_component *component,
+       unsigned int reg, unsigned int val)
+{
+       struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+
+       return codec->driver->write(codec, reg, val);
+}
+
+static int snd_soc_codec_drv_read(struct snd_soc_component *component,
+       unsigned int reg, unsigned int *val)
+{
+       struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+
+       *val = codec->driver->read(codec, reg);
+
+       return 0;
+}
+
 /**
  * snd_soc_register_codec - Register a codec with the ASoC core
  *
@@ -4263,6 +4303,7 @@ int snd_soc_register_codec(struct device *dev,
                           int num_dai)
 {
        struct snd_soc_codec *codec;
+       struct regmap *regmap;
        int ret, i;
 
        dev_dbg(dev, "codec register %s\n", dev_name(dev));
@@ -4278,22 +4319,40 @@ int snd_soc_register_codec(struct device *dev,
                goto fail_codec;
        }
 
-       codec->write = codec_drv->write;
-       codec->read = codec_drv->read;
-       codec->volatile_register = codec_drv->volatile_register;
-       codec->readable_register = codec_drv->readable_register;
-       codec->writable_register = codec_drv->writable_register;
+       if (codec_drv->write)
+               codec->component.write = snd_soc_codec_drv_write;
+       if (codec_drv->read)
+               codec->component.read = snd_soc_codec_drv_read;
        codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
        codec->dapm.bias_level = SND_SOC_BIAS_OFF;
        codec->dapm.dev = dev;
        codec->dapm.codec = codec;
+       codec->dapm.component = &codec->component;
        codec->dapm.seq_notifier = codec_drv->seq_notifier;
        codec->dapm.stream_event = codec_drv->stream_event;
        codec->dev = dev;
        codec->driver = codec_drv;
-       codec->num_dai = num_dai;
+       codec->component.val_bytes = codec_drv->reg_word_size;
        mutex_init(&codec->mutex);
 
+       if (!codec->component.write) {
+               if (codec_drv->get_regmap)
+                       regmap = codec_drv->get_regmap(dev);
+               else
+                       regmap = dev_get_regmap(dev, NULL);
+
+               if (regmap) {
+                       ret = snd_soc_component_init_io(&codec->component,
+                               regmap);
+                       if (ret) {
+                               dev_err(codec->dev,
+                                               "Failed to set cache I/O:%d\n",
+                                               ret);
+                               return ret;
+                       }
+               }
+       }
+
        for (i = 0; i < num_dai; i++) {
                fixup_codec_formats(&dai_drv[i].playback);
                fixup_codec_formats(&dai_drv[i].capture);
@@ -4343,7 +4402,7 @@ void snd_soc_unregister_codec(struct device *dev)
        return;
 
 found:
-       snd_soc_unregister_component(dev);
+       __snd_soc_unregister_component(&codec->component);
 
        mutex_lock(&client_mutex);
        list_del(&codec->list);
@@ -4554,7 +4613,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
 EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing);
 
 unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
-                                    const char *prefix)
+                                    const char *prefix,
+                                    struct device_node **bitclkmaster,
+                                    struct device_node **framemaster)
 {
        int ret, i;
        char prop[128];
@@ -4637,9 +4698,13 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
         */
        snprintf(prop, sizeof(prop), "%sbitclock-master", prefix);
        bit = !!of_get_property(np, prop, NULL);
+       if (bit && bitclkmaster)
+               *bitclkmaster = of_parse_phandle(np, prop, 0);
 
        snprintf(prop, sizeof(prop), "%sframe-master", prefix);
        frame = !!of_get_property(np, prop, NULL);
+       if (frame && framemaster)
+               *framemaster = of_parse_phandle(np, prop, 0);
 
        switch ((bit << 4) + frame) {
        case 0x11:
@@ -4698,7 +4763,7 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
 
                        if (id < 0 || id >= pos->num_dai) {
                                ret = -EINVAL;
-                               break;
+                               continue;
                        }
 
                        ret = 0;
index 6d6ceee..a74b9bf 100644 (file)
@@ -378,86 +378,24 @@ static void dapm_reset(struct snd_soc_card *card)
 static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg,
        unsigned int *value)
 {
-       if (w->codec) {
-               *value = snd_soc_read(w->codec, reg);
-               return 0;
-       } else if (w->platform) {
-               *value = snd_soc_platform_read(w->platform, reg);
-               return 0;
-       }
-
-       dev_err(w->dapm->dev, "ASoC: no valid widget read method\n");
-       return -1;
-}
-
-static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg,
-       unsigned int val)
-{
-       if (w->codec)
-               return snd_soc_write(w->codec, reg, val);
-       else if (w->platform)
-               return snd_soc_platform_write(w->platform, reg, val);
-
-       dev_err(w->dapm->dev, "ASoC: no valid widget write method\n");
-       return -1;
-}
-
-static inline void soc_widget_lock(struct snd_soc_dapm_widget *w)
-{
-       if (w->codec && !w->codec->using_regmap)
-               mutex_lock(&w->codec->mutex);
-       else if (w->platform)
-               mutex_lock(&w->platform->mutex);
+       if (!w->dapm->component)
+               return -EIO;
+       return snd_soc_component_read(w->dapm->component, reg, value);
 }
 
-static inline void soc_widget_unlock(struct snd_soc_dapm_widget *w)
+static int soc_widget_update_bits(struct snd_soc_dapm_widget *w,
+       int reg, unsigned int mask, unsigned int value)
 {
-       if (w->codec && !w->codec->using_regmap)
-               mutex_unlock(&w->codec->mutex);
-       else if (w->platform)
-               mutex_unlock(&w->platform->mutex);
+       if (!w->dapm->component)
+               return -EIO;
+       return snd_soc_component_update_bits_async(w->dapm->component, reg,
+               mask, value);
 }
 
 static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm)
 {
-       if (dapm->codec && dapm->codec->using_regmap)
-               regmap_async_complete(dapm->codec->control_data);
-}
-
-static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w,
-       unsigned short reg, unsigned int mask, unsigned int value)
-{
-       bool change;
-       unsigned int old, new;
-       int ret;
-
-       if (w->codec && w->codec->using_regmap) {
-               ret = regmap_update_bits_check_async(w->codec->control_data,
-                                                    reg, mask, value,
-                                                    &change);
-               if (ret != 0)
-                       return ret;
-       } else {
-               soc_widget_lock(w);
-               ret = soc_widget_read(w, reg, &old);
-               if (ret < 0) {
-                       soc_widget_unlock(w);
-                       return ret;
-               }
-
-               new = (old & ~mask) | (value & mask);
-               change = old != new;
-               if (change) {
-                       ret = soc_widget_write(w, reg, new);
-                       if (ret < 0) {
-                               soc_widget_unlock(w);
-                               return ret;
-                       }
-               }
-               soc_widget_unlock(w);
-       }
-
-       return change;
+       if (dapm->component)
+               snd_soc_component_async_complete(dapm->component);
 }
 
 /**
@@ -1119,26 +1057,6 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
        return paths;
 }
 
-/*
- * Handler for generic register modifier widget.
- */
-int dapm_reg_event(struct snd_soc_dapm_widget *w,
-                  struct snd_kcontrol *kcontrol, int event)
-{
-       unsigned int val;
-
-       if (SND_SOC_DAPM_EVENT_ON(event))
-               val = w->on_val;
-       else
-               val = w->off_val;
-
-       soc_widget_update_bits_locked(w, -(w->reg + 1),
-                           w->mask << w->shift, val << w->shift);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(dapm_reg_event);
-
 /*
  * Handler for regulator supply widget.
  */
@@ -1428,7 +1346,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card,
                        "pop test : Applying 0x%x/0x%x to %x in %dms\n",
                        value, mask, reg, card->pop_time);
                pop_wait(card->pop_time);
-               soc_widget_update_bits_locked(w, reg, mask, value);
+               soc_widget_update_bits(w, reg, mask, value);
        }
 
        list_for_each_entry(w, pending, power_list) {
@@ -1574,8 +1492,7 @@ static void dapm_widget_update(struct snd_soc_card *card)
        if (!w)
                return;
 
-       ret = soc_widget_update_bits_locked(w, update->reg, update->mask,
-                                 update->val);
+       ret = soc_widget_update_bits(w, update->reg, update->mask, update->val);
        if (ret < 0)
                dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n",
                        w->name, ret);
@@ -2446,8 +2363,7 @@ err:
 }
 
 static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
-                                 const struct snd_soc_dapm_route *route,
-                                 unsigned int is_prefixed)
+                                 const struct snd_soc_dapm_route *route)
 {
        struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
        struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;
@@ -2457,7 +2373,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
        char prefixed_source[80];
        int ret;
 
-       if (dapm->codec && dapm->codec->name_prefix && !is_prefixed) {
+       if (dapm->codec && dapm->codec->name_prefix) {
                snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
                         dapm->codec->name_prefix, route->sink);
                sink = prefixed_sink;
@@ -2585,7 +2501,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
 
        mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
        for (i = 0; i < num; i++) {
-               r = snd_soc_dapm_add_route(dapm, route, false);
+               r = snd_soc_dapm_add_route(dapm, route);
                if (r < 0) {
                        dev_err(dapm->dev, "ASoC: Failed to add route %s -> %s -> %s\n",
                                route->source,
@@ -2857,22 +2773,19 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
        change = dapm_kcontrol_set_value(kcontrol, val);
-
-       if (reg != SND_SOC_NOPM) {
-               mask = mask << shift;
-               val = val << shift;
-
-               change = snd_soc_test_bits(codec, reg, mask, val);
-       }
-
        if (change) {
                if (reg != SND_SOC_NOPM) {
-                       update.kcontrol = kcontrol;
-                       update.reg = reg;
-                       update.mask = mask;
-                       update.val = val;
+                       mask = mask << shift;
+                       val = val << shift;
+
+                       if (snd_soc_test_bits(codec, reg, mask, val)) {
+                               update.kcontrol = kcontrol;
+                               update.reg = reg;
+                               update.mask = mask;
+                               update.val = val;
+                               card->update = &update;
+                       }
 
-                       card->update = &update;
                }
 
                ret = soc_dapm_mixer_update_power(card, kcontrol, connect);
@@ -3311,11 +3224,11 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
                         struct snd_soc_dapm_widget *source,
                         struct snd_soc_dapm_widget *sink)
 {
-       struct snd_soc_dapm_route routes[2];
        struct snd_soc_dapm_widget template;
        struct snd_soc_dapm_widget *w;
        size_t len;
        char *link_name;
+       int ret;
 
        len = strlen(source->name) + strlen(sink->name) + 2;
        link_name = devm_kzalloc(card->dev, len, GFP_KERNEL);
@@ -3342,15 +3255,10 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
 
        w->params = params;
 
-       memset(&routes, 0, sizeof(routes));
-
-       routes[0].source = source->name;
-       routes[0].sink = link_name;
-       routes[1].source = link_name;
-       routes[1].sink = sink->name;
-
-       return snd_soc_dapm_add_routes(&card->dapm, routes,
-                                      ARRAY_SIZE(routes));
+       ret = snd_soc_dapm_add_path(&card->dapm, source, w, NULL, NULL);
+       if (ret)
+               return ret;
+       return snd_soc_dapm_add_path(&card->dapm, w, sink, NULL, NULL);
 }
 
 int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
@@ -3408,6 +3316,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
 {
        struct snd_soc_dapm_widget *dai_w, *w;
+       struct snd_soc_dapm_widget *src, *sink;
        struct snd_soc_dai *dai;
 
        /* For each DAI widget... */
@@ -3438,25 +3347,15 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
                        if (!w->sname || !strstr(w->sname, dai_w->name))
                                continue;
 
-                       if (dai->driver->playback.stream_name &&
-                           strstr(w->sname,
-                                  dai->driver->playback.stream_name)) {
-                               dev_dbg(dai->dev, "%s -> %s\n",
-                                        dai->playback_widget->name, w->name);
-
-                               snd_soc_dapm_add_path(w->dapm,
-                                       dai->playback_widget, w, NULL, NULL);
-                       }
-
-                       if (dai->driver->capture.stream_name &&
-                           strstr(w->sname,
-                                  dai->driver->capture.stream_name)) {
-                               dev_dbg(dai->dev, "%s -> %s\n",
-                                       w->name, dai->capture_widget->name);
-
-                               snd_soc_dapm_add_path(w->dapm, w,
-                                       dai->capture_widget, NULL, NULL);
+                       if (dai_w->id == snd_soc_dapm_dai_in) {
+                               src = dai_w;
+                               sink = w;
+                       } else {
+                               src = w;
+                               sink = dai_w;
                        }
+                       dev_dbg(dai->dev, "%s -> %s\n", src->name, sink->name);
+                       snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL);
                }
        }
 
@@ -3466,12 +3365,10 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
 void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
 {
        struct snd_soc_pcm_runtime *rtd = card->rtd;
+       struct snd_soc_dapm_widget *sink, *source;
        struct snd_soc_dai *cpu_dai, *codec_dai;
-       struct snd_soc_dapm_route r;
        int i;
 
-       memset(&r, 0, sizeof(r));
-
        /* for each BE DAI link... */
        for (i = 0; i < card->num_rtd; i++) {
                rtd = &card->rtd[i];
@@ -3492,55 +3389,49 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
 
                /* connect BE DAI playback if widgets are valid */
                if (codec_dai->playback_widget && cpu_dai->playback_widget) {
-                       r.source = cpu_dai->playback_widget->name;
-                       r.sink = codec_dai->playback_widget->name;
+                       source = cpu_dai->playback_widget;
+                       sink = codec_dai->playback_widget;
                        dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
-                               cpu_dai->codec->name, r.source,
-                               codec_dai->platform->name, r.sink);
+                               cpu_dai->codec->name, source->name,
+                               codec_dai->platform->name, sink->name);
 
-                       snd_soc_dapm_add_route(&card->dapm, &r, true);
+                       snd_soc_dapm_add_path(&card->dapm, source, sink,
+                               NULL, NULL);
                }
 
                /* connect BE DAI capture if widgets are valid */
                if (codec_dai->capture_widget && cpu_dai->capture_widget) {
-                       r.source = codec_dai->capture_widget->name;
-                       r.sink = cpu_dai->capture_widget->name;
+                       source = codec_dai->capture_widget;
+                       sink = cpu_dai->capture_widget;
                        dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
-                               codec_dai->codec->name, r.source,
-                               cpu_dai->platform->name, r.sink);
+                               codec_dai->codec->name, source->name,
+                               cpu_dai->platform->name, sink->name);
 
-                       snd_soc_dapm_add_route(&card->dapm, &r, true);
+                       snd_soc_dapm_add_path(&card->dapm, source, sink,
+                               NULL, NULL);
                }
-
        }
 }
 
-static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
+static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
        int event)
 {
+       struct snd_soc_dapm_widget *w;
 
-       struct snd_soc_dapm_widget *w_cpu, *w_codec;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               w_cpu = cpu_dai->playback_widget;
-               w_codec = codec_dai->playback_widget;
-       } else {
-               w_cpu = cpu_dai->capture_widget;
-               w_codec = codec_dai->capture_widget;
-       }
-
-       if (w_cpu) {
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               w = dai->playback_widget;
+       else
+               w = dai->capture_widget;
 
-               dapm_mark_dirty(w_cpu, "stream event");
+       if (w) {
+               dapm_mark_dirty(w, "stream event");
 
                switch (event) {
                case SND_SOC_DAPM_STREAM_START:
-                       w_cpu->active = 1;
+                       w->active = 1;
                        break;
                case SND_SOC_DAPM_STREAM_STOP:
-                       w_cpu->active = 0;
+                       w->active = 0;
                        break;
                case SND_SOC_DAPM_STREAM_SUSPEND:
                case SND_SOC_DAPM_STREAM_RESUME:
@@ -3549,25 +3440,13 @@ static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
                        break;
                }
        }
+}
 
-       if (w_codec) {
-
-               dapm_mark_dirty(w_codec, "stream event");
-
-               switch (event) {
-               case SND_SOC_DAPM_STREAM_START:
-                       w_codec->active = 1;
-                       break;
-               case SND_SOC_DAPM_STREAM_STOP:
-                       w_codec->active = 0;
-                       break;
-               case SND_SOC_DAPM_STREAM_SUSPEND:
-               case SND_SOC_DAPM_STREAM_RESUME:
-               case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
-               case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
-                       break;
-               }
-       }
+static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
+       int event)
+{
+       soc_dapm_dai_stream_event(rtd->cpu_dai, stream, event);
+       soc_dapm_dai_stream_event(rtd->codec_dai, stream, event);
 
        dapm_power_widgets(rtd->card, event);
 }
index 7ac745d..057e5ef 100644 (file)
@@ -52,6 +52,41 @@ int devm_snd_soc_register_component(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(devm_snd_soc_register_component);
 
+static void devm_platform_release(struct device *dev, void *res)
+{
+       snd_soc_unregister_platform(*(struct device **)res);
+}
+
+/**
+ * devm_snd_soc_register_platform - resource managed platform registration
+ * @dev: Device used to manage platform
+ * @platform: platform to register
+ *
+ * Register a platform driver with automatic unregistration when the device is
+ * unregistered.
+ */
+int devm_snd_soc_register_platform(struct device *dev,
+                       const struct snd_soc_platform_driver *platform_drv)
+{
+       struct device **ptr;
+       int ret;
+
+       ptr = devres_alloc(devm_platform_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return -ENOMEM;
+
+       ret = snd_soc_register_platform(dev, platform_drv);
+       if (ret == 0) {
+               *ptr = dev;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(devm_snd_soc_register_platform);
+
 static void devm_card_release(struct device *dev, void *res)
 {
        snd_soc_unregister_card(*(struct snd_soc_card **)res);
index 260efc8..7767fbd 100644 (file)
 #include <linux/export.h>
 #include <sound/soc.h>
 
-#include <trace/events/asoc.h>
+/**
+ * snd_soc_component_read() - Read register value
+ * @component: Component to read from
+ * @reg: Register to read
+ * @val: Pointer to where the read value is stored
+ *
+ * Return: 0 on success, a negative error code otherwise.
+ */
+int snd_soc_component_read(struct snd_soc_component *component,
+       unsigned int reg, unsigned int *val)
+{
+       int ret;
+
+       if (component->regmap)
+               ret = regmap_read(component->regmap, reg, val);
+       else if (component->read)
+               ret = component->read(component, reg, val);
+       else
+               ret = -EIO;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_read);
 
-#ifdef CONFIG_REGMAP
-static int hw_write(struct snd_soc_codec *codec, unsigned int reg,
-                   unsigned int value)
+/**
+ * snd_soc_component_write() - Write register value
+ * @component: Component to write to
+ * @reg: Register to write
+ * @val: Value to write to the register
+ *
+ * Return: 0 on success, a negative error code otherwise.
+ */
+int snd_soc_component_write(struct snd_soc_component *component,
+       unsigned int reg, unsigned int val)
 {
-       return regmap_write(codec->control_data, reg, value);
+       if (component->regmap)
+               return regmap_write(component->regmap, reg, val);
+       else if (component->write)
+               return component->write(component, reg, val);
+       else
+               return -EIO;
 }
+EXPORT_SYMBOL_GPL(snd_soc_component_write);
 
-static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg)
+static int snd_soc_component_update_bits_legacy(
+       struct snd_soc_component *component, unsigned int reg,
+       unsigned int mask, unsigned int val, bool *change)
 {
+       unsigned int old, new;
        int ret;
-       unsigned int val;
 
-       ret = regmap_read(codec->control_data, reg, &val);
-       if (ret == 0)
-               return val;
+       if (!component->read || !component->write)
+               return -EIO;
+
+       mutex_lock(&component->io_mutex);
+
+       ret = component->read(component, reg, &old);
+       if (ret < 0)
+               goto out_unlock;
+
+       new = (old & ~mask) | (val & mask);
+       *change = old != new;
+       if (*change)
+               ret = component->write(component, reg, new);
+out_unlock:
+       mutex_unlock(&component->io_mutex);
+
+       return ret;
+}
+
+/**
+ * snd_soc_component_update_bits() - Perform read/modify/write cycle
+ * @component: Component to update
+ * @reg: Register to update
+ * @mask: Mask that specifies which bits to update
+ * @val: New value for the bits specified by mask
+ *
+ * Return: 1 if the operation was successful and the value of the register
+ * changed, 0 if the operation was successful, but the value did not change.
+ * Returns a negative error code otherwise.
+ */
+int snd_soc_component_update_bits(struct snd_soc_component *component,
+       unsigned int reg, unsigned int mask, unsigned int val)
+{
+       bool change;
+       int ret;
+
+       if (component->regmap)
+               ret = regmap_update_bits_check(component->regmap, reg, mask,
+                       val, &change);
+       else
+               ret = snd_soc_component_update_bits_legacy(component, reg,
+                       mask, val, &change);
+
+       if (ret < 0)
+               return ret;
+       return change;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_update_bits);
+
+/**
+ * snd_soc_component_update_bits_async() - Perform asynchronous
+ *  read/modify/write cycle
+ * @component: Component to update
+ * @reg: Register to update
+ * @mask: Mask that specifies which bits to update
+ * @val: New value for the bits specified by mask
+ *
+ * This function is similar to snd_soc_component_update_bits(), but the update
+ * operation is scheduled asynchronously. This means it may not be completed
+ * when the function returns. To make sure that all scheduled updates have been
+ * completed snd_soc_component_async_complete() must be called.
+ *
+ * Return: 1 if the operation was successful and the value of the register
+ * changed, 0 if the operation was successful, but the value did not change.
+ * Returns a negative error code otherwise.
+ */
+int snd_soc_component_update_bits_async(struct snd_soc_component *component,
+       unsigned int reg, unsigned int mask, unsigned int val)
+{
+       bool change;
+       int ret;
+
+       if (component->regmap)
+               ret = regmap_update_bits_check_async(component->regmap, reg,
+                       mask, val, &change);
        else
+               ret = snd_soc_component_update_bits_legacy(component, reg,
+                       mask, val, &change);
+
+       if (ret < 0)
+               return ret;
+       return change;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async);
+
+/**
+ * snd_soc_component_async_complete() - Ensure asynchronous I/O has completed
+ * @component: Component for which to wait
+ *
+ * This function blocks until all asynchronous I/O which has previously been
+ * scheduled using snd_soc_component_update_bits_async() has completed.
+ */
+void snd_soc_component_async_complete(struct snd_soc_component *component)
+{
+       if (component->regmap)
+               regmap_async_complete(component->regmap);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_async_complete);
+
+/**
+ * snd_soc_component_test_bits - Test register for change
+ * @component: component
+ * @reg: Register to test
+ * @mask: Mask that specifies which bits to test
+ * @value: Value to test against
+ *
+ * Tests a register with a new value and checks if the new value is
+ * different from the old value.
+ *
+ * Return: 1 for change, otherwise 0.
+ */
+int snd_soc_component_test_bits(struct snd_soc_component *component,
+       unsigned int reg, unsigned int mask, unsigned int value)
+{
+       unsigned int old, new;
+       int ret;
+
+       ret = snd_soc_component_read(component, reg, &old);
+       if (ret < 0)
+               return ret;
+       new = (old & ~mask) | value;
+       return old != new;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_test_bits);
+
+unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+       unsigned int val;
+       int ret;
+
+       ret = snd_soc_component_read(&codec->component, reg, &val);
+       if (ret < 0)
                return -1;
+
+       return val;
 }
+EXPORT_SYMBOL_GPL(snd_soc_read);
+
+int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg,
+       unsigned int val)
+{
+       return snd_soc_component_write(&codec->component, reg, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_write);
 
 /**
- * snd_soc_codec_set_cache_io: Set up standard I/O functions.
+ * snd_soc_update_bits - update codec register bits
+ * @codec: audio codec
+ * @reg: codec register
+ * @mask: register mask
+ * @value: new value
  *
- * @codec: CODEC to configure.
- * @map: Register map to write to
+ * Writes new register value.
  *
- * 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.
+ * Returns 1 for change, 0 for no change, or negative error code.
+ */
+int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg,
+                               unsigned int mask, unsigned int value)
+{
+       return snd_soc_component_update_bits(&codec->component, reg, mask,
+               value);
+}
+EXPORT_SYMBOL_GPL(snd_soc_update_bits);
+
+/**
+ * snd_soc_test_bits - test register for change
+ * @codec: audio codec
+ * @reg: codec register
+ * @mask: register mask
+ * @value: new value
  *
- * The caller is responsible for allocating and initialising the
- * actual cache.
+ * Tests a register with a new value and checks if the new value is
+ * different from the old value.
  *
- * Note that at present this code cannot be used by CODECs with
- * volatile registers.
+ * Returns 1 for change else 0.
  */
-int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
-                              struct regmap *regmap)
+int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg,
+                               unsigned int mask, unsigned int value)
+{
+       return snd_soc_component_test_bits(&codec->component, reg, mask, value);
+}
+EXPORT_SYMBOL_GPL(snd_soc_test_bits);
+
+int snd_soc_platform_read(struct snd_soc_platform *platform,
+                                       unsigned int reg)
 {
+       unsigned int val;
        int ret;
 
-       /* Device has made its own regmap arrangements */
-       if (!regmap)
-               codec->control_data = dev_get_regmap(codec->dev, NULL);
-       else
-               codec->control_data = regmap;
+       ret = snd_soc_component_read(&platform->component, reg, &val);
+       if (ret < 0)
+               return -1;
+
+       return val;
+}
+EXPORT_SYMBOL_GPL(snd_soc_platform_read);
+
+int snd_soc_platform_write(struct snd_soc_platform *platform,
+                                        unsigned int reg, unsigned int val)
+{
+       return snd_soc_component_write(&platform->component, reg, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_platform_write);
 
-       if (IS_ERR(codec->control_data))
-               return PTR_ERR(codec->control_data);
+/**
+ * snd_soc_component_init_io() - Initialize regmap IO
+ *
+ * @component: component to initialize
+ * @regmap: regmap instance to use for IO operations
+ *
+ * Return: 0 on success, a negative error code otherwise
+ */
+int snd_soc_component_init_io(struct snd_soc_component *component,
+       struct regmap *regmap)
+{
+       int ret;
 
-       codec->write = hw_write;
-       codec->read = hw_read;
+       if (!regmap)
+               return -EINVAL;
 
-       ret = regmap_get_val_bytes(codec->control_data);
+       ret = regmap_get_val_bytes(regmap);
        /* Errors are legitimate for non-integer byte
         * multiples */
        if (ret > 0)
-               codec->val_bytes = ret;
+               component->val_bytes = ret;
 
-       codec->using_regmap = true;
+       component->regmap = regmap;
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
-#else
-int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
-                              struct regmap *regmap)
-{
-       return -ENOTSUPP;
-}
-EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
-#endif
+EXPORT_SYMBOL_GPL(snd_soc_component_init_io);
index b903f82..d0d9881 100644 (file)
@@ -14,6 +14,7 @@
 #include <sound/jack.h>
 #include <sound/soc.h>
 #include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/interrupt.h>
 #include <linux/workqueue.h>
 #include <linux/delay.h>
@@ -240,7 +241,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio)
        int enable;
        int report;
 
-       enable = gpio_get_value_cansleep(gpio->gpio);
+       enable = gpiod_get_value_cansleep(gpio->desc);
        if (gpio->invert)
                enable = !enable;
 
@@ -297,31 +298,50 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
        int i, ret;
 
        for (i = 0; i < count; i++) {
-               if (!gpio_is_valid(gpios[i].gpio)) {
-                       dev_err(jack->codec->dev, "ASoC: Invalid gpio %d\n",
-                               gpios[i].gpio);
-                       ret = -EINVAL;
-                       goto undo;
-               }
                if (!gpios[i].name) {
-                       dev_err(jack->codec->dev, "ASoC: No name for gpio %d\n",
-                               gpios[i].gpio);
+                       dev_err(jack->codec->dev,
+                               "ASoC: No name for gpio at index %d\n", i);
                        ret = -EINVAL;
                        goto undo;
                }
 
-               ret = gpio_request(gpios[i].gpio, gpios[i].name);
-               if (ret)
-                       goto undo;
+               if (gpios[i].gpiod_dev) {
+                       /* GPIO descriptor */
+                       gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev,
+                                                       gpios[i].name,
+                                                       gpios[i].idx);
+                       if (IS_ERR(gpios[i].desc)) {
+                               ret = PTR_ERR(gpios[i].desc);
+                               dev_err(gpios[i].gpiod_dev,
+                                       "ASoC: Cannot get gpio at index %d: %d",
+                                       i, ret);
+                               goto undo;
+                       }
+               } else {
+                       /* legacy GPIO number */
+                       if (!gpio_is_valid(gpios[i].gpio)) {
+                               dev_err(jack->codec->dev,
+                                       "ASoC: Invalid gpio %d\n",
+                                       gpios[i].gpio);
+                               ret = -EINVAL;
+                               goto undo;
+                       }
+
+                       ret = gpio_request(gpios[i].gpio, gpios[i].name);
+                       if (ret)
+                               goto undo;
+
+                       gpios[i].desc = gpio_to_desc(gpios[i].gpio);
+               }
 
-               ret = gpio_direction_input(gpios[i].gpio);
+               ret = gpiod_direction_input(gpios[i].desc);
                if (ret)
                        goto err;
 
                INIT_DELAYED_WORK(&gpios[i].work, gpio_work);
                gpios[i].jack = jack;
 
-               ret = request_any_context_irq(gpio_to_irq(gpios[i].gpio),
+               ret = request_any_context_irq(gpiod_to_irq(gpios[i].desc),
                                              gpio_handler,
                                              IRQF_TRIGGER_RISING |
                                              IRQF_TRIGGER_FALLING,
@@ -331,15 +351,15 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
                        goto err;
 
                if (gpios[i].wake) {
-                       ret = irq_set_irq_wake(gpio_to_irq(gpios[i].gpio), 1);
+                       ret = irq_set_irq_wake(gpiod_to_irq(gpios[i].desc), 1);
                        if (ret != 0)
-                               dev_err(jack->codec->dev, "ASoC: "
-                                 "Failed to mark GPIO %d as wake source: %d\n",
-                                       gpios[i].gpio, ret);
+                               dev_err(jack->codec->dev,
+                                       "ASoC: Failed to mark GPIO at index %d as wake source: %d\n",
+                                       i, ret);
                }
 
                /* Expose GPIO value over sysfs for diagnostic purposes */
-               gpio_export(gpios[i].gpio, false);
+               gpiod_export(gpios[i].desc, false);
 
                /* Update initial jack status */
                schedule_delayed_work(&gpios[i].work,
@@ -357,6 +377,30 @@ undo:
 }
 EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpios);
 
+/**
+ * snd_soc_jack_add_gpiods - Associate GPIO descriptor pins with an ASoC jack
+ *
+ * @gpiod_dev: GPIO consumer device
+ * @jack:      ASoC jack
+ * @count:     number of pins
+ * @gpios:     array of gpio pins
+ *
+ * This function will request gpio, set data direction and request irq
+ * for each gpio in the array.
+ */
+int snd_soc_jack_add_gpiods(struct device *gpiod_dev,
+                           struct snd_soc_jack *jack,
+                           int count, struct snd_soc_jack_gpio *gpios)
+{
+       int i;
+
+       for (i = 0; i < count; i++)
+               gpios[i].gpiod_dev = gpiod_dev;
+
+       return snd_soc_jack_add_gpios(jack, count, gpios);
+}
+EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpiods);
+
 /**
  * snd_soc_jack_free_gpios - Release GPIO pins' resources of an ASoC jack
  *
@@ -372,10 +416,10 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
        int i;
 
        for (i = 0; i < count; i++) {
-               gpio_unexport(gpios[i].gpio);
-               free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]);
+               gpiod_unexport(gpios[i].desc);
+               free_irq(gpiod_to_irq(gpios[i].desc), &gpios[i]);
                cancel_delayed_work_sync(&gpios[i].work);
-               gpio_free(gpios[i].gpio);
+               gpiod_put(gpios[i].desc);
                gpios[i].jack = NULL;
        }
 }
index a391de0..54d18f2 100644 (file)
@@ -555,7 +555,6 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
 
        if (platform->driver->ops && platform->driver->ops->close)
                platform->driver->ops->close(substream);
-       cpu_dai->runtime = NULL;
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
@@ -819,6 +818,13 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                if (ret < 0)
                        return ret;
        }
+
+       if (rtd->dai_link->ops && rtd->dai_link->ops->trigger) {
+               ret = rtd->dai_link->ops->trigger(substream, cmd);
+               if (ret < 0)
+                       return ret;
+       }
+
        return 0;
 }
 
@@ -1012,21 +1018,12 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
 }
 
 static inline struct snd_soc_dapm_widget *
-       rtd_get_cpu_widget(struct snd_soc_pcm_runtime *rtd, int stream)
-{
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-               return rtd->cpu_dai->playback_widget;
-       else
-               return rtd->cpu_dai->capture_widget;
-}
-
-static inline struct snd_soc_dapm_widget *
-       rtd_get_codec_widget(struct snd_soc_pcm_runtime *rtd, int stream)
+       dai_get_widget(struct snd_soc_dai *dai, int stream)
 {
        if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-               return rtd->codec_dai->playback_widget;
+               return dai->playback_widget;
        else
-               return rtd->codec_dai->capture_widget;
+               return dai->capture_widget;
 }
 
 static int widget_in_list(struct snd_soc_dapm_widget_list *list,
@@ -1076,14 +1073,14 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
        list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
 
                /* is there a valid CPU DAI widget for this BE */
-               widget = rtd_get_cpu_widget(dpcm->be, stream);
+               widget = dai_get_widget(dpcm->be->cpu_dai, stream);
 
                /* prune the BE if it's no longer in our active list */
                if (widget && widget_in_list(list, widget))
                        continue;
 
                /* is there a valid CODEC DAI widget for this BE */
-               widget = rtd_get_codec_widget(dpcm->be, stream);
+               widget = dai_get_widget(dpcm->be->codec_dai, stream);
 
                /* prune the BE if it's no longer in our active list */
                if (widget && widget_in_list(list, widget))
index c61ea3a..02734bd 100644 (file)
@@ -125,6 +125,18 @@ static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd)
        return 0;
 }
 
+static int tegra_alc5632_card_remove(struct snd_soc_card *card)
+{
+       struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(card);
+
+       if (gpio_is_valid(machine->gpio_hp_det)) {
+               snd_soc_jack_free_gpios(&tegra_alc5632_hs_jack, 1,
+                                       &tegra_alc5632_hp_jack_gpio);
+       }
+
+       return 0;
+}
+
 static struct snd_soc_dai_link tegra_alc5632_dai = {
        .name = "ALC5632",
        .stream_name = "ALC5632 PCM",
@@ -139,6 +151,7 @@ static struct snd_soc_dai_link tegra_alc5632_dai = {
 static struct snd_soc_card snd_soc_tegra_alc5632 = {
        .name = "tegra-alc5632",
        .owner = THIS_MODULE,
+       .remove = tegra_alc5632_card_remove,
        .dai_link = &tegra_alc5632_dai,
        .num_links = 1,
        .controls = tegra_alc5632_controls,
@@ -223,9 +236,6 @@ static int tegra_alc5632_remove(struct platform_device *pdev)
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(card);
 
-       snd_soc_jack_free_gpios(&tegra_alc5632_hs_jack, 1,
-                               &tegra_alc5632_hp_jack_gpio);
-
        snd_soc_unregister_card(card);
 
        tegra_asoc_utils_fini(&machine->util_data);
index 0283cfb..ce73e1f 100644 (file)
@@ -145,6 +145,18 @@ static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd)
        return 0;
 }
 
+static int tegra_max98090_card_remove(struct snd_soc_card *card)
+{
+       struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card);
+
+       if (gpio_is_valid(machine->gpio_hp_det)) {
+               snd_soc_jack_free_gpios(&tegra_max98090_hp_jack, 1,
+                                       &tegra_max98090_hp_jack_gpio);
+       }
+
+       return 0;
+}
+
 static struct snd_soc_dai_link tegra_max98090_dai = {
        .name = "max98090",
        .stream_name = "max98090 PCM",
@@ -158,6 +170,7 @@ static struct snd_soc_dai_link tegra_max98090_dai = {
 static struct snd_soc_card snd_soc_tegra_max98090 = {
        .name = "tegra-max98090",
        .owner = THIS_MODULE,
+       .remove = tegra_max98090_card_remove,
        .dai_link = &tegra_max98090_dai,
        .num_links = 1,
        .controls = tegra_max98090_controls,
@@ -241,9 +254,6 @@ static int tegra_max98090_remove(struct platform_device *pdev)
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card);
 
-       snd_soc_jack_free_gpios(&tegra_max98090_hp_jack, 1,
-                               &tegra_max98090_hp_jack_gpio);
-
        snd_soc_unregister_card(card);
 
        tegra_asoc_utils_fini(&machine->util_data);
index 4511c5a..4feb16a 100644 (file)
@@ -128,6 +128,18 @@ static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd)
        return 0;
 }
 
+static int tegra_rt5640_card_remove(struct snd_soc_card *card)
+{
+       struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
+
+       if (gpio_is_valid(machine->gpio_hp_det)) {
+               snd_soc_jack_free_gpios(&tegra_rt5640_hp_jack, 1,
+                                       &tegra_rt5640_hp_jack_gpio);
+       }
+
+       return 0;
+}
+
 static struct snd_soc_dai_link tegra_rt5640_dai = {
        .name = "RT5640",
        .stream_name = "RT5640 PCM",
@@ -141,6 +153,7 @@ static struct snd_soc_dai_link tegra_rt5640_dai = {
 static struct snd_soc_card snd_soc_tegra_rt5640 = {
        .name = "tegra-rt5640",
        .owner = THIS_MODULE,
+       .remove = tegra_rt5640_card_remove,
        .dai_link = &tegra_rt5640_dai,
        .num_links = 1,
        .controls = tegra_rt5640_controls,
@@ -224,9 +237,6 @@ static int tegra_rt5640_remove(struct platform_device *pdev)
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
 
-       snd_soc_jack_free_gpios(&tegra_rt5640_hp_jack, 1,
-                               &tegra_rt5640_hp_jack_gpio);
-
        snd_soc_unregister_card(card);
 
        tegra_asoc_utils_fini(&machine->util_data);
index 4ac7373..0939661 100644 (file)
@@ -206,6 +206,12 @@ static int tegra_wm8903_remove(struct snd_soc_card *card)
        struct snd_soc_pcm_runtime *rtd = &(card->rtd[0]);
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_codec *codec = codec_dai->codec;
+       struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+
+       if (gpio_is_valid(machine->gpio_hp_det)) {
+               snd_soc_jack_free_gpios(&tegra_wm8903_hp_jack, 1,
+                                       &tegra_wm8903_hp_jack_gpio);
+       }
 
        wm8903_mic_detect(codec, NULL, 0, 0);
 
@@ -228,9 +234,7 @@ static struct snd_soc_card snd_soc_tegra_wm8903 = {
        .owner = THIS_MODULE,
        .dai_link = &tegra_wm8903_dai,
        .num_links = 1,
-
        .remove = tegra_wm8903_remove,
-
        .controls = tegra_wm8903_controls,
        .num_controls = ARRAY_SIZE(tegra_wm8903_controls),
        .dapm_widgets = tegra_wm8903_dapm_widgets,
@@ -368,9 +372,6 @@ static int tegra_wm8903_driver_remove(struct platform_device *pdev)
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
 
-       snd_soc_jack_free_gpios(&tegra_wm8903_hp_jack, 1,
-                               &tegra_wm8903_hp_jack_gpio);
-
        snd_soc_unregister_card(card);
 
        tegra_asoc_utils_fini(&machine->util_data);
index 25a7f82..de087ee 100644 (file)
@@ -50,9 +50,7 @@ static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_codec *codec = codec_dai->codec;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
 
-       snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
-
-       return snd_soc_dapm_sync(dapm);
+       return snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
 }
 
 static struct snd_soc_dai_link tegra_wm9712_dai = {
index 7e923ec..be4f1ac 100644 (file)
@@ -411,7 +411,7 @@ int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd)
        drvdata->mclk_sel = MCLK_ULPCLK;
 
        /* Add controls */
-       ret = snd_soc_add_card_controls(codec->card, mop500_ab8500_ctrls,
+       ret = snd_soc_add_card_controls(rtd->card, mop500_ab8500_ctrls,
                        ARRAY_SIZE(mop500_ab8500_ctrls));
        if (ret < 0) {
                pr_err("%s: Failed to add machine-controls (%d)!\n",
index 1137b85..78683b2 100644 (file)
@@ -1021,6 +1021,7 @@ load_guspatch(struct snd_sf_list *sflist, const char __user *data,
                         data, count);
                if (rc < 0) {
                        sf_sample_delete(sflist, sf, smp);
+                       kfree(zone);
                        return rc;
                }
                /* memory offset is updated after */
index e05a86b..d393153 100644 (file)
@@ -147,5 +147,18 @@ config SND_USB_HIFACE
          To compile this driver as a module, choose M here: the module
          will be called snd-usb-hiface.
 
+config SND_BCD2000
+       tristate "Behringer BCD2000 MIDI driver"
+       select SND_RAWMIDI
+       help
+         Say Y here to include MIDI support for the Behringer BCD2000 DJ
+         controller.
+
+         Audio support is still work-in-progress at
+         https://github.com/anyc/snd-usb-bcd2000
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-bcd2000.
+
 endif  # SND_USB
 
index abe668f..2b92f0d 100644 (file)
@@ -23,4 +23,4 @@ obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o
 obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o
 obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o
 
-obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/
+obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/
diff --git a/sound/usb/bcd2000/Makefile b/sound/usb/bcd2000/Makefile
new file mode 100644 (file)
index 0000000..f09ccc0
--- /dev/null
@@ -0,0 +1,3 @@
+snd-bcd2000-y := bcd2000.o
+
+obj-$(CONFIG_SND_BCD2000) += snd-bcd2000.o
\ No newline at end of file
diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c
new file mode 100644 (file)
index 0000000..820d6ca
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * Behringer BCD2000 driver
+ *
+ *   Copyright (C) 2014 Mario Kicherer (dev@kicherer.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/bitmap.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+
+#define PREFIX "snd-bcd2000: "
+#define BUFSIZE 64
+
+static struct usb_device_id id_table[] = {
+       { USB_DEVICE(0x1397, 0x00bd) },
+       { },
+};
+
+static unsigned char device_cmd_prefix[] = {0x03, 0x00};
+
+static unsigned char bcd2000_init_sequence[] = {
+       0x07, 0x00, 0x00, 0x00, 0x78, 0x48, 0x1c, 0x81,
+       0xc4, 0x00, 0x00, 0x00, 0x5e, 0x53, 0x4a, 0xf7,
+       0x18, 0xfa, 0x11, 0xff, 0x6c, 0xf3, 0x90, 0xff,
+       0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+       0x18, 0xfa, 0x11, 0xff, 0x14, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0xf2, 0x34, 0x4a, 0xf7,
+       0x18, 0xfa, 0x11, 0xff
+};
+
+struct bcd2000 {
+       struct usb_device *dev;
+       struct snd_card *card;
+       struct usb_interface *intf;
+       int card_index;
+
+       int midi_out_active;
+       struct snd_rawmidi *rmidi;
+       struct snd_rawmidi_substream *midi_receive_substream;
+       struct snd_rawmidi_substream *midi_out_substream;
+
+       unsigned char midi_in_buf[BUFSIZE];
+       unsigned char midi_out_buf[BUFSIZE];
+
+       struct urb *midi_out_urb;
+       struct urb *midi_in_urb;
+
+       struct usb_anchor anchor;
+};
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+
+static DEFINE_MUTEX(devices_mutex);
+DECLARE_BITMAP(devices_used, SNDRV_CARDS);
+static struct usb_driver bcd2000_driver;
+
+#ifdef CONFIG_SND_DEBUG
+static void bcd2000_dump_buffer(const char *prefix, const char *buf, int len)
+{
+       print_hex_dump(KERN_DEBUG, prefix,
+                       DUMP_PREFIX_NONE, 16, 1,
+                       buf, len, false);
+}
+#else
+static void bcd2000_dump_buffer(const char *prefix, const char *buf, int len) {}
+#endif
+
+static int bcd2000_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+       return 0;
+}
+
+static int bcd2000_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+       return 0;
+}
+
+/* (de)register midi substream from client */
+static void bcd2000_midi_input_trigger(struct snd_rawmidi_substream *substream,
+                                               int up)
+{
+       struct bcd2000 *bcd2k = substream->rmidi->private_data;
+       bcd2k->midi_receive_substream = up ? substream : NULL;
+}
+
+static void bcd2000_midi_handle_input(struct bcd2000 *bcd2k,
+                               const unsigned char *buf, unsigned int buf_len)
+{
+       unsigned int payload_length, tocopy;
+       struct snd_rawmidi_substream *midi_receive_substream;
+
+       midi_receive_substream = ACCESS_ONCE(bcd2k->midi_receive_substream);
+       if (!midi_receive_substream)
+               return;
+
+       bcd2000_dump_buffer(PREFIX "received from device: ", buf, buf_len);
+
+       if (buf_len < 2)
+               return;
+
+       payload_length = buf[0];
+
+       /* ignore packets without payload */
+       if (payload_length == 0)
+               return;
+
+       tocopy = min(payload_length, buf_len-1);
+
+       bcd2000_dump_buffer(PREFIX "sending to userspace: ",
+                                       &buf[1], tocopy);
+
+       snd_rawmidi_receive(midi_receive_substream,
+                                       &buf[1], tocopy);
+}
+
+static void bcd2000_midi_send(struct bcd2000 *bcd2k)
+{
+       int len, ret;
+       struct snd_rawmidi_substream *midi_out_substream;
+
+       BUILD_BUG_ON(sizeof(device_cmd_prefix) >= BUFSIZE);
+
+       midi_out_substream = ACCESS_ONCE(bcd2k->midi_out_substream);
+       if (!midi_out_substream)
+               return;
+
+       /* copy command prefix bytes */
+       memcpy(bcd2k->midi_out_buf, device_cmd_prefix,
+               sizeof(device_cmd_prefix));
+
+       /*
+        * get MIDI packet and leave space for command prefix
+        * and payload length
+        */
+       len = snd_rawmidi_transmit(midi_out_substream,
+                               bcd2k->midi_out_buf + 3, BUFSIZE - 3);
+
+       if (len < 0)
+               dev_err(&bcd2k->dev->dev, "%s: snd_rawmidi_transmit error %d\n",
+                               __func__, len);
+
+       if (len <= 0)
+               return;
+
+       /* set payload length */
+       bcd2k->midi_out_buf[2] = len;
+       bcd2k->midi_out_urb->transfer_buffer_length = BUFSIZE;
+
+       bcd2000_dump_buffer(PREFIX "sending to device: ",
+                       bcd2k->midi_out_buf, len+3);
+
+       /* send packet to the BCD2000 */
+       ret = usb_submit_urb(bcd2k->midi_out_urb, GFP_ATOMIC);
+       if (ret < 0)
+               dev_err(&bcd2k->dev->dev, PREFIX
+                       "%s (%p): usb_submit_urb() failed, ret=%d, len=%d\n",
+                       __func__, midi_out_substream, ret, len);
+       else
+               bcd2k->midi_out_active = 1;
+}
+
+static int bcd2000_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+       return 0;
+}
+
+static int bcd2000_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+       struct bcd2000 *bcd2k = substream->rmidi->private_data;
+
+       if (bcd2k->midi_out_active) {
+               usb_kill_urb(bcd2k->midi_out_urb);
+               bcd2k->midi_out_active = 0;
+       }
+
+       return 0;
+}
+
+/* (de)register midi substream from client */
+static void bcd2000_midi_output_trigger(struct snd_rawmidi_substream *substream,
+                                               int up)
+{
+       struct bcd2000 *bcd2k = substream->rmidi->private_data;
+
+       if (up) {
+               bcd2k->midi_out_substream = substream;
+               /* check if there is data userspace wants to send */
+               if (!bcd2k->midi_out_active)
+                       bcd2000_midi_send(bcd2k);
+       } else {
+               bcd2k->midi_out_substream = NULL;
+       }
+}
+
+static void bcd2000_output_complete(struct urb *urb)
+{
+       struct bcd2000 *bcd2k = urb->context;
+
+       bcd2k->midi_out_active = 0;
+
+       if (urb->status)
+               dev_warn(&urb->dev->dev,
+                       PREFIX "output urb->status: %d\n", urb->status);
+
+       if (urb->status == -ESHUTDOWN)
+               return;
+
+       /* check if there is more data userspace wants to send */
+       bcd2000_midi_send(bcd2k);
+}
+
+static void bcd2000_input_complete(struct urb *urb)
+{
+       int ret;
+       struct bcd2000 *bcd2k = urb->context;
+
+       if (urb->status)
+               dev_warn(&urb->dev->dev,
+                       PREFIX "input urb->status: %i\n", urb->status);
+
+       if (!bcd2k || urb->status == -ESHUTDOWN)
+               return;
+
+       if (urb->actual_length > 0)
+               bcd2000_midi_handle_input(bcd2k, urb->transfer_buffer,
+                                       urb->actual_length);
+
+       /* return URB to device */
+       ret = usb_submit_urb(bcd2k->midi_in_urb, GFP_ATOMIC);
+       if (ret < 0)
+               dev_err(&bcd2k->dev->dev, PREFIX
+                       "%s: usb_submit_urb() failed, ret=%d\n",
+                       __func__, ret);
+}
+
+static struct snd_rawmidi_ops bcd2000_midi_output = {
+       .open =    bcd2000_midi_output_open,
+       .close =   bcd2000_midi_output_close,
+       .trigger = bcd2000_midi_output_trigger,
+};
+
+static struct snd_rawmidi_ops bcd2000_midi_input = {
+       .open =    bcd2000_midi_input_open,
+       .close =   bcd2000_midi_input_close,
+       .trigger = bcd2000_midi_input_trigger,
+};
+
+static void bcd2000_init_device(struct bcd2000 *bcd2k)
+{
+       int ret;
+
+       init_usb_anchor(&bcd2k->anchor);
+       usb_anchor_urb(bcd2k->midi_out_urb, &bcd2k->anchor);
+       usb_anchor_urb(bcd2k->midi_in_urb, &bcd2k->anchor);
+
+       /* copy init sequence into buffer */
+       memcpy(bcd2k->midi_out_buf, bcd2000_init_sequence, 52);
+       bcd2k->midi_out_urb->transfer_buffer_length = 52;
+
+       /* submit sequence */
+       ret = usb_submit_urb(bcd2k->midi_out_urb, GFP_KERNEL);
+       if (ret < 0)
+               dev_err(&bcd2k->dev->dev, PREFIX
+                       "%s: usb_submit_urb() out failed, ret=%d: ",
+                       __func__, ret);
+       else
+               bcd2k->midi_out_active = 1;
+
+       /* pass URB to device to enable button and controller events */
+       ret = usb_submit_urb(bcd2k->midi_in_urb, GFP_KERNEL);
+       if (ret < 0)
+               dev_err(&bcd2k->dev->dev, PREFIX
+                       "%s: usb_submit_urb() in failed, ret=%d: ",
+                       __func__, ret);
+
+       /* ensure initialization is finished */
+       usb_wait_anchor_empty_timeout(&bcd2k->anchor, 1000);
+}
+
+static int bcd2000_init_midi(struct bcd2000 *bcd2k)
+{
+       int ret;
+       struct snd_rawmidi *rmidi;
+
+       ret = snd_rawmidi_new(bcd2k->card, bcd2k->card->shortname, 0,
+                                       1, /* output */
+                                       1, /* input */
+                                       &rmidi);
+
+       if (ret < 0)
+               return ret;
+
+       strlcpy(rmidi->name, bcd2k->card->shortname, sizeof(rmidi->name));
+
+       rmidi->info_flags = SNDRV_RAWMIDI_INFO_DUPLEX;
+       rmidi->private_data = bcd2k;
+
+       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+                                       &bcd2000_midi_output);
+
+       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+                                       &bcd2000_midi_input);
+
+       bcd2k->rmidi = rmidi;
+
+       bcd2k->midi_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+       bcd2k->midi_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+       if (!bcd2k->midi_in_urb || !bcd2k->midi_out_urb) {
+               dev_err(&bcd2k->dev->dev, PREFIX "usb_alloc_urb failed\n");
+               return -ENOMEM;
+       }
+
+       usb_fill_int_urb(bcd2k->midi_in_urb, bcd2k->dev,
+                               usb_rcvintpipe(bcd2k->dev, 0x81),
+                               bcd2k->midi_in_buf, BUFSIZE,
+                               bcd2000_input_complete, bcd2k, 1);
+
+       usb_fill_int_urb(bcd2k->midi_out_urb, bcd2k->dev,
+                               usb_sndintpipe(bcd2k->dev, 0x1),
+                               bcd2k->midi_out_buf, BUFSIZE,
+                               bcd2000_output_complete, bcd2k, 1);
+
+       bcd2000_init_device(bcd2k);
+
+       return 0;
+}
+
+static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k,
+                                               struct usb_interface *interface)
+{
+       /* usb_kill_urb not necessary, urb is aborted automatically */
+
+       usb_free_urb(bcd2k->midi_out_urb);
+       usb_free_urb(bcd2k->midi_in_urb);
+
+       if (bcd2k->intf) {
+               usb_set_intfdata(bcd2k->intf, NULL);
+               bcd2k->intf = NULL;
+       }
+}
+
+static int bcd2000_probe(struct usb_interface *interface,
+                               const struct usb_device_id *usb_id)
+{
+       struct snd_card *card;
+       struct bcd2000 *bcd2k;
+       unsigned int card_index;
+       char usb_path[32];
+       int err;
+
+       mutex_lock(&devices_mutex);
+
+       for (card_index = 0; card_index < SNDRV_CARDS; ++card_index)
+               if (!test_bit(card_index, devices_used))
+                       break;
+
+       if (card_index >= SNDRV_CARDS) {
+               mutex_unlock(&devices_mutex);
+               return -ENOENT;
+       }
+
+       err = snd_card_new(&interface->dev, index[card_index], id[card_index],
+                       THIS_MODULE, sizeof(*bcd2k), &card);
+       if (err < 0) {
+               mutex_unlock(&devices_mutex);
+               return err;
+       }
+
+       bcd2k = card->private_data;
+       bcd2k->dev = interface_to_usbdev(interface);
+       bcd2k->card = card;
+       bcd2k->card_index = card_index;
+       bcd2k->intf = interface;
+
+       snd_card_set_dev(card, &interface->dev);
+
+       strncpy(card->driver, "snd-bcd2000", sizeof(card->driver));
+       strncpy(card->shortname, "BCD2000", sizeof(card->shortname));
+       usb_make_path(bcd2k->dev, usb_path, sizeof(usb_path));
+       snprintf(bcd2k->card->longname, sizeof(bcd2k->card->longname),
+                   "Behringer BCD2000 at %s",
+                       usb_path);
+
+       err = bcd2000_init_midi(bcd2k);
+       if (err < 0)
+               goto probe_error;
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto probe_error;
+
+       usb_set_intfdata(interface, bcd2k);
+       set_bit(card_index, devices_used);
+
+       mutex_unlock(&devices_mutex);
+       return 0;
+
+probe_error:
+       dev_info(&bcd2k->dev->dev, PREFIX "error during probing");
+       bcd2000_free_usb_related_resources(bcd2k, interface);
+       snd_card_free(card);
+       mutex_unlock(&devices_mutex);
+       return err;
+}
+
+static void bcd2000_disconnect(struct usb_interface *interface)
+{
+       struct bcd2000 *bcd2k = usb_get_intfdata(interface);
+
+       if (!bcd2k)
+               return;
+
+       mutex_lock(&devices_mutex);
+
+       /* make sure that userspace cannot create new requests */
+       snd_card_disconnect(bcd2k->card);
+
+       bcd2000_free_usb_related_resources(bcd2k, interface);
+
+       clear_bit(bcd2k->card_index, devices_used);
+
+       snd_card_free_when_closed(bcd2k->card);
+
+       mutex_unlock(&devices_mutex);
+}
+
+static struct usb_driver bcd2000_driver = {
+       .name =         "snd-bcd2000",
+       .probe =        bcd2000_probe,
+       .disconnect =   bcd2000_disconnect,
+       .id_table =     id_table,
+};
+
+module_usb_driver(bcd2000_driver);
+
+MODULE_DEVICE_TABLE(usb, id_table);
+MODULE_AUTHOR("Mario Kicherer, dev@kicherer.org");
+MODULE_DESCRIPTION("Behringer BCD2000 driver");
+MODULE_LICENSE("GPL");
index d40a285..0b728d8 100644 (file)
@@ -162,7 +162,7 @@ static int check_mapped_selector_name(struct mixer_build *state, int unitid,
 {
        const struct usbmix_selector_map *p;
 
-       if (! state->selector_map)
+       if (!state->selector_map)
                return 0;
        for (p = state->selector_map; p->id; p++) {
                if (p->id == unitid && index < p->count)
@@ -174,7 +174,8 @@ static int check_mapped_selector_name(struct mixer_build *state, int unitid,
 /*
  * find an audio control unit with the given unit id
  */
-static void *find_audio_control_unit(struct mixer_build *state, unsigned char unit)
+static void *find_audio_control_unit(struct mixer_build *state,
+                                    unsigned char unit)
 {
        /* we just parse the header */
        struct uac_feature_unit_descriptor *hdr = NULL;
@@ -194,7 +195,8 @@ static void *find_audio_control_unit(struct mixer_build *state, unsigned char un
 /*
  * copy a string with the given id
  */
-static int snd_usb_copy_string_desc(struct mixer_build *state, int index, char *buf, int maxlen)
+static int snd_usb_copy_string_desc(struct mixer_build *state,
+                                   int index, char *buf, int maxlen)
 {
        int len = usb_string(state->chip->dev, index, buf, maxlen - 1);
        buf[len] = 0;
@@ -253,7 +255,7 @@ static int convert_bytes_value(struct usb_mixer_elem_info *cval, int val)
 
 static int get_relative_value(struct usb_mixer_elem_info *cval, int val)
 {
-       if (! cval->res)
+       if (!cval->res)
                cval->res = 1;
        if (val < cval->min)
                return 0;
@@ -267,7 +269,7 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val)
 {
        if (val < 0)
                return cval->min;
-       if (! cval->res)
+       if (!cval->res)
                cval->res = 1;
        val *= cval->res;
        val += cval->min;
@@ -281,7 +283,8 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val)
  * retrieve a mixer value
  */
 
-static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
+                           int validx, int *value_ret)
 {
        struct snd_usb_audio *chip = cval->mixer->chip;
        unsigned char buf[2];
@@ -292,6 +295,7 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int v
        err = snd_usb_autoresume(cval->mixer->chip);
        if (err < 0)
                return -EIO;
+
        down_read(&chip->shutdown_rwsem);
        while (timeout-- > 0) {
                if (chip->shutdown)
@@ -316,10 +320,11 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int v
        return err;
 }
 
-static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
+                           int validx, int *value_ret)
 {
        struct snd_usb_audio *chip = cval->mixer->chip;
-       unsigned char buf[2 + 3*sizeof(__u16)]; /* enough space for one range */
+       unsigned char buf[2 + 3 * sizeof(__u16)]; /* enough space for one range */
        unsigned char *val;
        int idx = 0, ret, size;
        __u8 bRequest;
@@ -339,9 +344,9 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int v
                goto error;
 
        down_read(&chip->shutdown_rwsem);
-       if (chip->shutdown)
+       if (chip->shutdown) {
                ret = -ENODEV;
-       else {
+       else {
                idx = snd_usb_ctrl_intf(chip) | (cval->id << 8);
                ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
                              USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
@@ -382,7 +387,8 @@ error:
        return 0;
 }
 
-static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+static int get_ctl_value(struct usb_mixer_elem_info *cval, int request,
+                        int validx, int *value_ret)
 {
        validx += cval->idx_off;
 
@@ -391,7 +397,8 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int vali
                get_ctl_value_v2(cval, request, validx, value_ret);
 }
 
-static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *value)
+static int get_cur_ctl_value(struct usb_mixer_elem_info *cval,
+                            int validx, int *value)
 {
        return get_ctl_value(cval, UAC_GET_CUR, validx, value);
 }
@@ -400,7 +407,9 @@ static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *
 static inline int get_cur_mix_raw(struct usb_mixer_elem_info *cval,
                                  int channel, int *value)
 {
-       return get_ctl_value(cval, UAC_GET_CUR, (cval->control << 8) | channel, value);
+       return get_ctl_value(cval, UAC_GET_CUR,
+                            (cval->control << 8) | channel,
+                            value);
 }
 
 static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
@@ -417,7 +426,7 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
                if (!cval->mixer->ignore_ctl_error)
                        usb_audio_dbg(cval->mixer->chip,
                                "cannot get current value for control %d ch %d: err = %d\n",
-                                  cval->control, channel, err);
+                                     cval->control, channel, err);
                return err;
        }
        cval->cached |= 1 << channel;
@@ -425,7 +434,6 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
        return 0;
 }
 
-
 /*
  * set a mixer value
  */
@@ -474,7 +482,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
                }
        }
        usb_audio_dbg(chip, "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n",
-                   request, validx, idx, cval->val_type, buf[0], buf[1]);
+                     request, validx, idx, cval->val_type, buf[0], buf[1]);
        err = -EINVAL;
 
  out:
@@ -483,7 +491,8 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
        return err;
 }
 
-static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int value)
+static int set_cur_ctl_value(struct usb_mixer_elem_info *cval,
+                            int validx, int value)
 {
        return snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, validx, value);
 }
@@ -503,8 +512,9 @@ static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
                return 0;
        }
 
-       err = snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel,
-                           value);
+       err = snd_usb_mixer_set_ctl_value(cval,
+                                         UAC_SET_CUR, (cval->control << 8) | channel,
+                                         value);
        if (err < 0)
                return err;
        cval->cached |= 1 << channel;
@@ -541,13 +551,13 @@ static int parse_audio_unit(struct mixer_build *state, int unitid);
  * check if the input/output channel routing is enabled on the given bitmap.
  * used for mixer unit parser
  */
-static int check_matrix_bitmap(unsigned char *bmap, int ich, int och, int num_outs)
+static int check_matrix_bitmap(unsigned char *bmap,
+                              int ich, int och, int num_outs)
 {
        int idx = ich * num_outs + och;
        return bmap[idx >> 3] & (0x80 >> (idx & 7));
 }
 
-
 /*
  * add an alsa control element
  * search and increment the index until an empty slot is found.
@@ -564,7 +574,8 @@ int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer,
        while (snd_ctl_find_id(mixer->chip->card, &kctl->id))
                kctl->id.index++;
        if ((err = snd_ctl_add(mixer->chip->card, kctl)) < 0) {
-               usb_audio_dbg(mixer->chip, "cannot add control (err = %d)\n", err);
+               usb_audio_dbg(mixer->chip, "cannot add control (err = %d)\n",
+                             err);
                return err;
        }
        cval->elem_id = &kctl->id;
@@ -573,7 +584,6 @@ int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer,
        return 0;
 }
 
-
 /*
  * get a terminal name string
  */
@@ -627,7 +637,8 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
        struct iterm_name_combo *names;
 
        if (iterm->name)
-               return snd_usb_copy_string_desc(state, iterm->name, name, maxlen);
+               return snd_usb_copy_string_desc(state, iterm->name,
+                                               name, maxlen);
 
        /* virtual type - not a real terminal */
        if (iterm->type >> 16) {
@@ -635,13 +646,17 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
                        return 0;
                switch (iterm->type >> 16) {
                case UAC_SELECTOR_UNIT:
-                       strcpy(name, "Selector"); return 8;
+                       strcpy(name, "Selector");
+                       return 8;
                case UAC1_PROCESSING_UNIT:
-                       strcpy(name, "Process Unit"); return 12;
+                       strcpy(name, "Process Unit");
+                       return 12;
                case UAC1_EXTENSION_UNIT:
-                       strcpy(name, "Ext Unit"); return 8;
+                       strcpy(name, "Ext Unit");
+                       return 8;
                case UAC_MIXER_UNIT:
-                       strcpy(name, "Mixer"); return 5;
+                       strcpy(name, "Mixer");
+                       return 5;
                default:
                        return sprintf(name, "Unit %d", iterm->id);
                }
@@ -649,29 +664,35 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
 
        switch (iterm->type & 0xff00) {
        case 0x0100:
-               strcpy(name, "PCM"); return 3;
+               strcpy(name, "PCM");
+               return 3;
        case 0x0200:
-               strcpy(name, "Mic"); return 3;
+               strcpy(name, "Mic");
+               return 3;
        case 0x0400:
-               strcpy(name, "Headset"); return 7;
+               strcpy(name, "Headset");
+               return 7;
        case 0x0500:
-               strcpy(name, "Phone"); return 5;
+               strcpy(name, "Phone");
+               return 5;
        }
 
-       for (names = iterm_names; names->type; names++)
+       for (names = iterm_names; names->type; names++) {
                if (names->type == iterm->type) {
                        strcpy(name, names->name);
                        return strlen(names->name);
                }
+       }
+
        return 0;
 }
 
-
 /*
  * parse the source unit recursively until it reaches to a terminal
  * or a branched unit.
  */
-static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term)
+static int check_input_term(struct mixer_build *state, int id,
+                           struct usb_audio_term *term)
 {
        int err;
        void *p1;
@@ -766,7 +787,6 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
        return -ENODEV;
 }
 
-
 /*
  * Feature Unit
  */
@@ -794,7 +814,6 @@ static struct usb_feature_control_info audio_feature_info[] = {
        { "Phase Inverter Control",     USB_MIXER_BOOLEAN },
 };
 
-
 /* private_free callback */
 static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
 {
@@ -802,7 +821,6 @@ static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
        kctl->private_data = NULL;
 }
 
-
 /*
  * interface to ALSA control for feature/mixer units
  */
@@ -906,7 +924,6 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
                        cval->res = 384;
                }
                break;
-
        }
 }
 
@@ -939,21 +956,26 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
                    get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) {
                        usb_audio_err(cval->mixer->chip,
                                      "%d:%d: cannot get min/max values for control %d (id %d)\n",
-                                  cval->id, snd_usb_ctrl_intf(cval->mixer->chip), cval->control, cval->id);
+                                  cval->id, snd_usb_ctrl_intf(cval->mixer->chip),
+                                                              cval->control, cval->id);
                        return -EINVAL;
                }
-               if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) {
+               if (get_ctl_value(cval, UAC_GET_RES,
+                                 (cval->control << 8) | minchn,
+                                 &cval->res) < 0) {
                        cval->res = 1;
                } else {
                        int last_valid_res = cval->res;
 
                        while (cval->res > 1) {
                                if (snd_usb_mixer_set_ctl_value(cval, UAC_SET_RES,
-                                                               (cval->control << 8) | minchn, cval->res / 2) < 0)
+                                                               (cval->control << 8) | minchn,
+                                                               cval->res / 2) < 0)
                                        break;
                                cval->res /= 2;
                        }
-                       if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, &cval->res) < 0)
+                       if (get_ctl_value(cval, UAC_GET_RES,
+                                         (cval->control << 8) | minchn, &cval->res) < 0)
                                cval->res = last_valid_res;
                }
                if (cval->res == 0)
@@ -1017,7 +1039,8 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
 #define get_min_max(cval, def) get_min_max_with_quirks(cval, def, NULL)
 
 /* get a feature/mixer unit info */
-static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo)
 {
        struct usb_mixer_elem_info *cval = kcontrol->private_data;
 
@@ -1051,7 +1074,8 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
 }
 
 /* get the current value from feature/mixer unit */
-static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
 {
        struct usb_mixer_elem_info *cval = kcontrol->private_data;
        int c, cnt, val, err;
@@ -1082,7 +1106,8 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
 }
 
 /* put the current value to feature/mixer unit */
-static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
 {
        struct usb_mixer_elem_info *cval = kcontrol->private_data;
        int c, cnt, val, oval, err;
@@ -1136,22 +1161,25 @@ static struct snd_kcontrol_new usb_feature_unit_ctl_ro = {
        .put = NULL,
 };
 
-/* This symbol is exported in order to allow the mixer quirks to
- * hook up to the standard feature unit control mechanism */
+/*
+ * This symbol is exported in order to allow the mixer quirks to
+ * hook up to the standard feature unit control mechanism
+ */
 struct snd_kcontrol_new *snd_usb_feature_unit_ctl = &usb_feature_unit_ctl;
 
 /*
  * build a feature control
  */
-
 static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str)
 {
        return strlcat(kctl->id.name, str, sizeof(kctl->id.name));
 }
 
-/* A lot of headsets/headphones have a "Speaker" mixer. Make sure we
-   rename it to "Headphone". We determine if something is a headphone
-   similar to how udev determines form factor. */
+/*
+ * A lot of headsets/headphones have a "Speaker" mixer. Make sure we
+ * rename it to "Headphone". We determine if something is a headphone
+ * similar to how udev determines form factor.
+ */
 static void check_no_speaker_on_headset(struct snd_kcontrol *kctl,
                                        struct snd_card *card)
 {
@@ -1201,10 +1229,8 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
                return;
 
        cval = kzalloc(sizeof(*cval), GFP_KERNEL);
-       if (! cval) {
-               usb_audio_err(state->chip, "cannot malloc kcontrol\n");
+       if (!cval)
                return;
-       }
        cval->mixer = state->mixer;
        cval->id = unitid;
        cval->control = control;
@@ -1222,15 +1248,17 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
                cval->ch_readonly = readonly_mask;
        }
 
-       /* if all channels in the mask are marked read-only, make the control
+       /*
+        * If all channels in the mask are marked read-only, make the control
         * read-only. set_cur_mix_value() will check the mask again and won't
-        * issue write commands to read-only channels. */
+        * issue write commands to read-only channels.
+        */
        if (cval->channels == readonly_mask)
                kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval);
        else
                kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
 
-       if (! kctl) {
+       if (!kctl) {
                usb_audio_err(state->chip, "cannot malloc kcontrol\n");
                kfree(cval);
                return;
@@ -1239,48 +1267,53 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
 
        len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
        mapped_name = len != 0;
-       if (! len && nameid)
+       if (!len && nameid)
                len = snd_usb_copy_string_desc(state, nameid,
                                kctl->id.name, sizeof(kctl->id.name));
 
        switch (control) {
        case UAC_FU_MUTE:
        case UAC_FU_VOLUME:
-               /* determine the control name.  the rule is:
+               /*
+                * determine the control name.  the rule is:
                 * - if a name id is given in descriptor, use it.
                 * - if the connected input can be determined, then use the name
                 *   of terminal type.
                 * - if the connected output can be determined, use it.
                 * - otherwise, anonymous name.
                 */
-               if (! len) {
-                       len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 1);
-                       if (! len)
-                               len = get_term_name(state, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 1);
-                       if (! len)
-                               len = snprintf(kctl->id.name, sizeof(kctl->id.name),
+               if (!len) {
+                       len = get_term_name(state, iterm, kctl->id.name,
+                                           sizeof(kctl->id.name), 1);
+                       if (!len)
+                               len = get_term_name(state, &state->oterm,
+                                                   kctl->id.name,
+                                                   sizeof(kctl->id.name), 1);
+                       if (!len)
+                               len = snprintf(kctl->id.name,
+                                              sizeof(kctl->id.name),
                                               "Feature %d", unitid);
                }
 
                if (!mapped_name)
                        check_no_speaker_on_headset(kctl, state->mixer->chip->card);
 
-               /* determine the stream direction:
+               /*
+                * determine the stream direction:
                 * if the connected output is USB stream, then it's likely a
                 * capture stream.  otherwise it should be playback (hopefully :)
                 */
-               if (! mapped_name && ! (state->oterm.type >> 16)) {
-                       if ((state->oterm.type & 0xff00) == 0x0100) {
+               if (!mapped_name && !(state->oterm.type >> 16)) {
+                       if ((state->oterm.type & 0xff00) == 0x0100)
                                len = append_ctl_name(kctl, " Capture");
-                       } else {
+                       else
                                len = append_ctl_name(kctl, " Playback");
-                       }
                }
                append_ctl_name(kctl, control == UAC_FU_MUTE ?
                                " Switch" : " Volume");
                break;
        default:
-               if (! len)
+               if (!len)
                        strlcpy(kctl->id.name, audio_feature_info[control-1].name,
                                sizeof(kctl->id.name));
                break;
@@ -1300,33 +1333,35 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
        }
 
        range = (cval->max - cval->min) / cval->res;
-       /* Are there devices with volume range more than 255? I use a bit more
+       /*
+        * Are there devices with volume range more than 255? I use a bit more
         * to be sure. 384 is a resolution magic number found on Logitech
         * devices. It will definitively catch all buggy Logitech devices.
         */
        if (range > 384) {
-               usb_audio_warn(state->chip, "Warning! Unlikely big "
-                          "volume range (=%u), cval->res is probably wrong.",
-                          range);
+               usb_audio_warn(state->chip,
+                              "Warning! Unlikely big volume range (=%u), "
+                              "cval->res is probably wrong.",
+                              range);
                usb_audio_warn(state->chip, "[%d] FU [%s] ch = %d, "
-                          "val = %d/%d/%d", cval->id,
-                          kctl->id.name, cval->channels,
-                          cval->min, cval->max, cval->res);
+                              "val = %d/%d/%d", cval->id,
+                              kctl->id.name, cval->channels,
+                              cval->min, cval->max, cval->res);
        }
 
        usb_audio_dbg(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
-                   cval->id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res);
+                     cval->id, kctl->id.name, cval->channels,
+                     cval->min, cval->max, cval->res);
        snd_usb_mixer_add_control(state->mixer, kctl);
 }
 
-
-
 /*
  * parse a feature unit
  *
  * most of controls are defined here.
  */
-static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void *_ftr)
+static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
+                                   void *_ftr)
 {
        int channels, i, j;
        struct usb_audio_term iterm;
@@ -1400,15 +1435,25 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
                for (i = 0; i < 10; i++) {
                        unsigned int ch_bits = 0;
                        for (j = 0; j < channels; j++) {
-                               unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
+                               unsigned int mask;
+
+                               mask = snd_usb_combine_bytes(bmaControls +
+                                                            csize * (j+1), csize);
                                if (mask & (1 << i))
                                        ch_bits |= (1 << j);
                        }
                        /* audio class v1 controls are never read-only */
-                       if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
-                               build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, 0);
+
+                       /*
+                        * The first channel must be set
+                        * (for ease of programming).
+                        */
+                       if (ch_bits & 1)
+                               build_feature_ctl(state, _ftr, ch_bits, i,
+                                                 &iterm, unitid, 0);
                        if (master_bits & (1 << i))
-                               build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, 0);
+                               build_feature_ctl(state, _ftr, 0, i, &iterm,
+                                                 unitid, 0);
                }
        } else { /* UAC_VERSION_2 */
                for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
@@ -1416,7 +1461,10 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
                        unsigned int ch_read_only = 0;
 
                        for (j = 0; j < channels; j++) {
-                               unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
+                               unsigned int mask;
+
+                               mask = snd_usb_combine_bytes(bmaControls +
+                                                            csize * (j+1), csize);
                                if (uac2_control_is_readable(mask, i)) {
                                        ch_bits |= (1 << j);
                                        if (!uac2_control_is_writeable(mask, i))
@@ -1424,12 +1472,22 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
                                }
                        }
 
-                       /* NOTE: build_feature_ctl() will mark the control read-only if all channels
-                        * are marked read-only in the descriptors. Otherwise, the control will be
-                        * reported as writeable, but the driver will not actually issue a write
-                        * command for read-only channels */
-                       if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
-                               build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, ch_read_only);
+                       /*
+                        * NOTE: build_feature_ctl() will mark the control
+                        * read-only if all channels are marked read-only in
+                        * the descriptors. Otherwise, the control will be
+                        * reported as writeable, but the driver will not
+                        * actually issue a write command for read-only
+                        * channels.
+                        */
+
+                       /*
+                        * The first channel must be set
+                        * (for ease of programming).
+                        */
+                       if (ch_bits & 1)
+                               build_feature_ctl(state, _ftr, ch_bits, i,
+                                                 &iterm, unitid, ch_read_only);
                        if (uac2_control_is_readable(master_bits, i))
                                build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
                                                  !uac2_control_is_writeable(master_bits, i));
@@ -1439,7 +1497,6 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
        return 0;
 }
 
-
 /*
  * Mixer Unit
  */
@@ -1450,7 +1507,6 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
  * the callbacks are identical with feature unit.
  * input channel number (zero based) is given in control field instead.
  */
-
 static void build_mixer_unit_ctl(struct mixer_build *state,
                                 struct uac_mixer_unit_descriptor *desc,
                                 int in_pin, int in_ch, int unitid,
@@ -1467,7 +1523,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
                return;
 
        cval = kzalloc(sizeof(*cval), GFP_KERNEL);
-       if (! cval)
+       if (!cval)
                return;
 
        cval->mixer = state->mixer;
@@ -1475,7 +1531,9 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
        cval->control = in_ch + 1; /* based on 1 */
        cval->val_type = USB_MIXER_S16;
        for (i = 0; i < num_outs; i++) {
-               if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol), in_ch, i, num_outs)) {
+               __u8 *c = uac_mixer_unit_bmControls(desc, state->mixer->protocol);
+
+               if (check_matrix_bitmap(c, in_ch, i, num_outs)) {
                        cval->cmask |= (1 << i);
                        cval->channels++;
                }
@@ -1485,7 +1543,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
        get_min_max(cval, 0);
 
        kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
-       if (! kctl) {
+       if (!kctl) {
                usb_audio_err(state->chip, "cannot malloc kcontrol\n");
                kfree(cval);
                return;
@@ -1493,9 +1551,10 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
        kctl->private_free = usb_mixer_elem_free;
 
        len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
-       if (! len)
-               len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 0);
-       if (! len)
+       if (!len)
+               len = get_term_name(state, iterm, kctl->id.name,
+                                   sizeof(kctl->id.name), 0);
+       if (!len)
                len = sprintf(kctl->id.name, "Mixer Source %d", in_ch + 1);
        append_ctl_name(kctl, " Volume");
 
@@ -1504,24 +1563,28 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
        snd_usb_mixer_add_control(state->mixer, kctl);
 }
 
-
 /*
  * parse a mixer unit
  */
-static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *raw_desc)
+static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
+                                 void *raw_desc)
 {
        struct uac_mixer_unit_descriptor *desc = raw_desc;
        struct usb_audio_term iterm;
        int input_pins, num_ins, num_outs;
        int pin, ich, err;
 
-       if (desc->bLength < 11 || ! (input_pins = desc->bNrInPins) || ! (num_outs = uac_mixer_unit_bNrChannels(desc))) {
-               usb_audio_err(state->chip, "invalid MIXER UNIT descriptor %d\n", unitid);
+       if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) ||
+           !(num_outs = uac_mixer_unit_bNrChannels(desc))) {
+               usb_audio_err(state->chip,
+                             "invalid MIXER UNIT descriptor %d\n",
+                             unitid);
                return -EINVAL;
        }
        /* no bmControls field (e.g. Maya44) -> ignore */
        if (desc->bLength <= 10 + input_pins) {
-               usb_audio_dbg(state->chip, "MU %d has no bmControls field\n", unitid);
+               usb_audio_dbg(state->chip, "MU %d has no bmControls field\n",
+                             unitid);
                return 0;
        }
 
@@ -1535,12 +1598,14 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *r
                if (err < 0)
                        return err;
                num_ins += iterm.channels;
-               for (; ich < num_ins; ++ich) {
+               for (; ich < num_ins; ich++) {
                        int och, ich_has_controls = 0;
 
-                       for (och = 0; och < num_outs; ++och) {
-                               if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol),
-                                                       ich, och, num_outs)) {
+                       for (och = 0; och < num_outs; och++) {
+                               __u8 *c = uac_mixer_unit_bmControls(desc,
+                                               state->mixer->protocol);
+
+                               if (check_matrix_bitmap(c, ich, och, num_outs)) {
                                        ich_has_controls = 1;
                                        break;
                                }
@@ -1553,13 +1618,13 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *r
        return 0;
 }
 
-
 /*
  * Processing Unit / Extension Unit
  */
 
 /* get callback for processing/extension unit */
-static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
 {
        struct usb_mixer_elem_info *cval = kcontrol->private_data;
        int err, val;
@@ -1577,7 +1642,8 @@ static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol, struct snd_ctl_
 }
 
 /* put callback for processing/extension unit */
-static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
 {
        struct usb_mixer_elem_info *cval = kcontrol->private_data;
        int val, oval, err;
@@ -1606,7 +1672,6 @@ static struct snd_kcontrol_new mixer_procunit_ctl = {
        .put = mixer_ctl_procunit_put,
 };
 
-
 /*
  * predefined data for processing units
  */
@@ -1697,10 +1762,13 @@ static struct procunit_info extunits[] = {
        { USB_XU_DEVICE_OPTIONS, "AnalogueIn Soft Limit", soft_limit_xu_info },
        { 0 }
 };
+
 /*
  * build a processing/extension unit
  */
-static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw_desc, struct procunit_info *list, char *name)
+static int build_audio_procunit(struct mixer_build *state, int unitid,
+                               void *raw_desc, struct procunit_info *list,
+                               char *name)
 {
        struct uac_processing_unit_descriptor *desc = raw_desc;
        int num_ins = desc->bNrInPins;
@@ -1733,22 +1801,20 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
        for (info = list; info && info->type; info++)
                if (info->type == type)
                        break;
-       if (! info || ! info->type)
+       if (!info || !info->type)
                info = &default_info;
 
        for (valinfo = info->values; valinfo->control; valinfo++) {
                __u8 *controls = uac_processing_unit_bmControls(desc, state->mixer->protocol);
 
-               if (! (controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1))))
+               if (!(controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1))))
                        continue;
                map = find_map(state, unitid, valinfo->control);
                if (check_ignored_ctl(map))
                        continue;
                cval = kzalloc(sizeof(*cval), GFP_KERNEL);
-               if (! cval) {
-                       usb_audio_err(state->chip, "cannot malloc kcontrol\n");
+               if (!cval)
                        return -ENOMEM;
-               }
                cval->mixer = state->mixer;
                cval->id = unitid;
                cval->control = valinfo->control;
@@ -1765,7 +1831,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
                        cval->initialized = 1;
                } else {
                        if (type == USB_XU_CLOCK_RATE) {
-                               /* E-Mu USB 0404/0202/TrackerPre/0204
+                               /*
+                                * E-Mu USB 0404/0202/TrackerPre/0204
                                 * samplerate control quirk
                                 */
                                cval->min = 0;
@@ -1777,60 +1844,69 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw
                }
 
                kctl = snd_ctl_new1(&mixer_procunit_ctl, cval);
-               if (! kctl) {
-                       usb_audio_err(state->chip, "cannot malloc kcontrol\n");
+               if (!kctl) {
                        kfree(cval);
                        return -ENOMEM;
                }
                kctl->private_free = usb_mixer_elem_free;
 
-               if (check_mapped_name(map, kctl->id.name,
-                                               sizeof(kctl->id.name)))
+               if (check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name))) {
                        /* nothing */ ;
-               else if (info->name)
+               } else if (info->name) {
                        strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name));
-               else {
+               else {
                        nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol);
                        len = 0;
                        if (nameid)
-                               len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
-                       if (! len)
+                               len = snd_usb_copy_string_desc(state, nameid,
+                                                              kctl->id.name,
+                                                              sizeof(kctl->id.name));
+                       if (!len)
                                strlcpy(kctl->id.name, name, sizeof(kctl->id.name));
                }
                append_ctl_name(kctl, " ");
                append_ctl_name(kctl, valinfo->suffix);
 
                usb_audio_dbg(state->chip,
-                       "[%d] PU [%s] ch = %d, val = %d/%d\n",
-                           cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
-               if ((err = snd_usb_mixer_add_control(state->mixer, kctl)) < 0)
+                             "[%d] PU [%s] ch = %d, val = %d/%d\n",
+                             cval->id, kctl->id.name, cval->channels,
+                             cval->min, cval->max);
+
+               err = snd_usb_mixer_add_control(state->mixer, kctl);
+               if (err < 0)
                        return err;
        }
        return 0;
 }
 
-
-static int parse_audio_processing_unit(struct mixer_build *state, int unitid, void *raw_desc)
+static int parse_audio_processing_unit(struct mixer_build *state, int unitid,
+                                      void *raw_desc)
 {
-       return build_audio_procunit(state, unitid, raw_desc, procunits, "Processing Unit");
+       return build_audio_procunit(state, unitid, raw_desc,
+                                   procunits, "Processing Unit");
 }
 
-static int parse_audio_extension_unit(struct mixer_build *state, int unitid, void *raw_desc)
+static int parse_audio_extension_unit(struct mixer_build *state, int unitid,
+                                     void *raw_desc)
 {
-       /* Note that we parse extension units with processing unit descriptors.
-        * That's ok as the layout is the same */
-       return build_audio_procunit(state, unitid, raw_desc, extunits, "Extension Unit");
+       /*
+        * Note that we parse extension units with processing unit descriptors.
+        * That's ok as the layout is the same.
+        */
+       return build_audio_procunit(state, unitid, raw_desc,
+                                   extunits, "Extension Unit");
 }
 
-
 /*
  * Selector Unit
  */
 
-/* info callback for selector unit
+/*
+ * info callback for selector unit
  * use an enumerator type for routing
  */
-static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_info *uinfo)
 {
        struct usb_mixer_elem_info *cval = kcontrol->private_data;
        const char **itemlist = (const char **)kcontrol->private_value;
@@ -1841,7 +1917,8 @@ static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol, struct snd_ctl
 }
 
 /* get callback for selector unit */
-static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
 {
        struct usb_mixer_elem_info *cval = kcontrol->private_data;
        int val, err;
@@ -1860,7 +1937,8 @@ static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol, struct snd_ctl_
 }
 
 /* put callback for selector unit */
-static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
 {
        struct usb_mixer_elem_info *cval = kcontrol->private_data;
        int val, oval, err;
@@ -1889,8 +1967,8 @@ static struct snd_kcontrol_new mixer_selectunit_ctl = {
        .put = mixer_ctl_selector_put,
 };
 
-
-/* private free callback.
+/*
+ * private free callback.
  * free both private_data and private_value
  */
 static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl)
@@ -1915,7 +1993,8 @@ static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl)
 /*
  * parse a selector unit
  */
-static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void *raw_desc)
+static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
+                                    void *raw_desc)
 {
        struct uac_selector_unit_descriptor *desc = raw_desc;
        unsigned int i, nameid, len;
@@ -1944,10 +2023,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
                return 0;
 
        cval = kzalloc(sizeof(*cval), GFP_KERNEL);
-       if (! cval) {
-               usb_audio_err(state->chip, "cannot malloc kcontrol\n");
+       if (!cval)
                return -ENOMEM;
-       }
        cval->mixer = state->mixer;
        cval->id = unitid;
        cval->val_type = USB_MIXER_U8;
@@ -1963,8 +2040,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
                cval->control = 0;
 
        namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL);
-       if (! namelist) {
-               usb_audio_err(state->chip, "cannot malloc\n");
+       if (!namelist) {
                kfree(cval);
                return -ENOMEM;
        }
@@ -1973,8 +2049,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
                struct usb_audio_term iterm;
                len = 0;
                namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL);
-               if (! namelist[i]) {
-                       usb_audio_err(state->chip, "cannot malloc\n");
+               if (!namelist[i]) {
                        while (i--)
                                kfree(namelist[i]);
                        kfree(namelist);
@@ -1986,7 +2061,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
                if (! len && check_input_term(state, desc->baSourceID[i], &iterm) >= 0)
                        len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0);
                if (! len)
-                       sprintf(namelist[i], "Input %d", i);
+                       sprintf(namelist[i], "Input %u", i);
        }
 
        kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval);
@@ -2004,11 +2079,12 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
        if (len)
                ;
        else if (nameid)
-               snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
+               snd_usb_copy_string_desc(state, nameid, kctl->id.name,
+                                        sizeof(kctl->id.name));
        else {
                len = get_term_name(state, &state->oterm,
                                    kctl->id.name, sizeof(kctl->id.name), 0);
-               if (! len)
+               if (!len)
                        strlcpy(kctl->id.name, "USB", sizeof(kctl->id.name));
 
                if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR)
@@ -2027,7 +2103,6 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void
        return 0;
 }
 
-
 /*
  * parse an audio unit recursively
  */
@@ -2125,14 +2200,16 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
        }
 
        p = NULL;
-       while ((p = snd_usb_find_csint_desc(mixer->hostif->extra, mixer->hostif->extralen,
+       while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
+                                           mixer->hostif->extralen,
                                            p, UAC_OUTPUT_TERMINAL)) != NULL) {
                if (mixer->protocol == UAC_VERSION_1) {
                        struct uac1_output_terminal_descriptor *desc = p;
 
                        if (desc->bLength < sizeof(*desc))
                                continue; /* invalid descriptor? */
-                       set_bit(desc->bTerminalID, state.unitbitmap);  /* mark terminal ID as visited */
+                       /* mark terminal ID as visited */
+                       set_bit(desc->bTerminalID, state.unitbitmap);
                        state.oterm.id = desc->bTerminalID;
                        state.oterm.type = le16_to_cpu(desc->wTerminalType);
                        state.oterm.name = desc->iTerminal;
@@ -2144,7 +2221,8 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
 
                        if (desc->bLength < sizeof(*desc))
                                continue; /* invalid descriptor? */
-                       set_bit(desc->bTerminalID, state.unitbitmap);  /* mark terminal ID as visited */
+                       /* mark terminal ID as visited */
+                       set_bit(desc->bTerminalID, state.unitbitmap);
                        state.oterm.id = desc->bTerminalID;
                        state.oterm.type = le16_to_cpu(desc->wTerminalType);
                        state.oterm.name = desc->iTerminal;
@@ -2152,7 +2230,10 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
                        if (err < 0 && err != -EINVAL)
                                return err;
 
-                       /* for UAC2, use the same approach to also add the clock selectors */
+                       /*
+                        * For UAC2, use the same approach to also add the
+                        * clock selectors
+                        */
                        err = parse_audio_unit(&state, desc->bCSourceID);
                        if (err < 0 && err != -EINVAL)
                                return err;
@@ -2306,7 +2387,9 @@ static void snd_usb_mixer_interrupt(struct urb *urb)
        }
 
 requeue:
-       if (ustatus != -ENOENT && ustatus != -ECONNRESET && ustatus != -ESHUTDOWN) {
+       if (ustatus != -ENOENT &&
+           ustatus != -ECONNRESET &&
+           ustatus != -ESHUTDOWN) {
                urb->dev = mixer->chip->dev;
                usb_submit_urb(urb, GFP_ATOMIC);
        }
index c2c0f20..e5a3c4b 100644 (file)
@@ -19,6 +19,8 @@ OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
 $(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
 endif
 
+SUBDIRS = tools/ec
+
 # --- CONFIGURATION BEGIN ---
 
 # Set the following to `true' to make a unstripped, unoptimized
@@ -68,7 +70,8 @@ WARNINGS += $(call cc-supports,-Wstrict-prototypes)
 WARNINGS += $(call cc-supports,-Wdeclaration-after-statement)
 
 KERNEL_INCLUDE := ../../../include
-CFLAGS += -D_LINUX -DDEFINE_ALTERNATE_TYPES -I$(KERNEL_INCLUDE)
+ACPICA_INCLUDE := ../../../drivers/acpi/acpica
+CFLAGS += -D_LINUX -I$(KERNEL_INCLUDE) -I$(ACPICA_INCLUDE)
 CFLAGS += $(WARNINGS)
 
 ifeq ($(strip $(V)),false)
@@ -92,10 +95,29 @@ endif
 # --- ACPIDUMP BEGIN ---
 
 vpath %.c \
-       tools/acpidump
+       ../../../drivers/acpi/acpica\
+       tools/acpidump\
+       common\
+       os_specific/service_layers
+
+CFLAGS += -DACPI_DUMP_APP -Itools/acpidump
 
 DUMP_OBJS = \
-       acpidump.o
+       apdump.o\
+       apfiles.o\
+       apmain.o\
+       osunixdir.o\
+       osunixmap.o\
+       tbprint.o\
+       tbxfroot.o\
+       utbuffer.o\
+       utexcep.o\
+       utmath.o\
+       utstring.o\
+       utxferror.o\
+       oslinuxtbl.o\
+       cmfsize.o\
+       getopt.o
 
 DUMP_OBJS := $(addprefix $(OUTPUT)tools/acpidump/,$(DUMP_OBJS))
 
diff --git a/tools/power/acpi/common/cmfsize.c b/tools/power/acpi/common/cmfsize.c
new file mode 100644 (file)
index 0000000..5140e5e
--- /dev/null
@@ -0,0 +1,101 @@
+/******************************************************************************
+ *
+ * Module Name: cfsize - Common get file size function
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acapps.h"
+#include <stdio.h>
+
+#define _COMPONENT          ACPI_TOOLS
+ACPI_MODULE_NAME("cmfsize")
+
+/*******************************************************************************
+ *
+ * FUNCTION:    cm_get_file_size
+ *
+ * PARAMETERS:  file                    - Open file descriptor
+ *
+ * RETURN:      File Size. On error, -1 (ACPI_UINT32_MAX)
+ *
+ * DESCRIPTION: Get the size of a file. Uses seek-to-EOF. File must be open.
+ *              Does not disturb the current file pointer. Uses perror for
+ *              error messages.
+ *
+ ******************************************************************************/
+u32 cm_get_file_size(FILE * file)
+{
+       long file_size;
+       long current_offset;
+
+       /* Save the current file pointer, seek to EOF to obtain file size */
+
+       current_offset = ftell(file);
+       if (current_offset < 0) {
+               goto offset_error;
+       }
+
+       if (fseek(file, 0, SEEK_END)) {
+               goto seek_error;
+       }
+
+       file_size = ftell(file);
+       if (file_size < 0) {
+               goto offset_error;
+       }
+
+       /* Restore original file pointer */
+
+       if (fseek(file, current_offset, SEEK_SET)) {
+               goto seek_error;
+       }
+
+       return ((u32)file_size);
+
+offset_error:
+       perror("Could not get file offset");
+       return (ACPI_UINT32_MAX);
+
+seek_error:
+       perror("Could not seek file");
+       return (ACPI_UINT32_MAX);
+}
diff --git a/tools/power/acpi/common/getopt.c b/tools/power/acpi/common/getopt.c
new file mode 100644 (file)
index 0000000..a302f52
--- /dev/null
@@ -0,0 +1,239 @@
+/******************************************************************************
+ *
+ * Module Name: getopt
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+/*
+ * ACPICA getopt() implementation
+ *
+ * Option strings:
+ *    "f"       - Option has no arguments
+ *    "f:"      - Option requires an argument
+ *    "f^"      - Option has optional single-char sub-options
+ *    "f|"      - Option has required single-char sub-options
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acapps.h"
+
+#define ACPI_OPTION_ERROR(msg, badchar) \
+       if (acpi_gbl_opterr) {fprintf (stderr, "%s%c\n", msg, badchar);}
+
+int acpi_gbl_opterr = 1;
+int acpi_gbl_optind = 1;
+int acpi_gbl_sub_opt_char = 0;
+char *acpi_gbl_optarg;
+
+static int current_char_ptr = 1;
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_getopt_argument
+ *
+ * PARAMETERS:  argc, argv          - from main
+ *
+ * RETURN:      0 if an argument was found, -1 otherwise. Sets acpi_gbl_Optarg
+ *              to point to the next argument.
+ *
+ * DESCRIPTION: Get the next argument. Used to obtain arguments for the
+ *              two-character options after the original call to acpi_getopt.
+ *              Note: Either the argument starts at the next character after
+ *              the option, or it is pointed to by the next argv entry.
+ *              (After call to acpi_getopt, we need to backup to the previous
+ *              argv entry).
+ *
+ ******************************************************************************/
+
+int acpi_getopt_argument(int argc, char **argv)
+{
+       acpi_gbl_optind--;
+       current_char_ptr++;
+
+       if (argv[acpi_gbl_optind][(int)(current_char_ptr + 1)] != '\0') {
+               acpi_gbl_optarg =
+                   &argv[acpi_gbl_optind++][(int)(current_char_ptr + 1)];
+       } else if (++acpi_gbl_optind >= argc) {
+               ACPI_OPTION_ERROR("Option requires an argument: -", 'v');
+
+               current_char_ptr = 1;
+               return (-1);
+       } else {
+               acpi_gbl_optarg = argv[acpi_gbl_optind++];
+       }
+
+       current_char_ptr = 1;
+       return (0);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_getopt
+ *
+ * PARAMETERS:  argc, argv          - from main
+ *              opts                - options info list
+ *
+ * RETURN:      Option character or EOF
+ *
+ * DESCRIPTION: Get the next option
+ *
+ ******************************************************************************/
+
+int acpi_getopt(int argc, char **argv, char *opts)
+{
+       int current_char;
+       char *opts_ptr;
+
+       if (current_char_ptr == 1) {
+               if (acpi_gbl_optind >= argc ||
+                   argv[acpi_gbl_optind][0] != '-' ||
+                   argv[acpi_gbl_optind][1] == '\0') {
+                       return (EOF);
+               } else if (strcmp(argv[acpi_gbl_optind], "--") == 0) {
+                       acpi_gbl_optind++;
+                       return (EOF);
+               }
+       }
+
+       /* Get the option */
+
+       current_char = argv[acpi_gbl_optind][current_char_ptr];
+
+       /* Make sure that the option is legal */
+
+       if (current_char == ':' ||
+           (opts_ptr = strchr(opts, current_char)) == NULL) {
+               ACPI_OPTION_ERROR("Illegal option: -", current_char);
+
+               if (argv[acpi_gbl_optind][++current_char_ptr] == '\0') {
+                       acpi_gbl_optind++;
+                       current_char_ptr = 1;
+               }
+
+               return ('?');
+       }
+
+       /* Option requires an argument? */
+
+       if (*++opts_ptr == ':') {
+               if (argv[acpi_gbl_optind][(int)(current_char_ptr + 1)] != '\0') {
+                       acpi_gbl_optarg =
+                           &argv[acpi_gbl_optind++][(int)
+                                                    (current_char_ptr + 1)];
+               } else if (++acpi_gbl_optind >= argc) {
+                       ACPI_OPTION_ERROR("Option requires an argument: -",
+                                         current_char);
+
+                       current_char_ptr = 1;
+                       return ('?');
+               } else {
+                       acpi_gbl_optarg = argv[acpi_gbl_optind++];
+               }
+
+               current_char_ptr = 1;
+       }
+
+       /* Option has an optional argument? */
+
+       else if (*opts_ptr == '+') {
+               if (argv[acpi_gbl_optind][(int)(current_char_ptr + 1)] != '\0') {
+                       acpi_gbl_optarg =
+                           &argv[acpi_gbl_optind++][(int)
+                                                    (current_char_ptr + 1)];
+               } else if (++acpi_gbl_optind >= argc) {
+                       acpi_gbl_optarg = NULL;
+               } else {
+                       acpi_gbl_optarg = argv[acpi_gbl_optind++];
+               }
+
+               current_char_ptr = 1;
+       }
+
+       /* Option has optional single-char arguments? */
+
+       else if (*opts_ptr == '^') {
+               if (argv[acpi_gbl_optind][(int)(current_char_ptr + 1)] != '\0') {
+                       acpi_gbl_optarg =
+                           &argv[acpi_gbl_optind][(int)(current_char_ptr + 1)];
+               } else {
+                       acpi_gbl_optarg = "^";
+               }
+
+               acpi_gbl_sub_opt_char = acpi_gbl_optarg[0];
+               acpi_gbl_optind++;
+               current_char_ptr = 1;
+       }
+
+       /* Option has a required single-char argument? */
+
+       else if (*opts_ptr == '|') {
+               if (argv[acpi_gbl_optind][(int)(current_char_ptr + 1)] != '\0') {
+                       acpi_gbl_optarg =
+                           &argv[acpi_gbl_optind][(int)(current_char_ptr + 1)];
+               } else {
+                       ACPI_OPTION_ERROR
+                           ("Option requires a single-character suboption: -",
+                            current_char);
+
+                       current_char_ptr = 1;
+                       return ('?');
+               }
+
+               acpi_gbl_sub_opt_char = acpi_gbl_optarg[0];
+               acpi_gbl_optind++;
+               current_char_ptr = 1;
+       }
+
+       /* Option with no arguments */
+
+       else {
+               if (argv[acpi_gbl_optind][++current_char_ptr] == '\0') {
+                       current_char_ptr = 1;
+                       acpi_gbl_optind++;
+               }
+
+               acpi_gbl_optarg = NULL;
+       }
+
+       return (current_char);
+}
index adfa991..38f095d 100644 (file)
@@ -1,18 +1,64 @@
 .TH ACPIDUMP 8
 .SH NAME
-acpidump \- Dump system's ACPI tables to an ASCII file.
+acpidump \- dump a system's ACPI tables to an ASCII file
+
 .SH SYNOPSIS
-.ft B
-.B acpidump > acpidump.out
+.B acpidump
+.RI [ options ]
+.br
+
 .SH DESCRIPTION
-\fBacpidump \fP dumps the systems ACPI tables to an ASCII file
-appropriate for attaching to a bug report.
+.B acpidump
+dumps the systems ACPI tables to an ASCII file appropriate for
+attaching to a bug report.
 
 Subsequently, they can be processed by utilities in the ACPICA package.
-.SS Options
-no options worth worrying about.
-.PP
-.SH EXAMPLE
+
+.SH OPTIONS
+acpidump options are as follow:
+.TP
+.B Options
+.TP
+.B \-b
+Dump tables to binary files
+.TP
+.B \-c
+Dump customized tables
+.TP
+.B \-h \-?
+This help message
+.TP
+.B \-o <File>
+Redirect output to file
+.TP
+.B \-r <Address>
+Dump tables from specified RSDP
+.TP
+.B \-s
+Print table summaries only
+.TP
+.B \-v
+Display version information
+.TP
+.B \-z
+Verbose mode
+.TP
+.B Table Options
+.TP
+.B \-a <Address>
+Get table via a physical address
+.TP
+.B \-f <BinaryFile>
+Get table via a binary file
+.TP
+.B \-n <Signature>
+Get table via a name/signature
+.TP
+Invocation without parameters dumps all available tables
+.TP
+Multiple mixed instances of -a, -f, and -n are supported
+
+.SH EXAMPLES
 
 .nf
 # acpidump > acpidump.out
@@ -50,10 +96,25 @@ ACPICA: https://acpica.org/
 .ta
 .nf
 /dev/mem
+/sys/firmware/acpi/tables/*
 /sys/firmware/acpi/tables/dynamic/*
+/sys/firmware/efi/systab
 .fi
 
-.PP
 .SH AUTHOR
-.nf
-Written by Len Brown <len.brown@intel.com>
+.TP
+Original by:
+ Len Brown <len.brown@intel.com>
+.TP
+Written by:
+ Chao Guan <chao.guan@intel.com>
+.TP
+Updated by:
+ Bob Moore <robert.moore@intel.com>
+ Lv Zheng <lv.zheng@intel.com>
+
+.SH SEE ALSO
+\&\fIacpixtract\fR\|(8), \fIiasl\fR\|(8).
+
+.SH COPYRIGHT
+COPYRIGHT (c) 2013, Intel Corporation.
diff --git a/tools/power/acpi/os_specific/service_layers/oslinuxtbl.c b/tools/power/acpi/os_specific/service_layers/oslinuxtbl.c
new file mode 100644 (file)
index 0000000..28c5200
--- /dev/null
@@ -0,0 +1,1329 @@
+/******************************************************************************
+ *
+ * Module Name: oslinuxtbl - Linux OSL for obtaining ACPI tables
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpidump.h"
+
+#define _COMPONENT          ACPI_OS_SERVICES
+ACPI_MODULE_NAME("oslinuxtbl")
+
+#ifndef PATH_MAX
+#define PATH_MAX 256
+#endif
+/* List of information about obtained ACPI tables */
+typedef struct osl_table_info {
+       struct osl_table_info *next;
+       u32 instance;
+       char signature[ACPI_NAME_SIZE];
+
+} osl_table_info;
+
+/* Local prototypes */
+
+static acpi_status osl_table_initialize(void);
+
+static acpi_status
+osl_table_name_from_file(char *filename, char *signature, u32 *instance);
+
+static acpi_status osl_add_table_to_list(char *signature, u32 instance);
+
+static acpi_status
+osl_read_table_from_file(char *filename,
+                        acpi_size file_offset,
+                        char *signature, struct acpi_table_header **table);
+
+static acpi_status
+osl_map_table(acpi_size address,
+             char *signature, struct acpi_table_header **table);
+
+static void osl_unmap_table(struct acpi_table_header *table);
+
+static acpi_physical_address osl_find_rsdp_via_efi(void);
+
+static acpi_status osl_load_rsdp(void);
+
+static acpi_status osl_list_customized_tables(char *directory);
+
+static acpi_status
+osl_get_customized_table(char *pathname,
+                        char *signature,
+                        u32 instance,
+                        struct acpi_table_header **table,
+                        acpi_physical_address * address);
+
+static acpi_status osl_list_bios_tables(void);
+
+static acpi_status
+osl_get_bios_table(char *signature,
+                  u32 instance,
+                  struct acpi_table_header **table,
+                  acpi_physical_address * address);
+
+static acpi_status osl_get_last_status(acpi_status default_status);
+
+/* File locations */
+
+#define DYNAMIC_TABLE_DIR   "/sys/firmware/acpi/tables/dynamic"
+#define STATIC_TABLE_DIR    "/sys/firmware/acpi/tables"
+#define EFI_SYSTAB          "/sys/firmware/efi/systab"
+
+/* Should we get dynamically loaded SSDTs from DYNAMIC_TABLE_DIR? */
+
+u8 gbl_dump_dynamic_tables = TRUE;
+
+/* Initialization flags */
+
+u8 gbl_table_list_initialized = FALSE;
+
+/* Local copies of main ACPI tables */
+
+struct acpi_table_rsdp gbl_rsdp;
+struct acpi_table_fadt *gbl_fadt = NULL;
+struct acpi_table_rsdt *gbl_rsdt = NULL;
+struct acpi_table_xsdt *gbl_xsdt = NULL;
+
+/* Table addresses */
+
+acpi_physical_address gbl_fadt_address = 0;
+acpi_physical_address gbl_rsdp_address = 0;
+
+/* Revision of RSD PTR */
+
+u8 gbl_revision = 0;
+
+struct osl_table_info *gbl_table_list_head = NULL;
+u32 gbl_table_count = 0;
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_get_last_status
+ *
+ * PARAMETERS:  default_status  - Default error status to return
+ *
+ * RETURN:      Status; Converted from errno.
+ *
+ * DESCRIPTION: Get last errno and conver it to acpi_status.
+ *
+ *****************************************************************************/
+
+static acpi_status osl_get_last_status(acpi_status default_status)
+{
+
+       switch (errno) {
+       case EACCES:
+       case EPERM:
+
+               return (AE_ACCESS);
+
+       case ENOENT:
+
+               return (AE_NOT_FOUND);
+
+       case ENOMEM:
+
+               return (AE_NO_MEMORY);
+
+       default:
+
+               return (default_status);
+       }
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_os_get_table_by_address
+ *
+ * PARAMETERS:  address         - Physical address of the ACPI table
+ *              table           - Where a pointer to the table is returned
+ *
+ * RETURN:      Status; Table buffer is returned if AE_OK.
+ *              AE_NOT_FOUND: A valid table was not found at the address
+ *
+ * DESCRIPTION: Get an ACPI table via a physical memory address.
+ *
+ *****************************************************************************/
+
+acpi_status
+acpi_os_get_table_by_address(acpi_physical_address address,
+                            struct acpi_table_header ** table)
+{
+       u32 table_length;
+       struct acpi_table_header *mapped_table;
+       struct acpi_table_header *local_table = NULL;
+       acpi_status status = AE_OK;
+
+       /* Get main ACPI tables from memory on first invocation of this function */
+
+       status = osl_table_initialize();
+       if (ACPI_FAILURE(status)) {
+               return (status);
+       }
+
+       /* Map the table and validate it */
+
+       status = osl_map_table(address, NULL, &mapped_table);
+       if (ACPI_FAILURE(status)) {
+               return (status);
+       }
+
+       /* Copy table to local buffer and return it */
+
+       table_length = ap_get_table_length(mapped_table);
+       if (table_length == 0) {
+               status = AE_BAD_HEADER;
+               goto exit;
+       }
+
+       local_table = calloc(1, table_length);
+       if (!local_table) {
+               status = AE_NO_MEMORY;
+               goto exit;
+       }
+
+       ACPI_MEMCPY(local_table, mapped_table, table_length);
+
+exit:
+       osl_unmap_table(mapped_table);
+       *table = local_table;
+       return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_os_get_table_by_name
+ *
+ * PARAMETERS:  signature       - ACPI Signature for desired table. Must be
+ *                                a null terminated 4-character string.
+ *              instance        - Multiple table support for SSDT/UEFI (0...n)
+ *                                Must be 0 for other tables.
+ *              table           - Where a pointer to the table is returned
+ *              address         - Where the table physical address is returned
+ *
+ * RETURN:      Status; Table buffer and physical address returned if AE_OK.
+ *              AE_LIMIT: Instance is beyond valid limit
+ *              AE_NOT_FOUND: A table with the signature was not found
+ *
+ * NOTE:        Assumes the input signature is uppercase.
+ *
+ *****************************************************************************/
+
+acpi_status
+acpi_os_get_table_by_name(char *signature,
+                         u32 instance,
+                         struct acpi_table_header ** table,
+                         acpi_physical_address * address)
+{
+       acpi_status status;
+
+       /* Get main ACPI tables from memory on first invocation of this function */
+
+       status = osl_table_initialize();
+       if (ACPI_FAILURE(status)) {
+               return (status);
+       }
+
+       /* Not a main ACPI table, attempt to extract it from the RSDT/XSDT */
+
+       if (!gbl_dump_customized_tables) {
+
+               /* Attempt to get the table from the memory */
+
+               status =
+                   osl_get_bios_table(signature, instance, table, address);
+       } else {
+               /* Attempt to get the table from the static directory */
+
+               status = osl_get_customized_table(STATIC_TABLE_DIR, signature,
+                                                 instance, table, address);
+       }
+
+       if (ACPI_FAILURE(status) && status == AE_LIMIT) {
+               if (gbl_dump_dynamic_tables) {
+
+                       /* Attempt to get a dynamic table */
+
+                       status =
+                           osl_get_customized_table(DYNAMIC_TABLE_DIR,
+                                                    signature, instance, table,
+                                                    address);
+               }
+       }
+
+       return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_add_table_to_list
+ *
+ * PARAMETERS:  signature       - Table signature
+ *              instance        - Table instance
+ *
+ * RETURN:      Status; Successfully added if AE_OK.
+ *              AE_NO_MEMORY: Memory allocation error
+ *
+ * DESCRIPTION: Insert a table structure into OSL table list.
+ *
+ *****************************************************************************/
+
+static acpi_status osl_add_table_to_list(char *signature, u32 instance)
+{
+       struct osl_table_info *new_info;
+       struct osl_table_info *next;
+       u32 next_instance = 0;
+       u8 found = FALSE;
+
+       new_info = calloc(1, sizeof(struct osl_table_info));
+       if (!new_info) {
+               return (AE_NO_MEMORY);
+       }
+
+       ACPI_MOVE_NAME(new_info->signature, signature);
+
+       if (!gbl_table_list_head) {
+               gbl_table_list_head = new_info;
+       } else {
+               next = gbl_table_list_head;
+               while (1) {
+                       if (ACPI_COMPARE_NAME(next->signature, signature)) {
+                               if (next->instance == instance) {
+                                       found = TRUE;
+                               }
+                               if (next->instance >= next_instance) {
+                                       next_instance = next->instance + 1;
+                               }
+                       }
+
+                       if (!next->next) {
+                               break;
+                       }
+                       next = next->next;
+               }
+               next->next = new_info;
+       }
+
+       if (found) {
+               if (instance) {
+                       fprintf(stderr,
+                               "%4.4s: Warning unmatched table instance %d, expected %d\n",
+                               signature, instance, next_instance);
+               }
+               instance = next_instance;
+       }
+
+       new_info->instance = instance;
+       gbl_table_count++;
+
+       return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_os_get_table_by_index
+ *
+ * PARAMETERS:  index           - Which table to get
+ *              table           - Where a pointer to the table is returned
+ *              instance        - Where a pointer to the table instance no. is
+ *                                returned
+ *              address         - Where the table physical address is returned
+ *
+ * RETURN:      Status; Table buffer and physical address returned if AE_OK.
+ *              AE_LIMIT: Index is beyond valid limit
+ *
+ * DESCRIPTION: Get an ACPI table via an index value (0 through n). Returns
+ *              AE_LIMIT when an invalid index is reached. Index is not
+ *              necessarily an index into the RSDT/XSDT.
+ *
+ *****************************************************************************/
+
+acpi_status
+acpi_os_get_table_by_index(u32 index,
+                          struct acpi_table_header ** table,
+                          u32 *instance, acpi_physical_address * address)
+{
+       struct osl_table_info *info;
+       acpi_status status;
+       u32 i;
+
+       /* Get main ACPI tables from memory on first invocation of this function */
+
+       status = osl_table_initialize();
+       if (ACPI_FAILURE(status)) {
+               return (status);
+       }
+
+       /* Validate Index */
+
+       if (index >= gbl_table_count) {
+               return (AE_LIMIT);
+       }
+
+       /* Point to the table list entry specified by the Index argument */
+
+       info = gbl_table_list_head;
+       for (i = 0; i < index; i++) {
+               info = info->next;
+       }
+
+       /* Now we can just get the table via the signature */
+
+       status = acpi_os_get_table_by_name(info->signature, info->instance,
+                                          table, address);
+
+       if (ACPI_SUCCESS(status)) {
+               *instance = info->instance;
+       }
+       return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_find_rsdp_via_efi
+ *
+ * PARAMETERS:  None
+ *
+ * RETURN:      RSDP address if found
+ *
+ * DESCRIPTION: Find RSDP address via EFI.
+ *
+ *****************************************************************************/
+
+static acpi_physical_address osl_find_rsdp_via_efi(void)
+{
+       FILE *file;
+       char buffer[80];
+       unsigned long address = 0;
+
+       file = fopen(EFI_SYSTAB, "r");
+       if (file) {
+               while (fgets(buffer, 80, file)) {
+                       if (sscanf(buffer, "ACPI20=0x%lx", &address) == 1) {
+                               break;
+                       }
+               }
+               fclose(file);
+       }
+
+       return ((acpi_physical_address) (address));
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_load_rsdp
+ *
+ * PARAMETERS:  None
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Scan and load RSDP.
+ *
+ *****************************************************************************/
+
+static acpi_status osl_load_rsdp(void)
+{
+       struct acpi_table_header *mapped_table;
+       u8 *rsdp_address;
+       acpi_physical_address rsdp_base;
+       acpi_size rsdp_size;
+
+       /* Get RSDP from memory */
+
+       rsdp_size = sizeof(struct acpi_table_rsdp);
+       if (gbl_rsdp_base) {
+               rsdp_base = gbl_rsdp_base;
+       } else {
+               rsdp_base = osl_find_rsdp_via_efi();
+       }
+
+       if (!rsdp_base) {
+               rsdp_base = ACPI_HI_RSDP_WINDOW_BASE;
+               rsdp_size = ACPI_HI_RSDP_WINDOW_SIZE;
+       }
+
+       rsdp_address = acpi_os_map_memory(rsdp_base, rsdp_size);
+       if (!rsdp_address) {
+               return (osl_get_last_status(AE_BAD_ADDRESS));
+       }
+
+       /* Search low memory for the RSDP */
+
+       mapped_table = ACPI_CAST_PTR(struct acpi_table_header,
+                                    acpi_tb_scan_memory_for_rsdp(rsdp_address,
+                                                                 rsdp_size));
+       if (!mapped_table) {
+               acpi_os_unmap_memory(rsdp_address, rsdp_size);
+               return (AE_NOT_FOUND);
+       }
+
+       gbl_rsdp_address =
+           rsdp_base + (ACPI_CAST8(mapped_table) - rsdp_address);
+
+       ACPI_MEMCPY(&gbl_rsdp, mapped_table, sizeof(struct acpi_table_rsdp));
+       acpi_os_unmap_memory(rsdp_address, rsdp_size);
+
+       return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_can_use_xsdt
+ *
+ * PARAMETERS:  None
+ *
+ * RETURN:      TRUE if XSDT is allowed to be used.
+ *
+ * DESCRIPTION: This function collects logic that can be used to determine if
+ *              XSDT should be used instead of RSDT.
+ *
+ *****************************************************************************/
+
+static u8 osl_can_use_xsdt(void)
+{
+       if (gbl_revision && !acpi_gbl_do_not_use_xsdt) {
+               return (TRUE);
+       } else {
+               return (FALSE);
+       }
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_table_initialize
+ *
+ * PARAMETERS:  None
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Initialize ACPI table data. Get and store main ACPI tables to
+ *              local variables. Main ACPI tables include RSDT, FADT, RSDT,
+ *              and/or XSDT.
+ *
+ *****************************************************************************/
+
+static acpi_status osl_table_initialize(void)
+{
+       acpi_status status;
+       acpi_physical_address address;
+
+       if (gbl_table_list_initialized) {
+               return (AE_OK);
+       }
+
+       /* Get RSDP from memory */
+
+       status = osl_load_rsdp();
+       if (ACPI_FAILURE(status)) {
+               return (status);
+       }
+
+       /* Get XSDT from memory */
+
+       if (gbl_rsdp.revision && !gbl_do_not_dump_xsdt) {
+               if (gbl_xsdt) {
+                       free(gbl_xsdt);
+                       gbl_xsdt = NULL;
+               }
+
+               gbl_revision = 2;
+               status = osl_get_bios_table(ACPI_SIG_XSDT, 0,
+                                           ACPI_CAST_PTR(struct
+                                                         acpi_table_header *,
+                                                         &gbl_xsdt), &address);
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+       }
+
+       /* Get RSDT from memory */
+
+       if (gbl_rsdp.rsdt_physical_address) {
+               if (gbl_rsdt) {
+                       free(gbl_rsdt);
+                       gbl_rsdt = NULL;
+               }
+
+               status = osl_get_bios_table(ACPI_SIG_RSDT, 0,
+                                           ACPI_CAST_PTR(struct
+                                                         acpi_table_header *,
+                                                         &gbl_rsdt), &address);
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+       }
+
+       /* Get FADT from memory */
+
+       if (gbl_fadt) {
+               free(gbl_fadt);
+               gbl_fadt = NULL;
+       }
+
+       status = osl_get_bios_table(ACPI_SIG_FADT, 0,
+                                   ACPI_CAST_PTR(struct acpi_table_header *,
+                                                 &gbl_fadt),
+                                   &gbl_fadt_address);
+       if (ACPI_FAILURE(status)) {
+               return (status);
+       }
+
+       if (!gbl_dump_customized_tables) {
+
+               /* Add mandatory tables to global table list first */
+
+               status = osl_add_table_to_list(ACPI_RSDP_NAME, 0);
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+
+               status = osl_add_table_to_list(ACPI_SIG_RSDT, 0);
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+
+               if (gbl_revision == 2) {
+                       status = osl_add_table_to_list(ACPI_SIG_XSDT, 0);
+                       if (ACPI_FAILURE(status)) {
+                               return (status);
+                       }
+               }
+
+               status = osl_add_table_to_list(ACPI_SIG_DSDT, 0);
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+
+               status = osl_add_table_to_list(ACPI_SIG_FACS, 0);
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+
+               /* Add all tables found in the memory */
+
+               status = osl_list_bios_tables();
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+       } else {
+               /* Add all tables found in the static directory */
+
+               status = osl_list_customized_tables(STATIC_TABLE_DIR);
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+       }
+
+       if (gbl_dump_dynamic_tables) {
+
+               /* Add all dynamically loaded tables in the dynamic directory */
+
+               status = osl_list_customized_tables(DYNAMIC_TABLE_DIR);
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+       }
+
+       gbl_table_list_initialized = TRUE;
+       return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_list_bios_tables
+ *
+ * PARAMETERS:  None
+ *
+ * RETURN:      Status; Table list is initialized if AE_OK.
+ *
+ * DESCRIPTION: Add ACPI tables to the table list from memory.
+ *
+ * NOTE:        This works on Linux as table customization does not modify the
+ *              addresses stored in RSDP/RSDT/XSDT/FADT.
+ *
+ *****************************************************************************/
+
+static acpi_status osl_list_bios_tables(void)
+{
+       struct acpi_table_header *mapped_table = NULL;
+       u8 *table_data;
+       u8 number_of_tables;
+       u8 item_size;
+       acpi_physical_address table_address = 0;
+       acpi_status status = AE_OK;
+       u32 i;
+
+       if (osl_can_use_xsdt()) {
+               item_size = sizeof(u64);
+               table_data =
+                   ACPI_CAST8(gbl_xsdt) + sizeof(struct acpi_table_header);
+               number_of_tables =
+                   (u8)((gbl_xsdt->header.length -
+                         sizeof(struct acpi_table_header))
+                        / item_size);
+       } else {                /* Use RSDT if XSDT is not available */
+
+               item_size = sizeof(u32);
+               table_data =
+                   ACPI_CAST8(gbl_rsdt) + sizeof(struct acpi_table_header);
+               number_of_tables =
+                   (u8)((gbl_rsdt->header.length -
+                         sizeof(struct acpi_table_header))
+                        / item_size);
+       }
+
+       /* Search RSDT/XSDT for the requested table */
+
+       for (i = 0; i < number_of_tables; ++i, table_data += item_size) {
+               if (osl_can_use_xsdt()) {
+                       table_address =
+                           (acpi_physical_address) (*ACPI_CAST64(table_data));
+               } else {
+                       table_address =
+                           (acpi_physical_address) (*ACPI_CAST32(table_data));
+               }
+
+               /* Skip NULL entries in RSDT/XSDT */
+
+               if (!table_address) {
+                       continue;
+               }
+
+               status = osl_map_table(table_address, NULL, &mapped_table);
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+
+               osl_add_table_to_list(mapped_table->signature, 0);
+               osl_unmap_table(mapped_table);
+       }
+
+       return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_get_bios_table
+ *
+ * PARAMETERS:  signature       - ACPI Signature for common table. Must be
+ *                                a null terminated 4-character string.
+ *              instance        - Multiple table support for SSDT/UEFI (0...n)
+ *                                Must be 0 for other tables.
+ *              table           - Where a pointer to the table is returned
+ *              address         - Where the table physical address is returned
+ *
+ * RETURN:      Status; Table buffer and physical address returned if AE_OK.
+ *              AE_LIMIT: Instance is beyond valid limit
+ *              AE_NOT_FOUND: A table with the signature was not found
+ *
+ * DESCRIPTION: Get a BIOS provided ACPI table
+ *
+ * NOTE:        Assumes the input signature is uppercase.
+ *
+ *****************************************************************************/
+
+static acpi_status
+osl_get_bios_table(char *signature,
+                  u32 instance,
+                  struct acpi_table_header **table,
+                  acpi_physical_address * address)
+{
+       struct acpi_table_header *local_table = NULL;
+       struct acpi_table_header *mapped_table = NULL;
+       u8 *table_data;
+       u8 number_of_tables;
+       u8 item_size;
+       u32 current_instance = 0;
+       acpi_physical_address table_address = 0;
+       u32 table_length = 0;
+       acpi_status status = AE_OK;
+       u32 i;
+
+       /* Handle special tables whose addresses are not in RSDT/XSDT */
+
+       if (ACPI_COMPARE_NAME(signature, ACPI_RSDP_NAME) ||
+           ACPI_COMPARE_NAME(signature, ACPI_SIG_RSDT) ||
+           ACPI_COMPARE_NAME(signature, ACPI_SIG_XSDT) ||
+           ACPI_COMPARE_NAME(signature, ACPI_SIG_DSDT) ||
+           ACPI_COMPARE_NAME(signature, ACPI_SIG_FACS)) {
+               if (instance > 0) {
+                       return (AE_LIMIT);
+               }
+
+               /*
+                * Get the appropriate address, either 32-bit or 64-bit. Be very
+                * careful about the FADT length and validate table addresses.
+                * Note: The 64-bit addresses have priority.
+                */
+               if (ACPI_COMPARE_NAME(signature, ACPI_SIG_DSDT)) {
+                       if ((gbl_fadt->header.length >= MIN_FADT_FOR_XDSDT) &&
+                           gbl_fadt->Xdsdt) {
+                               table_address =
+                                   (acpi_physical_address) gbl_fadt->Xdsdt;
+                       } else
+                           if ((gbl_fadt->header.length >= MIN_FADT_FOR_DSDT)
+                               && gbl_fadt->dsdt) {
+                               table_address =
+                                   (acpi_physical_address) gbl_fadt->dsdt;
+                       }
+               } else if (ACPI_COMPARE_NAME(signature, ACPI_SIG_FACS)) {
+                       if ((gbl_fadt->header.length >= MIN_FADT_FOR_XFACS) &&
+                           gbl_fadt->Xfacs) {
+                               table_address =
+                                   (acpi_physical_address) gbl_fadt->Xfacs;
+                       } else
+                           if ((gbl_fadt->header.length >= MIN_FADT_FOR_FACS)
+                               && gbl_fadt->facs) {
+                               table_address =
+                                   (acpi_physical_address) gbl_fadt->facs;
+                       }
+               } else if (ACPI_COMPARE_NAME(signature, ACPI_SIG_XSDT)) {
+                       if (!gbl_revision) {
+                               return (AE_BAD_SIGNATURE);
+                       }
+                       table_address =
+                           (acpi_physical_address) gbl_rsdp.
+                           xsdt_physical_address;
+               } else if (ACPI_COMPARE_NAME(signature, ACPI_SIG_RSDT)) {
+                       table_address =
+                           (acpi_physical_address) gbl_rsdp.
+                           rsdt_physical_address;
+               } else {
+                       table_address =
+                           (acpi_physical_address) gbl_rsdp_address;
+                       signature = ACPI_SIG_RSDP;
+               }
+
+               /* Now we can get the requested special table */
+
+               status = osl_map_table(table_address, signature, &mapped_table);
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+
+               table_length = ap_get_table_length(mapped_table);
+       } else {                /* Case for a normal ACPI table */
+
+               if (osl_can_use_xsdt()) {
+                       item_size = sizeof(u64);
+                       table_data =
+                           ACPI_CAST8(gbl_xsdt) +
+                           sizeof(struct acpi_table_header);
+                       number_of_tables =
+                           (u8)((gbl_xsdt->header.length -
+                                 sizeof(struct acpi_table_header))
+                                / item_size);
+               } else {        /* Use RSDT if XSDT is not available */
+
+                       item_size = sizeof(u32);
+                       table_data =
+                           ACPI_CAST8(gbl_rsdt) +
+                           sizeof(struct acpi_table_header);
+                       number_of_tables =
+                           (u8)((gbl_rsdt->header.length -
+                                 sizeof(struct acpi_table_header))
+                                / item_size);
+               }
+
+               /* Search RSDT/XSDT for the requested table */
+
+               for (i = 0; i < number_of_tables; ++i, table_data += item_size) {
+                       if (osl_can_use_xsdt()) {
+                               table_address =
+                                   (acpi_physical_address) (*ACPI_CAST64
+                                                            (table_data));
+                       } else {
+                               table_address =
+                                   (acpi_physical_address) (*ACPI_CAST32
+                                                            (table_data));
+                       }
+
+                       /* Skip NULL entries in RSDT/XSDT */
+
+                       if (!table_address) {
+                               continue;
+                       }
+
+                       status =
+                           osl_map_table(table_address, NULL, &mapped_table);
+                       if (ACPI_FAILURE(status)) {
+                               return (status);
+                       }
+                       table_length = mapped_table->length;
+
+                       /* Does this table match the requested signature? */
+
+                       if (!ACPI_COMPARE_NAME
+                           (mapped_table->signature, signature)) {
+                               osl_unmap_table(mapped_table);
+                               mapped_table = NULL;
+                               continue;
+                       }
+
+                       /* Match table instance (for SSDT/UEFI tables) */
+
+                       if (current_instance != instance) {
+                               osl_unmap_table(mapped_table);
+                               mapped_table = NULL;
+                               current_instance++;
+                               continue;
+                       }
+
+                       break;
+               }
+       }
+
+       if (!mapped_table) {
+               return (AE_LIMIT);
+       }
+
+       if (table_length == 0) {
+               status = AE_BAD_HEADER;
+               goto exit;
+       }
+
+       /* Copy table to local buffer and return it */
+
+       local_table = calloc(1, table_length);
+       if (!local_table) {
+               status = AE_NO_MEMORY;
+               goto exit;
+       }
+
+       ACPI_MEMCPY(local_table, mapped_table, table_length);
+       *address = table_address;
+       *table = local_table;
+
+exit:
+       osl_unmap_table(mapped_table);
+       return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_list_customized_tables
+ *
+ * PARAMETERS:  directory           - Directory that contains the tables
+ *
+ * RETURN:      Status; Table list is initialized if AE_OK.
+ *
+ * DESCRIPTION: Add ACPI tables to the table list from a directory.
+ *
+ *****************************************************************************/
+
+static acpi_status osl_list_customized_tables(char *directory)
+{
+       void *table_dir;
+       u32 instance;
+       char temp_name[ACPI_NAME_SIZE];
+       char *filename;
+       acpi_status status = AE_OK;
+
+       /* Open the requested directory */
+
+       table_dir = acpi_os_open_directory(directory, "*", REQUEST_FILE_ONLY);
+       if (!table_dir) {
+               return (osl_get_last_status(AE_NOT_FOUND));
+       }
+
+       /* Examine all entries in this directory */
+
+       while ((filename = acpi_os_get_next_filename(table_dir))) {
+
+               /* Extract table name and instance number */
+
+               status =
+                   osl_table_name_from_file(filename, temp_name, &instance);
+
+               /* Ignore meaningless files */
+
+               if (ACPI_FAILURE(status)) {
+                       continue;
+               }
+
+               /* Add new info node to global table list */
+
+               status = osl_add_table_to_list(temp_name, instance);
+               if (ACPI_FAILURE(status)) {
+                       break;
+               }
+       }
+
+       acpi_os_close_directory(table_dir);
+       return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_map_table
+ *
+ * PARAMETERS:  address             - Address of the table in memory
+ *              signature           - Optional ACPI Signature for desired table.
+ *                                    Null terminated 4-character string.
+ *              table               - Where a pointer to the mapped table is
+ *                                    returned
+ *
+ * RETURN:      Status; Mapped table is returned if AE_OK.
+ *              AE_NOT_FOUND: A valid table was not found at the address
+ *
+ * DESCRIPTION: Map entire ACPI table into caller's address space.
+ *
+ *****************************************************************************/
+
+static acpi_status
+osl_map_table(acpi_size address,
+             char *signature, struct acpi_table_header **table)
+{
+       struct acpi_table_header *mapped_table;
+       u32 length;
+
+       if (!address) {
+               return (AE_BAD_ADDRESS);
+       }
+
+       /*
+        * Map the header so we can get the table length.
+        * Use sizeof (struct acpi_table_header) as:
+        * 1. it is bigger than 24 to include RSDP->Length
+        * 2. it is smaller than sizeof (struct acpi_table_rsdp)
+        */
+       mapped_table =
+           acpi_os_map_memory(address, sizeof(struct acpi_table_header));
+       if (!mapped_table) {
+               fprintf(stderr, "Could not map table header at 0x%8.8X%8.8X\n",
+                       ACPI_FORMAT_UINT64(address));
+               return (osl_get_last_status(AE_BAD_ADDRESS));
+       }
+
+       /* If specified, signature must match */
+
+       if (signature) {
+               if (ACPI_VALIDATE_RSDP_SIG(signature)) {
+                       if (!ACPI_VALIDATE_RSDP_SIG(mapped_table->signature)) {
+                               acpi_os_unmap_memory(mapped_table,
+                                                    sizeof(struct
+                                                           acpi_table_header));
+                               return (AE_BAD_SIGNATURE);
+                       }
+               } else
+                   if (!ACPI_COMPARE_NAME(signature, mapped_table->signature))
+               {
+                       acpi_os_unmap_memory(mapped_table,
+                                            sizeof(struct acpi_table_header));
+                       return (AE_BAD_SIGNATURE);
+               }
+       }
+
+       /* Map the entire table */
+
+       length = ap_get_table_length(mapped_table);
+       acpi_os_unmap_memory(mapped_table, sizeof(struct acpi_table_header));
+       if (length == 0) {
+               return (AE_BAD_HEADER);
+       }
+
+       mapped_table = acpi_os_map_memory(address, length);
+       if (!mapped_table) {
+               fprintf(stderr,
+                       "Could not map table at 0x%8.8X%8.8X length %8.8X\n",
+                       ACPI_FORMAT_UINT64(address), length);
+               return (osl_get_last_status(AE_INVALID_TABLE_LENGTH));
+       }
+
+       (void)ap_is_valid_checksum(mapped_table);
+
+       *table = mapped_table;
+       return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_unmap_table
+ *
+ * PARAMETERS:  table               - A pointer to the mapped table
+ *
+ * RETURN:      None
+ *
+ * DESCRIPTION: Unmap entire ACPI table.
+ *
+ *****************************************************************************/
+
+static void osl_unmap_table(struct acpi_table_header *table)
+{
+       if (table) {
+               acpi_os_unmap_memory(table, ap_get_table_length(table));
+       }
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_table_name_from_file
+ *
+ * PARAMETERS:  filename            - File that contains the desired table
+ *              signature           - Pointer to 4-character buffer to store
+ *                                    extracted table signature.
+ *              instance            - Pointer to integer to store extracted
+ *                                    table instance number.
+ *
+ * RETURN:      Status; Table name is extracted if AE_OK.
+ *
+ * DESCRIPTION: Extract table signature and instance number from a table file
+ *              name.
+ *
+ *****************************************************************************/
+
+static acpi_status
+osl_table_name_from_file(char *filename, char *signature, u32 *instance)
+{
+
+       /* Ignore meaningless files */
+
+       if (strlen(filename) < ACPI_NAME_SIZE) {
+               return (AE_BAD_SIGNATURE);
+       }
+
+       /* Extract instance number */
+
+       if (isdigit((int)filename[ACPI_NAME_SIZE])) {
+               sscanf(&filename[ACPI_NAME_SIZE], "%d", instance);
+       } else if (strlen(filename) != ACPI_NAME_SIZE) {
+               return (AE_BAD_SIGNATURE);
+       } else {
+               *instance = 0;
+       }
+
+       /* Extract signature */
+
+       ACPI_MOVE_NAME(signature, filename);
+       return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_read_table_from_file
+ *
+ * PARAMETERS:  filename            - File that contains the desired table
+ *              file_offset         - Offset of the table in file
+ *              signature           - Optional ACPI Signature for desired table.
+ *                                    A null terminated 4-character string.
+ *              table               - Where a pointer to the table is returned
+ *
+ * RETURN:      Status; Table buffer is returned if AE_OK.
+ *
+ * DESCRIPTION: Read a ACPI table from a file.
+ *
+ *****************************************************************************/
+
+static acpi_status
+osl_read_table_from_file(char *filename,
+                        acpi_size file_offset,
+                        char *signature, struct acpi_table_header **table)
+{
+       FILE *table_file;
+       struct acpi_table_header header;
+       struct acpi_table_header *local_table = NULL;
+       u32 table_length;
+       s32 count;
+       acpi_status status = AE_OK;
+
+       /* Open the file */
+
+       table_file = fopen(filename, "rb");
+       if (table_file == NULL) {
+               fprintf(stderr, "Could not open table file: %s\n", filename);
+               return (osl_get_last_status(AE_NOT_FOUND));
+       }
+
+       fseek(table_file, file_offset, SEEK_SET);
+
+       /* Read the Table header to get the table length */
+
+       count = fread(&header, 1, sizeof(struct acpi_table_header), table_file);
+       if (count != sizeof(struct acpi_table_header)) {
+               fprintf(stderr, "Could not read table header: %s\n", filename);
+               status = AE_BAD_HEADER;
+               goto exit;
+       }
+
+       /* If signature is specified, it must match the table */
+
+       if (signature) {
+               if (ACPI_VALIDATE_RSDP_SIG(signature)) {
+                       if (!ACPI_VALIDATE_RSDP_SIG(header.signature)) {
+                               fprintf(stderr,
+                                       "Incorrect RSDP signature: found %8.8s\n",
+                                       header.signature);
+                               status = AE_BAD_SIGNATURE;
+                               goto exit;
+                       }
+               } else if (!ACPI_COMPARE_NAME(signature, header.signature)) {
+                       fprintf(stderr,
+                               "Incorrect signature: Expecting %4.4s, found %4.4s\n",
+                               signature, header.signature);
+                       status = AE_BAD_SIGNATURE;
+                       goto exit;
+               }
+       }
+
+       table_length = ap_get_table_length(&header);
+       if (table_length == 0) {
+               status = AE_BAD_HEADER;
+               goto exit;
+       }
+
+       /* Read the entire table into a local buffer */
+
+       local_table = calloc(1, table_length);
+       if (!local_table) {
+               fprintf(stderr,
+                       "%4.4s: Could not allocate buffer for table of length %X\n",
+                       header.signature, table_length);
+               status = AE_NO_MEMORY;
+               goto exit;
+       }
+
+       fseek(table_file, file_offset, SEEK_SET);
+
+       count = fread(local_table, 1, table_length, table_file);
+       if (count != table_length) {
+               fprintf(stderr, "%4.4s: Could not read table content\n",
+                       header.signature);
+               status = AE_INVALID_TABLE_LENGTH;
+               goto exit;
+       }
+
+       /* Validate checksum */
+
+       (void)ap_is_valid_checksum(local_table);
+
+exit:
+       fclose(table_file);
+       *table = local_table;
+       return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    osl_get_customized_table
+ *
+ * PARAMETERS:  pathname        - Directory to find Linux customized table
+ *              signature       - ACPI Signature for desired table. Must be
+ *                                a null terminated 4-character string.
+ *              instance        - Multiple table support for SSDT/UEFI (0...n)
+ *                                Must be 0 for other tables.
+ *              table           - Where a pointer to the table is returned
+ *              address         - Where the table physical address is returned
+ *
+ * RETURN:      Status; Table buffer is returned if AE_OK.
+ *              AE_LIMIT: Instance is beyond valid limit
+ *              AE_NOT_FOUND: A table with the signature was not found
+ *
+ * DESCRIPTION: Get an OS customized table.
+ *
+ *****************************************************************************/
+
+static acpi_status
+osl_get_customized_table(char *pathname,
+                        char *signature,
+                        u32 instance,
+                        struct acpi_table_header **table,
+                        acpi_physical_address * address)
+{
+       void *table_dir;
+       u32 current_instance = 0;
+       char temp_name[ACPI_NAME_SIZE];
+       char table_filename[PATH_MAX];
+       char *filename;
+       acpi_status status;
+
+       /* Open the directory for customized tables */
+
+       table_dir = acpi_os_open_directory(pathname, "*", REQUEST_FILE_ONLY);
+       if (!table_dir) {
+               return (osl_get_last_status(AE_NOT_FOUND));
+       }
+
+       /* Attempt to find the table in the directory */
+
+       while ((filename = acpi_os_get_next_filename(table_dir))) {
+
+               /* Ignore meaningless files */
+
+               if (!ACPI_COMPARE_NAME(filename, signature)) {
+                       continue;
+               }
+
+               /* Extract table name and instance number */
+
+               status =
+                   osl_table_name_from_file(filename, temp_name,
+                                            &current_instance);
+
+               /* Ignore meaningless files */
+
+               if (ACPI_FAILURE(status) || current_instance != instance) {
+                       continue;
+               }
+
+               /* Create the table pathname */
+
+               if (instance != 0) {
+                       sprintf(table_filename, "%s/%4.4s%d", pathname,
+                               temp_name, instance);
+               } else {
+                       sprintf(table_filename, "%s/%4.4s", pathname,
+                               temp_name);
+               }
+               break;
+       }
+
+       acpi_os_close_directory(table_dir);
+
+       if (!filename) {
+               return (AE_LIMIT);
+       }
+
+       /* There is no physical address saved for customized tables, use zero */
+
+       *address = 0;
+       status = osl_read_table_from_file(table_filename, 0, NULL, table);
+
+       return (status);
+}
diff --git a/tools/power/acpi/os_specific/service_layers/osunixdir.c b/tools/power/acpi/os_specific/service_layers/osunixdir.c
new file mode 100644 (file)
index 0000000..733f9e4
--- /dev/null
@@ -0,0 +1,204 @@
+/******************************************************************************
+ *
+ * Module Name: osunixdir - Unix directory access interfaces
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <fnmatch.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+/*
+ * Allocated structure returned from os_open_directory
+ */
+typedef struct external_find_info {
+       char *dir_pathname;
+       DIR *dir_ptr;
+       char temp_buffer[256];
+       char *wildcard_spec;
+       char requested_file_type;
+
+} external_find_info;
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_os_open_directory
+ *
+ * PARAMETERS:  dir_pathname        - Full pathname to the directory
+ *              wildcard_spec       - string of the form "*.c", etc.
+ *
+ * RETURN:      A directory "handle" to be used in subsequent search operations.
+ *              NULL returned on failure.
+ *
+ * DESCRIPTION: Open a directory in preparation for a wildcard search
+ *
+ ******************************************************************************/
+
+void *acpi_os_open_directory(char *dir_pathname,
+                            char *wildcard_spec, char requested_file_type)
+{
+       struct external_find_info *external_info;
+       DIR *dir;
+
+       /* Allocate the info struct that will be returned to the caller */
+
+       external_info = calloc(1, sizeof(struct external_find_info));
+       if (!external_info) {
+               return (NULL);
+       }
+
+       /* Get the directory stream */
+
+       dir = opendir(dir_pathname);
+       if (!dir) {
+               fprintf(stderr, "Cannot open directory - %s\n", dir_pathname);
+               free(external_info);
+               return (NULL);
+       }
+
+       /* Save the info in the return structure */
+
+       external_info->wildcard_spec = wildcard_spec;
+       external_info->requested_file_type = requested_file_type;
+       external_info->dir_pathname = dir_pathname;
+       external_info->dir_ptr = dir;
+       return (external_info);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_os_get_next_filename
+ *
+ * PARAMETERS:  dir_handle          - Created via acpi_os_open_directory
+ *
+ * RETURN:      Next filename matched. NULL if no more matches.
+ *
+ * DESCRIPTION: Get the next file in the directory that matches the wildcard
+ *              specification.
+ *
+ ******************************************************************************/
+
+char *acpi_os_get_next_filename(void *dir_handle)
+{
+       struct external_find_info *external_info = dir_handle;
+       struct dirent *dir_entry;
+       char *temp_str;
+       int str_len;
+       struct stat temp_stat;
+       int err;
+
+       while ((dir_entry = readdir(external_info->dir_ptr))) {
+               if (!fnmatch
+                   (external_info->wildcard_spec, dir_entry->d_name, 0)) {
+                       if (dir_entry->d_name[0] == '.') {
+                               continue;
+                       }
+
+                       str_len = strlen(dir_entry->d_name) +
+                           strlen(external_info->dir_pathname) + 2;
+
+                       temp_str = calloc(str_len, 1);
+                       if (!temp_str) {
+                               fprintf(stderr,
+                                       "Could not allocate buffer for temporary string\n");
+                               return (NULL);
+                       }
+
+                       strcpy(temp_str, external_info->dir_pathname);
+                       strcat(temp_str, "/");
+                       strcat(temp_str, dir_entry->d_name);
+
+                       err = stat(temp_str, &temp_stat);
+                       if (err == -1) {
+                               fprintf(stderr,
+                                       "Cannot stat file (should not happen) - %s\n",
+                                       temp_str);
+                               free(temp_str);
+                               return (NULL);
+                       }
+
+                       free(temp_str);
+
+                       if ((S_ISDIR(temp_stat.st_mode)
+                            && (external_info->requested_file_type ==
+                                REQUEST_DIR_ONLY))
+                           || ((!S_ISDIR(temp_stat.st_mode)
+                                && external_info->requested_file_type ==
+                                REQUEST_FILE_ONLY))) {
+
+                               /* copy to a temp buffer because dir_entry struct is on the stack */
+
+                               strcpy(external_info->temp_buffer,
+                                      dir_entry->d_name);
+                               return (external_info->temp_buffer);
+                       }
+               }
+       }
+
+       return (NULL);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_os_close_directory
+ *
+ * PARAMETERS:  dir_handle          - Created via acpi_os_open_directory
+ *
+ * RETURN:      None.
+ *
+ * DESCRIPTION: Close the open directory and cleanup.
+ *
+ ******************************************************************************/
+
+void acpi_os_close_directory(void *dir_handle)
+{
+       struct external_find_info *external_info = dir_handle;
+
+       /* Close the directory and free allocations */
+
+       closedir(external_info->dir_ptr);
+       free(dir_handle);
+}
diff --git a/tools/power/acpi/os_specific/service_layers/osunixmap.c b/tools/power/acpi/os_specific/service_layers/osunixmap.c
new file mode 100644 (file)
index 0000000..99b47b6
--- /dev/null
@@ -0,0 +1,151 @@
+/******************************************************************************
+ *
+ * Module Name: osunixmap - Unix OSL for file mappings
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpidump.h"
+#include <unistd.h>
+#include <sys/mman.h>
+#ifdef _free_BSD
+#include <sys/param.h>
+#endif
+
+#define _COMPONENT          ACPI_OS_SERVICES
+ACPI_MODULE_NAME("osunixmap")
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifdef _free_BSD
+#define MMAP_FLAGS          MAP_SHARED
+#else
+#define MMAP_FLAGS          MAP_PRIVATE
+#endif
+#define SYSTEM_MEMORY       "/dev/mem"
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_os_get_page_size
+ *
+ * PARAMETERS:  None
+ *
+ * RETURN:      Page size of the platform.
+ *
+ * DESCRIPTION: Obtain page size of the platform.
+ *
+ ******************************************************************************/
+static acpi_size acpi_os_get_page_size(void)
+{
+
+#ifdef PAGE_SIZE
+       return PAGE_SIZE;
+#else
+       return sysconf(_SC_PAGESIZE);
+#endif
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_os_map_memory
+ *
+ * PARAMETERS:  where               - Physical address of memory to be mapped
+ *              length              - How much memory to map
+ *
+ * RETURN:      Pointer to mapped memory. Null on error.
+ *
+ * DESCRIPTION: Map physical memory into local address space.
+ *
+ *****************************************************************************/
+
+void *acpi_os_map_memory(acpi_physical_address where, acpi_size length)
+{
+       u8 *mapped_memory;
+       acpi_physical_address offset;
+       acpi_size page_size;
+       int fd;
+
+       fd = open(SYSTEM_MEMORY, O_RDONLY | O_BINARY);
+       if (fd < 0) {
+               fprintf(stderr, "Cannot open %s\n", SYSTEM_MEMORY);
+               return (NULL);
+       }
+
+       /* Align the offset to use mmap */
+
+       page_size = acpi_os_get_page_size();
+       offset = where % page_size;
+
+       /* Map the table header to get the length of the full table */
+
+       mapped_memory = mmap(NULL, (length + offset), PROT_READ, MMAP_FLAGS,
+                            fd, (where - offset));
+       if (mapped_memory == MAP_FAILED) {
+               fprintf(stderr, "Cannot map %s\n", SYSTEM_MEMORY);
+               close(fd);
+               return (NULL);
+       }
+
+       close(fd);
+       return (ACPI_CAST8(mapped_memory + offset));
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_os_unmap_memory
+ *
+ * PARAMETERS:  where               - Logical address of memory to be unmapped
+ *              length              - How much memory to unmap
+ *
+ * RETURN:      None.
+ *
+ * DESCRIPTION: Delete a previously created mapping. Where and Length must
+ *              correspond to a previous mapping exactly.
+ *
+ *****************************************************************************/
+
+void acpi_os_unmap_memory(void *where, acpi_size length)
+{
+       acpi_physical_address offset;
+       acpi_size page_size;
+
+       page_size = acpi_os_get_page_size();
+       offset = (acpi_physical_address) where % page_size;
+       munmap((u8 *)where - offset, (length + offset));
+}
diff --git a/tools/power/acpi/tools/acpidump/acpidump.c b/tools/power/acpi/tools/acpidump/acpidump.c
deleted file mode 100644 (file)
index a84553a..0000000
+++ /dev/null
@@ -1,559 +0,0 @@
-/*
- * (c) Alexey Starikovskiy, Intel, 2005-2006.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions, and the following disclaimer,
- *    without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- *    substantially similar to the "NO WARRANTY" disclaimer below
- *    ("Disclaimer") and any redistribution must be conditioned upon
- *    including a substantially similar Disclaimer requirement for further
- *    binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- *    of any contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- */
-
-#ifdef DEFINE_ALTERNATE_TYPES
-/* hack to enable building old application with new headers -lenb */
-#define acpi_fadt_descriptor acpi_table_fadt
-#define acpi_rsdp_descriptor acpi_table_rsdp
-#define DSDT_SIG ACPI_SIG_DSDT
-#define FACS_SIG ACPI_SIG_FACS
-#define FADT_SIG ACPI_SIG_FADT
-#define xfirmware_ctrl Xfacs
-#define firmware_ctrl facs
-
-typedef int                            s32;
-typedef unsigned char                  u8;
-typedef unsigned short                 u16;
-typedef unsigned int                   u32;
-typedef unsigned long long             u64;
-typedef long long                      s64;
-#endif
-
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <getopt.h>
-
-#include <dirent.h>
-
-#include <acpi/acconfig.h>
-#include <acpi/platform/acenv.h>
-#include <acpi/actypes.h>
-#include <acpi/actbl.h>
-
-static inline u8 checksum(u8 * buffer, u32 length)
-{
-       u8 sum = 0, *i = buffer;
-       buffer += length;
-       for (; i < buffer; sum += *(i++));
-       return sum;
-}
-
-static unsigned long psz, addr, length;
-static int print, connect, skip;
-static u8 select_sig[4];
-
-static unsigned long read_efi_systab( void )
-{
-       char buffer[80];
-       unsigned long addr;
-       FILE *f = fopen("/sys/firmware/efi/systab", "r");
-       if (f) {
-               while (fgets(buffer, 80, f)) {
-                       if (sscanf(buffer, "ACPI20=0x%lx", &addr) == 1)
-                               return addr;
-               }
-               fclose(f);
-       }
-       return 0;
-}
-
-static u8 *acpi_map_memory(unsigned long where, unsigned length)
-{
-       unsigned long offset;
-       u8 *there;
-       int fd = open("/dev/mem", O_RDONLY);
-       if (fd < 0) {
-               fprintf(stderr, "acpi_os_map_memory: cannot open /dev/mem\n");
-               exit(1);
-       }
-       offset = where % psz;
-       there = mmap(NULL, length + offset, PROT_READ, MAP_PRIVATE,
-                        fd, where - offset);
-       close(fd);
-       if (there == MAP_FAILED) return 0;
-       return (there + offset);
-}
-
-static void acpi_unmap_memory(u8 * there, unsigned length)
-{
-       unsigned long offset = (unsigned long)there % psz;
-       munmap(there - offset, length + offset);
-}
-
-static struct acpi_table_header *acpi_map_table(unsigned long where, char *sig)
-{
-       unsigned size;
-       struct acpi_table_header *tbl = (struct acpi_table_header *)
-           acpi_map_memory(where, sizeof(struct acpi_table_header));
-       if (!tbl || (sig && memcmp(sig, tbl->signature, 4))) return 0;
-       size = tbl->length;
-       acpi_unmap_memory((u8 *) tbl, sizeof(struct acpi_table_header));
-       return (struct acpi_table_header *)acpi_map_memory(where, size);
-}
-
-static void acpi_unmap_table(struct acpi_table_header *tbl)
-{
-       acpi_unmap_memory((u8 *)tbl, tbl->length);
-}
-
-static struct acpi_rsdp_descriptor *acpi_scan_for_rsdp(u8 *begin, u32 length)
-{
-       struct acpi_rsdp_descriptor *rsdp;
-       u8 *i, *end = begin + length;
-       /* Search from given start address for the requested length */
-       for (i = begin; i < end; i += ACPI_RSDP_SCAN_STEP) {
-               /* The signature and checksum must both be correct */
-               if (memcmp((char *)i, "RSD PTR ", 8)) continue;
-               rsdp = (struct acpi_rsdp_descriptor *)i;
-               /* Signature matches, check the appropriate checksum */
-               if (!checksum((u8 *) rsdp, (rsdp->revision < 2) ?
-                             ACPI_RSDP_CHECKSUM_LENGTH :
-                             ACPI_RSDP_XCHECKSUM_LENGTH))
-                       /* Checksum valid, we have found a valid RSDP */
-                       return rsdp;
-       }
-       /* Searched entire block, no RSDP was found */
-       return 0;
-}
-
-/*
- * Output data
- */
-static void acpi_show_data(int fd, u8 * data, int size)
-{
-       char buffer[256];
-       int len;
-       int i, remain = size;
-       while (remain > 0) {
-               len = snprintf(buffer, 256, "  %04x:", size - remain);
-               for (i = 0; i < 16 && i < remain; i++) {
-                       len +=
-                           snprintf(&buffer[len], 256 - len, " %02x", data[i]);
-               }
-               for (; i < 16; i++) {
-                       len += snprintf(&buffer[len], 256 - len, "   ");
-               }
-               len += snprintf(&buffer[len], 256 - len, "  ");
-               for (i = 0; i < 16 && i < remain; i++) {
-                       buffer[len++] = (isprint(data[i])) ? data[i] : '.';
-               }
-               buffer[len++] = '\n';
-               write(fd, buffer, len);
-               data += 16;
-               remain -= 16;
-       }
-}
-
-/*
- * Output ACPI table
- */
-static void acpi_show_table(int fd, struct acpi_table_header *table, unsigned long addr)
-{
-       char buff[80];
-       int len = snprintf(buff, 80, "%.4s @ %p\n", table->signature, (void *)addr);
-       write(fd, buff, len);
-       acpi_show_data(fd, (u8 *) table, table->length);
-       buff[0] = '\n';
-       write(fd, buff, 1);
-}
-
-static void write_table(int fd, struct acpi_table_header *tbl, unsigned long addr)
-{
-       static int select_done = 0;
-       if (!select_sig[0]) {
-               if (print) {
-                       acpi_show_table(fd, tbl, addr);
-               } else {
-                       write(fd, tbl, tbl->length);
-               }
-       } else if (!select_done && !memcmp(select_sig, tbl->signature, 4)) {
-               if (skip > 0) {
-                       --skip;
-                       return;
-               }
-               if (print) {
-                       acpi_show_table(fd, tbl, addr);
-               } else {
-                       write(fd, tbl, tbl->length);
-               }
-               select_done = 1;
-       }
-}
-
-static void acpi_dump_FADT(int fd, struct acpi_table_header *tbl, unsigned long xaddr) {
-       struct acpi_fadt_descriptor x;
-       unsigned long addr;
-       size_t len = sizeof(struct acpi_fadt_descriptor);
-       if (len > tbl->length) len = tbl->length;
-       memcpy(&x, tbl, len);
-       x.header.length = len;
-       if (checksum((u8 *)tbl, len)) {
-               fprintf(stderr, "Wrong checksum for FADT!\n");
-       }
-       if (x.header.length >= 148 && x.Xdsdt) {
-               addr = (unsigned long)x.Xdsdt;
-               if (connect) {
-                       x.Xdsdt = lseek(fd, 0, SEEK_CUR);
-               }
-       } else if (x.header.length >= 44 && x.dsdt) {
-               addr = (unsigned long)x.dsdt;
-               if (connect) {
-                       x.dsdt = lseek(fd, 0, SEEK_CUR);
-               }
-       } else {
-               fprintf(stderr, "No DSDT in FADT!\n");
-               goto no_dsdt;
-       }
-       tbl = acpi_map_table(addr, DSDT_SIG);
-       if (!tbl) goto no_dsdt;
-       if (checksum((u8 *)tbl, tbl->length))
-               fprintf(stderr, "Wrong checksum for DSDT!\n");
-       write_table(fd, tbl, addr);
-       acpi_unmap_table(tbl);
-no_dsdt:
-       if (x.header.length >= 140 && x.xfirmware_ctrl) {
-               addr = (unsigned long)x.xfirmware_ctrl;
-               if (connect) {
-                       x.xfirmware_ctrl = lseek(fd, 0, SEEK_CUR);
-               }
-       } else if (x.header.length >= 40 && x.firmware_ctrl) {
-               addr = (unsigned long)x.firmware_ctrl;
-               if (connect) {
-                       x.firmware_ctrl = lseek(fd, 0, SEEK_CUR);
-               }
-       } else {
-               fprintf(stderr, "No FACS in FADT!\n");
-               goto no_facs;
-       }
-       tbl = acpi_map_table(addr, FACS_SIG);
-       if (!tbl) goto no_facs;
-       /* do not checksum FACS */
-       write_table(fd, tbl, addr);
-       acpi_unmap_table(tbl);
-no_facs:
-       write_table(fd, (struct acpi_table_header *)&x, xaddr);
-}
-
-static int acpi_dump_SDT(int fd, struct acpi_rsdp_descriptor *rsdp)
-{
-       struct acpi_table_header *sdt, *tbl = 0;
-       int xsdt = 1, i, num;
-       char *offset;
-       unsigned long addr;
-       if (rsdp->revision > 1 && rsdp->xsdt_physical_address) {
-               tbl = acpi_map_table(rsdp->xsdt_physical_address, "XSDT");
-       }
-       if (!tbl && rsdp->rsdt_physical_address) {
-               xsdt = 0;
-               tbl = acpi_map_table(rsdp->rsdt_physical_address, "RSDT");
-       }
-       if (!tbl) return 0;
-       sdt = malloc(tbl->length);
-       memcpy(sdt, tbl, tbl->length);
-       acpi_unmap_table(tbl);
-       if (checksum((u8 *)sdt, sdt->length))
-               fprintf(stderr, "Wrong checksum for %s!\n", (xsdt)?"XSDT":"RSDT");
-       num = (sdt->length - sizeof(struct acpi_table_header))/((xsdt)?sizeof(u64):sizeof(u32));
-       offset = (char *)sdt + sizeof(struct acpi_table_header);
-       for (i = 0; i < num; ++i, offset += ((xsdt) ? sizeof(u64) : sizeof(u32))) {
-               addr = (xsdt) ? (unsigned long)(*(u64 *)offset):
-                               (unsigned long)(*(u32 *)offset);
-               if (!addr) continue;
-               tbl = acpi_map_table(addr, 0);
-               if (!tbl) continue;
-               if (!memcmp(tbl->signature, FADT_SIG, 4)) {
-                       acpi_dump_FADT(fd, tbl, addr);
-               } else {
-                       if (checksum((u8 *)tbl, tbl->length))
-                               fprintf(stderr, "Wrong checksum for generic table!\n");
-                       write_table(fd, tbl, addr);
-               }
-               acpi_unmap_table(tbl);
-               if (connect) {
-                       if (xsdt)
-                               (*(u64*)offset) = lseek(fd, 0, SEEK_CUR);
-                       else
-                               (*(u32*)offset) = lseek(fd, 0, SEEK_CUR);
-               }
-       }
-       if (xsdt) {
-               addr = (unsigned long)rsdp->xsdt_physical_address;
-               if (connect) {
-                       rsdp->xsdt_physical_address = lseek(fd, 0, SEEK_CUR);
-               }
-       } else {
-               addr = (unsigned long)rsdp->rsdt_physical_address;
-               if (connect) {
-                       rsdp->rsdt_physical_address = lseek(fd, 0, SEEK_CUR);
-               }
-       }
-       write_table(fd, sdt, addr);
-       free (sdt);
-       return 1;
-}
-
-#define DYNAMIC_SSDT   "/sys/firmware/acpi/tables/dynamic"
-
-static void acpi_dump_dynamic_SSDT(int fd)
-{
-       struct stat file_stat;
-       char filename[256], *ptr;
-       DIR *tabledir;
-       struct dirent *entry;
-       FILE *fp;
-       int count, readcount, length;
-       struct acpi_table_header table_header, *ptable;
-
-       if (stat(DYNAMIC_SSDT, &file_stat) == -1) {
-               /* The directory doesn't exist */
-               return;
-       }
-       tabledir = opendir(DYNAMIC_SSDT);
-       if(!tabledir){
-               /*can't open the directory */
-               return;
-         }
-
-       while ((entry = readdir(tabledir)) != 0){
-               /* skip the file of . /.. */
-               if (entry->d_name[0] == '.')
-                       continue;
-
-               sprintf(filename, "%s/%s", DYNAMIC_SSDT, entry->d_name);
-               fp = fopen(filename, "r");
-               if (fp == NULL) {
-                       fprintf(stderr, "Can't open the file of %s\n",
-                                               filename);
-                       continue;
-               }
-               /* Read the Table header to parse the table length */
-               count = fread(&table_header, 1, sizeof(struct acpi_table_header), fp);
-               if (count < sizeof(table_header)) {
-                       /* the length is lessn than ACPI table header. skip it */
-                       fclose(fp);
-                       continue;
-               }
-               length = table_header.length;
-               ptr = malloc(table_header.length);
-               fseek(fp, 0, SEEK_SET);
-               readcount = 0;
-               while(!feof(fp) && readcount < length) {
-                       count = fread(ptr + readcount, 1, 256, fp);
-                       readcount += count;
-               }
-               fclose(fp);
-               ptable = (struct acpi_table_header *) ptr;
-               if (checksum((u8 *) ptable, ptable->length))
-                       fprintf(stderr, "Wrong checksum "
-                                       "for dynamic SSDT table!\n");
-               write_table(fd, ptable, 0);
-               free(ptr);
-       }
-       closedir(tabledir);
-       return;
-}
-
-static void usage(const char *progname)
-{
-       puts("Usage:");
-       printf("%s [--addr 0x1234][--table DSDT][--output filename]"
-               "[--binary][--length 0x456][--help]\n", progname);
-       puts("\t--addr 0x1234 or -a 0x1234 -- look for tables at this physical address");
-       puts("\t--table DSDT or -t DSDT -- only dump table with DSDT signature");
-       puts("\t--output filename or -o filename -- redirect output from stdin to filename");
-       puts("\t--binary or -b -- dump data in binary form rather than in hex-dump format");
-       puts("\t--length 0x456 or -l 0x456 -- works only with --addr, dump physical memory"
-               "\n\t\tregion without trying to understand it's contents");
-       puts("\t--skip 2 or -s 2 -- skip 2 tables of the given name and output only 3rd one");
-       puts("\t--help or -h -- this help message");
-       exit(0);
-}
-
-static struct option long_options[] = {
-       {"addr", 1, 0, 0},
-       {"table", 1, 0, 0},
-       {"output", 1, 0, 0},
-       {"binary", 0, 0, 0},
-       {"length", 1, 0, 0},
-       {"skip", 1, 0, 0},
-       {"help", 0, 0, 0},
-       {0, 0, 0, 0}
-};
-int main(int argc, char **argv)
-{
-       int option_index, c, fd;
-       u8 *raw;
-       struct acpi_rsdp_descriptor rsdpx, *x = 0;
-       char *filename = 0;
-       char buff[80];
-       memset(select_sig, 0, 4);
-       print = 1;
-       connect = 0;
-       addr = length = 0;
-       skip = 0;
-       while (1) {
-               option_index = 0;
-               c = getopt_long(argc, argv, "a:t:o:bl:s:h",
-                                   long_options, &option_index);
-               if (c == -1)
-                       break;
-
-               switch (c) {
-               case 0:
-                       switch (option_index) {
-                       case 0:
-                               addr = strtoul(optarg, (char **)NULL, 16);
-                               break;
-                       case 1:
-                               memcpy(select_sig, optarg, 4);
-                               break;
-                       case 2:
-                               filename = optarg;
-                               break;
-                       case 3:
-                               print = 0;
-                               break;
-                       case 4:
-                               length = strtoul(optarg, (char **)NULL, 16);
-                               break;
-                       case 5:
-                               skip = strtoul(optarg, (char **)NULL, 10);
-                               break;
-                       case 6:
-                               usage(argv[0]);
-                               exit(0);
-                       }
-                       break;
-               case 'a':
-                       addr = strtoul(optarg, (char **)NULL, 16);
-                       break;
-               case 't':
-                       memcpy(select_sig, optarg, 4);
-                       break;
-               case 'o':
-                       filename = optarg;
-                       break;
-               case 'b':
-                       print = 0;
-                       break;
-               case 'l':
-                       length = strtoul(optarg, (char **)NULL, 16);
-                       break;
-               case 's':
-                       skip = strtoul(optarg, (char **)NULL, 10);
-                       break;
-               case 'h':
-                       usage(argv[0]);
-                       exit(0);
-               default:
-                       printf("Unknown option!\n");
-                       usage(argv[0]);
-                       exit(0);
-               }
-       }
-
-       fd = STDOUT_FILENO;
-       if (filename) {
-               fd = creat(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
-               if (fd < 0)
-                       return fd;
-       }
-
-       if (!select_sig[0] && !print) {
-               connect = 1;
-       }
-
-       psz = sysconf(_SC_PAGESIZE);
-       if (length && addr) {
-               /* We know length and address, it means we just want a memory dump */
-               if (!(raw = acpi_map_memory(addr, length)))
-                       goto not_found;
-               write(fd, raw, length);
-               acpi_unmap_memory(raw, length);
-               close(fd);
-               return 0;
-       }
-
-       length = sizeof(struct acpi_rsdp_descriptor);
-       if (!addr) {
-               addr = read_efi_systab();
-               if (!addr) {
-                       addr = ACPI_HI_RSDP_WINDOW_BASE;
-                       length = ACPI_HI_RSDP_WINDOW_SIZE;
-               }
-       }
-
-       if (!(raw = acpi_map_memory(addr, length)) ||
-           !(x = acpi_scan_for_rsdp(raw, length)))
-               goto not_found;
-
-       /* Find RSDP and print all found tables */
-       memcpy(&rsdpx, x, sizeof(struct acpi_rsdp_descriptor));
-       acpi_unmap_memory(raw, length);
-       if (connect) {
-               lseek(fd, sizeof(struct acpi_rsdp_descriptor), SEEK_SET);
-       }
-       if (!acpi_dump_SDT(fd, &rsdpx))
-               goto not_found;
-       if (connect) {
-               lseek(fd, 0, SEEK_SET);
-               write(fd, x, (rsdpx.revision < 2) ?
-                       ACPI_RSDP_CHECKSUM_LENGTH : ACPI_RSDP_XCHECKSUM_LENGTH);
-       } else if (!select_sig[0] || !memcmp("RSD PTR ", select_sig, 4)) {
-               addr += (long)x - (long)raw;
-               length = snprintf(buff, 80, "RSD PTR @ %p\n", (void *)addr);
-               write(fd, buff, length);
-               acpi_show_data(fd, (u8 *) & rsdpx, (rsdpx.revision < 2) ?
-                               ACPI_RSDP_CHECKSUM_LENGTH : ACPI_RSDP_XCHECKSUM_LENGTH);
-               buff[0] = '\n';
-               write(fd, buff, 1);
-       }
-       acpi_dump_dynamic_SSDT(fd);
-       close(fd);
-       return 0;
-not_found:
-       close(fd);
-       fprintf(stderr, "ACPI tables were not found. If you know location "
-               "of RSD PTR table (from dmesg, etc), "
-               "supply it with either --addr or -a option\n");
-       return 1;
-}
diff --git a/tools/power/acpi/tools/acpidump/acpidump.h b/tools/power/acpi/tools/acpidump/acpidump.h
new file mode 100644 (file)
index 0000000..46f5195
--- /dev/null
@@ -0,0 +1,130 @@
+/******************************************************************************
+ *
+ * Module Name: acpidump.h - Include file for acpi_dump utility
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+/*
+ * Global variables. Defined in main.c only, externed in all other files
+ */
+#ifdef _DECLARE_GLOBALS
+#define EXTERN
+#define INIT_GLOBAL(a,b)        a=b
+#define DEFINE_ACPI_GLOBALS     1
+#else
+#define EXTERN                  extern
+#define INIT_GLOBAL(a,b)        a
+#endif
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "actables.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+/* Globals */
+
+EXTERN u8 INIT_GLOBAL(gbl_summary_mode, FALSE);
+EXTERN u8 INIT_GLOBAL(gbl_verbose_mode, FALSE);
+EXTERN u8 INIT_GLOBAL(gbl_binary_mode, FALSE);
+EXTERN u8 INIT_GLOBAL(gbl_dump_customized_tables, FALSE);
+EXTERN u8 INIT_GLOBAL(gbl_do_not_dump_xsdt, FALSE);
+EXTERN FILE INIT_GLOBAL(*gbl_output_file, NULL);
+EXTERN char INIT_GLOBAL(*gbl_output_filename, NULL);
+EXTERN u64 INIT_GLOBAL(gbl_rsdp_base, 0);
+
+/* Globals required for use with ACPICA modules */
+
+#ifdef _DECLARE_GLOBALS
+u8 acpi_gbl_integer_byte_width = 8;
+#endif
+
+/* Action table used to defer requested options */
+
+struct ap_dump_action {
+       char *argument;
+       u32 to_be_done;
+};
+
+#define AP_MAX_ACTIONS              32
+
+#define AP_DUMP_ALL_TABLES          0
+#define AP_DUMP_TABLE_BY_ADDRESS    1
+#define AP_DUMP_TABLE_BY_NAME       2
+#define AP_DUMP_TABLE_BY_FILE       3
+
+#define AP_MAX_ACPI_FILES           256        /* Prevent infinite loops */
+
+/* Minimum FADT sizes for various table addresses */
+
+#define MIN_FADT_FOR_DSDT           (ACPI_FADT_OFFSET (dsdt) + sizeof (u32))
+#define MIN_FADT_FOR_FACS           (ACPI_FADT_OFFSET (facs) + sizeof (u32))
+#define MIN_FADT_FOR_XDSDT          (ACPI_FADT_OFFSET (Xdsdt) + sizeof (u64))
+#define MIN_FADT_FOR_XFACS          (ACPI_FADT_OFFSET (Xfacs) + sizeof (u64))
+
+/*
+ * apdump - Table get/dump routines
+ */
+int ap_dump_table_from_file(char *pathname);
+
+int ap_dump_table_by_name(char *signature);
+
+int ap_dump_table_by_address(char *ascii_address);
+
+int ap_dump_all_tables(void);
+
+u8 ap_is_valid_header(struct acpi_table_header *table);
+
+u8 ap_is_valid_checksum(struct acpi_table_header *table);
+
+u32 ap_get_table_length(struct acpi_table_header *table);
+
+/*
+ * apfiles - File I/O utilities
+ */
+int ap_open_output_file(char *pathname);
+
+int ap_write_to_binary_file(struct acpi_table_header *table, u32 instance);
+
+struct acpi_table_header *ap_get_table_from_file(char *pathname,
+                                                u32 *file_size);
diff --git a/tools/power/acpi/tools/acpidump/apdump.c b/tools/power/acpi/tools/acpidump/apdump.c
new file mode 100644 (file)
index 0000000..3cac123
--- /dev/null
@@ -0,0 +1,451 @@
+/******************************************************************************
+ *
+ * Module Name: apdump - Dump routines for ACPI tables (acpidump)
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpidump.h"
+
+/* Local prototypes */
+
+static int
+ap_dump_table_buffer(struct acpi_table_header *table,
+                    u32 instance, acpi_physical_address address);
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_is_valid_header
+ *
+ * PARAMETERS:  table               - Pointer to table to be validated
+ *
+ * RETURN:      TRUE if the header appears to be valid. FALSE otherwise
+ *
+ * DESCRIPTION: Check for a valid ACPI table header
+ *
+ ******************************************************************************/
+
+u8 ap_is_valid_header(struct acpi_table_header *table)
+{
+
+       if (!ACPI_VALIDATE_RSDP_SIG(table->signature)) {
+
+               /* Make sure signature is all ASCII and a valid ACPI name */
+
+               if (!acpi_ut_valid_acpi_name(table->signature)) {
+                       fprintf(stderr,
+                               "Table signature (0x%8.8X) is invalid\n",
+                               *(u32 *)table->signature);
+                       return (FALSE);
+               }
+
+               /* Check for minimum table length */
+
+               if (table->length < sizeof(struct acpi_table_header)) {
+                       fprintf(stderr, "Table length (0x%8.8X) is invalid\n",
+                               table->length);
+                       return (FALSE);
+               }
+       }
+
+       return (TRUE);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_is_valid_checksum
+ *
+ * PARAMETERS:  table               - Pointer to table to be validated
+ *
+ * RETURN:      TRUE if the checksum appears to be valid. FALSE otherwise.
+ *
+ * DESCRIPTION: Check for a valid ACPI table checksum.
+ *
+ ******************************************************************************/
+
+u8 ap_is_valid_checksum(struct acpi_table_header *table)
+{
+       acpi_status status;
+       struct acpi_table_rsdp *rsdp;
+
+       if (ACPI_VALIDATE_RSDP_SIG(table->signature)) {
+               /*
+                * Checksum for RSDP.
+                * Note: Other checksums are computed during the table dump.
+                */
+               rsdp = ACPI_CAST_PTR(struct acpi_table_rsdp, table);
+               status = acpi_tb_validate_rsdp(rsdp);
+       } else {
+               status = acpi_tb_verify_checksum(table, table->length);
+       }
+
+       if (ACPI_FAILURE(status)) {
+               fprintf(stderr, "%4.4s: Warning: wrong checksum in table\n",
+                       table->signature);
+       }
+
+       return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_get_table_length
+ *
+ * PARAMETERS:  table               - Pointer to the table
+ *
+ * RETURN:      Table length
+ *
+ * DESCRIPTION: Obtain table length according to table signature.
+ *
+ ******************************************************************************/
+
+u32 ap_get_table_length(struct acpi_table_header *table)
+{
+       struct acpi_table_rsdp *rsdp;
+
+       /* Check if table is valid */
+
+       if (!ap_is_valid_header(table)) {
+               return (0);
+       }
+
+       if (ACPI_VALIDATE_RSDP_SIG(table->signature)) {
+               rsdp = ACPI_CAST_PTR(struct acpi_table_rsdp, table);
+               return (rsdp->length);
+       }
+
+       /* Normal ACPI table */
+
+       return (table->length);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_dump_table_buffer
+ *
+ * PARAMETERS:  table               - ACPI table to be dumped
+ *              instance            - ACPI table instance no. to be dumped
+ *              address             - Physical address of the table
+ *
+ * RETURN:      None
+ *
+ * DESCRIPTION: Dump an ACPI table in standard ASCII hex format, with a
+ *              header that is compatible with the acpi_xtract utility.
+ *
+ ******************************************************************************/
+
+static int
+ap_dump_table_buffer(struct acpi_table_header *table,
+                    u32 instance, acpi_physical_address address)
+{
+       u32 table_length;
+
+       table_length = ap_get_table_length(table);
+
+       /* Print only the header if requested */
+
+       if (gbl_summary_mode) {
+               acpi_tb_print_table_header(address, table);
+               return (0);
+       }
+
+       /* Dump to binary file if requested */
+
+       if (gbl_binary_mode) {
+               return (ap_write_to_binary_file(table, instance));
+       }
+
+       /*
+        * Dump the table with header for use with acpixtract utility.
+        * Note: simplest to just always emit a 64-bit address. acpi_xtract
+        * utility can handle this.
+        */
+       printf("%4.4s @ 0x%8.8X%8.8X\n", table->signature,
+              ACPI_FORMAT_UINT64(address));
+
+       acpi_ut_dump_buffer(ACPI_CAST_PTR(u8, table), table_length,
+                           DB_BYTE_DISPLAY, 0);
+       printf("\n");
+       return (0);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_dump_all_tables
+ *
+ * PARAMETERS:  None
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Get all tables from the RSDT/XSDT (or at least all of the
+ *              tables that we can possibly get).
+ *
+ ******************************************************************************/
+
+int ap_dump_all_tables(void)
+{
+       struct acpi_table_header *table;
+       u32 instance = 0;
+       acpi_physical_address address;
+       acpi_status status;
+       int table_status;
+       u32 i;
+
+       /* Get and dump all available ACPI tables */
+
+       for (i = 0; i < AP_MAX_ACPI_FILES; i++) {
+               status =
+                   acpi_os_get_table_by_index(i, &table, &instance, &address);
+               if (ACPI_FAILURE(status)) {
+
+                       /* AE_LIMIT means that no more tables are available */
+
+                       if (status == AE_LIMIT) {
+                               return (0);
+                       } else if (i == 0) {
+                               fprintf(stderr,
+                                       "Could not get ACPI tables, %s\n",
+                                       acpi_format_exception(status));
+                               return (-1);
+                       } else {
+                               fprintf(stderr,
+                                       "Could not get ACPI table at index %u, %s\n",
+                                       i, acpi_format_exception(status));
+                               continue;
+                       }
+               }
+
+               table_status = ap_dump_table_buffer(table, instance, address);
+               free(table);
+
+               if (table_status) {
+                       break;
+               }
+       }
+
+       /* Something seriously bad happened if the loop terminates here */
+
+       return (-1);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_dump_table_by_address
+ *
+ * PARAMETERS:  ascii_address       - Address for requested ACPI table
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Get an ACPI table via a physical address and dump it.
+ *
+ ******************************************************************************/
+
+int ap_dump_table_by_address(char *ascii_address)
+{
+       acpi_physical_address address;
+       struct acpi_table_header *table;
+       acpi_status status;
+       int table_status;
+       u64 long_address;
+
+       /* Convert argument to an integer physical address */
+
+       status = acpi_ut_strtoul64(ascii_address, 0, &long_address);
+       if (ACPI_FAILURE(status)) {
+               fprintf(stderr, "%s: Could not convert to a physical address\n",
+                       ascii_address);
+               return (-1);
+       }
+
+       address = (acpi_physical_address) long_address;
+       status = acpi_os_get_table_by_address(address, &table);
+       if (ACPI_FAILURE(status)) {
+               fprintf(stderr, "Could not get table at 0x%8.8X%8.8X, %s\n",
+                       ACPI_FORMAT_UINT64(address),
+                       acpi_format_exception(status));
+               return (-1);
+       }
+
+       table_status = ap_dump_table_buffer(table, 0, address);
+       free(table);
+       return (table_status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_dump_table_by_name
+ *
+ * PARAMETERS:  signature           - Requested ACPI table signature
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Get an ACPI table via a signature and dump it. Handles
+ *              multiple tables with the same signature (SSDTs).
+ *
+ ******************************************************************************/
+
+int ap_dump_table_by_name(char *signature)
+{
+       char local_signature[ACPI_NAME_SIZE + 1];
+       u32 instance;
+       struct acpi_table_header *table;
+       acpi_physical_address address;
+       acpi_status status;
+       int table_status;
+
+       if (strlen(signature) != ACPI_NAME_SIZE) {
+               fprintf(stderr,
+                       "Invalid table signature [%s]: must be exactly 4 characters\n",
+                       signature);
+               return (-1);
+       }
+
+       /* Table signatures are expected to be uppercase */
+
+       strcpy(local_signature, signature);
+       acpi_ut_strupr(local_signature);
+
+       /* To be friendly, handle tables whose signatures do not match the name */
+
+       if (ACPI_COMPARE_NAME(local_signature, "FADT")) {
+               strcpy(local_signature, ACPI_SIG_FADT);
+       } else if (ACPI_COMPARE_NAME(local_signature, "MADT")) {
+               strcpy(local_signature, ACPI_SIG_MADT);
+       }
+
+       /* Dump all instances of this signature (to handle multiple SSDTs) */
+
+       for (instance = 0; instance < AP_MAX_ACPI_FILES; instance++) {
+               status = acpi_os_get_table_by_name(local_signature, instance,
+                                                  &table, &address);
+               if (ACPI_FAILURE(status)) {
+
+                       /* AE_LIMIT means that no more tables are available */
+
+                       if (status == AE_LIMIT) {
+                               return (0);
+                       }
+
+                       fprintf(stderr,
+                               "Could not get ACPI table with signature [%s], %s\n",
+                               local_signature, acpi_format_exception(status));
+                       return (-1);
+               }
+
+               table_status = ap_dump_table_buffer(table, instance, address);
+               free(table);
+
+               if (table_status) {
+                       break;
+               }
+       }
+
+       /* Something seriously bad happened if the loop terminates here */
+
+       return (-1);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_dump_table_from_file
+ *
+ * PARAMETERS:  pathname            - File containing the binary ACPI table
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Dump an ACPI table from a binary file
+ *
+ ******************************************************************************/
+
+int ap_dump_table_from_file(char *pathname)
+{
+       struct acpi_table_header *table;
+       u32 file_size = 0;
+       int table_status = -1;
+
+       /* Get the entire ACPI table from the file */
+
+       table = ap_get_table_from_file(pathname, &file_size);
+       if (!table) {
+               return (-1);
+       }
+
+       /* File must be at least as long as the table length */
+
+       if (table->length > file_size) {
+               fprintf(stderr,
+                       "Table length (0x%X) is too large for input file (0x%X) %s\n",
+                       table->length, file_size, pathname);
+               goto exit;
+       }
+
+       if (gbl_verbose_mode) {
+               fprintf(stderr,
+                       "Input file:  %s contains table [%4.4s], 0x%X (%u) bytes\n",
+                       pathname, table->signature, file_size, file_size);
+       }
+
+       table_status = ap_dump_table_buffer(table, 0, 0);
+
+exit:
+       free(table);
+       return (table_status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_os* print functions
+ *
+ * DESCRIPTION: Used for linkage with ACPICA modules
+ *
+ ******************************************************************************/
+
+void ACPI_INTERNAL_VAR_XFACE acpi_os_printf(const char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       vfprintf(stdout, fmt, args);
+       va_end(args);
+}
+
+void acpi_os_vprintf(const char *fmt, va_list args)
+{
+       vfprintf(stdout, fmt, args);
+}
diff --git a/tools/power/acpi/tools/acpidump/apfiles.c b/tools/power/acpi/tools/acpidump/apfiles.c
new file mode 100644 (file)
index 0000000..4488acc
--- /dev/null
@@ -0,0 +1,228 @@
+/******************************************************************************
+ *
+ * Module Name: apfiles - File-related functions for acpidump utility
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "acpidump.h"
+#include "acapps.h"
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_open_output_file
+ *
+ * PARAMETERS:  pathname            - Output filename
+ *
+ * RETURN:      Open file handle
+ *
+ * DESCRIPTION: Open a text output file for acpidump. Checks if file already
+ *              exists.
+ *
+ ******************************************************************************/
+
+int ap_open_output_file(char *pathname)
+{
+       struct stat stat_info;
+       FILE *file;
+
+       /* If file exists, prompt for overwrite */
+
+       if (!stat(pathname, &stat_info)) {
+               fprintf(stderr,
+                       "Target path already exists, overwrite? [y|n] ");
+
+               if (getchar() != 'y') {
+                       return (-1);
+               }
+       }
+
+       /* Point stdout to the file */
+
+       file = freopen(pathname, "w", stdout);
+       if (!file) {
+               perror("Could not open output file");
+               return (-1);
+       }
+
+       /* Save the file and path */
+
+       gbl_output_file = file;
+       gbl_output_filename = pathname;
+       return (0);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_write_to_binary_file
+ *
+ * PARAMETERS:  table               - ACPI table to be written
+ *              instance            - ACPI table instance no. to be written
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Write an ACPI table to a binary file. Builds the output
+ *              filename from the table signature.
+ *
+ ******************************************************************************/
+
+int ap_write_to_binary_file(struct acpi_table_header *table, u32 instance)
+{
+       char filename[ACPI_NAME_SIZE + 16];
+       char instance_str[16];
+       FILE *file;
+       size_t actual;
+       u32 table_length;
+
+       /* Obtain table length */
+
+       table_length = ap_get_table_length(table);
+
+       /* Construct lower-case filename from the table local signature */
+
+       if (ACPI_VALIDATE_RSDP_SIG(table->signature)) {
+               ACPI_MOVE_NAME(filename, ACPI_RSDP_NAME);
+       } else {
+               ACPI_MOVE_NAME(filename, table->signature);
+       }
+       filename[0] = (char)ACPI_TOLOWER(filename[0]);
+       filename[1] = (char)ACPI_TOLOWER(filename[1]);
+       filename[2] = (char)ACPI_TOLOWER(filename[2]);
+       filename[3] = (char)ACPI_TOLOWER(filename[3]);
+       filename[ACPI_NAME_SIZE] = 0;
+
+       /* Handle multiple SSDts - create different filenames for each */
+
+       if (instance > 0) {
+               sprintf(instance_str, "%u", instance);
+               strcat(filename, instance_str);
+       }
+
+       strcat(filename, ACPI_TABLE_FILE_SUFFIX);
+
+       if (gbl_verbose_mode) {
+               fprintf(stderr,
+                       "Writing [%4.4s] to binary file: %s 0x%X (%u) bytes\n",
+                       table->signature, filename, table->length,
+                       table->length);
+       }
+
+       /* Open the file and dump the entire table in binary mode */
+
+       file = fopen(filename, "wb");
+       if (!file) {
+               perror("Could not open output file");
+               return (-1);
+       }
+
+       actual = fwrite(table, 1, table_length, file);
+       if (actual != table_length) {
+               perror("Error writing binary output file");
+               fclose(file);
+               return (-1);
+       }
+
+       fclose(file);
+       return (0);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_get_table_from_file
+ *
+ * PARAMETERS:  pathname            - File containing the binary ACPI table
+ *              out_file_size       - Where the file size is returned
+ *
+ * RETURN:      Buffer containing the ACPI table. NULL on error.
+ *
+ * DESCRIPTION: Open a file and read it entirely into a new buffer
+ *
+ ******************************************************************************/
+
+struct acpi_table_header *ap_get_table_from_file(char *pathname,
+                                                u32 *out_file_size)
+{
+       struct acpi_table_header *buffer = NULL;
+       FILE *file;
+       u32 file_size;
+       size_t actual;
+
+       /* Must use binary mode */
+
+       file = fopen(pathname, "rb");
+       if (!file) {
+               perror("Could not open input file");
+               return (NULL);
+       }
+
+       /* Need file size to allocate a buffer */
+
+       file_size = cm_get_file_size(file);
+       if (file_size == ACPI_UINT32_MAX) {
+               fprintf(stderr,
+                       "Could not get input file size: %s\n", pathname);
+               goto cleanup;
+       }
+
+       /* Allocate a buffer for the entire file */
+
+       buffer = calloc(1, file_size);
+       if (!buffer) {
+               fprintf(stderr,
+                       "Could not allocate file buffer of size: %u\n",
+                       file_size);
+               goto cleanup;
+       }
+
+       /* Read the entire file */
+
+       actual = fread(buffer, 1, file_size, file);
+       if (actual != file_size) {
+               fprintf(stderr, "Could not read input file: %s\n", pathname);
+               free(buffer);
+               buffer = NULL;
+               goto cleanup;
+       }
+
+       *out_file_size = file_size;
+
+cleanup:
+       fclose(file);
+       return (buffer);
+}
diff --git a/tools/power/acpi/tools/acpidump/apmain.c b/tools/power/acpi/tools/acpidump/apmain.c
new file mode 100644 (file)
index 0000000..51e8d63
--- /dev/null
@@ -0,0 +1,351 @@
+/******************************************************************************
+ *
+ * Module Name: apmain - Main module for the acpidump utility
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2014, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#define _DECLARE_GLOBALS
+#include "acpidump.h"
+#include "acapps.h"
+
+/*
+ * acpidump - A portable utility for obtaining system ACPI tables and dumping
+ * them in an ASCII hex format suitable for binary extraction via acpixtract.
+ *
+ * Obtaining the system ACPI tables is an OS-specific operation.
+ *
+ * This utility can be ported to any host operating system by providing a
+ * module containing system-specific versions of these interfaces:
+ *
+ *      acpi_os_get_table_by_address
+ *      acpi_os_get_table_by_index
+ *      acpi_os_get_table_by_name
+ *
+ * See the ACPICA Reference Guide for the exact definitions of these
+ * interfaces. Also, see these ACPICA source code modules for example
+ * implementations:
+ *
+ *      source/os_specific/service_layers/oswintbl.c
+ *      source/os_specific/service_layers/oslinuxtbl.c
+ */
+
+/* Local prototypes */
+
+static void ap_display_usage(void);
+
+static int ap_do_options(int argc, char **argv);
+
+static void ap_insert_action(char *argument, u32 to_be_done);
+
+/* Table for deferred actions from command line options */
+
+struct ap_dump_action action_table[AP_MAX_ACTIONS];
+u32 current_action = 0;
+
+#define AP_UTILITY_NAME             "ACPI Binary Table Dump Utility"
+#define AP_SUPPORTED_OPTIONS        "?a:bcf:hn:o:r:svxz"
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_display_usage
+ *
+ * DESCRIPTION: Usage message for the acpi_dump utility
+ *
+ ******************************************************************************/
+
+static void ap_display_usage(void)
+{
+
+       ACPI_USAGE_HEADER("acpidump [options]");
+
+       ACPI_OPTION("-b", "Dump tables to binary files");
+       ACPI_OPTION("-c", "Dump customized tables");
+       ACPI_OPTION("-h -?", "This help message");
+       ACPI_OPTION("-o <File>", "Redirect output to file");
+       ACPI_OPTION("-r <Address>", "Dump tables from specified RSDP");
+       ACPI_OPTION("-s", "Print table summaries only");
+       ACPI_OPTION("-v", "Display version information");
+       ACPI_OPTION("-z", "Verbose mode");
+
+       printf("\nTable Options:\n");
+
+       ACPI_OPTION("-a <Address>", "Get table via a physical address");
+       ACPI_OPTION("-f <BinaryFile>", "Get table via a binary file");
+       ACPI_OPTION("-n <Signature>", "Get table via a name/signature");
+       ACPI_OPTION("-x", "Do not use but dump XSDT");
+       ACPI_OPTION("-x -x", "Do not use or dump XSDT");
+
+       printf("\n"
+              "Invocation without parameters dumps all available tables\n"
+              "Multiple mixed instances of -a, -f, and -n are supported\n\n");
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_insert_action
+ *
+ * PARAMETERS:  argument            - Pointer to the argument for this action
+ *              to_be_done          - What to do to process this action
+ *
+ * RETURN:      None. Exits program if action table becomes full.
+ *
+ * DESCRIPTION: Add an action item to the action table
+ *
+ ******************************************************************************/
+
+static void ap_insert_action(char *argument, u32 to_be_done)
+{
+
+       /* Insert action and check for table overflow */
+
+       action_table[current_action].argument = argument;
+       action_table[current_action].to_be_done = to_be_done;
+
+       current_action++;
+       if (current_action > AP_MAX_ACTIONS) {
+               fprintf(stderr, "Too many table options (max %u)\n",
+                       AP_MAX_ACTIONS);
+               exit(-1);
+       }
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    ap_do_options
+ *
+ * PARAMETERS:  argc/argv           - Standard argc/argv
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Command line option processing. The main actions for getting
+ *              and dumping tables are deferred via the action table.
+ *
+ *****************************************************************************/
+
+static int ap_do_options(int argc, char **argv)
+{
+       int j;
+       acpi_status status;
+
+       /* Command line options */
+
+       while ((j = acpi_getopt(argc, argv, AP_SUPPORTED_OPTIONS)) != EOF)
+               switch (j) {
+                       /*
+                        * Global options
+                        */
+               case 'b':       /* Dump all input tables to binary files */
+
+                       gbl_binary_mode = TRUE;
+                       continue;
+
+               case 'c':       /* Dump customized tables */
+
+                       gbl_dump_customized_tables = TRUE;
+                       continue;
+
+               case 'h':
+               case '?':
+
+                       ap_display_usage();
+                       exit(0);
+
+               case 'o':       /* Redirect output to a single file */
+
+                       if (ap_open_output_file(acpi_gbl_optarg)) {
+                               exit(-1);
+                       }
+                       continue;
+
+               case 'r':       /* Dump tables from specified RSDP */
+
+                       status =
+                           acpi_ut_strtoul64(acpi_gbl_optarg, 0,
+                                             &gbl_rsdp_base);
+                       if (ACPI_FAILURE(status)) {
+                               fprintf(stderr,
+                                       "%s: Could not convert to a physical address\n",
+                                       acpi_gbl_optarg);
+                               exit(-1);
+                       }
+                       continue;
+
+               case 's':       /* Print table summaries only */
+
+                       gbl_summary_mode = TRUE;
+                       continue;
+
+               case 'x':       /* Do not use XSDT */
+
+                       if (!acpi_gbl_do_not_use_xsdt) {
+                               acpi_gbl_do_not_use_xsdt = TRUE;
+                       } else {
+                               gbl_do_not_dump_xsdt = TRUE;
+                       }
+                       continue;
+
+               case 'v':       /* Revision/version */
+
+                       printf(ACPI_COMMON_SIGNON(AP_UTILITY_NAME));
+                       exit(0);
+
+               case 'z':       /* Verbose mode */
+
+                       gbl_verbose_mode = TRUE;
+                       fprintf(stderr, ACPI_COMMON_SIGNON(AP_UTILITY_NAME));
+                       continue;
+
+                       /*
+                        * Table options
+                        */
+               case 'a':       /* Get table by physical address */
+
+                       ap_insert_action(acpi_gbl_optarg,
+                                        AP_DUMP_TABLE_BY_ADDRESS);
+                       break;
+
+               case 'f':       /* Get table from a file */
+
+                       ap_insert_action(acpi_gbl_optarg,
+                                        AP_DUMP_TABLE_BY_FILE);
+                       break;
+
+               case 'n':       /* Get table by input name (signature) */
+
+                       ap_insert_action(acpi_gbl_optarg,
+                                        AP_DUMP_TABLE_BY_NAME);
+                       break;
+
+               default:
+
+                       ap_display_usage();
+                       exit(-1);
+               }
+
+       /* If there are no actions, this means "get/dump all tables" */
+
+       if (current_action == 0) {
+               ap_insert_action(NULL, AP_DUMP_ALL_TABLES);
+       }
+
+       return (0);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    main
+ *
+ * PARAMETERS:  argc/argv           - Standard argc/argv
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: C main function for acpidump utility
+ *
+ ******************************************************************************/
+
+int ACPI_SYSTEM_XFACE main(int argc, char *argv[])
+{
+       int status = 0;
+       struct ap_dump_action *action;
+       u32 file_size;
+       u32 i;
+
+       ACPI_DEBUG_INITIALIZE();        /* For debug version only */
+
+       /* Process command line options */
+
+       if (ap_do_options(argc, argv)) {
+               return (-1);
+       }
+
+       /* Get/dump ACPI table(s) as requested */
+
+       for (i = 0; i < current_action; i++) {
+               action = &action_table[i];
+               switch (action->to_be_done) {
+               case AP_DUMP_ALL_TABLES:
+
+                       status = ap_dump_all_tables();
+                       break;
+
+               case AP_DUMP_TABLE_BY_ADDRESS:
+
+                       status = ap_dump_table_by_address(action->argument);
+                       break;
+
+               case AP_DUMP_TABLE_BY_NAME:
+
+                       status = ap_dump_table_by_name(action->argument);
+                       break;
+
+               case AP_DUMP_TABLE_BY_FILE:
+
+                       status = ap_dump_table_from_file(action->argument);
+                       break;
+
+               default:
+
+                       fprintf(stderr,
+                               "Internal error, invalid action: 0x%X\n",
+                               action->to_be_done);
+                       return (-1);
+               }
+
+               if (status) {
+                       return (status);
+               }
+       }
+
+       if (gbl_output_file) {
+               if (gbl_verbose_mode) {
+
+                       /* Summary for the output file */
+
+                       file_size = cm_get_file_size(gbl_output_file);
+                       fprintf(stderr,
+                               "Output file %s contains 0x%X (%u) bytes\n\n",
+                               gbl_output_filename, file_size, file_size);
+               }
+
+               fclose(gbl_output_file);
+       }
+
+       return (status);
+}
diff --git a/tools/power/acpi/tools/ec/Makefile b/tools/power/acpi/tools/ec/Makefile
new file mode 100644 (file)
index 0000000..b7b0b92
--- /dev/null
@@ -0,0 +1,22 @@
+ec_access: ec_access.o
+       $(ECHO) "  LD      " $@
+       $(QUIET) $(LD) $(CFLAGS) $(LDFLAGS) $< -o $@
+       $(QUIET) $(STRIPCMD) $@
+
+%.o: %.c
+       $(ECHO) "  CC      " $@
+       $(QUIET) $(CC) -c $(CFLAGS) -o $@ $<
+
+all: ec_access
+
+install:
+       $(INSTALL) -d $(DESTDIR)${sbindir}
+       $(INSTALL_PROGRAM) ec_access $(DESTDIR)${sbindir}
+
+uninstall:
+       - rm -f $(DESTDIR)${sbindir}/ec_access
+
+clean:
+       -rm -f $(OUTPUT)ec_access
+
+.PHONY: all install uninstall
diff --git a/tools/power/acpi/tools/ec/ec_access.c b/tools/power/acpi/tools/ec/ec_access.c
new file mode 100644 (file)
index 0000000..6b8aaed
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * ec_access.c
+ *
+ * Copyright (C) 2010 SUSE Linux Products GmbH
+ * Author:
+ *      Thomas Renninger <trenn@suse.de>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ */
+
+#include <fcntl.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#define EC_SPACE_SIZE 256
+#define SYSFS_PATH "/sys/kernel/debug/ec/ec0/io"
+
+/* TBD/Enhancements:
+   - Provide param for accessing different ECs (not supported by kernel yet)
+*/
+
+static int read_mode = -1;
+static int sleep_time;
+static int write_byte_offset = -1;
+static int read_byte_offset = -1;
+static uint8_t write_value = -1;
+
+void usage(char progname[], int exit_status)
+{
+       printf("Usage:\n");
+       printf("1) %s -r [-s sleep]\n", basename(progname));
+       printf("2) %s -b byte_offset\n", basename(progname));
+       printf("3) %s -w byte_offset -v value\n\n", basename(progname));
+
+       puts("\t-r [-s sleep]      : Dump EC registers");
+       puts("\t                     If sleep is given, sleep x seconds,");
+       puts("\t                     re-read EC registers and show changes");
+       puts("\t-b offset          : Read value at byte_offset (in hex)");
+       puts("\t-w offset -v value : Write value at byte_offset");
+       puts("\t-h                 : Print this help\n\n");
+       puts("Offsets and values are in hexadecimal number sytem.");
+       puts("The offset and value must be between 0 and 0xff.");
+       exit(exit_status);
+}
+
+void parse_opts(int argc, char *argv[])
+{
+       int c;
+
+       while ((c = getopt(argc, argv, "rs:b:w:v:h")) != -1) {
+
+               switch (c) {
+               case 'r':
+                       if (read_mode != -1)
+                               usage(argv[0], EXIT_FAILURE);
+                       read_mode = 1;
+                       break;
+               case 's':
+                       if (read_mode != -1 && read_mode != 1)
+                               usage(argv[0], EXIT_FAILURE);
+
+                       sleep_time = atoi(optarg);
+                       if (sleep_time <= 0) {
+                               sleep_time = 0;
+                               usage(argv[0], EXIT_FAILURE);
+                               printf("Bad sleep time: %s\n", optarg);
+                       }
+                       break;
+               case 'b':
+                       if (read_mode != -1)
+                               usage(argv[0], EXIT_FAILURE);
+                       read_mode = 1;
+                       read_byte_offset = strtoul(optarg, NULL, 16);
+                       break;
+               case 'w':
+                       if (read_mode != -1)
+                               usage(argv[0], EXIT_FAILURE);
+                       read_mode = 0;
+                       write_byte_offset = strtoul(optarg, NULL, 16);
+                       break;
+               case 'v':
+                       write_value = strtoul(optarg, NULL, 16);
+                       break;
+               case 'h':
+                       usage(argv[0], EXIT_SUCCESS);
+               default:
+                       fprintf(stderr, "Unknown option!\n");
+                       usage(argv[0], EXIT_FAILURE);
+               }
+       }
+       if (read_mode == 0) {
+               if (write_byte_offset < 0 ||
+                   write_byte_offset >= EC_SPACE_SIZE) {
+                       fprintf(stderr, "Wrong byte offset 0x%.2x, valid: "
+                               "[0-0x%.2x]\n",
+                               write_byte_offset, EC_SPACE_SIZE - 1);
+                       usage(argv[0], EXIT_FAILURE);
+               }
+               if (write_value < 0 ||
+                   write_value >= 255) {
+                       fprintf(stderr, "Wrong byte offset 0x%.2x, valid:"
+                               "[0-0xff]\n", write_byte_offset);
+                       usage(argv[0], EXIT_FAILURE);
+               }
+       }
+       if (read_mode == 1 && read_byte_offset != -1) {
+               if (read_byte_offset < -1 ||
+                   read_byte_offset >= EC_SPACE_SIZE) {
+                       fprintf(stderr, "Wrong byte offset 0x%.2x, valid: "
+                               "[0-0x%.2x]\n",
+                               read_byte_offset, EC_SPACE_SIZE - 1);
+                       usage(argv[0], EXIT_FAILURE);
+               }
+       }
+       /* Add additional parameter checks here */
+}
+
+void dump_ec(int fd)
+{
+       char buf[EC_SPACE_SIZE];
+       char buf2[EC_SPACE_SIZE];
+       int byte_off, bytes_read;
+
+       bytes_read = read(fd, buf, EC_SPACE_SIZE);
+
+       if (bytes_read == -1)
+               err(EXIT_FAILURE, "Could not read from %s\n", SYSFS_PATH);
+
+       if (bytes_read != EC_SPACE_SIZE)
+               fprintf(stderr, "Could only read %d bytes\n", bytes_read);
+
+       printf("     00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F");
+       for (byte_off = 0; byte_off < bytes_read; byte_off++) {
+               if ((byte_off % 16) == 0)
+                       printf("\n%.2X: ", byte_off);
+               printf(" %.2x ", (uint8_t)buf[byte_off]);
+       }
+       printf("\n");
+
+       if (!sleep_time)
+               return;
+
+       printf("\n");
+       lseek(fd, 0, SEEK_SET);
+       sleep(sleep_time);
+
+       bytes_read = read(fd, buf2, EC_SPACE_SIZE);
+
+       if (bytes_read == -1)
+               err(EXIT_FAILURE, "Could not read from %s\n", SYSFS_PATH);
+
+       if (bytes_read != EC_SPACE_SIZE)
+               fprintf(stderr, "Could only read %d bytes\n", bytes_read);
+
+       printf("     00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F");
+       for (byte_off = 0; byte_off < bytes_read; byte_off++) {
+               if ((byte_off % 16) == 0)
+                       printf("\n%.2X: ", byte_off);
+
+               if (buf[byte_off] == buf2[byte_off])
+                       printf(" %.2x ", (uint8_t)buf2[byte_off]);
+               else
+                       printf("*%.2x ", (uint8_t)buf2[byte_off]);
+       }
+       printf("\n");
+}
+
+void read_ec_val(int fd, int byte_offset)
+{
+       uint8_t buf;
+       int error;
+
+       error = lseek(fd, byte_offset, SEEK_SET);
+       if (error != byte_offset)
+               err(EXIT_FAILURE, "Cannot set offset to 0x%.2x", byte_offset);
+
+       error = read(fd, &buf, 1);
+       if (error != 1)
+               err(EXIT_FAILURE, "Could not read byte 0x%.2x from %s\n",
+                   byte_offset, SYSFS_PATH);
+       printf("0x%.2x\n", buf);
+       return;
+}
+
+void write_ec_val(int fd, int byte_offset, uint8_t value)
+{
+       int error;
+
+       error = lseek(fd, byte_offset, SEEK_SET);
+       if (error != byte_offset)
+               err(EXIT_FAILURE, "Cannot set offset to 0x%.2x", byte_offset);
+
+       error = write(fd, &value, 1);
+       if (error != 1)
+               err(EXIT_FAILURE, "Cannot write value 0x%.2x to offset 0x%.2x",
+                   value, byte_offset);
+}
+
+int main(int argc, char *argv[])
+{
+       int file_mode = O_RDONLY;
+       int fd;
+
+       parse_opts(argc, argv);
+
+       if (read_mode == 0)
+               file_mode = O_WRONLY;
+       else if (read_mode == 1)
+               file_mode = O_RDONLY;
+       else
+               usage(argv[0], EXIT_FAILURE);
+
+       fd = open(SYSFS_PATH, file_mode);
+       if (fd == -1)
+               err(EXIT_FAILURE, "%s", SYSFS_PATH);
+
+       if (read_mode)
+               if (read_byte_offset == -1)
+                       dump_ec(fd);
+               else if (read_byte_offset < 0 ||
+                        read_byte_offset >= EC_SPACE_SIZE)
+                       usage(argv[0], EXIT_FAILURE);
+               else
+                       read_ec_val(fd, read_byte_offset);
+       else
+               write_ec_val(fd, write_byte_offset, write_value);
+       close(fd);
+
+       exit(EXIT_SUCCESS);
+}
index cbfec92..2e2ba2e 100644 (file)
@@ -62,7 +62,7 @@ LIB_MAJ=                      0.0.0
 LIB_MIN=                       0
 
 PACKAGE =                      cpupower
-PACKAGE_BUGREPORT =            cpufreq@vger.kernel.org
+PACKAGE_BUGREPORT =            linux-pm@vger.kernel.org
 LANGUAGES =                    de fr it cs pt
 
 
@@ -274,6 +274,8 @@ install-man:
        $(INSTALL_DATA) -D man/cpupower.1 $(DESTDIR)${mandir}/man1/cpupower.1
        $(INSTALL_DATA) -D man/cpupower-frequency-set.1 $(DESTDIR)${mandir}/man1/cpupower-frequency-set.1
        $(INSTALL_DATA) -D man/cpupower-frequency-info.1 $(DESTDIR)${mandir}/man1/cpupower-frequency-info.1
+       $(INSTALL_DATA) -D man/cpupower-idle-set.1 $(DESTDIR)${mandir}/man1/cpupower-idle-set.1
+       $(INSTALL_DATA) -D man/cpupower-idle-info.1 $(DESTDIR)${mandir}/man1/cpupower-idle-info.1
        $(INSTALL_DATA) -D man/cpupower-set.1 $(DESTDIR)${mandir}/man1/cpupower-set.1
        $(INSTALL_DATA) -D man/cpupower-info.1 $(DESTDIR)${mandir}/man1/cpupower-info.1
        $(INSTALL_DATA) -D man/cpupower-monitor.1 $(DESTDIR)${mandir}/man1/cpupower-monitor.1
@@ -295,8 +297,12 @@ uninstall:
        - rm -f $(DESTDIR)${libdir}/libcpupower.*
        - rm -f $(DESTDIR)${includedir}/cpufreq.h
        - rm -f $(DESTDIR)${bindir}/utils/cpupower
-       - rm -f $(DESTDIR)${mandir}/man1/cpufreq-set.1
-       - rm -f $(DESTDIR)${mandir}/man1/cpufreq-info.1
+       - rm -f $(DESTDIR)${mandir}/man1/cpupower.1
+       - rm -f $(DESTDIR)${mandir}/man1/cpupower-frequency-set.1
+       - rm -f $(DESTDIR)${mandir}/man1/cpupower-frequency-info.1
+       - rm -f $(DESTDIR)${mandir}/man1/cpupower-set.1
+       - rm -f $(DESTDIR)${mandir}/man1/cpupower-info.1
+       - rm -f $(DESTDIR)${mandir}/man1/cpupower-monitor.1
        - for HLANG in $(LANGUAGES); do \
                rm -f $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpupower.mo; \
          done;
index fd9d4c0..1c68f47 100644 (file)
@@ -1,6 +1,4 @@
-The cpufrequtils package (homepage: 
-http://www.kernel.org/pub/linux/utils/kernel/cpufreq/cpufrequtils.html ) 
-consists of the following elements:
+The cpupower package consists of the following elements:
 
 requirements
 ------------
@@ -11,10 +9,10 @@ providing cpuid.h is needed.
 For both it's not explicitly checked for (yet).
 
 
-libcpufreq
+libcpupower
 ----------
 
-"libcpufreq" is a library which offers a unified access method for userspace
+"libcpupower" is a library which offers a unified access method for userspace
 tools and programs to the cpufreq core and drivers in the Linux kernel. This
 allows for code reduction in userspace tools, a clean implementation of
 the interaction to the cpufreq core, and support for both the sysfs and proc
@@ -28,22 +26,22 @@ make
 su
 make install
 
-should suffice on most systems. It builds default libcpufreq,
-cpufreq-set and cpufreq-info files and installs them in /usr/lib and
-/usr/bin, respectively. If you want to set up the paths differently and/or
-want to configure the package to your specific needs, you need to open
-"Makefile" with an editor of your choice and edit the block marked
-CONFIGURATION.
+should suffice on most systems. It builds libcpupower to put in
+/usr/lib; cpupower, cpufreq-bench_plot.sh to put in /usr/bin; and
+cpufreq-bench to put in /usr/sbin. If you want to set up the paths
+differently and/or want to configure the package to your specific
+needs, you need to open "Makefile" with an editor of your choice and
+edit the block marked CONFIGURATION.
 
 
 THANKS
 ------
 Many thanks to Mattia Dongili who wrote the autotoolization and
-libtoolization, the manpages and the italian language file for cpufrequtils;
+libtoolization, the manpages and the italian language file for cpupower;
 to Dave Jones for his feedback and his dump_psb tool; to Bruno Ducrot for his
 powernow-k8-decode and intel_gsic tools as well as the french language file;
 and to various others commenting on the previous (pre-)releases of 
-cpufrequtils.
+cpupower.
 
 
         Dominik Brodowski
index 874b78b..6e8b89f 100644 (file)
@@ -3,7 +3,6 @@ ToDos sorted by priority:
 - Use bitmask functions to parse CPU topology more robust
   (current implementation has issues on AMD)
 - Try to read out boost states and frequencies on Intel
-- Adjust README
 - Somewhere saw the ability to read power consumption of
   RAM from HW on Intel SandyBridge -> another monitor?
 - Add another c1e debug idle monitor
index 0f10b81..5224ee5 100644 (file)
@@ -18,7 +18,7 @@
  * 5.) if the third value, "diff_pmtmr", changes between 2. and 4., the
  *     TSC-based delay routine on the Linux kernel does not correctly
  *     handle the cpufreq transition. Please report this to
- *     cpufreq@vger.kernel.org
+ *     linux-pm@vger.kernel.org
  */
 
 #include <linux/kernel.h>
index 4a1918e..9c85a38 100644 (file)
@@ -50,6 +50,9 @@ Prints out information like provided by the /proc/cpufreq interface in 2.4. and
 \fB\-m\fR \fB\-\-human\fR
 human\-readable output for the \-f, \-w, \-s and \-y parameters.
 .TP  
+\fB\-n\fR \fB\-\-no-rounding\fR
+Output frequencies and latencies without rounding off values.
+.TP  
 .SH "REMARKS"
 .LP 
 By default only values of core zero are displayed. How to display settings of
index 6b16072..3e6799d 100644 (file)
@@ -13,11 +13,17 @@ sleep states. This can be handy for power vs performance tuning.
 .SH "OPTIONS"
 .LP
 .TP
-\fB\-d\fR \fB\-\-disable\fR
+\fB\-d\fR \fB\-\-disable\fR <STATE_NO>
 Disable a specific processor sleep state.
 .TP
-\fB\-e\fR \fB\-\-enable\fR
+\fB\-e\fR \fB\-\-enable\fR <STATE_NO>
 Enable a specific processor sleep state.
+.TP
+\fB\-D\fR \fB\-\-disable-by-latency\fR <LATENCY>
+Disable all idle states with a equal or higher latency than <LATENCY>
+.TP
+\fB\-E\fR \fB\-\-enable-all\fR
+Enable all idle states if not enabled already.
 
 .SH "REMARKS"
 .LP
index 58e2119..340bcd0 100644 (file)
@@ -3,7 +3,7 @@
 cpupower\-info \- Shows processor power related kernel or hardware configurations
 .SH SYNOPSIS
 .ft B
-.B cpupower info [ \-b ] [ \-s ] [ \-m ]
+.B cpupower info [ \-b ]
 
 .SH DESCRIPTION
 \fBcpupower info \fP shows kernel configurations or processor hardware
index 9dbd536..2bcc696 100644 (file)
@@ -3,7 +3,7 @@
 cpupower\-set \- Set processor power related kernel or hardware configurations
 .SH SYNOPSIS
 .ft B
-.B cpupower set [ \-b VAL ] [ \-s VAL ] [ \-m VAL ]
+.B cpupower set [ \-b VAL ]
 
 
 .SH DESCRIPTION
@@ -55,35 +55,6 @@ Use \fBcpupower -c all info -b\fP to verify.
 
 This options needs the msr kernel driver (CONFIG_X86_MSR) loaded.
 .RE
-.PP
-\-\-sched\-mc,  \-m [ VAL ]
-.RE
-\-\-sched\-smt, \-s [ VAL ]
-.RS 4
-\-\-sched\-mc utilizes cores in one processor package/socket first before
-processes are scheduled to other processor packages/sockets.
-
-\-\-sched\-smt utilizes thread siblings of one processor core first before
-processes are scheduled to other cores.
-
-The impact on power consumption and performance (positiv or negativ) heavily
-depends on processor support for deep sleep states, frequency scaling and
-frequency boost modes and their dependencies between other thread siblings
-and processor cores.
-
-Taken over from kernel documentation:
-
-Adjust the kernel's multi-core scheduler support.
-
-Possible values are:
-.RS 2
-0 - No power saving load balance (default value)
-
-1 - Fill one thread/core/package first for long running threads
-
-2 - Also bias task wakeups to semi-idle cpu package for power
-savings
-.RE
 
 .SH "SEE ALSO"
 cpupower-info(1), cpupower-monitor(1), powertop(1)
index 28953c9..b4b90a9 100644 (file)
@@ -82,29 +82,42 @@ static void proc_cpufreq_output(void)
        }
 }
 
+static int no_rounding;
 static void print_speed(unsigned long speed)
 {
        unsigned long tmp;
 
-       if (speed > 1000000) {
-               tmp = speed % 10000;
-               if (tmp >= 5000)
-                       speed += 10000;
-               printf("%u.%02u GHz", ((unsigned int) speed/1000000),
-                       ((unsigned int) (speed%1000000)/10000));
-       } else if (speed > 100000) {
-               tmp = speed % 1000;
-               if (tmp >= 500)
-                       speed += 1000;
-               printf("%u MHz", ((unsigned int) speed / 1000));
-       } else if (speed > 1000) {
-               tmp = speed % 100;
-               if (tmp >= 50)
-                       speed += 100;
-               printf("%u.%01u MHz", ((unsigned int) speed/1000),
-                       ((unsigned int) (speed%1000)/100));
-       } else
-               printf("%lu kHz", speed);
+       if (no_rounding) {
+               if (speed > 1000000)
+                       printf("%u.%06u GHz", ((unsigned int) speed/1000000),
+                               ((unsigned int) speed%1000000));
+               else if (speed > 100000)
+                       printf("%u MHz", (unsigned int) speed);
+               else if (speed > 1000)
+                       printf("%u.%03u MHz", ((unsigned int) speed/1000),
+                               (unsigned int) (speed%1000));
+               else
+                       printf("%lu kHz", speed);
+       } else {
+               if (speed > 1000000) {
+                       tmp = speed%10000;
+                       if (tmp >= 5000)
+                               speed += 10000;
+                       printf("%u.%02u GHz", ((unsigned int) speed/1000000),
+                               ((unsigned int) (speed%1000000)/10000));
+               } else if (speed > 100000) {
+                       tmp = speed%1000;
+                       if (tmp >= 500)
+                               speed += 1000;
+                       printf("%u MHz", ((unsigned int) speed/1000));
+               } else if (speed > 1000) {
+                       tmp = speed%100;
+                       if (tmp >= 50)
+                               speed += 100;
+                       printf("%u.%01u MHz", ((unsigned int) speed/1000),
+                               ((unsigned int) (speed%1000)/100));
+               }
+       }
 
        return;
 }
@@ -113,26 +126,38 @@ static void print_duration(unsigned long duration)
 {
        unsigned long tmp;
 
-       if (duration > 1000000) {
-               tmp = duration % 10000;
-               if (tmp >= 5000)
-                       duration += 10000;
-               printf("%u.%02u ms", ((unsigned int) duration/1000000),
-                       ((unsigned int) (duration%1000000)/10000));
-       } else if (duration > 100000) {
-               tmp = duration % 1000;
-               if (tmp >= 500)
-                       duration += 1000;
-               printf("%u us", ((unsigned int) duration / 1000));
-       } else if (duration > 1000) {
-               tmp = duration % 100;
-               if (tmp >= 50)
-                       duration += 100;
-               printf("%u.%01u us", ((unsigned int) duration/1000),
-                       ((unsigned int) (duration%1000)/100));
-       } else
-               printf("%lu ns", duration);
-
+       if (no_rounding) {
+               if (duration > 1000000)
+                       printf("%u.%06u ms", ((unsigned int) duration/1000000),
+                               ((unsigned int) duration%1000000));
+               else if (duration > 100000)
+                       printf("%u us", ((unsigned int) duration/1000));
+               else if (duration > 1000)
+                       printf("%u.%03u us", ((unsigned int) duration/1000),
+                               ((unsigned int) duration%1000));
+               else
+                       printf("%lu ns", duration);
+       } else {
+               if (duration > 1000000) {
+                       tmp = duration%10000;
+                       if (tmp >= 5000)
+                               duration += 10000;
+                       printf("%u.%02u ms", ((unsigned int) duration/1000000),
+                               ((unsigned int) (duration%1000000)/10000));
+               } else if (duration > 100000) {
+                       tmp = duration%1000;
+                       if (tmp >= 500)
+                               duration += 1000;
+                       printf("%u us", ((unsigned int) duration / 1000));
+               } else if (duration > 1000) {
+                       tmp = duration%100;
+                       if (tmp >= 50)
+                               duration += 100;
+                       printf("%u.%01u us", ((unsigned int) duration/1000),
+                               ((unsigned int) (duration%1000)/100));
+               } else
+                       printf("%lu ns", duration);
+       }
        return;
 }
 
@@ -525,6 +550,7 @@ static struct option info_opts[] = {
        { .name = "latency",    .has_arg = no_argument,         .flag = NULL,   .val = 'y'},
        { .name = "proc",       .has_arg = no_argument,         .flag = NULL,   .val = 'o'},
        { .name = "human",      .has_arg = no_argument,         .flag = NULL,   .val = 'm'},
+       { .name = "no-rounding", .has_arg = no_argument,        .flag = NULL,   .val = 'n'},
        { },
 };
 
@@ -538,7 +564,8 @@ int cmd_freq_info(int argc, char **argv)
        int output_param = 0;
 
        do {
-               ret = getopt_long(argc, argv, "oefwldpgrasmyb", info_opts, NULL);
+               ret = getopt_long(argc, argv, "oefwldpgrasmybn", info_opts,
+                                 NULL);
                switch (ret) {
                case '?':
                        output_param = '?';
@@ -575,6 +602,9 @@ int cmd_freq_info(int argc, char **argv)
                        }
                        human = 1;
                        break;
+               case 'n':
+                       no_rounding = 1;
+                       break;
                default:
                        fprintf(stderr, "invalid or unknown argument\n");
                        return EXIT_FAILURE;
index c78141c..d45d8d7 100644 (file)
 #include "helpers/sysfs.h"
 
 static struct option info_opts[] = {
-       { .name = "disable",    .has_arg = required_argument,   .flag = NULL,   .val = 'd'},
-       { .name = "enable",     .has_arg = required_argument,   .flag = NULL,   .val = 'e'},
+       { .name = "disable",
+         .has_arg = required_argument, .flag = NULL,   .val = 'd'},
+       { .name = "enable",
+         .has_arg = required_argument, .flag = NULL,   .val = 'e'},
+       { .name = "disable-by-latency",
+         .has_arg = required_argument, .flag = NULL,   .val = 'D'},
+       { .name = "enable-all",
+         .has_arg = no_argument,       .flag = NULL,   .val = 'E'},
        { },
 };
 
@@ -23,11 +29,13 @@ int cmd_idle_set(int argc, char **argv)
 {
        extern char *optarg;
        extern int optind, opterr, optopt;
-       int ret = 0, cont = 1, param = 0, idlestate = 0;
-       unsigned int cpu = 0;
+       int ret = 0, cont = 1, param = 0, disabled;
+       unsigned long long latency = 0, state_latency;
+       unsigned int cpu = 0, idlestate = 0, idlestates = 0;
+       char *endptr;
 
        do {
-               ret = getopt_long(argc, argv, "d:e:", info_opts, NULL);
+               ret = getopt_long(argc, argv, "d:e:ED:", info_opts, NULL);
                if (ret == -1)
                        break;
                switch (ret) {
@@ -53,6 +61,27 @@ int cmd_idle_set(int argc, char **argv)
                        param = ret;
                        idlestate = atoi(optarg);
                        break;
+               case 'D':
+                       if (param) {
+                               param = -1;
+                               cont = 0;
+                               break;
+                       }
+                       param = ret;
+                       latency = strtoull(optarg, &endptr, 10);
+                       if (*endptr != '\0') {
+                               printf(_("Bad latency value: %s\n"), optarg);
+                               exit(EXIT_FAILURE);
+                       }
+                       break;
+               case 'E':
+                       if (param) {
+                               param = -1;
+                               cont = 0;
+                               break;
+                       }
+                       param = ret;
+                       break;
                case -1:
                        cont = 0;
                        break;
@@ -79,8 +108,14 @@ int cmd_idle_set(int argc, char **argv)
                if (!bitmask_isbitset(cpus_chosen, cpu))
                        continue;
 
-               switch (param) {
+               if (sysfs_is_cpu_online(cpu) != 1)
+                       continue;
+
+               idlestates = sysfs_get_idlestate_count(cpu);
+               if (idlestates <= 0)
+                       continue;
 
+               switch (param) {
                case 'd':
                        ret = sysfs_idlestate_disable(cpu, idlestate, 1);
                        if (ret == 0)
@@ -107,6 +142,34 @@ int cmd_idle_set(int argc, char **argv)
                printf(_("Idlestate %u not enabled on CPU %u\n"),
                       idlestate, cpu);
                        break;
+               case 'D':
+                       for (idlestate = 0; idlestate < idlestates; idlestate++) {
+                               disabled = sysfs_is_idlestate_disabled
+                                       (cpu, idlestate);
+                               state_latency = sysfs_get_idlestate_latency
+                                       (cpu, idlestate);
+                               printf("CPU: %u - idlestate %u - state_latency: %llu - latency: %llu\n",
+                                      cpu, idlestate, state_latency, latency);
+                               if (disabled == 1 || latency > state_latency)
+                                       continue;
+                               ret = sysfs_idlestate_disable
+                                       (cpu, idlestate, 1);
+                               if (ret == 0)
+               printf(_("Idlestate %u disabled on CPU %u\n"), idlestate, cpu);
+                       }
+                       break;
+               case 'E':
+                       for (idlestate = 0; idlestate < idlestates; idlestate++) {
+                               disabled = sysfs_is_idlestate_disabled
+                                       (cpu, idlestate);
+                               if (disabled == 1) {
+                                       ret = sysfs_idlestate_disable
+                                               (cpu, idlestate, 0);
+                                       if (ret == 0)
+               printf(_("Idlestate %u enabled on CPU %u\n"), idlestate, cpu);
+                               }
+                       }
+                       break;
                default:
                        /* Not reachable with proper args checking */
                        printf(_("Invalid or unknown argument\n"));
index 3f68632..136d979 100644 (file)
@@ -18,8 +18,6 @@
 
 static struct option set_opts[] = {
        { .name = "perf-bias",  .has_arg = optional_argument,   .flag = NULL,   .val = 'b'},
-       { .name = "sched-mc",   .has_arg = optional_argument,   .flag = NULL,   .val = 'm'},
-       { .name = "sched-smt",  .has_arg = optional_argument,   .flag = NULL,   .val = 's'},
        { },
 };
 
@@ -37,8 +35,6 @@ int cmd_info(int argc, char **argv)
 
        union {
                struct {
-                       int sched_mc:1;
-                       int sched_smt:1;
                        int perf_bias:1;
                };
                int params;
@@ -49,23 +45,13 @@ int cmd_info(int argc, char **argv)
        textdomain(PACKAGE);
 
        /* parameter parsing */
-       while ((ret = getopt_long(argc, argv, "msb", set_opts, NULL)) != -1) {
+       while ((ret = getopt_long(argc, argv, "b", set_opts, NULL)) != -1) {
                switch (ret) {
                case 'b':
                        if (params.perf_bias)
                                print_wrong_arg_exit();
                        params.perf_bias = 1;
                        break;
-               case 'm':
-                       if (params.sched_mc)
-                               print_wrong_arg_exit();
-                       params.sched_mc = 1;
-                       break;
-               case 's':
-                       if (params.sched_smt)
-                               print_wrong_arg_exit();
-                       params.sched_smt = 1;
-                       break;
                default:
                        print_wrong_arg_exit();
                }
@@ -78,25 +64,6 @@ int cmd_info(int argc, char **argv)
        if (bitmask_isallclear(cpus_chosen))
                bitmask_setbit(cpus_chosen, 0);
 
-       if (params.sched_mc) {
-               ret = sysfs_get_sched("mc");
-               printf(_("System's multi core scheduler setting: "));
-               if (ret < 0)
-                       /* if sysfs file is missing it's: errno == ENOENT */
-                       printf(_("not supported\n"));
-               else
-                       printf("%d\n", ret);
-       }
-       if (params.sched_smt) {
-               ret = sysfs_get_sched("smt");
-               printf(_("System's thread sibling scheduler setting: "));
-               if (ret < 0)
-                       /* if sysfs file is missing it's: errno == ENOENT */
-                       printf(_("not supported\n"));
-               else
-                       printf("%d\n", ret);
-       }
-
        /* Add more per cpu options here */
        if (!params.perf_bias)
                return ret;
@@ -125,11 +92,12 @@ int cmd_info(int argc, char **argv)
                if (params.perf_bias) {
                        ret = msr_intel_get_perf_bias(cpu);
                        if (ret < 0) {
-                               printf(_("Could not read perf-bias value\n"));
-                               break;
+                               fprintf(stderr,
+                       _("Could not read perf-bias value[%d]\n"), ret);
+                               exit(EXIT_FAILURE);
                        } else
                                printf(_("perf-bias: %d\n"), ret);
                }
        }
-       return ret;
+       return 0;
 }
index bcf1d2f..573c75f 100644 (file)
@@ -19,8 +19,6 @@
 
 static struct option set_opts[] = {
        { .name = "perf-bias",  .has_arg = required_argument,   .flag = NULL,   .val = 'b'},
-       { .name = "sched-mc",   .has_arg = required_argument,   .flag = NULL,   .val = 'm'},
-       { .name = "sched-smt",  .has_arg = required_argument,   .flag = NULL,   .val = 's'},
        { },
 };
 
@@ -38,13 +36,11 @@ int cmd_set(int argc, char **argv)
 
        union {
                struct {
-                       int sched_mc:1;
-                       int sched_smt:1;
                        int perf_bias:1;
                };
                int params;
        } params;
-       int sched_mc = 0, sched_smt = 0, perf_bias = 0;
+       int perf_bias = 0;
        int ret = 0;
 
        setlocale(LC_ALL, "");
@@ -52,7 +48,7 @@ int cmd_set(int argc, char **argv)
 
        params.params = 0;
        /* parameter parsing */
-       while ((ret = getopt_long(argc, argv, "m:s:b:",
+       while ((ret = getopt_long(argc, argv, "b:",
                                                set_opts, NULL)) != -1) {
                switch (ret) {
                case 'b':
@@ -66,28 +62,6 @@ int cmd_set(int argc, char **argv)
                        }
                        params.perf_bias = 1;
                        break;
-               case 'm':
-                       if (params.sched_mc)
-                               print_wrong_arg_exit();
-                       sched_mc = atoi(optarg);
-                       if (sched_mc < 0 || sched_mc > 2) {
-                               printf(_("--sched-mc param out "
-                                        "of range [0-%d]\n"), 2);
-                               print_wrong_arg_exit();
-                       }
-                       params.sched_mc = 1;
-                       break;
-               case 's':
-                       if (params.sched_smt)
-                               print_wrong_arg_exit();
-                       sched_smt = atoi(optarg);
-                       if (sched_smt < 0 || sched_smt > 2) {
-                               printf(_("--sched-smt param out "
-                                        "of range [0-%d]\n"), 2);
-                               print_wrong_arg_exit();
-                       }
-                       params.sched_smt = 1;
-                       break;
                default:
                        print_wrong_arg_exit();
                }
@@ -96,19 +70,6 @@ int cmd_set(int argc, char **argv)
        if (!params.params)
                print_wrong_arg_exit();
 
-       if (params.sched_mc) {
-               ret = sysfs_set_sched("mc", sched_mc);
-               if (ret)
-                       fprintf(stderr, _("Error setting sched-mc %s\n"),
-                               (ret == -ENODEV) ? "not supported" : "");
-       }
-       if (params.sched_smt) {
-               ret = sysfs_set_sched("smt", sched_smt);
-               if (ret)
-                       fprintf(stderr, _("Error setting sched-smt %s\n"),
-                               (ret == -ENODEV) ? "not supported" : "");
-       }
-
        /* Default is: set all CPUs */
        if (bitmask_isallclear(cpus_chosen))
                bitmask_setall(cpus_chosen);
index 7efc570..7cdcf88 100644 (file)
@@ -12,6 +12,9 @@
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
 
 #include "builtin.h"
 #include "helpers/helpers.h"
@@ -169,6 +172,8 @@ int main(int argc, const char *argv[])
 {
        const char *cmd;
        unsigned int i, ret;
+       struct stat statbuf;
+       struct utsname uts;
 
        cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
 
@@ -195,6 +200,15 @@ int main(int argc, const char *argv[])
 
        get_cpu_info(0, &cpupower_cpu_info);
        run_as_root = !getuid();
+       if (run_as_root) {
+               ret = uname(&uts);
+               if (!ret && !strcmp(uts.machine, "x86_64") &&
+                   stat("/dev/cpu/0/msr", &statbuf) != 0) {
+                       if (system("modprobe msr") == -1)
+       fprintf(stderr, _("MSR access not available.\n"));
+               }
+       }
+               
 
        for (i = 0; i < ARRAY_SIZE(commands); i++) {
                struct cmd_struct *p = commands + i;
index 7c9d8e7..d0396af 100644 (file)
@@ -1971,13 +1971,13 @@ int set_temperature_target(struct thread_data *t, struct core_data *c, struct pk
        if (get_msr(0, MSR_IA32_TEMPERATURE_TARGET, &msr))
                goto guess;
 
-       target_c_local = (msr >> 16) & 0x7F;
+       target_c_local = (msr >> 16) & 0xFF;
 
        if (verbose)
                fprintf(stderr, "cpu%d: MSR_IA32_TEMPERATURE_TARGET: 0x%08llx (%d C)\n",
                        cpu, msr, target_c_local);
 
-       if (target_c_local < 85 || target_c_local > 127)
+       if (!target_c_local)
                goto guess;
 
        tcc_activation_temp = target_c_local;
index 06e6401..d6a3d09 100644 (file)
@@ -80,12 +80,10 @@ static void async_pf_execute(struct work_struct *work)
 
        might_sleep();
 
-       use_mm(mm);
        down_read(&mm->mmap_sem);
-       get_user_pages(current, mm, addr, 1, 1, 0, NULL, NULL);
+       get_user_pages(NULL, mm, addr, 1, 1, 0, NULL, NULL);
        up_read(&mm->mmap_sem);
        kvm_async_page_present_sync(vcpu, apf);
-       unuse_mm(mm);
 
        spin_lock(&vcpu->async_pf.lock);
        list_add_tail(&apf->link, &vcpu->async_pf.done);
index 29c2a04..20c3af7 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/list.h>
 #include <linux/eventfd.h>
 #include <linux/kernel.h>
+#include <linux/srcu.h>
 #include <linux/slab.h>
 
 #include "iodev.h"
@@ -118,19 +119,22 @@ static void
 irqfd_resampler_ack(struct kvm_irq_ack_notifier *kian)
 {
        struct _irqfd_resampler *resampler;
+       struct kvm *kvm;
        struct _irqfd *irqfd;
+       int idx;
 
        resampler = container_of(kian, struct _irqfd_resampler, notifier);
+       kvm = resampler->kvm;
 
-       kvm_set_irq(resampler->kvm, KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID,
+       kvm_set_irq(kvm, KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID,
                    resampler->notifier.gsi, 0, false);
 
-       rcu_read_lock();
+       idx = srcu_read_lock(&kvm->irq_srcu);
 
        list_for_each_entry_rcu(irqfd, &resampler->list, resampler_link)
                eventfd_signal(irqfd->resamplefd, 1);
 
-       rcu_read_unlock();
+       srcu_read_unlock(&kvm->irq_srcu, idx);
 }
 
 static void
@@ -142,7 +146,7 @@ irqfd_resampler_shutdown(struct _irqfd *irqfd)
        mutex_lock(&kvm->irqfds.resampler_lock);
 
        list_del_rcu(&irqfd->resampler_link);
-       synchronize_rcu();
+       synchronize_srcu(&kvm->irq_srcu);
 
        if (list_empty(&resampler->list)) {
                list_del(&resampler->link);
@@ -221,17 +225,18 @@ irqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key)
        unsigned long flags = (unsigned long)key;
        struct kvm_kernel_irq_routing_entry *irq;
        struct kvm *kvm = irqfd->kvm;
+       int idx;
 
        if (flags & POLLIN) {
-               rcu_read_lock();
-               irq = rcu_dereference(irqfd->irq_entry);
+               idx = srcu_read_lock(&kvm->irq_srcu);
+               irq = srcu_dereference(irqfd->irq_entry, &kvm->irq_srcu);
                /* An event has been signaled, inject an interrupt */
                if (irq)
                        kvm_set_msi(irq, kvm, KVM_USERSPACE_IRQ_SOURCE_ID, 1,
                                        false);
                else
                        schedule_work(&irqfd->inject);
-               rcu_read_unlock();
+               srcu_read_unlock(&kvm->irq_srcu, idx);
        }
 
        if (flags & POLLHUP) {
@@ -363,7 +368,7 @@ kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)
                }
 
                list_add_rcu(&irqfd->resampler_link, &irqfd->resampler->list);
-               synchronize_rcu();
+               synchronize_srcu(&kvm->irq_srcu);
 
                mutex_unlock(&kvm->irqfds.resampler_lock);
        }
@@ -465,7 +470,7 @@ kvm_irqfd_deassign(struct kvm *kvm, struct kvm_irqfd *args)
                         * another thread calls kvm_irq_routing_update before
                         * we flush workqueue below (we synchronize with
                         * kvm_irq_routing_update using irqfds.lock).
-                        * It is paired with synchronize_rcu done by caller
+                        * It is paired with synchronize_srcu done by caller
                         * of that function.
                         */
                        rcu_assign_pointer(irqfd->irq_entry, NULL);
@@ -524,7 +529,7 @@ kvm_irqfd_release(struct kvm *kvm)
 
 /*
  * Change irq_routing and irqfd.
- * Caller must invoke synchronize_rcu afterwards.
+ * Caller must invoke synchronize_srcu(&kvm->irq_srcu) afterwards.
  */
 void kvm_irq_routing_update(struct kvm *kvm,
                            struct kvm_irq_routing_table *irq_rt)
@@ -600,7 +605,15 @@ ioeventfd_in_range(struct _ioeventfd *p, gpa_t addr, int len, const void *val)
 {
        u64 _val;
 
-       if (!(addr == p->addr && len == p->length))
+       if (addr != p->addr)
+               /* address must be precise for a hit */
+               return false;
+
+       if (!p->length)
+               /* length = 0 means only look at the address, so always a hit */
+               return true;
+
+       if (len != p->length)
                /* address-range must be precise for a hit */
                return false;
 
@@ -671,9 +684,11 @@ ioeventfd_check_collision(struct kvm *kvm, struct _ioeventfd *p)
 
        list_for_each_entry(_p, &kvm->ioeventfds, list)
                if (_p->bus_idx == p->bus_idx &&
-                   _p->addr == p->addr && _p->length == p->length &&
-                   (_p->wildcard || p->wildcard ||
-                    _p->datamatch == p->datamatch))
+                   _p->addr == p->addr &&
+                   (!_p->length || !p->length ||
+                    (_p->length == p->length &&
+                     (_p->wildcard || p->wildcard ||
+                      _p->datamatch == p->datamatch))))
                        return true;
 
        return false;
@@ -697,8 +712,9 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
        int                       ret;
 
        bus_idx = ioeventfd_bus_from_flags(args->flags);
-       /* must be natural-word sized */
+       /* must be natural-word sized, or 0 to ignore length */
        switch (args->len) {
+       case 0:
        case 1:
        case 2:
        case 4:
@@ -716,6 +732,12 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
        if (args->flags & ~KVM_IOEVENTFD_VALID_FLAG_MASK)
                return -EINVAL;
 
+       /* ioeventfd with no length can't be combined with DATAMATCH */
+       if (!args->len &&
+           args->flags & (KVM_IOEVENTFD_FLAG_PIO |
+                          KVM_IOEVENTFD_FLAG_DATAMATCH))
+               return -EINVAL;
+
        eventfd = eventfd_ctx_fdget(args->fd);
        if (IS_ERR(eventfd))
                return PTR_ERR(eventfd);
@@ -753,6 +775,16 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
        if (ret < 0)
                goto unlock_fail;
 
+       /* When length is ignored, MMIO is also put on a separate bus, for
+        * faster lookups.
+        */
+       if (!args->len && !(args->flags & KVM_IOEVENTFD_FLAG_PIO)) {
+               ret = kvm_io_bus_register_dev(kvm, KVM_FAST_MMIO_BUS,
+                                             p->addr, 0, &p->dev);
+               if (ret < 0)
+                       goto register_fail;
+       }
+
        kvm->buses[bus_idx]->ioeventfd_count++;
        list_add_tail(&p->list, &kvm->ioeventfds);
 
@@ -760,6 +792,8 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
 
        return 0;
 
+register_fail:
+       kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev);
 unlock_fail:
        mutex_unlock(&kvm->slots_lock);
 
@@ -799,6 +833,10 @@ kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
                        continue;
 
                kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev);
+               if (!p->length) {
+                       kvm_io_bus_unregister_dev(kvm, KVM_FAST_MMIO_BUS,
+                                                 &p->dev);
+               }
                kvm->buses[bus_idx]->ioeventfd_count--;
                ioeventfd_release(p);
                ret = 0;
index e2e6b44..ced4a54 100644 (file)
@@ -163,6 +163,7 @@ int kvm_set_irq_inatomic(struct kvm *kvm, int irq_source_id, u32 irq, int level)
        struct kvm_kernel_irq_routing_entry *e;
        int ret = -EINVAL;
        struct kvm_irq_routing_table *irq_rt;
+       int idx;
 
        trace_kvm_set_irq(irq, level, irq_source_id);
 
@@ -174,8 +175,8 @@ int kvm_set_irq_inatomic(struct kvm *kvm, int irq_source_id, u32 irq, int level)
         * Since there's no easy way to do this, we only support injecting MSI
         * which is limited to 1:1 GSI mapping.
         */
-       rcu_read_lock();
-       irq_rt = rcu_dereference(kvm->irq_routing);
+       idx = srcu_read_lock(&kvm->irq_srcu);
+       irq_rt = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu);
        if (irq < irq_rt->nr_rt_entries)
                hlist_for_each_entry(e, &irq_rt->map[irq], link) {
                        if (likely(e->type == KVM_IRQ_ROUTING_MSI))
@@ -184,7 +185,7 @@ int kvm_set_irq_inatomic(struct kvm *kvm, int irq_source_id, u32 irq, int level)
                                ret = -EWOULDBLOCK;
                        break;
                }
-       rcu_read_unlock();
+       srcu_read_unlock(&kvm->irq_srcu, idx);
        return ret;
 }
 
@@ -253,22 +254,22 @@ void kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq,
        mutex_lock(&kvm->irq_lock);
        hlist_del_rcu(&kimn->link);
        mutex_unlock(&kvm->irq_lock);
-       synchronize_rcu();
+       synchronize_srcu(&kvm->irq_srcu);
 }
 
 void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin,
                             bool mask)
 {
        struct kvm_irq_mask_notifier *kimn;
-       int gsi;
+       int idx, gsi;
 
-       rcu_read_lock();
-       gsi = rcu_dereference(kvm->irq_routing)->chip[irqchip][pin];
+       idx = srcu_read_lock(&kvm->irq_srcu);
+       gsi = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu)->chip[irqchip][pin];
        if (gsi != -1)
                hlist_for_each_entry_rcu(kimn, &kvm->mask_notifier_list, link)
                        if (kimn->irq == gsi)
                                kimn->func(kimn, mask);
-       rcu_read_unlock();
+       srcu_read_unlock(&kvm->irq_srcu, idx);
 }
 
 int kvm_set_routing_entry(struct kvm_irq_routing_table *rt,
index 20dc9e4..b43c275 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <linux/kvm_host.h>
 #include <linux/slab.h>
+#include <linux/srcu.h>
 #include <linux/export.h>
 #include <trace/events/kvm.h>
 #include "irq.h"
 bool kvm_irq_has_notifier(struct kvm *kvm, unsigned irqchip, unsigned pin)
 {
        struct kvm_irq_ack_notifier *kian;
-       int gsi;
+       int gsi, idx;
 
-       rcu_read_lock();
-       gsi = rcu_dereference(kvm->irq_routing)->chip[irqchip][pin];
+       idx = srcu_read_lock(&kvm->irq_srcu);
+       gsi = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu)->chip[irqchip][pin];
        if (gsi != -1)
                hlist_for_each_entry_rcu(kian, &kvm->irq_ack_notifier_list,
                                         link)
                        if (kian->gsi == gsi) {
-                               rcu_read_unlock();
+                               srcu_read_unlock(&kvm->irq_srcu, idx);
                                return true;
                        }
 
-       rcu_read_unlock();
+       srcu_read_unlock(&kvm->irq_srcu, idx);
 
        return false;
 }
@@ -54,18 +55,18 @@ EXPORT_SYMBOL_GPL(kvm_irq_has_notifier);
 void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin)
 {
        struct kvm_irq_ack_notifier *kian;
-       int gsi;
+       int gsi, idx;
 
        trace_kvm_ack_irq(irqchip, pin);
 
-       rcu_read_lock();
-       gsi = rcu_dereference(kvm->irq_routing)->chip[irqchip][pin];
+       idx = srcu_read_lock(&kvm->irq_srcu);
+       gsi = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu)->chip[irqchip][pin];
        if (gsi != -1)
                hlist_for_each_entry_rcu(kian, &kvm->irq_ack_notifier_list,
                                         link)
                        if (kian->gsi == gsi)
                                kian->irq_acked(kian);
-       rcu_read_unlock();
+       srcu_read_unlock(&kvm->irq_srcu, idx);
 }
 
 void kvm_register_irq_ack_notifier(struct kvm *kvm,
@@ -85,7 +86,7 @@ void kvm_unregister_irq_ack_notifier(struct kvm *kvm,
        mutex_lock(&kvm->irq_lock);
        hlist_del_init_rcu(&kian->link);
        mutex_unlock(&kvm->irq_lock);
-       synchronize_rcu();
+       synchronize_srcu(&kvm->irq_srcu);
 #ifdef __KVM_HAVE_IOAPIC
        kvm_vcpu_request_scan_ioapic(kvm);
 #endif
@@ -115,7 +116,7 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level,
                bool line_status)
 {
        struct kvm_kernel_irq_routing_entry *e, irq_set[KVM_NR_IRQCHIPS];
-       int ret = -1, i = 0;
+       int ret = -1, i = 0, idx;
        struct kvm_irq_routing_table *irq_rt;
 
        trace_kvm_set_irq(irq, level, irq_source_id);
@@ -124,12 +125,12 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level,
         * IOAPIC.  So set the bit in both. The guest will ignore
         * writes to the unused one.
         */
-       rcu_read_lock();
-       irq_rt = rcu_dereference(kvm->irq_routing);
+       idx = srcu_read_lock(&kvm->irq_srcu);
+       irq_rt = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu);
        if (irq < irq_rt->nr_rt_entries)
                hlist_for_each_entry(e, &irq_rt->map[irq], link)
                        irq_set[i++] = *e;
-       rcu_read_unlock();
+       srcu_read_unlock(&kvm->irq_srcu, idx);
 
        while(i--) {
                int r;
@@ -226,7 +227,7 @@ int kvm_set_irq_routing(struct kvm *kvm,
        kvm_irq_routing_update(kvm, new);
        mutex_unlock(&kvm->irq_lock);
 
-       synchronize_rcu();
+       synchronize_srcu_expedited(&kvm->irq_srcu);
 
        new = old;
        r = 0;
index 56baae8..c86be0f 100644 (file)
@@ -186,9 +186,12 @@ static bool make_all_cpus_request(struct kvm *kvm, unsigned int req)
 
 void kvm_flush_remote_tlbs(struct kvm *kvm)
 {
+       long dirty_count = kvm->tlbs_dirty;
+
+       smp_mb();
        if (make_all_cpus_request(kvm, KVM_REQ_TLB_FLUSH))
                ++kvm->stat.remote_tlb_flush;
-       kvm->tlbs_dirty = false;
+       cmpxchg(&kvm->tlbs_dirty, dirty_count, 0);
 }
 EXPORT_SYMBOL_GPL(kvm_flush_remote_tlbs);
 
@@ -454,11 +457,11 @@ static struct kvm *kvm_create_vm(unsigned long type)
 
        r = kvm_arch_init_vm(kvm, type);
        if (r)
-               goto out_err_nodisable;
+               goto out_err_no_disable;
 
        r = hardware_enable_all();
        if (r)
-               goto out_err_nodisable;
+               goto out_err_no_disable;
 
 #ifdef CONFIG_HAVE_KVM_IRQCHIP
        INIT_HLIST_HEAD(&kvm->mask_notifier_list);
@@ -470,10 +473,12 @@ static struct kvm *kvm_create_vm(unsigned long type)
        r = -ENOMEM;
        kvm->memslots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);
        if (!kvm->memslots)
-               goto out_err_nosrcu;
+               goto out_err_no_srcu;
        kvm_init_memslots_id(kvm);
        if (init_srcu_struct(&kvm->srcu))
-               goto out_err_nosrcu;
+               goto out_err_no_srcu;
+       if (init_srcu_struct(&kvm->irq_srcu))
+               goto out_err_no_irq_srcu;
        for (i = 0; i < KVM_NR_BUSES; i++) {
                kvm->buses[i] = kzalloc(sizeof(struct kvm_io_bus),
                                        GFP_KERNEL);
@@ -502,10 +507,12 @@ static struct kvm *kvm_create_vm(unsigned long type)
        return kvm;
 
 out_err:
+       cleanup_srcu_struct(&kvm->irq_srcu);
+out_err_no_irq_srcu:
        cleanup_srcu_struct(&kvm->srcu);
-out_err_nosrcu:
+out_err_no_srcu:
        hardware_disable_all();
-out_err_nodisable:
+out_err_no_disable:
        for (i = 0; i < KVM_NR_BUSES; i++)
                kfree(kvm->buses[i]);
        kfree(kvm->memslots);
@@ -601,6 +608,7 @@ static void kvm_destroy_vm(struct kvm *kvm)
        kvm_arch_destroy_vm(kvm);
        kvm_destroy_devices(kvm);
        kvm_free_physmem(kvm);
+       cleanup_srcu_struct(&kvm->irq_srcu);
        cleanup_srcu_struct(&kvm->srcu);
        kvm_arch_free_vm(kvm);
        hardware_disable_all();
@@ -637,14 +645,12 @@ static int kvm_vm_release(struct inode *inode, struct file *filp)
  */
 static int kvm_create_dirty_bitmap(struct kvm_memory_slot *memslot)
 {
-#ifndef CONFIG_S390
        unsigned long dirty_bytes = 2 * kvm_dirty_bitmap_bytes(memslot);
 
        memslot->dirty_bitmap = kvm_kvzalloc(dirty_bytes);
        if (!memslot->dirty_bitmap)
                return -ENOMEM;
 
-#endif /* !CONFIG_S390 */
        return 0;
 }
 
@@ -2922,6 +2928,7 @@ static int __kvm_io_bus_read(struct kvm_io_bus *bus, struct kvm_io_range *range,
 
        return -EOPNOTSUPP;
 }
+EXPORT_SYMBOL_GPL(kvm_io_bus_write);
 
 /* kvm_io_bus_read - called under kvm->slots_lock */
 int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,